@rc-component/drawer 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/README.md +81 -0
- package/assets/index.css +42 -0
- package/es/Drawer.d.ts +18 -0
- package/es/Drawer.js +108 -0
- package/es/DrawerPanel.d.ts +26 -0
- package/es/DrawerPanel.js +34 -0
- package/es/DrawerPopup.d.ts +40 -0
- package/es/DrawerPopup.js +274 -0
- package/es/context.d.ts +12 -0
- package/es/context.js +4 -0
- package/es/index.d.ts +4 -0
- package/es/index.js +3 -0
- package/es/inter.d.ts +11 -0
- package/es/inter.js +1 -0
- package/es/util.d.ts +3 -0
- package/es/util.js +13 -0
- package/lib/Drawer.d.ts +18 -0
- package/lib/Drawer.js +117 -0
- package/lib/DrawerPanel.d.ts +26 -0
- package/lib/DrawerPanel.js +43 -0
- package/lib/DrawerPopup.d.ts +40 -0
- package/lib/DrawerPopup.js +283 -0
- package/lib/context.d.ts +12 -0
- package/lib/context.js +12 -0
- package/lib/index.d.ts +4 -0
- package/lib/index.js +10 -0
- package/lib/inter.d.ts +11 -0
- package/lib/inter.js +5 -0
- package/lib/util.d.ts +3 -0
- package/lib/util.js +21 -0
- package/package.json +90 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT LICENSE
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2015-present Alipay.com, https://www.alipay.com/
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
6
|
+
a copy of this software and associated documentation files (the
|
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
11
|
+
the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be
|
|
14
|
+
included in all copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# rc-drawer
|
|
2
|
+
|
|
3
|
+
[![NPM version][npm-image]][npm-url] [](https://github.com/umijs/dumi) [![build status][github-actions-image]][github-actions-url] [](https://codecov.io/gh/react-component/drawer) [![node version][node-image]][node-url] [![npm download][download-image]][download-url]
|
|
4
|
+
|
|
5
|
+
[npm-image]: http://img.shields.io/npm/v/rc-drawer.svg?style=flat-square
|
|
6
|
+
[npm-url]: http://npmjs.org/package/rc-drawer
|
|
7
|
+
[github-actions-image]: https://github.com/react-component/drawer/workflows/CI/badge.svg
|
|
8
|
+
[github-actions-url]: https://github.com/react-component/drawer/actions
|
|
9
|
+
[node-image]: https://img.shields.io/badge/node.js-%3E=_0.10-green.svg?style=flat-square
|
|
10
|
+
[node-url]: http://nodejs.org/download/
|
|
11
|
+
[download-image]: https://img.shields.io/npm/dm/rc-drawer.svg?style=flat-square
|
|
12
|
+
[download-url]: https://npmjs.org/package/rc-drawer
|
|
13
|
+
|
|
14
|
+
## Example
|
|
15
|
+
|
|
16
|
+
https://drawer-react-component.vercel.app/
|
|
17
|
+
|
|
18
|
+
## Usage
|
|
19
|
+
|
|
20
|
+
```js
|
|
21
|
+
import Drawer from 'rc-drawer';
|
|
22
|
+
import React from 'react';
|
|
23
|
+
import ReactDom from 'react-dom';
|
|
24
|
+
|
|
25
|
+
ReactDom.render(
|
|
26
|
+
<Drawer>
|
|
27
|
+
{menu children}
|
|
28
|
+
</Drawer>
|
|
29
|
+
, mountNode);
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Install
|
|
33
|
+
|
|
34
|
+
[](https://npmjs.org/package/rc-drawer)
|
|
35
|
+
|
|
36
|
+
## Browser Support
|
|
37
|
+
|
|
38
|
+
|  |  |  |  |  |
|
|
39
|
+
| ------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
|
|
40
|
+
| IE 10+ ✔ | Chrome 31.0+ ✔ | Firefox 31.0+ ✔ | Opera 30.0+ ✔ | Safari 7.0+ ✔ |
|
|
41
|
+
|
|
42
|
+
## API
|
|
43
|
+
|
|
44
|
+
| props | type | default | description |
|
|
45
|
+
| ------------------ | --------------------------------------------------------------------------- | -------------------------------------- | ----------------------------------------------------------------------------- |
|
|
46
|
+
| className | string | null | - |
|
|
47
|
+
| classNames | { mask?: string; content?: string; wrapper?: string; } | - | pass className to target area |
|
|
48
|
+
| styles | { mask?: CSSProperties; content?: CSSProperties; wrapper?: CSSProperties; } | - | pass style to target area |
|
|
49
|
+
| prefixCls | string | 'drawer' | prefix class |
|
|
50
|
+
| width | string \| number | null | drawer content wrapper width, drawer level transition width |
|
|
51
|
+
| height | string \| number | null | drawer content wrapper height, drawer level transition height |
|
|
52
|
+
| open | boolean | false | open or close menu |
|
|
53
|
+
| defaultOpen | boolean | false | default open menu |
|
|
54
|
+
| placement | string | `left` | `left` `top` `right` `bottom` |
|
|
55
|
+
| level | string \| array | `all` | With the drawer level element. `all`/ null / className / id / tagName / array |
|
|
56
|
+
| levelMove | number \| array \| func | null | level move value. default is drawer width |
|
|
57
|
+
| duration | string | `.3s` | level animation duration |
|
|
58
|
+
| ease | string | `cubic-bezier(0.78, 0.14, 0.15, 0.86)` | level animation timing function |
|
|
59
|
+
| getContainer | string \| func \| HTMLElement | `body` | Return the mount node for Drawer. if is `null` use React.creactElement |
|
|
60
|
+
| showMask | boolean | true | mask is show |
|
|
61
|
+
| maskClosable | boolean | true | Clicking on the mask (area outside the Drawer) to close the Drawer or not. |
|
|
62
|
+
| maskStyle | CSSProperties | null | mask style |
|
|
63
|
+
| afterOpenChange | func | null | transition end callback(open) |
|
|
64
|
+
| onClose | func | null | close click function |
|
|
65
|
+
| keyboard | boolean | true | Whether support press esc to close |
|
|
66
|
+
| autoFocus | boolean | true | Whether focusing on the drawer after it opened |
|
|
67
|
+
| onMouseEnter | React.MouseEventHandler\<HTMLDivElement\> | - | Trigger when mouse enter drawer panel |
|
|
68
|
+
| onMouseOver | React.MouseEventHandler\<HTMLDivElement\> | - | Trigger when mouse over drawer panel |
|
|
69
|
+
| onMouseLeave | React.MouseEventHandler\<HTMLDivElement\> | - | Trigger when mouse leave drawer panel |
|
|
70
|
+
| onClick | React.MouseEventHandler\<HTMLDivElement\> | - | Trigger when mouse click drawer panel |
|
|
71
|
+
| onKeyDown | React.MouseEventHandler\<HTMLDivElement\> | - | Trigger when mouse keydown on drawer panel |
|
|
72
|
+
| onKeyUp | React.MouseEventHandler\<HTMLDivElement\> | - | Trigger when mouse keyup on drawer panel |
|
|
73
|
+
|
|
74
|
+
> 2.0 Rename `onMaskClick` -> `onClose`, add `maskClosable`.
|
|
75
|
+
|
|
76
|
+
## Development
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
npm install
|
|
80
|
+
npm start
|
|
81
|
+
```
|
package/assets/index.css
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
.rc-drawer {
|
|
2
|
+
inset: 0;
|
|
3
|
+
position: fixed;
|
|
4
|
+
z-index: 1050;
|
|
5
|
+
pointer-events: none;
|
|
6
|
+
}
|
|
7
|
+
.rc-drawer-inline {
|
|
8
|
+
position: absolute;
|
|
9
|
+
}
|
|
10
|
+
.rc-drawer-mask {
|
|
11
|
+
inset: 0;
|
|
12
|
+
position: absolute;
|
|
13
|
+
z-index: 1050;
|
|
14
|
+
background: rgba(0, 0, 0, 0.5);
|
|
15
|
+
pointer-events: auto;
|
|
16
|
+
}
|
|
17
|
+
.rc-drawer-content-wrapper {
|
|
18
|
+
position: absolute;
|
|
19
|
+
z-index: 1050;
|
|
20
|
+
overflow: hidden;
|
|
21
|
+
transition: transform 0.3s;
|
|
22
|
+
}
|
|
23
|
+
.rc-drawer-content-wrapper-hidden {
|
|
24
|
+
display: none;
|
|
25
|
+
}
|
|
26
|
+
.rc-drawer-left .rc-drawer-content-wrapper {
|
|
27
|
+
top: 0;
|
|
28
|
+
bottom: 0;
|
|
29
|
+
left: 0;
|
|
30
|
+
}
|
|
31
|
+
.rc-drawer-right .rc-drawer-content-wrapper {
|
|
32
|
+
top: 0;
|
|
33
|
+
right: 0;
|
|
34
|
+
bottom: 0;
|
|
35
|
+
}
|
|
36
|
+
.rc-drawer-content {
|
|
37
|
+
width: 100%;
|
|
38
|
+
height: 100%;
|
|
39
|
+
overflow: auto;
|
|
40
|
+
background: #fff;
|
|
41
|
+
pointer-events: auto;
|
|
42
|
+
}
|
package/es/Drawer.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { PortalProps } from '@rc-component/portal';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import type { DrawerPanelAccessibility, DrawerPanelEvents } from './DrawerPanel';
|
|
4
|
+
import type { DrawerPopupProps } from './DrawerPopup';
|
|
5
|
+
import type { DrawerClassNames, DrawerStyles } from './inter';
|
|
6
|
+
export type Placement = 'left' | 'top' | 'right' | 'bottom';
|
|
7
|
+
export interface DrawerProps extends Omit<DrawerPopupProps, 'prefixCls' | 'inline' | 'scrollLocker'>, DrawerPanelEvents, DrawerPanelAccessibility {
|
|
8
|
+
prefixCls?: string;
|
|
9
|
+
open?: boolean;
|
|
10
|
+
onClose?: (e: React.MouseEvent | React.KeyboardEvent) => void;
|
|
11
|
+
destroyOnClose?: boolean;
|
|
12
|
+
getContainer?: PortalProps['getContainer'];
|
|
13
|
+
panelRef?: React.Ref<HTMLDivElement>;
|
|
14
|
+
classNames?: DrawerClassNames;
|
|
15
|
+
styles?: DrawerStyles;
|
|
16
|
+
}
|
|
17
|
+
declare const Drawer: React.FC<DrawerProps>;
|
|
18
|
+
export default Drawer;
|
package/es/Drawer.js
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import Portal from '@rc-component/portal';
|
|
2
|
+
import useLayoutEffect from "@rc-component/util/es/hooks/useLayoutEffect";
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { RefContext } from "./context";
|
|
5
|
+
import DrawerPopup from "./DrawerPopup";
|
|
6
|
+
import { warnCheck } from "./util";
|
|
7
|
+
const Drawer = props => {
|
|
8
|
+
const {
|
|
9
|
+
open = false,
|
|
10
|
+
prefixCls = 'rc-drawer',
|
|
11
|
+
placement = 'right',
|
|
12
|
+
autoFocus = true,
|
|
13
|
+
keyboard = true,
|
|
14
|
+
width = 378,
|
|
15
|
+
mask = true,
|
|
16
|
+
maskClosable = true,
|
|
17
|
+
getContainer,
|
|
18
|
+
forceRender,
|
|
19
|
+
afterOpenChange,
|
|
20
|
+
destroyOnClose,
|
|
21
|
+
onMouseEnter,
|
|
22
|
+
onMouseOver,
|
|
23
|
+
onMouseLeave,
|
|
24
|
+
onClick,
|
|
25
|
+
onKeyDown,
|
|
26
|
+
onKeyUp,
|
|
27
|
+
// Refs
|
|
28
|
+
panelRef
|
|
29
|
+
} = props;
|
|
30
|
+
const [animatedVisible, setAnimatedVisible] = React.useState(false);
|
|
31
|
+
|
|
32
|
+
// ============================= Warn =============================
|
|
33
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
34
|
+
warnCheck(props);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// ============================= Open =============================
|
|
38
|
+
const [mounted, setMounted] = React.useState(false);
|
|
39
|
+
useLayoutEffect(() => {
|
|
40
|
+
setMounted(true);
|
|
41
|
+
}, []);
|
|
42
|
+
const mergedOpen = mounted ? open : false;
|
|
43
|
+
|
|
44
|
+
// ============================ Focus =============================
|
|
45
|
+
const popupRef = React.useRef(null);
|
|
46
|
+
const lastActiveRef = React.useRef(null);
|
|
47
|
+
useLayoutEffect(() => {
|
|
48
|
+
if (mergedOpen) {
|
|
49
|
+
lastActiveRef.current = document.activeElement;
|
|
50
|
+
}
|
|
51
|
+
}, [mergedOpen]);
|
|
52
|
+
|
|
53
|
+
// ============================= Open =============================
|
|
54
|
+
const internalAfterOpenChange = nextVisible => {
|
|
55
|
+
setAnimatedVisible(nextVisible);
|
|
56
|
+
afterOpenChange?.(nextVisible);
|
|
57
|
+
if (!nextVisible && lastActiveRef.current && !popupRef.current?.contains(lastActiveRef.current)) {
|
|
58
|
+
lastActiveRef.current?.focus({
|
|
59
|
+
preventScroll: true
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
// =========================== Context ============================
|
|
65
|
+
const refContext = React.useMemo(() => ({
|
|
66
|
+
panel: panelRef
|
|
67
|
+
}), [panelRef]);
|
|
68
|
+
|
|
69
|
+
// ============================ Render ============================
|
|
70
|
+
if (!forceRender && !animatedVisible && !mergedOpen && destroyOnClose) {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
const eventHandlers = {
|
|
74
|
+
onMouseEnter,
|
|
75
|
+
onMouseOver,
|
|
76
|
+
onMouseLeave,
|
|
77
|
+
onClick,
|
|
78
|
+
onKeyDown,
|
|
79
|
+
onKeyUp
|
|
80
|
+
};
|
|
81
|
+
const drawerPopupProps = {
|
|
82
|
+
...props,
|
|
83
|
+
open: mergedOpen,
|
|
84
|
+
prefixCls,
|
|
85
|
+
placement,
|
|
86
|
+
autoFocus,
|
|
87
|
+
keyboard,
|
|
88
|
+
width,
|
|
89
|
+
mask,
|
|
90
|
+
maskClosable,
|
|
91
|
+
inline: getContainer === false,
|
|
92
|
+
afterOpenChange: internalAfterOpenChange,
|
|
93
|
+
ref: popupRef,
|
|
94
|
+
...eventHandlers
|
|
95
|
+
};
|
|
96
|
+
return /*#__PURE__*/React.createElement(RefContext.Provider, {
|
|
97
|
+
value: refContext
|
|
98
|
+
}, /*#__PURE__*/React.createElement(Portal, {
|
|
99
|
+
open: mergedOpen || forceRender || animatedVisible,
|
|
100
|
+
autoDestroy: false,
|
|
101
|
+
getContainer: getContainer,
|
|
102
|
+
autoLock: mask && (mergedOpen || animatedVisible)
|
|
103
|
+
}, /*#__PURE__*/React.createElement(DrawerPopup, drawerPopupProps)));
|
|
104
|
+
};
|
|
105
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
106
|
+
Drawer.displayName = 'Drawer';
|
|
107
|
+
}
|
|
108
|
+
export default Drawer;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
export interface DrawerPanelRef {
|
|
3
|
+
focus: VoidFunction;
|
|
4
|
+
}
|
|
5
|
+
export interface DrawerPanelEvents {
|
|
6
|
+
onMouseEnter?: React.MouseEventHandler<HTMLDivElement>;
|
|
7
|
+
onMouseOver?: React.MouseEventHandler<HTMLDivElement>;
|
|
8
|
+
onMouseLeave?: React.MouseEventHandler<HTMLDivElement>;
|
|
9
|
+
onClick?: React.MouseEventHandler<HTMLDivElement>;
|
|
10
|
+
onKeyDown?: React.KeyboardEventHandler<HTMLDivElement>;
|
|
11
|
+
onKeyUp?: React.KeyboardEventHandler<HTMLDivElement>;
|
|
12
|
+
}
|
|
13
|
+
export type DrawerPanelAccessibility = Pick<React.DialogHTMLAttributes<HTMLDivElement>, keyof React.AriaAttributes>;
|
|
14
|
+
export interface DrawerPanelProps extends DrawerPanelEvents, DrawerPanelAccessibility {
|
|
15
|
+
prefixCls: string;
|
|
16
|
+
className?: string;
|
|
17
|
+
id?: string;
|
|
18
|
+
style?: React.CSSProperties;
|
|
19
|
+
children?: React.ReactNode;
|
|
20
|
+
containerRef?: React.Ref<HTMLDivElement>;
|
|
21
|
+
}
|
|
22
|
+
declare const DrawerPanel: {
|
|
23
|
+
(props: DrawerPanelProps): React.JSX.Element;
|
|
24
|
+
displayName: string;
|
|
25
|
+
};
|
|
26
|
+
export default DrawerPanel;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
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); }
|
|
2
|
+
import classNames from 'classnames';
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { RefContext } from "./context";
|
|
5
|
+
import pickAttrs from "@rc-component/util/es/pickAttrs";
|
|
6
|
+
import { useComposeRef } from "@rc-component/util/es/ref";
|
|
7
|
+
const DrawerPanel = props => {
|
|
8
|
+
const {
|
|
9
|
+
prefixCls,
|
|
10
|
+
className,
|
|
11
|
+
containerRef,
|
|
12
|
+
...restProps
|
|
13
|
+
} = props;
|
|
14
|
+
const {
|
|
15
|
+
panel: panelRef
|
|
16
|
+
} = React.useContext(RefContext);
|
|
17
|
+
const mergedRef = useComposeRef(panelRef, containerRef);
|
|
18
|
+
|
|
19
|
+
// =============================== Render ===============================
|
|
20
|
+
|
|
21
|
+
return /*#__PURE__*/React.createElement("div", _extends({
|
|
22
|
+
className: classNames(`${prefixCls}-section`, className),
|
|
23
|
+
role: "dialog",
|
|
24
|
+
ref: mergedRef
|
|
25
|
+
}, pickAttrs(props, {
|
|
26
|
+
aria: true
|
|
27
|
+
}), {
|
|
28
|
+
"aria-modal": "true"
|
|
29
|
+
}, restProps));
|
|
30
|
+
};
|
|
31
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
32
|
+
DrawerPanel.displayName = 'DrawerPanel';
|
|
33
|
+
}
|
|
34
|
+
export default DrawerPanel;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { CSSMotionProps } from '@rc-component/motion';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import type { DrawerPanelAccessibility, DrawerPanelEvents } from './DrawerPanel';
|
|
4
|
+
import type { DrawerClassNames, DrawerStyles } from './inter';
|
|
5
|
+
export type Placement = 'left' | 'right' | 'top' | 'bottom';
|
|
6
|
+
export interface PushConfig {
|
|
7
|
+
distance?: number | string;
|
|
8
|
+
}
|
|
9
|
+
export interface DrawerPopupProps extends DrawerPanelEvents, DrawerPanelAccessibility {
|
|
10
|
+
prefixCls: string;
|
|
11
|
+
open?: boolean;
|
|
12
|
+
inline?: boolean;
|
|
13
|
+
push?: boolean | PushConfig;
|
|
14
|
+
forceRender?: boolean;
|
|
15
|
+
autoFocus?: boolean;
|
|
16
|
+
keyboard?: boolean;
|
|
17
|
+
rootClassName?: string;
|
|
18
|
+
rootStyle?: React.CSSProperties;
|
|
19
|
+
zIndex?: number;
|
|
20
|
+
placement?: Placement;
|
|
21
|
+
id?: string;
|
|
22
|
+
className?: string;
|
|
23
|
+
style?: React.CSSProperties;
|
|
24
|
+
children?: React.ReactNode;
|
|
25
|
+
width?: number | string;
|
|
26
|
+
height?: number | string;
|
|
27
|
+
mask?: boolean;
|
|
28
|
+
maskClosable?: boolean;
|
|
29
|
+
maskClassName?: string;
|
|
30
|
+
maskStyle?: React.CSSProperties;
|
|
31
|
+
motion?: CSSMotionProps | ((placement: Placement) => CSSMotionProps);
|
|
32
|
+
maskMotion?: CSSMotionProps;
|
|
33
|
+
afterOpenChange?: (open: boolean) => void;
|
|
34
|
+
onClose?: (event: React.MouseEvent<HTMLElement> | React.KeyboardEvent<HTMLElement>) => void;
|
|
35
|
+
classNames?: DrawerClassNames;
|
|
36
|
+
styles?: DrawerStyles;
|
|
37
|
+
drawerRender?: (node: React.ReactNode) => React.ReactNode;
|
|
38
|
+
}
|
|
39
|
+
declare const RefDrawerPopup: React.ForwardRefExoticComponent<DrawerPopupProps & React.RefAttributes<HTMLDivElement>>;
|
|
40
|
+
export default RefDrawerPopup;
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
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); }
|
|
2
|
+
import classNames from 'classnames';
|
|
3
|
+
import CSSMotion from '@rc-component/motion';
|
|
4
|
+
import KeyCode from "@rc-component/util/es/KeyCode";
|
|
5
|
+
import pickAttrs from "@rc-component/util/es/pickAttrs";
|
|
6
|
+
import * as React from 'react';
|
|
7
|
+
import DrawerContext from "./context";
|
|
8
|
+
import DrawerPanel from "./DrawerPanel";
|
|
9
|
+
import { parseWidthHeight } from "./util";
|
|
10
|
+
const sentinelStyle = {
|
|
11
|
+
width: 0,
|
|
12
|
+
height: 0,
|
|
13
|
+
overflow: 'hidden',
|
|
14
|
+
outline: 'none',
|
|
15
|
+
position: 'absolute'
|
|
16
|
+
};
|
|
17
|
+
function DrawerPopup(props, ref) {
|
|
18
|
+
const {
|
|
19
|
+
prefixCls,
|
|
20
|
+
open,
|
|
21
|
+
placement,
|
|
22
|
+
inline,
|
|
23
|
+
push,
|
|
24
|
+
forceRender,
|
|
25
|
+
autoFocus,
|
|
26
|
+
keyboard,
|
|
27
|
+
// classNames
|
|
28
|
+
classNames: drawerClassNames,
|
|
29
|
+
// Root
|
|
30
|
+
rootClassName,
|
|
31
|
+
rootStyle,
|
|
32
|
+
zIndex,
|
|
33
|
+
// Drawer
|
|
34
|
+
className,
|
|
35
|
+
id,
|
|
36
|
+
style,
|
|
37
|
+
motion,
|
|
38
|
+
width,
|
|
39
|
+
height,
|
|
40
|
+
children,
|
|
41
|
+
// Mask
|
|
42
|
+
mask,
|
|
43
|
+
maskClosable,
|
|
44
|
+
maskMotion,
|
|
45
|
+
maskClassName,
|
|
46
|
+
maskStyle,
|
|
47
|
+
// Events
|
|
48
|
+
afterOpenChange,
|
|
49
|
+
onClose,
|
|
50
|
+
onMouseEnter,
|
|
51
|
+
onMouseOver,
|
|
52
|
+
onMouseLeave,
|
|
53
|
+
onClick,
|
|
54
|
+
onKeyDown,
|
|
55
|
+
onKeyUp,
|
|
56
|
+
styles,
|
|
57
|
+
drawerRender
|
|
58
|
+
} = props;
|
|
59
|
+
|
|
60
|
+
// ================================ Refs ================================
|
|
61
|
+
const panelRef = React.useRef(null);
|
|
62
|
+
const sentinelStartRef = React.useRef(null);
|
|
63
|
+
const sentinelEndRef = React.useRef(null);
|
|
64
|
+
React.useImperativeHandle(ref, () => panelRef.current);
|
|
65
|
+
const onPanelKeyDown = event => {
|
|
66
|
+
const {
|
|
67
|
+
keyCode,
|
|
68
|
+
shiftKey
|
|
69
|
+
} = event;
|
|
70
|
+
switch (keyCode) {
|
|
71
|
+
// Tab active
|
|
72
|
+
case KeyCode.TAB:
|
|
73
|
+
{
|
|
74
|
+
if (keyCode === KeyCode.TAB) {
|
|
75
|
+
if (!shiftKey && document.activeElement === sentinelEndRef.current) {
|
|
76
|
+
sentinelStartRef.current?.focus({
|
|
77
|
+
preventScroll: true
|
|
78
|
+
});
|
|
79
|
+
} else if (shiftKey && document.activeElement === sentinelStartRef.current) {
|
|
80
|
+
sentinelEndRef.current?.focus({
|
|
81
|
+
preventScroll: true
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Close
|
|
89
|
+
case KeyCode.ESC:
|
|
90
|
+
{
|
|
91
|
+
if (onClose && keyboard) {
|
|
92
|
+
event.stopPropagation();
|
|
93
|
+
onClose(event);
|
|
94
|
+
}
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
// ========================== Control ===========================
|
|
101
|
+
// Auto Focus
|
|
102
|
+
React.useEffect(() => {
|
|
103
|
+
if (open && autoFocus) {
|
|
104
|
+
panelRef.current?.focus({
|
|
105
|
+
preventScroll: true
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}, [open]);
|
|
109
|
+
|
|
110
|
+
// ============================ Push ============================
|
|
111
|
+
const [pushed, setPushed] = React.useState(false);
|
|
112
|
+
const parentContext = React.useContext(DrawerContext);
|
|
113
|
+
|
|
114
|
+
// Merge push distance
|
|
115
|
+
let pushConfig;
|
|
116
|
+
if (typeof push === 'boolean') {
|
|
117
|
+
pushConfig = push ? {} : {
|
|
118
|
+
distance: 0
|
|
119
|
+
};
|
|
120
|
+
} else {
|
|
121
|
+
pushConfig = push || {};
|
|
122
|
+
}
|
|
123
|
+
const pushDistance = pushConfig?.distance ?? parentContext?.pushDistance ?? 180;
|
|
124
|
+
const mergedContext = React.useMemo(() => ({
|
|
125
|
+
pushDistance,
|
|
126
|
+
push: () => {
|
|
127
|
+
setPushed(true);
|
|
128
|
+
},
|
|
129
|
+
pull: () => {
|
|
130
|
+
setPushed(false);
|
|
131
|
+
}
|
|
132
|
+
}), [pushDistance]);
|
|
133
|
+
|
|
134
|
+
// ========================= ScrollLock =========================
|
|
135
|
+
// Tell parent to push
|
|
136
|
+
React.useEffect(() => {
|
|
137
|
+
if (open) {
|
|
138
|
+
parentContext?.push?.();
|
|
139
|
+
} else {
|
|
140
|
+
parentContext?.pull?.();
|
|
141
|
+
}
|
|
142
|
+
}, [open]);
|
|
143
|
+
|
|
144
|
+
// Clean up
|
|
145
|
+
React.useEffect(() => () => {
|
|
146
|
+
parentContext?.pull?.();
|
|
147
|
+
}, []);
|
|
148
|
+
|
|
149
|
+
// ============================ Mask ============================
|
|
150
|
+
const maskNode = mask && /*#__PURE__*/React.createElement(CSSMotion, _extends({
|
|
151
|
+
key: "mask"
|
|
152
|
+
}, maskMotion, {
|
|
153
|
+
visible: open
|
|
154
|
+
}), ({
|
|
155
|
+
className: motionMaskClassName,
|
|
156
|
+
style: motionMaskStyle
|
|
157
|
+
}, maskRef) => /*#__PURE__*/React.createElement("div", {
|
|
158
|
+
className: classNames(`${prefixCls}-mask`, motionMaskClassName, drawerClassNames?.mask, maskClassName),
|
|
159
|
+
style: {
|
|
160
|
+
...motionMaskStyle,
|
|
161
|
+
...maskStyle,
|
|
162
|
+
...styles?.mask
|
|
163
|
+
},
|
|
164
|
+
onClick: maskClosable && open ? onClose : undefined,
|
|
165
|
+
ref: maskRef
|
|
166
|
+
}));
|
|
167
|
+
|
|
168
|
+
// =========================== Panel ============================
|
|
169
|
+
const motionProps = typeof motion === 'function' ? motion(placement) : motion;
|
|
170
|
+
const wrapperStyle = {};
|
|
171
|
+
if (pushed && pushDistance) {
|
|
172
|
+
switch (placement) {
|
|
173
|
+
case 'top':
|
|
174
|
+
wrapperStyle.transform = `translateY(${pushDistance}px)`;
|
|
175
|
+
break;
|
|
176
|
+
case 'bottom':
|
|
177
|
+
wrapperStyle.transform = `translateY(${-pushDistance}px)`;
|
|
178
|
+
break;
|
|
179
|
+
case 'left':
|
|
180
|
+
wrapperStyle.transform = `translateX(${pushDistance}px)`;
|
|
181
|
+
break;
|
|
182
|
+
default:
|
|
183
|
+
wrapperStyle.transform = `translateX(${-pushDistance}px)`;
|
|
184
|
+
break;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
if (placement === 'left' || placement === 'right') {
|
|
188
|
+
wrapperStyle.width = parseWidthHeight(width);
|
|
189
|
+
} else {
|
|
190
|
+
wrapperStyle.height = parseWidthHeight(height);
|
|
191
|
+
}
|
|
192
|
+
const eventHandlers = {
|
|
193
|
+
onMouseEnter,
|
|
194
|
+
onMouseOver,
|
|
195
|
+
onMouseLeave,
|
|
196
|
+
onClick,
|
|
197
|
+
onKeyDown,
|
|
198
|
+
onKeyUp
|
|
199
|
+
};
|
|
200
|
+
const panelNode = /*#__PURE__*/React.createElement(CSSMotion, _extends({
|
|
201
|
+
key: "panel"
|
|
202
|
+
}, motionProps, {
|
|
203
|
+
visible: open,
|
|
204
|
+
forceRender: forceRender,
|
|
205
|
+
onVisibleChanged: nextVisible => {
|
|
206
|
+
afterOpenChange?.(nextVisible);
|
|
207
|
+
},
|
|
208
|
+
removeOnLeave: false,
|
|
209
|
+
leavedClassName: `${prefixCls}-content-wrapper-hidden`
|
|
210
|
+
}), ({
|
|
211
|
+
className: motionClassName,
|
|
212
|
+
style: motionStyle
|
|
213
|
+
}, motionRef) => {
|
|
214
|
+
const content = /*#__PURE__*/React.createElement(DrawerPanel, _extends({
|
|
215
|
+
id: id,
|
|
216
|
+
containerRef: motionRef,
|
|
217
|
+
prefixCls: prefixCls,
|
|
218
|
+
className: classNames(className, drawerClassNames?.section),
|
|
219
|
+
style: {
|
|
220
|
+
...style,
|
|
221
|
+
...styles?.section
|
|
222
|
+
}
|
|
223
|
+
}, pickAttrs(props, {
|
|
224
|
+
aria: true
|
|
225
|
+
}), eventHandlers), children);
|
|
226
|
+
return /*#__PURE__*/React.createElement("div", _extends({
|
|
227
|
+
className: classNames(`${prefixCls}-content-wrapper`, drawerClassNames?.wrapper, motionClassName),
|
|
228
|
+
style: {
|
|
229
|
+
...wrapperStyle,
|
|
230
|
+
...motionStyle,
|
|
231
|
+
...styles?.wrapper
|
|
232
|
+
}
|
|
233
|
+
}, pickAttrs(props, {
|
|
234
|
+
data: true
|
|
235
|
+
})), drawerRender ? drawerRender(content) : content);
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
// =========================== Render ===========================
|
|
239
|
+
const containerStyle = {
|
|
240
|
+
...rootStyle
|
|
241
|
+
};
|
|
242
|
+
if (zIndex) {
|
|
243
|
+
containerStyle.zIndex = zIndex;
|
|
244
|
+
}
|
|
245
|
+
return /*#__PURE__*/React.createElement(DrawerContext.Provider, {
|
|
246
|
+
value: mergedContext
|
|
247
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
248
|
+
className: classNames(prefixCls, `${prefixCls}-${placement}`, rootClassName, {
|
|
249
|
+
[`${prefixCls}-open`]: open,
|
|
250
|
+
[`${prefixCls}-inline`]: inline
|
|
251
|
+
}),
|
|
252
|
+
style: containerStyle,
|
|
253
|
+
tabIndex: -1,
|
|
254
|
+
ref: panelRef,
|
|
255
|
+
onKeyDown: onPanelKeyDown
|
|
256
|
+
}, maskNode, /*#__PURE__*/React.createElement("div", {
|
|
257
|
+
tabIndex: 0,
|
|
258
|
+
ref: sentinelStartRef,
|
|
259
|
+
style: sentinelStyle,
|
|
260
|
+
"aria-hidden": "true",
|
|
261
|
+
"data-sentinel": "start"
|
|
262
|
+
}), panelNode, /*#__PURE__*/React.createElement("div", {
|
|
263
|
+
tabIndex: 0,
|
|
264
|
+
ref: sentinelEndRef,
|
|
265
|
+
style: sentinelStyle,
|
|
266
|
+
"aria-hidden": "true",
|
|
267
|
+
"data-sentinel": "end"
|
|
268
|
+
})));
|
|
269
|
+
}
|
|
270
|
+
const RefDrawerPopup = /*#__PURE__*/React.forwardRef(DrawerPopup);
|
|
271
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
272
|
+
RefDrawerPopup.displayName = 'DrawerPopup';
|
|
273
|
+
}
|
|
274
|
+
export default RefDrawerPopup;
|
package/es/context.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
export interface DrawerContextProps {
|
|
3
|
+
pushDistance?: number | string;
|
|
4
|
+
push: VoidFunction;
|
|
5
|
+
pull: VoidFunction;
|
|
6
|
+
}
|
|
7
|
+
declare const DrawerContext: React.Context<DrawerContextProps>;
|
|
8
|
+
export interface RefContextProps {
|
|
9
|
+
panel?: React.Ref<HTMLDivElement>;
|
|
10
|
+
}
|
|
11
|
+
export declare const RefContext: React.Context<RefContextProps>;
|
|
12
|
+
export default DrawerContext;
|
package/es/context.js
ADDED
package/es/index.d.ts
ADDED