import * as React from 'react'; import * as PropTypes from 'prop-types'; import RcDrawer from 'rc-drawer'; import createReactContext, { Context } from '@ant-design/create-react-context'; import warning from '../_util/warning'; import classNames from 'classnames'; import Icon from '../icon'; import { withConfigConsumer, ConfigConsumerProps } from '../config-provider'; import { tuple } from '../_util/type'; const DrawerContext: Context = createReactContext(null); type EventType = React.MouseEvent | React.MouseEvent; type getContainerFunc = () => HTMLElement; const PlacementTypes = tuple('top', 'right', 'bottom', 'left'); type placementType = (typeof PlacementTypes)[number]; export interface DrawerProps { closable?: boolean; destroyOnClose?: boolean; getContainer?: string | HTMLElement | getContainerFunc; maskClosable?: boolean; mask?: boolean; maskStyle?: React.CSSProperties; style?: React.CSSProperties; bodyStyle?: React.CSSProperties; title?: React.ReactNode; visible?: boolean; width?: number | string; height?: number | string; /* deprecated, use className instead */ wrapClassName?: string; zIndex?: number; prefixCls?: string; push?: boolean; placement?: placementType; onClose?: (e: EventType) => void; afterVisibleChange?: (visible: boolean) => void; className?: string; handler?: React.ReactNode; } export interface IDrawerState { push?: boolean; } class Drawer extends React.Component { static propTypes = { closable: PropTypes.bool, destroyOnClose: PropTypes.bool, getContainer: PropTypes.oneOfType([ PropTypes.string, PropTypes.object as PropTypes.Requireable, PropTypes.func, PropTypes.bool, ]), maskClosable: PropTypes.bool, mask: PropTypes.bool, maskStyle: PropTypes.object, style: PropTypes.object, title: PropTypes.node, visible: PropTypes.bool, width: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), zIndex: PropTypes.number, prefixCls: PropTypes.string, placement: PropTypes.oneOf(PlacementTypes), onClose: PropTypes.func, afterVisibleChange: PropTypes.func, className: PropTypes.string, }; static defaultProps = { width: 256, height: 256, closable: true, placement: 'right' as placementType, maskClosable: true, mask: true, level: null, }; readonly state = { push: false, }; parentDrawer: Drawer; destroyClose: boolean; public componentDidUpdate(preProps: DrawerProps) { const { visible } = this.props; if (preProps.visible !== visible && this.parentDrawer) { if (visible) { this.parentDrawer.push(); } else { this.parentDrawer.pull(); } } } close = (e: EventType) => { const { visible, onClose } = this.props; if (visible !== undefined && onClose) { onClose(e); } }; onMaskClick = (e: EventType) => { if (!this.props.maskClosable) { return; } this.close(e); }; push = () => { this.setState({ push: true, }); }; pull = () => { this.setState({ push: false, }); }; onDestroyTransitionEnd = () => { const isDestroyOnClose = this.getDestroyOnClose(); if (!isDestroyOnClose) { return; } if (!this.props.visible) { this.destroyClose = true; this.forceUpdate(); } }; getDestroyOnClose = () => this.props.destroyOnClose && !this.props.visible; // get drawar push width or height getPushTransform = (placement?: placementType) => { if (placement === 'left' || placement === 'right') { return `translateX(${placement === 'left' ? 180 : -180}px)`; } if (placement === 'top' || placement === 'bottom') { return `translateY(${placement === 'top' ? 180 : -180}px)`; } }; getRcDrawerStyle = () => { const { zIndex, placement, style } = this.props; const { push } = this.state; return { zIndex, transform: push ? this.getPushTransform(placement) : undefined, ...style, }; }; renderHeader() { const { title, prefixCls, closable } = this.props; if (!title && !closable) { return null; } const headerClassName = title ? `${prefixCls}-header` : `${prefixCls}-header-no-title`; return (
{title &&
{title}
} {closable && this.renderCloseIcon()}
); } renderCloseIcon() { const { closable, prefixCls } = this.props; return ( closable && ( ) ); } // render drawer body dom renderBody = () => { const { bodyStyle, placement, prefixCls, visible } = this.props; if (this.destroyClose && !visible) { return null; } this.destroyClose = false; const containerStyle: React.CSSProperties = placement === 'left' || placement === 'right' ? { overflow: 'auto', height: '100%', } : {}; const isDestroyOnClose = this.getDestroyOnClose(); if (isDestroyOnClose) { // Increase the opacity transition, delete children after closing. containerStyle.opacity = 0; containerStyle.transition = 'opacity .3s'; } return (
{this.renderHeader()}
{this.props.children}
); }; // render Provider for Multi-level drawe renderProvider = (value: Drawer) => { const { prefixCls, zIndex, style, placement, className, wrapClassName, width, height, ...rest } = this.props; warning( wrapClassName === undefined, 'Drawer', 'wrapClassName is deprecated, please use className instead.', ); const haveMask = rest.mask ? '' : 'no-mask'; this.parentDrawer = value; const offsetStyle: any = {}; if (placement === 'left' || placement === 'right') { offsetStyle.width = width; } else { offsetStyle.height = height; } return ( {this.renderBody()} ); }; render() { return {this.renderProvider}; } } export default withConfigConsumer({ prefixCls: 'drawer', })(Drawer);