1
1
import type { CSSProperties , ExtractPropTypes , PropType } from 'vue' ;
2
2
import {
3
+ watch ,
3
4
defineComponent ,
4
5
nextTick ,
5
6
onBeforeUnmount ,
@@ -9,6 +10,7 @@ import {
9
10
ref ,
10
11
computed ,
11
12
} from 'vue' ;
13
+ import scrollIntoView from 'scroll-into-view-if-needed' ;
12
14
import classNames from '../_util/classNames' ;
13
15
import addEventListener from '../vc-util/Dom/addEventListener' ;
14
16
import Affix from '../affix' ;
@@ -20,13 +22,18 @@ import useStyle from './style';
20
22
import type { AnchorLinkProps } from './AnchorLink' ;
21
23
import AnchorLink from './AnchorLink' ;
22
24
import type { Key } from '../_util/type' ;
25
+ import PropTypes from '../_util/vue-types' ;
26
+ import devWarning from '../vc-util/devWarning' ;
23
27
24
28
export interface AnchorLinkItemProps extends AnchorLinkProps {
25
29
key : Key ;
26
30
class ?: String ;
27
31
style ?: CSSProperties ;
28
32
children ?: AnchorLinkItemProps [ ] ;
29
33
}
34
+
35
+ export type AnchorDirection = 'vertical' | 'horizontal' ;
36
+
30
37
function getDefaultContainer ( ) {
31
38
return window ;
32
39
}
@@ -73,6 +80,7 @@ export const anchorProps = () => ({
73
80
type : Array as PropType < AnchorLinkItemProps [ ] > ,
74
81
default : undefined as AnchorLinkItemProps [ ] ,
75
82
} ,
83
+ direction : PropTypes . oneOf ( [ 'vertical' , 'horizontal' ] as AnchorDirection [ ] ) . def ( 'vertical' ) ,
76
84
onChange : Function as PropType < ( currentActiveLink : string ) => void > ,
77
85
onClick : Function as PropType < ( e : MouseEvent , link : { title : any ; href : string } ) => void > ,
78
86
} ) ;
@@ -93,6 +101,24 @@ export default defineComponent({
93
101
props : anchorProps ( ) ,
94
102
setup ( props , { emit, attrs, slots, expose } ) {
95
103
const { prefixCls, getTargetContainer, direction } = useConfigInject ( 'anchor' , props ) ;
104
+ const anchorDirection = computed ( ( ) => props . direction ?? 'vertical' ) ;
105
+
106
+ if ( process . env . NODE_ENV !== 'production' ) {
107
+ devWarning (
108
+ typeof slots . default !== 'function' ,
109
+ 'Anchor' ,
110
+ '`Anchor children` is deprecated. Please use `items` instead.' ,
111
+ ) ;
112
+ }
113
+
114
+ if ( process . env . NODE_ENV !== 'production' ) {
115
+ devWarning (
116
+ ! ( anchorDirection . value === 'horizontal' && props . items ?. some ( n => 'children' in n ) ) ,
117
+ 'Anchor' ,
118
+ '`Anchor items#children` is not supported when `Anchor` direction is horizontal.' ,
119
+ ) ;
120
+ }
121
+
96
122
const spanLinkNode = ref < HTMLSpanElement > ( null ) ;
97
123
const anchorRef = ref ( ) ;
98
124
const state = reactive < AnchorState > ( {
@@ -184,12 +210,21 @@ export default defineComponent({
184
210
} ;
185
211
186
212
const updateInk = ( ) => {
187
- const linkNode = anchorRef . value . getElementsByClassName (
188
- `${ prefixCls . value } -link-title-active` ,
189
- ) [ 0 ] ;
213
+ const linkNode = anchorRef . value . querySelector ( `.${ prefixCls . value } -link-title-active` ) ;
190
214
if ( linkNode && spanLinkNode . value ) {
191
- spanLinkNode . value . style . top = `${ linkNode . offsetTop + linkNode . clientHeight / 2 } px` ;
192
- spanLinkNode . value . style . height = `${ linkNode . clientHeight } px` ;
215
+ const horizontalAnchor = anchorDirection . value === 'horizontal' ;
216
+ spanLinkNode . value . style . top = horizontalAnchor
217
+ ? ''
218
+ : `${ linkNode . offsetTop + linkNode . clientHeight / 2 } px` ;
219
+ spanLinkNode . value . style . height = horizontalAnchor ? '' : `${ linkNode . clientHeight } px` ;
220
+ spanLinkNode . value . style . left = horizontalAnchor ? `${ linkNode . offsetLeft } px` : '' ;
221
+ spanLinkNode . value . style . width = horizontalAnchor ? `${ linkNode . clientWidth } px` : '' ;
222
+ if ( horizontalAnchor ) {
223
+ scrollIntoView ( linkNode , {
224
+ scrollMode : 'if-needed' ,
225
+ block : 'nearest' ,
226
+ } ) ;
227
+ }
193
228
}
194
229
} ;
195
230
@@ -210,6 +245,7 @@ export default defineComponent({
210
245
handleClick : ( e , info ) => {
211
246
emit ( 'click' , e , info ) ;
212
247
} ,
248
+ direction : anchorDirection ,
213
249
} ) ;
214
250
215
251
onMounted ( ( ) => {
@@ -237,23 +273,31 @@ export default defineComponent({
237
273
}
238
274
updateInk ( ) ;
239
275
} ) ;
276
+
277
+ watch ( [ anchorDirection , getCurrentAnchor , state . links , activeLink ] , ( ) => {
278
+ updateInk ( ) ;
279
+ } ) ;
280
+
240
281
const createNestedLink = ( options ?: AnchorLinkItemProps [ ] ) =>
241
282
Array . isArray ( options )
242
283
? options . map ( item => (
243
284
< AnchorLink { ...item } key = { item . key } >
244
- { createNestedLink ( item . children ) }
285
+ { anchorDirection . value === 'vertical' ? createNestedLink ( item . children ) : null }
245
286
</ AnchorLink >
246
287
) )
247
288
: null ;
289
+
248
290
const [ wrapSSR , hashId ] = useStyle ( prefixCls ) ;
291
+
249
292
return ( ) => {
250
293
const { offsetTop, affix, showInkInFixed } = props ;
251
294
const pre = prefixCls . value ;
252
- const inkClass = classNames ( `${ pre } -ink-ball ` , {
253
- [ `${ pre } -ink-ball- visible` ] : activeLink . value ,
295
+ const inkClass = classNames ( `${ pre } -ink` , {
296
+ [ `${ pre } -ink-visible` ] : activeLink . value ,
254
297
} ) ;
255
298
256
299
const wrapperClass = classNames ( hashId . value , props . wrapperClass , `${ pre } -wrapper` , {
300
+ [ `${ pre } -wrapper-horizontal` ] : anchorDirection . value === 'horizontal' ,
257
301
[ `${ pre } -rtl` ] : direction . value === 'rtl' ,
258
302
} ) ;
259
303
@@ -268,9 +312,7 @@ export default defineComponent({
268
312
const anchorContent = (
269
313
< div class = { wrapperClass } style = { wrapperStyle } ref = { anchorRef } >
270
314
< div class = { anchorClass } >
271
- < div class = { `${ pre } -ink` } >
272
- < span class = { inkClass } ref = { spanLinkNode } />
273
- </ div >
315
+ < span class = { inkClass } ref = { spanLinkNode } />
274
316
{ Array . isArray ( props . items ) ? createNestedLink ( props . items ) : slots . default ?.( ) }
275
317
</ div >
276
318
</ div >
0 commit comments