Skip to content

Commit 1e91a7e

Browse files
committed
feat: support useFocusLock
1 parent a5a7e61 commit 1e91a7e

File tree

6 files changed

+47
-44
lines changed

6 files changed

+47
-44
lines changed

assets/index.less

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
z-index: @zIndex;
3333
// overflow: hidden;
3434
transition: transform @duration;
35+
pointer-events: auto;
3536

3637
&-hidden {
3738
display: none;

docs/examples/base.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ const Demo = () => {
2727
{...motionProps}
2828
>
2929
content
30+
<button>Button 1</button>
31+
<button>Button 2</button>
3032
</Drawer>
3133
<div>
3234
<button onClick={onSwitch}>打开</button>

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
},
4545
"dependencies": {
4646
"@rc-component/motion": "^1.1.4",
47-
"@rc-component/portal": "^2.0.0",
47+
"@rc-component/portal": "^2.1.3",
4848
"@rc-component/util": "^1.2.1",
4949
"clsx": "^2.1.1"
5050
},

src/DrawerPopup.tsx

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import useDrag from './hooks/useDrag';
1414
import { parseWidthHeight } from './util';
1515
import type { DrawerClassNames, DrawerStyles } from './inter';
1616
import { useEvent } from '@rc-component/util';
17-
import { useLockFocus } from '@rc-component/util/lib/Dom/focus';
17+
import useFocusable from './hooks/useFocusable';
1818

1919
export type Placement = 'left' | 'right' | 'top' | 'bottom';
2020

@@ -23,7 +23,7 @@ export interface PushConfig {
2323
}
2424

2525
export interface FocusableConfig {
26-
autoFocus?: boolean | string;
26+
autoFocus?: boolean;
2727
focusTriggerAfterClose?: boolean;
2828
trap?: boolean;
2929
}
@@ -105,7 +105,10 @@ const DrawerPopup: React.ForwardRefRenderFunction<
105105
inline,
106106
push,
107107
forceRender,
108+
109+
// Focus
108110
autoFocus,
111+
focusable,
109112

110113
// classNames
111114
classNames: drawerClassNames,
@@ -153,15 +156,8 @@ const DrawerPopup: React.ForwardRefRenderFunction<
153156

154157
React.useImperativeHandle(ref, () => panelRef.current);
155158

156-
useLockFocus(open, () => panelRef.current);
157-
158-
// ========================== Control ===========================
159-
// Auto Focus
160-
React.useEffect(() => {
161-
if (open && autoFocus) {
162-
panelRef.current?.focus({ preventScroll: true });
163-
}
164-
}, [open]);
159+
// ========================= Focusable ==========================
160+
useFocusable(() => panelRef.current, open, focusable, autoFocus, mask);
165161

166162
// ============================ Push ============================
167163
const [pushed, setPushed] = React.useState(false);

src/hooks/useFocusable.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import React from 'react';
2+
import type { FocusableConfig } from '../DrawerPopup';
3+
import { useLockFocus } from '@rc-component/util/lib/Dom/focus';
4+
5+
export default function useFocusable(
6+
getContainer: () => HTMLElement,
7+
open: boolean,
8+
focusable?: FocusableConfig,
9+
autoFocus?: boolean,
10+
mask?: boolean,
11+
) {
12+
// Parse config
13+
const config = React.useMemo<FocusableConfig>(() => {
14+
const merged: FocusableConfig = {
15+
trap: mask !== false,
16+
focusTriggerAfterClose: true,
17+
...focusable,
18+
};
19+
20+
if (autoFocus !== undefined) {
21+
merged.autoFocus = autoFocus;
22+
}
23+
24+
return merged;
25+
}, [focusable, autoFocus, mask]);
26+
27+
// Focus lock
28+
useLockFocus(open && config.trap, getContainer);
29+
30+
// Auto Focus
31+
React.useEffect(() => {
32+
if (open && config.autoFocus === true) {
33+
getContainer()?.focus({ preventScroll: true });
34+
}
35+
}, [open]);
36+
}

tests/index.spec.tsx

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { cleanup, fireEvent, render, act } from '@testing-library/react';
2-
import KeyCode from '@rc-component/util/lib/KeyCode';
32
import { resetWarned } from '@rc-component/util/lib/warning';
43
import React from 'react';
54
import type { DrawerProps } from '../src';
@@ -265,37 +264,6 @@ describe('rc-drawer-menu', () => {
265264
expect(container.contains(document.activeElement)).toBeTruthy();
266265
});
267266

268-
it('tab should always in the content', () => {
269-
const { container } = render(
270-
<Drawer open getContainer={false}>
271-
<div>Hello World</div>
272-
</Drawer>,
273-
);
274-
275-
const firstSentinel = container.querySelector<HTMLElement>(
276-
'[data-sentinel="start"]',
277-
);
278-
const lastSentinel = container.querySelector<HTMLElement>(
279-
'[data-sentinel="end"]',
280-
);
281-
282-
// First shift to last
283-
firstSentinel.focus();
284-
fireEvent.keyDown(firstSentinel, {
285-
shiftKey: true,
286-
keyCode: KeyCode.TAB,
287-
which: KeyCode.TAB,
288-
});
289-
expect(document.activeElement).toBe(lastSentinel);
290-
291-
// Last tab to first
292-
fireEvent.keyDown(lastSentinel, {
293-
keyCode: KeyCode.TAB,
294-
which: KeyCode.TAB,
295-
});
296-
expect(document.activeElement).toBe(firstSentinel);
297-
});
298-
299267
describe('keyboard', () => {
300268
it('ESC to exit', () => {
301269
const onClose = jest.fn();

0 commit comments

Comments
 (0)