index.tsx 3.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. import React, { PureComponent } from 'react';
  2. import cls from 'classnames';
  3. import PropTypes from 'prop-types';
  4. import '@douyinfe/semi-foundation/timeline/timeline.scss';
  5. import { cssClasses, strings } from '@douyinfe/semi-foundation/timeline/constants';
  6. import ConfigContext from '../configProvider/context';
  7. import Item, { TimelineItemProps } from './item';
  8. export type { TimelineItemProps } from './item';
  9. export interface Data extends TimelineItemProps {
  10. content: React.ReactNode
  11. }
  12. export interface TimelineProps extends Pick<React.AriaAttributes, 'aria-label'> {
  13. mode?: 'left' | 'right' | 'center' | 'alternate';
  14. className?: string;
  15. style?: React.CSSProperties;
  16. dataSource?: Data[];
  17. children?: React.ReactNode
  18. }
  19. const prefixCls = cssClasses.PREFIX;
  20. class Timeline extends PureComponent<TimelineProps> {
  21. static contextType = ConfigContext;
  22. static Item = Item;
  23. static propTypes = {
  24. mode: PropTypes.oneOf(strings.MODE),
  25. className: PropTypes.string,
  26. style: PropTypes.object,
  27. dataSource: PropTypes.array,
  28. };
  29. static defaultProps = {
  30. mode: 'left',
  31. };
  32. getPosCls = (ele: React.ReactElement, idx: number) => {
  33. const { mode } = this.props;
  34. if (mode === 'alternate') {
  35. if (ele.props.position) {
  36. return `${prefixCls}-item-${ele.props.position}`;
  37. }
  38. return idx % 2 === 0 ? `${prefixCls}-item-left` : `${prefixCls}-item-right`;
  39. }
  40. if (mode === 'center') {
  41. if (ele.props.position) {
  42. return `${prefixCls}-item-${ele.props.position}`;
  43. }
  44. return `${prefixCls}-item-left`;
  45. }
  46. if (mode === 'left' || mode === 'right') {
  47. return `${prefixCls}-item-${mode}`;
  48. }
  49. if (ele.props.position) {
  50. return `${prefixCls}-item-${ele.props.position}`;
  51. }
  52. return '';
  53. };
  54. addClassName = (items: React.ReactNode) => React.Children.map(items, (ele, idx) => {
  55. if (React.isValidElement(ele)) {
  56. return React.cloneElement(ele, {
  57. // @ts-ignore
  58. className: cls(
  59. ele.props.className,
  60. this.getPosCls(ele, idx)
  61. ),
  62. });
  63. }
  64. return ele;
  65. });
  66. render() {
  67. const { children, className, style, mode, dataSource } = this.props;
  68. const classString = cls(
  69. prefixCls,
  70. className,
  71. { [`${prefixCls}-${mode}`]: mode }
  72. );
  73. let childrenList;
  74. if (dataSource && dataSource.length) {
  75. const items = dataSource.map(
  76. (item, index) => <Item key={`timeline-item-${index}`} {...item}>{item.content}</Item>
  77. );
  78. childrenList = this.addClassName(items);
  79. }
  80. const items = childrenList || this.addClassName(children);
  81. return (
  82. <ul aria-label={this.props['aria-label']} style={style} className={classString}>
  83. {items}
  84. </ul>
  85. );
  86. }
  87. }
  88. export default Timeline;