1
0

SideSheetContent.tsx 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. import React, { CSSProperties } from 'react';
  2. import PropTypes from 'prop-types';
  3. import cls from 'classnames';
  4. import { cssClasses } from '@douyinfe/semi-foundation/sideSheet/constants';
  5. import Button from '../iconButton';
  6. import { noop } from 'lodash';
  7. import { IconClose } from '@douyinfe/semi-icons';
  8. let uuid = 0;
  9. const prefixCls = cssClasses.PREFIX;
  10. export interface SideSheetContentProps {
  11. onClose?: (e: React.MouseEvent) => void;
  12. mask?: boolean;
  13. maskStyle?: CSSProperties;
  14. maskClosable?: boolean;
  15. title?: React.ReactNode;
  16. closable?: boolean;
  17. headerStyle?: CSSProperties;
  18. width: CSSProperties['width'];
  19. height: CSSProperties['height'];
  20. style: CSSProperties;
  21. bodyStyle?: CSSProperties;
  22. className: string;
  23. children?: React.ReactNode;
  24. footer?: React.ReactNode;
  25. 'aria-label'?: string;
  26. }
  27. export default class SideSheetContent extends React.PureComponent<SideSheetContentProps> {
  28. static propTypes = {
  29. onClose: PropTypes.func,
  30. };
  31. static defaultProps = {
  32. onClose: noop,
  33. };
  34. private sideSheetId: string;
  35. private timeoutId: number;
  36. componentDidMount() {
  37. this.sideSheetId = `sidesheet-${uuid++}`;
  38. }
  39. componentWillUnmount() {
  40. clearTimeout(this.timeoutId);
  41. }
  42. onMaskClick = (e: React.MouseEvent) => {
  43. if (e.target === e.currentTarget) {
  44. this.close(e);
  45. }
  46. };
  47. close = (e: React.MouseEvent) => {
  48. const { onClose } = this.props;
  49. onClose && onClose(e);
  50. };
  51. getMaskElement() {
  52. const {
  53. mask,
  54. maskStyle,
  55. maskClosable,
  56. } = this.props;
  57. if (mask) {
  58. return (
  59. <div
  60. aria-hidden={true}
  61. key="mask"
  62. className={`${prefixCls}-mask`}
  63. style={maskStyle}
  64. onClick={maskClosable ? this.onMaskClick : null}
  65. />
  66. );
  67. }
  68. return null;
  69. }
  70. renderHeader() {
  71. const {
  72. title,
  73. closable,
  74. headerStyle,
  75. } = this.props;
  76. let header, closer;
  77. if (title) {
  78. header = (
  79. <div className={`${prefixCls}-title`}>
  80. {this.props.title}
  81. </div>
  82. );
  83. }
  84. if (closable) {
  85. closer = (
  86. <Button
  87. className={`${prefixCls}-close`}
  88. key="close-btn"
  89. onClick={this.close}
  90. type="tertiary"
  91. icon={<IconClose/>}
  92. theme="borderless"
  93. size="small"
  94. />
  95. );
  96. }
  97. return (
  98. <div className={`${prefixCls}-header`} role={'heading'} aria-level={1} style={{ ...headerStyle }}>
  99. {header}
  100. {closer}
  101. </div>
  102. );
  103. }
  104. getDialogElement() {
  105. const { ...props } = this.props;
  106. const style: CSSProperties = {};
  107. if (props.width) {
  108. style.width = props.width;
  109. // When the mask is false, the width is set on the wrapper. At this time, sidesheet-inner does not need to set the width again, otherwise, the percentage will be accumulated repeatedly when the width is a percentage
  110. if (!props.mask) {
  111. style.width = '100%';
  112. }
  113. }
  114. if (props.height) {
  115. style.height = props.height;
  116. }
  117. const header = this.renderHeader();
  118. const dialogElement = (
  119. <div
  120. key="dialog-element"
  121. role="dialog"
  122. tabIndex={-1}
  123. className={`${prefixCls}-inner ${prefixCls}-inner-wrap`}
  124. // onMouseDown={this.onDialogMouseDown}
  125. style={{ ...props.style, ...style }}
  126. // id={this.dialogId}
  127. >
  128. <div className={`${prefixCls}-content`}>
  129. {header}
  130. <div className={`${prefixCls}-body`} style={props.bodyStyle}>
  131. {props.children}
  132. </div>
  133. {props.footer ? (
  134. <div className={`${prefixCls}-footer`}>
  135. {props.footer}
  136. </div>
  137. ) : null}
  138. </div>
  139. </div>
  140. );
  141. return dialogElement;
  142. }
  143. render() {
  144. const {
  145. mask,
  146. className,
  147. width,
  148. } = this.props;
  149. const wrapperCls = cls(className, {
  150. [`${prefixCls}-fixed`]: !mask,
  151. });
  152. const wrapperStyle: CSSProperties = {};
  153. if (!mask && width) {
  154. wrapperStyle.width = width;
  155. }
  156. return (
  157. <div className={wrapperCls} style={wrapperStyle}>
  158. {this.getMaskElement()}
  159. {this.getDialogElement()}
  160. </div>
  161. );
  162. }
  163. }