mirror of
https://gitee.com/ant-design/ant-design.git
synced 2024-11-30 11:08:45 +08:00
feat: Anchor horizontal (#39372)
* feat: AnchorInk * feat: update * feat: update ink * test: add test * feat: handle with nested link * feat: improve * fix: lint * feat: get direction from context in AnchorLink * docs: update demo * test: update snapshot * test: update snapshot * test: update snapshot * test: update test cases * test: update snapshot * test: update snapshots * test: update snapshots * test: update test cases * test: update test cases * docs: update * test: improve * docs: update demo * doc: update demos * doc: update demos again * feat: use scroll-into-view-if-needed * fix: hide scrollbar * docs: update demos * fix: active transition for horizontal anchor * chore: fix terser (#39617) * chore: fix terser * chore: clean up * test: update snapshot * feat: improve code style * feat: anchor ink improvement * feat: improve code style * fix: lint issue * test: update snapshots * docs: simplified the demo * feat: Merge the AnchorInk component back into the Anchor component * feat: Adjust DOM order * test: update snapshots * docs: Improve the document * feat: simplify css classnames * test: update snapshots Co-authored-by: 二货爱吃白萝卜 <smith3816@gmail.com>
This commit is contained in:
parent
00655846d7
commit
4eff22497d
@ -1,6 +1,8 @@
|
||||
import classNames from 'classnames';
|
||||
import addEventListener from 'rc-util/lib/Dom/addEventListener';
|
||||
import * as React from 'react';
|
||||
import scrollIntoView from 'scroll-into-view-if-needed';
|
||||
|
||||
import Affix from '../affix';
|
||||
import type { ConfigConsumerProps } from '../config-provider';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
@ -73,6 +75,7 @@ export interface AnchorProps {
|
||||
/** Listening event when scrolling change active link */
|
||||
onChange?: (currentActiveLink: string) => void;
|
||||
items?: AnchorLinkItemProps[];
|
||||
direction?: AnchorDirection;
|
||||
}
|
||||
|
||||
interface InternalAnchorProps extends AnchorProps {
|
||||
@ -91,6 +94,8 @@ export interface AnchorDefaultProps extends AnchorProps {
|
||||
getContainer: () => AnchorContainer;
|
||||
}
|
||||
|
||||
export type AnchorDirection = 'vertical' | 'horizontal';
|
||||
|
||||
export interface AntAnchor {
|
||||
registerLink: (link: string) => void;
|
||||
unregisterLink: (link: string) => void;
|
||||
@ -100,6 +105,7 @@ export interface AntAnchor {
|
||||
e: React.MouseEvent<HTMLAnchorElement, MouseEvent>,
|
||||
link: { title: React.ReactNode; href: string },
|
||||
) => void;
|
||||
direction: AnchorDirection;
|
||||
}
|
||||
|
||||
const AnchorContent: React.FC<InternalAnchorProps> = (props) => {
|
||||
@ -113,6 +119,7 @@ const AnchorContent: React.FC<InternalAnchorProps> = (props) => {
|
||||
showInkInFixed = false,
|
||||
children,
|
||||
items,
|
||||
direction: anchorDirection = 'vertical',
|
||||
bounds,
|
||||
targetOffset,
|
||||
onClick,
|
||||
@ -126,6 +133,14 @@ const AnchorContent: React.FC<InternalAnchorProps> = (props) => {
|
||||
warning(!children, 'Anchor', '`Anchor children` is deprecated. Please use `items` instead.');
|
||||
}
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
warning(
|
||||
!(anchorDirection === 'horizontal' && items?.some((n) => 'children' in n)),
|
||||
'Anchor',
|
||||
'`Anchor items#children` is not supported when `Anchor` direction is horizontal.',
|
||||
);
|
||||
}
|
||||
|
||||
const [links, setLinks] = React.useState<string[]>([]);
|
||||
const [activeLink, setActiveLink] = React.useState<string | null>(null);
|
||||
const activeLinkRef = React.useRef<string | null>(activeLink);
|
||||
@ -163,8 +178,17 @@ const AnchorContent: React.FC<InternalAnchorProps> = (props) => {
|
||||
`.${prefixCls}-link-title-active`,
|
||||
);
|
||||
if (linkNode && spanLinkNode.current) {
|
||||
spanLinkNode.current.style.top = `${linkNode.offsetTop + linkNode.clientHeight / 2}px`;
|
||||
spanLinkNode.current.style.height = `${linkNode.clientHeight}px`;
|
||||
if (anchorDirection !== 'horizontal') {
|
||||
spanLinkNode.current.style.top = `${linkNode.offsetTop + linkNode.clientHeight / 2}px`;
|
||||
spanLinkNode.current.style.height = `${linkNode.clientHeight}px`;
|
||||
} else {
|
||||
spanLinkNode.current.style.left = `${linkNode.offsetLeft}px`;
|
||||
spanLinkNode.current.style.width = `${linkNode.clientWidth}px`;
|
||||
scrollIntoView(linkNode, {
|
||||
scrollMode: 'if-needed',
|
||||
block: 'nearest',
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -250,17 +274,11 @@ const AnchorContent: React.FC<InternalAnchorProps> = (props) => {
|
||||
[targetOffset, offsetTop],
|
||||
);
|
||||
|
||||
const inkClass = classNames(
|
||||
{
|
||||
[`${prefixCls}-ink-ball-visible`]: activeLink,
|
||||
},
|
||||
`${prefixCls}-ink-ball`,
|
||||
);
|
||||
|
||||
const wrapperClass = classNames(
|
||||
rootClassName,
|
||||
`${prefixCls}-wrapper`,
|
||||
{
|
||||
[`${prefixCls}-wrapper-horizontal`]: anchorDirection === 'horizontal',
|
||||
[`${prefixCls}-rtl`]: direction === 'rtl',
|
||||
},
|
||||
className,
|
||||
@ -270,6 +288,10 @@ const AnchorContent: React.FC<InternalAnchorProps> = (props) => {
|
||||
[`${prefixCls}-fixed`]: !affix && !showInkInFixed,
|
||||
});
|
||||
|
||||
const inkClass = classNames(`${prefixCls}-ink`, {
|
||||
[`${prefixCls}-ink-visible`]: activeLink,
|
||||
});
|
||||
|
||||
const wrapperStyle: React.CSSProperties = {
|
||||
maxHeight: offsetTop ? `calc(100vh - ${offsetTop}px)` : '100vh',
|
||||
...style,
|
||||
@ -279,7 +301,7 @@ const AnchorContent: React.FC<InternalAnchorProps> = (props) => {
|
||||
Array.isArray(options)
|
||||
? options.map((item) => (
|
||||
<AnchorLink {...item} key={item.key}>
|
||||
{createNestedLink(item.children)}
|
||||
{anchorDirection === 'vertical' && createNestedLink(item.children)}
|
||||
</AnchorLink>
|
||||
))
|
||||
: null;
|
||||
@ -287,9 +309,7 @@ const AnchorContent: React.FC<InternalAnchorProps> = (props) => {
|
||||
const anchorContent = (
|
||||
<div ref={wrapperRef} className={wrapperClass} style={wrapperStyle}>
|
||||
<div className={anchorClass}>
|
||||
<div className={`${prefixCls}-ink`}>
|
||||
<span className={inkClass} ref={spanLinkNode} />
|
||||
</div>
|
||||
<span className={inkClass} ref={spanLinkNode} />
|
||||
{'items' in props ? createNestedLink(items) : children}
|
||||
</div>
|
||||
</div>
|
||||
@ -312,7 +332,7 @@ const AnchorContent: React.FC<InternalAnchorProps> = (props) => {
|
||||
|
||||
React.useEffect(() => {
|
||||
updateInk();
|
||||
}, [getCurrentAnchor, dependencyListItem, activeLink]);
|
||||
}, [anchorDirection, getCurrentAnchor, dependencyListItem, activeLink]);
|
||||
|
||||
const memoizedContextValue = React.useMemo<AntAnchor>(
|
||||
() => ({
|
||||
@ -321,8 +341,9 @@ const AnchorContent: React.FC<InternalAnchorProps> = (props) => {
|
||||
scrollTo: handleScrollTo,
|
||||
activeLink,
|
||||
onClick,
|
||||
direction: anchorDirection,
|
||||
}),
|
||||
[activeLink, onClick, handleScrollTo],
|
||||
[activeLink, onClick, handleScrollTo, anchorDirection],
|
||||
);
|
||||
|
||||
return (
|
||||
|
@ -1,7 +1,8 @@
|
||||
import classNames from 'classnames';
|
||||
import * as React from 'react';
|
||||
import type { ConfigConsumerProps } from '../config-provider';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import { ConfigConsumer } from '../config-provider';
|
||||
import warning from '../_util/warning';
|
||||
import type { AntAnchor } from './Anchor';
|
||||
import AnchorContext from './context';
|
||||
|
||||
@ -22,7 +23,7 @@ const AnchorLink: React.FC<AnchorLinkProps> = (props) => {
|
||||
|
||||
const context = React.useContext<AntAnchor | undefined>(AnchorContext);
|
||||
|
||||
const { registerLink, unregisterLink, scrollTo, onClick, activeLink } = context || {};
|
||||
const { registerLink, unregisterLink, scrollTo, onClick, activeLink, direction } = context || {};
|
||||
|
||||
React.useEffect(() => {
|
||||
registerLink?.(href);
|
||||
@ -36,31 +37,42 @@ const AnchorLink: React.FC<AnchorLinkProps> = (props) => {
|
||||
scrollTo?.(href);
|
||||
};
|
||||
|
||||
const { getPrefixCls } = React.useContext<ConfigConsumerProps>(ConfigContext);
|
||||
|
||||
const prefixCls = getPrefixCls('anchor', customizePrefixCls);
|
||||
|
||||
const wrapperClassName = classNames(`${prefixCls}-link`, className, {
|
||||
[`${prefixCls}-link-active`]: activeLink === href,
|
||||
});
|
||||
|
||||
const titleClassName = classNames(`${prefixCls}-link-title`, {
|
||||
[`${prefixCls}-link-title-active`]: activeLink === href,
|
||||
});
|
||||
// =================== Warning =====================
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
warning(
|
||||
!children || direction !== 'horizontal',
|
||||
'Anchor.Link',
|
||||
'`Anchor.Link children` is not supported when `Anchor` direction is horizontal',
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={wrapperClassName}>
|
||||
<a
|
||||
className={titleClassName}
|
||||
href={href}
|
||||
title={typeof title === 'string' ? title : ''}
|
||||
target={target}
|
||||
onClick={handleClick}
|
||||
>
|
||||
{title}
|
||||
</a>
|
||||
{children}
|
||||
</div>
|
||||
<ConfigConsumer>
|
||||
{({ getPrefixCls }: ConfigConsumerProps) => {
|
||||
const prefixCls = getPrefixCls('anchor', customizePrefixCls);
|
||||
const active = activeLink === href;
|
||||
const wrapperClassName = classNames(`${prefixCls}-link`, className, {
|
||||
[`${prefixCls}-link-active`]: active,
|
||||
});
|
||||
const titleClassName = classNames(`${prefixCls}-link-title`, {
|
||||
[`${prefixCls}-link-title-active`]: active,
|
||||
});
|
||||
return (
|
||||
<div className={wrapperClassName}>
|
||||
<a
|
||||
className={titleClassName}
|
||||
href={href}
|
||||
title={typeof title === 'string' ? title : ''}
|
||||
target={target}
|
||||
onClick={handleClick}
|
||||
>
|
||||
{title}
|
||||
</a>
|
||||
{direction !== 'horizontal' ? children : null}
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
</ConfigConsumer>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -1,4 +1,7 @@
|
||||
import React from 'react';
|
||||
import { resetWarned } from 'rc-util/lib/warning';
|
||||
import scrollIntoView from 'scroll-into-view-if-needed';
|
||||
|
||||
import Anchor from '..';
|
||||
import { fireEvent, render, waitFakeTimer } from '../../../tests/utils';
|
||||
|
||||
@ -13,12 +16,15 @@ function createDiv() {
|
||||
let idCounter = 0;
|
||||
const getHashUrl = () => `Anchor-API-${idCounter++}`;
|
||||
|
||||
jest.mock('scroll-into-view-if-needed', () => jest.fn());
|
||||
|
||||
describe('Anchor Render', () => {
|
||||
const getBoundingClientRectMock = jest.spyOn(
|
||||
HTMLHeadingElement.prototype,
|
||||
'getBoundingClientRect',
|
||||
);
|
||||
const getClientRectsMock = jest.spyOn(HTMLHeadingElement.prototype, 'getClientRects');
|
||||
const scrollIntoViewMock = jest.createMockFromModule<any>('scroll-into-view-if-needed');
|
||||
|
||||
beforeAll(() => {
|
||||
jest.useFakeTimers();
|
||||
@ -27,11 +33,12 @@ describe('Anchor Render', () => {
|
||||
height: 100,
|
||||
top: 1000,
|
||||
} as DOMRect);
|
||||
getClientRectsMock.mockReturnValue({ length: 1 } as DOMRectList);
|
||||
getClientRectsMock.mockReturnValue([1] as unknown as DOMRectList);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
jest.useFakeTimers();
|
||||
scrollIntoViewMock.mockReset();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@ -46,22 +53,133 @@ describe('Anchor Render', () => {
|
||||
getClientRectsMock.mockRestore();
|
||||
});
|
||||
|
||||
it('renders correctly', () => {
|
||||
const hash = getHashUrl();
|
||||
const { container } = render(
|
||||
<Anchor>
|
||||
<Link href={`#${hash}`} title={hash} />
|
||||
it('renders items correctly', () => {
|
||||
const { container, asFragment } = render(
|
||||
<Anchor
|
||||
items={[
|
||||
{
|
||||
key: '1',
|
||||
href: '#components-anchor-demo-basic',
|
||||
title: 'Item Basic Demo',
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
href: '#components-anchor-demo-static',
|
||||
title: 'Static demo',
|
||||
},
|
||||
{
|
||||
key: '3',
|
||||
href: '#api',
|
||||
title: 'API',
|
||||
children: [
|
||||
{
|
||||
key: '4',
|
||||
href: '#anchor-props',
|
||||
title: 'Anchor Props',
|
||||
children: [
|
||||
{
|
||||
key: '5',
|
||||
href: '#link-props',
|
||||
title: 'Link Props',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
/>,
|
||||
);
|
||||
expect(container.querySelectorAll('.ant-anchor .ant-anchor-link').length).toBe(5);
|
||||
const linkTitles = Array.from(container.querySelector('.ant-anchor')?.childNodes!).map((n) =>
|
||||
(n as HTMLElement).querySelector('.ant-anchor-link-title'),
|
||||
);
|
||||
expect((linkTitles[1] as HTMLAnchorElement).href).toContain('#components-anchor-demo-basic');
|
||||
expect((linkTitles[2] as HTMLAnchorElement).href).toContain('#components-anchor-demo-static');
|
||||
expect((linkTitles[3] as HTMLAnchorElement).href).toContain('#api');
|
||||
expect(
|
||||
(
|
||||
container.querySelector(
|
||||
'.ant-anchor .ant-anchor-link .ant-anchor-link .ant-anchor-link-title',
|
||||
) as HTMLAnchorElement
|
||||
)?.href,
|
||||
).toContain('#anchor-props');
|
||||
expect(
|
||||
(
|
||||
container.querySelector(
|
||||
'.ant-anchor .ant-anchor-link .ant-anchor-link .ant-anchor-link .ant-anchor-link-title',
|
||||
) as HTMLAnchorElement
|
||||
)?.href,
|
||||
).toContain('#link-props');
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('renders items correctly#horizontal', () => {
|
||||
const { container, asFragment } = render(
|
||||
<Anchor
|
||||
items={[
|
||||
{
|
||||
key: '1',
|
||||
href: '#components-anchor-demo-basic',
|
||||
title: 'Item Basic Demo',
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
href: '#components-anchor-demo-static',
|
||||
title: 'Static demo',
|
||||
},
|
||||
{
|
||||
key: '3',
|
||||
href: '#api',
|
||||
title: 'API',
|
||||
},
|
||||
]}
|
||||
/>,
|
||||
);
|
||||
expect(container.querySelectorAll('.ant-anchor .ant-anchor-link').length).toBe(3);
|
||||
const linkTitles = Array.from(container.querySelector('.ant-anchor')?.childNodes!).map((n) =>
|
||||
(n as HTMLElement).querySelector('.ant-anchor-link-title'),
|
||||
);
|
||||
expect((linkTitles[1] as HTMLAnchorElement).href).toContain('#components-anchor-demo-basic');
|
||||
expect((linkTitles[2] as HTMLAnchorElement).href).toContain('#components-anchor-demo-static');
|
||||
expect((linkTitles[3] as HTMLAnchorElement).href).toContain('#api');
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('render items and ignore jsx children', () => {
|
||||
const { container, asFragment } = render(
|
||||
<Anchor
|
||||
items={[
|
||||
{
|
||||
key: '1',
|
||||
href: '#components-anchor-demo-basic',
|
||||
title: 'Item Basic Demo',
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Link href="#api" title="API" />
|
||||
</Anchor>,
|
||||
);
|
||||
expect(container.querySelector(`a[href="#${hash}"]`)).not.toBe(null);
|
||||
expect(container.querySelectorAll('.ant-anchor .ant-anchor-link').length).toBe(1);
|
||||
expect(
|
||||
(container.querySelector('.ant-anchor .ant-anchor-link-title') as HTMLAnchorElement).href,
|
||||
).toContain('#components-anchor-demo-basic');
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('actives the target when clicking a link', async () => {
|
||||
const hash = getHashUrl();
|
||||
const { container } = render(
|
||||
<Anchor prefixCls="ant-anchor">
|
||||
<Link href={`http://www.example.com/#${hash}`} title={hash} />
|
||||
</Anchor>,
|
||||
<Anchor
|
||||
prefixCls="ant-anchor"
|
||||
direction="horizontal"
|
||||
items={[
|
||||
{
|
||||
key: hash,
|
||||
title: hash,
|
||||
href: `http://www.example.com/#${hash}`,
|
||||
},
|
||||
]}
|
||||
/>,
|
||||
);
|
||||
const link = container.querySelector(`a[href="http://www.example.com/#${hash}"]`)!;
|
||||
fireEvent.click(link);
|
||||
@ -74,9 +192,7 @@ describe('Anchor Render', () => {
|
||||
const scrollToSpy = jest.spyOn(window, 'scrollTo');
|
||||
render(<div id="/faq?locale=en#Q1">Q1</div>, { container: root });
|
||||
const { container } = render(
|
||||
<Anchor>
|
||||
<Link href="/#/faq?locale=en#Q1" title="Q1" />
|
||||
</Anchor>,
|
||||
<Anchor items={[{ key: 'Q1', title: 'Q1', href: '/#/faq?locale=en#Q1' }]} />,
|
||||
);
|
||||
const link = container.querySelector(`a[href="/#/faq?locale=en#Q1"]`)!;
|
||||
fireEvent.click(link);
|
||||
@ -97,10 +213,13 @@ describe('Anchor Render', () => {
|
||||
{ container: root },
|
||||
);
|
||||
const { container } = render(
|
||||
<Anchor onChange={onChange}>
|
||||
<Link href={`#${hash1}`} title={hash1} />
|
||||
<Link href={`#${hash2}`} title={hash2} />
|
||||
</Anchor>,
|
||||
<Anchor
|
||||
onChange={onChange}
|
||||
items={[
|
||||
{ key: hash1, href: `#${hash1}`, title: hash1 },
|
||||
{ key: hash2, href: `#${hash2}`, title: hash2 },
|
||||
]}
|
||||
/>,
|
||||
);
|
||||
onChange.mockClear();
|
||||
|
||||
@ -119,9 +238,7 @@ describe('Anchor Render', () => {
|
||||
it('should update DOM when children are unmounted', () => {
|
||||
const hash = getHashUrl();
|
||||
const { container, rerender } = render(
|
||||
<Anchor>
|
||||
<Link href={`#${hash}`} title={hash} />
|
||||
</Anchor>,
|
||||
<Anchor items={[{ key: hash, href: `#${hash}`, title: hash }]} />,
|
||||
);
|
||||
|
||||
expect(container.querySelectorAll('.ant-anchor-link-title')).toHaveLength(1);
|
||||
@ -134,11 +251,7 @@ describe('Anchor Render', () => {
|
||||
it('should update DOM when link href is changed', async () => {
|
||||
const hash = getHashUrl();
|
||||
function AnchorUpdate({ href }: { href: string }) {
|
||||
return (
|
||||
<Anchor>
|
||||
<Link href={href} title={hash} />
|
||||
</Anchor>
|
||||
);
|
||||
return <Anchor items={[{ key: hash, href, title: hash }]} />;
|
||||
}
|
||||
const { container, rerender } = render(<AnchorUpdate href={`#${hash}`} />);
|
||||
|
||||
@ -154,17 +267,11 @@ describe('Anchor Render', () => {
|
||||
const root = createDiv();
|
||||
render(<h1 id={hash}>Hello</h1>, { container: root });
|
||||
const { container, rerender } = render(
|
||||
<Anchor>
|
||||
<Link href={`#${hash}`} title={hash} />
|
||||
</Anchor>,
|
||||
<Anchor items={[{ key: hash, href: `#${hash}`, title: hash }]} />,
|
||||
);
|
||||
|
||||
const setProps = (props: Record<string, any>) =>
|
||||
rerender(
|
||||
<Anchor {...props}>
|
||||
<Link href={`#${hash}`} title={hash} />
|
||||
</Anchor>,
|
||||
);
|
||||
rerender(<Anchor {...props} items={[{ key: hash, href: `#${hash}`, title: hash }]} />);
|
||||
|
||||
fireEvent.click(container.querySelector(`a[href="#${hash}"]`)!);
|
||||
await waitFakeTimer();
|
||||
@ -191,17 +298,11 @@ describe('Anchor Render', () => {
|
||||
const root = createDiv();
|
||||
render(<h1 id={hash}>Hello</h1>, { container: root });
|
||||
const { container, rerender } = render(
|
||||
<Anchor>
|
||||
<Link href={`#${hash}`} title={hash} />
|
||||
</Anchor>,
|
||||
<Anchor items={[{ key: hash, href: `#${hash}`, title: hash }]} />,
|
||||
);
|
||||
|
||||
const setProps = (props: Record<string, any>) =>
|
||||
rerender(
|
||||
<Anchor {...props}>
|
||||
<Link href={`#${hash}`} title={hash} />
|
||||
</Anchor>,
|
||||
);
|
||||
rerender(<Anchor {...props} items={[{ key: hash, href: `#${hash}`, title: hash }]} />);
|
||||
|
||||
fireEvent.click(container.querySelector(`a[href="#${hash}"]`)!);
|
||||
await waitFakeTimer();
|
||||
@ -233,9 +334,7 @@ describe('Anchor Render', () => {
|
||||
const href = `#${hash}`;
|
||||
const title = hash;
|
||||
const { container } = render(
|
||||
<Anchor onClick={handleClick}>
|
||||
<Link href={href} title={title} />
|
||||
</Anchor>,
|
||||
<Anchor onClick={handleClick} items={[{ key: hash, href, title }]} />,
|
||||
);
|
||||
|
||||
fireEvent.click(container.querySelector(`a[href="${href}"]`)!);
|
||||
@ -248,10 +347,21 @@ describe('Anchor Render', () => {
|
||||
const hash2 = getHashUrl();
|
||||
const onChange = jest.fn();
|
||||
const { container } = render(
|
||||
<Anchor onChange={onChange}>
|
||||
<Link href={`#${hash1}`} title={hash1} />
|
||||
<Link href={`#${hash2}`} title={hash2} />
|
||||
</Anchor>,
|
||||
<Anchor
|
||||
onChange={onChange}
|
||||
items={[
|
||||
{
|
||||
key: hash1,
|
||||
href: `#${hash1}`,
|
||||
title: hash1,
|
||||
},
|
||||
{
|
||||
key: hash2,
|
||||
href: `#${hash2}`,
|
||||
title: hash2,
|
||||
},
|
||||
]}
|
||||
/>,
|
||||
// https://github.com/testing-library/react-testing-library/releases/tag/v13.0.0
|
||||
// @ts-ignore
|
||||
{ legacyRoot: true },
|
||||
@ -265,9 +375,7 @@ describe('Anchor Render', () => {
|
||||
|
||||
it('handles invalid hash correctly', () => {
|
||||
const { container } = render(
|
||||
<Anchor>
|
||||
<Link href="notexsited" title="title" />
|
||||
</Anchor>,
|
||||
<Anchor items={[{ key: 'title', href: 'notexsited', title: 'title' }]} />,
|
||||
);
|
||||
|
||||
const link = container.querySelector(`a[href="notexsited"]`)!;
|
||||
@ -357,10 +465,13 @@ describe('Anchor Render', () => {
|
||||
const hash2 = getHashUrl();
|
||||
const getCurrentAnchor = () => `#${hash2}`;
|
||||
const { container } = render(
|
||||
<Anchor getCurrentAnchor={getCurrentAnchor}>
|
||||
<Link href={`#${hash1}`} title={hash1} />
|
||||
<Link href={`#${hash2}`} title={hash2} />
|
||||
</Anchor>,
|
||||
<Anchor
|
||||
getCurrentAnchor={getCurrentAnchor}
|
||||
items={[
|
||||
{ key: hash1, href: `#${hash1}`, title: hash1 },
|
||||
{ key: hash2, href: `#${hash2}`, title: hash2 },
|
||||
]}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(container.querySelector(`.ant-anchor-link-title-active`)?.textContent).toBe(hash2);
|
||||
@ -372,10 +483,14 @@ describe('Anchor Render', () => {
|
||||
const hash2 = getHashUrl();
|
||||
const onChange = jest.fn();
|
||||
const { container } = render(
|
||||
<Anchor onChange={onChange} getCurrentAnchor={() => hash1}>
|
||||
<Link href={`#${hash1}`} title={hash1} />
|
||||
<Link href={`#${hash2}`} title={hash2} />
|
||||
</Anchor>,
|
||||
<Anchor
|
||||
onChange={onChange}
|
||||
getCurrentAnchor={() => hash1}
|
||||
items={[
|
||||
{ key: hash1, href: `#${hash1}`, title: hash1 },
|
||||
{ key: hash2, href: `#${hash2}`, title: hash2 },
|
||||
]}
|
||||
/>,
|
||||
// https://github.com/testing-library/react-testing-library/releases/tag/v13.0.0
|
||||
// @ts-ignore
|
||||
{ legacyRoot: true },
|
||||
@ -393,10 +508,13 @@ describe('Anchor Render', () => {
|
||||
const hash2 = getHashUrl();
|
||||
const getCurrentAnchor = jest.fn();
|
||||
const { container } = render(
|
||||
<Anchor getCurrentAnchor={getCurrentAnchor}>
|
||||
<Link href={`#${hash1}`} title={hash1} />
|
||||
<Link href={`#${hash2}`} title={hash2} />
|
||||
</Anchor>,
|
||||
<Anchor
|
||||
getCurrentAnchor={getCurrentAnchor}
|
||||
items={[
|
||||
{ key: hash1, href: `#${hash1}`, title: hash1 },
|
||||
{ key: hash2, href: `#${hash2}`, title: hash2 },
|
||||
]}
|
||||
/>,
|
||||
);
|
||||
|
||||
fireEvent.click(container.querySelector(`a[href="#${hash1}"]`)!);
|
||||
@ -410,10 +528,13 @@ describe('Anchor Render', () => {
|
||||
const hash1 = getHashUrl();
|
||||
const hash2 = getHashUrl();
|
||||
const Demo: React.FC<{ current: string }> = ({ current }) => (
|
||||
<Anchor getCurrentAnchor={() => `#${current}`}>
|
||||
<Link href={`#${hash1}`} title={hash1} />
|
||||
<Link href={`#${hash2}`} title={hash2} />
|
||||
</Anchor>
|
||||
<Anchor
|
||||
getCurrentAnchor={() => `#${current}`}
|
||||
items={[
|
||||
{ key: hash1, href: `#${hash1}`, title: hash1 },
|
||||
{ key: hash2, href: `#${hash2}`, title: hash2 },
|
||||
]}
|
||||
/>
|
||||
);
|
||||
const { container, rerender } = render(<Demo current={hash1} />);
|
||||
expect(container.querySelector(`.ant-anchor-link-title-active`)?.textContent).toBe(hash1);
|
||||
@ -424,72 +545,348 @@ describe('Anchor Render', () => {
|
||||
it('should render correctly when href is null', () => {
|
||||
expect(() => {
|
||||
render(
|
||||
<Anchor>
|
||||
<Link href={null as unknown as string} title="test" />
|
||||
</Anchor>,
|
||||
<Anchor items={[{ key: 'test', href: null as unknown as string, title: 'test' }]} />,
|
||||
);
|
||||
fireEvent.scroll(window || document);
|
||||
}).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
it('renders items correctly', () => {
|
||||
const { container, asFragment } = render(
|
||||
<Anchor
|
||||
items={[
|
||||
{
|
||||
key: '1',
|
||||
href: '#components-anchor-demo-basic',
|
||||
title: 'Item Basic Demo',
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
href: '#components-anchor-demo-static',
|
||||
title: 'Static demo',
|
||||
},
|
||||
{
|
||||
key: '3',
|
||||
href: '#api',
|
||||
title: 'API',
|
||||
children: [
|
||||
describe('horizontal anchor', () => {
|
||||
describe('scroll x', () => {
|
||||
it('targetOffset horizontal', async () => {
|
||||
const hash = getHashUrl();
|
||||
const scrollToSpy = jest.spyOn(window, 'scrollTo');
|
||||
const root = createDiv();
|
||||
render(<h1 id={hash}>Hello</h1>, { container: root });
|
||||
const { container, rerender } = render(
|
||||
<Anchor
|
||||
direction="horizontal"
|
||||
items={[
|
||||
{
|
||||
key: '4',
|
||||
href: '#anchor-props',
|
||||
title: 'Anchor Props',
|
||||
children: [
|
||||
{
|
||||
key: '5',
|
||||
href: '#link-props',
|
||||
title: 'Link Props',
|
||||
},
|
||||
],
|
||||
key: hash,
|
||||
href: `#${hash}`,
|
||||
title: hash,
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
/>,
|
||||
);
|
||||
expect(container.querySelectorAll('.ant-anchor .ant-anchor-link').length).toBe(5);
|
||||
const linkTitles = Array.from(container.querySelector('.ant-anchor')?.childNodes!)
|
||||
.slice(1)
|
||||
.map((n) => (n as HTMLElement).querySelector('.ant-anchor-link-title'));
|
||||
expect((linkTitles[0] as HTMLAnchorElement).href).toContain('#components-anchor-demo-basic');
|
||||
expect((linkTitles[1] as HTMLAnchorElement).href).toContain('#components-anchor-demo-static');
|
||||
expect((linkTitles[2] as HTMLAnchorElement).href).toContain('#api');
|
||||
expect(asFragment().firstChild).toMatchSnapshot();
|
||||
expect(
|
||||
(
|
||||
container.querySelector(
|
||||
'.ant-anchor .ant-anchor-link .ant-anchor-link .ant-anchor-link-title',
|
||||
) as HTMLAnchorElement
|
||||
)?.href,
|
||||
).toContain('#anchor-props');
|
||||
expect(
|
||||
(
|
||||
container.querySelector(
|
||||
'.ant-anchor .ant-anchor-link .ant-anchor-link .ant-anchor-link .ant-anchor-link-title',
|
||||
) as HTMLAnchorElement
|
||||
)?.href,
|
||||
).toContain('#link-props');
|
||||
]}
|
||||
/>,
|
||||
);
|
||||
const setProps = (props: Record<string, any>) =>
|
||||
rerender(
|
||||
<Anchor
|
||||
{...props}
|
||||
direction="horizontal"
|
||||
items={[
|
||||
{
|
||||
key: hash,
|
||||
href: `#${hash}`,
|
||||
title: hash,
|
||||
},
|
||||
]}
|
||||
/>,
|
||||
);
|
||||
fireEvent.click(container.querySelector(`a[href="#${hash}"]`)!);
|
||||
await waitFakeTimer();
|
||||
|
||||
expect(scrollIntoView).toHaveBeenCalled();
|
||||
expect(scrollToSpy).toHaveBeenLastCalledWith(0, 1000);
|
||||
|
||||
setProps({ offsetTop: 100 });
|
||||
|
||||
fireEvent.click(container.querySelector(`a[href="#${hash}"]`)!);
|
||||
await waitFakeTimer();
|
||||
expect(scrollToSpy).toHaveBeenLastCalledWith(0, 900);
|
||||
|
||||
setProps({ targetOffset: 200 });
|
||||
fireEvent.click(container.querySelector(`a[href="#${hash}"]`)!);
|
||||
await waitFakeTimer();
|
||||
expect(scrollToSpy).toHaveBeenLastCalledWith(0, 800);
|
||||
});
|
||||
});
|
||||
|
||||
it('test direction prop', () => {
|
||||
const { container } = render(
|
||||
<Anchor
|
||||
direction="horizontal"
|
||||
items={[
|
||||
{
|
||||
key: '1',
|
||||
href: '#components-anchor-demo-basic',
|
||||
title: 'Item Basic Demo',
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
href: '#components-anchor-demo-static',
|
||||
title: 'Static demo',
|
||||
},
|
||||
{
|
||||
key: '3',
|
||||
href: '#api',
|
||||
title: 'API',
|
||||
},
|
||||
]}
|
||||
/>,
|
||||
);
|
||||
expect(container.querySelectorAll('.ant-anchor-ink').length).toBe(1);
|
||||
expect(
|
||||
container
|
||||
.querySelector('.ant-anchor-wrapper')
|
||||
?.classList.contains('ant-anchor-wrapper-horizontal'),
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
it('nested children via items should be filtered out when direction is horizontal', () => {
|
||||
const { container } = render(
|
||||
<Anchor
|
||||
direction="horizontal"
|
||||
items={[
|
||||
{
|
||||
key: '1',
|
||||
href: '#components-anchor-demo-basic',
|
||||
title: 'Item Basic Demo',
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
href: '#components-anchor-demo-static',
|
||||
title: 'Static demo',
|
||||
},
|
||||
{
|
||||
key: '3',
|
||||
href: '#api',
|
||||
title: 'API',
|
||||
children: [
|
||||
{
|
||||
key: '4',
|
||||
href: '#anchor-props',
|
||||
title: 'Anchor Props',
|
||||
},
|
||||
{
|
||||
key: '5',
|
||||
href: '#link-props',
|
||||
title: 'Link Props',
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
/>,
|
||||
);
|
||||
expect(container.querySelectorAll('.ant-anchor-link').length).toBe(3);
|
||||
});
|
||||
|
||||
it('nested children via jsx should be filtered out when direction is horizontal', () => {
|
||||
const { container } = render(
|
||||
<Anchor direction="horizontal">
|
||||
<Link href="#components-anchor-demo-basic" title="Basic demo" />
|
||||
<Link href="#components-anchor-demo-static" title="Static demo" />
|
||||
<Link href="#api" title="API">
|
||||
<Link href="#anchor-props" title="Anchor Props" />
|
||||
<Link href="#link-props" title="Link Props" />
|
||||
</Link>
|
||||
</Anchor>,
|
||||
);
|
||||
expect(container.querySelectorAll('.ant-anchor-link').length).toBe(3);
|
||||
});
|
||||
});
|
||||
|
||||
describe('deprecated/legacy jsx syntax', () => {
|
||||
it('renders jsx correctly', () => {
|
||||
const hash = getHashUrl();
|
||||
const { container } = render(
|
||||
<Anchor>
|
||||
<Link href={`#${hash}`} title={hash} />
|
||||
</Anchor>,
|
||||
);
|
||||
expect(container.querySelector(`a[href="#${hash}"]`)).not.toBe(null);
|
||||
});
|
||||
|
||||
it('actives the target when clicking a link', async () => {
|
||||
const hash = getHashUrl();
|
||||
const { container } = render(
|
||||
<Anchor prefixCls="ant-anchor">
|
||||
<Link href={`http://www.example.com/#${hash}`} title={hash} />
|
||||
</Anchor>,
|
||||
);
|
||||
const link = container.querySelector(`a[href="http://www.example.com/#${hash}"]`)!;
|
||||
fireEvent.click(link);
|
||||
await waitFakeTimer();
|
||||
expect(link.classList).toContain('ant-anchor-link-title-active');
|
||||
});
|
||||
|
||||
it('scrolls the page when clicking a link', async () => {
|
||||
const root = createDiv();
|
||||
const scrollToSpy = jest.spyOn(window, 'scrollTo');
|
||||
render(<div id="/faq?locale=en#Q1">Q1</div>, { container: root });
|
||||
const { container } = render(
|
||||
<Anchor>
|
||||
<Link href="/#/faq?locale=en#Q1" title="Q1" />
|
||||
</Anchor>,
|
||||
);
|
||||
const link = container.querySelector(`a[href="/#/faq?locale=en#Q1"]`)!;
|
||||
fireEvent.click(link);
|
||||
await waitFakeTimer();
|
||||
expect(scrollToSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('handleScroll should not be triggered when scrolling caused by clicking a link', async () => {
|
||||
const hash1 = getHashUrl();
|
||||
const hash2 = getHashUrl();
|
||||
const root = createDiv();
|
||||
const onChange = jest.fn();
|
||||
render(
|
||||
<div>
|
||||
<div id={hash1}>Hello</div>
|
||||
<div id={hash2}>World</div>
|
||||
</div>,
|
||||
{ container: root },
|
||||
);
|
||||
const { container } = render(
|
||||
<Anchor onChange={onChange}>
|
||||
<Link href={`#${hash1}`} title={hash1} />
|
||||
<Link href={`#${hash2}`} title={hash2} />
|
||||
</Anchor>,
|
||||
);
|
||||
onChange.mockClear();
|
||||
|
||||
const link = container.querySelector(`a[href="#${hash2}"]`)!;
|
||||
// this will trigger 1 onChange
|
||||
fireEvent.click(link);
|
||||
// smooth scroll caused by clicking needs time to finish.
|
||||
// we scroll the window before it finish, the scroll listener should not be triggered,
|
||||
fireEvent.scroll(window);
|
||||
|
||||
await waitFakeTimer();
|
||||
// if the scroll listener is triggered, we will get 2 onChange, now we expect only 1.
|
||||
expect(onChange).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should update DOM when children are unmounted', () => {
|
||||
const hash = getHashUrl();
|
||||
const { container, rerender } = render(
|
||||
<Anchor>
|
||||
<Link href={`#${hash}`} title={hash} />
|
||||
</Anchor>,
|
||||
);
|
||||
|
||||
expect(container.querySelectorAll('.ant-anchor-link-title')).toHaveLength(1);
|
||||
expect(container.querySelector('.ant-anchor-link-title')).toHaveAttribute('href', `#${hash}`);
|
||||
|
||||
rerender(<Anchor />);
|
||||
expect(container.querySelector('.ant-anchor-link-title')).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should update DOM when link href is changed', async () => {
|
||||
const hash = getHashUrl();
|
||||
function AnchorUpdate({ href }: { href: string }) {
|
||||
return (
|
||||
<Anchor>
|
||||
<Link href={href} title={hash} />
|
||||
</Anchor>
|
||||
);
|
||||
}
|
||||
const { container, rerender } = render(<AnchorUpdate href={`#${hash}`} />);
|
||||
|
||||
expect(container.querySelector(`a[href="#${hash}"]`)).toBeTruthy();
|
||||
rerender(<AnchorUpdate href={`#${hash}_1`} />);
|
||||
expect(container.querySelector(`a[href="#${hash}_1"]`)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('handles invalid hash correctly', () => {
|
||||
const { container } = render(
|
||||
<Anchor>
|
||||
<Link href="notexsited" title="title" />
|
||||
</Anchor>,
|
||||
);
|
||||
|
||||
const link = container.querySelector(`a[href="notexsited"]`)!;
|
||||
fireEvent.click(link);
|
||||
expect(container.querySelector(`.ant-anchor-link-title-active`)?.textContent).toBe('title');
|
||||
});
|
||||
});
|
||||
|
||||
describe('warning', () => {
|
||||
let errSpy: jest.SpyInstance;
|
||||
beforeEach(() => {
|
||||
resetWarned();
|
||||
errSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
errSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('warning nested children when direction is horizontal ', () => {
|
||||
render(
|
||||
<Anchor
|
||||
direction="horizontal"
|
||||
items={[
|
||||
{
|
||||
key: '1',
|
||||
href: '#components-anchor-demo-basic',
|
||||
title: 'Item Basic Demo',
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
href: '#components-anchor-demo-static',
|
||||
title: 'Static demo',
|
||||
},
|
||||
{
|
||||
key: '3',
|
||||
href: '#api',
|
||||
title: 'API',
|
||||
children: [
|
||||
{
|
||||
key: '4',
|
||||
href: '#anchor-props',
|
||||
title: 'Anchor Props',
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
/>,
|
||||
);
|
||||
expect(errSpy).toHaveBeenCalledWith(
|
||||
'Warning: [antd: Anchor] `Anchor items#children` is not supported when `Anchor` direction is horizontal.',
|
||||
);
|
||||
});
|
||||
|
||||
it('deprecated jsx style', () => {
|
||||
render(
|
||||
<Anchor direction="horizontal">
|
||||
<Link href="#components-anchor-demo-basic" title="Basic demo" />
|
||||
<Link href="#components-anchor-demo-static" title="Static demo" />
|
||||
</Anchor>,
|
||||
);
|
||||
expect(errSpy).toHaveBeenCalledWith(
|
||||
'Warning: [antd: Anchor] `Anchor children` is deprecated. Please use `items` instead.',
|
||||
);
|
||||
});
|
||||
|
||||
it('deprecated jsx style for direction#vertical', () => {
|
||||
render(
|
||||
<Anchor>
|
||||
<Link href="#components-anchor-demo-basic" title="Basic demo" />
|
||||
<Link href="#components-anchor-demo-static" title="Static demo" />
|
||||
</Anchor>,
|
||||
);
|
||||
expect(errSpy).toHaveBeenCalledWith(
|
||||
'Warning: [antd: Anchor] `Anchor children` is deprecated. Please use `items` instead.',
|
||||
);
|
||||
});
|
||||
|
||||
it('deprecated jsx style for direction#vertical 1: with nested children', () => {
|
||||
render(
|
||||
<Anchor direction="horizontal">
|
||||
<Link href="#api" title="API">
|
||||
<Link href="#anchor-props" title="Anchor Props" />
|
||||
</Link>
|
||||
</Anchor>,
|
||||
);
|
||||
expect(errSpy).toHaveBeenCalledWith(
|
||||
'Warning: [antd: Anchor] `Anchor children` is deprecated. Please use `items` instead.',
|
||||
);
|
||||
expect(errSpy).toHaveBeenCalledWith(
|
||||
'Warning: [antd: Anchor.Link] `Anchor.Link children` is not supported when `Anchor` direction is horizontal',
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,5 +1,37 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Anchor Render render items and ignore jsx children 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class=""
|
||||
>
|
||||
<div
|
||||
class="ant-anchor-wrapper"
|
||||
style="max-height: 100vh;"
|
||||
>
|
||||
<div
|
||||
class="ant-anchor"
|
||||
>
|
||||
<span
|
||||
class="ant-anchor-ink"
|
||||
/>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#components-anchor-demo-basic"
|
||||
title="Item Basic Demo"
|
||||
>
|
||||
Item Basic Demo
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Anchor Render renders items correctly 1`] = `
|
||||
<div>
|
||||
<div
|
||||
@ -12,13 +44,9 @@ exports[`Anchor Render renders items correctly 1`] = `
|
||||
<div
|
||||
class="ant-anchor"
|
||||
>
|
||||
<div
|
||||
<span
|
||||
class="ant-anchor-ink"
|
||||
>
|
||||
<span
|
||||
class="ant-anchor-ink-ball"
|
||||
/>
|
||||
</div>
|
||||
/>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
@ -79,3 +107,57 @@ exports[`Anchor Render renders items correctly 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Anchor Render renders items correctly#horizontal 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class=""
|
||||
>
|
||||
<div
|
||||
class="ant-anchor-wrapper"
|
||||
style="max-height: 100vh;"
|
||||
>
|
||||
<div
|
||||
class="ant-anchor"
|
||||
>
|
||||
<span
|
||||
class="ant-anchor-ink"
|
||||
/>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#components-anchor-demo-basic"
|
||||
title="Item Basic Demo"
|
||||
>
|
||||
Item Basic Demo
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#components-anchor-demo-static"
|
||||
title="Static demo"
|
||||
>
|
||||
Static demo
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#api"
|
||||
title="API"
|
||||
>
|
||||
API
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
@ -34,13 +34,9 @@ exports[`renders ./components/anchor/demo/basic.tsx extend context correctly 1`]
|
||||
<div
|
||||
class="ant-anchor"
|
||||
>
|
||||
<div
|
||||
<span
|
||||
class="ant-anchor-ink"
|
||||
>
|
||||
<span
|
||||
class="ant-anchor-ink-ball"
|
||||
/>
|
||||
</div>
|
||||
/>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
@ -90,13 +86,9 @@ exports[`renders ./components/anchor/demo/customizeHighlight.tsx extend context
|
||||
<div
|
||||
class="ant-anchor ant-anchor-fixed"
|
||||
>
|
||||
<div
|
||||
<span
|
||||
class="ant-anchor-ink"
|
||||
>
|
||||
<span
|
||||
class="ant-anchor-ink-ball"
|
||||
/>
|
||||
</div>
|
||||
/>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
@ -156,82 +148,191 @@ exports[`renders ./components/anchor/demo/customizeHighlight.tsx extend context
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/anchor/demo/legacy-anchor.tsx extend context correctly 1`] = `
|
||||
<div>
|
||||
exports[`renders ./components/anchor/demo/horizontal.tsx extend context correctly 1`] = `
|
||||
Array [
|
||||
<div
|
||||
class=""
|
||||
style="padding:20px"
|
||||
>
|
||||
<div
|
||||
class="ant-anchor-wrapper"
|
||||
style="max-height:100vh"
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
class="ant-anchor"
|
||||
class=""
|
||||
>
|
||||
<div
|
||||
class="ant-anchor-ink"
|
||||
class="ant-anchor-wrapper ant-anchor-wrapper-horizontal"
|
||||
style="max-height:100vh"
|
||||
>
|
||||
<span
|
||||
class="ant-anchor-ink-ball"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#components-anchor-demo-basic"
|
||||
title="Basic demo"
|
||||
>
|
||||
Basic demo
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#components-anchor-demo-static"
|
||||
title="Static demo"
|
||||
>
|
||||
Static demo
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#api"
|
||||
title="API"
|
||||
>
|
||||
API
|
||||
</a>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
class="ant-anchor"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#anchor-props"
|
||||
title="Anchor Props"
|
||||
<span
|
||||
class="ant-anchor-ink"
|
||||
/>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
Anchor Props
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#link-props"
|
||||
title="Link Props"
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#part-1"
|
||||
title="Part 1"
|
||||
>
|
||||
Part 1
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
Link Props
|
||||
</a>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#part-2"
|
||||
title="Part 2"
|
||||
>
|
||||
Part 2
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#part-3"
|
||||
title="Part 3"
|
||||
>
|
||||
Part 3
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#part-4"
|
||||
title="Part 4"
|
||||
>
|
||||
Part 4
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#part-5"
|
||||
title="Part 5"
|
||||
>
|
||||
Part 5
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#part-6"
|
||||
title="Part 6"
|
||||
>
|
||||
Part 6
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
<div>
|
||||
<div
|
||||
id="part-1"
|
||||
style="width:100vw;height:100vh;text-align:center;background:rgba(0,255,0,0.02)"
|
||||
/>
|
||||
<div
|
||||
id="part-2"
|
||||
style="width:100vw;height:100vh;text-align:center;background:rgba(0,0,255,0.02)"
|
||||
/>
|
||||
<div
|
||||
id="part-3"
|
||||
style="width:100vw;height:100vh;text-align:center;background:#FFFBE9"
|
||||
/>
|
||||
<div
|
||||
id="part-4"
|
||||
style="width:100vw;height:100vh;text-align:center;background:#F4EAD5"
|
||||
/>
|
||||
<div
|
||||
id="part-5"
|
||||
style="width:100vw;height:100vh;text-align:center;background:#DAE2B6"
|
||||
/>
|
||||
<div
|
||||
id="part-6"
|
||||
style="width:100vw;height:100vh;text-align:center;background:#CCD6A6"
|
||||
/>
|
||||
</div>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders ./components/anchor/demo/legacy-anchor.tsx extend context correctly 1`] = `
|
||||
<div
|
||||
class="ant-anchor-wrapper"
|
||||
style="max-height:100vh"
|
||||
>
|
||||
<div
|
||||
class="ant-anchor ant-anchor-fixed"
|
||||
>
|
||||
<span
|
||||
class="ant-anchor-ink"
|
||||
/>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#components-anchor-demo-basic"
|
||||
title="Basic demo"
|
||||
>
|
||||
Basic demo
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#components-anchor-demo-static"
|
||||
title="Static demo"
|
||||
>
|
||||
Static demo
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#api"
|
||||
title="API"
|
||||
>
|
||||
API
|
||||
</a>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#anchor-props"
|
||||
title="Anchor Props"
|
||||
>
|
||||
Anchor Props
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#link-props"
|
||||
title="Link Props"
|
||||
>
|
||||
Link Props
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
@ -244,13 +345,9 @@ exports[`renders ./components/anchor/demo/onChange.tsx extend context correctly
|
||||
<div
|
||||
class="ant-anchor ant-anchor-fixed"
|
||||
>
|
||||
<div
|
||||
<span
|
||||
class="ant-anchor-ink"
|
||||
>
|
||||
<span
|
||||
class="ant-anchor-ink-ball"
|
||||
/>
|
||||
</div>
|
||||
/>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
@ -318,13 +415,9 @@ exports[`renders ./components/anchor/demo/onClick.tsx extend context correctly 1
|
||||
<div
|
||||
class="ant-anchor ant-anchor-fixed"
|
||||
>
|
||||
<div
|
||||
<span
|
||||
class="ant-anchor-ink"
|
||||
>
|
||||
<span
|
||||
class="ant-anchor-ink-ball"
|
||||
/>
|
||||
</div>
|
||||
/>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
@ -392,13 +485,9 @@ exports[`renders ./components/anchor/demo/static.tsx extend context correctly 1`
|
||||
<div
|
||||
class="ant-anchor ant-anchor-fixed"
|
||||
>
|
||||
<div
|
||||
<span
|
||||
class="ant-anchor-ink"
|
||||
>
|
||||
<span
|
||||
class="ant-anchor-ink-ball"
|
||||
/>
|
||||
</div>
|
||||
/>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
@ -499,13 +588,9 @@ exports[`renders ./components/anchor/demo/targetOffset.tsx extend context correc
|
||||
<div
|
||||
class="ant-anchor"
|
||||
>
|
||||
<div
|
||||
<span
|
||||
class="ant-anchor-ink"
|
||||
>
|
||||
<span
|
||||
class="ant-anchor-ink-ball"
|
||||
/>
|
||||
</div>
|
||||
/>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
|
@ -34,13 +34,9 @@ exports[`renders ./components/anchor/demo/basic.tsx correctly 1`] = `
|
||||
<div
|
||||
class="ant-anchor"
|
||||
>
|
||||
<div
|
||||
<span
|
||||
class="ant-anchor-ink"
|
||||
>
|
||||
<span
|
||||
class="ant-anchor-ink-ball"
|
||||
/>
|
||||
</div>
|
||||
/>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
@ -90,13 +86,9 @@ exports[`renders ./components/anchor/demo/customizeHighlight.tsx correctly 1`] =
|
||||
<div
|
||||
class="ant-anchor ant-anchor-fixed"
|
||||
>
|
||||
<div
|
||||
<span
|
||||
class="ant-anchor-ink"
|
||||
>
|
||||
<span
|
||||
class="ant-anchor-ink-ball"
|
||||
/>
|
||||
</div>
|
||||
/>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
@ -156,82 +148,191 @@ exports[`renders ./components/anchor/demo/customizeHighlight.tsx correctly 1`] =
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`renders ./components/anchor/demo/legacy-anchor.tsx correctly 1`] = `
|
||||
<div>
|
||||
exports[`renders ./components/anchor/demo/horizontal.tsx correctly 1`] = `
|
||||
Array [
|
||||
<div
|
||||
class=""
|
||||
style="padding:20px"
|
||||
>
|
||||
<div
|
||||
class="ant-anchor-wrapper"
|
||||
style="max-height:100vh"
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
class="ant-anchor"
|
||||
class=""
|
||||
>
|
||||
<div
|
||||
class="ant-anchor-ink"
|
||||
class="ant-anchor-wrapper ant-anchor-wrapper-horizontal"
|
||||
style="max-height:100vh"
|
||||
>
|
||||
<span
|
||||
class="ant-anchor-ink-ball"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#components-anchor-demo-basic"
|
||||
title="Basic demo"
|
||||
>
|
||||
Basic demo
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#components-anchor-demo-static"
|
||||
title="Static demo"
|
||||
>
|
||||
Static demo
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#api"
|
||||
title="API"
|
||||
>
|
||||
API
|
||||
</a>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
class="ant-anchor"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#anchor-props"
|
||||
title="Anchor Props"
|
||||
<span
|
||||
class="ant-anchor-ink"
|
||||
/>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
Anchor Props
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#link-props"
|
||||
title="Link Props"
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#part-1"
|
||||
title="Part 1"
|
||||
>
|
||||
Part 1
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
Link Props
|
||||
</a>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#part-2"
|
||||
title="Part 2"
|
||||
>
|
||||
Part 2
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#part-3"
|
||||
title="Part 3"
|
||||
>
|
||||
Part 3
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#part-4"
|
||||
title="Part 4"
|
||||
>
|
||||
Part 4
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#part-5"
|
||||
title="Part 5"
|
||||
>
|
||||
Part 5
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#part-6"
|
||||
title="Part 6"
|
||||
>
|
||||
Part 6
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
<div>
|
||||
<div
|
||||
id="part-1"
|
||||
style="width:100vw;height:100vh;text-align:center;background:rgba(0,255,0,0.02)"
|
||||
/>
|
||||
<div
|
||||
id="part-2"
|
||||
style="width:100vw;height:100vh;text-align:center;background:rgba(0,0,255,0.02)"
|
||||
/>
|
||||
<div
|
||||
id="part-3"
|
||||
style="width:100vw;height:100vh;text-align:center;background:#FFFBE9"
|
||||
/>
|
||||
<div
|
||||
id="part-4"
|
||||
style="width:100vw;height:100vh;text-align:center;background:#F4EAD5"
|
||||
/>
|
||||
<div
|
||||
id="part-5"
|
||||
style="width:100vw;height:100vh;text-align:center;background:#DAE2B6"
|
||||
/>
|
||||
<div
|
||||
id="part-6"
|
||||
style="width:100vw;height:100vh;text-align:center;background:#CCD6A6"
|
||||
/>
|
||||
</div>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`renders ./components/anchor/demo/legacy-anchor.tsx correctly 1`] = `
|
||||
<div
|
||||
class="ant-anchor-wrapper"
|
||||
style="max-height:100vh"
|
||||
>
|
||||
<div
|
||||
class="ant-anchor ant-anchor-fixed"
|
||||
>
|
||||
<span
|
||||
class="ant-anchor-ink"
|
||||
/>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#components-anchor-demo-basic"
|
||||
title="Basic demo"
|
||||
>
|
||||
Basic demo
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#components-anchor-demo-static"
|
||||
title="Static demo"
|
||||
>
|
||||
Static demo
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#api"
|
||||
title="API"
|
||||
>
|
||||
API
|
||||
</a>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#anchor-props"
|
||||
title="Anchor Props"
|
||||
>
|
||||
Anchor Props
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
<a
|
||||
class="ant-anchor-link-title"
|
||||
href="#link-props"
|
||||
title="Link Props"
|
||||
>
|
||||
Link Props
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
@ -244,13 +345,9 @@ exports[`renders ./components/anchor/demo/onChange.tsx correctly 1`] = `
|
||||
<div
|
||||
class="ant-anchor ant-anchor-fixed"
|
||||
>
|
||||
<div
|
||||
<span
|
||||
class="ant-anchor-ink"
|
||||
>
|
||||
<span
|
||||
class="ant-anchor-ink-ball"
|
||||
/>
|
||||
</div>
|
||||
/>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
@ -318,13 +415,9 @@ exports[`renders ./components/anchor/demo/onClick.tsx correctly 1`] = `
|
||||
<div
|
||||
class="ant-anchor ant-anchor-fixed"
|
||||
>
|
||||
<div
|
||||
<span
|
||||
class="ant-anchor-ink"
|
||||
>
|
||||
<span
|
||||
class="ant-anchor-ink-ball"
|
||||
/>
|
||||
</div>
|
||||
/>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
@ -392,13 +485,9 @@ exports[`renders ./components/anchor/demo/static.tsx correctly 1`] = `
|
||||
<div
|
||||
class="ant-anchor ant-anchor-fixed"
|
||||
>
|
||||
<div
|
||||
<span
|
||||
class="ant-anchor-ink"
|
||||
>
|
||||
<span
|
||||
class="ant-anchor-ink-ball"
|
||||
/>
|
||||
</div>
|
||||
/>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
@ -499,13 +588,9 @@ exports[`renders ./components/anchor/demo/targetOffset.tsx correctly 1`] = `
|
||||
<div
|
||||
class="ant-anchor"
|
||||
>
|
||||
<div
|
||||
<span
|
||||
class="ant-anchor-ink"
|
||||
>
|
||||
<span
|
||||
class="ant-anchor-ink-ball"
|
||||
/>
|
||||
</div>
|
||||
/>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
|
7
components/anchor/demo/horizontal.md
Normal file
7
components/anchor/demo/horizontal.md
Normal file
@ -0,0 +1,7 @@
|
||||
## zh-CN
|
||||
|
||||
横向 Anchor。
|
||||
|
||||
## en-US
|
||||
|
||||
Horizontally aligned anchors
|
82
components/anchor/demo/horizontal.tsx
Normal file
82
components/anchor/demo/horizontal.tsx
Normal file
@ -0,0 +1,82 @@
|
||||
import React from 'react';
|
||||
import { Anchor } from 'antd';
|
||||
|
||||
const App: React.FC = () => (
|
||||
<>
|
||||
<div style={{ padding: '20px' }}>
|
||||
<Anchor
|
||||
direction="horizontal"
|
||||
items={[
|
||||
{
|
||||
key: 'part-1',
|
||||
href: '#part-1',
|
||||
title: 'Part 1',
|
||||
},
|
||||
{
|
||||
key: 'part-2',
|
||||
href: '#part-2',
|
||||
title: 'Part 2',
|
||||
},
|
||||
{
|
||||
key: 'part-3',
|
||||
href: '#part-3',
|
||||
title: 'Part 3',
|
||||
},
|
||||
{
|
||||
key: 'part-4',
|
||||
href: '#part-4',
|
||||
title: 'Part 4',
|
||||
},
|
||||
{
|
||||
key: 'part-5',
|
||||
href: '#part-5',
|
||||
title: 'Part 5',
|
||||
},
|
||||
{
|
||||
key: 'part-6',
|
||||
href: '#part-6',
|
||||
title: 'Part 6',
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
id="part-1"
|
||||
style={{
|
||||
width: '100vw',
|
||||
height: '100vh',
|
||||
textAlign: 'center',
|
||||
background: 'rgba(0,255,0,0.02)',
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
id="part-2"
|
||||
style={{
|
||||
width: '100vw',
|
||||
height: '100vh',
|
||||
textAlign: 'center',
|
||||
background: 'rgba(0,0,255,0.02)',
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
id="part-3"
|
||||
style={{ width: '100vw', height: '100vh', textAlign: 'center', background: '#FFFBE9' }}
|
||||
/>
|
||||
<div
|
||||
id="part-4"
|
||||
style={{ width: '100vw', height: '100vh', textAlign: 'center', background: '#F4EAD5' }}
|
||||
/>
|
||||
<div
|
||||
id="part-5"
|
||||
style={{ width: '100vw', height: '100vh', textAlign: 'center', background: '#DAE2B6' }}
|
||||
/>
|
||||
<div
|
||||
id="part-6"
|
||||
style={{ width: '100vw', height: '100vh', textAlign: 'center', background: '#CCD6A6' }}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
export default App;
|
@ -4,7 +4,7 @@ import { Anchor } from 'antd';
|
||||
const { Link } = Anchor;
|
||||
|
||||
const App: React.FC = () => (
|
||||
<Anchor>
|
||||
<Anchor affix={false}>
|
||||
<Link href="#components-anchor-demo-basic" title="Basic demo" />
|
||||
<Link href="#components-anchor-demo-static" title="Static demo" />
|
||||
<Link href="#api" title="API">
|
||||
|
@ -3,7 +3,6 @@ category: Components
|
||||
title: Anchor
|
||||
cover: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*TBTSR4PyVmkAAAAAAAAAAAAADrJ8AQ/original
|
||||
demo:
|
||||
cols: 2
|
||||
group:
|
||||
title: Navigation
|
||||
order: 3
|
||||
@ -23,7 +22,8 @@ For displaying anchor hyperlinks on page and jumping between them.
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
<code src="./demo/basic.tsx" iframe="200">Basic</code>
|
||||
<code src="./demo/static.tsx">Static Anchor</code>
|
||||
<code src="./demo/horizontal.tsx" iframe="200">Horizontal Anchor</code>
|
||||
<code src="./demo/static.tsx" >Static Anchor</code>
|
||||
<code src="./demo/onClick.tsx">Customize the onClick event</code>
|
||||
<code src="./demo/customizeHighlight.tsx">Customize the anchor highlight</code>
|
||||
<code src="./demo/targetOffset.tsx" iframe="200">Set Anchor scroll offset</code>
|
||||
@ -45,10 +45,23 @@ For displaying anchor hyperlinks on page and jumping between them.
|
||||
| targetOffset | Anchor scroll offset, default as `offsetTop`, [example](#components-anchor-demo-targetoffset) | number | - | |
|
||||
| onChange | Listening for anchor link change | (currentActiveLink: string) => void | | |
|
||||
| onClick | Set the handler to handle `click` event | (e: MouseEvent, link: object) => void | - | |
|
||||
| items | Data configuration option content, support nesting through children | { href, title, target, children }\[] | - | |
|
||||
| items | Data configuration option content, support nesting through children | { key, href, title, target, children }\[] [see](#anchoritem) | - | 5.1.0 |
|
||||
| direction | Set Anchor direction | `vertical` \| `horizontal` | `vertical` | 5.2.0 |
|
||||
|
||||
### AnchorItem
|
||||
|
||||
| Property | Description | Type | Default | Version |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| key | The unique identifier of the Anchor Link | string \| number | - | |
|
||||
| href | The target of hyperlink | string | | |
|
||||
| target | Specifies where to display the linked URL | string | | |
|
||||
| title | The content of hyperlink | ReactNode | | |
|
||||
| children | Nested Anchor Link, `Attention: This attribute does not support horizontal orientation` | [AnchorItem](#anchoritem)\[] | - | |
|
||||
|
||||
### Link Props
|
||||
|
||||
We recommend using the items form instead.
|
||||
|
||||
| Property | Description | Type | Default | Version |
|
||||
| -------- | ----------------------------------------- | --------- | ------- | ------- |
|
||||
| href | The target of hyperlink | string | | |
|
||||
|
@ -4,7 +4,6 @@ title: Anchor
|
||||
subtitle: 锚点
|
||||
cover: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*TBTSR4PyVmkAAAAAAAAAAAAADrJ8AQ/original
|
||||
demo:
|
||||
cols: 2
|
||||
group:
|
||||
title: 导航
|
||||
order: 3
|
||||
@ -24,6 +23,7 @@ group:
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
<code src="./demo/basic.tsx" iframe="200">基本</code>
|
||||
<code src="./demo/horizontal.tsx" iframe="200">横向 Anchor</code>
|
||||
<code src="./demo/static.tsx">静态位置</code>
|
||||
<code src="./demo/onClick.tsx">自定义 onClick 事件</code>
|
||||
<code src="./demo/customizeHighlight.tsx">自定义锚点高亮</code>
|
||||
@ -46,12 +46,25 @@ group:
|
||||
| targetOffset | 锚点滚动偏移量,默认与 offsetTop 相同,[例子](#components-anchor-demo-targetoffset) | number | - | |
|
||||
| onChange | 监听锚点链接改变 | (currentActiveLink: string) => void | - | |
|
||||
| onClick | `click` 事件的 handler | (e: MouseEvent, link: object) => void | - | |
|
||||
| items | 数据化配置选项内容,支持通过 children 嵌套 | { href, title, target, children }\[] | - | |
|
||||
| items | 数据化配置选项内容,支持通过 children 嵌套 | { key, href, title, target, children }\[] [具体见](#anchoritem) | - | 5.1.0 |
|
||||
| direction | 设置导航方向 | `vertical` \| `horizontal` | `vertical` | 5.2.0 |
|
||||
|
||||
### AnchorItem
|
||||
|
||||
| 成员 | 说明 | 类型 | 默认值 | 版本 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| key | 唯一标志 | string \| number | - | |
|
||||
| href | 锚点链接 | string | - | |
|
||||
| target | 该属性指定在何处显示链接的资源 | string | - | |
|
||||
| title | 文字内容 | ReactNode | - | |
|
||||
| children | 嵌套的 Anchor Link,`注意:水平方向该属性不支持` | [AnchorItem](#anchoritem)\[] | - | |
|
||||
|
||||
### Link Props
|
||||
|
||||
| 成员 | 说明 | 类型 | 默认值 | 版本 |
|
||||
| ------ | -------------------------------- | --------- | ------ | ---- |
|
||||
| href | 锚点链接 | string | - | |
|
||||
| target | 该属性指定在何处显示链接的资源。 | string | - | |
|
||||
| title | 文字内容 | ReactNode | - | |
|
||||
建议使用 items 形式。
|
||||
|
||||
| 成员 | 说明 | 类型 | 默认值 | 版本 |
|
||||
| ------ | ------------------------------ | --------- | ------ | ---- |
|
||||
| href | 锚点链接 | string | - | |
|
||||
| target | 该属性指定在何处显示链接的资源 | string | - | |
|
||||
| title | 文字内容 | ReactNode | - | |
|
||||
|
@ -16,8 +16,15 @@ interface AnchorToken extends FullToken<'Anchor'> {
|
||||
|
||||
// ============================== Shared ==============================
|
||||
const genSharedAnchorStyle: GenerateStyle<AnchorToken> = (token): CSSObject => {
|
||||
const { componentCls, holderOffsetBlock, motionDurationSlow, lineWidthBold, colorPrimary } =
|
||||
token;
|
||||
const {
|
||||
componentCls,
|
||||
holderOffsetBlock,
|
||||
motionDurationSlow,
|
||||
lineWidthBold,
|
||||
colorPrimary,
|
||||
lineType,
|
||||
colorSplit,
|
||||
} = token;
|
||||
|
||||
return {
|
||||
[`${componentCls}-wrapper`]: {
|
||||
@ -34,40 +41,6 @@ const genSharedAnchorStyle: GenerateStyle<AnchorToken> = (token): CSSObject => {
|
||||
position: 'relative',
|
||||
paddingInlineStart: lineWidthBold,
|
||||
|
||||
[`${componentCls}-ink`]: {
|
||||
position: 'absolute',
|
||||
insetBlockStart: 0,
|
||||
insetInlineStart: 0,
|
||||
height: '100%',
|
||||
|
||||
'&::before': {
|
||||
position: 'relative',
|
||||
display: 'block',
|
||||
width: lineWidthBold,
|
||||
height: '100%',
|
||||
margin: '0 auto',
|
||||
backgroundColor: token.colorSplit,
|
||||
content: '" "',
|
||||
},
|
||||
},
|
||||
|
||||
[`${componentCls}-ink-ball`]: {
|
||||
position: 'absolute',
|
||||
left: {
|
||||
_skip_check_: true,
|
||||
value: 0,
|
||||
},
|
||||
display: 'none',
|
||||
transform: 'translateY(-50%)',
|
||||
transition: `top ${motionDurationSlow} ease-in-out`,
|
||||
width: lineWidthBold,
|
||||
backgroundColor: colorPrimary,
|
||||
|
||||
[`&${componentCls}-ink-ball-visible`]: {
|
||||
display: 'inline-block',
|
||||
},
|
||||
},
|
||||
|
||||
[`${componentCls}-link`]: {
|
||||
paddingBlock: token.anchorPaddingBlock,
|
||||
paddingInline: `${token.anchorPaddingInline}px 0`,
|
||||
@ -96,13 +69,94 @@ const genSharedAnchorStyle: GenerateStyle<AnchorToken> = (token): CSSObject => {
|
||||
},
|
||||
},
|
||||
|
||||
[`${componentCls}-fixed ${componentCls}-ink ${componentCls}-ink-ball`]: {
|
||||
[`&:not(&-horizontal)`]: {
|
||||
[componentCls]: {
|
||||
'&::before': {
|
||||
position: 'absolute',
|
||||
left: {
|
||||
_skip_check_: true,
|
||||
value: 0,
|
||||
},
|
||||
top: 0,
|
||||
height: '100%',
|
||||
borderInlineStart: `${lineWidthBold}px ${lineType} ${colorSplit}`,
|
||||
content: '" "',
|
||||
},
|
||||
|
||||
[`${componentCls}-ink`]: {
|
||||
position: 'absolute',
|
||||
left: {
|
||||
_skip_check_: true,
|
||||
value: 0,
|
||||
},
|
||||
display: 'none',
|
||||
transform: 'translateY(-50%)',
|
||||
transition: `top ${motionDurationSlow} ease-in-out`,
|
||||
width: lineWidthBold,
|
||||
backgroundColor: colorPrimary,
|
||||
|
||||
[`&${componentCls}-ink-visible`]: {
|
||||
display: 'inline-block',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
[`${componentCls}-fixed ${componentCls}-ink ${componentCls}-ink`]: {
|
||||
display: 'none',
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const genSharedAnchorHorizontalStyle: GenerateStyle<AnchorToken> = (token): CSSObject => {
|
||||
const { componentCls, motionDurationSlow, lineWidthBold, colorPrimary } = token;
|
||||
|
||||
return {
|
||||
[`${componentCls}-wrapper-horizontal`]: {
|
||||
position: 'relative',
|
||||
|
||||
'&::before': {
|
||||
position: 'absolute',
|
||||
left: {
|
||||
_skip_check_: true,
|
||||
value: 0,
|
||||
},
|
||||
right: {
|
||||
_skip_check_: true,
|
||||
value: 0,
|
||||
},
|
||||
bottom: 0,
|
||||
borderBottom: `1px ${token.lineType} ${token.colorSplit}`,
|
||||
content: '" "',
|
||||
},
|
||||
|
||||
[componentCls]: {
|
||||
overflowX: 'scroll',
|
||||
position: 'relative',
|
||||
display: 'flex',
|
||||
scrollbarWidth: 'none' /* Firefox */,
|
||||
|
||||
'&::-webkit-scrollbar': {
|
||||
display: 'none' /* Safari and Chrome */,
|
||||
},
|
||||
|
||||
[`${componentCls}-link:first-of-type`]: {
|
||||
paddingInline: 0,
|
||||
},
|
||||
|
||||
[`${componentCls}-ink`]: {
|
||||
position: 'absolute',
|
||||
bottom: 0,
|
||||
transition: `left ${motionDurationSlow} ease-in-out, width ${motionDurationSlow} ease-in-out`,
|
||||
height: lineWidthBold,
|
||||
backgroundColor: colorPrimary,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
// ============================== Export ==============================
|
||||
export default genComponentStyleHook('Anchor', (token) => {
|
||||
const { fontSize, fontSizeLG, padding, paddingXXS } = token;
|
||||
@ -115,5 +169,5 @@ export default genComponentStyleHook('Anchor', (token) => {
|
||||
anchorTitleBlock: (fontSize / 14) * 3,
|
||||
anchorBallSize: fontSizeLG / 2,
|
||||
});
|
||||
return [genSharedAnchorStyle(anchorToken)];
|
||||
return [genSharedAnchorStyle(anchorToken), genSharedAnchorHorizontalStyle(anchorToken)];
|
||||
});
|
||||
|
@ -138,13 +138,9 @@ exports[`ConfigProvider components Anchor configProvider 1`] = `
|
||||
<div
|
||||
class="config-anchor"
|
||||
>
|
||||
<div
|
||||
<span
|
||||
class="config-anchor-ink"
|
||||
>
|
||||
<span
|
||||
class="config-anchor-ink-ball"
|
||||
/>
|
||||
</div>
|
||||
/>
|
||||
<div
|
||||
class="config-anchor-link"
|
||||
>
|
||||
@ -174,13 +170,9 @@ exports[`ConfigProvider components Anchor configProvider componentDisabled 1`] =
|
||||
<div
|
||||
class="config-anchor"
|
||||
>
|
||||
<div
|
||||
<span
|
||||
class="config-anchor-ink"
|
||||
>
|
||||
<span
|
||||
class="config-anchor-ink-ball"
|
||||
/>
|
||||
</div>
|
||||
/>
|
||||
<div
|
||||
class="config-anchor-link"
|
||||
>
|
||||
@ -210,13 +202,9 @@ exports[`ConfigProvider components Anchor configProvider componentSize large 1`]
|
||||
<div
|
||||
class="config-anchor"
|
||||
>
|
||||
<div
|
||||
<span
|
||||
class="config-anchor-ink"
|
||||
>
|
||||
<span
|
||||
class="config-anchor-ink-ball"
|
||||
/>
|
||||
</div>
|
||||
/>
|
||||
<div
|
||||
class="config-anchor-link"
|
||||
>
|
||||
@ -246,13 +234,9 @@ exports[`ConfigProvider components Anchor configProvider componentSize middle 1`
|
||||
<div
|
||||
class="config-anchor"
|
||||
>
|
||||
<div
|
||||
<span
|
||||
class="config-anchor-ink"
|
||||
>
|
||||
<span
|
||||
class="config-anchor-ink-ball"
|
||||
/>
|
||||
</div>
|
||||
/>
|
||||
<div
|
||||
class="config-anchor-link"
|
||||
>
|
||||
@ -282,13 +266,9 @@ exports[`ConfigProvider components Anchor configProvider virtual and dropdownMat
|
||||
<div
|
||||
class="ant-anchor"
|
||||
>
|
||||
<div
|
||||
<span
|
||||
class="ant-anchor-ink"
|
||||
>
|
||||
<span
|
||||
class="ant-anchor-ink-ball"
|
||||
/>
|
||||
</div>
|
||||
/>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
@ -318,13 +298,9 @@ exports[`ConfigProvider components Anchor normal 1`] = `
|
||||
<div
|
||||
class="ant-anchor"
|
||||
>
|
||||
<div
|
||||
<span
|
||||
class="ant-anchor-ink"
|
||||
>
|
||||
<span
|
||||
class="ant-anchor-ink-ball"
|
||||
/>
|
||||
</div>
|
||||
/>
|
||||
<div
|
||||
class="ant-anchor-link"
|
||||
>
|
||||
@ -354,13 +330,9 @@ exports[`ConfigProvider components Anchor prefixCls 1`] = `
|
||||
<div
|
||||
class="prefix-Anchor"
|
||||
>
|
||||
<div
|
||||
<span
|
||||
class="prefix-Anchor-ink"
|
||||
>
|
||||
<span
|
||||
class="prefix-Anchor-ink-ball"
|
||||
/>
|
||||
</div>
|
||||
/>
|
||||
<div
|
||||
class="prefix-Anchor-link"
|
||||
>
|
||||
|
Loading…
Reference in New Issue
Block a user