1
This commit is contained in:
+22
@@ -0,0 +1,22 @@
|
||||
MIT LICENSE
|
||||
|
||||
Copyright (c) 2015-present Alipay.com, https://www.alipay.com/
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
# @rc-component/drawer
|
||||
|
||||
[![NPM version][npm-image]][npm-url]
|
||||
[![npm download][download-image]][download-url]
|
||||
[![build status][github-actions-image]][github-actions-url]
|
||||
[![codecov][codecov-image]][codecov-url]
|
||||
[![dumi][dumi-image]][dumi-url]
|
||||
|
||||
[npm-image]: http://img.shields.io/npm/v/@rc-component/drawer.svg?style=flat-square
|
||||
[npm-url]: http://npmjs.org/package/@rc-component/drawer
|
||||
[dumi-image]: https://img.shields.io/badge/docs%20by-dumi-blue?style=flat-square
|
||||
[dumi-url]: https://github.com/umijs/dumi
|
||||
[github-actions-image]: https://github.com/react-component/drawer/actions/workflows/react-component-ci.yml/badge.svg
|
||||
[github-actions-url]: https://github.com/react-component/drawer/actions/workflows/react-component-ci.yml
|
||||
[download-image]: https://img.shields.io/npm/dm/@rc-component/drawer.svg?style=flat-square
|
||||
[download-url]: https://npmjs.org/package/@rc-component/drawer
|
||||
[codecov-image]: https://codecov.io/gh/react-component/drawer/branch/master/graph/badge.svg
|
||||
[codecov-url]: https://codecov.io/gh/react-component/drawer
|
||||
|
||||
|
||||
## Example
|
||||
|
||||
https://drawer-react-component.vercel.app/
|
||||
|
||||
## Usage
|
||||
|
||||
```js
|
||||
import Drawer from '@rc-component/drawer';
|
||||
import React from 'react';
|
||||
import ReactDom from 'react-dom';
|
||||
|
||||
ReactDom.render(
|
||||
<Drawer>
|
||||
{menu children}
|
||||
</Drawer>
|
||||
, mountNode);
|
||||
```
|
||||
|
||||
## Install
|
||||
|
||||
[](https://npmjs.org/package/@rc-component/drawer)
|
||||
|
||||
## Browser Support
|
||||
|
||||
|  |  |  |  |  |
|
||||
| ------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
|
||||
| IE 10+ ✔ | Chrome 31.0+ ✔ | Firefox 31.0+ ✔ | Opera 30.0+ ✔ | Safari 7.0+ ✔ |
|
||||
|
||||
## API
|
||||
|
||||
| props | type | default | description |
|
||||
| ------------------ | --------------------------------------------------------------------------- | -------------------------------------- | ----------------------------------------------------------------------------- |
|
||||
| className | string | null | - |
|
||||
| classNames | { mask?: string; content?: string; wrapper?: string; } | - | pass className to target area |
|
||||
| styles | { mask?: CSSProperties; content?: CSSProperties; wrapper?: CSSProperties; } | `rc-drawer` | pass style to target area |
|
||||
| prefixCls | string | 'drawer' | prefix class |
|
||||
| width | string \| number | null | drawer content wrapper width, drawer level transition width |
|
||||
| height | string \| number | null | drawer content wrapper height, drawer level transition height |
|
||||
| defaultWidth | string \| number | null | default width for uncontrolled resizable drawer |
|
||||
| defaultHeight | string \| number | null | default height for uncontrolled resizable drawer |
|
||||
| open | boolean | false | open or close menu |
|
||||
| defaultOpen | boolean | false | default open menu |
|
||||
| placement | string | `left` | `left` `top` `right` `bottom` |
|
||||
| level | string \| array | `all` | With the drawer level element. `all`/ null / className / id / tagName / array |
|
||||
| levelMove | number \| array \| func | null | level move value. default is drawer width |
|
||||
| duration | string | `.3s` | level animation duration |
|
||||
| ease | string | `cubic-bezier(0.78, 0.14, 0.15, 0.86)` | level animation timing function |
|
||||
| getContainer | string \| func \| HTMLElement | `body` | Return the mount node for Drawer. if is `null` use React.creactElement |
|
||||
| showMask | boolean | true | mask is show |
|
||||
| maskClosable | boolean | true | Clicking on the mask (area outside the Drawer) to close the Drawer or not. |
|
||||
| maskStyle | CSSProperties | null | mask style |
|
||||
| afterOpenChange | func | null | transition end callback(open) |
|
||||
| onClose | func | null | close click function |
|
||||
| keyboard | boolean | true | Whether support press esc to close |
|
||||
| autoFocus | boolean | true | Whether focusing on the drawer after it opened |
|
||||
| resizable | { onResize?: (size: number) => void; onResizeStart?: () => void; onResizeEnd?: () => void; } | - | Resizable configuration with optional callbacks |
|
||||
| onMouseEnter | React.MouseEventHandler\<HTMLDivElement\> | - | Trigger when mouse enter drawer panel |
|
||||
| onMouseOver | React.MouseEventHandler\<HTMLDivElement\> | - | Trigger when mouse over drawer panel |
|
||||
| onMouseLeave | React.MouseEventHandler\<HTMLDivElement\> | - | Trigger when mouse leave drawer panel |
|
||||
| onClick | React.MouseEventHandler\<HTMLDivElement\> | - | Trigger when mouse click drawer panel |
|
||||
| onKeyDown | React.MouseEventHandler\<HTMLDivElement\> | - | Trigger when mouse keydown on drawer panel |
|
||||
| onKeyUp | React.MouseEventHandler\<HTMLDivElement\> | - | Trigger when mouse keyup on drawer panel |
|
||||
|
||||
> 2.0 Rename `onMaskClick` -> `onClose`, add `maskClosable`.
|
||||
|
||||
## Development
|
||||
|
||||
```
|
||||
npm install
|
||||
npm start
|
||||
```
|
||||
+273
@@ -0,0 +1,273 @@
|
||||
.rc-drawer {
|
||||
inset: 0;
|
||||
position: fixed;
|
||||
z-index: 1050;
|
||||
pointer-events: none;
|
||||
}
|
||||
.rc-drawer-inline {
|
||||
position: absolute;
|
||||
}
|
||||
.rc-drawer-mask {
|
||||
inset: 0;
|
||||
position: absolute;
|
||||
z-index: 1050;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
pointer-events: auto;
|
||||
}
|
||||
.rc-drawer-content-wrapper {
|
||||
position: absolute;
|
||||
z-index: 1050;
|
||||
transition: transform 0.3s;
|
||||
pointer-events: auto;
|
||||
}
|
||||
.rc-drawer-content-wrapper-hidden {
|
||||
display: none;
|
||||
}
|
||||
.rc-drawer-left .rc-drawer-content-wrapper {
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
}
|
||||
.rc-drawer-right .rc-drawer-content-wrapper {
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
.rc-drawer-content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
background: #fff;
|
||||
pointer-events: auto;
|
||||
}
|
||||
.rc-drawer-resizable-dragger {
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
pointer-events: auto;
|
||||
}
|
||||
.rc-drawer-resizable-dragger:hover {
|
||||
background-color: #1890ff !important;
|
||||
}
|
||||
.rc-drawer-resizable-dragger-dragging {
|
||||
background-color: #1890ff !important;
|
||||
}
|
||||
.rc-drawer-resizable-dragger-left {
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: -3px;
|
||||
width: 6px;
|
||||
cursor: ew-resize;
|
||||
}
|
||||
.rc-drawer-resizable-dragger-right {
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: -3px;
|
||||
width: 6px;
|
||||
cursor: ew-resize;
|
||||
}
|
||||
.rc-drawer-resizable-dragger-top {
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: -3px;
|
||||
height: 6px;
|
||||
cursor: ns-resize;
|
||||
}
|
||||
.rc-drawer-resizable-dragger-bottom {
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: -3px;
|
||||
height: 6px;
|
||||
cursor: ns-resize;
|
||||
}
|
||||
.rc-drawer {
|
||||
overflow: hidden;
|
||||
z-index: 9999;
|
||||
transition: width 0s ease 0.3s, height 0s ease 0.3s, transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
|
||||
}
|
||||
.rc-drawer > * {
|
||||
transition: transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86), opacity 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86), box-shadow 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
|
||||
}
|
||||
.rc-drawer.rc-drawer-open {
|
||||
transition: transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
|
||||
}
|
||||
.rc-drawer .rc-drawer-mask {
|
||||
background: #000;
|
||||
opacity: 0;
|
||||
width: 100%;
|
||||
height: 0;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
transition: opacity 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86), height 0s ease 0.3s;
|
||||
}
|
||||
.rc-drawer-content-wrapper {
|
||||
position: absolute;
|
||||
background: #fff;
|
||||
}
|
||||
.rc-drawer-content {
|
||||
overflow: auto;
|
||||
z-index: 1;
|
||||
position: relative;
|
||||
}
|
||||
.rc-drawer-handle {
|
||||
position: absolute;
|
||||
top: 72px;
|
||||
width: 41px;
|
||||
height: 40px;
|
||||
cursor: pointer;
|
||||
z-index: 0;
|
||||
text-align: center;
|
||||
line-height: 40px;
|
||||
font-size: 16px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: #fff;
|
||||
}
|
||||
.rc-drawer-handle-icon {
|
||||
width: 14px;
|
||||
height: 2px;
|
||||
background: #333;
|
||||
position: relative;
|
||||
transition: background 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
|
||||
}
|
||||
.rc-drawer-handle-icon::before,
|
||||
.rc-drawer-handle-icon::after {
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
background: #333;
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
transition: transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
|
||||
}
|
||||
.rc-drawer-handle-icon::before {
|
||||
top: -5px;
|
||||
}
|
||||
.rc-drawer-handle-icon::after {
|
||||
top: 5px;
|
||||
}
|
||||
.rc-drawer-left,
|
||||
.rc-drawer-right {
|
||||
width: 0%;
|
||||
height: 100%;
|
||||
}
|
||||
.rc-drawer-left .rc-drawer-content-wrapper,
|
||||
.rc-drawer-right .rc-drawer-content-wrapper,
|
||||
.rc-drawer-left .rc-drawer-content,
|
||||
.rc-drawer-right .rc-drawer-content {
|
||||
height: 100%;
|
||||
}
|
||||
.rc-drawer-left.rc-drawer-open,
|
||||
.rc-drawer-right.rc-drawer-open {
|
||||
width: 100%;
|
||||
}
|
||||
.rc-drawer-left.rc-drawer-open.no-mask,
|
||||
.rc-drawer-right.rc-drawer-open.no-mask {
|
||||
width: 0%;
|
||||
}
|
||||
.rc-drawer-left {
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
.rc-drawer-left .rc-drawer-handle {
|
||||
right: -40px;
|
||||
box-shadow: 2px 0 8px rgba(0, 0, 0, 0.15);
|
||||
border-radius: 0 4px 4px 0;
|
||||
}
|
||||
.rc-drawer-left.rc-drawer-open .rc-drawer-content-wrapper {
|
||||
box-shadow: 2px 0 8px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
.rc-drawer-right {
|
||||
top: 0;
|
||||
right: 0;
|
||||
}
|
||||
.rc-drawer-right .rc-drawer-content-wrapper {
|
||||
right: 0;
|
||||
}
|
||||
.rc-drawer-right .rc-drawer-handle {
|
||||
left: -40px;
|
||||
box-shadow: -2px 0 8px rgba(0, 0, 0, 0.15);
|
||||
border-radius: 4px 0 0 4px;
|
||||
}
|
||||
.rc-drawer-right.rc-drawer-open .rc-drawer-content-wrapper {
|
||||
box-shadow: -2px 0 8px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
.rc-drawer-right.rc-drawer-open.no-mask {
|
||||
right: 1px;
|
||||
transform: translateX(1px);
|
||||
}
|
||||
.rc-drawer-top,
|
||||
.rc-drawer-bottom {
|
||||
width: 100%;
|
||||
height: 0%;
|
||||
}
|
||||
.rc-drawer-top .rc-drawer-content-wrapper,
|
||||
.rc-drawer-bottom .rc-drawer-content-wrapper,
|
||||
.rc-drawer-top .rc-drawer-content,
|
||||
.rc-drawer-bottom .rc-drawer-content {
|
||||
width: 100%;
|
||||
}
|
||||
.rc-drawer-top .rc-drawer-content,
|
||||
.rc-drawer-bottom .rc-drawer-content {
|
||||
height: 100%;
|
||||
}
|
||||
.rc-drawer-top.rc-drawer-open,
|
||||
.rc-drawer-bottom.rc-drawer-open {
|
||||
height: 100%;
|
||||
}
|
||||
.rc-drawer-top.rc-drawer-open.no-mask,
|
||||
.rc-drawer-bottom.rc-drawer-open.no-mask {
|
||||
height: 0%;
|
||||
}
|
||||
.rc-drawer-top .rc-drawer-handle,
|
||||
.rc-drawer-bottom .rc-drawer-handle {
|
||||
left: 50%;
|
||||
margin-left: -20px;
|
||||
}
|
||||
.rc-drawer-top {
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
.rc-drawer-top .rc-drawer-handle {
|
||||
top: auto;
|
||||
bottom: -40px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
||||
border-radius: 0 0 4px 4px;
|
||||
}
|
||||
.rc-drawer-top.rc-drawer-open .rc-drawer-content-wrapper {
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
.rc-drawer-bottom {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
}
|
||||
.rc-drawer-bottom .rc-drawer-content-wrapper {
|
||||
bottom: 0;
|
||||
}
|
||||
.rc-drawer-bottom .rc-drawer-handle {
|
||||
top: -40px;
|
||||
box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.15);
|
||||
border-radius: 4px 4px 0 0;
|
||||
}
|
||||
.rc-drawer-bottom.rc-drawer-open .rc-drawer-content-wrapper {
|
||||
box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
.rc-drawer-bottom.rc-drawer-open.no-mask {
|
||||
bottom: 1px;
|
||||
transform: translateY(1px);
|
||||
}
|
||||
.rc-drawer.rc-drawer-open .rc-drawer-mask {
|
||||
opacity: 0.3;
|
||||
height: 100%;
|
||||
transition: opacity 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
|
||||
}
|
||||
.rc-drawer.rc-drawer-open .rc-drawer-handle-icon {
|
||||
background: transparent;
|
||||
}
|
||||
.rc-drawer.rc-drawer-open .rc-drawer-handle-icon::before {
|
||||
transform: translateY(5px) rotate(45deg);
|
||||
}
|
||||
.rc-drawer.rc-drawer-open .rc-drawer-handle-icon::after {
|
||||
transform: translateY(-5px) rotate(-45deg);
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
import type { PortalProps } from '@rc-component/portal';
|
||||
import * as React from 'react';
|
||||
import type { DrawerPanelAccessibility, DrawerPanelEvents } from './DrawerPanel';
|
||||
import type { DrawerPopupProps } from './DrawerPopup';
|
||||
import type { DrawerClassNames, DrawerStyles } from './inter';
|
||||
export type Placement = 'left' | 'top' | 'right' | 'bottom';
|
||||
export interface DrawerProps extends Omit<DrawerPopupProps, 'prefixCls' | 'inline' | 'scrollLocker'>, DrawerPanelEvents, DrawerPanelAccessibility {
|
||||
prefixCls?: string;
|
||||
open?: boolean;
|
||||
onClose?: (e: React.MouseEvent | React.KeyboardEvent | KeyboardEvent) => void;
|
||||
destroyOnHidden?: boolean;
|
||||
getContainer?: PortalProps['getContainer'];
|
||||
panelRef?: React.Ref<HTMLDivElement>;
|
||||
classNames?: DrawerClassNames;
|
||||
styles?: DrawerStyles;
|
||||
/**
|
||||
* @deprecated Use `size` instead. Will be removed in next major version.
|
||||
*/
|
||||
width?: number | string;
|
||||
/**
|
||||
* @deprecated Use `size` instead. Will be removed in next major version.
|
||||
*/
|
||||
height?: number | string;
|
||||
/** Size of the drawer (width for left/right placement, height for top/bottom placement) */
|
||||
size?: number | string;
|
||||
/** Maximum size of the drawer */
|
||||
maxSize?: number;
|
||||
/** Default size for uncontrolled resizable drawer */
|
||||
defaultSize?: number | string;
|
||||
/** Resizable configuration - boolean to enable/disable or object with optional callbacks */
|
||||
resizable?: boolean | {
|
||||
onResize?: (size: number) => void;
|
||||
onResizeStart?: () => void;
|
||||
onResizeEnd?: () => void;
|
||||
};
|
||||
focusTriggerAfterClose?: boolean;
|
||||
}
|
||||
declare const Drawer: React.FC<DrawerProps>;
|
||||
export default Drawer;
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
import Portal from '@rc-component/portal';
|
||||
import useLayoutEffect from "@rc-component/util/es/hooks/useLayoutEffect";
|
||||
import * as React from 'react';
|
||||
import { RefContext } from "./context";
|
||||
import DrawerPopup from "./DrawerPopup";
|
||||
import { warnCheck } from "./util";
|
||||
const Drawer = props => {
|
||||
const {
|
||||
open = false,
|
||||
prefixCls = 'rc-drawer',
|
||||
placement = 'right',
|
||||
autoFocus = true,
|
||||
keyboard = true,
|
||||
width,
|
||||
height,
|
||||
size,
|
||||
maxSize,
|
||||
mask = true,
|
||||
maskClosable = true,
|
||||
getContainer,
|
||||
forceRender,
|
||||
afterOpenChange,
|
||||
destroyOnHidden,
|
||||
onMouseEnter,
|
||||
onMouseOver,
|
||||
onMouseLeave,
|
||||
onClick,
|
||||
onKeyDown,
|
||||
onKeyUp,
|
||||
onClose,
|
||||
resizable,
|
||||
defaultSize,
|
||||
focusTriggerAfterClose,
|
||||
// Refs
|
||||
panelRef
|
||||
} = props;
|
||||
const [animatedVisible, setAnimatedVisible] = React.useState(false);
|
||||
|
||||
// ============================= Warn =============================
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
warnCheck(props);
|
||||
}
|
||||
|
||||
// ============================= Open =============================
|
||||
const [mounted, setMounted] = React.useState(false);
|
||||
useLayoutEffect(() => {
|
||||
setMounted(true);
|
||||
}, []);
|
||||
const mergedOpen = mounted ? open : false;
|
||||
|
||||
// ============================ Focus =============================
|
||||
const popupRef = React.useRef(null);
|
||||
const lastActiveRef = React.useRef(null);
|
||||
useLayoutEffect(() => {
|
||||
if (mergedOpen) {
|
||||
lastActiveRef.current = document.activeElement;
|
||||
}
|
||||
}, [mergedOpen]);
|
||||
|
||||
// ============================= Open =============================
|
||||
const internalAfterOpenChange = nextVisible => {
|
||||
setAnimatedVisible(nextVisible);
|
||||
afterOpenChange?.(nextVisible);
|
||||
if (!nextVisible && focusTriggerAfterClose !== false && lastActiveRef.current && !popupRef.current?.contains(lastActiveRef.current)) {
|
||||
lastActiveRef.current?.focus({
|
||||
preventScroll: true
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// =========================== Context ============================
|
||||
const refContext = React.useMemo(() => ({
|
||||
panel: panelRef
|
||||
}), [panelRef]);
|
||||
|
||||
// ============================ Render ============================
|
||||
if (!forceRender && !animatedVisible && !mergedOpen && destroyOnHidden) {
|
||||
return null;
|
||||
}
|
||||
const eventHandlers = {
|
||||
onMouseEnter,
|
||||
onMouseOver,
|
||||
onMouseLeave,
|
||||
onClick,
|
||||
onKeyDown,
|
||||
onKeyUp
|
||||
};
|
||||
const drawerPopupProps = {
|
||||
...props,
|
||||
open: mergedOpen,
|
||||
prefixCls,
|
||||
placement,
|
||||
autoFocus,
|
||||
keyboard,
|
||||
width,
|
||||
height,
|
||||
size,
|
||||
maxSize,
|
||||
defaultSize,
|
||||
mask,
|
||||
maskClosable,
|
||||
inline: getContainer === false,
|
||||
afterOpenChange: internalAfterOpenChange,
|
||||
ref: popupRef,
|
||||
resizable,
|
||||
...eventHandlers
|
||||
};
|
||||
const onEsc = ({
|
||||
top,
|
||||
event
|
||||
}) => {
|
||||
if (top && keyboard) {
|
||||
event.stopPropagation();
|
||||
onClose?.(event);
|
||||
}
|
||||
};
|
||||
return /*#__PURE__*/React.createElement(RefContext.Provider, {
|
||||
value: refContext
|
||||
}, /*#__PURE__*/React.createElement(Portal, {
|
||||
open: mergedOpen || forceRender || animatedVisible,
|
||||
autoDestroy: false,
|
||||
getContainer: getContainer,
|
||||
autoLock: mask && (mergedOpen || animatedVisible),
|
||||
onEsc: onEsc
|
||||
}, /*#__PURE__*/React.createElement(DrawerPopup, drawerPopupProps)));
|
||||
};
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
Drawer.displayName = 'Drawer';
|
||||
}
|
||||
export default Drawer;
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
import * as React from 'react';
|
||||
export interface DrawerPanelRef {
|
||||
focus: VoidFunction;
|
||||
}
|
||||
export interface DrawerPanelEvents {
|
||||
onMouseEnter?: React.MouseEventHandler<HTMLDivElement>;
|
||||
onMouseOver?: React.MouseEventHandler<HTMLDivElement>;
|
||||
onMouseLeave?: React.MouseEventHandler<HTMLDivElement>;
|
||||
onClick?: React.MouseEventHandler<HTMLDivElement>;
|
||||
onKeyDown?: React.KeyboardEventHandler<HTMLDivElement>;
|
||||
onKeyUp?: React.KeyboardEventHandler<HTMLDivElement>;
|
||||
onFocus?: React.FocusEventHandler<HTMLDivElement>;
|
||||
}
|
||||
export type DrawerPanelAccessibility = Pick<React.DialogHTMLAttributes<HTMLDivElement>, keyof React.AriaAttributes>;
|
||||
export interface DrawerPanelProps extends DrawerPanelEvents, DrawerPanelAccessibility {
|
||||
prefixCls: string;
|
||||
className?: string;
|
||||
id?: string;
|
||||
style?: React.CSSProperties;
|
||||
children?: React.ReactNode;
|
||||
containerRef?: React.Ref<HTMLDivElement>;
|
||||
}
|
||||
declare const DrawerPanel: React.FC<Readonly<DrawerPanelProps>>;
|
||||
export default DrawerPanel;
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
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 { clsx } from 'clsx';
|
||||
import * as React from 'react';
|
||||
import { RefContext } from "./context";
|
||||
import pickAttrs from "@rc-component/util/es/pickAttrs";
|
||||
import { useComposeRef } from "@rc-component/util/es/ref";
|
||||
const DrawerPanel = props => {
|
||||
const {
|
||||
prefixCls,
|
||||
className,
|
||||
containerRef,
|
||||
...restProps
|
||||
} = props;
|
||||
const {
|
||||
panel: panelRef
|
||||
} = React.useContext(RefContext);
|
||||
const mergedRef = useComposeRef(panelRef, containerRef);
|
||||
|
||||
// =============================== Render ===============================
|
||||
|
||||
return /*#__PURE__*/React.createElement("div", _extends({
|
||||
className: clsx(`${prefixCls}-section`, className),
|
||||
role: "dialog",
|
||||
ref: mergedRef
|
||||
}, pickAttrs(props, {
|
||||
aria: true
|
||||
}), {
|
||||
"aria-modal": "true"
|
||||
}, restProps));
|
||||
};
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
DrawerPanel.displayName = 'DrawerPanel';
|
||||
}
|
||||
export default DrawerPanel;
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
import type { CSSMotionProps } from '@rc-component/motion';
|
||||
import * as React from 'react';
|
||||
import type { DrawerPanelAccessibility, DrawerPanelEvents } from './DrawerPanel';
|
||||
import type { DrawerClassNames, DrawerStyles } from './inter';
|
||||
export type Placement = 'left' | 'right' | 'top' | 'bottom';
|
||||
export interface PushConfig {
|
||||
distance?: number | string;
|
||||
}
|
||||
export interface DrawerPopupProps extends DrawerPanelEvents, DrawerPanelAccessibility {
|
||||
prefixCls: string;
|
||||
open?: boolean;
|
||||
inline?: boolean;
|
||||
push?: boolean | PushConfig;
|
||||
forceRender?: boolean;
|
||||
keyboard?: boolean;
|
||||
autoFocus?: boolean;
|
||||
focusTrap?: boolean;
|
||||
rootClassName?: string;
|
||||
rootStyle?: React.CSSProperties;
|
||||
zIndex?: number;
|
||||
placement?: Placement;
|
||||
id?: string;
|
||||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
children?: React.ReactNode;
|
||||
width?: number | string;
|
||||
height?: number | string;
|
||||
/** Size of the drawer (width for left/right placement, height for top/bottom placement) */
|
||||
size?: number | string;
|
||||
/** Maximum size of the drawer */
|
||||
maxSize?: number;
|
||||
mask?: boolean;
|
||||
maskClosable?: boolean;
|
||||
maskClassName?: string;
|
||||
maskStyle?: React.CSSProperties;
|
||||
motion?: CSSMotionProps | ((placement: Placement) => CSSMotionProps);
|
||||
maskMotion?: CSSMotionProps;
|
||||
afterOpenChange?: (open: boolean) => void;
|
||||
onClose?: (event: React.MouseEvent<HTMLElement> | React.KeyboardEvent<HTMLElement>) => void;
|
||||
classNames?: DrawerClassNames;
|
||||
styles?: DrawerStyles;
|
||||
drawerRender?: (node: React.ReactNode) => React.ReactNode;
|
||||
/** Default size for uncontrolled resizable drawer */
|
||||
defaultSize?: number | string;
|
||||
resizable?: boolean | {
|
||||
onResize?: (size: number) => void;
|
||||
onResizeStart?: () => void;
|
||||
onResizeEnd?: () => void;
|
||||
};
|
||||
}
|
||||
declare const RefDrawerPopup: React.ForwardRefExoticComponent<DrawerPopupProps & React.RefAttributes<HTMLDivElement>>;
|
||||
export default RefDrawerPopup;
|
||||
+265
@@ -0,0 +1,265 @@
|
||||
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 { clsx } from 'clsx';
|
||||
import CSSMotion from '@rc-component/motion';
|
||||
import pickAttrs from "@rc-component/util/es/pickAttrs";
|
||||
import * as React from 'react';
|
||||
import DrawerContext from "./context";
|
||||
import DrawerPanel from "./DrawerPanel";
|
||||
import useDrag from "./hooks/useDrag";
|
||||
import { parseWidthHeight } from "./util";
|
||||
import { useEvent } from '@rc-component/util';
|
||||
import useFocusable from "./hooks/useFocusable";
|
||||
const DrawerPopup = (props, ref) => {
|
||||
const {
|
||||
prefixCls,
|
||||
open,
|
||||
placement,
|
||||
inline,
|
||||
push,
|
||||
forceRender,
|
||||
// Focus
|
||||
autoFocus,
|
||||
focusTrap,
|
||||
// classNames
|
||||
classNames: drawerClassNames,
|
||||
// Root
|
||||
rootClassName,
|
||||
rootStyle,
|
||||
zIndex,
|
||||
// Drawer
|
||||
className,
|
||||
id,
|
||||
style,
|
||||
motion,
|
||||
width,
|
||||
height,
|
||||
size,
|
||||
maxSize,
|
||||
children,
|
||||
// Mask
|
||||
mask,
|
||||
maskClosable,
|
||||
maskMotion,
|
||||
maskClassName,
|
||||
maskStyle,
|
||||
// Events
|
||||
afterOpenChange,
|
||||
onClose,
|
||||
onMouseEnter,
|
||||
onMouseOver,
|
||||
onMouseLeave,
|
||||
onClick,
|
||||
onKeyDown,
|
||||
onKeyUp,
|
||||
styles,
|
||||
drawerRender,
|
||||
resizable,
|
||||
defaultSize
|
||||
} = props;
|
||||
|
||||
// ================================ Refs ================================
|
||||
const panelRef = React.useRef(null);
|
||||
React.useImperativeHandle(ref, () => panelRef.current);
|
||||
|
||||
// ========================= Focusable ==========================
|
||||
const ignoreElement = useFocusable(() => panelRef.current, open, autoFocus, focusTrap, mask);
|
||||
|
||||
// ============================ 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 = /*#__PURE__*/React.createElement(CSSMotion, _extends({
|
||||
key: "mask"
|
||||
}, maskMotion, {
|
||||
visible: mask && open
|
||||
}), ({
|
||||
className: motionMaskClassName,
|
||||
style: motionMaskStyle
|
||||
}, maskRef) => /*#__PURE__*/React.createElement("div", {
|
||||
className: clsx(`${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;
|
||||
|
||||
// ============================ Size ============================
|
||||
const [currentSize, setCurrentSize] = React.useState();
|
||||
const isHorizontal = placement === 'left' || placement === 'right';
|
||||
|
||||
// Aggregate size logic with backward compatibility using useMemo
|
||||
const mergedSize = React.useMemo(() => {
|
||||
const legacySize = isHorizontal ? width : height;
|
||||
const nextMergedSize = size ?? legacySize ?? currentSize ?? defaultSize ?? (isHorizontal ? 378 : undefined);
|
||||
return parseWidthHeight(nextMergedSize);
|
||||
}, [size, width, height, defaultSize, isHorizontal, currentSize]);
|
||||
|
||||
// >>> Style
|
||||
const wrapperStyle = React.useMemo(() => {
|
||||
const nextWrapperStyle = {};
|
||||
if (pushed && pushDistance) {
|
||||
switch (placement) {
|
||||
case 'top':
|
||||
nextWrapperStyle.transform = `translateY(${pushDistance}px)`;
|
||||
break;
|
||||
case 'bottom':
|
||||
nextWrapperStyle.transform = `translateY(${-pushDistance}px)`;
|
||||
break;
|
||||
case 'left':
|
||||
nextWrapperStyle.transform = `translateX(${pushDistance}px)`;
|
||||
break;
|
||||
default:
|
||||
nextWrapperStyle.transform = `translateX(${-pushDistance}px)`;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isHorizontal) {
|
||||
nextWrapperStyle.width = parseWidthHeight(mergedSize);
|
||||
} else {
|
||||
nextWrapperStyle.height = parseWidthHeight(mergedSize);
|
||||
}
|
||||
return nextWrapperStyle;
|
||||
}, [pushed, pushDistance, placement, isHorizontal, mergedSize]);
|
||||
|
||||
// =========================== Resize ===========================
|
||||
const wrapperRef = React.useRef(null);
|
||||
const isResizable = !!resizable;
|
||||
const resizeConfig = typeof resizable === 'object' && resizable || {};
|
||||
const onInternalResize = useEvent(size => {
|
||||
setCurrentSize(size);
|
||||
resizeConfig.onResize?.(size);
|
||||
});
|
||||
const {
|
||||
dragElementProps,
|
||||
isDragging
|
||||
} = useDrag({
|
||||
prefixCls: `${prefixCls}-resizable`,
|
||||
direction: placement,
|
||||
className: drawerClassNames?.dragger,
|
||||
style: styles?.dragger,
|
||||
maxSize,
|
||||
containerRef: wrapperRef,
|
||||
currentSize: mergedSize,
|
||||
onResize: onInternalResize,
|
||||
onResizeStart: resizeConfig.onResizeStart,
|
||||
onResizeEnd: resizeConfig.onResizeEnd
|
||||
});
|
||||
|
||||
// =========================== Events ===========================
|
||||
const eventHandlers = {
|
||||
onMouseEnter,
|
||||
onMouseOver,
|
||||
onMouseLeave,
|
||||
onClick,
|
||||
onKeyDown,
|
||||
onKeyUp,
|
||||
onFocus: e => {
|
||||
ignoreElement(e.target);
|
||||
}
|
||||
};
|
||||
|
||||
// =========================== Render ==========================
|
||||
// >>>>> Panel
|
||||
const panelNode = /*#__PURE__*/React.createElement(CSSMotion, _extends({
|
||||
key: "panel"
|
||||
}, motionProps, {
|
||||
visible: open,
|
||||
forceRender: forceRender,
|
||||
onVisibleChanged: afterOpenChange,
|
||||
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: clsx(className, drawerClassNames?.section),
|
||||
style: {
|
||||
...style,
|
||||
...styles?.section
|
||||
}
|
||||
}, pickAttrs(props, {
|
||||
aria: true
|
||||
}), eventHandlers), children);
|
||||
return /*#__PURE__*/React.createElement("div", _extends({
|
||||
ref: wrapperRef,
|
||||
className: clsx(`${prefixCls}-content-wrapper`, isDragging && `${prefixCls}-content-wrapper-dragging`, drawerClassNames?.wrapper, !isDragging && motionClassName),
|
||||
style: {
|
||||
...motionStyle,
|
||||
...wrapperStyle,
|
||||
...styles?.wrapper
|
||||
}
|
||||
}, pickAttrs(props, {
|
||||
data: true
|
||||
})), isResizable && /*#__PURE__*/React.createElement("div", dragElementProps), drawerRender ? drawerRender(content) : content);
|
||||
});
|
||||
|
||||
// >>>>> Container
|
||||
const containerStyle = {
|
||||
...rootStyle
|
||||
};
|
||||
if (zIndex) {
|
||||
containerStyle.zIndex = zIndex;
|
||||
}
|
||||
return /*#__PURE__*/React.createElement(DrawerContext.Provider, {
|
||||
value: mergedContext
|
||||
}, /*#__PURE__*/React.createElement("div", {
|
||||
className: clsx(prefixCls, `${prefixCls}-${placement}`, rootClassName, {
|
||||
[`${prefixCls}-open`]: open,
|
||||
[`${prefixCls}-inline`]: inline
|
||||
}),
|
||||
style: containerStyle,
|
||||
tabIndex: -1,
|
||||
ref: panelRef
|
||||
}, maskNode, panelNode));
|
||||
};
|
||||
const RefDrawerPopup = /*#__PURE__*/React.forwardRef(DrawerPopup);
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
RefDrawerPopup.displayName = 'DrawerPopup';
|
||||
}
|
||||
export default RefDrawerPopup;
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
import * as React from 'react';
|
||||
export interface DrawerContextProps {
|
||||
pushDistance?: number | string;
|
||||
push: VoidFunction;
|
||||
pull: VoidFunction;
|
||||
}
|
||||
declare const DrawerContext: React.Context<DrawerContextProps>;
|
||||
export interface RefContextProps {
|
||||
panel?: React.Ref<HTMLDivElement>;
|
||||
}
|
||||
export declare const RefContext: React.Context<RefContextProps>;
|
||||
export default DrawerContext;
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
import * as React from 'react';
|
||||
const DrawerContext = /*#__PURE__*/React.createContext(null);
|
||||
export const RefContext = /*#__PURE__*/React.createContext({});
|
||||
export default DrawerContext;
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
import * as React from 'react';
|
||||
import type { Placement } from '../Drawer';
|
||||
export interface UseDragOptions {
|
||||
prefixCls: string;
|
||||
direction: Placement;
|
||||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
maxSize?: number;
|
||||
containerRef?: React.RefObject<HTMLElement>;
|
||||
currentSize?: number | string;
|
||||
onResize?: (size: number) => void;
|
||||
onResizeEnd?: (size: number) => void;
|
||||
onResizeStart?: (size: number) => void;
|
||||
}
|
||||
export interface UseDragReturn {
|
||||
dragElementProps: {
|
||||
className: string;
|
||||
style: React.CSSProperties;
|
||||
onMouseDown: (e: React.MouseEvent) => void;
|
||||
};
|
||||
isDragging: boolean;
|
||||
}
|
||||
export default function useDrag(options: UseDragOptions): UseDragReturn;
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
import * as React from 'react';
|
||||
import { clsx } from 'clsx';
|
||||
import { useEvent } from '@rc-component/util';
|
||||
export default function useDrag(options) {
|
||||
const {
|
||||
prefixCls,
|
||||
direction,
|
||||
className,
|
||||
style,
|
||||
maxSize,
|
||||
containerRef,
|
||||
currentSize,
|
||||
onResize,
|
||||
onResizeEnd,
|
||||
onResizeStart
|
||||
} = options;
|
||||
const [isDragging, setIsDragging] = React.useState(false);
|
||||
const [startPos, setStartPos] = React.useState(0);
|
||||
const [startSize, setStartSize] = React.useState(0);
|
||||
const isHorizontal = direction === 'left' || direction === 'right';
|
||||
const handleMouseDown = useEvent(e => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
setIsDragging(true);
|
||||
if (isHorizontal) {
|
||||
setStartPos(e.clientX);
|
||||
} else {
|
||||
setStartPos(e.clientY);
|
||||
}
|
||||
|
||||
// Use provided currentSize, or fallback to container size
|
||||
let startSize;
|
||||
if (typeof currentSize === 'number') {
|
||||
startSize = currentSize;
|
||||
} else if (containerRef?.current) {
|
||||
const rect = containerRef.current.getBoundingClientRect();
|
||||
startSize = isHorizontal ? rect.width : rect.height;
|
||||
}
|
||||
setStartSize(startSize);
|
||||
onResizeStart?.(startSize);
|
||||
});
|
||||
const handleMouseMove = useEvent(e => {
|
||||
if (!isDragging) return;
|
||||
const currentPos = isHorizontal ? e.clientX : e.clientY;
|
||||
let delta = currentPos - startPos;
|
||||
|
||||
// Adjust delta direction based on placement
|
||||
if (direction === 'right' || direction === 'bottom') {
|
||||
delta = -delta;
|
||||
}
|
||||
let newSize = startSize + delta;
|
||||
|
||||
// Apply min/max size limits
|
||||
if (newSize < 0) {
|
||||
newSize = 0;
|
||||
}
|
||||
// Only apply maxSize if it's a valid positive number
|
||||
if (maxSize && newSize > maxSize) {
|
||||
newSize = maxSize;
|
||||
}
|
||||
onResize?.(newSize);
|
||||
});
|
||||
const handleMouseUp = React.useCallback(() => {
|
||||
if (isDragging) {
|
||||
setIsDragging(false);
|
||||
|
||||
// Get the final size after resize
|
||||
if (containerRef?.current) {
|
||||
const rect = containerRef.current.getBoundingClientRect();
|
||||
const finalSize = isHorizontal ? rect.width : rect.height;
|
||||
onResizeEnd?.(finalSize);
|
||||
}
|
||||
}
|
||||
}, [isDragging, containerRef, onResizeEnd, isHorizontal]);
|
||||
React.useEffect(() => {
|
||||
if (isDragging) {
|
||||
document.addEventListener('mousemove', handleMouseMove);
|
||||
document.addEventListener('mouseup', handleMouseUp);
|
||||
return () => {
|
||||
document.removeEventListener('mousemove', handleMouseMove);
|
||||
document.removeEventListener('mouseup', handleMouseUp);
|
||||
};
|
||||
}
|
||||
}, [isDragging, handleMouseMove, handleMouseUp]);
|
||||
const dragElementClassName = clsx(`${prefixCls}-dragger`, `${prefixCls}-dragger-${direction}`, {
|
||||
[`${prefixCls}-dragger-dragging`]: isDragging,
|
||||
[`${prefixCls}-dragger-horizontal`]: isHorizontal,
|
||||
[`${prefixCls}-dragger-vertical`]: !isHorizontal
|
||||
}, className);
|
||||
return {
|
||||
dragElementProps: {
|
||||
className: dragElementClassName,
|
||||
style,
|
||||
onMouseDown: handleMouseDown
|
||||
},
|
||||
isDragging
|
||||
};
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
export default function useFocusable(getContainer: () => HTMLElement, open: boolean, autoFocus?: boolean, focusTrap?: boolean, mask?: boolean): (ele: HTMLElement) => void;
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
import React from 'react';
|
||||
import { useLockFocus } from "@rc-component/util/es/Dom/focus";
|
||||
export default function useFocusable(getContainer, open, autoFocus, focusTrap, mask) {
|
||||
const mergedFocusTrap = focusTrap ?? mask !== false;
|
||||
|
||||
// Focus lock
|
||||
const [ignoreElement] = useLockFocus(open && mergedFocusTrap, getContainer);
|
||||
|
||||
// Auto Focus
|
||||
React.useEffect(() => {
|
||||
if (open && autoFocus === true) {
|
||||
getContainer()?.focus({
|
||||
preventScroll: true
|
||||
});
|
||||
}
|
||||
}, [open]);
|
||||
return ignoreElement;
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
import Drawer from './Drawer';
|
||||
import type { DrawerProps, Placement } from './Drawer';
|
||||
export type { DrawerProps, Placement };
|
||||
export default Drawer;
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
// export this package's api
|
||||
import Drawer from "./Drawer";
|
||||
export default Drawer;
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
/// <reference types="react" />
|
||||
export interface DrawerClassNames {
|
||||
mask?: string;
|
||||
wrapper?: string;
|
||||
section?: string;
|
||||
dragger?: string;
|
||||
}
|
||||
export interface DrawerStyles {
|
||||
mask?: React.CSSProperties;
|
||||
wrapper?: React.CSSProperties;
|
||||
section?: React.CSSProperties;
|
||||
dragger?: React.CSSProperties;
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
export {};
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
import type { DrawerProps } from './Drawer';
|
||||
export declare function parseWidthHeight(value?: number | string): string | number;
|
||||
export declare function warnCheck(props: DrawerProps): void;
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
import warning from "@rc-component/util/es/warning";
|
||||
import canUseDom from "@rc-component/util/es/Dom/canUseDom";
|
||||
export function parseWidthHeight(value) {
|
||||
if (typeof value === 'string') {
|
||||
const num = Number(value.replace(/px$/i, ''));
|
||||
const floatNum = parseFloat(value);
|
||||
if (floatNum === num) {
|
||||
warning(false, 'Invalid value type of `width` or `height` which should be number type instead.');
|
||||
}
|
||||
if (!Number.isNaN(num)) {
|
||||
return num;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
export function warnCheck(props) {
|
||||
warning(!('wrapperClassName' in props), `'wrapperClassName' is removed. Please use 'rootClassName' instead.`);
|
||||
warning(canUseDom() || !props.open, `Drawer with 'open' in SSR is not work since no place to createPortal. Please move to 'useEffect' instead.`);
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
import type { PortalProps } from '@rc-component/portal';
|
||||
import * as React from 'react';
|
||||
import type { DrawerPanelAccessibility, DrawerPanelEvents } from './DrawerPanel';
|
||||
import type { DrawerPopupProps } from './DrawerPopup';
|
||||
import type { DrawerClassNames, DrawerStyles } from './inter';
|
||||
export type Placement = 'left' | 'top' | 'right' | 'bottom';
|
||||
export interface DrawerProps extends Omit<DrawerPopupProps, 'prefixCls' | 'inline' | 'scrollLocker'>, DrawerPanelEvents, DrawerPanelAccessibility {
|
||||
prefixCls?: string;
|
||||
open?: boolean;
|
||||
onClose?: (e: React.MouseEvent | React.KeyboardEvent | KeyboardEvent) => void;
|
||||
destroyOnHidden?: boolean;
|
||||
getContainer?: PortalProps['getContainer'];
|
||||
panelRef?: React.Ref<HTMLDivElement>;
|
||||
classNames?: DrawerClassNames;
|
||||
styles?: DrawerStyles;
|
||||
/**
|
||||
* @deprecated Use `size` instead. Will be removed in next major version.
|
||||
*/
|
||||
width?: number | string;
|
||||
/**
|
||||
* @deprecated Use `size` instead. Will be removed in next major version.
|
||||
*/
|
||||
height?: number | string;
|
||||
/** Size of the drawer (width for left/right placement, height for top/bottom placement) */
|
||||
size?: number | string;
|
||||
/** Maximum size of the drawer */
|
||||
maxSize?: number;
|
||||
/** Default size for uncontrolled resizable drawer */
|
||||
defaultSize?: number | string;
|
||||
/** Resizable configuration - boolean to enable/disable or object with optional callbacks */
|
||||
resizable?: boolean | {
|
||||
onResize?: (size: number) => void;
|
||||
onResizeStart?: () => void;
|
||||
onResizeEnd?: () => void;
|
||||
};
|
||||
focusTriggerAfterClose?: boolean;
|
||||
}
|
||||
declare const Drawer: React.FC<DrawerProps>;
|
||||
export default Drawer;
|
||||
+139
@@ -0,0 +1,139 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = void 0;
|
||||
var _portal = _interopRequireDefault(require("@rc-component/portal"));
|
||||
var _useLayoutEffect = _interopRequireDefault(require("@rc-component/util/lib/hooks/useLayoutEffect"));
|
||||
var React = _interopRequireWildcard(require("react"));
|
||||
var _context = require("./context");
|
||||
var _DrawerPopup = _interopRequireDefault(require("./DrawerPopup"));
|
||||
var _util = require("./util");
|
||||
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
||||
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
const Drawer = props => {
|
||||
const {
|
||||
open = false,
|
||||
prefixCls = 'rc-drawer',
|
||||
placement = 'right',
|
||||
autoFocus = true,
|
||||
keyboard = true,
|
||||
width,
|
||||
height,
|
||||
size,
|
||||
maxSize,
|
||||
mask = true,
|
||||
maskClosable = true,
|
||||
getContainer,
|
||||
forceRender,
|
||||
afterOpenChange,
|
||||
destroyOnHidden,
|
||||
onMouseEnter,
|
||||
onMouseOver,
|
||||
onMouseLeave,
|
||||
onClick,
|
||||
onKeyDown,
|
||||
onKeyUp,
|
||||
onClose,
|
||||
resizable,
|
||||
defaultSize,
|
||||
focusTriggerAfterClose,
|
||||
// Refs
|
||||
panelRef
|
||||
} = props;
|
||||
const [animatedVisible, setAnimatedVisible] = React.useState(false);
|
||||
|
||||
// ============================= Warn =============================
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
(0, _util.warnCheck)(props);
|
||||
}
|
||||
|
||||
// ============================= Open =============================
|
||||
const [mounted, setMounted] = React.useState(false);
|
||||
(0, _useLayoutEffect.default)(() => {
|
||||
setMounted(true);
|
||||
}, []);
|
||||
const mergedOpen = mounted ? open : false;
|
||||
|
||||
// ============================ Focus =============================
|
||||
const popupRef = React.useRef(null);
|
||||
const lastActiveRef = React.useRef(null);
|
||||
(0, _useLayoutEffect.default)(() => {
|
||||
if (mergedOpen) {
|
||||
lastActiveRef.current = document.activeElement;
|
||||
}
|
||||
}, [mergedOpen]);
|
||||
|
||||
// ============================= Open =============================
|
||||
const internalAfterOpenChange = nextVisible => {
|
||||
setAnimatedVisible(nextVisible);
|
||||
afterOpenChange?.(nextVisible);
|
||||
if (!nextVisible && focusTriggerAfterClose !== false && lastActiveRef.current && !popupRef.current?.contains(lastActiveRef.current)) {
|
||||
lastActiveRef.current?.focus({
|
||||
preventScroll: true
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// =========================== Context ============================
|
||||
const refContext = React.useMemo(() => ({
|
||||
panel: panelRef
|
||||
}), [panelRef]);
|
||||
|
||||
// ============================ Render ============================
|
||||
if (!forceRender && !animatedVisible && !mergedOpen && destroyOnHidden) {
|
||||
return null;
|
||||
}
|
||||
const eventHandlers = {
|
||||
onMouseEnter,
|
||||
onMouseOver,
|
||||
onMouseLeave,
|
||||
onClick,
|
||||
onKeyDown,
|
||||
onKeyUp
|
||||
};
|
||||
const drawerPopupProps = {
|
||||
...props,
|
||||
open: mergedOpen,
|
||||
prefixCls,
|
||||
placement,
|
||||
autoFocus,
|
||||
keyboard,
|
||||
width,
|
||||
height,
|
||||
size,
|
||||
maxSize,
|
||||
defaultSize,
|
||||
mask,
|
||||
maskClosable,
|
||||
inline: getContainer === false,
|
||||
afterOpenChange: internalAfterOpenChange,
|
||||
ref: popupRef,
|
||||
resizable,
|
||||
...eventHandlers
|
||||
};
|
||||
const onEsc = ({
|
||||
top,
|
||||
event
|
||||
}) => {
|
||||
if (top && keyboard) {
|
||||
event.stopPropagation();
|
||||
onClose?.(event);
|
||||
}
|
||||
};
|
||||
return /*#__PURE__*/React.createElement(_context.RefContext.Provider, {
|
||||
value: refContext
|
||||
}, /*#__PURE__*/React.createElement(_portal.default, {
|
||||
open: mergedOpen || forceRender || animatedVisible,
|
||||
autoDestroy: false,
|
||||
getContainer: getContainer,
|
||||
autoLock: mask && (mergedOpen || animatedVisible),
|
||||
onEsc: onEsc
|
||||
}, /*#__PURE__*/React.createElement(_DrawerPopup.default, drawerPopupProps)));
|
||||
};
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
Drawer.displayName = 'Drawer';
|
||||
}
|
||||
var _default = exports.default = Drawer;
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
import * as React from 'react';
|
||||
export interface DrawerPanelRef {
|
||||
focus: VoidFunction;
|
||||
}
|
||||
export interface DrawerPanelEvents {
|
||||
onMouseEnter?: React.MouseEventHandler<HTMLDivElement>;
|
||||
onMouseOver?: React.MouseEventHandler<HTMLDivElement>;
|
||||
onMouseLeave?: React.MouseEventHandler<HTMLDivElement>;
|
||||
onClick?: React.MouseEventHandler<HTMLDivElement>;
|
||||
onKeyDown?: React.KeyboardEventHandler<HTMLDivElement>;
|
||||
onKeyUp?: React.KeyboardEventHandler<HTMLDivElement>;
|
||||
onFocus?: React.FocusEventHandler<HTMLDivElement>;
|
||||
}
|
||||
export type DrawerPanelAccessibility = Pick<React.DialogHTMLAttributes<HTMLDivElement>, keyof React.AriaAttributes>;
|
||||
export interface DrawerPanelProps extends DrawerPanelEvents, DrawerPanelAccessibility {
|
||||
prefixCls: string;
|
||||
className?: string;
|
||||
id?: string;
|
||||
style?: React.CSSProperties;
|
||||
children?: React.ReactNode;
|
||||
containerRef?: React.Ref<HTMLDivElement>;
|
||||
}
|
||||
declare const DrawerPanel: React.FC<Readonly<DrawerPanelProps>>;
|
||||
export default DrawerPanel;
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = void 0;
|
||||
var _clsx = require("clsx");
|
||||
var React = _interopRequireWildcard(require("react"));
|
||||
var _context = require("./context");
|
||||
var _pickAttrs = _interopRequireDefault(require("@rc-component/util/lib/pickAttrs"));
|
||||
var _ref = require("@rc-component/util/lib/ref");
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
||||
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
||||
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); }
|
||||
const DrawerPanel = props => {
|
||||
const {
|
||||
prefixCls,
|
||||
className,
|
||||
containerRef,
|
||||
...restProps
|
||||
} = props;
|
||||
const {
|
||||
panel: panelRef
|
||||
} = React.useContext(_context.RefContext);
|
||||
const mergedRef = (0, _ref.useComposeRef)(panelRef, containerRef);
|
||||
|
||||
// =============================== Render ===============================
|
||||
|
||||
return /*#__PURE__*/React.createElement("div", _extends({
|
||||
className: (0, _clsx.clsx)(`${prefixCls}-section`, className),
|
||||
role: "dialog",
|
||||
ref: mergedRef
|
||||
}, (0, _pickAttrs.default)(props, {
|
||||
aria: true
|
||||
}), {
|
||||
"aria-modal": "true"
|
||||
}, restProps));
|
||||
};
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
DrawerPanel.displayName = 'DrawerPanel';
|
||||
}
|
||||
var _default = exports.default = DrawerPanel;
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
import type { CSSMotionProps } from '@rc-component/motion';
|
||||
import * as React from 'react';
|
||||
import type { DrawerPanelAccessibility, DrawerPanelEvents } from './DrawerPanel';
|
||||
import type { DrawerClassNames, DrawerStyles } from './inter';
|
||||
export type Placement = 'left' | 'right' | 'top' | 'bottom';
|
||||
export interface PushConfig {
|
||||
distance?: number | string;
|
||||
}
|
||||
export interface DrawerPopupProps extends DrawerPanelEvents, DrawerPanelAccessibility {
|
||||
prefixCls: string;
|
||||
open?: boolean;
|
||||
inline?: boolean;
|
||||
push?: boolean | PushConfig;
|
||||
forceRender?: boolean;
|
||||
keyboard?: boolean;
|
||||
autoFocus?: boolean;
|
||||
focusTrap?: boolean;
|
||||
rootClassName?: string;
|
||||
rootStyle?: React.CSSProperties;
|
||||
zIndex?: number;
|
||||
placement?: Placement;
|
||||
id?: string;
|
||||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
children?: React.ReactNode;
|
||||
width?: number | string;
|
||||
height?: number | string;
|
||||
/** Size of the drawer (width for left/right placement, height for top/bottom placement) */
|
||||
size?: number | string;
|
||||
/** Maximum size of the drawer */
|
||||
maxSize?: number;
|
||||
mask?: boolean;
|
||||
maskClosable?: boolean;
|
||||
maskClassName?: string;
|
||||
maskStyle?: React.CSSProperties;
|
||||
motion?: CSSMotionProps | ((placement: Placement) => CSSMotionProps);
|
||||
maskMotion?: CSSMotionProps;
|
||||
afterOpenChange?: (open: boolean) => void;
|
||||
onClose?: (event: React.MouseEvent<HTMLElement> | React.KeyboardEvent<HTMLElement>) => void;
|
||||
classNames?: DrawerClassNames;
|
||||
styles?: DrawerStyles;
|
||||
drawerRender?: (node: React.ReactNode) => React.ReactNode;
|
||||
/** Default size for uncontrolled resizable drawer */
|
||||
defaultSize?: number | string;
|
||||
resizable?: boolean | {
|
||||
onResize?: (size: number) => void;
|
||||
onResizeStart?: () => void;
|
||||
onResizeEnd?: () => void;
|
||||
};
|
||||
}
|
||||
declare const RefDrawerPopup: React.ForwardRefExoticComponent<DrawerPopupProps & React.RefAttributes<HTMLDivElement>>;
|
||||
export default RefDrawerPopup;
|
||||
+274
@@ -0,0 +1,274 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = void 0;
|
||||
var _clsx = require("clsx");
|
||||
var _motion = _interopRequireDefault(require("@rc-component/motion"));
|
||||
var _pickAttrs = _interopRequireDefault(require("@rc-component/util/lib/pickAttrs"));
|
||||
var React = _interopRequireWildcard(require("react"));
|
||||
var _context = _interopRequireDefault(require("./context"));
|
||||
var _DrawerPanel = _interopRequireDefault(require("./DrawerPanel"));
|
||||
var _useDrag = _interopRequireDefault(require("./hooks/useDrag"));
|
||||
var _util = require("./util");
|
||||
var _util2 = require("@rc-component/util");
|
||||
var _useFocusable = _interopRequireDefault(require("./hooks/useFocusable"));
|
||||
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
||||
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
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); }
|
||||
const DrawerPopup = (props, ref) => {
|
||||
const {
|
||||
prefixCls,
|
||||
open,
|
||||
placement,
|
||||
inline,
|
||||
push,
|
||||
forceRender,
|
||||
// Focus
|
||||
autoFocus,
|
||||
focusTrap,
|
||||
// classNames
|
||||
classNames: drawerClassNames,
|
||||
// Root
|
||||
rootClassName,
|
||||
rootStyle,
|
||||
zIndex,
|
||||
// Drawer
|
||||
className,
|
||||
id,
|
||||
style,
|
||||
motion,
|
||||
width,
|
||||
height,
|
||||
size,
|
||||
maxSize,
|
||||
children,
|
||||
// Mask
|
||||
mask,
|
||||
maskClosable,
|
||||
maskMotion,
|
||||
maskClassName,
|
||||
maskStyle,
|
||||
// Events
|
||||
afterOpenChange,
|
||||
onClose,
|
||||
onMouseEnter,
|
||||
onMouseOver,
|
||||
onMouseLeave,
|
||||
onClick,
|
||||
onKeyDown,
|
||||
onKeyUp,
|
||||
styles,
|
||||
drawerRender,
|
||||
resizable,
|
||||
defaultSize
|
||||
} = props;
|
||||
|
||||
// ================================ Refs ================================
|
||||
const panelRef = React.useRef(null);
|
||||
React.useImperativeHandle(ref, () => panelRef.current);
|
||||
|
||||
// ========================= Focusable ==========================
|
||||
const ignoreElement = (0, _useFocusable.default)(() => panelRef.current, open, autoFocus, focusTrap, mask);
|
||||
|
||||
// ============================ Push ============================
|
||||
const [pushed, setPushed] = React.useState(false);
|
||||
const parentContext = React.useContext(_context.default);
|
||||
|
||||
// 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 = /*#__PURE__*/React.createElement(_motion.default, _extends({
|
||||
key: "mask"
|
||||
}, maskMotion, {
|
||||
visible: mask && open
|
||||
}), ({
|
||||
className: motionMaskClassName,
|
||||
style: motionMaskStyle
|
||||
}, maskRef) => /*#__PURE__*/React.createElement("div", {
|
||||
className: (0, _clsx.clsx)(`${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;
|
||||
|
||||
// ============================ Size ============================
|
||||
const [currentSize, setCurrentSize] = React.useState();
|
||||
const isHorizontal = placement === 'left' || placement === 'right';
|
||||
|
||||
// Aggregate size logic with backward compatibility using useMemo
|
||||
const mergedSize = React.useMemo(() => {
|
||||
const legacySize = isHorizontal ? width : height;
|
||||
const nextMergedSize = size ?? legacySize ?? currentSize ?? defaultSize ?? (isHorizontal ? 378 : undefined);
|
||||
return (0, _util.parseWidthHeight)(nextMergedSize);
|
||||
}, [size, width, height, defaultSize, isHorizontal, currentSize]);
|
||||
|
||||
// >>> Style
|
||||
const wrapperStyle = React.useMemo(() => {
|
||||
const nextWrapperStyle = {};
|
||||
if (pushed && pushDistance) {
|
||||
switch (placement) {
|
||||
case 'top':
|
||||
nextWrapperStyle.transform = `translateY(${pushDistance}px)`;
|
||||
break;
|
||||
case 'bottom':
|
||||
nextWrapperStyle.transform = `translateY(${-pushDistance}px)`;
|
||||
break;
|
||||
case 'left':
|
||||
nextWrapperStyle.transform = `translateX(${pushDistance}px)`;
|
||||
break;
|
||||
default:
|
||||
nextWrapperStyle.transform = `translateX(${-pushDistance}px)`;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isHorizontal) {
|
||||
nextWrapperStyle.width = (0, _util.parseWidthHeight)(mergedSize);
|
||||
} else {
|
||||
nextWrapperStyle.height = (0, _util.parseWidthHeight)(mergedSize);
|
||||
}
|
||||
return nextWrapperStyle;
|
||||
}, [pushed, pushDistance, placement, isHorizontal, mergedSize]);
|
||||
|
||||
// =========================== Resize ===========================
|
||||
const wrapperRef = React.useRef(null);
|
||||
const isResizable = !!resizable;
|
||||
const resizeConfig = typeof resizable === 'object' && resizable || {};
|
||||
const onInternalResize = (0, _util2.useEvent)(size => {
|
||||
setCurrentSize(size);
|
||||
resizeConfig.onResize?.(size);
|
||||
});
|
||||
const {
|
||||
dragElementProps,
|
||||
isDragging
|
||||
} = (0, _useDrag.default)({
|
||||
prefixCls: `${prefixCls}-resizable`,
|
||||
direction: placement,
|
||||
className: drawerClassNames?.dragger,
|
||||
style: styles?.dragger,
|
||||
maxSize,
|
||||
containerRef: wrapperRef,
|
||||
currentSize: mergedSize,
|
||||
onResize: onInternalResize,
|
||||
onResizeStart: resizeConfig.onResizeStart,
|
||||
onResizeEnd: resizeConfig.onResizeEnd
|
||||
});
|
||||
|
||||
// =========================== Events ===========================
|
||||
const eventHandlers = {
|
||||
onMouseEnter,
|
||||
onMouseOver,
|
||||
onMouseLeave,
|
||||
onClick,
|
||||
onKeyDown,
|
||||
onKeyUp,
|
||||
onFocus: e => {
|
||||
ignoreElement(e.target);
|
||||
}
|
||||
};
|
||||
|
||||
// =========================== Render ==========================
|
||||
// >>>>> Panel
|
||||
const panelNode = /*#__PURE__*/React.createElement(_motion.default, _extends({
|
||||
key: "panel"
|
||||
}, motionProps, {
|
||||
visible: open,
|
||||
forceRender: forceRender,
|
||||
onVisibleChanged: afterOpenChange,
|
||||
removeOnLeave: false,
|
||||
leavedClassName: `${prefixCls}-content-wrapper-hidden`
|
||||
}), ({
|
||||
className: motionClassName,
|
||||
style: motionStyle
|
||||
}, motionRef) => {
|
||||
const content = /*#__PURE__*/React.createElement(_DrawerPanel.default, _extends({
|
||||
id: id,
|
||||
containerRef: motionRef,
|
||||
prefixCls: prefixCls,
|
||||
className: (0, _clsx.clsx)(className, drawerClassNames?.section),
|
||||
style: {
|
||||
...style,
|
||||
...styles?.section
|
||||
}
|
||||
}, (0, _pickAttrs.default)(props, {
|
||||
aria: true
|
||||
}), eventHandlers), children);
|
||||
return /*#__PURE__*/React.createElement("div", _extends({
|
||||
ref: wrapperRef,
|
||||
className: (0, _clsx.clsx)(`${prefixCls}-content-wrapper`, isDragging && `${prefixCls}-content-wrapper-dragging`, drawerClassNames?.wrapper, !isDragging && motionClassName),
|
||||
style: {
|
||||
...motionStyle,
|
||||
...wrapperStyle,
|
||||
...styles?.wrapper
|
||||
}
|
||||
}, (0, _pickAttrs.default)(props, {
|
||||
data: true
|
||||
})), isResizable && /*#__PURE__*/React.createElement("div", dragElementProps), drawerRender ? drawerRender(content) : content);
|
||||
});
|
||||
|
||||
// >>>>> Container
|
||||
const containerStyle = {
|
||||
...rootStyle
|
||||
};
|
||||
if (zIndex) {
|
||||
containerStyle.zIndex = zIndex;
|
||||
}
|
||||
return /*#__PURE__*/React.createElement(_context.default.Provider, {
|
||||
value: mergedContext
|
||||
}, /*#__PURE__*/React.createElement("div", {
|
||||
className: (0, _clsx.clsx)(prefixCls, `${prefixCls}-${placement}`, rootClassName, {
|
||||
[`${prefixCls}-open`]: open,
|
||||
[`${prefixCls}-inline`]: inline
|
||||
}),
|
||||
style: containerStyle,
|
||||
tabIndex: -1,
|
||||
ref: panelRef
|
||||
}, maskNode, panelNode));
|
||||
};
|
||||
const RefDrawerPopup = /*#__PURE__*/React.forwardRef(DrawerPopup);
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
RefDrawerPopup.displayName = 'DrawerPopup';
|
||||
}
|
||||
var _default = exports.default = RefDrawerPopup;
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
import * as React from 'react';
|
||||
export interface DrawerContextProps {
|
||||
pushDistance?: number | string;
|
||||
push: VoidFunction;
|
||||
pull: VoidFunction;
|
||||
}
|
||||
declare const DrawerContext: React.Context<DrawerContextProps>;
|
||||
export interface RefContextProps {
|
||||
panel?: React.Ref<HTMLDivElement>;
|
||||
}
|
||||
export declare const RefContext: React.Context<RefContextProps>;
|
||||
export default DrawerContext;
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = exports.RefContext = void 0;
|
||||
var React = _interopRequireWildcard(require("react"));
|
||||
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
||||
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
||||
const DrawerContext = /*#__PURE__*/React.createContext(null);
|
||||
const RefContext = exports.RefContext = /*#__PURE__*/React.createContext({});
|
||||
var _default = exports.default = DrawerContext;
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
import * as React from 'react';
|
||||
import type { Placement } from '../Drawer';
|
||||
export interface UseDragOptions {
|
||||
prefixCls: string;
|
||||
direction: Placement;
|
||||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
maxSize?: number;
|
||||
containerRef?: React.RefObject<HTMLElement>;
|
||||
currentSize?: number | string;
|
||||
onResize?: (size: number) => void;
|
||||
onResizeEnd?: (size: number) => void;
|
||||
onResizeStart?: (size: number) => void;
|
||||
}
|
||||
export interface UseDragReturn {
|
||||
dragElementProps: {
|
||||
className: string;
|
||||
style: React.CSSProperties;
|
||||
onMouseDown: (e: React.MouseEvent) => void;
|
||||
};
|
||||
isDragging: boolean;
|
||||
}
|
||||
export default function useDrag(options: UseDragOptions): UseDragReturn;
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = useDrag;
|
||||
var React = _interopRequireWildcard(require("react"));
|
||||
var _clsx = require("clsx");
|
||||
var _util = require("@rc-component/util");
|
||||
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
||||
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
||||
function useDrag(options) {
|
||||
const {
|
||||
prefixCls,
|
||||
direction,
|
||||
className,
|
||||
style,
|
||||
maxSize,
|
||||
containerRef,
|
||||
currentSize,
|
||||
onResize,
|
||||
onResizeEnd,
|
||||
onResizeStart
|
||||
} = options;
|
||||
const [isDragging, setIsDragging] = React.useState(false);
|
||||
const [startPos, setStartPos] = React.useState(0);
|
||||
const [startSize, setStartSize] = React.useState(0);
|
||||
const isHorizontal = direction === 'left' || direction === 'right';
|
||||
const handleMouseDown = (0, _util.useEvent)(e => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
setIsDragging(true);
|
||||
if (isHorizontal) {
|
||||
setStartPos(e.clientX);
|
||||
} else {
|
||||
setStartPos(e.clientY);
|
||||
}
|
||||
|
||||
// Use provided currentSize, or fallback to container size
|
||||
let startSize;
|
||||
if (typeof currentSize === 'number') {
|
||||
startSize = currentSize;
|
||||
} else if (containerRef?.current) {
|
||||
const rect = containerRef.current.getBoundingClientRect();
|
||||
startSize = isHorizontal ? rect.width : rect.height;
|
||||
}
|
||||
setStartSize(startSize);
|
||||
onResizeStart?.(startSize);
|
||||
});
|
||||
const handleMouseMove = (0, _util.useEvent)(e => {
|
||||
if (!isDragging) return;
|
||||
const currentPos = isHorizontal ? e.clientX : e.clientY;
|
||||
let delta = currentPos - startPos;
|
||||
|
||||
// Adjust delta direction based on placement
|
||||
if (direction === 'right' || direction === 'bottom') {
|
||||
delta = -delta;
|
||||
}
|
||||
let newSize = startSize + delta;
|
||||
|
||||
// Apply min/max size limits
|
||||
if (newSize < 0) {
|
||||
newSize = 0;
|
||||
}
|
||||
// Only apply maxSize if it's a valid positive number
|
||||
if (maxSize && newSize > maxSize) {
|
||||
newSize = maxSize;
|
||||
}
|
||||
onResize?.(newSize);
|
||||
});
|
||||
const handleMouseUp = React.useCallback(() => {
|
||||
if (isDragging) {
|
||||
setIsDragging(false);
|
||||
|
||||
// Get the final size after resize
|
||||
if (containerRef?.current) {
|
||||
const rect = containerRef.current.getBoundingClientRect();
|
||||
const finalSize = isHorizontal ? rect.width : rect.height;
|
||||
onResizeEnd?.(finalSize);
|
||||
}
|
||||
}
|
||||
}, [isDragging, containerRef, onResizeEnd, isHorizontal]);
|
||||
React.useEffect(() => {
|
||||
if (isDragging) {
|
||||
document.addEventListener('mousemove', handleMouseMove);
|
||||
document.addEventListener('mouseup', handleMouseUp);
|
||||
return () => {
|
||||
document.removeEventListener('mousemove', handleMouseMove);
|
||||
document.removeEventListener('mouseup', handleMouseUp);
|
||||
};
|
||||
}
|
||||
}, [isDragging, handleMouseMove, handleMouseUp]);
|
||||
const dragElementClassName = (0, _clsx.clsx)(`${prefixCls}-dragger`, `${prefixCls}-dragger-${direction}`, {
|
||||
[`${prefixCls}-dragger-dragging`]: isDragging,
|
||||
[`${prefixCls}-dragger-horizontal`]: isHorizontal,
|
||||
[`${prefixCls}-dragger-vertical`]: !isHorizontal
|
||||
}, className);
|
||||
return {
|
||||
dragElementProps: {
|
||||
className: dragElementClassName,
|
||||
style,
|
||||
onMouseDown: handleMouseDown
|
||||
},
|
||||
isDragging
|
||||
};
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
export default function useFocusable(getContainer: () => HTMLElement, open: boolean, autoFocus?: boolean, focusTrap?: boolean, mask?: boolean): (ele: HTMLElement) => void;
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = useFocusable;
|
||||
var _react = _interopRequireDefault(require("react"));
|
||||
var _focus = require("@rc-component/util/lib/Dom/focus");
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
function useFocusable(getContainer, open, autoFocus, focusTrap, mask) {
|
||||
const mergedFocusTrap = focusTrap ?? mask !== false;
|
||||
|
||||
// Focus lock
|
||||
const [ignoreElement] = (0, _focus.useLockFocus)(open && mergedFocusTrap, getContainer);
|
||||
|
||||
// Auto Focus
|
||||
_react.default.useEffect(() => {
|
||||
if (open && autoFocus === true) {
|
||||
getContainer()?.focus({
|
||||
preventScroll: true
|
||||
});
|
||||
}
|
||||
}, [open]);
|
||||
return ignoreElement;
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
import Drawer from './Drawer';
|
||||
import type { DrawerProps, Placement } from './Drawer';
|
||||
export type { DrawerProps, Placement };
|
||||
export default Drawer;
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = void 0;
|
||||
var _Drawer = _interopRequireDefault(require("./Drawer"));
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
// export this package's api
|
||||
var _default = exports.default = _Drawer.default;
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
/// <reference types="react" />
|
||||
export interface DrawerClassNames {
|
||||
mask?: string;
|
||||
wrapper?: string;
|
||||
section?: string;
|
||||
dragger?: string;
|
||||
}
|
||||
export interface DrawerStyles {
|
||||
mask?: React.CSSProperties;
|
||||
wrapper?: React.CSSProperties;
|
||||
section?: React.CSSProperties;
|
||||
dragger?: React.CSSProperties;
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
import type { DrawerProps } from './Drawer';
|
||||
export declare function parseWidthHeight(value?: number | string): string | number;
|
||||
export declare function warnCheck(props: DrawerProps): void;
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.parseWidthHeight = parseWidthHeight;
|
||||
exports.warnCheck = warnCheck;
|
||||
var _warning = _interopRequireDefault(require("@rc-component/util/lib/warning"));
|
||||
var _canUseDom = _interopRequireDefault(require("@rc-component/util/lib/Dom/canUseDom"));
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
function parseWidthHeight(value) {
|
||||
if (typeof value === 'string') {
|
||||
const num = Number(value.replace(/px$/i, ''));
|
||||
const floatNum = parseFloat(value);
|
||||
if (floatNum === num) {
|
||||
(0, _warning.default)(false, 'Invalid value type of `width` or `height` which should be number type instead.');
|
||||
}
|
||||
if (!Number.isNaN(num)) {
|
||||
return num;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
function warnCheck(props) {
|
||||
(0, _warning.default)(!('wrapperClassName' in props), `'wrapperClassName' is removed. Please use 'rootClassName' instead.`);
|
||||
(0, _warning.default)((0, _canUseDom.default)() || !props.open, `Drawer with 'open' in SSR is not work since no place to createPortal. Please move to 'useEffect' instead.`);
|
||||
}
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
{
|
||||
"name": "@rc-component/drawer",
|
||||
"version": "1.4.2",
|
||||
"description": "drawer component for react",
|
||||
"keywords": [
|
||||
"react",
|
||||
"react-component",
|
||||
"react-drawer",
|
||||
"drawer",
|
||||
"drawer-menu",
|
||||
"rc-drawer-menu",
|
||||
"react-drawer-menu",
|
||||
"animation",
|
||||
"drawer-motion",
|
||||
"drawer-animation"
|
||||
],
|
||||
"homepage": "https://github.com/react-component/drawer",
|
||||
"bugs": {
|
||||
"url": "https://github.com/react-component/drawer/issues"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/react-component/drawer.git"
|
||||
},
|
||||
"license": "MIT",
|
||||
"author": "155259966@qq.com",
|
||||
"main": "./lib/index",
|
||||
"module": "./es/index",
|
||||
"files": [
|
||||
"lib",
|
||||
"assets/*.css",
|
||||
"es"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "dumi build",
|
||||
"compile": "father build && lessc assets/index.less assets/index.css",
|
||||
"lint": "eslint src/ --ext .tsx,.ts",
|
||||
"now-build": "npm run build",
|
||||
"prepublishOnly": "npm run compile && rc-np",
|
||||
"start": "dumi dev",
|
||||
"test": "rc-test",
|
||||
"prettier": "prettier --write .",
|
||||
"prepare": "husky"
|
||||
},
|
||||
"dependencies": {
|
||||
"@rc-component/motion": "^1.1.4",
|
||||
"@rc-component/portal": "^2.1.3",
|
||||
"@rc-component/util": "^1.9.0",
|
||||
"clsx": "^2.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@ant-design/icons": "^5.3.0",
|
||||
"@rc-component/father-plugin": "^2.0.2",
|
||||
"@rc-component/np": "^1.0.0",
|
||||
"@testing-library/jest-dom": "^6.2.0",
|
||||
"@testing-library/react": "^16.3.0",
|
||||
"@types/jest": "^29.5.11",
|
||||
"@types/node": "^25.0.0",
|
||||
"@types/raf": "^3.4.0",
|
||||
"@types/react": "^19.0.0",
|
||||
"@types/react-dom": "^19.0.0",
|
||||
"@types/warning": "^3.0.0",
|
||||
"antd": "^5.12.7",
|
||||
"dumi": "^2.2.0",
|
||||
"eslint": "^8.56.0",
|
||||
"eslint-plugin-jest": "^29.0.1",
|
||||
"eslint-plugin-unicorn": "^51.0.1",
|
||||
"father": "^4.0.0",
|
||||
"glob": "^10.3.10",
|
||||
"husky": "^9.0.10",
|
||||
"less": "^4.2.0",
|
||||
"lint-staged": "^16.1.5",
|
||||
"prettier": "^3.0.0",
|
||||
"rc-test": "^7.0.9",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"stylelint": "^17.1.0",
|
||||
"stylelint-config-standard-less": "^4.0.0",
|
||||
"typescript": "^5.3.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=18.0.0",
|
||||
"react-dom": ">=18.0.0"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{js,jsx,ts,tsx}": "eslint --fix",
|
||||
"*.{css,less}": "stylelint --fix",
|
||||
"*.{js,jsx,ts,tsx,json,less,css,md}": "prettier --write"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user