@rc-component/drawer 1.3.0 → 1.4.1

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/README.md CHANGED
@@ -1,13 +1,22 @@
1
1
  # @rc-component/drawer
2
2
 
3
- [![NPM version][npm-image]][npm-url] [![dumi](https://img.shields.io/badge/docs%20by-dumi-blue?style=flat-square)](https://github.com/umijs/dumi) [![build status][github-actions-image]][github-actions-url] [![codecov](https://codecov.io/gh/react-component/drawer/branch/master/graph/badge.svg)](https://codecov.io/gh/react-component/drawer) [![npm download][download-image]][download-url]
3
+ [![NPM version][npm-image]][npm-url]
4
+ [![npm download][download-image]][download-url]
5
+ [![build status][github-actions-image]][github-actions-url]
6
+ [![codecov][codecov-image]][codecov-url]
7
+ [![dumi][dumi-image]][dumi-url]
4
8
 
5
9
  [npm-image]: http://img.shields.io/npm/v/@rc-component/drawer.svg?style=flat-square
6
10
  [npm-url]: http://npmjs.org/package/@rc-component/drawer
11
+ [dumi-image]: https://img.shields.io/badge/docs%20by-dumi-blue?style=flat-square
12
+ [dumi-url]: https://github.com/umijs/dumi
7
13
  [github-actions-image]: https://github.com/react-component/drawer/actions/workflows/react-component-ci.yml/badge.svg
8
14
  [github-actions-url]: https://github.com/react-component/drawer/actions/workflows/react-component-ci.yml
9
15
  [download-image]: https://img.shields.io/npm/dm/@rc-component/drawer.svg?style=flat-square
10
16
  [download-url]: https://npmjs.org/package/@rc-component/drawer
17
+ [codecov-image]: https://codecov.io/gh/react-component/drawer/branch/master/graph/badge.svg
18
+ [codecov-url]: https://codecov.io/gh/react-component/drawer
19
+
11
20
 
12
21
  ## Example
13
22
 
package/assets/index.css CHANGED
@@ -18,6 +18,7 @@
18
18
  position: absolute;
19
19
  z-index: 1050;
20
20
  transition: transform 0.3s;
21
+ pointer-events: auto;
21
22
  }
22
23
  .rc-drawer-content-wrapper-hidden {
23
24
  display: none;
package/es/Drawer.d.ts CHANGED
@@ -7,7 +7,7 @@ export type Placement = 'left' | 'top' | 'right' | 'bottom';
7
7
  export interface DrawerProps extends Omit<DrawerPopupProps, 'prefixCls' | 'inline' | 'scrollLocker'>, DrawerPanelEvents, DrawerPanelAccessibility {
8
8
  prefixCls?: string;
9
9
  open?: boolean;
10
- onClose?: (e: React.MouseEvent | React.KeyboardEvent) => void;
10
+ onClose?: (e: React.MouseEvent | React.KeyboardEvent | KeyboardEvent) => void;
11
11
  destroyOnHidden?: boolean;
12
12
  getContainer?: PortalProps['getContainer'];
13
13
  panelRef?: React.Ref<HTMLDivElement>;
@@ -33,6 +33,7 @@ export interface DrawerProps extends Omit<DrawerPopupProps, 'prefixCls' | 'inlin
33
33
  onResizeStart?: () => void;
34
34
  onResizeEnd?: () => void;
35
35
  };
36
+ focusTriggerAfterClose?: boolean;
36
37
  }
37
38
  declare const Drawer: React.FC<DrawerProps>;
38
39
  export default Drawer;
package/es/Drawer.js CHANGED
@@ -27,8 +27,10 @@ const Drawer = props => {
27
27
  onClick,
28
28
  onKeyDown,
29
29
  onKeyUp,
30
+ onClose,
30
31
  resizable,
31
32
  defaultSize,
33
+ focusTriggerAfterClose,
32
34
  // Refs
33
35
  panelRef
34
36
  } = props;
@@ -59,7 +61,7 @@ const Drawer = props => {
59
61
  const internalAfterOpenChange = nextVisible => {
60
62
  setAnimatedVisible(nextVisible);
61
63
  afterOpenChange?.(nextVisible);
62
- if (!nextVisible && lastActiveRef.current && !popupRef.current?.contains(lastActiveRef.current)) {
64
+ if (!nextVisible && focusTriggerAfterClose !== false && lastActiveRef.current && !popupRef.current?.contains(lastActiveRef.current)) {
63
65
  lastActiveRef.current?.focus({
64
66
  preventScroll: true
65
67
  });
@@ -103,13 +105,23 @@ const Drawer = props => {
103
105
  resizable,
104
106
  ...eventHandlers
105
107
  };
108
+ const onEsc = ({
109
+ top,
110
+ event
111
+ }) => {
112
+ if (top && keyboard) {
113
+ event.stopPropagation();
114
+ onClose?.(event);
115
+ }
116
+ };
106
117
  return /*#__PURE__*/React.createElement(RefContext.Provider, {
107
118
  value: refContext
108
119
  }, /*#__PURE__*/React.createElement(Portal, {
109
120
  open: mergedOpen || forceRender || animatedVisible,
110
121
  autoDestroy: false,
111
122
  getContainer: getContainer,
112
- autoLock: mask && (mergedOpen || animatedVisible)
123
+ autoLock: mask && (mergedOpen || animatedVisible),
124
+ onEsc: onEsc
113
125
  }, /*#__PURE__*/React.createElement(DrawerPopup, drawerPopupProps)));
114
126
  };
115
127
  if (process.env.NODE_ENV !== 'production') {
@@ -12,8 +12,9 @@ export interface DrawerPopupProps extends DrawerPanelEvents, DrawerPanelAccessib
12
12
  inline?: boolean;
13
13
  push?: boolean | PushConfig;
14
14
  forceRender?: boolean;
15
- autoFocus?: boolean;
16
15
  keyboard?: boolean;
16
+ autoFocus?: boolean;
17
+ focusTrap?: boolean;
17
18
  rootClassName?: string;
18
19
  rootStyle?: React.CSSProperties;
19
20
  zIndex?: number;
package/es/DrawerPopup.js CHANGED
@@ -1,7 +1,6 @@
1
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
2
  import { clsx } from 'clsx';
3
3
  import CSSMotion from '@rc-component/motion';
4
- import KeyCode from "@rc-component/util/es/KeyCode";
5
4
  import pickAttrs from "@rc-component/util/es/pickAttrs";
6
5
  import * as React from 'react';
7
6
  import DrawerContext from "./context";
@@ -9,13 +8,7 @@ import DrawerPanel from "./DrawerPanel";
9
8
  import useDrag from "./hooks/useDrag";
10
9
  import { parseWidthHeight } from "./util";
11
10
  import { useEvent } from '@rc-component/util';
12
- const sentinelStyle = {
13
- width: 0,
14
- height: 0,
15
- overflow: 'hidden',
16
- outline: 'none',
17
- position: 'absolute'
18
- };
11
+ import useFocusable from "./hooks/useFocusable";
19
12
  const DrawerPopup = (props, ref) => {
20
13
  const {
21
14
  prefixCls,
@@ -24,8 +17,9 @@ const DrawerPopup = (props, ref) => {
24
17
  inline,
25
18
  push,
26
19
  forceRender,
20
+ // Focus
27
21
  autoFocus,
28
- keyboard,
22
+ focusTrap,
29
23
  // classNames
30
24
  classNames: drawerClassNames,
31
25
  // Root
@@ -65,53 +59,10 @@ const DrawerPopup = (props, ref) => {
65
59
 
66
60
  // ================================ Refs ================================
67
61
  const panelRef = React.useRef(null);
68
- const sentinelStartRef = React.useRef(null);
69
- const sentinelEndRef = React.useRef(null);
70
62
  React.useImperativeHandle(ref, () => panelRef.current);
71
- const onPanelKeyDown = event => {
72
- const {
73
- keyCode,
74
- shiftKey
75
- } = event;
76
- switch (keyCode) {
77
- // Tab active
78
- case KeyCode.TAB:
79
- {
80
- if (keyCode === KeyCode.TAB) {
81
- if (!shiftKey && document.activeElement === sentinelEndRef.current) {
82
- sentinelStartRef.current?.focus({
83
- preventScroll: true
84
- });
85
- } else if (shiftKey && document.activeElement === sentinelStartRef.current) {
86
- sentinelEndRef.current?.focus({
87
- preventScroll: true
88
- });
89
- }
90
- }
91
- break;
92
- }
93
-
94
- // Close
95
- case KeyCode.ESC:
96
- {
97
- if (onClose && keyboard) {
98
- event.stopPropagation();
99
- onClose(event);
100
- }
101
- break;
102
- }
103
- }
104
- };
105
63
 
106
- // ========================== Control ===========================
107
- // Auto Focus
108
- React.useEffect(() => {
109
- if (open && autoFocus) {
110
- panelRef.current?.focus({
111
- preventScroll: true
112
- });
113
- }
114
- }, [open]);
64
+ // ========================= Focusable ==========================
65
+ useFocusable(() => panelRef.current, open, autoFocus, focusTrap, mask);
115
66
 
116
67
  // ============================ Push ============================
117
68
  const [pushed, setPushed] = React.useState(false);
@@ -253,9 +204,7 @@ const DrawerPopup = (props, ref) => {
253
204
  }, motionProps, {
254
205
  visible: open,
255
206
  forceRender: forceRender,
256
- onVisibleChanged: nextVisible => {
257
- afterOpenChange?.(nextVisible);
258
- },
207
+ onVisibleChanged: afterOpenChange,
259
208
  removeOnLeave: false,
260
209
  leavedClassName: `${prefixCls}-content-wrapper-hidden`
261
210
  }), ({
@@ -303,21 +252,8 @@ const DrawerPopup = (props, ref) => {
303
252
  }),
304
253
  style: containerStyle,
305
254
  tabIndex: -1,
306
- ref: panelRef,
307
- onKeyDown: onPanelKeyDown
308
- }, maskNode, /*#__PURE__*/React.createElement("div", {
309
- tabIndex: 0,
310
- ref: sentinelStartRef,
311
- style: sentinelStyle,
312
- "aria-hidden": "true",
313
- "data-sentinel": "start"
314
- }), panelNode, /*#__PURE__*/React.createElement("div", {
315
- tabIndex: 0,
316
- ref: sentinelEndRef,
317
- style: sentinelStyle,
318
- "aria-hidden": "true",
319
- "data-sentinel": "end"
320
- })));
255
+ ref: panelRef
256
+ }, maskNode, panelNode));
321
257
  };
322
258
  const RefDrawerPopup = /*#__PURE__*/React.forwardRef(DrawerPopup);
323
259
  if (process.env.NODE_ENV !== 'production') {
@@ -0,0 +1 @@
1
+ export default function useFocusable(getContainer: () => HTMLElement, open: boolean, autoFocus?: boolean, focusTrap?: boolean, mask?: boolean): void;
@@ -0,0 +1,17 @@
1
+ import React from 'react';
2
+ import { useLockFocus } from "@rc-component/util/es/Dom/focus";
3
+ export default function useFocusable(getContainer, open, autoFocus, focusTrap, mask) {
4
+ const mergedFocusTrap = focusTrap ?? mask !== false;
5
+
6
+ // Focus lock
7
+ useLockFocus(open && mergedFocusTrap, getContainer);
8
+
9
+ // Auto Focus
10
+ React.useEffect(() => {
11
+ if (open && autoFocus === true) {
12
+ getContainer()?.focus({
13
+ preventScroll: true
14
+ });
15
+ }
16
+ }, [open]);
17
+ }
package/lib/Drawer.d.ts CHANGED
@@ -7,7 +7,7 @@ export type Placement = 'left' | 'top' | 'right' | 'bottom';
7
7
  export interface DrawerProps extends Omit<DrawerPopupProps, 'prefixCls' | 'inline' | 'scrollLocker'>, DrawerPanelEvents, DrawerPanelAccessibility {
8
8
  prefixCls?: string;
9
9
  open?: boolean;
10
- onClose?: (e: React.MouseEvent | React.KeyboardEvent) => void;
10
+ onClose?: (e: React.MouseEvent | React.KeyboardEvent | KeyboardEvent) => void;
11
11
  destroyOnHidden?: boolean;
12
12
  getContainer?: PortalProps['getContainer'];
13
13
  panelRef?: React.Ref<HTMLDivElement>;
@@ -33,6 +33,7 @@ export interface DrawerProps extends Omit<DrawerPopupProps, 'prefixCls' | 'inlin
33
33
  onResizeStart?: () => void;
34
34
  onResizeEnd?: () => void;
35
35
  };
36
+ focusTriggerAfterClose?: boolean;
36
37
  }
37
38
  declare const Drawer: React.FC<DrawerProps>;
38
39
  export default Drawer;
package/lib/Drawer.js CHANGED
@@ -36,8 +36,10 @@ const Drawer = props => {
36
36
  onClick,
37
37
  onKeyDown,
38
38
  onKeyUp,
39
+ onClose,
39
40
  resizable,
40
41
  defaultSize,
42
+ focusTriggerAfterClose,
41
43
  // Refs
42
44
  panelRef
43
45
  } = props;
@@ -68,7 +70,7 @@ const Drawer = props => {
68
70
  const internalAfterOpenChange = nextVisible => {
69
71
  setAnimatedVisible(nextVisible);
70
72
  afterOpenChange?.(nextVisible);
71
- if (!nextVisible && lastActiveRef.current && !popupRef.current?.contains(lastActiveRef.current)) {
73
+ if (!nextVisible && focusTriggerAfterClose !== false && lastActiveRef.current && !popupRef.current?.contains(lastActiveRef.current)) {
72
74
  lastActiveRef.current?.focus({
73
75
  preventScroll: true
74
76
  });
@@ -112,13 +114,23 @@ const Drawer = props => {
112
114
  resizable,
113
115
  ...eventHandlers
114
116
  };
117
+ const onEsc = ({
118
+ top,
119
+ event
120
+ }) => {
121
+ if (top && keyboard) {
122
+ event.stopPropagation();
123
+ onClose?.(event);
124
+ }
125
+ };
115
126
  return /*#__PURE__*/React.createElement(_context.RefContext.Provider, {
116
127
  value: refContext
117
128
  }, /*#__PURE__*/React.createElement(_portal.default, {
118
129
  open: mergedOpen || forceRender || animatedVisible,
119
130
  autoDestroy: false,
120
131
  getContainer: getContainer,
121
- autoLock: mask && (mergedOpen || animatedVisible)
132
+ autoLock: mask && (mergedOpen || animatedVisible),
133
+ onEsc: onEsc
122
134
  }, /*#__PURE__*/React.createElement(_DrawerPopup.default, drawerPopupProps)));
123
135
  };
124
136
  if (process.env.NODE_ENV !== 'production') {
@@ -12,8 +12,9 @@ export interface DrawerPopupProps extends DrawerPanelEvents, DrawerPanelAccessib
12
12
  inline?: boolean;
13
13
  push?: boolean | PushConfig;
14
14
  forceRender?: boolean;
15
- autoFocus?: boolean;
16
15
  keyboard?: boolean;
16
+ autoFocus?: boolean;
17
+ focusTrap?: boolean;
17
18
  rootClassName?: string;
18
19
  rootStyle?: React.CSSProperties;
19
20
  zIndex?: number;
@@ -6,7 +6,6 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.default = void 0;
7
7
  var _clsx = require("clsx");
8
8
  var _motion = _interopRequireDefault(require("@rc-component/motion"));
9
- var _KeyCode = _interopRequireDefault(require("@rc-component/util/lib/KeyCode"));
10
9
  var _pickAttrs = _interopRequireDefault(require("@rc-component/util/lib/pickAttrs"));
11
10
  var React = _interopRequireWildcard(require("react"));
12
11
  var _context = _interopRequireDefault(require("./context"));
@@ -14,17 +13,11 @@ var _DrawerPanel = _interopRequireDefault(require("./DrawerPanel"));
14
13
  var _useDrag = _interopRequireDefault(require("./hooks/useDrag"));
15
14
  var _util = require("./util");
16
15
  var _util2 = require("@rc-component/util");
16
+ var _useFocusable = _interopRequireDefault(require("./hooks/useFocusable"));
17
17
  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); }
18
18
  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; }
19
19
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
20
20
  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); }
21
- const sentinelStyle = {
22
- width: 0,
23
- height: 0,
24
- overflow: 'hidden',
25
- outline: 'none',
26
- position: 'absolute'
27
- };
28
21
  const DrawerPopup = (props, ref) => {
29
22
  const {
30
23
  prefixCls,
@@ -33,8 +26,9 @@ const DrawerPopup = (props, ref) => {
33
26
  inline,
34
27
  push,
35
28
  forceRender,
29
+ // Focus
36
30
  autoFocus,
37
- keyboard,
31
+ focusTrap,
38
32
  // classNames
39
33
  classNames: drawerClassNames,
40
34
  // Root
@@ -74,53 +68,10 @@ const DrawerPopup = (props, ref) => {
74
68
 
75
69
  // ================================ Refs ================================
76
70
  const panelRef = React.useRef(null);
77
- const sentinelStartRef = React.useRef(null);
78
- const sentinelEndRef = React.useRef(null);
79
71
  React.useImperativeHandle(ref, () => panelRef.current);
80
- const onPanelKeyDown = event => {
81
- const {
82
- keyCode,
83
- shiftKey
84
- } = event;
85
- switch (keyCode) {
86
- // Tab active
87
- case _KeyCode.default.TAB:
88
- {
89
- if (keyCode === _KeyCode.default.TAB) {
90
- if (!shiftKey && document.activeElement === sentinelEndRef.current) {
91
- sentinelStartRef.current?.focus({
92
- preventScroll: true
93
- });
94
- } else if (shiftKey && document.activeElement === sentinelStartRef.current) {
95
- sentinelEndRef.current?.focus({
96
- preventScroll: true
97
- });
98
- }
99
- }
100
- break;
101
- }
102
-
103
- // Close
104
- case _KeyCode.default.ESC:
105
- {
106
- if (onClose && keyboard) {
107
- event.stopPropagation();
108
- onClose(event);
109
- }
110
- break;
111
- }
112
- }
113
- };
114
72
 
115
- // ========================== Control ===========================
116
- // Auto Focus
117
- React.useEffect(() => {
118
- if (open && autoFocus) {
119
- panelRef.current?.focus({
120
- preventScroll: true
121
- });
122
- }
123
- }, [open]);
73
+ // ========================= Focusable ==========================
74
+ (0, _useFocusable.default)(() => panelRef.current, open, autoFocus, focusTrap, mask);
124
75
 
125
76
  // ============================ Push ============================
126
77
  const [pushed, setPushed] = React.useState(false);
@@ -262,9 +213,7 @@ const DrawerPopup = (props, ref) => {
262
213
  }, motionProps, {
263
214
  visible: open,
264
215
  forceRender: forceRender,
265
- onVisibleChanged: nextVisible => {
266
- afterOpenChange?.(nextVisible);
267
- },
216
+ onVisibleChanged: afterOpenChange,
268
217
  removeOnLeave: false,
269
218
  leavedClassName: `${prefixCls}-content-wrapper-hidden`
270
219
  }), ({
@@ -312,21 +261,8 @@ const DrawerPopup = (props, ref) => {
312
261
  }),
313
262
  style: containerStyle,
314
263
  tabIndex: -1,
315
- ref: panelRef,
316
- onKeyDown: onPanelKeyDown
317
- }, maskNode, /*#__PURE__*/React.createElement("div", {
318
- tabIndex: 0,
319
- ref: sentinelStartRef,
320
- style: sentinelStyle,
321
- "aria-hidden": "true",
322
- "data-sentinel": "start"
323
- }), panelNode, /*#__PURE__*/React.createElement("div", {
324
- tabIndex: 0,
325
- ref: sentinelEndRef,
326
- style: sentinelStyle,
327
- "aria-hidden": "true",
328
- "data-sentinel": "end"
329
- })));
264
+ ref: panelRef
265
+ }, maskNode, panelNode));
330
266
  };
331
267
  const RefDrawerPopup = /*#__PURE__*/React.forwardRef(DrawerPopup);
332
268
  if (process.env.NODE_ENV !== 'production') {
@@ -0,0 +1 @@
1
+ export default function useFocusable(getContainer: () => HTMLElement, open: boolean, autoFocus?: boolean, focusTrap?: boolean, mask?: boolean): void;
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = useFocusable;
7
+ var _react = _interopRequireDefault(require("react"));
8
+ var _focus = require("@rc-component/util/lib/Dom/focus");
9
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
10
+ function useFocusable(getContainer, open, autoFocus, focusTrap, mask) {
11
+ const mergedFocusTrap = focusTrap ?? mask !== false;
12
+
13
+ // Focus lock
14
+ (0, _focus.useLockFocus)(open && mergedFocusTrap, getContainer);
15
+
16
+ // Auto Focus
17
+ _react.default.useEffect(() => {
18
+ if (open && autoFocus === true) {
19
+ getContainer()?.focus({
20
+ preventScroll: true
21
+ });
22
+ }
23
+ }, [open]);
24
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rc-component/drawer",
3
- "version": "1.3.0",
3
+ "version": "1.4.1",
4
4
  "description": "drawer component for react",
5
5
  "keywords": [
6
6
  "react",
@@ -44,8 +44,8 @@
44
44
  },
45
45
  "dependencies": {
46
46
  "@rc-component/motion": "^1.1.4",
47
- "@rc-component/portal": "^2.0.0",
48
- "@rc-component/util": "^1.2.1",
47
+ "@rc-component/portal": "^2.1.3",
48
+ "@rc-component/util": "^1.7.0",
49
49
  "clsx": "^2.1.1"
50
50
  },
51
51
  "devDependencies": {
@@ -55,7 +55,7 @@
55
55
  "@testing-library/jest-dom": "^6.2.0",
56
56
  "@testing-library/react": "^16.3.0",
57
57
  "@types/jest": "^29.5.11",
58
- "@types/node": "^24.3.0",
58
+ "@types/node": "^25.0.0",
59
59
  "@types/raf": "^3.4.0",
60
60
  "@types/react": "^19.0.0",
61
61
  "@types/react-dom": "^19.0.0",
@@ -75,7 +75,7 @@
75
75
  "react": "^19.0.0",
76
76
  "react-dom": "^19.0.0",
77
77
  "stylelint": "^16.2.1",
78
- "stylelint-config-standard-less": "^3.0.1",
78
+ "stylelint-config-standard-less": "^4.0.0",
79
79
  "typescript": "^5.3.3"
80
80
  },
81
81
  "peerDependencies": {