feat: Time line data (#40424)

* feat: update timeline to data driven
This commit is contained in:
黑雨 2023-02-07 16:16:48 +08:00 committed by GitHub
parent 6b55a32eef
commit 751bf40f60
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 447 additions and 196 deletions

View File

@ -1,11 +1,10 @@
import LoadingOutlined from '@ant-design/icons/LoadingOutlined';
import classNames from 'classnames';
import * as React from 'react';
import { ConfigContext } from '../config-provider';
import { cloneElement } from '../_util/reactNode';
import type { TimelineItemProps } from './TimelineItem';
import TimelineItemList from './TimelineItemList';
import TimelineItem from './TimelineItem';
import warning from '../_util/warning';
import useItems from './useItems';
// CSSINJS
import useStyle from './style';
@ -20,6 +19,7 @@ export interface TimelineProps {
style?: React.CSSProperties;
reverse?: boolean;
mode?: 'left' | 'alternate' | 'right';
items?: TimelineItemProps[];
children?: React.ReactNode;
}
@ -29,83 +29,27 @@ type CompoundedComponent = React.FC<TimelineProps> & {
const Timeline: CompoundedComponent = (props) => {
const { getPrefixCls, direction } = React.useContext(ConfigContext);
const {
prefixCls: customizePrefixCls,
pending = null,
pendingDot,
children,
className,
rootClassName,
reverse = false,
mode = '' as TimelineProps['mode'],
...restProps
} = props;
const { prefixCls: customizePrefixCls, children, items, ...restProps } = props;
const prefixCls = getPrefixCls('timeline', customizePrefixCls);
const pendingNode = typeof pending === 'boolean' ? null : pending;
// =================== Warning =====================
if (process.env.NODE_ENV !== 'production') {
warning(!children, 'Timeline', '`Timeline.Item` is deprecated. Please use `items` instead.');
}
// Style
const [wrapSSR, hashId] = useStyle(prefixCls);
const pendingItem = pending ? (
<TimelineItem pending={!!pending} dot={pendingDot || <LoadingOutlined />}>
{pendingNode}
</TimelineItem>
) : null;
const timeLineItems = React.Children.toArray(children);
timeLineItems.push(pendingItem!);
if (reverse) {
timeLineItems.reverse();
}
const getPositionCls = (ele: React.ReactElement<any>, idx: number) => {
if (mode === 'alternate') {
if (ele.props.position === 'right') return `${prefixCls}-item-right`;
if (ele.props.position === 'left') return `${prefixCls}-item-left`;
return idx % 2 === 0 ? `${prefixCls}-item-left` : `${prefixCls}-item-right`;
}
if (mode === 'left') return `${prefixCls}-item-left`;
if (mode === 'right') return `${prefixCls}-item-right`;
if (ele.props.position === 'right') return `${prefixCls}-item-right`;
return '';
};
// Remove falsy items
const truthyItems = timeLineItems.filter((item) => !!item);
const itemsCount = React.Children.count(truthyItems);
const lastCls = `${prefixCls}-item-last`;
const items = React.Children.map(truthyItems, (ele: React.ReactElement<any>, idx) => {
const pendingClass = idx === itemsCount - 2 ? lastCls : '';
const readyClass = idx === itemsCount - 1 ? lastCls : '';
return cloneElement(ele, {
className: classNames([
ele.props.className,
!reverse && !!pending ? pendingClass : readyClass,
getPositionCls(ele, idx),
]),
});
});
const hasLabelItem = timeLineItems.some((item: React.ReactElement<any>) => !!item?.props?.label);
const classString = classNames(
prefixCls,
{
[`${prefixCls}-pending`]: !!pending,
[`${prefixCls}-reverse`]: !!reverse,
[`${prefixCls}-${mode}`]: !!mode && !hasLabelItem,
[`${prefixCls}-label`]: hasLabelItem,
[`${prefixCls}-rtl`]: direction === 'rtl',
},
className,
rootClassName,
hashId,
);
const mergedItems: TimelineItemProps[] = useItems(items, children);
return wrapSSR(
<ul {...restProps} className={classString}>
{items}
</ul>,
<TimelineItemList
{...restProps}
prefixCls={prefixCls}
direction={direction}
items={mergedItems}
hashId={hashId}
/>,
);
};

View File

@ -6,6 +6,7 @@ import type { LiteralUnion } from '../_util/type';
type Color = 'blue' | 'red' | 'green' | 'gray';
export interface TimelineItemProps {
key?: React.Key;
prefixCls?: string;
className?: string;
color?: LiteralUnion<Color>;

View File

@ -0,0 +1,92 @@
import classNames from 'classnames';
import * as React from 'react';
import LoadingOutlined from '@ant-design/icons/LoadingOutlined';
import TimelineItem from './TimelineItem';
import type { TimelineProps } from './Timeline';
import type { TimelineItemProps } from './TimelineItem';
const TimelineItemList: React.FC<TimelineProps & { hashId: string; direction?: string }> = ({
prefixCls,
className,
pending = false,
children,
items,
rootClassName,
reverse = false,
direction,
hashId,
pendingDot,
mode = '' as TimelineProps['mode'],
...restProps
}) => {
const getPositionCls = (position: string, idx: number) => {
if (mode === 'alternate') {
if (position === 'right') return `${prefixCls}-item-right`;
if (position === 'left') return `${prefixCls}-item-left`;
return idx % 2 === 0 ? `${prefixCls}-item-left` : `${prefixCls}-item-right`;
}
if (mode === 'left') return `${prefixCls}-item-left`;
if (mode === 'right') return `${prefixCls}-item-right`;
if (position === 'right') return `${prefixCls}-item-right`;
return '';
};
const mergedItems = [...(items || [])];
const pendingNode = typeof pending === 'boolean' ? null : pending;
if (pending) {
mergedItems.push({
pending: !!pending,
dot: pendingDot || <LoadingOutlined />,
children: pendingNode,
});
}
if (reverse) {
mergedItems.reverse();
}
const itemsCount = mergedItems.length;
const lastCls = `${prefixCls}-item-last`;
const itemsList = mergedItems
.filter((item: TimelineItemProps) => !!item)
.map((item: TimelineItemProps, idx: number) => {
const pendingClass = idx === itemsCount - 2 ? lastCls : '';
const readyClass = idx === itemsCount - 1 ? lastCls : '';
return (
<TimelineItem
className={classNames([
className,
!reverse && !!pending ? pendingClass : readyClass,
getPositionCls(item?.position ?? '', idx),
])}
{...item}
/* eslint-disable-next-line react/no-array-index-key */
key={item?.key || idx}
/>
);
});
const hasLabelItem = mergedItems.some((item: TimelineItemProps) => !!item?.label);
const classString = classNames(
prefixCls,
{
[`${prefixCls}-pending`]: !!pending,
[`${prefixCls}-reverse`]: !!reverse,
[`${prefixCls}-${mode}`]: !!mode && !hasLabelItem,
[`${prefixCls}-label`]: hasLabelItem,
[`${prefixCls}-rtl`]: direction === 'rtl',
},
className,
rootClassName,
hashId,
);
return (
<ul {...restProps} className={classString}>
{itemsList}
</ul>
);
};
export default TimelineItemList;

View File

@ -5,19 +5,22 @@ import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest';
import { render } from '../../../tests/utils';
const { Item } = TimeLine;
const renderFactory = (
timeLineProps: TimelineProps = {},
labelItems: TimelineProps['children'] = null,
) =>
const renderFactory = (timeLineProps: TimelineProps) =>
render(
<TimeLine {...timeLineProps}>
<Item key="1">foo</Item>
<Item key="2">bar</Item>
<Item key="3">baz</Item>
{labelItems}
</TimeLine>,
<TimeLine
{...timeLineProps}
items={[
{
children: 'foo',
},
{
children: 'bar',
},
{
children: 'baz',
},
]}
/>,
);
describe('TimeLine', () => {
@ -26,8 +29,60 @@ describe('TimeLine', () => {
rtlTest(TimeLine);
rtlTest(TimeLine.Item);
describe('render TimeLine.Item', () => {
it('TimeLine.Item should correctly', () => {
const errSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
const { container } = render(
<TimeLine reverse>
<TimeLine.Item>foo</TimeLine.Item>
<TimeLine.Item>bar</TimeLine.Item>
<TimeLine.Item>baz</TimeLine.Item>
</TimeLine>,
);
// has 3 timeline item
expect(container.querySelectorAll('li.ant-timeline-item')).toHaveLength(3);
// has only 1 timeline item is marked as the last item
expect(container.querySelectorAll('li.ant-timeline-item-last')).toHaveLength(1);
// its last item is marked as the last item
expect(container.querySelectorAll('li.ant-timeline-item')[2]).toHaveClass(
'ant-timeline-item-last',
);
expect(errSpy).toHaveBeenCalledWith(
'Warning: [antd: Timeline] `Timeline.Item` is deprecated. Please use `items` instead.',
);
errSpy.mockRestore();
});
it('has extra pending timeline item', () => {
const { container } = render(
<TimeLine pending={<div>pending...</div>} reverse mode="alternate">
<TimeLine.Item>foo</TimeLine.Item>
<TimeLine.Item position="right">bar</TimeLine.Item>
<TimeLine.Item position="left">baz</TimeLine.Item>
</TimeLine>,
);
expect(container.querySelectorAll('li.ant-timeline-item-pending')).toHaveLength(1);
});
it("has no pending dot if without passing a truthy 'pending' prop", () => {
const { queryByText } = render(
<TimeLine pendingDot={<i>dot</i>} reverse>
<TimeLine.Item>foo</TimeLine.Item>
<TimeLine.Item>bar</TimeLine.Item>
<TimeLine.Item position="right">baz</TimeLine.Item>
</TimeLine>,
);
expect(queryByText('dot')).toBeFalsy();
});
});
it('renders items without passing any props correctly', () => {
const { container } = renderFactory();
const { container } = renderFactory({});
// has 3 timeline item
expect(container.querySelectorAll('li.ant-timeline-item')).toHaveLength(3);
@ -132,11 +187,21 @@ describe('TimeLine', () => {
it('renders Timeline item with label correctly', () => {
const label = '2020-01-01';
const { container } = renderFactory(
{},
<Item key="1" label={label}>
foo
</Item>,
const { container } = render(
<TimeLine
items={[
{
label,
children: 'foo',
},
{
children: 'bar',
},
{
children: 'baz',
},
]}
/>,
);
expect(container.querySelectorAll('.ant-timeline-label')).toHaveLength(1);
expect(container.querySelector('.ant-timeline-item-label')).toHaveTextContent(label);
@ -148,9 +213,14 @@ describe('TimeLine', () => {
presetColors.forEach((color) => {
it(`className should have a preset color ${color}`, () => {
const { container } = render(
<TimeLine>
<Item color={color}>foo</Item>
</TimeLine>,
<TimeLine
items={[
{
color,
children: 'foo',
},
]}
/>,
);
expect(container.querySelector('.ant-timeline-item-head')).toHaveClass(
`ant-timeline-item-head-${color}`,
@ -167,9 +237,14 @@ describe('TimeLine', () => {
nonPresetColors.forEach((color) => {
it(`className should not have a non-preset color ${color}`, () => {
const { container } = render(
<TimeLine>
<Item color={color}>foo</Item>
</TimeLine>,
<TimeLine
items={[
{
color,
children: 'foo',
},
]}
/>,
);
expect(container.querySelector('.ant-timeline-item-head')).not.toHaveClass(
`ant-timeline-item-head-${color}`,

View File

@ -3,20 +3,33 @@ import { ClockCircleOutlined } from '@ant-design/icons';
import { Timeline } from 'antd';
const App: React.FC = () => (
<Timeline mode="alternate">
<Timeline.Item>Create a services site 2015-09-01</Timeline.Item>
<Timeline.Item color="green">Solve initial network problems 2015-09-01</Timeline.Item>
<Timeline.Item dot={<ClockCircleOutlined style={{ fontSize: '16px' }} />}>
Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque
laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto
beatae vitae dicta sunt explicabo.
</Timeline.Item>
<Timeline.Item color="red">Network problems being solved 2015-09-01</Timeline.Item>
<Timeline.Item>Create a services site 2015-09-01</Timeline.Item>
<Timeline.Item dot={<ClockCircleOutlined style={{ fontSize: '16px' }} />}>
Technical testing 2015-09-01
</Timeline.Item>
</Timeline>
<Timeline
mode="alternate"
items={[
{
children: 'Create a services site 2015-09-01',
},
{
children: 'Solve initial network problems 2015-09-01',
color: 'green',
},
{
dot: <ClockCircleOutlined style={{ fontSize: '16px' }} />,
children: `Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo.`,
},
{
color: 'red',
children: 'Network problems being solved 2015-09-01',
},
{
children: 'Create a services site 2015-09-01',
},
{
dot: <ClockCircleOutlined style={{ fontSize: '16px' }} />,
children: 'Technical testing 2015-09-01',
},
]}
/>
);
export default App;

View File

@ -2,12 +2,22 @@ import React from 'react';
import { Timeline } from 'antd';
const App: React.FC = () => (
<Timeline>
<Timeline.Item>Create a services site 2015-09-01</Timeline.Item>
<Timeline.Item>Solve initial network problems 2015-09-01</Timeline.Item>
<Timeline.Item>Technical testing 2015-09-01</Timeline.Item>
<Timeline.Item>Network problems being solved 2015-09-01</Timeline.Item>
</Timeline>
<Timeline
items={[
{
children: 'Create a services site 2015-09-01',
},
{
children: 'Solve initial network problems 2015-09-01',
},
{
children: 'Technical testing 2015-09-01',
},
{
children: 'Network problems being solved 2015-09-01',
},
]}
/>
);
export default App;

View File

@ -3,33 +3,62 @@ import { SmileOutlined } from '@ant-design/icons';
import { Timeline } from 'antd';
const App: React.FC = () => (
<Timeline>
<Timeline.Item color="green">Create a services site 2015-09-01</Timeline.Item>
<Timeline.Item color="green">Create a services site 2015-09-01</Timeline.Item>
<Timeline.Item color="red">
<p>Solve initial network problems 1</p>
<p>Solve initial network problems 2</p>
<p>Solve initial network problems 3 2015-09-01</p>
</Timeline.Item>
<Timeline.Item>
<p>Technical testing 1</p>
<p>Technical testing 2</p>
<p>Technical testing 3 2015-09-01</p>
</Timeline.Item>
<Timeline.Item color="gray">
<p>Technical testing 1</p>
<p>Technical testing 2</p>
<p>Technical testing 3 2015-09-01</p>
</Timeline.Item>
<Timeline.Item color="gray">
<p>Technical testing 1</p>
<p>Technical testing 2</p>
<p>Technical testing 3 2015-09-01</p>
</Timeline.Item>
<Timeline.Item color="#00CCFF" dot={<SmileOutlined />}>
<p>Custom color testing</p>
</Timeline.Item>
</Timeline>
<Timeline
items={[
{
color: 'green',
children: 'Create a services site 2015-09-01',
},
{
color: 'green',
children: 'Create a services site 2015-09-01',
},
{
color: 'red',
children: (
<>
<p>Solve initial network problems 1</p>
<p>Solve initial network problems 2</p>
<p>Solve initial network problems 3 2015-09-01</p>
</>
),
},
{
children: (
<>
<p>Technical testing 1</p>
<p>Technical testing 2</p>
<p>Technical testing 3 2015-09-01</p>
</>
),
},
{
color: 'gray',
children: (
<>
<p>Technical testing 1</p>
<p>Technical testing 2</p>
<p>Technical testing 3 2015-09-01</p>
</>
),
},
{
color: 'gray',
children: (
<>
<p>Technical testing 1</p>
<p>Technical testing 2</p>
<p>Technical testing 3 2015-09-01</p>
</>
),
},
{
color: '#00CCFF',
dot: <SmileOutlined />,
children: <p>Custom color testing</p>,
},
]}
/>
);
export default App;

View File

@ -3,14 +3,24 @@ import { ClockCircleOutlined } from '@ant-design/icons';
import { Timeline } from 'antd';
const App: React.FC = () => (
<Timeline>
<Timeline.Item>Create a services site 2015-09-01</Timeline.Item>
<Timeline.Item>Solve initial network problems 2015-09-01</Timeline.Item>
<Timeline.Item dot={<ClockCircleOutlined className="timeline-clock-icon" />} color="red">
Technical testing 2015-09-01
</Timeline.Item>
<Timeline.Item>Network problems being solved 2015-09-01</Timeline.Item>
</Timeline>
<Timeline
items={[
{
children: 'Create a services site 2015-09-01',
},
{
children: 'Solve initial network problems 2015-09-01',
},
{
dot: <ClockCircleOutlined className="timeline-clock-icon" />,
color: 'red',
children: 'Technical testing 2015-09-01',
},
{
children: 'Network problems being solved 2015-09-01',
},
]}
/>
);
export default App;

View File

@ -22,12 +22,26 @@ const App: React.FC = () => {
<Radio value="right">Right</Radio>
<Radio value="alternate">Alternate</Radio>
</Radio.Group>
<Timeline mode={mode}>
<Timeline.Item label="2015-09-01">Create a services</Timeline.Item>
<Timeline.Item label="2015-09-01 09:12:11">Solve initial network problems</Timeline.Item>
<Timeline.Item>Technical testing</Timeline.Item>
<Timeline.Item label="2015-09-01 09:12:11">Network problems being solved</Timeline.Item>
</Timeline>
<Timeline
mode={mode}
items={[
{
label: '2015-09-01',
children: 'Create a services',
},
{
label: '2015-09-01 09:12:11',
children: 'Solve initial network problems',
},
{
children: 'Technical testing',
},
{
label: '2015-09-01 09:12:11',
children: 'Network problems being solved',
},
]}
/>
</>
);
};

View File

@ -10,11 +10,21 @@ const App: React.FC = () => {
return (
<div>
<Timeline pending="Recording..." reverse={reverse}>
<Timeline.Item>Create a services site 2015-09-01</Timeline.Item>
<Timeline.Item>Solve initial network problems 2015-09-01</Timeline.Item>
<Timeline.Item>Technical testing 2015-09-01</Timeline.Item>
</Timeline>
<Timeline
pending="Recording..."
reverse={reverse}
items={[
{
children: 'Create a services site 2015-09-01',
},
{
children: 'Solve initial network problems 2015-09-01',
},
{
children: 'Technical testing 2015-09-01',
},
]}
/>
<Button type="primary" style={{ marginTop: 16 }} onClick={handleClick}>
Toggle Reverse
</Button>

View File

@ -3,14 +3,25 @@ import { ClockCircleOutlined } from '@ant-design/icons';
import { Timeline } from 'antd';
const App: React.FC = () => (
<Timeline mode="right">
<Timeline.Item>Create a services site 2015-09-01</Timeline.Item>
<Timeline.Item>Solve initial network problems 2015-09-01</Timeline.Item>
<Timeline.Item dot={<ClockCircleOutlined style={{ fontSize: '16px' }} />} color="red">
Technical testing 2015-09-01
</Timeline.Item>
<Timeline.Item>Network problems being solved 2015-09-01</Timeline.Item>
</Timeline>
<Timeline
mode="right"
items={[
{
children: 'Create a services site 2015-09-01',
},
{
children: 'Solve initial network problems 2015-09-01',
},
{
dot: <ClockCircleOutlined style={{ fontSize: '16px' }} />,
color: 'red',
children: 'Technical testing 2015-09-01',
},
{
children: 'Network problems being solved 2015-09-01',
},
]}
/>
);
export default App;

View File

@ -3,12 +3,22 @@ import { ConfigProvider, Timeline } from 'antd';
const App: React.FC = () => (
<ConfigProvider theme={{ token: { wireframe: true } }}>
<Timeline>
<Timeline.Item>Create a services site 2015-09-01</Timeline.Item>
<Timeline.Item>Solve initial network problems 2015-09-01</Timeline.Item>
<Timeline.Item>Technical testing 2015-09-01</Timeline.Item>
<Timeline.Item>Network problems being solved 2015-09-01</Timeline.Item>
</Timeline>
<Timeline
items={[
{
children: 'Create a services site 2015-09-01',
},
{
children: 'Solve initial network problems 2015-09-01',
},
{
children: 'Technical testing 2015-09-01',
},
{
children: 'Network problems being solved 2015-09-01',
},
]}
/>
</ConfigProvider>
);

View File

@ -26,29 +26,37 @@ Vertical display timeline.
<code src="./demo/label.tsx">Label</code>
<code src="./demo/wireframe.tsx" debug>Wireframe</code>
## API
```__react
import Alert from '../alert';
ReactDOM.render(<Alert message="After version 5.3.0, we provide a simpler usage <Timeline items={[...]} /> with better performance and potential of writing simpler code style in your applications. Meanwhile, we deprecated the old usage in browser console, we will remove it in antd 6.0." />, mountNode);
```
```jsx
<Timeline>
<Timeline.Item>step1 2015-09-01</Timeline.Item>
<Timeline.Item>step2 2015-09-01</Timeline.Item>
<Timeline.Item>step3 2015-09-01</Timeline.Item>
<Timeline.Item>step4 2015-09-01</Timeline.Item>
</Timeline>
// works when >=5.3.0, recommended ✅
const items = [{ children: 'sample', label: 'sample' }];
return <Timeline items={items} />;
// works when <5.3.0, deprecated when >=5.3.0 🙅🏻‍♀️
<Timeline onChange={onChange}>
<Timeline.Item>Sample</Timeline.Item>
</Timeline>;
```
## API
### Timeline
Timeline
| Property | Description | Type | Default |
| --- | --- | --- | --- |
| --- | --- | --- | --- | --- |
| mode | By sending `alternate` the timeline will distribute the nodes to the left and right | `left` \| `alternate` \| `right` | - |
| pending | Set the last ghost node's existence or its content | boolean \| ReactNode | false |
| pendingDot | Set the dot of the last ghost node when pending is true | ReactNode | &lt;LoadingOutlined /> |
| reverse | Whether reverse nodes or not | boolean | false |
| items | 选项配置 | [Items](#Items) | [] | 5.3.0 |
### Timeline.Item
### Items
Node of timeline
@ -57,4 +65,5 @@ Node of timeline
| color | Set the circle's color to `blue`, `red`, `green`, `gray` or other custom colors | string | `blue` |
| dot | Customize timeline dot | ReactNode | - |
| label | Set the label | ReactNode | - |
| children | Set the content | ReactNode | - |
| position | Customize node position | `left` \| `right` | - |

View File

@ -27,29 +27,37 @@ demo:
<code src="./demo/label.tsx">标签</code>
<code src="./demo/wireframe.tsx" debug>线框风格</code>
## API
```__react
import Alert from '../alert';
ReactDOM.render(<Alert message="After version 5.3.0, we provide a simpler usage <Timeline items={[...]} /> with better performance and potential of writing simpler code style in your applications. Meanwhile, we deprecated the old usage in browser console, we will remove it in antd 6.0." />, mountNode);
```
```jsx
<Timeline>
<Timeline.Item>创建服务现场 2015-09-01</Timeline.Item>
<Timeline.Item>初步排除网络异常 2015-09-01</Timeline.Item>
<Timeline.Item>技术测试异常 2015-09-01</Timeline.Item>
<Timeline.Item>网络异常正在修复 2015-09-01</Timeline.Item>
</Timeline>
// >=5.3.0 可用,推荐的写法 ✅
const items = [{ children: 'sample', label: 'sample' }];
return <Timeline items={items} />;
// <5.3.0 可用>=5.3.0 时不推荐 🙅🏻‍♀️
<Timeline onChange={onChange}>
<Timeline.Item>Sample</Timeline.Item>
</Timeline>;
```
## API
### Timeline
时间轴。
| 参数 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| --- | --- | --- | --- | --- |
| mode | 通过设置 `mode` 可以改变时间轴和内容的相对位置 | `left` \| `alternate` \| `right` | - |
| pending | 指定最后一个幽灵节点是否存在或内容 | boolean \| ReactNode | false |
| pendingDot | 当最后一个幽灵节点存在時,指定其时间图点 | ReactNode | &lt;LoadingOutlined /> |
| reverse | 节点排序 | boolean | false |
| items | 选项配置 | [Items](#Items) | [] | 5.3.0 |
### Timeline.Item
### Items
时间轴的每一个节点。
@ -58,4 +66,5 @@ demo:
| color | 指定圆圈颜色 `blue`、`red`、`green`、`gray`,或自定义的色值 | string | `blue` |
| dot | 自定义时间轴点 | ReactNode | - |
| label | 设置标签 | ReactNode | - |
| children | 设置内容 | ReactNode | - |
| position | 自定义节点位置 | `left` \| `right` | - |

View File

@ -0,0 +1,14 @@
import type * as React from 'react';
import toArray from 'rc-util/lib/Children/toArray';
import type { TimelineItemProps } from './TimelineItem';
function useItems(items?: TimelineItemProps[], children?: React.ReactNode): TimelineItemProps[] {
if (items && Array.isArray(items)) return items;
return toArray(children).map((ele: React.ReactElement<any>) => ({
children: ele?.props?.children ?? '',
...ele.props,
}));
}
export default useItems;