gridsmith-ui 0.29.1 → 0.30.1

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.
Files changed (108) hide show
  1. package/CHANGELOG.md +94 -0
  2. package/README.md +5 -1
  3. package/dist/DropdownMenu-DqT7ZQ0D.d.ts +39 -0
  4. package/dist/charts.js +24 -9
  5. package/dist/charts.js.map +1 -1
  6. package/dist/chat.js +7 -7
  7. package/dist/{chunk-MFAWJSMJ.js → chunk-6C2IUMY7.js} +4 -23
  8. package/dist/chunk-6C2IUMY7.js.map +1 -0
  9. package/dist/{chunk-OYNUU346.js → chunk-7QLXYRPD.js} +3 -12
  10. package/dist/chunk-7QLXYRPD.js.map +1 -0
  11. package/dist/{chunk-FD4YED7O.js → chunk-DBHVJ7Z3.js} +5 -49
  12. package/dist/chunk-DBHVJ7Z3.js.map +1 -0
  13. package/dist/chunk-IELIF4PJ.js +110 -0
  14. package/dist/chunk-IELIF4PJ.js.map +1 -0
  15. package/dist/chunk-ISONVOA7.js +316 -0
  16. package/dist/chunk-ISONVOA7.js.map +1 -0
  17. package/dist/{chunk-4TUJXDRU.js → chunk-KMKBOTFR.js} +93 -32
  18. package/dist/chunk-KMKBOTFR.js.map +1 -0
  19. package/dist/{chunk-MKFWHWBK.js → chunk-L6HWACK7.js} +7 -7
  20. package/dist/chunk-L6HWACK7.js.map +1 -0
  21. package/dist/chunk-LJYSVZMI.js +25 -0
  22. package/dist/chunk-LJYSVZMI.js.map +1 -0
  23. package/dist/{chunk-6A5RHW2W.js → chunk-NEUWZUK6.js} +4 -5
  24. package/dist/chunk-NEUWZUK6.js.map +1 -0
  25. package/dist/chunk-NKUCS67T.js +13 -0
  26. package/dist/chunk-NKUCS67T.js.map +1 -0
  27. package/dist/{chunk-RECCCA7Z.js → chunk-OVHBQT3Z.js} +6 -5
  28. package/dist/chunk-OVHBQT3Z.js.map +1 -0
  29. package/dist/{chunk-I3MTQ3CS.js → chunk-OX3NNMWG.js} +6 -6
  30. package/dist/{chunk-I3MTQ3CS.js.map → chunk-OX3NNMWG.js.map} +1 -1
  31. package/dist/{chunk-ZI3OQDET.js → chunk-R5QXMNW7.js} +297 -15
  32. package/dist/chunk-R5QXMNW7.js.map +1 -0
  33. package/dist/chunk-SJZWEBHL.js +58 -0
  34. package/dist/chunk-SJZWEBHL.js.map +1 -0
  35. package/dist/{chunk-FUKSLQ77.js → chunk-SLRTSBCJ.js} +91 -51
  36. package/dist/chunk-SLRTSBCJ.js.map +1 -0
  37. package/dist/chunk-T52LAFKG.js +1350 -0
  38. package/dist/chunk-T52LAFKG.js.map +1 -0
  39. package/dist/chunk-TYNDXMBV.js +105 -0
  40. package/dist/chunk-TYNDXMBV.js.map +1 -0
  41. package/dist/{chunk-37WOC7F7.js → chunk-UDJOIVKQ.js} +31 -113
  42. package/dist/chunk-UDJOIVKQ.js.map +1 -0
  43. package/dist/{chunk-RJEUQ7RL.js → chunk-ULCACQSA.js} +47 -90
  44. package/dist/chunk-ULCACQSA.js.map +1 -0
  45. package/dist/{chunk-HP7WUZPK.js → chunk-WIUGPZOH.js} +10 -3
  46. package/dist/chunk-WIUGPZOH.js.map +1 -0
  47. package/dist/{chunk-JTU7OBYJ.js → chunk-YL5KD5RC.js} +4 -3
  48. package/dist/chunk-YL5KD5RC.js.map +1 -0
  49. package/dist/data-grid.d.ts +25 -6
  50. package/dist/data-grid.js +180 -58
  51. package/dist/data-grid.js.map +1 -1
  52. package/dist/editor-mentions.js +4 -5
  53. package/dist/editor-mentions.js.map +1 -1
  54. package/dist/editor.js +11 -8
  55. package/dist/editor.js.map +1 -1
  56. package/dist/{feather-FNBQ74RR.js → feather-XFFEFLTV.js} +5 -3
  57. package/dist/feather-XFFEFLTV.js.map +1 -0
  58. package/dist/{heroicons-DODCSYWD.js → heroicons-L6DOHBEA.js} +5 -3
  59. package/dist/heroicons-L6DOHBEA.js.map +1 -0
  60. package/dist/icons.d.ts +1 -1
  61. package/dist/icons.js +1 -1
  62. package/dist/index.d.ts +138 -39
  63. package/dist/index.js +846 -1667
  64. package/dist/index.js.map +1 -1
  65. package/dist/kanban.d.ts +26 -8
  66. package/dist/kanban.js +204 -79
  67. package/dist/kanban.js.map +1 -1
  68. package/dist/{lucide-ZWE3EPGP.js → lucide-JOORDTO6.js} +5 -3
  69. package/dist/lucide-JOORDTO6.js.map +1 -0
  70. package/dist/{phosphor-DIHPA4AJ.js → phosphor-TKGDYNGR.js} +5 -3
  71. package/dist/phosphor-TKGDYNGR.js.map +1 -0
  72. package/dist/resizable.js +47 -16
  73. package/dist/resizable.js.map +1 -1
  74. package/dist/roots.css +12 -8
  75. package/dist/{table-utils-CxnpFHRF.d.ts → table-utils-BXkP-2Hx.d.ts} +3 -1
  76. package/dist/{tabler-D3VYTSGI.js → tabler-YGETLYRU.js} +5 -3
  77. package/dist/tabler-YGETLYRU.js.map +1 -0
  78. package/dist/tokens.css +14 -0
  79. package/dist/utils.js.map +1 -1
  80. package/dist/virtualized.d.ts +6 -1
  81. package/dist/virtualized.js +17 -13
  82. package/dist/virtualized.js.map +1 -1
  83. package/dist-static/tokens.css +14 -0
  84. package/package.json +1 -1
  85. package/dist/chunk-37WOC7F7.js.map +0 -1
  86. package/dist/chunk-4TUJXDRU.js.map +0 -1
  87. package/dist/chunk-6A5RHW2W.js.map +0 -1
  88. package/dist/chunk-FD4YED7O.js.map +0 -1
  89. package/dist/chunk-FUKSLQ77.js.map +0 -1
  90. package/dist/chunk-HP7WUZPK.js.map +0 -1
  91. package/dist/chunk-JTU7OBYJ.js.map +0 -1
  92. package/dist/chunk-MFAWJSMJ.js.map +0 -1
  93. package/dist/chunk-MKFWHWBK.js.map +0 -1
  94. package/dist/chunk-OYNUU346.js.map +0 -1
  95. package/dist/chunk-QM4SJKWV.js +0 -51
  96. package/dist/chunk-QM4SJKWV.js.map +0 -1
  97. package/dist/chunk-QNHVPE2B.js +0 -117
  98. package/dist/chunk-QNHVPE2B.js.map +0 -1
  99. package/dist/chunk-RECCCA7Z.js.map +0 -1
  100. package/dist/chunk-RJEUQ7RL.js.map +0 -1
  101. package/dist/chunk-WH2FUFAC.js +0 -15
  102. package/dist/chunk-WH2FUFAC.js.map +0 -1
  103. package/dist/chunk-ZI3OQDET.js.map +0 -1
  104. package/dist/feather-FNBQ74RR.js.map +0 -1
  105. package/dist/heroicons-DODCSYWD.js.map +0 -1
  106. package/dist/lucide-ZWE3EPGP.js.map +0 -1
  107. package/dist/phosphor-DIHPA4AJ.js.map +0 -1
  108. package/dist/tabler-D3VYTSGI.js.map +0 -1
@@ -1,10 +1,11 @@
1
1
  "use client";
2
- import { useLatest } from './chunk-WH2FUFAC.js';
2
+ import { useLatest } from './chunk-WIUGPZOH.js';
3
3
  import { useRef, useInsertionEffect, useEffect, useCallback, useState, useMemo, useLayoutEffect } from 'react';
4
4
  import { flushSync } from 'react-dom';
5
5
 
6
6
  var FOCUSABLE = 'a[href], button:not(:disabled), input:not(:disabled), select:not(:disabled), textarea:not(:disabled), [tabindex]:not([tabindex="-1"]), [contenteditable]:not([contenteditable="false"])';
7
7
  var FIELD = "input, select, textarea, [contenteditable='true']";
8
+ var CONTROL_ANCESTOR = 'a[href], button, summary, label, [role="button"], [role="link"], [role="option"], [role="menuitem"], [role="menuitemcheckbox"], [role="menuitemradio"], [role="tab"], [role="treeitem"], [role="row"], [role="gridcell"], [role="checkbox"], [role="radio"], [role="switch"]';
8
9
  var SAFE_BUTTON = 'button:not([data-variant="destructive"])';
9
10
  var OVERLAY_CLOSE_ATTR = "data-overlay-close";
10
11
  function isRendered(el) {
@@ -54,10 +55,34 @@ function focusInitial(root, opts = {}) {
54
55
  function useFocusBeforeOpen(open) {
55
56
  const before = useRef(null);
56
57
  useInsertionEffect(() => {
57
- if (open) before.current = document.activeElement;
58
+ trackFocus();
59
+ if (!open) return;
60
+ const active = document.activeElement;
61
+ before.current = !active || active === document.body ? justLost() ?? active : active;
58
62
  }, [open]);
59
63
  return before;
60
64
  }
65
+ var lastFocused = null;
66
+ var lastFocusedAt = 0;
67
+ var lastActedAt = 0;
68
+ var tracking = false;
69
+ function trackFocus() {
70
+ if (tracking || typeof document === "undefined") return;
71
+ tracking = true;
72
+ document.addEventListener("focusin", (e) => {
73
+ lastFocused = e.target instanceof Element ? e.target : null;
74
+ lastFocusedAt = performance.now();
75
+ }, true);
76
+ const acted = () => {
77
+ lastActedAt = performance.now();
78
+ };
79
+ for (const type of ["keydown", "pointerdown", "click"]) document.addEventListener(type, acted, true);
80
+ }
81
+ function justLost() {
82
+ const recent = lastActedAt >= lastFocusedAt && performance.now() - lastActedAt < 1e3;
83
+ return lastFocused && !lastFocused.isConnected && recent ? lastFocused : null;
84
+ }
85
+ trackFocus();
61
86
  function pathAbove(to, from) {
62
87
  const up = to.replace(/\/+$/, "");
63
88
  return from.startsWith(`${up}/`) && from.replace(/\/+$/, "") !== up;
@@ -73,6 +98,59 @@ function deletedRow(before, now) {
73
98
  for (let j = k; j < (refilled ? last : now.length); j++) if (now[j] !== before[j + 1]) return -1;
74
99
  return k;
75
100
  }
101
+ function focusForTheMoment(el) {
102
+ const own = el.getAttribute("tabindex");
103
+ if (own === null) el.setAttribute("tabindex", "-1");
104
+ el.focus();
105
+ if (document.activeElement !== el) {
106
+ if (own === null) el.removeAttribute("tabindex");
107
+ return false;
108
+ }
109
+ let ring = false;
110
+ try {
111
+ ring = el.matches(":focus-visible");
112
+ } catch {
113
+ }
114
+ const { outline, outlineOffset } = el.style;
115
+ if (ring) {
116
+ el.style.outline = "2px solid var(--ds-focus)";
117
+ el.style.outlineOffset = "2px";
118
+ }
119
+ el.addEventListener("blur", () => {
120
+ if (own === null) el.removeAttribute("tabindex");
121
+ if (ring) Object.assign(el.style, { outline, outlineOffset });
122
+ }, { once: true });
123
+ return true;
124
+ }
125
+ var HEADING = 'h1, h2, h3, h4, h5, h6, [role="heading"]';
126
+ var LAST_RESORT_MS = 200;
127
+ function placesOf(el) {
128
+ const places = [];
129
+ for (let c = el, box = el.parentElement; box && box !== document.body && box.parentElement && box.parentElement !== document.body; c = box, box = box.parentElement) {
130
+ places.push({ box, prev: c.previousElementSibling, next: c.nextElementSibling });
131
+ }
132
+ return places;
133
+ }
134
+ function landLastResort(places, closing) {
135
+ const at = places.findIndex((p) => p.box.isConnected);
136
+ if (at < 0) return false;
137
+ const { box, prev, next } = places[at];
138
+ const kids = Array.from(box.children);
139
+ const from = prev?.parentElement === box ? kids.indexOf(prev) + 1 : 0;
140
+ const to = next?.parentElement === box ? kids.indexOf(next) : kids.length;
141
+ const areas = [kids.slice(from, Math.max(from, to)), ...places.slice(at).map((p) => [p.box])];
142
+ const counts = (el) => !closing?.contains(el);
143
+ for (const area of areas) {
144
+ const stop = area.flatMap((a) => [...isTabStop(a) ? [a] : [], ...tabStops(a)]).find(counts);
145
+ if (stop) {
146
+ stop.focus();
147
+ if (document.activeElement === stop) return true;
148
+ }
149
+ const heading = area.flatMap((a) => [...a.matches(HEADING) ? [a] : [], ...a.querySelectorAll(HEADING)]).find((h) => h instanceof HTMLElement && isRendered(h) && counts(h));
150
+ if (heading && focusForTheMoment(heading)) return true;
151
+ }
152
+ return false;
153
+ }
76
154
  function rememberFocus(el, { ignoreRoute = false } = {}) {
77
155
  const { href, pathname } = location;
78
156
  const row = el.closest(ROW);
@@ -85,6 +163,7 @@ function rememberFocus(el, { ignoreRoute = false } = {}) {
85
163
  const texts = rows.map((r) => r.textContent ?? "");
86
164
  const keyOf = (r) => r?.getAttribute("data-row-key") ?? null;
87
165
  const key = keyOf(row);
166
+ const places = placesOf(el);
88
167
  const pageChanged = () => {
89
168
  if (ignoreRoute) return false;
90
169
  if (location.pathname !== pathname) return !pathAbove(location.pathname, pathname);
@@ -117,16 +196,26 @@ function rememberFocus(el, { ignoreRoute = false } = {}) {
117
196
  return k >= 0 && k <= at ? list.children[Math.max(0, at - 1)] ?? void 0 : void 0;
118
197
  };
119
198
  const neighbour = () => inRow(byKey(keyOf(before)) ?? before) || inRow(byKey(keyOf(after)) ?? after) || !!list?.isConnected && land(tabStops(list)[0]);
199
+ const lastResort = () => {
200
+ setTimeout(() => {
201
+ const active = document.activeElement;
202
+ if (active && active !== document.body && active.isConnected || pageChanged()) return;
203
+ landLastResort(places);
204
+ }, LAST_RESORT_MS);
205
+ return false;
206
+ };
120
207
  let unwatch = null;
121
208
  const watch = () => {
122
209
  unwatch?.();
123
210
  if (!list || typeof MutationObserver === "undefined") return;
124
- const observer = new MutationObserver(() => {
211
+ const observer = new MutationObserver((changes) => {
125
212
  if (el.isConnected) return;
126
213
  const active = document.activeElement;
127
214
  const dropped = !active || active === document.body || !active.isConnected;
128
215
  stop();
129
- if (dropped && !pageChanged()) neighbour();
216
+ if (!dropped || pageChanged()) return;
217
+ const fromList = changes.some((c) => c.target === list || c.target === list.parentNode);
218
+ if (!(fromList && neighbour())) lastResort();
130
219
  });
131
220
  const onFocusIn = (e) => {
132
221
  if (e.target !== el) stop();
@@ -140,6 +229,7 @@ function rememberFocus(el, { ignoreRoute = false } = {}) {
140
229
  }
141
230
  observer.observe(list, { childList: true });
142
231
  if (list.parentNode) observer.observe(list.parentNode, { childList: true });
232
+ for (const { box } of places) observer.observe(box, { childList: true });
143
233
  document.addEventListener("focusin", onFocusIn, true);
144
234
  unwatch = stop;
145
235
  };
@@ -153,7 +243,89 @@ function rememberFocus(el, { ignoreRoute = false } = {}) {
153
243
  return true;
154
244
  }
155
245
  if (row && !row.isConnected && neighbour()) return true;
156
- return land(fallback);
246
+ return land(fallback) || lastResort();
247
+ }
248
+ };
249
+ }
250
+ function useFocusKeptInList(listRef) {
251
+ useEffect(() => {
252
+ if (typeof MutationObserver === "undefined") return;
253
+ let held = null;
254
+ let observer = null;
255
+ const forget = () => {
256
+ held = null;
257
+ observer?.disconnect();
258
+ observer = null;
259
+ };
260
+ const onFocusIn = (e) => {
261
+ const list = listRef.current;
262
+ const t = e.target;
263
+ if (!list || !(t instanceof HTMLElement) || !list.contains(t)) {
264
+ if (held) forget();
265
+ return;
266
+ }
267
+ held = rememberFocus(t);
268
+ if (observer) return;
269
+ observer = new MutationObserver(() => {
270
+ const h = held;
271
+ if (!h || h.el.isConnected) return;
272
+ const active = document.activeElement;
273
+ forget();
274
+ if (!active || active === document.body || !active.isConnected) h.restore();
275
+ });
276
+ observer.observe(list, { childList: true, subtree: true });
277
+ };
278
+ const onFocusOut = (e) => {
279
+ if (!held || e.target !== held.el) return;
280
+ queueMicrotask(() => {
281
+ if (held?.el.isConnected && !listRef.current?.contains(document.activeElement)) forget();
282
+ });
283
+ };
284
+ document.addEventListener("focusin", onFocusIn, true);
285
+ document.addEventListener("focusout", onFocusOut, true);
286
+ return () => {
287
+ document.removeEventListener("focusin", onFocusIn, true);
288
+ document.removeEventListener("focusout", onFocusOut, true);
289
+ const h = held;
290
+ forget();
291
+ const active = document.activeElement;
292
+ if (h && !h.el.isConnected && (!active || active === document.body || !active.isConnected)) h.restore();
293
+ };
294
+ }, [listRef]);
295
+ }
296
+ var openers = /* @__PURE__ */ new WeakMap();
297
+ function rememberOpener(active, root) {
298
+ let own = active instanceof HTMLElement && active !== document.body && active.isConnected ? rememberFocus(active) : null;
299
+ const controller = !own && root?.id ? document.querySelector(`[aria-controls~="${CSS.escape(root.id)}"]`) : null;
300
+ if (controller) own = rememberFocus(controller);
301
+ let around = null;
302
+ for (let n = active; n && !around; n = n.parentElement) {
303
+ const opener = openers.get(n);
304
+ if (opener) around = { root: n, opener };
305
+ }
306
+ const back = own ?? around?.opener;
307
+ if (!back) {
308
+ if (!root) return null;
309
+ const places = placesOf(root);
310
+ const { pathname } = location;
311
+ return {
312
+ el: null,
313
+ restore() {
314
+ setTimeout(() => {
315
+ const now = document.activeElement;
316
+ if (now && now !== document.body && now.isConnected && !root.contains(now) || location.pathname !== pathname) return;
317
+ landLastResort(places, root);
318
+ }, LAST_RESORT_MS);
319
+ return false;
320
+ }
321
+ };
322
+ }
323
+ if (root) openers.set(root, back);
324
+ return {
325
+ el: own?.el ?? null,
326
+ restore(fallback) {
327
+ if (own?.restore(fallback)) return true;
328
+ return !!around && !around.root.isConnected && around.opener.restore();
157
329
  }
158
330
  };
159
331
  }
@@ -162,8 +334,7 @@ function useReturnFocus(open, root) {
162
334
  const before = useFocusBeforeOpen(open);
163
335
  useEffect(() => {
164
336
  if (open) {
165
- const active2 = before.current;
166
- remembered.current = active2 instanceof HTMLElement && active2 !== document.body && active2.isConnected ? rememberFocus(active2) : null;
337
+ remembered.current = rememberOpener(before.current, root?.current);
167
338
  return;
168
339
  }
169
340
  const r = remembered.current;
@@ -190,6 +361,64 @@ function rovingTabStop(items, isChecked, isDisabled = () => false) {
190
361
  function focusAfterRemoval(items, index, fallback) {
191
362
  (items[index - 1] ?? items[index] ?? fallback)?.focus();
192
363
  }
364
+ function mayStop(root) {
365
+ const disabled = (el) => (el instanceof HTMLButtonElement || el instanceof HTMLInputElement || el instanceof HTMLSelectElement || el instanceof HTMLTextAreaElement) && el.disabled;
366
+ return Array.from(root.querySelectorAll(`${FOCUSABLE}, button:disabled, input:disabled, select:disabled, textarea:disabled`)).filter((el) => disabled(el) ? !(el instanceof HTMLInputElement && el.type === "hidden") : isTabStop(el)).sort((a, b) => a.compareDocumentPosition(b) & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1);
367
+ }
368
+ function focusAfterSelfRemoval(control, { item, group, to } = {}) {
369
+ const goes = item ?? control;
370
+ const within = group === void 0 ? goes.parentElement : group;
371
+ const stops = within ? mayStop(within).filter((el) => !goes.contains(el)) : [];
372
+ const before = stops.filter((el) => goes.compareDocumentPosition(el) & Node.DOCUMENT_POSITION_PRECEDING).reverse();
373
+ const after = stops.filter((el) => goes.compareDocumentPosition(el) & Node.DOCUMENT_POSITION_FOLLOWING);
374
+ const stopsOf = (el) => [...isTabStop(el) ? [el] : [], ...tabStops(el)];
375
+ const index = stopsOf(goes).indexOf(control);
376
+ const neighbour = (step) => {
377
+ for (let el = goes[step]; el; el = el[step]) {
378
+ const own = stopsOf(el);
379
+ if (own.length) return own[Math.max(0, Math.min(index, own.length - 1))];
380
+ }
381
+ return null;
382
+ };
383
+ const sameBefore = neighbour("previousElementSibling");
384
+ const sameAfter = neighbour("nextElementSibling");
385
+ const gone = () => !control.isConnected || control instanceof HTMLButtonElement && control.disabled;
386
+ const lost = () => {
387
+ const active = document.activeElement;
388
+ return !active || active === document.body || !active.isConnected || active === control;
389
+ };
390
+ const land = (el) => {
391
+ if (!el || !el.isConnected || !isTabStop(el)) return false;
392
+ el.focus();
393
+ return document.activeElement === el;
394
+ };
395
+ const landOnGroup = () => {
396
+ if (!within?.isConnected) return false;
397
+ if (!within.hasAttribute("tabindex") && within.tabIndex < 0) within.setAttribute("tabindex", "-1");
398
+ within.focus();
399
+ return document.activeElement === within;
400
+ };
401
+ const place = () => land(typeof to === "function" ? to() : to) || land(sameBefore) || before.some(land) || land(sameAfter) || after.some(land) || landOnGroup();
402
+ if (typeof MutationObserver === "undefined") return () => {
403
+ };
404
+ const observer = new MutationObserver(() => {
405
+ if (!gone()) return;
406
+ stop();
407
+ if (lost()) place();
408
+ });
409
+ const onFocusIn = (e) => {
410
+ if (e.target !== control) stop();
411
+ };
412
+ const timer = setTimeout(() => stop(), 1e4);
413
+ function stop() {
414
+ observer.disconnect();
415
+ clearTimeout(timer);
416
+ document.removeEventListener("focusin", onFocusIn, true);
417
+ }
418
+ observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ["disabled"] });
419
+ document.addEventListener("focusin", onFocusIn, true);
420
+ return stop;
421
+ }
193
422
  var layers = [];
194
423
  function useOverlayLayer(open) {
195
424
  const [id] = useState(() => /* @__PURE__ */ Symbol("overlay-layer"));
@@ -243,6 +472,28 @@ function usePressMarks() {
243
472
  return { props: { onPointerDownCapture: mark, onMouseDownCapture: mark }, marked: (e) => marks.has(e) };
244
473
  }, [marks]);
245
474
  }
475
+ function useFocusLeave(onLeave) {
476
+ const onLeaveRef = useLatest(onLeave);
477
+ const leaving = useRef(null);
478
+ useEffect(() => () => {
479
+ if (leaving.current) clearTimeout(leaving.current);
480
+ }, []);
481
+ return useMemo(() => ({
482
+ onFocusCapture: () => {
483
+ if (leaving.current) {
484
+ clearTimeout(leaving.current);
485
+ leaving.current = null;
486
+ }
487
+ },
488
+ onBlurCapture: () => {
489
+ if (leaving.current) clearTimeout(leaving.current);
490
+ leaving.current = setTimeout(() => {
491
+ leaving.current = null;
492
+ onLeaveRef.current();
493
+ }, 0);
494
+ }
495
+ }), [onLeaveRef]);
496
+ }
246
497
  var openPopups = /* @__PURE__ */ new Map();
247
498
  function usePopupFocus(open, { trigger, panel, close, tabExits, onTabOut, marked }) {
248
499
  const isTop = useOverlayLayer(open);
@@ -261,6 +512,7 @@ function usePopupFocus(open, { trigger, panel, close, tabExits, onTabOut, marked
261
512
  const inPanel = active instanceof Node && !!panel.current?.contains(active);
262
513
  const opener = active instanceof HTMLElement && active !== document.body && !inPanel ? active : trigger.current;
263
514
  remembered.current = opener ? rememberFocus(opener) : null;
515
+ if (panel.current && remembered.current) openers.set(panel.current, remembered.current);
264
516
  lastPress.current = null;
265
517
  }, [open, trigger, panel, before]);
266
518
  useEffect(() => {
@@ -335,7 +587,19 @@ function usePopupFocus(open, { trigger, panel, close, tabExits, onTabOut, marked
335
587
  const r = remembered.current;
336
588
  remembered.current = null;
337
589
  const press = lastPress.current;
338
- if (!r || press && !press.inside && !markedRef.current?.(press.e)) return;
590
+ if (!r) return;
591
+ if (press && !press.inside && !markedRef.current?.(press.e)) {
592
+ const modal = r.el.closest('[aria-modal="true"]');
593
+ const target = press.e.target;
594
+ if (modal && target instanceof Node && modal.contains(target)) {
595
+ setTimeout(() => {
596
+ const active2 = document.activeElement;
597
+ if (active2 && active2 !== document.body && active2.isConnected) return;
598
+ if (modal.isConnected) r.restore(trigger.current);
599
+ }, 0);
600
+ }
601
+ return;
602
+ }
339
603
  const active = document.activeElement;
340
604
  if (active !== r.el && active && active !== document.body && active.isConnected) return;
341
605
  r.restore(trigger.current);
@@ -428,12 +692,13 @@ function useGridKeyboard({ container, active, setActive, keyOf, move, onActivate
428
692
  return { onKeyDown, tabIndexOf, focusActive, focus };
429
693
  }
430
694
  var ALWAYS_TOP = () => true;
431
- function useFocusTrap(root, active, isTop = ALWAYS_TOP) {
695
+ function useFocusTrap(root, active, isTop = ALWAYS_TOP, also) {
432
696
  useEffect(() => {
433
697
  if (!active) return;
434
698
  const onKeyDown = (e) => {
435
699
  if (e.key !== "Tab" || e.defaultPrevented || !root.current || !isTop()) return;
436
- const focusable = tabStops(root.current);
700
+ const scope = [root.current, also?.current].filter((el) => !!el);
701
+ const focusable = scope.flatMap((el) => tabStops(el)).sort((a, b) => a.compareDocumentPosition(b) & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1);
437
702
  if (focusable.length === 0) {
438
703
  e.preventDefault();
439
704
  return;
@@ -441,7 +706,7 @@ function useFocusTrap(root, active, isTop = ALWAYS_TOP) {
441
706
  const first = focusable[0];
442
707
  const last = focusable[focusable.length - 1];
443
708
  const current = document.activeElement;
444
- const inside = current instanceof Node && root.current.contains(current);
709
+ const inside = current instanceof Node && scope.some((el) => el.contains(current));
445
710
  if (e.shiftKey) {
446
711
  if (!inside || current === first) {
447
712
  e.preventDefault();
@@ -454,7 +719,24 @@ function useFocusTrap(root, active, isTop = ALWAYS_TOP) {
454
719
  };
455
720
  document.addEventListener("keydown", onKeyDown);
456
721
  return () => document.removeEventListener("keydown", onKeyDown);
457
- }, [root, active, isTop]);
722
+ }, [root, active, isTop, also]);
723
+ }
724
+ function inertOutside(keep, within) {
725
+ const made = [];
726
+ const visit = (el) => {
727
+ for (const child of Array.from(el.children)) {
728
+ if (keep.includes(child)) continue;
729
+ if (keep.some((k) => child.contains(k))) visit(child);
730
+ else if (!child.hasAttribute("inert")) {
731
+ child.setAttribute("inert", "");
732
+ made.push(child);
733
+ }
734
+ }
735
+ };
736
+ visit(within);
737
+ return () => {
738
+ for (const el of made) el.removeAttribute("inert");
739
+ };
458
740
  }
459
741
  function useNonModalDialog(root, active, onClose, isTop = ALWAYS_TOP) {
460
742
  const onCloseRef = useLatest(onClose);
@@ -478,6 +760,6 @@ function useNonModalDialog(root, active, onClose, isTop = ALWAYS_TOP) {
478
760
  }, [active, root, isTop, onCloseRef]);
479
761
  }
480
762
 
481
- export { FIELD, FOCUSABLE, OVERLAY_CLOSE_ATTR, ZOOM_KEYS, focusAfterRemoval, focusInitial, isTabStop, rememberFocus, rovingTabStop, tabStops, useCloseOnFocusOut, useFocusBeforeOpen, useFocusTrap, useGridKeyboard, useNonModalDialog, useOverlayLayer, usePopupFocus, usePressMarks, useReturnFocus };
482
- //# sourceMappingURL=chunk-ZI3OQDET.js.map
483
- //# sourceMappingURL=chunk-ZI3OQDET.js.map
763
+ export { CONTROL_ANCESTOR, FIELD, FOCUSABLE, OVERLAY_CLOSE_ATTR, ZOOM_KEYS, focusAfterRemoval, focusAfterSelfRemoval, focusInitial, inertOutside, isTabStop, rememberFocus, rememberOpener, rovingTabStop, tabStops, useCloseOnFocusOut, useFocusBeforeOpen, useFocusKeptInList, useFocusLeave, useFocusTrap, useGridKeyboard, useNonModalDialog, useOverlayLayer, usePopupFocus, usePressMarks, useReturnFocus };
764
+ //# sourceMappingURL=chunk-R5QXMNW7.js.map
765
+ //# sourceMappingURL=chunk-R5QXMNW7.js.map