index.tsx 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. import React, { CSSProperties } from 'react';
  2. import cls from 'classnames';
  3. import PropTypes from 'prop-types';
  4. import { cssClasses, strings } from '@douyinfe/semi-foundation/collapse/constants';
  5. import CollapseFoundation, {
  6. ArgsType,
  7. CollapseAdapter,
  8. CollapseProps,
  9. CollapseState,
  10. } from '@douyinfe/semi-foundation/collapse/foundation';
  11. import BaseComponent from '../_base/baseComponent';
  12. import CollapsePanel from './item';
  13. import '@douyinfe/semi-foundation/collapse/collapse.scss';
  14. import { noop } from '@douyinfe/semi-foundation/utils/function';
  15. import { isEqual } from 'lodash';
  16. import CollapseContext from './collapse-context';
  17. import { getDefaultPropsFromGlobalConfig } from '../_utils';
  18. export type { CollapsePanelProps } from './item';
  19. export interface CollapseReactProps extends CollapseProps {
  20. expandIcon?: React.ReactNode;
  21. collapseIcon?: React.ReactNode;
  22. children?: React.ReactNode;
  23. style?: CSSProperties;
  24. onChange?: (activeKey: CollapseProps['activeKey'], e: React.MouseEvent) => void;
  25. lazyRender?: boolean
  26. }
  27. export type { CollapseState };
  28. class Collapse extends BaseComponent<CollapseReactProps, CollapseState> {
  29. static Panel = CollapsePanel;
  30. static propTypes = {
  31. activeKey: PropTypes.oneOfType([PropTypes.string, PropTypes.array]),
  32. defaultActiveKey: PropTypes.oneOfType([PropTypes.string, PropTypes.array]),
  33. accordion: PropTypes.bool,
  34. clickHeaderToExpand: PropTypes.bool,
  35. onChange: PropTypes.func,
  36. expandIcon: PropTypes.node,
  37. collapseIcon: PropTypes.node,
  38. style: PropTypes.object,
  39. className: PropTypes.string,
  40. keepDOM: PropTypes.bool,
  41. motion: PropTypes.oneOfType([PropTypes.bool, PropTypes.func, PropTypes.object]),
  42. expandIconPosition: PropTypes.oneOf(strings.iconPosition),
  43. lazyRender: PropTypes.bool,
  44. };
  45. static __SemiComponentName__ = 'Collapse';
  46. static defaultProps = getDefaultPropsFromGlobalConfig(Collapse.__SemiComponentName__, {
  47. defaultActiveKey: '',
  48. clickHeaderToExpand: true,
  49. onChange: noop,
  50. expandIconPosition: 'right',
  51. lazyRender: false,
  52. });
  53. constructor(props: CollapseReactProps) {
  54. super(props);
  55. this.foundation = new CollapseFoundation(this.adapter);
  56. const initKeys = this.foundation.initActiveKey();
  57. this.state = {
  58. activeSet: new Set(initKeys),
  59. };
  60. this.onChange = this.onChange.bind(this);
  61. }
  62. get adapter(): CollapseAdapter {
  63. return {
  64. ...super.adapter,
  65. handleChange: (activeKey: CollapseProps['activeKey'], e: React.MouseEvent) => this.props.onChange(activeKey, e),
  66. addActiveKey: (activeSet: CollapseState['activeSet']) => this.setState({ activeSet }),
  67. };
  68. }
  69. static getDerivedStateFromProps(props: CollapseReactProps, state: CollapseState) {
  70. if (props.activeKey) {
  71. const keys = Array.isArray(props.activeKey) ? props.activeKey : [props.activeKey];
  72. const newSet = new Set(keys);
  73. if (!isEqual(newSet, state.activeSet)) {
  74. return {
  75. ...state,
  76. activeSet: newSet,
  77. };
  78. }
  79. return state;
  80. }
  81. return state;
  82. }
  83. componentWillUnmount() {
  84. this.foundation.destroy();
  85. }
  86. onChange = (activeKey: string, e: React.MouseEvent) => {
  87. this.foundation.handleChange(activeKey, e);
  88. };
  89. render() {
  90. const {
  91. defaultActiveKey,
  92. lazyRender,
  93. accordion,
  94. style,
  95. motion,
  96. className,
  97. keepDOM,
  98. expandIconPosition,
  99. expandIcon,
  100. collapseIcon,
  101. children,
  102. clickHeaderToExpand,
  103. ...rest
  104. } = this.props;
  105. const clsPrefix = cls(cssClasses.PREFIX, className);
  106. const { activeSet } = this.state;
  107. return (
  108. <div className={clsPrefix} style={style} {...this.getDataAttr(this.props)}>
  109. <CollapseContext.Provider
  110. value={{
  111. activeSet,
  112. expandIcon,
  113. collapseIcon,
  114. clickHeaderToExpand,
  115. keepDOM,
  116. expandIconPosition,
  117. onClick: this.onChange,
  118. motion,
  119. lazyRender,
  120. }}
  121. >
  122. {children}
  123. </CollapseContext.Provider>
  124. </div>
  125. );
  126. }
  127. }
  128. export default Collapse;