@x-plat/design-system 0.14.14 → 0.14.16
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/dist/components/DatePicker/index.cjs +46 -41
- package/dist/components/DatePicker/index.js +46 -41
- package/dist/components/Drawer/index.cjs +14 -9
- package/dist/components/Drawer/index.js +14 -9
- package/dist/components/Dropdown/index.cjs +17 -14
- package/dist/components/Dropdown/index.js +17 -14
- package/dist/components/Modal/index.cjs +15 -10
- package/dist/components/Modal/index.js +15 -10
- package/dist/components/PopOver/index.cjs +17 -14
- package/dist/components/PopOver/index.js +17 -14
- package/dist/components/Select/index.cjs +17 -14
- package/dist/components/Select/index.js +17 -14
- package/dist/components/TimePicker/index.cjs +17 -14
- package/dist/components/TimePicker/index.js +17 -14
- package/dist/components/index.cjs +199 -194
- package/dist/components/index.js +199 -194
- package/dist/index.cjs +199 -194
- package/dist/index.js +199 -194
- package/guidelines/MIGRATION.md +40 -29
- package/package.json +1 -1
|
@@ -230,22 +230,25 @@ var useEscapeKey = (enabled, onEscape) => {
|
|
|
230
230
|
return () => document.removeEventListener("keydown", onKey);
|
|
231
231
|
}, [enabled, onEscape]);
|
|
232
232
|
};
|
|
233
|
-
var
|
|
233
|
+
var useCloseWhenFocusHidden = (enabled, triggerRef, popRef, onClose) => {
|
|
234
234
|
const onCloseRef = import_react3.default.useRef(onClose);
|
|
235
235
|
onCloseRef.current = onClose;
|
|
236
236
|
import_react3.default.useEffect(() => {
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
return;
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
237
|
+
const trigger = triggerRef.current;
|
|
238
|
+
if (!enabled || !trigger) return;
|
|
239
|
+
const observer = new IntersectionObserver(
|
|
240
|
+
([entry]) => {
|
|
241
|
+
if (entry.isIntersecting) return;
|
|
242
|
+
if (!popRef.current?.contains(document.activeElement)) return;
|
|
243
|
+
onCloseRef.current();
|
|
244
|
+
trigger.focus({ preventScroll: true });
|
|
245
|
+
},
|
|
246
|
+
// 그릇이 없으면 root: null → 뷰포트 기준
|
|
247
|
+
{ root: findScrollParent(trigger), threshold: 0 }
|
|
248
|
+
);
|
|
249
|
+
observer.observe(trigger);
|
|
250
|
+
return () => observer.disconnect();
|
|
251
|
+
}, [enabled, triggerRef, popRef]);
|
|
249
252
|
};
|
|
250
253
|
|
|
251
254
|
// ../../node_modules/clsx/dist/clsx.mjs
|
|
@@ -275,7 +278,7 @@ var Dropdown = (props) => {
|
|
|
275
278
|
const menuRef = import_react4.default.useRef(null);
|
|
276
279
|
useAutoPosition_default(triggerRef, menuRef, mounted);
|
|
277
280
|
useClickOutside_default([triggerRef, menuRef], () => setIsOpen(false), isOpen);
|
|
278
|
-
|
|
281
|
+
useCloseWhenFocusHidden(isOpen, triggerRef, menuRef, () => setIsOpen(false));
|
|
279
282
|
useEscapeKey(isOpen, () => {
|
|
280
283
|
setIsOpen(false);
|
|
281
284
|
triggerRef.current?.focus({ preventScroll: true });
|
|
@@ -194,22 +194,25 @@ var useEscapeKey = (enabled, onEscape) => {
|
|
|
194
194
|
return () => document.removeEventListener("keydown", onKey);
|
|
195
195
|
}, [enabled, onEscape]);
|
|
196
196
|
};
|
|
197
|
-
var
|
|
197
|
+
var useCloseWhenFocusHidden = (enabled, triggerRef, popRef, onClose) => {
|
|
198
198
|
const onCloseRef = React3.useRef(onClose);
|
|
199
199
|
onCloseRef.current = onClose;
|
|
200
200
|
React3.useEffect(() => {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
return;
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
201
|
+
const trigger = triggerRef.current;
|
|
202
|
+
if (!enabled || !trigger) return;
|
|
203
|
+
const observer = new IntersectionObserver(
|
|
204
|
+
([entry]) => {
|
|
205
|
+
if (entry.isIntersecting) return;
|
|
206
|
+
if (!popRef.current?.contains(document.activeElement)) return;
|
|
207
|
+
onCloseRef.current();
|
|
208
|
+
trigger.focus({ preventScroll: true });
|
|
209
|
+
},
|
|
210
|
+
// 그릇이 없으면 root: null → 뷰포트 기준
|
|
211
|
+
{ root: findScrollParent(trigger), threshold: 0 }
|
|
212
|
+
);
|
|
213
|
+
observer.observe(trigger);
|
|
214
|
+
return () => observer.disconnect();
|
|
215
|
+
}, [enabled, triggerRef, popRef]);
|
|
213
216
|
};
|
|
214
217
|
|
|
215
218
|
// ../../node_modules/clsx/dist/clsx.mjs
|
|
@@ -239,7 +242,7 @@ var Dropdown = (props) => {
|
|
|
239
242
|
const menuRef = React4.useRef(null);
|
|
240
243
|
useAutoPosition_default(triggerRef, menuRef, mounted);
|
|
241
244
|
useClickOutside_default([triggerRef, menuRef], () => setIsOpen(false), isOpen);
|
|
242
|
-
|
|
245
|
+
useCloseWhenFocusHidden(isOpen, triggerRef, menuRef, () => setIsOpen(false));
|
|
243
246
|
useEscapeKey(isOpen, () => {
|
|
244
247
|
setIsOpen(false);
|
|
245
248
|
triggerRef.current?.focus({ preventScroll: true });
|
|
@@ -35,11 +35,16 @@ __export(Modal_exports, {
|
|
|
35
35
|
module.exports = __toCommonJS(Modal_exports);
|
|
36
36
|
|
|
37
37
|
// src/components/Modal/Modal.tsx
|
|
38
|
-
var
|
|
38
|
+
var import_react3 = __toESM(require("react"), 1);
|
|
39
39
|
var import_react_dom = require("react-dom");
|
|
40
40
|
|
|
41
41
|
// src/tokens/hooks/useOverlayKeyboard.ts
|
|
42
|
+
var import_react2 = __toESM(require("react"), 1);
|
|
43
|
+
|
|
44
|
+
// src/tokens/hooks/useAutoPosition.ts
|
|
42
45
|
var import_react = __toESM(require("react"), 1);
|
|
46
|
+
|
|
47
|
+
// src/tokens/hooks/useOverlayKeyboard.ts
|
|
43
48
|
var FOCUSABLE = [
|
|
44
49
|
"a[href]",
|
|
45
50
|
"button:not([disabled])",
|
|
@@ -53,7 +58,7 @@ var focusableIn = (el) => Array.from(el.querySelectorAll(FOCUSABLE)).filter(
|
|
|
53
58
|
(n) => n.offsetWidth > 0 || n.offsetHeight > 0 || n === document.activeElement
|
|
54
59
|
);
|
|
55
60
|
var useEscapeKey = (enabled, onEscape) => {
|
|
56
|
-
|
|
61
|
+
import_react2.default.useEffect(() => {
|
|
57
62
|
if (!enabled) return;
|
|
58
63
|
const onKey = (e) => {
|
|
59
64
|
if (e.key !== "Escape") return;
|
|
@@ -67,8 +72,8 @@ var useEscapeKey = (enabled, onEscape) => {
|
|
|
67
72
|
};
|
|
68
73
|
var useFocusTrap = (containerRef, enabled, options = {}) => {
|
|
69
74
|
const { trap = false, initialFocus } = options;
|
|
70
|
-
const restoreRef =
|
|
71
|
-
|
|
75
|
+
const restoreRef = import_react2.default.useRef(null);
|
|
76
|
+
import_react2.default.useEffect(() => {
|
|
72
77
|
if (!enabled) return;
|
|
73
78
|
restoreRef.current = document.activeElement;
|
|
74
79
|
const raf = requestAnimationFrame(() => {
|
|
@@ -86,7 +91,7 @@ var useFocusTrap = (containerRef, enabled, options = {}) => {
|
|
|
86
91
|
if (prev && document.contains(prev)) prev.focus({ preventScroll: true });
|
|
87
92
|
};
|
|
88
93
|
}, [enabled, containerRef, initialFocus]);
|
|
89
|
-
|
|
94
|
+
import_react2.default.useEffect(() => {
|
|
90
95
|
if (!enabled || !trap) return;
|
|
91
96
|
const onKey = (e) => {
|
|
92
97
|
if (e.key !== "Tab") return;
|
|
@@ -134,13 +139,13 @@ var import_jsx_runtime = require("react/jsx-runtime");
|
|
|
134
139
|
var ANIMATION_DURATION_MS = 200;
|
|
135
140
|
var Modal = (props) => {
|
|
136
141
|
const { isOpen, onClose, children } = props;
|
|
137
|
-
const [mounted, setMounted] =
|
|
138
|
-
const [visible, setVisible] =
|
|
139
|
-
const [settled, setSettled] =
|
|
140
|
-
const boxRef =
|
|
142
|
+
const [mounted, setMounted] = import_react3.default.useState(false);
|
|
143
|
+
const [visible, setVisible] = import_react3.default.useState(false);
|
|
144
|
+
const [settled, setSettled] = import_react3.default.useState(false);
|
|
145
|
+
const boxRef = import_react3.default.useRef(null);
|
|
141
146
|
useEscapeKey(isOpen, onClose);
|
|
142
147
|
useFocusTrap(boxRef, isOpen && mounted, { trap: true });
|
|
143
|
-
|
|
148
|
+
import_react3.default.useEffect(() => {
|
|
144
149
|
if (isOpen) {
|
|
145
150
|
setMounted(true);
|
|
146
151
|
const show = setTimeout(() => setVisible(true), 1);
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
// src/components/Modal/Modal.tsx
|
|
2
|
-
import
|
|
2
|
+
import React3 from "react";
|
|
3
3
|
import { createPortal } from "react-dom";
|
|
4
4
|
|
|
5
5
|
// src/tokens/hooks/useOverlayKeyboard.ts
|
|
6
|
+
import React2 from "react";
|
|
7
|
+
|
|
8
|
+
// src/tokens/hooks/useAutoPosition.ts
|
|
6
9
|
import React from "react";
|
|
10
|
+
|
|
11
|
+
// src/tokens/hooks/useOverlayKeyboard.ts
|
|
7
12
|
var FOCUSABLE = [
|
|
8
13
|
"a[href]",
|
|
9
14
|
"button:not([disabled])",
|
|
@@ -17,7 +22,7 @@ var focusableIn = (el) => Array.from(el.querySelectorAll(FOCUSABLE)).filter(
|
|
|
17
22
|
(n) => n.offsetWidth > 0 || n.offsetHeight > 0 || n === document.activeElement
|
|
18
23
|
);
|
|
19
24
|
var useEscapeKey = (enabled, onEscape) => {
|
|
20
|
-
|
|
25
|
+
React2.useEffect(() => {
|
|
21
26
|
if (!enabled) return;
|
|
22
27
|
const onKey = (e) => {
|
|
23
28
|
if (e.key !== "Escape") return;
|
|
@@ -31,8 +36,8 @@ var useEscapeKey = (enabled, onEscape) => {
|
|
|
31
36
|
};
|
|
32
37
|
var useFocusTrap = (containerRef, enabled, options = {}) => {
|
|
33
38
|
const { trap = false, initialFocus } = options;
|
|
34
|
-
const restoreRef =
|
|
35
|
-
|
|
39
|
+
const restoreRef = React2.useRef(null);
|
|
40
|
+
React2.useEffect(() => {
|
|
36
41
|
if (!enabled) return;
|
|
37
42
|
restoreRef.current = document.activeElement;
|
|
38
43
|
const raf = requestAnimationFrame(() => {
|
|
@@ -50,7 +55,7 @@ var useFocusTrap = (containerRef, enabled, options = {}) => {
|
|
|
50
55
|
if (prev && document.contains(prev)) prev.focus({ preventScroll: true });
|
|
51
56
|
};
|
|
52
57
|
}, [enabled, containerRef, initialFocus]);
|
|
53
|
-
|
|
58
|
+
React2.useEffect(() => {
|
|
54
59
|
if (!enabled || !trap) return;
|
|
55
60
|
const onKey = (e) => {
|
|
56
61
|
if (e.key !== "Tab") return;
|
|
@@ -98,13 +103,13 @@ import { jsx } from "react/jsx-runtime";
|
|
|
98
103
|
var ANIMATION_DURATION_MS = 200;
|
|
99
104
|
var Modal = (props) => {
|
|
100
105
|
const { isOpen, onClose, children } = props;
|
|
101
|
-
const [mounted, setMounted] =
|
|
102
|
-
const [visible, setVisible] =
|
|
103
|
-
const [settled, setSettled] =
|
|
104
|
-
const boxRef =
|
|
106
|
+
const [mounted, setMounted] = React3.useState(false);
|
|
107
|
+
const [visible, setVisible] = React3.useState(false);
|
|
108
|
+
const [settled, setSettled] = React3.useState(false);
|
|
109
|
+
const boxRef = React3.useRef(null);
|
|
105
110
|
useEscapeKey(isOpen, onClose);
|
|
106
111
|
useFocusTrap(boxRef, isOpen && mounted, { trap: true });
|
|
107
|
-
|
|
112
|
+
React3.useEffect(() => {
|
|
108
113
|
if (isOpen) {
|
|
109
114
|
setMounted(true);
|
|
110
115
|
const show = setTimeout(() => setVisible(true), 1);
|
|
@@ -230,22 +230,25 @@ var useEscapeKey = (enabled, onEscape) => {
|
|
|
230
230
|
return () => document.removeEventListener("keydown", onKey);
|
|
231
231
|
}, [enabled, onEscape]);
|
|
232
232
|
};
|
|
233
|
-
var
|
|
233
|
+
var useCloseWhenFocusHidden = (enabled, triggerRef, popRef, onClose) => {
|
|
234
234
|
const onCloseRef = import_react3.default.useRef(onClose);
|
|
235
235
|
onCloseRef.current = onClose;
|
|
236
236
|
import_react3.default.useEffect(() => {
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
return;
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
237
|
+
const trigger = triggerRef.current;
|
|
238
|
+
if (!enabled || !trigger) return;
|
|
239
|
+
const observer = new IntersectionObserver(
|
|
240
|
+
([entry]) => {
|
|
241
|
+
if (entry.isIntersecting) return;
|
|
242
|
+
if (!popRef.current?.contains(document.activeElement)) return;
|
|
243
|
+
onCloseRef.current();
|
|
244
|
+
trigger.focus({ preventScroll: true });
|
|
245
|
+
},
|
|
246
|
+
// 그릇이 없으면 root: null → 뷰포트 기준
|
|
247
|
+
{ root: findScrollParent(trigger), threshold: 0 }
|
|
248
|
+
);
|
|
249
|
+
observer.observe(trigger);
|
|
250
|
+
return () => observer.disconnect();
|
|
251
|
+
}, [enabled, triggerRef, popRef]);
|
|
249
252
|
};
|
|
250
253
|
|
|
251
254
|
// ../../node_modules/clsx/dist/clsx.mjs
|
|
@@ -273,7 +276,7 @@ var PopOver = (props) => {
|
|
|
273
276
|
const [localOpen, setLocalOpen] = import_react4.default.useState(false);
|
|
274
277
|
const [eventTrigger, setEventTrigger] = import_react4.default.useState(false);
|
|
275
278
|
useClickOutside_default([popRef, triggerRef], onClose, isOpen);
|
|
276
|
-
|
|
279
|
+
useCloseWhenFocusHidden(isOpen, triggerRef, popRef, onClose);
|
|
277
280
|
useEscapeKey(isOpen, () => {
|
|
278
281
|
onClose();
|
|
279
282
|
triggerRef.current?.focus({ preventScroll: true });
|
|
@@ -194,22 +194,25 @@ var useEscapeKey = (enabled, onEscape) => {
|
|
|
194
194
|
return () => document.removeEventListener("keydown", onKey);
|
|
195
195
|
}, [enabled, onEscape]);
|
|
196
196
|
};
|
|
197
|
-
var
|
|
197
|
+
var useCloseWhenFocusHidden = (enabled, triggerRef, popRef, onClose) => {
|
|
198
198
|
const onCloseRef = React3.useRef(onClose);
|
|
199
199
|
onCloseRef.current = onClose;
|
|
200
200
|
React3.useEffect(() => {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
return;
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
201
|
+
const trigger = triggerRef.current;
|
|
202
|
+
if (!enabled || !trigger) return;
|
|
203
|
+
const observer = new IntersectionObserver(
|
|
204
|
+
([entry]) => {
|
|
205
|
+
if (entry.isIntersecting) return;
|
|
206
|
+
if (!popRef.current?.contains(document.activeElement)) return;
|
|
207
|
+
onCloseRef.current();
|
|
208
|
+
trigger.focus({ preventScroll: true });
|
|
209
|
+
},
|
|
210
|
+
// 그릇이 없으면 root: null → 뷰포트 기준
|
|
211
|
+
{ root: findScrollParent(trigger), threshold: 0 }
|
|
212
|
+
);
|
|
213
|
+
observer.observe(trigger);
|
|
214
|
+
return () => observer.disconnect();
|
|
215
|
+
}, [enabled, triggerRef, popRef]);
|
|
213
216
|
};
|
|
214
217
|
|
|
215
218
|
// ../../node_modules/clsx/dist/clsx.mjs
|
|
@@ -237,7 +240,7 @@ var PopOver = (props) => {
|
|
|
237
240
|
const [localOpen, setLocalOpen] = React4.useState(false);
|
|
238
241
|
const [eventTrigger, setEventTrigger] = React4.useState(false);
|
|
239
242
|
useClickOutside_default([popRef, triggerRef], onClose, isOpen);
|
|
240
|
-
|
|
243
|
+
useCloseWhenFocusHidden(isOpen, triggerRef, popRef, onClose);
|
|
241
244
|
useEscapeKey(isOpen, () => {
|
|
242
245
|
onClose();
|
|
243
246
|
triggerRef.current?.focus({ preventScroll: true });
|
|
@@ -231,22 +231,25 @@ var useEscapeKey = (enabled, onEscape) => {
|
|
|
231
231
|
return () => document.removeEventListener("keydown", onKey);
|
|
232
232
|
}, [enabled, onEscape]);
|
|
233
233
|
};
|
|
234
|
-
var
|
|
234
|
+
var useCloseWhenFocusHidden = (enabled, triggerRef, popRef, onClose) => {
|
|
235
235
|
const onCloseRef = import_react3.default.useRef(onClose);
|
|
236
236
|
onCloseRef.current = onClose;
|
|
237
237
|
import_react3.default.useEffect(() => {
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
return;
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
238
|
+
const trigger = triggerRef.current;
|
|
239
|
+
if (!enabled || !trigger) return;
|
|
240
|
+
const observer = new IntersectionObserver(
|
|
241
|
+
([entry]) => {
|
|
242
|
+
if (entry.isIntersecting) return;
|
|
243
|
+
if (!popRef.current?.contains(document.activeElement)) return;
|
|
244
|
+
onCloseRef.current();
|
|
245
|
+
trigger.focus({ preventScroll: true });
|
|
246
|
+
},
|
|
247
|
+
// 그릇이 없으면 root: null → 뷰포트 기준
|
|
248
|
+
{ root: findScrollParent(trigger), threshold: 0 }
|
|
249
|
+
);
|
|
250
|
+
observer.observe(trigger);
|
|
251
|
+
return () => observer.disconnect();
|
|
252
|
+
}, [enabled, triggerRef, popRef]);
|
|
250
253
|
};
|
|
251
254
|
|
|
252
255
|
// src/tokens/svg/action/CopyIcon.tsx
|
|
@@ -1404,7 +1407,7 @@ var SelectRoot = (props) => {
|
|
|
1404
1407
|
}, []);
|
|
1405
1408
|
const toggle = import_react6.default.useCallback(() => setOpen((prev) => !prev), []);
|
|
1406
1409
|
useClickOutside_default([contentRef, triggerRef], close, isOpen);
|
|
1407
|
-
|
|
1410
|
+
useCloseWhenFocusHidden(isOpen, triggerRef, contentRef, close);
|
|
1408
1411
|
useEscapeKey(isOpen, () => {
|
|
1409
1412
|
close();
|
|
1410
1413
|
triggerRef.current?.focus({ preventScroll: true });
|
|
@@ -194,22 +194,25 @@ var useEscapeKey = (enabled, onEscape) => {
|
|
|
194
194
|
return () => document.removeEventListener("keydown", onKey);
|
|
195
195
|
}, [enabled, onEscape]);
|
|
196
196
|
};
|
|
197
|
-
var
|
|
197
|
+
var useCloseWhenFocusHidden = (enabled, triggerRef, popRef, onClose) => {
|
|
198
198
|
const onCloseRef = React3.useRef(onClose);
|
|
199
199
|
onCloseRef.current = onClose;
|
|
200
200
|
React3.useEffect(() => {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
return;
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
201
|
+
const trigger = triggerRef.current;
|
|
202
|
+
if (!enabled || !trigger) return;
|
|
203
|
+
const observer = new IntersectionObserver(
|
|
204
|
+
([entry]) => {
|
|
205
|
+
if (entry.isIntersecting) return;
|
|
206
|
+
if (!popRef.current?.contains(document.activeElement)) return;
|
|
207
|
+
onCloseRef.current();
|
|
208
|
+
trigger.focus({ preventScroll: true });
|
|
209
|
+
},
|
|
210
|
+
// 그릇이 없으면 root: null → 뷰포트 기준
|
|
211
|
+
{ root: findScrollParent(trigger), threshold: 0 }
|
|
212
|
+
);
|
|
213
|
+
observer.observe(trigger);
|
|
214
|
+
return () => observer.disconnect();
|
|
215
|
+
}, [enabled, triggerRef, popRef]);
|
|
213
216
|
};
|
|
214
217
|
|
|
215
218
|
// src/tokens/svg/action/CopyIcon.tsx
|
|
@@ -1367,7 +1370,7 @@ var SelectRoot = (props) => {
|
|
|
1367
1370
|
}, []);
|
|
1368
1371
|
const toggle = React6.useCallback(() => setOpen((prev) => !prev), []);
|
|
1369
1372
|
useClickOutside_default([contentRef, triggerRef], close, isOpen);
|
|
1370
|
-
|
|
1373
|
+
useCloseWhenFocusHidden(isOpen, triggerRef, contentRef, close);
|
|
1371
1374
|
useEscapeKey(isOpen, () => {
|
|
1372
1375
|
close();
|
|
1373
1376
|
triggerRef.current?.focus({ preventScroll: true });
|
|
@@ -230,22 +230,25 @@ var useEscapeKey = (enabled, onEscape) => {
|
|
|
230
230
|
return () => document.removeEventListener("keydown", onKey);
|
|
231
231
|
}, [enabled, onEscape]);
|
|
232
232
|
};
|
|
233
|
-
var
|
|
233
|
+
var useCloseWhenFocusHidden = (enabled, triggerRef, popRef, onClose) => {
|
|
234
234
|
const onCloseRef = import_react3.default.useRef(onClose);
|
|
235
235
|
onCloseRef.current = onClose;
|
|
236
236
|
import_react3.default.useEffect(() => {
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
return;
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
237
|
+
const trigger = triggerRef.current;
|
|
238
|
+
if (!enabled || !trigger) return;
|
|
239
|
+
const observer = new IntersectionObserver(
|
|
240
|
+
([entry]) => {
|
|
241
|
+
if (entry.isIntersecting) return;
|
|
242
|
+
if (!popRef.current?.contains(document.activeElement)) return;
|
|
243
|
+
onCloseRef.current();
|
|
244
|
+
trigger.focus({ preventScroll: true });
|
|
245
|
+
},
|
|
246
|
+
// 그릇이 없으면 root: null → 뷰포트 기준
|
|
247
|
+
{ root: findScrollParent(trigger), threshold: 0 }
|
|
248
|
+
);
|
|
249
|
+
observer.observe(trigger);
|
|
250
|
+
return () => observer.disconnect();
|
|
251
|
+
}, [enabled, triggerRef, popRef]);
|
|
249
252
|
};
|
|
250
253
|
|
|
251
254
|
// ../../node_modules/clsx/dist/clsx.mjs
|
|
@@ -456,7 +459,7 @@ var TimePicker = (props) => {
|
|
|
456
459
|
}, [step, min, max]);
|
|
457
460
|
const close = import_react4.default.useCallback(() => setOpen(false), []);
|
|
458
461
|
useClickOutside_default([triggerRef, contentRef], close, isOpen);
|
|
459
|
-
|
|
462
|
+
useCloseWhenFocusHidden(isOpen, triggerRef, contentRef, close);
|
|
460
463
|
useEscapeKey(isOpen, () => {
|
|
461
464
|
close();
|
|
462
465
|
triggerRef.current?.focus({ preventScroll: true });
|
|
@@ -194,22 +194,25 @@ var useEscapeKey = (enabled, onEscape) => {
|
|
|
194
194
|
return () => document.removeEventListener("keydown", onKey);
|
|
195
195
|
}, [enabled, onEscape]);
|
|
196
196
|
};
|
|
197
|
-
var
|
|
197
|
+
var useCloseWhenFocusHidden = (enabled, triggerRef, popRef, onClose) => {
|
|
198
198
|
const onCloseRef = React3.useRef(onClose);
|
|
199
199
|
onCloseRef.current = onClose;
|
|
200
200
|
React3.useEffect(() => {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
return;
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
201
|
+
const trigger = triggerRef.current;
|
|
202
|
+
if (!enabled || !trigger) return;
|
|
203
|
+
const observer = new IntersectionObserver(
|
|
204
|
+
([entry]) => {
|
|
205
|
+
if (entry.isIntersecting) return;
|
|
206
|
+
if (!popRef.current?.contains(document.activeElement)) return;
|
|
207
|
+
onCloseRef.current();
|
|
208
|
+
trigger.focus({ preventScroll: true });
|
|
209
|
+
},
|
|
210
|
+
// 그릇이 없으면 root: null → 뷰포트 기준
|
|
211
|
+
{ root: findScrollParent(trigger), threshold: 0 }
|
|
212
|
+
);
|
|
213
|
+
observer.observe(trigger);
|
|
214
|
+
return () => observer.disconnect();
|
|
215
|
+
}, [enabled, triggerRef, popRef]);
|
|
213
216
|
};
|
|
214
217
|
|
|
215
218
|
// ../../node_modules/clsx/dist/clsx.mjs
|
|
@@ -420,7 +423,7 @@ var TimePicker = (props) => {
|
|
|
420
423
|
}, [step, min, max]);
|
|
421
424
|
const close = React4.useCallback(() => setOpen(false), []);
|
|
422
425
|
useClickOutside_default([triggerRef, contentRef], close, isOpen);
|
|
423
|
-
|
|
426
|
+
useCloseWhenFocusHidden(isOpen, triggerRef, contentRef, close);
|
|
424
427
|
useEscapeKey(isOpen, () => {
|
|
425
428
|
close();
|
|
426
429
|
triggerRef.current?.focus({ preventScroll: true });
|