From 587c1ca89d64ae44b6597b32985ae68710bf3bd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9E=9C=E5=86=BB=E6=A9=99?= Date: Wed, 15 Feb 2023 13:52:47 +0800 Subject: [PATCH] refactor:timeline (#6263) * refactor:timeline * docs:update & refactor: timeline type --- components/input/style/index.ts | 2 +- components/style.ts | 2 +- components/theme/interface/components.ts | 4 +- components/timeline/Timeline.tsx | 39 ++-- components/timeline/TimelineItem.tsx | 4 +- components/timeline/index.en-US.md | 2 +- components/timeline/index.zh-CN.md | 2 +- components/timeline/style/index.less | 185 ------------------ components/timeline/style/index.tsx | 239 ++++++++++++++++++++++- components/timeline/style/rtl.less | 135 ------------- 10 files changed, 273 insertions(+), 341 deletions(-) delete mode 100644 components/timeline/style/index.less delete mode 100644 components/timeline/style/rtl.less diff --git a/components/input/style/index.ts b/components/input/style/index.ts index 41a0e718d..edbcb16dd 100644 --- a/components/input/style/index.ts +++ b/components/input/style/index.ts @@ -904,7 +904,7 @@ const genTextAreaStyle: GenerateStyle = token => { [`> ${componentCls}`]: { height: '100%', }, - + '&::after': { color: token.colorTextDescription, whiteSpace: 'nowrap', diff --git a/components/style.ts b/components/style.ts index c377c48a6..ddb30f7ee 100644 --- a/components/style.ts +++ b/components/style.ts @@ -39,7 +39,7 @@ import './calendar/style'; // import './slider/style'; import './table/style'; // import './progress/style'; -import './timeline/style'; +// import './timeline/style'; import './input-number/style'; // import './transfer/style'; import './tree/style'; diff --git a/components/theme/interface/components.ts b/components/theme/interface/components.ts index aeba18e24..123d2c71b 100644 --- a/components/theme/interface/components.ts +++ b/components/theme/interface/components.ts @@ -40,7 +40,7 @@ import type { ComponentToken as SpinComponentToken } from '../../spin/style'; // import type { ComponentToken as TableComponentToken } from '../../table/style'; // import type { ComponentToken as TabsComponentToken } from '../../tabs/style'; import type { ComponentToken as TagComponentToken } from '../../tag/style'; -// import type { ComponentToken as TimelineComponentToken } from '../../timeline/style'; +import type { ComponentToken as TimelineComponentToken } from '../../timeline/style'; import type { ComponentToken as TooltipComponentToken } from '../../tooltip/style'; import type { ComponentToken as TransferComponentToken } from '../../transfer/style'; import type { ComponentToken as TypographyComponentToken } from '../../typography/style'; @@ -99,7 +99,7 @@ export interface ComponentTokenMap { Tree?: {}; TreeSelect?: {}; Typography?: TypographyComponentToken; - // Timeline?: TimelineComponentToken; + Timeline?: TimelineComponentToken; Transfer?: TransferComponentToken; // Tabs?: TabsComponentToken; // Calendar?: CalendarComponentToken; diff --git a/components/timeline/Timeline.tsx b/components/timeline/Timeline.tsx index 79c07e110..a808711f8 100644 --- a/components/timeline/Timeline.tsx +++ b/components/timeline/Timeline.tsx @@ -6,15 +6,18 @@ import { filterEmpty } from '../_util/props-util'; import initDefaultProps from '../_util/props-util/initDefaultProps'; import TimelineItem from './TimelineItem'; import LoadingOutlined from '@ant-design/icons-vue/LoadingOutlined'; -import { tuple } from '../_util/type'; +import { tuple, booleanType } from '../_util/type'; import useConfigInject from '../config-provider/hooks/useConfigInject'; +// CSSINJS +import useStyle from './style'; + export const timelineProps = () => ({ prefixCls: String, /** 指定最后一个幽灵节点是否存在或内容 */ pending: PropTypes.any, pendingDot: PropTypes.any, - reverse: { type: Boolean, default: undefined }, + reverse: booleanType(), mode: PropTypes.oneOf(tuple('left', 'alternate', 'right', '')), }); @@ -23,13 +26,18 @@ export type TimelineProps = Partial { const eleProps = ele.props || {}; if (props.mode === 'alternate') { @@ -80,14 +88,23 @@ export default defineComponent({ const hasLabelItem = timeLineItems.some( item => !!(item.props?.label || item.children?.label), ); - const classString = classNames(prefixCls.value, { - [`${prefixCls.value}-pending`]: !!pending, - [`${prefixCls.value}-reverse`]: !!reverse, - [`${prefixCls.value}-${mode}`]: !!mode && !hasLabelItem, - [`${prefixCls.value}-label`]: hasLabelItem, - [`${prefixCls.value}-rtl`]: direction.value === 'rtl', - }); - return
    {items}
; + const classString = classNames( + prefixCls.value, + { + [`${prefixCls.value}-pending`]: !!pending, + [`${prefixCls.value}-reverse`]: !!reverse, + [`${prefixCls.value}-${mode}`]: !!mode && !hasLabelItem, + [`${prefixCls.value}-label`]: hasLabelItem, + [`${prefixCls.value}-rtl`]: direction.value === 'rtl', + }, + attrs.class, + hashId.value, + ); + return wrapSSR( +
    + {items} +
, + ); }; }, }); diff --git a/components/timeline/TimelineItem.tsx b/components/timeline/TimelineItem.tsx index fde51a0ed..9b6a7a4e0 100644 --- a/components/timeline/TimelineItem.tsx +++ b/components/timeline/TimelineItem.tsx @@ -3,14 +3,14 @@ import { defineComponent } from 'vue'; import classNames from '../_util/classNames'; import PropTypes from '../_util/vue-types'; import initDefaultProps from '../_util/props-util/initDefaultProps'; -import { tuple } from '../_util/type'; +import { tuple, booleanType } from '../_util/type'; import useConfigInject from '../config-provider/hooks/useConfigInject'; export const timelineItemProps = () => ({ prefixCls: String, color: String, dot: PropTypes.any, - pending: { type: Boolean, default: undefined }, + pending: booleanType(), position: PropTypes.oneOf(tuple('left', 'right', '')).def(''), label: PropTypes.any, }); diff --git a/components/timeline/index.en-US.md b/components/timeline/index.en-US.md index a4e1c767d..0058ee480 100644 --- a/components/timeline/index.en-US.md +++ b/components/timeline/index.en-US.md @@ -2,7 +2,7 @@ category: Components type: Data Display title: Timeline -cover: https://gw.alipayobjects.com/zos/antfincdn/vJmo00mmgR/Timeline.svg +cover: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*FkTySqNt3sYAAAAAAAAAAAAADrJ8AQ/original --- Vertical display timeline. diff --git a/components/timeline/index.zh-CN.md b/components/timeline/index.zh-CN.md index 6765274b2..5c7a20ff5 100644 --- a/components/timeline/index.zh-CN.md +++ b/components/timeline/index.zh-CN.md @@ -3,7 +3,7 @@ category: Components type: 数据展示 title: Timeline subtitle: 时间轴 -cover: https://gw.alipayobjects.com/zos/antfincdn/vJmo00mmgR/Timeline.svg +cover: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*FkTySqNt3sYAAAAAAAAAAAAADrJ8AQ/original --- 垂直展示的时间流信息。 diff --git a/components/timeline/style/index.less b/components/timeline/style/index.less deleted file mode 100644 index cca335a6b..000000000 --- a/components/timeline/style/index.less +++ /dev/null @@ -1,185 +0,0 @@ -@import '../../style/themes/index'; -@import '../../style/mixins/index'; - -@timeline-prefix-cls: ~'@{ant-prefix}-timeline'; - -.@{timeline-prefix-cls} { - .reset-component(); - - margin: 0; - padding: 0; - list-style: none; - - &-item { - position: relative; - margin: 0; - padding-bottom: @timeline-item-padding-bottom; - font-size: @font-size-base; - list-style: none; - - &-tail { - position: absolute; - top: 10px; - left: 4px; - height: calc(100% - 10px); - border-left: @timeline-width solid @timeline-color; - } - - &-pending &-head { - font-size: @font-size-sm; - background-color: transparent; - } - - &-pending &-tail { - display: none; - } - - &-head { - position: absolute; - width: 10px; - height: 10px; - background-color: @timeline-dot-bg; - border: @timeline-dot-border-width solid transparent; - border-radius: 100px; - - &-blue { - color: @primary-color; - border-color: @primary-color; - } - - &-red { - color: @error-color; - border-color: @error-color; - } - - &-green { - color: @success-color; - border-color: @success-color; - } - - &-gray { - color: @disabled-color; - border-color: @disabled-color; - } - } - - &-head-custom { - position: absolute; - top: 5.5px; - left: 5px; - width: auto; - height: auto; - margin-top: 0; - padding: 3px 1px; - line-height: 1; - text-align: center; - border: 0; - border-radius: 0; - transform: translate(-50%, -50%); - } - - &-content { - position: relative; - top: -(@font-size-base * @line-height-base - @font-size-base) + 1px; - margin: 0 0 0 @margin-lg + 2px; - word-break: break-word; - } - - &-last { - > .@{timeline-prefix-cls}-item-tail { - display: none; - } - > .@{timeline-prefix-cls}-item-content { - min-height: 48px; - } - } - } - - &.@{timeline-prefix-cls}-alternate, - &.@{timeline-prefix-cls}-right, - &.@{timeline-prefix-cls}-label { - .@{timeline-prefix-cls}-item { - &-tail, - &-head, - &-head-custom { - left: 50%; - } - - &-head { - margin-left: -4px; - - &-custom { - margin-left: 1px; - } - } - - &-left { - .@{timeline-prefix-cls}-item-content { - left: calc(50% - 4px); - width: calc(50% - 14px); - text-align: left; - } - } - - &-right { - .@{timeline-prefix-cls}-item-content { - width: calc(50% - 12px); - margin: 0; - text-align: right; - } - } - } - } - - &.@{timeline-prefix-cls}-right { - .@{timeline-prefix-cls}-item-right { - .@{timeline-prefix-cls}-item-tail, - .@{timeline-prefix-cls}-item-head, - .@{timeline-prefix-cls}-item-head-custom { - left: calc(100% - 4px - @timeline-width); - } - .@{timeline-prefix-cls}-item-content { - width: calc(100% - 18px); - } - } - } - - &&-pending &-item-last &-item-tail { - display: block; - height: calc(100% - 14px); - border-left: 2px dotted @timeline-color; - } - - &&-reverse &-item-last &-item-tail { - display: none; - } - - &&-reverse &-item-pending { - .@{timeline-prefix-cls}-item-tail { - top: 15px; - display: block; - height: calc(100% - 15px); - border-left: 2px dotted @timeline-color; - } - .@{timeline-prefix-cls}-item-content { - min-height: 48px; - } - } - &.@{timeline-prefix-cls}-label { - .@{timeline-prefix-cls}-item-label { - position: absolute; - top: -(@font-size-base * @line-height-base - @font-size-base) + 1px; - width: calc(50% - 12px); - text-align: right; - } - .@{timeline-prefix-cls}-item-right { - .@{timeline-prefix-cls}-item-label { - left: calc(50% + 14px); - width: calc(50% - 14px); - text-align: left; - } - } - } -} - -@import './rtl'; diff --git a/components/timeline/style/index.tsx b/components/timeline/style/index.tsx index 3a3ab0de5..e3b94f92e 100644 --- a/components/timeline/style/index.tsx +++ b/components/timeline/style/index.tsx @@ -1,2 +1,237 @@ -import '../../style/index.less'; -import './index.less'; +import type { CSSObject } from '../../_util/cssinjs'; +import type { FullToken, GenerateStyle } from '../../theme/internal'; +import { genComponentStyleHook, mergeToken } from '../../theme/internal'; +import { resetComponent } from '../../_style'; + +export interface ComponentToken {} + +interface TimelineToken extends FullToken<'Timeline'> { + timeLineItemPaddingBottom: number; + timeLineItemHeadSize: number; + timeLineItemCustomHeadPaddingVertical: number; + timeLineItemTailWidth: number; + timeLinePaddingInlineEnd: number; + timeLineHeadBorderWidth: number; +} + +const genTimelineStyle: GenerateStyle = token => { + const { componentCls } = token; + + return { + [componentCls]: { + ...resetComponent(token), + margin: 0, + padding: 0, + listStyle: 'none', + + [`${componentCls}-item`]: { + position: 'relative', + margin: 0, + paddingBottom: token.timeLineItemPaddingBottom, + fontSize: token.fontSize, + listStyle: 'none', + + '&-tail': { + position: 'absolute', + insetBlockStart: token.timeLineItemHeadSize, + insetInlineStart: (token.timeLineItemHeadSize - token.timeLineItemTailWidth) / 2, + height: `calc(100% - ${token.timeLineItemHeadSize}px)`, + borderInlineStart: `${token.timeLineItemTailWidth}px ${token.lineType} ${token.colorSplit}`, + }, + + '&-pending': { + [`${componentCls}-item-head`]: { + fontSize: token.fontSizeSM, + backgroundColor: 'transparent', + }, + + [`${componentCls}-item-tail`]: { + display: 'none', + }, + }, + + '&-head': { + position: 'absolute', + width: token.timeLineItemHeadSize, + height: token.timeLineItemHeadSize, + backgroundColor: token.colorBgContainer, + border: `${token.timeLineHeadBorderWidth}px ${token.lineType} transparent`, + borderRadius: '50%', + + '&-blue': { + color: token.colorPrimary, + borderColor: token.colorPrimary, + }, + + '&-red': { + color: token.colorError, + borderColor: token.colorError, + }, + + '&-green': { + color: token.colorSuccess, + borderColor: token.colorSuccess, + }, + + '&-gray': { + color: token.colorTextDisabled, + borderColor: token.colorTextDisabled, + }, + }, + + '&-head-custom': { + position: 'absolute', + insetBlockStart: token.timeLineItemHeadSize / 2, + insetInlineStart: token.timeLineItemHeadSize / 2, + width: 'auto', + height: 'auto', + marginBlockStart: 0, + paddingBlock: token.timeLineItemCustomHeadPaddingVertical, + lineHeight: 1, + textAlign: 'center', + border: 0, + borderRadius: 0, + transform: `translate(-50%, -50%)`, + }, + + '&-content': { + position: 'relative', + insetBlockStart: -(token.fontSize * token.lineHeight - token.fontSize) + token.lineWidth, + marginInlineStart: token.margin + token.timeLineItemHeadSize, + marginInlineEnd: 0, + marginBlockStart: 0, + marginBlockEnd: 0, + wordBreak: 'break-word', + }, + + '&-last': { + [`> ${componentCls}-item-tail`]: { + display: 'none', + }, + + [`> ${componentCls}-item-content`]: { + minHeight: token.controlHeightLG * 1.2, + }, + }, + }, + + [`&${componentCls}-alternate, + &${componentCls}-right, + &${componentCls}-label`]: { + [`${componentCls}-item`]: { + '&-tail, &-head, &-head-custom': { + insetInlineStart: '50%', + }, + + '&-head': { + marginInlineStart: `-${token.marginXXS}px`, + + '&-custom': { + marginInlineStart: token.timeLineItemTailWidth / 2, + }, + }, + + '&-left': { + [`${componentCls}-item-content`]: { + insetInlineStart: `calc(50% - ${token.marginXXS}px)`, + width: `calc(50% - ${token.marginSM}px)`, + textAlign: 'start', + }, + }, + + '&-right': { + [`${componentCls}-item-content`]: { + width: `calc(50% - ${token.marginSM}px)`, + margin: 0, + textAlign: 'end', + }, + }, + }, + }, + + [`&${componentCls}-right`]: { + [`${componentCls}-item-right`]: { + [`${componentCls}-item-tail, + ${componentCls}-item-head, + ${componentCls}-item-head-custom`]: { + insetInlineStart: `calc(100% - ${ + (token.timeLineItemHeadSize + token.timeLineItemTailWidth) / 2 + }px)`, + }, + + [`${componentCls}-item-content`]: { + width: `calc(100% - ${token.timeLineItemHeadSize + token.marginXS}px)`, + }, + }, + }, + + [`&${componentCls}-pending + ${componentCls}-item-last + ${componentCls}-item-tail`]: { + display: 'block', + height: `calc(100% - ${token.margin}px)`, + borderInlineStart: `${token.timeLineItemTailWidth}px dotted ${token.colorSplit}`, + }, + + [`&${componentCls}-reverse + ${componentCls}-item-last + ${componentCls}-item-tail`]: { + display: 'none', + }, + + [`&${componentCls}-reverse ${componentCls}-item-pending`]: { + [`${componentCls}-item-tail`]: { + insetBlockStart: token.margin, + display: 'block', + height: `calc(100% - ${token.margin}px)`, + borderInlineStart: `${token.timeLineItemTailWidth}px dotted ${token.colorSplit}`, + }, + + [`${componentCls}-item-content`]: { + minHeight: token.controlHeightLG * 1.2, + }, + }, + + [`&${componentCls}-label`]: { + [`${componentCls}-item-label`]: { + position: 'absolute', + insetBlockStart: + -(token.fontSize * token.lineHeight - token.fontSize) + token.timeLineItemTailWidth, + width: `calc(50% - ${token.marginSM}px)`, + textAlign: 'end', + }, + + [`${componentCls}-item-right`]: { + [`${componentCls}-item-label`]: { + insetInlineStart: `calc(50% + ${token.marginSM}px)`, + width: `calc(50% - ${token.marginSM}px)`, + textAlign: 'start', + }, + }, + }, + + // ====================== RTL ======================= + '&-rtl': { + direction: 'rtl', + + [`${componentCls}-item-head-custom`]: { + transform: `translate(50%, -50%)`, + }, + }, + }, + }; +}; + +// ============================== Export ============================== +export default genComponentStyleHook('Timeline', token => { + const timeLineToken = mergeToken(token, { + timeLineItemPaddingBottom: token.padding * 1.25, + timeLineItemHeadSize: 10, + timeLineItemCustomHeadPaddingVertical: token.paddingXXS, + timeLinePaddingInlineEnd: 2, + timeLineItemTailWidth: token.lineWidthBold, + timeLineHeadBorderWidth: token.wireframe ? token.lineWidthBold : token.lineWidth * 3, + }); + + return [genTimelineStyle(timeLineToken)]; +}); diff --git a/components/timeline/style/rtl.less b/components/timeline/style/rtl.less deleted file mode 100644 index 011f5aa3b..000000000 --- a/components/timeline/style/rtl.less +++ /dev/null @@ -1,135 +0,0 @@ -@import '../../style/themes/index'; -@import '../../style/mixins/index'; - -@timeline-prefix-cls: ~'@{ant-prefix}-timeline'; - -.@{timeline-prefix-cls} { - &-rtl { - direction: rtl; - } - - &-item { - &-tail { - .@{timeline-prefix-cls}-rtl & { - right: 4px; - left: auto; - border-right: @timeline-width solid @timeline-color; - border-left: none; - } - } - - &-head-custom { - .@{timeline-prefix-cls}-rtl & { - right: 5px; - left: auto; - transform: translate(50%, -50%); - } - } - - &-content { - .@{timeline-prefix-cls}-rtl & { - margin: 0 18px 0 0; - } - } - } - - &.@{timeline-prefix-cls}-alternate, - &.@{timeline-prefix-cls}-right, - &.@{timeline-prefix-cls}-label { - .@{timeline-prefix-cls}-item { - &-tail, - &-head, - &-head-custom { - .@{timeline-prefix-cls}-rtl& { - right: 50%; - left: auto; - } - } - - &-head { - .@{timeline-prefix-cls}-rtl& { - margin-right: -4px; - margin-left: 0; - } - - &-custom { - .@{timeline-prefix-cls}-rtl& { - margin-right: 1px; - margin-left: 0; - } - } - } - - &-left { - .@{timeline-prefix-cls}-item-content { - .@{timeline-prefix-cls}-rtl& { - right: calc(50% - 4px); - left: auto; - text-align: right; - } - } - } - - &-right { - .@{timeline-prefix-cls}-item-content { - .@{timeline-prefix-cls}-rtl& { - text-align: left; - } - } - } - } - } - - &.@{timeline-prefix-cls}-right { - .@{timeline-prefix-cls}-item-right { - .@{timeline-prefix-cls}-item-tail, - .@{timeline-prefix-cls}-item-head, - .@{timeline-prefix-cls}-item-head-custom { - .@{timeline-prefix-cls}-rtl& { - right: 0; - left: auto; - } - } - - .@{timeline-prefix-cls}-item-content { - .@{timeline-prefix-cls}-rtl& { - width: 100%; - margin-right: 18px; - text-align: right; - } - } - } - } - - &&-pending &-item-last &-item-tail { - .@{timeline-prefix-cls}-rtl& { - border-right: 2px dotted @timeline-color; - border-left: none; - } - } - - &&-reverse &-item-pending { - .@{timeline-prefix-cls}-item-tail { - .@{timeline-prefix-cls}-rtl& { - border-right: 2px dotted @timeline-color; - border-left: none; - } - } - } - - &.@{timeline-prefix-cls}-label { - .@{timeline-prefix-cls}-item-label { - .@{timeline-prefix-cls}-rtl& { - text-align: left; - } - } - .@{timeline-prefix-cls}-item-right { - .@{timeline-prefix-cls}-item-label { - .@{timeline-prefix-cls}-rtl& { - right: calc(50% + 14px); - text-align: right; - } - } - } - } -}