Skip to content

Commit 8eb6531

Browse files
committed
chore: support autoFocus
1 parent 87198f2 commit 8eb6531

File tree

3 files changed

+103
-15
lines changed

3 files changed

+103
-15
lines changed

docs/examples/assets/motion.less

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
@duration: 0.3s;
1+
@duration: 3s;
22

33
.mask-motion {
44
&-enter,

src/DrawerPanel.tsx

Lines changed: 89 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
import * as React from 'react';
22
import classNames from 'classnames';
33
import type { Placement } from './Drawer';
4+
import KeyCode from 'rc-util/lib/KeyCode';
5+
6+
export interface DrawerPanelRef {
7+
focus: VoidFunction;
8+
}
49

510
export interface DrawerPanelProps {
611
prefixCls: string;
@@ -11,18 +16,88 @@ export interface DrawerPanelProps {
1116
children?: React.ReactNode;
1217
}
1318

14-
export default function DrawerPanel(props: DrawerPanelProps) {
15-
const { prefixCls, className, style, placement, width, children } = props;
16-
17-
return (
18-
<div
19-
className={classNames(`${prefixCls}-panel`, className)}
20-
style={{
21-
width: placement === 'left' || placement === 'right' ? width : '100%',
22-
...style,
23-
}}
24-
>
25-
{children}
26-
</div>
27-
);
19+
const sentinelStyle: React.CSSProperties = {
20+
width: 0,
21+
height: 0,
22+
overflow: 'hidden',
23+
outline: 'none',
24+
position: 'absolute',
25+
};
26+
27+
const DrawerPanel = React.forwardRef<DrawerPanelRef, DrawerPanelProps>(
28+
(props, ref) => {
29+
const { prefixCls, className, style, placement, width, children } = props;
30+
31+
// ================================ Refs ================================
32+
const panelRef = React.useRef<HTMLDivElement>();
33+
const sentinelStartRef = React.useRef<HTMLDivElement>();
34+
const sentinelEndRef = React.useRef<HTMLDivElement>();
35+
36+
React.useImperativeHandle(ref, () => ({
37+
focus: () => {
38+
Promise.resolve().then(() => {
39+
sentinelStartRef.current?.focus({ preventScroll: true });
40+
});
41+
},
42+
}));
43+
44+
const onPanelKeyDown: React.KeyboardEventHandler<HTMLDivElement> = ({
45+
keyCode,
46+
shiftKey,
47+
}) => {
48+
switch (keyCode) {
49+
case KeyCode.TAB: {
50+
if (!shiftKey && document.activeElement === sentinelEndRef.current) {
51+
sentinelStartRef.current?.focus({ preventScroll: true });
52+
} else if (
53+
shiftKey &&
54+
document.activeElement === sentinelStartRef.current
55+
) {
56+
sentinelEndRef.current?.focus({ preventScroll: true });
57+
}
58+
}
59+
}
60+
};
61+
62+
// =============================== Render ===============================
63+
return (
64+
<>
65+
<div
66+
className={classNames(`${prefixCls}-panel`, className)}
67+
style={{
68+
width:
69+
placement === 'left' || placement === 'right' ? width : '100%',
70+
...style,
71+
}}
72+
aria-modal="true"
73+
role="dialog"
74+
tabIndex={-1}
75+
ref={panelRef}
76+
onKeyDown={onPanelKeyDown}
77+
>
78+
<div
79+
tabIndex={0}
80+
ref={sentinelStartRef}
81+
style={sentinelStyle}
82+
aria-hidden="true"
83+
/>
84+
85+
{children}
86+
87+
<div
88+
tabIndex={0}
89+
ref={sentinelEndRef}
90+
style={sentinelStyle}
91+
aria-hidden="true"
92+
/>
93+
</div>
94+
</>
95+
);
96+
},
97+
);
98+
99+
if (process.env.NODE_ENV === 'development') {
100+
DrawerPanel.displayName = 'DrawerPanel';
28101
}
102+
103+
export default DrawerPanel;

src/DrawerPopup.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import * as React from 'react';
22
import classNames from 'classnames';
33
import CSSMotion from 'rc-motion';
44
import type { CSSMotionProps } from 'rc-motion';
5+
import type { DrawerPanelRef } from './DrawerPanel';
56
import DrawerPanel from './DrawerPanel';
67
import type ScrollLocker from 'rc-util/lib/Dom/scrollLocker';
78
import DrawerContext from './context';
@@ -16,6 +17,7 @@ export interface DrawerPopupProps {
1617
inline?: boolean;
1718
push?: { distance?: number | string };
1819
forceRender?: boolean;
20+
autoFocus?: boolean;
1921

2022
// MISC
2123
scrollLocker?: ScrollLocker;
@@ -53,6 +55,7 @@ export default function DrawerPopup(props: DrawerPopupProps) {
5355
inline,
5456
push,
5557
forceRender,
58+
autoFocus = true,
5659

5760
// MISC
5861
scrollLocker,
@@ -101,6 +104,8 @@ export default function DrawerPopup(props: DrawerPopupProps) {
101104
);
102105

103106
// ========================= ScrollLock =========================
107+
const panelRef = React.useRef<DrawerPanelRef>();
108+
104109
React.useEffect(() => {
105110
if (open) {
106111
scrollLocker?.lock();
@@ -110,6 +115,13 @@ export default function DrawerPopup(props: DrawerPopupProps) {
110115
}
111116
}, [open]);
112117

118+
// Auto Focus
119+
React.useEffect(() => {
120+
if (open && autoFocus) {
121+
panelRef.current?.focus();
122+
}
123+
}, [open, autoFocus]);
124+
113125
React.useEffect(
114126
() => () => {
115127
scrollLocker?.unLock();
@@ -167,6 +179,7 @@ export default function DrawerPopup(props: DrawerPopupProps) {
167179
{({ className: motionClassName, style: motionStyle }) => {
168180
return (
169181
<DrawerPanel
182+
ref={panelRef}
170183
prefixCls={prefixCls}
171184
className={classNames(className, motionClassName)}
172185
style={{

0 commit comments

Comments
 (0)