Skip to content

Commit c1de21d

Browse files
committed
refactor(ui): add dataset to handle Escape keydown
1 parent 136d324 commit c1de21d

12 files changed

Lines changed: 85 additions & 35 deletions

File tree

packages/ui/src/components/_date-input/DateInput.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { checkNodeExist, getClassName, getVerticalSidePosition } from '@react-de
1212
import { dayjs } from '../../dayjs';
1313
import { useDValue, useMaxIndex } from '../../hooks';
1414
import { cloneHTMLElement, TTANSITION_DURING_POPUP, WINDOW_SPACE } from '../../utils';
15+
import { EXPANDED_DATA } from '../../utils/checkNoExpandedEl';
1516
import { DBaseDesign } from '../_base-design';
1617
import { DBaseInput } from '../_base-input';
1718
import { DComboboxKeyboard } from '../_keyboard';
@@ -377,6 +378,7 @@ function DateInput(props: DDateInputProps, ref: React.ForwardedRef<DDateInputRef
377378
renderBaseDesign(
378379
<div
379380
{...restProps}
381+
{...{ [EXPANDED_DATA]: visible }}
380382
ref={boxRef}
381383
className={getClassName(restProps.className, prefix, {
382384
[`${prefix}--${dSize}`]: dSize,

packages/ui/src/components/_popup/Popup.tsx

Lines changed: 1 addition & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ export interface DPopupProps {
1414
children: (props: { renderTrigger: DCloneHTMLElement; renderPopup: DCloneHTMLElement }) => JSX.Element | null;
1515
dVisible: boolean;
1616
dTrigger: 'hover' | 'click';
17-
dEscClosable?: boolean;
1817
dMouseEnterDelay?: number;
1918
dMouseLeaveDelay?: number;
2019
dUpdatePosition: {
@@ -27,16 +26,7 @@ export interface DPopupProps {
2726
}
2827

2928
export function DPopup(props: DPopupProps): JSX.Element | null {
30-
const {
31-
children,
32-
dVisible,
33-
dTrigger,
34-
dEscClosable = true,
35-
dMouseEnterDelay = 150,
36-
dMouseLeaveDelay = 200,
37-
dUpdatePosition,
38-
onVisibleChange,
39-
} = props;
29+
const { children, dVisible, dTrigger, dMouseEnterDelay = 150, dMouseLeaveDelay = 200, dUpdatePosition, onVisibleChange } = props;
4030

4131
//#region Context
4232
const { dPageScrollRef, dContentResizeRef } = useLayout();
@@ -122,18 +112,6 @@ export function DPopup(props: DPopupProps): JSX.Element | null {
122112
!dVisible || dTrigger !== 'click'
123113
);
124114

125-
useEvent<KeyboardEvent>(
126-
windowRef,
127-
'keydown',
128-
(e) => {
129-
if (e.code === 'Escape') {
130-
handleTrigger(false);
131-
}
132-
},
133-
{},
134-
!dVisible || !dEscClosable
135-
);
136-
137115
return children({
138116
renderTrigger: (el) => {
139117
const triggerProps: React.HTMLAttributes<HTMLElement> = {};

packages/ui/src/components/_selectbox/Selectbox.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { checkNodeExist, getClassName } from '@react-devui/utils';
1111

1212
import { useMaxIndex } from '../../hooks';
1313
import { cloneHTMLElement } from '../../utils';
14+
import { EXPANDED_DATA } from '../../utils/checkNoExpandedEl';
1415
import { DBaseDesign } from '../_base-design';
1516
import { DBaseInput } from '../_base-input';
1617
import { DFocusVisible } from '../_focus-visible';
@@ -143,6 +144,7 @@ export function DSelectbox(props: DSelectboxProps): JSX.Element | null {
143144
renderBaseDesign(
144145
<div
145146
{...restProps}
147+
{...{ [EXPANDED_DATA]: dVisible }}
146148
ref={combineBoxRef}
147149
className={getClassName(restProps.className, prefix, {
148150
[`${prefix}--${dSize}`]: dSize,

packages/ui/src/components/drawer/Drawer.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { useId, useLockScroll, useRefExtra } from '@react-devui/hooks';
1111
import { getClassName, toPx } from '@react-devui/utils';
1212

1313
import { useMaxIndex, useDValue } from '../../hooks';
14-
import { registerComponentMate, handleModalKeyDown, TTANSITION_DURING_BASE } from '../../utils';
14+
import { registerComponentMate, handleModalKeyDown, TTANSITION_DURING_BASE, checkNoExpandedEl } from '../../utils';
1515
import { DMask } from '../_mask';
1616
import { DTransition } from '../_transition';
1717
import { useComponentConfig, usePrefixConfig } from '../root';
@@ -262,7 +262,7 @@ export const DDrawer: {
262262
onKeyDown={(e) => {
263263
restProps.onKeyDown?.(e);
264264

265-
if (dEscClosable && e.code === 'Escape') {
265+
if (dEscClosable && checkNoExpandedEl(e.currentTarget) && e.code === 'Escape') {
266266
changeVisible(false);
267267
}
268268

packages/ui/src/components/dropdown/Dropdown.tsx

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@ import { isNull, isNumber, isUndefined, nth } from 'lodash';
44
import React, { useImperativeHandle, useRef, useState } from 'react';
55
import ReactDOM from 'react-dom';
66

7-
import { useEventCallback, useId, useRefExtra } from '@react-devui/hooks';
7+
import { useEvent, useEventCallback, useId, useRefExtra } from '@react-devui/hooks';
88
import { getClassName, getVerticalSidePosition, scrollToView } from '@react-devui/utils';
99

1010
import { useMaxIndex, useDValue } from '../../hooks';
1111
import { registerComponentMate, TTANSITION_DURING_POPUP, WINDOW_SPACE } from '../../utils';
12+
import { EXPANDED_DATA } from '../../utils/checkNoExpandedEl';
1213
import { DFocusVisible } from '../_focus-visible';
1314
import { DPopup, useNestedPopup } from '../_popup';
1415
import { DTransition } from '../_transition';
@@ -74,6 +75,7 @@ function Dropdown<ID extends DId, T extends DDropdownItem<ID>>(
7475
//#endregion
7576

7677
//#region Ref
78+
const windowRef = useRefExtra(() => window);
7779
const dropdownRef = useRef<HTMLDivElement>(null);
7880
const childRef = useRefExtra(() => document.getElementById(triggerId));
7981
const ulRef = useRef<HTMLUListElement>(null);
@@ -202,6 +204,18 @@ function Dropdown<ID extends DId, T extends DDropdownItem<ID>>(
202204
}
203205
};
204206

207+
useEvent<KeyboardEvent>(
208+
windowRef,
209+
'keydown',
210+
(e) => {
211+
if (e.code === 'Escape') {
212+
changeVisible(false);
213+
}
214+
},
215+
{},
216+
!visible
217+
);
218+
205219
let handleKeyDown: React.KeyboardEventHandler<HTMLElement> | undefined;
206220
const nodes = (() => {
207221
const getNodes = (arr: T[], level: number, subParents: T[]): JSX.Element[] =>
@@ -431,6 +445,7 @@ function Dropdown<ID extends DId, T extends DDropdownItem<ID>>(
431445
'aria-haspopup': 'menu',
432446
'aria-expanded': visible,
433447
'aria-controls': id,
448+
[EXPANDED_DATA as string]: visible,
434449
onFocus: (e) => {
435450
children.props.onFocus?.(e);
436451

packages/ui/src/components/menu/Menu.tsx

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@ import type { DId } from '../../utils/types';
33
import { isNull, isUndefined, nth } from 'lodash';
44
import React, { useImperativeHandle, useRef, useState } from 'react';
55

6-
import { useId } from '@react-devui/hooks';
6+
import { useEvent, useId, useRefExtra } from '@react-devui/hooks';
77
import { findNested, getClassName } from '@react-devui/utils';
88

99
import { useDValue } from '../../hooks';
1010
import { registerComponentMate, TTANSITION_DURING_BASE } from '../../utils';
11+
import { EXPANDED_DATA } from '../../utils/checkNoExpandedEl';
1112
import { DFocusVisible } from '../_focus-visible';
1213
import { useNestedPopup } from '../_popup';
1314
import { DCollapseTransition } from '../_transition';
@@ -64,6 +65,10 @@ function Menu<ID extends DId, T extends DMenuItem<ID>>(props: DMenuProps<ID, T>,
6465
const dPrefix = usePrefixConfig();
6566
//#endregion
6667

68+
//#region Ref
69+
const windowRef = useRefExtra(() => window);
70+
//#endregion
71+
6772
const dataRef = useRef<{
6873
updatePosition: Map<ID, () => void>;
6974
}>({
@@ -171,6 +176,18 @@ function Menu<ID extends DId, T extends DMenuItem<ID>>(props: DMenuProps<ID, T>,
171176
setFocusIds(ids.length === 0 ? (isUndefined(firstId) ? [] : [firstId]) : ids);
172177
};
173178

179+
useEvent<KeyboardEvent>(
180+
windowRef,
181+
'keydown',
182+
(e) => {
183+
if (e.code === 'Escape') {
184+
setPopupIds([]);
185+
}
186+
},
187+
{},
188+
popupIds.length === 0
189+
);
190+
174191
let handleKeyDown: React.KeyboardEventHandler<HTMLElement> | undefined;
175192
const nodes = (() => {
176193
const getNodes = (arr: T[], level: number, subParents: T[], inNav = false): JSX.Element[] => {
@@ -487,6 +504,7 @@ function Menu<ID extends DId, T extends DMenuItem<ID>>(props: DMenuProps<ID, T>,
487504
// eslint-disable-next-line jsx-a11y/aria-activedescendant-has-tabindex
488505
<nav
489506
{...restProps}
507+
{...{ [EXPANDED_DATA]: popupIds.length > 0 }}
490508
ref={collapseRef}
491509
className={getClassName(restProps.className, `${dPrefix}menu`, {
492510
[`${dPrefix}menu--horizontal`]: dMode === 'horizontal',

packages/ui/src/components/modal/Modal.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { CheckCircleOutlined, CloseCircleOutlined, ExclamationCircleOutlined, Wa
1010
import { checkNodeExist, getClassName } from '@react-devui/utils';
1111

1212
import { useMaxIndex, useDValue } from '../../hooks';
13-
import { registerComponentMate, handleModalKeyDown, TTANSITION_DURING_BASE } from '../../utils';
13+
import { registerComponentMate, handleModalKeyDown, TTANSITION_DURING_BASE, checkNoExpandedEl } from '../../utils';
1414
import { DMask } from '../_mask';
1515
import { DTransition } from '../_transition';
1616
import { ROOT_DATA, useComponentConfig, usePrefixConfig } from '../root';
@@ -204,7 +204,7 @@ export const DModal: {
204204
onKeyDown={(e) => {
205205
restProps.onKeyDown?.(e);
206206

207-
if (dEscClosable && e.code === 'Escape') {
207+
if (dEscClosable && checkNoExpandedEl(e.currentTarget) && e.code === 'Escape') {
208208
changeVisible(false);
209209
}
210210

packages/ui/src/components/popover/Popover.tsx

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,12 @@ import { isString, isUndefined } from 'lodash';
77
import React, { useEffect, useImperativeHandle, useRef, useState } from 'react';
88
import ReactDOM from 'react-dom';
99

10-
import { useEventCallback, useId, useLockScroll, useRefExtra } from '@react-devui/hooks';
10+
import { useEvent, useEventCallback, useId, useLockScroll, useRefExtra } from '@react-devui/hooks';
1111
import { getClassName, getPopupPosition } from '@react-devui/utils';
1212

1313
import { useMaxIndex, useDValue } from '../../hooks';
14-
import { registerComponentMate, handleModalKeyDown, cloneHTMLElement } from '../../utils';
14+
import { registerComponentMate, handleModalKeyDown, cloneHTMLElement, checkNoExpandedEl } from '../../utils';
15+
import { EXPANDED_DATA } from '../../utils/checkNoExpandedEl';
1516
import { DPopup } from '../_popup';
1617
import { DTransition } from '../_transition';
1718
import { useComponentConfig, usePrefixConfig } from '../root';
@@ -76,6 +77,7 @@ function Popover(props: DPopoverProps, ref: React.ForwardedRef<DPopoverRef>): JS
7677
//#endregion
7778

7879
//#region Ref
80+
const windowRef = useRefExtra(() => window);
7981
const triggerRef = useRefExtra(() => document.getElementById(triggerId));
8082
const popoverRef = useRef<HTMLDivElement>(null);
8183
const popupRef = useRef<HTMLDivElement>(null);
@@ -232,6 +234,17 @@ function Popover(props: DPopoverProps, ref: React.ForwardedRef<DPopoverRef>): JS
232234
});
233235

234236
useLockScroll(dModal && visible);
237+
useEvent<KeyboardEvent>(
238+
windowRef,
239+
'keydown',
240+
(e) => {
241+
if (e.code === 'Escape' && popoverRef.current && checkNoExpandedEl(popoverRef.current)) {
242+
changeVisible(false);
243+
}
244+
},
245+
{},
246+
!dEscClosable || !visible
247+
);
235248

236249
useEffect(() => {
237250
if (dModal) {
@@ -271,7 +284,6 @@ function Popover(props: DPopoverProps, ref: React.ForwardedRef<DPopoverRef>): JS
271284
<DPopup
272285
dVisible={visible}
273286
dTrigger={dTrigger}
274-
dEscClosable={dEscClosable}
275287
dMouseEnterDelay={dMouseEnterDelay}
276288
dMouseLeaveDelay={dMouseLeaveDelay}
277289
dUpdatePosition={{
@@ -287,6 +299,7 @@ function Popover(props: DPopoverProps, ref: React.ForwardedRef<DPopoverRef>): JS
287299
{renderTrigger(
288300
cloneHTMLElement(children, {
289301
id: triggerId,
302+
[EXPANDED_DATA]: visible,
290303
})
291304
)}
292305
{containerRef.current &&

packages/ui/src/components/tooltip/Tooltip.tsx

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@ import { isUndefined } from 'lodash';
55
import React, { useImperativeHandle, useRef, useState } from 'react';
66
import ReactDOM from 'react-dom';
77

8-
import { useEventCallback, useId, useRefExtra } from '@react-devui/hooks';
8+
import { useEvent, useEventCallback, useId, useRefExtra } from '@react-devui/hooks';
99
import { getClassName, getPopupPosition } from '@react-devui/utils';
1010

1111
import { useMaxIndex, useDValue } from '../../hooks';
12-
import { registerComponentMate } from '../../utils';
12+
import { checkNoExpandedEl, registerComponentMate } from '../../utils';
13+
import { EXPANDED_DATA } from '../../utils/checkNoExpandedEl';
1314
import { DPopup } from '../_popup';
1415
import { DTransition } from '../_transition';
1516
import { useComponentConfig, usePrefixConfig } from '../root';
@@ -64,6 +65,7 @@ function Tooltip(props: DTooltipProps, ref: React.ForwardedRef<DTooltipRef>): JS
6465
//#endregion
6566

6667
//#region Ref
68+
const windowRef = useRefExtra(() => window);
6769
const triggerRef = useRefExtra<HTMLElement>(() => document.querySelector(`[aria-describedby="${id}"]`));
6870
const popupRef = useRef<HTMLDivElement>(null);
6971
const containerRef = useRefExtra(
@@ -210,6 +212,18 @@ function Tooltip(props: DTooltipProps, ref: React.ForwardedRef<DTooltipRef>): JS
210212
}
211213
});
212214

215+
useEvent<KeyboardEvent>(
216+
windowRef,
217+
'keydown',
218+
(e) => {
219+
if (e.code === 'Escape' && popupRef.current && checkNoExpandedEl(popupRef.current)) {
220+
changeVisible(false);
221+
}
222+
},
223+
{},
224+
!dEscClosable || !visible
225+
);
226+
213227
useImperativeHandle(
214228
ref,
215229
() => ({
@@ -222,7 +236,6 @@ function Tooltip(props: DTooltipProps, ref: React.ForwardedRef<DTooltipRef>): JS
222236
<DPopup
223237
dVisible={visible}
224238
dTrigger={dTrigger}
225-
dEscClosable={dEscClosable}
226239
dMouseEnterDelay={dMouseEnterDelay}
227240
dMouseLeaveDelay={dMouseLeaveDelay}
228241
dUpdatePosition={{
@@ -238,6 +251,7 @@ function Tooltip(props: DTooltipProps, ref: React.ForwardedRef<DTooltipRef>): JS
238251
{renderTrigger(
239252
React.cloneElement(children, {
240253
'aria-describedby': id,
254+
[EXPANDED_DATA]: visible,
241255
})
242256
)}
243257
{containerRef.current &&

packages/ui/src/styles/components/tooltip.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
white-space: pre;
1313
background-color: var(--#{$rd-prefix}tooltip-background-color);
1414
border-radius: var(--#{$rd-prefix}border-radius-larger);
15+
outline: none;
1516

1617
@include m(top) {
1718
@include tooltip-arrow {

0 commit comments

Comments
 (0)