@x-plat/design-system 0.14.15 → 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/Dropdown/index.cjs +7 -4
- package/dist/components/Dropdown/index.js +7 -4
- package/dist/components/PopOver/index.cjs +7 -4
- package/dist/components/PopOver/index.js +7 -4
- package/dist/components/Select/index.cjs +7 -4
- package/dist/components/Select/index.js +7 -4
- package/dist/components/TimePicker/index.cjs +7 -4
- package/dist/components/TimePicker/index.js +7 -4
- package/dist/components/index.cjs +10 -7
- package/dist/components/index.js +10 -7
- package/dist/index.cjs +10 -7
- package/dist/index.js +10 -7
- package/guidelines/MIGRATION.md +29 -16
- package/package.json +1 -1
|
@@ -230,7 +230,7 @@ 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(() => {
|
|
@@ -238,14 +238,17 @@ var useCloseWhenTriggerHidden = (enabled, triggerRef, onClose) => {
|
|
|
238
238
|
if (!enabled || !trigger) return;
|
|
239
239
|
const observer = new IntersectionObserver(
|
|
240
240
|
([entry]) => {
|
|
241
|
-
if (
|
|
241
|
+
if (entry.isIntersecting) return;
|
|
242
|
+
if (!popRef.current?.contains(document.activeElement)) return;
|
|
243
|
+
onCloseRef.current();
|
|
244
|
+
trigger.focus({ preventScroll: true });
|
|
242
245
|
},
|
|
243
246
|
// 그릇이 없으면 root: null → 뷰포트 기준
|
|
244
247
|
{ root: findScrollParent(trigger), threshold: 0 }
|
|
245
248
|
);
|
|
246
249
|
observer.observe(trigger);
|
|
247
250
|
return () => observer.disconnect();
|
|
248
|
-
}, [enabled, triggerRef]);
|
|
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,7 +194,7 @@ 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(() => {
|
|
@@ -202,14 +202,17 @@ var useCloseWhenTriggerHidden = (enabled, triggerRef, onClose) => {
|
|
|
202
202
|
if (!enabled || !trigger) return;
|
|
203
203
|
const observer = new IntersectionObserver(
|
|
204
204
|
([entry]) => {
|
|
205
|
-
if (
|
|
205
|
+
if (entry.isIntersecting) return;
|
|
206
|
+
if (!popRef.current?.contains(document.activeElement)) return;
|
|
207
|
+
onCloseRef.current();
|
|
208
|
+
trigger.focus({ preventScroll: true });
|
|
206
209
|
},
|
|
207
210
|
// 그릇이 없으면 root: null → 뷰포트 기준
|
|
208
211
|
{ root: findScrollParent(trigger), threshold: 0 }
|
|
209
212
|
);
|
|
210
213
|
observer.observe(trigger);
|
|
211
214
|
return () => observer.disconnect();
|
|
212
|
-
}, [enabled, triggerRef]);
|
|
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 });
|
|
@@ -230,7 +230,7 @@ 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(() => {
|
|
@@ -238,14 +238,17 @@ var useCloseWhenTriggerHidden = (enabled, triggerRef, onClose) => {
|
|
|
238
238
|
if (!enabled || !trigger) return;
|
|
239
239
|
const observer = new IntersectionObserver(
|
|
240
240
|
([entry]) => {
|
|
241
|
-
if (
|
|
241
|
+
if (entry.isIntersecting) return;
|
|
242
|
+
if (!popRef.current?.contains(document.activeElement)) return;
|
|
243
|
+
onCloseRef.current();
|
|
244
|
+
trigger.focus({ preventScroll: true });
|
|
242
245
|
},
|
|
243
246
|
// 그릇이 없으면 root: null → 뷰포트 기준
|
|
244
247
|
{ root: findScrollParent(trigger), threshold: 0 }
|
|
245
248
|
);
|
|
246
249
|
observer.observe(trigger);
|
|
247
250
|
return () => observer.disconnect();
|
|
248
|
-
}, [enabled, triggerRef]);
|
|
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,7 +194,7 @@ 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(() => {
|
|
@@ -202,14 +202,17 @@ var useCloseWhenTriggerHidden = (enabled, triggerRef, onClose) => {
|
|
|
202
202
|
if (!enabled || !trigger) return;
|
|
203
203
|
const observer = new IntersectionObserver(
|
|
204
204
|
([entry]) => {
|
|
205
|
-
if (
|
|
205
|
+
if (entry.isIntersecting) return;
|
|
206
|
+
if (!popRef.current?.contains(document.activeElement)) return;
|
|
207
|
+
onCloseRef.current();
|
|
208
|
+
trigger.focus({ preventScroll: true });
|
|
206
209
|
},
|
|
207
210
|
// 그릇이 없으면 root: null → 뷰포트 기준
|
|
208
211
|
{ root: findScrollParent(trigger), threshold: 0 }
|
|
209
212
|
);
|
|
210
213
|
observer.observe(trigger);
|
|
211
214
|
return () => observer.disconnect();
|
|
212
|
-
}, [enabled, triggerRef]);
|
|
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,7 +231,7 @@ 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(() => {
|
|
@@ -239,14 +239,17 @@ var useCloseWhenTriggerHidden = (enabled, triggerRef, onClose) => {
|
|
|
239
239
|
if (!enabled || !trigger) return;
|
|
240
240
|
const observer = new IntersectionObserver(
|
|
241
241
|
([entry]) => {
|
|
242
|
-
if (
|
|
242
|
+
if (entry.isIntersecting) return;
|
|
243
|
+
if (!popRef.current?.contains(document.activeElement)) return;
|
|
244
|
+
onCloseRef.current();
|
|
245
|
+
trigger.focus({ preventScroll: true });
|
|
243
246
|
},
|
|
244
247
|
// 그릇이 없으면 root: null → 뷰포트 기준
|
|
245
248
|
{ root: findScrollParent(trigger), threshold: 0 }
|
|
246
249
|
);
|
|
247
250
|
observer.observe(trigger);
|
|
248
251
|
return () => observer.disconnect();
|
|
249
|
-
}, [enabled, triggerRef]);
|
|
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,7 +194,7 @@ 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(() => {
|
|
@@ -202,14 +202,17 @@ var useCloseWhenTriggerHidden = (enabled, triggerRef, onClose) => {
|
|
|
202
202
|
if (!enabled || !trigger) return;
|
|
203
203
|
const observer = new IntersectionObserver(
|
|
204
204
|
([entry]) => {
|
|
205
|
-
if (
|
|
205
|
+
if (entry.isIntersecting) return;
|
|
206
|
+
if (!popRef.current?.contains(document.activeElement)) return;
|
|
207
|
+
onCloseRef.current();
|
|
208
|
+
trigger.focus({ preventScroll: true });
|
|
206
209
|
},
|
|
207
210
|
// 그릇이 없으면 root: null → 뷰포트 기준
|
|
208
211
|
{ root: findScrollParent(trigger), threshold: 0 }
|
|
209
212
|
);
|
|
210
213
|
observer.observe(trigger);
|
|
211
214
|
return () => observer.disconnect();
|
|
212
|
-
}, [enabled, triggerRef]);
|
|
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,7 +230,7 @@ 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(() => {
|
|
@@ -238,14 +238,17 @@ var useCloseWhenTriggerHidden = (enabled, triggerRef, onClose) => {
|
|
|
238
238
|
if (!enabled || !trigger) return;
|
|
239
239
|
const observer = new IntersectionObserver(
|
|
240
240
|
([entry]) => {
|
|
241
|
-
if (
|
|
241
|
+
if (entry.isIntersecting) return;
|
|
242
|
+
if (!popRef.current?.contains(document.activeElement)) return;
|
|
243
|
+
onCloseRef.current();
|
|
244
|
+
trigger.focus({ preventScroll: true });
|
|
242
245
|
},
|
|
243
246
|
// 그릇이 없으면 root: null → 뷰포트 기준
|
|
244
247
|
{ root: findScrollParent(trigger), threshold: 0 }
|
|
245
248
|
);
|
|
246
249
|
observer.observe(trigger);
|
|
247
250
|
return () => observer.disconnect();
|
|
248
|
-
}, [enabled, triggerRef]);
|
|
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,7 +194,7 @@ 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(() => {
|
|
@@ -202,14 +202,17 @@ var useCloseWhenTriggerHidden = (enabled, triggerRef, onClose) => {
|
|
|
202
202
|
if (!enabled || !trigger) return;
|
|
203
203
|
const observer = new IntersectionObserver(
|
|
204
204
|
([entry]) => {
|
|
205
|
-
if (
|
|
205
|
+
if (entry.isIntersecting) return;
|
|
206
|
+
if (!popRef.current?.contains(document.activeElement)) return;
|
|
207
|
+
onCloseRef.current();
|
|
208
|
+
trigger.focus({ preventScroll: true });
|
|
206
209
|
},
|
|
207
210
|
// 그릇이 없으면 root: null → 뷰포트 기준
|
|
208
211
|
{ root: findScrollParent(trigger), threshold: 0 }
|
|
209
212
|
);
|
|
210
213
|
observer.observe(trigger);
|
|
211
214
|
return () => observer.disconnect();
|
|
212
|
-
}, [enabled, triggerRef]);
|
|
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 });
|
|
@@ -3441,7 +3441,7 @@ var useFocusTrap = (containerRef, enabled, options = {}) => {
|
|
|
3441
3441
|
return () => document.removeEventListener("keydown", onKey);
|
|
3442
3442
|
}, [enabled, trap, containerRef]);
|
|
3443
3443
|
};
|
|
3444
|
-
var
|
|
3444
|
+
var useCloseWhenFocusHidden = (enabled, triggerRef, popRef, onClose) => {
|
|
3445
3445
|
const onCloseRef = import_react11.default.useRef(onClose);
|
|
3446
3446
|
onCloseRef.current = onClose;
|
|
3447
3447
|
import_react11.default.useEffect(() => {
|
|
@@ -3449,14 +3449,17 @@ var useCloseWhenTriggerHidden = (enabled, triggerRef, onClose) => {
|
|
|
3449
3449
|
if (!enabled || !trigger) return;
|
|
3450
3450
|
const observer = new IntersectionObserver(
|
|
3451
3451
|
([entry]) => {
|
|
3452
|
-
if (
|
|
3452
|
+
if (entry.isIntersecting) return;
|
|
3453
|
+
if (!popRef.current?.contains(document.activeElement)) return;
|
|
3454
|
+
onCloseRef.current();
|
|
3455
|
+
trigger.focus({ preventScroll: true });
|
|
3453
3456
|
},
|
|
3454
3457
|
// 그릇이 없으면 root: null → 뷰포트 기준
|
|
3455
3458
|
{ root: findScrollParent(trigger), threshold: 0 }
|
|
3456
3459
|
);
|
|
3457
3460
|
observer.observe(trigger);
|
|
3458
3461
|
return () => observer.disconnect();
|
|
3459
|
-
}, [enabled, triggerRef]);
|
|
3462
|
+
}, [enabled, triggerRef, popRef]);
|
|
3460
3463
|
};
|
|
3461
3464
|
|
|
3462
3465
|
// src/components/Modal/Modal.tsx
|
|
@@ -4164,7 +4167,7 @@ var Dropdown = (props) => {
|
|
|
4164
4167
|
const menuRef = import_react21.default.useRef(null);
|
|
4165
4168
|
useAutoPosition_default(triggerRef, menuRef, mounted);
|
|
4166
4169
|
useClickOutside_default([triggerRef, menuRef], () => setIsOpen(false), isOpen);
|
|
4167
|
-
|
|
4170
|
+
useCloseWhenFocusHidden(isOpen, triggerRef, menuRef, () => setIsOpen(false));
|
|
4168
4171
|
useEscapeKey(isOpen, () => {
|
|
4169
4172
|
setIsOpen(false);
|
|
4170
4173
|
triggerRef.current?.focus({ preventScroll: true });
|
|
@@ -6095,7 +6098,7 @@ var PopOver = (props) => {
|
|
|
6095
6098
|
const [localOpen, setLocalOpen] = import_react27.default.useState(false);
|
|
6096
6099
|
const [eventTrigger, setEventTrigger] = import_react27.default.useState(false);
|
|
6097
6100
|
useClickOutside_default([popRef, triggerRef], onClose, isOpen);
|
|
6098
|
-
|
|
6101
|
+
useCloseWhenFocusHidden(isOpen, triggerRef, popRef, onClose);
|
|
6099
6102
|
useEscapeKey(isOpen, () => {
|
|
6100
6103
|
onClose();
|
|
6101
6104
|
triggerRef.current?.focus({ preventScroll: true });
|
|
@@ -6375,7 +6378,7 @@ var SelectRoot = (props) => {
|
|
|
6375
6378
|
}, []);
|
|
6376
6379
|
const toggle = import_react31.default.useCallback(() => setOpen((prev) => !prev), []);
|
|
6377
6380
|
useClickOutside_default([contentRef, triggerRef], close, isOpen);
|
|
6378
|
-
|
|
6381
|
+
useCloseWhenFocusHidden(isOpen, triggerRef, contentRef, close);
|
|
6379
6382
|
useEscapeKey(isOpen, () => {
|
|
6380
6383
|
close();
|
|
6381
6384
|
triggerRef.current?.focus({ preventScroll: true });
|
|
@@ -7316,7 +7319,7 @@ var TimePicker = (props) => {
|
|
|
7316
7319
|
}, [step, min, max]);
|
|
7317
7320
|
const close = import_react40.default.useCallback(() => setOpen(false), []);
|
|
7318
7321
|
useClickOutside_default([triggerRef, contentRef], close, isOpen);
|
|
7319
|
-
|
|
7322
|
+
useCloseWhenFocusHidden(isOpen, triggerRef, contentRef, close);
|
|
7320
7323
|
useEscapeKey(isOpen, () => {
|
|
7321
7324
|
close();
|
|
7322
7325
|
triggerRef.current?.focus({ preventScroll: true });
|
package/dist/components/index.js
CHANGED
|
@@ -3339,7 +3339,7 @@ var useFocusTrap = (containerRef, enabled, options = {}) => {
|
|
|
3339
3339
|
return () => document.removeEventListener("keydown", onKey);
|
|
3340
3340
|
}, [enabled, trap, containerRef]);
|
|
3341
3341
|
};
|
|
3342
|
-
var
|
|
3342
|
+
var useCloseWhenFocusHidden = (enabled, triggerRef, popRef, onClose) => {
|
|
3343
3343
|
const onCloseRef = React11.useRef(onClose);
|
|
3344
3344
|
onCloseRef.current = onClose;
|
|
3345
3345
|
React11.useEffect(() => {
|
|
@@ -3347,14 +3347,17 @@ var useCloseWhenTriggerHidden = (enabled, triggerRef, onClose) => {
|
|
|
3347
3347
|
if (!enabled || !trigger) return;
|
|
3348
3348
|
const observer = new IntersectionObserver(
|
|
3349
3349
|
([entry]) => {
|
|
3350
|
-
if (
|
|
3350
|
+
if (entry.isIntersecting) return;
|
|
3351
|
+
if (!popRef.current?.contains(document.activeElement)) return;
|
|
3352
|
+
onCloseRef.current();
|
|
3353
|
+
trigger.focus({ preventScroll: true });
|
|
3351
3354
|
},
|
|
3352
3355
|
// 그릇이 없으면 root: null → 뷰포트 기준
|
|
3353
3356
|
{ root: findScrollParent(trigger), threshold: 0 }
|
|
3354
3357
|
);
|
|
3355
3358
|
observer.observe(trigger);
|
|
3356
3359
|
return () => observer.disconnect();
|
|
3357
|
-
}, [enabled, triggerRef]);
|
|
3360
|
+
}, [enabled, triggerRef, popRef]);
|
|
3358
3361
|
};
|
|
3359
3362
|
|
|
3360
3363
|
// src/components/Modal/Modal.tsx
|
|
@@ -4062,7 +4065,7 @@ var Dropdown = (props) => {
|
|
|
4062
4065
|
const menuRef = React21.useRef(null);
|
|
4063
4066
|
useAutoPosition_default(triggerRef, menuRef, mounted);
|
|
4064
4067
|
useClickOutside_default([triggerRef, menuRef], () => setIsOpen(false), isOpen);
|
|
4065
|
-
|
|
4068
|
+
useCloseWhenFocusHidden(isOpen, triggerRef, menuRef, () => setIsOpen(false));
|
|
4066
4069
|
useEscapeKey(isOpen, () => {
|
|
4067
4070
|
setIsOpen(false);
|
|
4068
4071
|
triggerRef.current?.focus({ preventScroll: true });
|
|
@@ -5993,7 +5996,7 @@ var PopOver = (props) => {
|
|
|
5993
5996
|
const [localOpen, setLocalOpen] = React27.useState(false);
|
|
5994
5997
|
const [eventTrigger, setEventTrigger] = React27.useState(false);
|
|
5995
5998
|
useClickOutside_default([popRef, triggerRef], onClose, isOpen);
|
|
5996
|
-
|
|
5999
|
+
useCloseWhenFocusHidden(isOpen, triggerRef, popRef, onClose);
|
|
5997
6000
|
useEscapeKey(isOpen, () => {
|
|
5998
6001
|
onClose();
|
|
5999
6002
|
triggerRef.current?.focus({ preventScroll: true });
|
|
@@ -6273,7 +6276,7 @@ var SelectRoot = (props) => {
|
|
|
6273
6276
|
}, []);
|
|
6274
6277
|
const toggle = React31.useCallback(() => setOpen((prev) => !prev), []);
|
|
6275
6278
|
useClickOutside_default([contentRef, triggerRef], close, isOpen);
|
|
6276
|
-
|
|
6279
|
+
useCloseWhenFocusHidden(isOpen, triggerRef, contentRef, close);
|
|
6277
6280
|
useEscapeKey(isOpen, () => {
|
|
6278
6281
|
close();
|
|
6279
6282
|
triggerRef.current?.focus({ preventScroll: true });
|
|
@@ -7214,7 +7217,7 @@ var TimePicker = (props) => {
|
|
|
7214
7217
|
}, [step, min, max]);
|
|
7215
7218
|
const close = React40.useCallback(() => setOpen(false), []);
|
|
7216
7219
|
useClickOutside_default([triggerRef, contentRef], close, isOpen);
|
|
7217
|
-
|
|
7220
|
+
useCloseWhenFocusHidden(isOpen, triggerRef, contentRef, close);
|
|
7218
7221
|
useEscapeKey(isOpen, () => {
|
|
7219
7222
|
close();
|
|
7220
7223
|
triggerRef.current?.focus({ preventScroll: true });
|
package/dist/index.cjs
CHANGED
|
@@ -7853,7 +7853,7 @@ var useFocusTrap = (containerRef, enabled, options = {}) => {
|
|
|
7853
7853
|
return () => document.removeEventListener("keydown", onKey);
|
|
7854
7854
|
}, [enabled, trap, containerRef]);
|
|
7855
7855
|
};
|
|
7856
|
-
var
|
|
7856
|
+
var useCloseWhenFocusHidden = (enabled, triggerRef, popRef, onClose) => {
|
|
7857
7857
|
const onCloseRef = import_react11.default.useRef(onClose);
|
|
7858
7858
|
onCloseRef.current = onClose;
|
|
7859
7859
|
import_react11.default.useEffect(() => {
|
|
@@ -7861,14 +7861,17 @@ var useCloseWhenTriggerHidden = (enabled, triggerRef, onClose) => {
|
|
|
7861
7861
|
if (!enabled || !trigger) return;
|
|
7862
7862
|
const observer = new IntersectionObserver(
|
|
7863
7863
|
([entry]) => {
|
|
7864
|
-
if (
|
|
7864
|
+
if (entry.isIntersecting) return;
|
|
7865
|
+
if (!popRef.current?.contains(document.activeElement)) return;
|
|
7866
|
+
onCloseRef.current();
|
|
7867
|
+
trigger.focus({ preventScroll: true });
|
|
7865
7868
|
},
|
|
7866
7869
|
// 그릇이 없으면 root: null → 뷰포트 기준
|
|
7867
7870
|
{ root: findScrollParent(trigger), threshold: 0 }
|
|
7868
7871
|
);
|
|
7869
7872
|
observer.observe(trigger);
|
|
7870
7873
|
return () => observer.disconnect();
|
|
7871
|
-
}, [enabled, triggerRef]);
|
|
7874
|
+
}, [enabled, triggerRef, popRef]);
|
|
7872
7875
|
};
|
|
7873
7876
|
|
|
7874
7877
|
// src/components/Modal/Modal.tsx
|
|
@@ -8576,7 +8579,7 @@ var Dropdown = (props) => {
|
|
|
8576
8579
|
const menuRef = import_react21.default.useRef(null);
|
|
8577
8580
|
useAutoPosition_default(triggerRef, menuRef, mounted);
|
|
8578
8581
|
useClickOutside_default([triggerRef, menuRef], () => setIsOpen(false), isOpen);
|
|
8579
|
-
|
|
8582
|
+
useCloseWhenFocusHidden(isOpen, triggerRef, menuRef, () => setIsOpen(false));
|
|
8580
8583
|
useEscapeKey(isOpen, () => {
|
|
8581
8584
|
setIsOpen(false);
|
|
8582
8585
|
triggerRef.current?.focus({ preventScroll: true });
|
|
@@ -10507,7 +10510,7 @@ var PopOver = (props) => {
|
|
|
10507
10510
|
const [localOpen, setLocalOpen] = import_react27.default.useState(false);
|
|
10508
10511
|
const [eventTrigger, setEventTrigger] = import_react27.default.useState(false);
|
|
10509
10512
|
useClickOutside_default([popRef, triggerRef], onClose, isOpen);
|
|
10510
|
-
|
|
10513
|
+
useCloseWhenFocusHidden(isOpen, triggerRef, popRef, onClose);
|
|
10511
10514
|
useEscapeKey(isOpen, () => {
|
|
10512
10515
|
onClose();
|
|
10513
10516
|
triggerRef.current?.focus({ preventScroll: true });
|
|
@@ -10787,7 +10790,7 @@ var SelectRoot = (props) => {
|
|
|
10787
10790
|
}, []);
|
|
10788
10791
|
const toggle = import_react31.default.useCallback(() => setOpen((prev) => !prev), []);
|
|
10789
10792
|
useClickOutside_default([contentRef, triggerRef], close, isOpen);
|
|
10790
|
-
|
|
10793
|
+
useCloseWhenFocusHidden(isOpen, triggerRef, contentRef, close);
|
|
10791
10794
|
useEscapeKey(isOpen, () => {
|
|
10792
10795
|
close();
|
|
10793
10796
|
triggerRef.current?.focus({ preventScroll: true });
|
|
@@ -11728,7 +11731,7 @@ var TimePicker = (props) => {
|
|
|
11728
11731
|
}, [step, min, max]);
|
|
11729
11732
|
const close = import_react40.default.useCallback(() => setOpen(false), []);
|
|
11730
11733
|
useClickOutside_default([triggerRef, contentRef], close, isOpen);
|
|
11731
|
-
|
|
11734
|
+
useCloseWhenFocusHidden(isOpen, triggerRef, contentRef, close);
|
|
11732
11735
|
useEscapeKey(isOpen, () => {
|
|
11733
11736
|
close();
|
|
11734
11737
|
triggerRef.current?.focus({ preventScroll: true });
|
package/dist/index.js
CHANGED
|
@@ -7442,7 +7442,7 @@ var useFocusTrap = (containerRef, enabled, options = {}) => {
|
|
|
7442
7442
|
return () => document.removeEventListener("keydown", onKey);
|
|
7443
7443
|
}, [enabled, trap, containerRef]);
|
|
7444
7444
|
};
|
|
7445
|
-
var
|
|
7445
|
+
var useCloseWhenFocusHidden = (enabled, triggerRef, popRef, onClose) => {
|
|
7446
7446
|
const onCloseRef = React11.useRef(onClose);
|
|
7447
7447
|
onCloseRef.current = onClose;
|
|
7448
7448
|
React11.useEffect(() => {
|
|
@@ -7450,14 +7450,17 @@ var useCloseWhenTriggerHidden = (enabled, triggerRef, onClose) => {
|
|
|
7450
7450
|
if (!enabled || !trigger) return;
|
|
7451
7451
|
const observer = new IntersectionObserver(
|
|
7452
7452
|
([entry]) => {
|
|
7453
|
-
if (
|
|
7453
|
+
if (entry.isIntersecting) return;
|
|
7454
|
+
if (!popRef.current?.contains(document.activeElement)) return;
|
|
7455
|
+
onCloseRef.current();
|
|
7456
|
+
trigger.focus({ preventScroll: true });
|
|
7454
7457
|
},
|
|
7455
7458
|
// 그릇이 없으면 root: null → 뷰포트 기준
|
|
7456
7459
|
{ root: findScrollParent(trigger), threshold: 0 }
|
|
7457
7460
|
);
|
|
7458
7461
|
observer.observe(trigger);
|
|
7459
7462
|
return () => observer.disconnect();
|
|
7460
|
-
}, [enabled, triggerRef]);
|
|
7463
|
+
}, [enabled, triggerRef, popRef]);
|
|
7461
7464
|
};
|
|
7462
7465
|
|
|
7463
7466
|
// src/components/Modal/Modal.tsx
|
|
@@ -8165,7 +8168,7 @@ var Dropdown = (props) => {
|
|
|
8165
8168
|
const menuRef = React21.useRef(null);
|
|
8166
8169
|
useAutoPosition_default(triggerRef, menuRef, mounted);
|
|
8167
8170
|
useClickOutside_default([triggerRef, menuRef], () => setIsOpen(false), isOpen);
|
|
8168
|
-
|
|
8171
|
+
useCloseWhenFocusHidden(isOpen, triggerRef, menuRef, () => setIsOpen(false));
|
|
8169
8172
|
useEscapeKey(isOpen, () => {
|
|
8170
8173
|
setIsOpen(false);
|
|
8171
8174
|
triggerRef.current?.focus({ preventScroll: true });
|
|
@@ -10096,7 +10099,7 @@ var PopOver = (props) => {
|
|
|
10096
10099
|
const [localOpen, setLocalOpen] = React27.useState(false);
|
|
10097
10100
|
const [eventTrigger, setEventTrigger] = React27.useState(false);
|
|
10098
10101
|
useClickOutside_default([popRef, triggerRef], onClose, isOpen);
|
|
10099
|
-
|
|
10102
|
+
useCloseWhenFocusHidden(isOpen, triggerRef, popRef, onClose);
|
|
10100
10103
|
useEscapeKey(isOpen, () => {
|
|
10101
10104
|
onClose();
|
|
10102
10105
|
triggerRef.current?.focus({ preventScroll: true });
|
|
@@ -10376,7 +10379,7 @@ var SelectRoot = (props) => {
|
|
|
10376
10379
|
}, []);
|
|
10377
10380
|
const toggle = React31.useCallback(() => setOpen((prev) => !prev), []);
|
|
10378
10381
|
useClickOutside_default([contentRef, triggerRef], close, isOpen);
|
|
10379
|
-
|
|
10382
|
+
useCloseWhenFocusHidden(isOpen, triggerRef, contentRef, close);
|
|
10380
10383
|
useEscapeKey(isOpen, () => {
|
|
10381
10384
|
close();
|
|
10382
10385
|
triggerRef.current?.focus({ preventScroll: true });
|
|
@@ -11317,7 +11320,7 @@ var TimePicker = (props) => {
|
|
|
11317
11320
|
}, [step, min, max]);
|
|
11318
11321
|
const close = React40.useCallback(() => setOpen(false), []);
|
|
11319
11322
|
useClickOutside_default([triggerRef, contentRef], close, isOpen);
|
|
11320
|
-
|
|
11323
|
+
useCloseWhenFocusHidden(isOpen, triggerRef, contentRef, close);
|
|
11321
11324
|
useEscapeKey(isOpen, () => {
|
|
11322
11325
|
close();
|
|
11323
11326
|
triggerRef.current?.focus({ preventScroll: true });
|
package/guidelines/MIGRATION.md
CHANGED
|
@@ -284,30 +284,36 @@ Dropdown · PopOver · TimePicker 가 전부 애니메이션 없이 툭 튀어
|
|
|
284
284
|
**올리기만 하면 된다. 쓰는 쪽에서 고칠 것은 없다.** `.repositioning` 클래스를
|
|
285
285
|
직접 잡는 CSS 를 쓰고 있었다면 그 규칙만 지우면 된다.
|
|
286
286
|
|
|
287
|
-
## 0.14.7 ~ 0.14.
|
|
287
|
+
## 0.14.7 ~ 0.14.15 — 스크롤하면 오버레이가 닫혔다 (0.14.16 에서 되돌림)
|
|
288
288
|
|
|
289
289
|
**이 구간을 쓰고 있었다면** `Select` · `Dropdown` · `PopOver` · `TimePicker`
|
|
290
|
-
를 열어 둔 채
|
|
291
|
-
닫지 않는다** — 따라간다.
|
|
290
|
+
를 열어 둔 채 스크롤하면 닫혔다. **0.14.16 부터는 안 닫힌다** — 따라간다.
|
|
292
291
|
|
|
293
|
-
|
|
294
|
-
옮기느라 한 프레임 늦었다) 그릇 밖으로 나가도 안 잘려 헤더를 덮었기
|
|
295
|
-
때문이다. 어차피 어긋나 보이니 닫는 쪽이 나았다.
|
|
292
|
+
두 번에 걸쳐 걷어냈다.
|
|
296
293
|
|
|
297
|
-
|
|
294
|
+
0.14.7 스크롤이 나면 바로 닫는다
|
|
295
|
+
0.14.15 트리거가 그릇 밖으로 나가면 닫는다
|
|
296
|
+
0.14.16 안 닫는다 (포커스가 패널 안에 있을 때만 예외)
|
|
298
297
|
|
|
299
|
-
|
|
300
|
-
|
|
298
|
+
0.14.7 의 땜질은 팝업이 스크롤을 **제때 못 따라가고**(JS 로 옮기느라 한 프레임
|
|
299
|
+
늦었다) 그릇 밖으로 나가도 안 잘려 헤더를 덮었기 때문이다. 어차피 어긋나
|
|
300
|
+
보이니 닫는 쪽이 나았다. 그 전제는 0.14.3~0.14.6(지연 제거)과
|
|
301
|
+
0.14.9~0.14.14(그릇 기준 자리 + `clip-path`)로 무너졌다.
|
|
301
302
|
|
|
302
|
-
|
|
303
|
-
|
|
303
|
+
0.14.15 는 "안 보이는데 열린 상태" 를 걱정해 남겨 둔 것인데, 쓰는 사람에게는
|
|
304
|
+
**굴리다가 목록이 툭 사라지는 쪽**이 더 거슬렸다. 목록은 트리거에 딸린
|
|
305
|
+
것이라 **트리거가 돌아오면 목록도 돌아오는 것**이 맞는 그림이다. 잠깐
|
|
306
|
+
지나쳤다는 이유로 고르던 것이 없어질 이유가 없다.
|
|
304
307
|
|
|
305
|
-
###
|
|
308
|
+
### 예외 하나 — 포커스가 패널 안에 있을 때
|
|
306
309
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
310
|
+
그때는 **보이지 않는 곳에 커서가 있는** 상태가 된다. 패널이 통째로 잘려
|
|
311
|
+
있는데 거기 포커스가 살아 있고, 자리가 `absolute` 라 브라우저가 스크롤로
|
|
312
|
+
되돌려 주지도 않는다. 이때만 닫고 포커스를 트리거로 돌린다
|
|
313
|
+
(`preventScroll: true` — 사용자가 굴린 자리를 빼앗지 않는다).
|
|
314
|
+
|
|
315
|
+
키보드로 열어 항목을 오르내리다 굴리는 경우라 흔하지 않다. 마우스로 쓰면
|
|
316
|
+
겪을 일이 없다.
|
|
311
317
|
|
|
312
318
|
**쓰는 쪽에서 할 일은 없다.**
|
|
313
319
|
|
|
@@ -384,6 +390,7 @@ export { Button, Input, Select, SideBar /* 쓰는 것만 */ } from "@x-plat/desi
|
|
|
384
390
|
| 팝업 목록 안에서 휠 → 안 닫힘 (0.14.7) | **사람이 화면에서 확인함** |
|
|
385
391
|
| 긴 항목 줄바꿈 (0.14.8) | **사람이 화면에서 확인함** — 두세 줄로 떨어진다 |
|
|
386
392
|
| 자리·간격·여는 동안 (0.14.14) | **프레임 단위로 측정함** — 멈춘 상태 간격 8px, 여는 동안 4→8 로 벌어짐, 포커스 링 여유가 한 프레임도 음수가 아님 |
|
|
393
|
+
| 열린 채 스크롤 (0.14.15) | **프레임 단위로 측정함** — 간격이 8px 에서 한 번도 안 흔들림(지연 없음), 헤더 침범 0.0px(경계에 닿되 안 넘음), 트리거가 29px 걸쳤을 때는 열려 있고 0 이 된 뒤 닫힘 |
|
|
387
394
|
| 페이지(문서) 스크롤 시 안 닫힘 (0.14.7) | **확인 안 됨.** 규칙상 맞지만 그 조건의 화면에 고르개가 없어 볼 자리가 없었다 |
|
|
388
395
|
| `Layout` 타입 (0.14.2) | 소비자 흉내 프로젝트로 타입 검사 통과 |
|
|
389
396
|
| `ToggleChip` 커서·초점 테두리 | 마크업·CSS 만 확인. 눌러 본 사람 없음 |
|
|
@@ -398,6 +405,12 @@ export { Button, Input, Select, SideBar /* 쓰는 것만 */ } from "@x-plat/desi
|
|
|
398
405
|
> `div[tabindex]` 라 마우스 클릭에는 `:focus-visible` 이 안 걸린다. 마우스로만
|
|
399
406
|
> 열면 링이 아예 안 떠서 그 항목이 **늘 통과한다.**
|
|
400
407
|
>
|
|
408
|
+
> **읽는 시점이 화면보다 빠르면 그것도 거짓말이다.** 스크롤 뒤 32ms 만에
|
|
409
|
+
> 읽었더니 헤더 침범이 −1.6 → −2.8 → −3.5 로 나왔고 끝까지 안 닫혔다.
|
|
410
|
+
> `clip-path` 갱신과 `IntersectionObserver` 가 아직 안 돈 것이다. 120ms 로
|
|
411
|
+
> 늘리니 침범 0, 제때 닫힘이었다. 재는 **범위**만이 아니라 **시점**도 맞아야
|
|
412
|
+
> 한다.
|
|
413
|
+
>
|
|
401
414
|
> 확인할 때는 **배포가 앉은 뒤에** 볼 것. 한 번은 "안 닫힌다" 로 나왔다가
|
|
402
415
|
> 다시 보니 닫혔다 — 반쯤 배포된 화면이었다. 그리고 버전 번호가 아니라
|
|
403
416
|
> 받은 파일 안을 열어 확인하는 편이 낫다(0.14.1 은 번호만 올라가고 내용이
|