Skip to content

Commit 0c219fe

Browse files
committed
feat(anchor): support direction api and update test snap
1 parent 9d3f5f2 commit 0c219fe

5 files changed

Lines changed: 867 additions & 57 deletions

File tree

packages/components/anchor/Anchor.tsx

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,17 @@ import type { TdAnchorProps } from './type';
2020

2121
export interface AnchorProps extends TdAnchorProps, StyledProps {
2222
children?: React.ReactNode;
23+
direction?: Direction;
2324
}
2425

26+
type Direction = 'vertical' | 'horizontal';
27+
28+
type CursorStyle<T = Direction> = T extends 'vertical'
29+
? { top: string; height?: string; opacity: number }
30+
: T extends 'horizontal'
31+
? { left: string; width?: string; opacity: number }
32+
: never;
33+
2534
interface IntervalRef {
2635
// 收集 anchor-item
2736
items: string[];
@@ -47,13 +56,14 @@ const Anchor = forwardRefWithStatics(
4756
onChange,
4857
className,
4958
getCurrentAnchor,
59+
direction,
5060
...rest
5161
} = useDefaultProps(props, anchorDefaultProps);
5262

5363
const { classPrefix } = useConfig();
5464

5565
const [activeItem, setActiveItem] = useState<string>('');
56-
const [cursorStyle, setCursorStyle] = useState<{ top: string; height?: string; opacity: number }>({
66+
const [cursorStyle, setCursorStyle] = useState<CursorStyle>({
5767
top: '0px',
5868
height: '0px',
5969
opacity: 0,
@@ -66,6 +76,8 @@ const Anchor = forwardRefWithStatics(
6676
handleScrollLock: false,
6777
});
6878

79+
const isHorizontal = direction === 'horizontal';
80+
6981
useImperativeHandle(ref, () => anchorEl.current);
7082

7183
/**
@@ -112,10 +124,13 @@ const Anchor = forwardRefWithStatics(
112124
if (!pointEl) {
113125
setCursorStyle(null);
114126
} else {
115-
const { offsetTop: top, offsetHeight: height } = pointEl;
116-
setCursorStyle({ top: `${top}px`, height: `${height}px`, opacity: 1 });
127+
const { offsetTop: top, offsetHeight: height, offsetLeft: left, offsetWidth: width } = pointEl;
128+
const style = isHorizontal
129+
? { left: `${left}px`, width: `${width}px`, opacity: 1 }
130+
: { top: `${top}px`, height: `${height}px`, opacity: 1 };
131+
setCursorStyle(style);
117132
}
118-
}, [activeItem, classPrefix]);
133+
}, [activeItem, classPrefix, isHorizontal]);
119134

120135
const handleScroll = useCallback(() => {
121136
const { scrollContainer, handleScrollLock } = intervalRef.current;
@@ -164,6 +179,7 @@ const Anchor = forwardRefWithStatics(
164179
[`${classPrefix}-size-s`]: size === 'small',
165180
[`${classPrefix}-size-m`]: size === 'medium',
166181
[`${classPrefix}-size-l`]: size === 'large',
182+
[`${classPrefix}-anchor__horizontal`]: isHorizontal,
167183
},
168184
className,
169185
);
@@ -194,7 +210,30 @@ const Anchor = forwardRefWithStatics(
194210
</AnchorContext.Provider>
195211
);
196212

197-
return isEmpty(affixProps) ? Cmp : <Affix {...affixProps}>{Cmp}</Affix>;
213+
return (
214+
<>
215+
<style>
216+
{`
217+
.t-anchor__horizontal{
218+
display:flex;
219+
width: fit-content;
220+
}
221+
.t-anchor__horizontal .t-anchor__line{
222+
top:100%;
223+
width:100%;
224+
height:1px;
225+
}
226+
.t-anchor__horizontal .t-anchor__line-cursor-wrapper{
227+
height:1px;
228+
}
229+
.t-anchor__horizontal .t-anchor__line-cursor-wrapper .t-anchor__line-cursor{
230+
width:100%;
231+
}
232+
`}
233+
</style>
234+
{isEmpty(affixProps) ? Cmp : <Affix {...affixProps}>{Cmp}</Affix>}
235+
</>
236+
);
198237
},
199238
{
200239
AnchorItem,
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import React from 'react';
2+
import { Anchor } from 'tdesign-react';
3+
4+
import type { TdAnchorProps } from 'tdesign-react';
5+
6+
const { AnchorItem } = Anchor;
7+
8+
export default function AnchorAttach() {
9+
const handleClick: TdAnchorProps['onClick'] = ({ e, href, title }) => {
10+
e.preventDefault();
11+
console.log('handleClick', href, title);
12+
};
13+
14+
return (
15+
<>
16+
<div id="attach" className="anchor-demo-attach">
17+
<Anchor direction="horizontal" container="#anchor-container" onClick={handleClick}>
18+
<AnchorItem href="#content-1" title="content-1" />
19+
<AnchorItem href="#content-2" title="content-2" />
20+
<AnchorItem href="#content-3" title="content-3" />
21+
<AnchorItem href="#content-4" title="content-4" />
22+
</Anchor>
23+
<div
24+
id="anchor-container"
25+
style={{ width: '100%', height: '200px', overflow: 'auto', textAlign: 'center', fontSize: '22px' }}
26+
>
27+
<div id="content-1" style={{ background: '#DFEFFF', lineHeight: '100px' }}>
28+
content-1
29+
</div>
30+
<div id="content-2" style={{ background: '#BFDBF7', lineHeight: '100px' }}>
31+
content-2
32+
</div>
33+
<div id="content-3" style={{ background: '#9BC5F2', lineHeight: '100px' }}>
34+
content-3
35+
</div>
36+
<div id="content-4" style={{ background: '#7BAFED', lineHeight: '100px' }}>
37+
content-4
38+
</div>
39+
<div id="content-5" style={{ background: '#5C99EB', lineHeight: '100px' }}>
40+
content-5
41+
</div>
42+
</div>
43+
</div>
44+
</>
45+
);
46+
}

packages/components/anchor/_example/cursor.tsx

Lines changed: 42 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,52 @@ const cursorStyle: React.CSSProperties = {
1313
marginLeft: '-5px',
1414
top: '50%',
1515
marginTop: '-5px',
16+
zIndex: 2,
1617
};
1718

1819
export default function AnchorBase() {
1920
return (
20-
<div style={{ display: 'flex' }}>
21-
<Anchor targetOffset={150} cursor={<div style={cursorStyle}></div>}>
22-
<AnchorItem href="#基础锚点" title="基础锚点" />
23-
<AnchorItem href="#多级锚点" title="多级锚点" />
24-
<AnchorItem href="#指定容器锚点" title="指定容器锚点" />
25-
<AnchorItem href="#特定交互锚点" title="特定交互锚点" />
26-
<AnchorItem href="#尺寸" title="尺寸"></AnchorItem>
21+
<>
22+
<Anchor
23+
direction="horizontal"
24+
targetOffset={0}
25+
container="#anchor-container"
26+
cursor={<div style={cursorStyle}></div>}
27+
>
28+
<AnchorItem href="#content-1" title="基础锚点" />
29+
<AnchorItem href="#content-2" title="多级锚点" />
30+
<AnchorItem href="#content-3" title="指定容器锚点" />
31+
<AnchorItem href="#content-4" title="特定交互锚点" />
2732
</Anchor>
28-
</div>
33+
<div style={{ display: 'flex', marginTop: '10px' }}>
34+
<Anchor targetOffset={0} container="#anchor-container" cursor={<div style={cursorStyle}></div>}>
35+
<AnchorItem href="#content-1" title="基础锚点" />
36+
<AnchorItem href="#content-2" title="多级锚点" />
37+
<AnchorItem href="#content-3" title="指定容器锚点" />
38+
<AnchorItem href="#content-4" title="特定交互锚点" />
39+
</Anchor>
40+
41+
<div
42+
id="anchor-container"
43+
style={{ width: '100%', height: '200px', overflow: 'auto', textAlign: 'center', fontSize: '22px' }}
44+
>
45+
<div id="content-1" style={{ background: '#DFEFFF', lineHeight: '100px' }}>
46+
content-1
47+
</div>
48+
<div id="content-2" style={{ background: '#BFDBF7', lineHeight: '100px' }}>
49+
content-2
50+
</div>
51+
<div id="content-3" style={{ background: '#9BC5F2', lineHeight: '100px' }}>
52+
content-3
53+
</div>
54+
<div id="content-4" style={{ background: '#7BAFED', lineHeight: '100px' }}>
55+
content-4
56+
</div>
57+
<div id="content-5" style={{ background: '#5C99EB', lineHeight: '100px' }}>
58+
content-5
59+
</div>
60+
</div>
61+
</div>
62+
</>
2963
);
3064
}

0 commit comments

Comments
 (0)