From 6c735fee67446de7c8a9959e1322a514e0d2ec24 Mon Sep 17 00:00:00 2001 From: tangjinzhou <415800467@qq.com> Date: Wed, 25 Jan 2023 17:23:03 +0800 Subject: [PATCH] refactor: anchor & add items --- components/anchor/Anchor.tsx | 59 +++++++---- components/anchor/index.en-US.md | 5 +- components/anchor/index.zh-CN.md | 11 ++- components/anchor/style/index.less | 86 ---------------- components/anchor/style/index.ts | 119 +++++++++++++++++++++++ components/anchor/style/index.tsx | 5 - components/anchor/style/rtl.less | 35 ------- components/style.ts | 2 +- components/theme/interface/components.ts | 4 +- 9 files changed, 172 insertions(+), 154 deletions(-) delete mode 100644 components/anchor/style/index.less create mode 100644 components/anchor/style/index.ts delete mode 100644 components/anchor/style/index.tsx delete mode 100644 components/anchor/style/rtl.less diff --git a/components/anchor/Anchor.tsx b/components/anchor/Anchor.tsx index 941b8ecfe..ca637e3e4 100644 --- a/components/anchor/Anchor.tsx +++ b/components/anchor/Anchor.tsx @@ -16,7 +16,17 @@ import scrollTo from '../_util/scrollTo'; import getScroll from '../_util/getScroll'; import useConfigInject from '../_util/hooks/useConfigInject'; import useProvideAnchor from './context'; +import useStyle from './style'; +import type { AnchorLinkProps } from './AnchorLink'; +import AnchorLink from './AnchorLink'; +import type { Key } from '../_util/type'; +export interface AnchorLinkItemProps extends AnchorLinkProps { + key: Key; + class?: String; + style?: CSSProperties; + children?: AnchorLinkItemProps[]; +} function getDefaultContainer() { return window; } @@ -41,10 +51,10 @@ function getOffsetTop(element: HTMLElement, container: AnchorContainer): number const sharpMatcherRegx = /#([\S ]+)$/; -type Section = { +interface Section { link: string; top: number; -}; +} export type AnchorContainer = HTMLElement | Window; @@ -59,6 +69,10 @@ export const anchorProps = () => ({ wrapperStyle: { type: Object as PropType, default: undefined as CSSProperties }, getCurrentAnchor: Function as PropType<(activeLink: string) => string>, targetOffset: Number, + items: { + type: Array as PropType, + default: undefined as AnchorLinkItemProps[], + }, onChange: Function as PropType<(currentActiveLink: string) => void>, onClick: Function as PropType<(e: MouseEvent, link: { title: any; href: string }) => void>, }); @@ -79,7 +93,7 @@ export default defineComponent({ props: anchorProps(), setup(props, { emit, attrs, slots, expose }) { const { prefixCls, getTargetContainer, direction } = useConfigInject('anchor', props); - const inkNodeRef = ref(); + const spanLinkNode = ref(null); const anchorRef = ref(); const state = reactive({ links: [], @@ -173,10 +187,9 @@ export default defineComponent({ const linkNode = anchorRef.value.getElementsByClassName( `${prefixCls.value}-link-title-active`, )[0]; - if (linkNode) { - (inkNodeRef.value as HTMLElement).style.top = `${ - linkNode.offsetTop + linkNode.clientHeight / 2 - 4.5 - }px`; + if (linkNode && spanLinkNode.value) { + spanLinkNode.value.style.top = `${linkNode.offsetTop + linkNode.clientHeight / 2}px`; + spanLinkNode.value.style.height = `${linkNode.clientHeight}px`; } }; @@ -224,15 +237,23 @@ export default defineComponent({ } updateInk(); }); - + const createNestedLink = (options?: AnchorLinkItemProps[]) => + Array.isArray(options) + ? options.map(item => ( + + {createNestedLink(item.children)} + + )) + : null; + const [wrapSSR, hashId] = useStyle(prefixCls); return () => { const { offsetTop, affix, showInkInFixed } = props; const pre = prefixCls.value; const inkClass = classNames(`${pre}-ink-ball`, { - visible: activeLink.value, + [`${pre}-ink-ball-visible`]: activeLink.value, }); - const wrapperClass = classNames(props.wrapperClass, `${pre}-wrapper`, { + const wrapperClass = classNames(hashId.value, props.wrapperClass, `${pre}-wrapper`, { [`${pre}-rtl`]: direction.value === 'rtl', }); @@ -248,19 +269,21 @@ export default defineComponent({
- +
- {slots.default?.()} + {Array.isArray(props.items) ? createNestedLink(props.items) : slots.default?.()}
); - return !affix ? ( - anchorContent - ) : ( - - {anchorContent} - + return wrapSSR( + !affix ? ( + anchorContent + ) : ( + + {anchorContent} + + ), ); }; }, diff --git a/components/anchor/index.en-US.md b/components/anchor/index.en-US.md index bbe7aa61d..253a551e1 100644 --- a/components/anchor/index.en-US.md +++ b/components/anchor/index.en-US.md @@ -24,17 +24,18 @@ For displaying anchor hyperlinks on page and jumping between them. | getCurrentAnchor | Customize the anchor highlight | (activeLink: string) => string | - | activeLink(3.3) | | offsetBottom | Pixels to offset from bottom when calculating position of scroll | number | - | | | offsetTop | Pixels to offset from top when calculating position of scroll | number | 0 | | -| showInkInFixed | Whether show ink-balls when `:affix="false"` | boolean | false | | +| showInkInFixed | Whether show ink-square when `:affix="false"` | boolean | false | | | targetOffset | Anchor scroll offset, default as `offsetTop`, [example](#components-anchor-demo-targetOffset) | number | `offsetTop` | 1.5.0 | | wrapperClass | The class name of the container | string | - | | | wrapperStyle | The style of the container | object | - | | +| items | Data configuration option content, support nesting through children | { href, title, target, children, key }\[] | - | 4.0 | ### Events | Events Name | Description | Arguments | Version | | | --- | --- | --- | --- | --- | | change | Listening for anchor link change | (currentActiveLink: string) => void | | 1.5.0 | -| click | set the handler to handle `click` event | Function(e: Event, link: Object) | | | +| click | set the handler to handle `click` event | Function(e: MouseEvent, link: Object) | | | ### Link Props diff --git a/components/anchor/index.zh-CN.md b/components/anchor/index.zh-CN.md index 097e4fd9f..a5ef9e128 100644 --- a/components/anchor/index.zh-CN.md +++ b/components/anchor/index.zh-CN.md @@ -25,17 +25,18 @@ cover: https://gw.alipayobjects.com/zos/alicdn/_1-C1JwsC/Anchor.svg | getCurrentAnchor | 自定义高亮的锚点 | (activeLink: string) => string | - | activeLink(3.3) | | offsetBottom | 距离窗口底部达到指定偏移量后触发 | number | | | | offsetTop | 距离窗口顶部达到指定偏移量后触发 | number | | | -| showInkInFixed | `:affix="false"` 时是否显示小圆点 | boolean | false | | +| showInkInFixed | `:affix="false"` 时是否显示小方块 | boolean | false | | | targetOffset | 锚点滚动偏移量,默认与 offsetTop 相同,[例子](#components-anchor-demo-targetOffset) | number | `offsetTop` | 1.5.0 | | wrapperClass | 容器的类名 | string | - | | | wrapperStyle | 容器样式 | object | - | | +| items | 数据化配置选项内容,支持通过 children 嵌套 | { href, title, target, children, key }\[] | - | 4.0 | ### 事件 -| 事件名称 | 说明 | 回调参数 | 版本 | | -| -------- | ---------------------- | ----------------------------------- | ---- | ----- | -| change | 监听锚点链接改变 | (currentActiveLink: string) => void | | 1.5.0 | -| click | `click` 事件的 handler | Function(e: Event, link: Object) | | | +| 事件名称 | 说明 | 回调参数 | 版本 | | +| -------- | ---------------------- | ------------------------------------- | ---- | ----- | +| change | 监听锚点链接改变 | (currentActiveLink: string) => void | | 1.5.0 | +| click | `click` 事件的 handler | Function(e: MouseEvent, link: Object) | | | ### Link Props diff --git a/components/anchor/style/index.less b/components/anchor/style/index.less deleted file mode 100644 index 92d0042b2..000000000 --- a/components/anchor/style/index.less +++ /dev/null @@ -1,86 +0,0 @@ -@import '../../style/themes/index'; -@import '../../style/mixins/index'; - -@anchor-border-width: 2px; - -.@{ant-prefix}-anchor { - .reset-component(); - - position: relative; - padding-left: @anchor-border-width; - - &-wrapper { - margin-left: -4px; - padding-left: 4px; - overflow: auto; - background-color: @anchor-bg; - } - - &-ink { - position: absolute; - top: 0; - left: 0; - height: 100%; - - &::before { - position: relative; - display: block; - width: @anchor-border-width; - height: 100%; - margin: 0 auto; - background-color: @anchor-border-color; - content: ' '; - } - - &-ball { - position: absolute; - left: 50%; - display: none; - width: 8px; - height: 8px; - background-color: @component-background; - border: 2px solid @primary-color; - border-radius: 8px; - transform: translateX(-50%); - transition: top 0.3s ease-in-out; - - &.visible { - display: inline-block; - } - } - } - - &-fixed &-ink &-ink-ball { - display: none; - } - - &-link { - padding: @anchor-link-padding; - - &-title { - position: relative; - display: block; - margin-bottom: 3px; - overflow: hidden; - color: @text-color; - white-space: nowrap; - text-overflow: ellipsis; - transition: all 0.3s; - - &:only-child { - margin-bottom: 0; - } - } - - &-active > &-title { - color: @primary-color; - } - } - - &-link &-link { - padding-top: 2px; - padding-bottom: 2px; - } -} - -@import './rtl'; diff --git a/components/anchor/style/index.ts b/components/anchor/style/index.ts new file mode 100644 index 000000000..45357a012 --- /dev/null +++ b/components/anchor/style/index.ts @@ -0,0 +1,119 @@ +import type { CSSObject } from '../../_util/cssinjs'; +import type { FullToken, GenerateStyle } from '../../theme/internal'; +import { genComponentStyleHook, mergeToken } from '../../theme/internal'; +import { resetComponent, textEllipsis } from '../../_style'; + +export interface ComponentToken {} + +interface AnchorToken extends FullToken<'Anchor'> { + holderOffsetBlock: number; + anchorPaddingBlock: number; + anchorPaddingBlockSecondary: number; + anchorPaddingInline: number; + anchorBallSize: number; + anchorTitleBlock: number; +} + +// ============================== Shared ============================== +const genSharedAnchorStyle: GenerateStyle = (token): CSSObject => { + const { componentCls, holderOffsetBlock, motionDurationSlow, lineWidthBold, colorPrimary } = + token; + + return { + [`${componentCls}-wrapper`]: { + marginBlockStart: -holderOffsetBlock, + paddingBlockStart: holderOffsetBlock, + + // delete overflow: auto + // overflow: 'auto', + + backgroundColor: 'transparent', + + [componentCls]: { + ...resetComponent(token), + 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`, + + '&-title': { + ...textEllipsis, + position: 'relative', + display: 'block', + marginBlockEnd: token.anchorTitleBlock, + color: token.colorText, + transition: `all ${token.motionDurationSlow}`, + + '&:only-child': { + marginBlockEnd: 0, + }, + }, + + [`&-active > ${componentCls}-link-title`]: { + color: token.colorPrimary, + }, + + // link link + [`${componentCls}-link`]: { + paddingBlock: token.anchorPaddingBlockSecondary, + }, + }, + }, + + [`${componentCls}-fixed ${componentCls}-ink ${componentCls}-ink-ball`]: { + display: 'none', + }, + }, + }; +}; + +// ============================== Export ============================== +export default genComponentStyleHook('Anchor', token => { + const { fontSize, fontSizeLG, padding, paddingXXS } = token; + + const anchorToken = mergeToken(token, { + holderOffsetBlock: paddingXXS, + anchorPaddingBlock: paddingXXS, + anchorPaddingBlockSecondary: paddingXXS / 2, + anchorPaddingInline: padding, + anchorTitleBlock: (fontSize / 14) * 3, + anchorBallSize: fontSizeLG / 2, + }); + return [genSharedAnchorStyle(anchorToken)]; +}); diff --git a/components/anchor/style/index.tsx b/components/anchor/style/index.tsx deleted file mode 100644 index ff41dcf70..000000000 --- a/components/anchor/style/index.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import '../../style/index.less'; -import './index.less'; - -// style dependencies -import '../../affix/style'; diff --git a/components/anchor/style/rtl.less b/components/anchor/style/rtl.less deleted file mode 100644 index f1774d51a..000000000 --- a/components/anchor/style/rtl.less +++ /dev/null @@ -1,35 +0,0 @@ -.@{ant-prefix}-anchor { - &-rtl { - direction: rtl; - } - - &-wrapper { - .@{ant-prefix}-anchor-rtl& { - margin-right: -4px; - margin-left: 0; - padding-right: 4px; - padding-left: 0; - } - } - - &-ink { - .@{ant-prefix}-anchor-rtl & { - right: 0; - left: auto; - } - - &-ball { - .@{ant-prefix}-anchor-rtl & { - right: 50%; - left: 0; - transform: translateX(50%); - } - } - } - - &-link { - .@{ant-prefix}-anchor-rtl & { - padding: @anchor-link-top @anchor-link-left @anchor-link-top 0; - } - } -} diff --git a/components/style.ts b/components/style.ts index 3c162fc93..ce1d3f154 100644 --- a/components/style.ts +++ b/components/style.ts @@ -45,7 +45,7 @@ import './transfer/style'; import './tree/style'; import './upload/style'; import './layout/style'; -import './anchor/style'; +// import './anchor/style'; import './list/style'; import './tree-select/style'; import './drawer/style'; diff --git a/components/theme/interface/components.ts b/components/theme/interface/components.ts index ecdf51d4f..fedf1ddeb 100644 --- a/components/theme/interface/components.ts +++ b/components/theme/interface/components.ts @@ -1,5 +1,5 @@ import type { ComponentToken as AlertComponentToken } from '../../alert/style'; -// import type { ComponentToken as AnchorComponentToken } from '../../anchor/style'; +import type { ComponentToken as AnchorComponentToken } from '../../anchor/style'; import type { ComponentToken as AvatarComponentToken } from '../../avatar/style'; // import type { ComponentToken as BackTopComponentToken } from '../../back-top/style'; // import type { ComponentToken as ButtonComponentToken } from '../../button/style'; @@ -53,7 +53,7 @@ import type { ComponentToken as AvatarComponentToken } from '../../avatar/style' export interface ComponentTokenMap { Affix?: {}; Alert?: AlertComponentToken; - // Anchor?: AnchorComponentToken; + Anchor?: AnchorComponentToken; Avatar?: AvatarComponentToken; // BackTop?: BackTopComponentToken; // Badge?: {};