item.tsx 4.7 KB

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