1
0

index.tsx 3.2 KB

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