@rc-component/drawer
Version:
drawer component for react
274 lines (264 loc) • 7.55 kB
JavaScript
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;