123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267 |
- /* eslint-disable eqeqeq */
- import React, { CSSProperties } from 'react';
- import PropTypes from 'prop-types';
- import cls from 'classnames';
- import { cssClasses } from '@douyinfe/semi-foundation/modal/constants';
- import ConfigContext from '../configProvider/context';
- import Button from '../iconButton';
- import Typography from '../typography';
- import BaseComponent from '../_base/baseComponent';
- // eslint-disable-next-line max-len
- import ModalContentFoundation, {
- ModalContentAdapter,
- ModalContentProps,
- ModalContentState
- } from '@douyinfe/semi-foundation/modal/modalContentFoundation';
- import { noop } from 'lodash-es';
- import { IconClose } from '@douyinfe/semi-icons';
- let uuid = 0;
- export default class ModalContent extends BaseComponent<ModalContentProps, ModalContentState> {
- static contextType = ConfigContext;
- static propTypes = {
- close: PropTypes.func,
- getContainerContext: PropTypes.func,
- };
- static defaultProps = {
- close: noop,
- getContainerContext: noop,
- };
- dialogId: string;
- private timeoutId: NodeJS.Timeout;
- constructor(props: ModalContentProps) {
- super(props);
- this.state = {
- dialogMouseDown: false,
- };
- this.foundation = new ModalContentFoundation(this.adapter);
- this.dialogId = `dialog-${uuid++}`;
- }
- get adapter(): ModalContentAdapter {
- return {
- ...super.adapter,
- notifyClose: (e: React.MouseEvent) => {
- this.props.onClose(e);
- },
- notifyDialogMouseDown: () => {
- this.setState({ dialogMouseDown: true });
- },
- notifyDialogMouseUp: () => {
- if (this.state.dialogMouseDown) {
- // Not setting setTimeout triggers close when modal external mouseUp
- this.timeoutId = setTimeout(() => {
- this.setState({ dialogMouseDown: false });
- }, 0);
- }
- },
- addKeyDownEventListener: () => {
- if (this.props.closeOnEsc) {
- document.addEventListener('keydown', this.foundation.handleKeyDown.bind(this.foundation));
- }
- },
- removeKeyDownEventListener: () => {
- if (this.props.closeOnEsc) {
- document.removeEventListener('keydown', this.foundation.handleKeyDown.bind(this.foundation));
- }
- },
- getMouseState: () => this.state.dialogMouseDown,
- };
- }
- componentDidMount() {
- this.foundation.handleKeyDownEventListenerMount();
- }
- componentWillUnmount() {
- clearTimeout(this.timeoutId);
- this.foundation.destroy();
- }
- onKeyDown = (e: React.MouseEvent) => {
- this.foundation.handleKeyDown(e);
- };
- // Record when clicking the modal box
- onDialogMouseDown = () => {
- this.foundation.handleDialogMouseDown();
- };
- // Cancel recording when clicking the modal box at the end
- onMaskMouseUp = () => {
- this.foundation.handleMaskMouseUp();
- };
- // onMaskClick will judge dialogMouseDown before onMaskMouseUp updates dialogMouseDown
- onMaskClick = (e: React.MouseEvent) => {
- this.foundation.handleMaskClick(e);
- };
- close = (e: React.MouseEvent) => {
- this.foundation.close(e);
- };
- getMaskElement = () => {
- const { ...props } = this.props;
- const { mask } = props;
- if (mask) {
- const className = cls(`${cssClasses.DIALOG}-mask`, {
- // [`${cssClasses.DIALOG}-mask-hidden`]: !props.visible,
- });
- return <div key="mask" className={className} style={props.maskStyle} />;
- }
- return null;
- };
- renderCloseBtn = () => {
- const {
- closable,
- closeIcon,
- } = this.props;
- let closer;
- if (closable) {
- const iconType = closeIcon || <IconClose />;
- closer = (
- <Button
- className={`${cssClasses.DIALOG}-close`}
- key="close-btn"
- onClick={this.close}
- type="tertiary"
- icon={iconType}
- theme="borderless"
- size="small"
- />
- );
- }
- return closer;
- };
- renderIcon = () => {
- const { icon } = this.props;
- return icon ? <span className={`${cssClasses.DIALOG}-icon-wrapper`}>{icon}</span> : null;
- };
- renderHeader = () => {
- if ('header' in this.props) {
- return this.props.header;
- }
- const { title } = this.props;
- const closer = this.renderCloseBtn();
- const icon = this.renderIcon();
- return (title === null || title === undefined) ?
- null :
- (
- <div className={`${cssClasses.DIALOG}-header`}>
- {icon}
- <Typography.Title heading={5} className={`${cssClasses.DIALOG}-title`}>{title}</Typography.Title>
- {closer}
- </div>
- );
- };
- renderBody = () => {
- const {
- bodyStyle,
- children,
- title,
- } = this.props;
- const bodyCls = cls(`${cssClasses.DIALOG}-body`, {
- [`${cssClasses.DIALOG}-withIcon`]: this.props.icon,
- });
- const closer = this.renderCloseBtn();
- const icon = this.renderIcon();
- const hasHeader = title !== null && title !== undefined || 'header' in this.props;
- return hasHeader ?
- <div className={bodyCls} style={bodyStyle}>{children}</div> :
- (
- <div className={`${cssClasses.DIALOG}-body-wrapper`}>
- {icon}
- <div className={bodyCls} style={bodyStyle}>{children}</div>
- {closer}
- </div>
- );
- };
- getDialogElement = () => {
- const { ...props } = this.props;
- const style: CSSProperties = {};
- const digCls = cls(`${cssClasses.DIALOG}`, {
- [`${cssClasses.DIALOG}-centered`]: props.centered,
- [`${cssClasses.DIALOG}-${props.size}`]: props.size,
- });
- if (props.width) {
- style.width = props.width;
- }
- if (props.height) {
- style.height = props.height;
- }
- if (props.isFullScreen) {
- style.width = '100%';
- style.height = '100%';
- style.margin = 'unset';
- }
- const body = this.renderBody();
- const header = this.renderHeader();
- const footer = props.footer ? <div className={`${cssClasses.DIALOG}-footer`}>{props.footer}</div> : null;
- const dialogElement = (
- <div
- key="dialog-element"
- className={digCls}
- onMouseDown={this.onDialogMouseDown}
- style={{ ...props.style, ...style }}
- id={this.dialogId}
- >
- <div className={cls([`${cssClasses.DIALOG}-content`,
- { [`${cssClasses.DIALOG}-content-fullScreen`]: props.isFullScreen }])}>
- {header}
- {body}
- {footer}
- </div>
- </div>
- );
- // return props.visible ? dialogElement : null;
- return dialogElement;
- };
- render() {
- const {
- maskClosable,
- className,
- getPopupContainer,
- maskFixed,
- getContainerContext,
- } = this.props;
- const { direction } = this.context;
- const classList = cls(className, {
- [`${cssClasses.DIALOG}-popup`]: getPopupContainer && !maskFixed,
- [`${cssClasses.DIALOG}-fixed`]: maskFixed,
- [`${cssClasses.DIALOG}-rtl`]: direction === 'rtl',
- });
- const containerContext = getContainerContext();
- const elem = (
- <div className={classList}>
- {this.getMaskElement()}
- <div
- role="modal"
- tabIndex={-1}
- className={`${cssClasses.DIALOG}-wrap`}
- onClick={maskClosable ? this.onMaskClick : null}
- onMouseUp={maskClosable ? this.onMaskMouseUp : null}
- >
- {this.getDialogElement()}
- </div>
- </div>
- );
- // @ts-ignore Unreachable branch
- // eslint-disable-next-line max-len
- return containerContext && containerContext.Provider ? <containerContext.Provider value={containerContext.value}>{elem}</containerContext.Provider> : elem;
- }
- }
|