item.tsx 5.2 KB

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