UNPKG

@rc-component/drawer

Version:
274 lines (264 loc) 7.55 kB
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } import classNames from 'classnames'; import CSSMotion from '@rc-component/motion'; import KeyCode from "@rc-component/util/es/KeyCode"; import pickAttrs from "@rc-component/util/es/pickAttrs"; import * as React from 'react'; import DrawerContext from "./context"; import DrawerPanel from "./DrawerPanel"; import { parseWidthHeight } from "./util"; const sentinelStyle = { width: 0, height: 0, overflow: 'hidden', outline: 'none', position: 'absolute' }; function DrawerPopup(props, ref) { const { prefixCls, open, placement, inline, push, forceRender, autoFocus, keyboard, // classNames classNames: drawerClassNames, // Root rootClassName, rootStyle, zIndex, // Drawer className, id, style, motion, width, height, children, // Mask mask, maskClosable, maskMotion, maskClassName, maskStyle, // Events afterOpenChange, onClose, onMouseEnter, onMouseOver, onMouseLeave, onClick, onKeyDown, onKeyUp, styles, drawerRender } = props; // ================================ Refs ================================ const panelRef = React.useRef(null); const sentinelStartRef = React.useRef(null); const sentinelEndRef = React.useRef(null); React.useImperativeHandle(ref, () => panelRef.current); const onPanelKeyDown = event => { const { keyCode, shiftKey } = event; switch (keyCode) { // Tab active case KeyCode.TAB: { if (keyCode === KeyCode.TAB) { if (!shiftKey && document.activeElement === sentinelEndRef.current) { sentinelStartRef.current?.focus({ preventScroll: true }); } else if (shiftKey && document.activeElement === sentinelStartRef.current) { sentinelEndRef.current?.focus({ preventScroll: true }); } } break; } // Close case KeyCode.ESC: { if (onClose && keyboard) { event.stopPropagation(); onClose(event); } break; } } }; // ========================== Control =========================== // Auto Focus React.useEffect(() => { if (open && autoFocus) { panelRef.current?.focus({ preventScroll: true }); } }, [open]); // ============================ Push ============================ const [pushed, setPushed] = React.useState(false); const parentContext = React.useContext(DrawerContext); // Merge push distance let pushConfig; if (typeof push === 'boolean') { pushConfig = push ? {} : { distance: 0 }; } else { pushConfig = push || {}; } const pushDistance = pushConfig?.distance ?? parentContext?.pushDistance ?? 180; const mergedContext = React.useMemo(() => ({ pushDistance, push: () => { setPushed(true); }, pull: () => { setPushed(false); } }), [pushDistance]); // ========================= ScrollLock ========================= // Tell parent to push React.useEffect(() => { if (open) { parentContext?.push?.(); } else { parentContext?.pull?.(); } }, [open]); // Clean up React.useEffect(() => () => { parentContext?.pull?.(); }, []); // ============================ Mask ============================ const maskNode = mask && /*#__PURE__*/React.createElement(CSSMotion, _extends({ key: "mask" }, maskMotion, { visible: open }), ({ className: motionMaskClassName, style: motionMaskStyle }, maskRef) => /*#__PURE__*/React.createElement("div", { className: classNames(`${prefixCls}-mask`, motionMaskClassName, drawerClassNames?.mask, maskClassName), style: { ...motionMaskStyle, ...maskStyle, ...styles?.mask }, onClick: maskClosable && open ? onClose : undefined, ref: maskRef })); // =========================== Panel ============================ const motionProps = typeof motion === 'function' ? motion(placement) : motion; const wrapperStyle = {}; if (pushed && pushDistance) { switch (placement) { case 'top': wrapperStyle.transform = `translateY(${pushDistance}px)`; break; case 'bottom': wrapperStyle.transform = `translateY(${-pushDistance}px)`; break; case 'left': wrapperStyle.transform = `translateX(${pushDistance}px)`; break; default: wrapperStyle.transform = `translateX(${-pushDistance}px)`; break; } } if (placement === 'left' || placement === 'right') { wrapperStyle.width = parseWidthHeight(width); } else { wrapperStyle.height = parseWidthHeight(height); } const eventHandlers = { onMouseEnter, onMouseOver, onMouseLeave, onClick, onKeyDown, onKeyUp }; const panelNode = /*#__PURE__*/React.createElement(CSSMotion, _extends({ key: "panel" }, motionProps, { visible: open, forceRender: forceRender, onVisibleChanged: nextVisible => { afterOpenChange?.(nextVisible); }, removeOnLeave: false, leavedClassName: `${prefixCls}-content-wrapper-hidden` }), ({ className: motionClassName, style: motionStyle }, motionRef) => { const content = /*#__PURE__*/React.createElement(DrawerPanel, _extends({ id: id, containerRef: motionRef, prefixCls: prefixCls, className: classNames(className, drawerClassNames?.section), style: { ...style, ...styles?.section } }, pickAttrs(props, { aria: true }), eventHandlers), children); return /*#__PURE__*/React.createElement("div", _extends({ className: classNames(`${prefixCls}-content-wrapper`, drawerClassNames?.wrapper, motionClassName), style: { ...wrapperStyle, ...motionStyle, ...styles?.wrapper } }, pickAttrs(props, { data: true })), drawerRender ? drawerRender(content) : content); }); // =========================== Render =========================== const containerStyle = { ...rootStyle }; if (zIndex) { containerStyle.zIndex = zIndex; } return /*#__PURE__*/React.createElement(DrawerContext.Provider, { value: mergedContext }, /*#__PURE__*/React.createElement("div", { className: classNames(prefixCls, `${prefixCls}-${placement}`, rootClassName, { [`${prefixCls}-open`]: open, [`${prefixCls}-inline`]: inline }), style: containerStyle, tabIndex: -1, ref: panelRef, onKeyDown: onPanelKeyDown }, maskNode, /*#__PURE__*/React.createElement("div", { tabIndex: 0, ref: sentinelStartRef, style: sentinelStyle, "aria-hidden": "true", "data-sentinel": "start" }), panelNode, /*#__PURE__*/React.createElement("div", { tabIndex: 0, ref: sentinelEndRef, style: sentinelStyle, "aria-hidden": "true", "data-sentinel": "end" }))); } const RefDrawerPopup = /*#__PURE__*/React.forwardRef(DrawerPopup); if (process.env.NODE_ENV !== 'production') { RefDrawerPopup.displayName = 'DrawerPopup'; } export default RefDrawerPopup;