item.tsx 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. import React, { CSSProperties, PureComponent, ReactNode } from 'react';
  2. import cls from 'classnames';
  3. import PropTypes from 'prop-types';
  4. import { cssClasses } from '@douyinfe/semi-foundation/collapse/constants';
  5. import Collapsible from '../collapsible';
  6. import CollapseContext, { CollapseContextType } from './collapse-context';
  7. import { IconChevronDown, IconChevronUp } from '@douyinfe/semi-icons';
  8. import { getUuidShort } from '@douyinfe/semi-foundation/utils/uuid';
  9. export interface CollapsePanelProps {
  10. itemKey: string;
  11. extra?: ReactNode;
  12. header?: ReactNode;
  13. className?: string;
  14. children?: React.ReactNode;
  15. reCalcKey?: number | string;
  16. style?: CSSProperties;
  17. }
  18. export default class CollapsePanel extends PureComponent<CollapsePanelProps> {
  19. static contextType: React.Context<CollapseContextType> = CollapseContext;
  20. static propTypes = {
  21. itemKey: PropTypes.string,
  22. extra: PropTypes.node,
  23. header: PropTypes.oneOfType([
  24. PropTypes.string,
  25. PropTypes.node,
  26. ]),
  27. className: PropTypes.string,
  28. reCalcKey: PropTypes.oneOfType([
  29. PropTypes.string,
  30. PropTypes.number,
  31. ]),
  32. };
  33. private ariaID = getUuidShort({});
  34. context: CollapseContextType;
  35. renderHeader(active: boolean, expandIconEnable = true) {
  36. const {
  37. header,
  38. extra,
  39. } = this.props;
  40. let {
  41. expandIcon,
  42. collapseIcon,
  43. } = this.context;
  44. const { expandIconPosition } = this.context;
  45. if (typeof expandIcon === 'undefined') {
  46. expandIcon = (<IconChevronDown/>);
  47. }
  48. if (typeof collapseIcon === 'undefined') {
  49. collapseIcon = (<IconChevronUp/>);
  50. }
  51. const icon = (
  52. <span aria-hidden='true' className={cls([`${cssClasses.PREFIX}-header-icon`,
  53. { [`${cssClasses.PREFIX}-header-iconDisabled`]: !expandIconEnable }])}>
  54. {/* eslint-disable-next-line no-nested-ternary */}
  55. {expandIconEnable ? (active ? collapseIcon : expandIcon) : expandIcon}
  56. </span>
  57. );
  58. const iconPosLeft = expandIconPosition === 'left';
  59. if (typeof header === 'string') {
  60. return (
  61. <>
  62. {iconPosLeft ? icon : null}
  63. <span>{header}</span>
  64. <span className={`${cssClasses.PREFIX}-header-right`}>
  65. <span>{extra}</span>
  66. {iconPosLeft ? null : icon}
  67. </span>
  68. </>
  69. );
  70. }
  71. return (
  72. <>
  73. {iconPosLeft ? icon : null}
  74. {header}
  75. {iconPosLeft ? null : icon}
  76. </>
  77. );
  78. }
  79. render() {
  80. const {
  81. className,
  82. children,
  83. itemKey,
  84. reCalcKey,
  85. header,
  86. extra,
  87. ...restProps
  88. } = this.props;
  89. const {
  90. keepDOM,
  91. expandIconPosition,
  92. activeSet,
  93. onClick,
  94. motion,
  95. } = this.context;
  96. const active = activeSet.has(itemKey);
  97. const itemCls = cls(className, {
  98. [`${cssClasses.PREFIX}-item`]: true,
  99. });
  100. const headerCls = cls({
  101. [`${cssClasses.PREFIX}-header`]: true,
  102. [`${cssClasses.PREFIX}-header-iconLeft`]: expandIconPosition === 'left',
  103. });
  104. const contentCls = cls({
  105. [`${cssClasses.PREFIX}-content`]: true,
  106. });
  107. return (
  108. <div
  109. className={itemCls}
  110. {...restProps}
  111. >
  112. <div
  113. role="button"
  114. tabIndex={0}
  115. className={headerCls}
  116. aria-expanded={active ? 'true' : 'false'}
  117. aria-owns={this.ariaID}
  118. onClick={e => onClick(itemKey, e)}
  119. >
  120. {this.renderHeader(active, children !== undefined)}
  121. </div>
  122. {
  123. children && (
  124. <Collapsible
  125. isOpen={active} keepDOM={keepDOM} motion={motion}
  126. reCalcKey={reCalcKey}>
  127. <div
  128. className={contentCls}
  129. aria-hidden={!active}
  130. id={this.ariaID}
  131. >
  132. <div className={`${cssClasses.PREFIX}-content-wrapper`}>
  133. {children}
  134. </div>
  135. </div>
  136. </Collapsible>
  137. )
  138. }
  139. </div>
  140. );
  141. }
  142. }