gridsmith-ui 0.31.1 → 0.33.0

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 (84) hide show
  1. package/CHANGELOG.md +78 -1
  2. package/README.md +4 -2
  3. package/dist/charts.d.ts +111 -31
  4. package/dist/charts.js +288 -148
  5. package/dist/charts.js.map +1 -1
  6. package/dist/chat.js +6 -6
  7. package/dist/{chunk-EZ3ZGFGA.js → chunk-2724SJO2.js} +12 -4
  8. package/dist/chunk-2724SJO2.js.map +1 -0
  9. package/dist/{chunk-I6JJ7KKP.js → chunk-6NRLTG5W.js} +16 -11
  10. package/dist/chunk-6NRLTG5W.js.map +1 -0
  11. package/dist/{chunk-BHTAVJIG.js → chunk-6XCWKLJZ.js} +41 -6
  12. package/dist/chunk-6XCWKLJZ.js.map +1 -0
  13. package/dist/{chunk-TH272DIC.js → chunk-AB4UQ4G5.js} +125 -235
  14. package/dist/chunk-AB4UQ4G5.js.map +1 -0
  15. package/dist/{chunk-IOHGE4EW.js → chunk-AVSP6NDX.js} +8 -8
  16. package/dist/chunk-AVSP6NDX.js.map +1 -0
  17. package/dist/chunk-F2XMZOUP.js +81 -0
  18. package/dist/chunk-F2XMZOUP.js.map +1 -0
  19. package/dist/{chunk-HQFTP7Y5.js → chunk-FF57N3P7.js} +10 -8
  20. package/dist/chunk-FF57N3P7.js.map +1 -0
  21. package/dist/chunk-GIA4BMGI.js +18 -0
  22. package/dist/chunk-GIA4BMGI.js.map +1 -0
  23. package/dist/{chunk-DR6WEI3A.js → chunk-HCEHTJ5E.js} +3 -3
  24. package/dist/{chunk-DR6WEI3A.js.map → chunk-HCEHTJ5E.js.map} +1 -1
  25. package/dist/{chunk-IZW5XHKA.js → chunk-OWGF362U.js} +10 -8
  26. package/dist/chunk-OWGF362U.js.map +1 -0
  27. package/dist/chunk-P3NUUIL6.js +9 -0
  28. package/dist/chunk-P3NUUIL6.js.map +1 -0
  29. package/dist/{chunk-W3GY5ROH.js → chunk-R2IAK3J3.js} +3 -3
  30. package/dist/{chunk-W3GY5ROH.js.map → chunk-R2IAK3J3.js.map} +1 -1
  31. package/dist/chunk-R7FG4ZQ5.js +16 -0
  32. package/dist/chunk-R7FG4ZQ5.js.map +1 -0
  33. package/dist/{chunk-V73LHNRL.js → chunk-RS624TD5.js} +29 -10
  34. package/dist/chunk-RS624TD5.js.map +1 -0
  35. package/dist/{chunk-DBHVJ7Z3.js → chunk-SS3C24IJ.js} +3 -2
  36. package/dist/chunk-SS3C24IJ.js.map +1 -0
  37. package/dist/{chunk-K3NAFQ4S.js → chunk-TMPPBXX3.js} +3 -3
  38. package/dist/{chunk-K3NAFQ4S.js.map → chunk-TMPPBXX3.js.map} +1 -1
  39. package/dist/{chunk-NDL2JGQ7.js → chunk-UBLBVCQN.js} +5 -5
  40. package/dist/{chunk-NDL2JGQ7.js.map → chunk-UBLBVCQN.js.map} +1 -1
  41. package/dist/{chunk-2TCCSKL3.js → chunk-WD6T57ZJ.js} +5 -5
  42. package/dist/{chunk-2TCCSKL3.js.map → chunk-WD6T57ZJ.js.map} +1 -1
  43. package/dist/{chunk-SLGGZ3MU.js → chunk-WSKPESAX.js} +12 -10
  44. package/dist/chunk-WSKPESAX.js.map +1 -0
  45. package/dist/chunk-ZQV26NND.js +213 -0
  46. package/dist/chunk-ZQV26NND.js.map +1 -0
  47. package/dist/dashboard.d.ts +207 -0
  48. package/dist/dashboard.js +1210 -0
  49. package/dist/dashboard.js.map +1 -0
  50. package/dist/data-grid.js +11 -10
  51. package/dist/data-grid.js.map +1 -1
  52. package/dist/editor-mentions.js +2 -2
  53. package/dist/editor.js +3 -2
  54. package/dist/editor.js.map +1 -1
  55. package/dist/index.d.ts +94 -12
  56. package/dist/index.js +601 -316
  57. package/dist/index.js.map +1 -1
  58. package/dist/kanban.js +9 -9
  59. package/dist/resizable.d.ts +11 -2
  60. package/dist/resizable.js +97 -22
  61. package/dist/resizable.js.map +1 -1
  62. package/dist/roots.css +2 -2
  63. package/dist/tokens.css +6 -6
  64. package/dist/utils.d.ts +51 -1
  65. package/dist/utils.js +55 -4
  66. package/dist/utils.js.map +1 -1
  67. package/dist/virtualized.js +9 -8
  68. package/dist/virtualized.js.map +1 -1
  69. package/dist-static/tokens.css +6 -6
  70. package/package.json +6 -1
  71. package/dist/chunk-BHTAVJIG.js.map +0 -1
  72. package/dist/chunk-DBHVJ7Z3.js.map +0 -1
  73. package/dist/chunk-EZ3ZGFGA.js.map +0 -1
  74. package/dist/chunk-HQFTP7Y5.js.map +0 -1
  75. package/dist/chunk-I6JJ7KKP.js.map +0 -1
  76. package/dist/chunk-IOHGE4EW.js.map +0 -1
  77. package/dist/chunk-IZW5XHKA.js.map +0 -1
  78. package/dist/chunk-KSJTMSYA.js +0 -71
  79. package/dist/chunk-KSJTMSYA.js.map +0 -1
  80. package/dist/chunk-LJYSVZMI.js +0 -25
  81. package/dist/chunk-LJYSVZMI.js.map +0 -1
  82. package/dist/chunk-SLGGZ3MU.js.map +0 -1
  83. package/dist/chunk-TH272DIC.js.map +0 -1
  84. package/dist/chunk-V73LHNRL.js.map +0 -1
@@ -1,15 +1,14 @@
1
1
  "use client";
2
- import { useKeyboardScroll } from './chunk-KSJTMSYA.js';
3
2
  import { PreferencesContext } from './chunk-QOMP4D6V.js';
4
- import { FormChangeCtx } from './chunk-72SDAX54.js';
5
- import { SurfaceBoundary } from './chunk-V73LHNRL.js';
6
- import { Button } from './chunk-HQFTP7Y5.js';
7
- import { useBreakpoint, readBreakpointPx } from './chunk-ZHUVLBCC.js';
8
- import { useTextOverflow } from './chunk-W3GY5ROH.js';
9
- import { useRevealFocus, AutoTooltip } from './chunk-DR6WEI3A.js';
10
- import { Icon } from './chunk-53F7GVBQ.js';
11
- import { cn, FOCUS_INSET_WHILE_STOP, usePopupFocus, usePosition, FOCUS_RING, FOCUS_INSET, useLatest, useOverlayLayer, lockBodyScroll, unlockBodyScroll, useReturnFocus, useFocusTrap, inertOutside, focusInitial, useNonModalDialog, OVERLAY_CLOSE_ATTR, OVERLAY_SURFACE, GLASS_OVERLAY, GROUP_FOCUS_RING } from './chunk-EZ3ZGFGA.js';
12
- import { createContext, version, forwardRef, useState, useCallback, useRef, useId, useEffect, useContext, useMemo, useLayoutEffect } from 'react';
3
+ import { SurfaceBoundary } from './chunk-RS624TD5.js';
4
+ import { useTextOverflow } from './chunk-R2IAK3J3.js';
5
+ import { useOverlayMark, useDirtyGuard, DiscardConfirm, OverlayMarkScope, OverlayCloseCtx, OVERLAY_ROOT_ATTR } from './chunk-ZQV26NND.js';
6
+ import { useBreakpoint } from './chunk-ZHUVLBCC.js';
7
+ import { useKeyboardScroll } from './chunk-F2XMZOUP.js';
8
+ import { useRevealFocus, AutoTooltip } from './chunk-HCEHTJ5E.js';
9
+ import { warnOnce, Icon } from './chunk-53F7GVBQ.js';
10
+ import { cn, FOCUS_INSET_WHILE_STOP, usePopupFocus, usePosition, FOCUS_RING, FOCUS_INSET, useLatest, useOverlayLayer, lockBodyScroll, unlockBodyScroll, useReturnFocus, useFocusTrap, inertOutside, focusInitial, useNonModalDialog, OVERLAY_CLOSE_ATTR, OVERLAY_SURFACE, GLASS_OVERLAY, GROUP_FOCUS_RING } from './chunk-2724SJO2.js';
11
+ import { createContext, version, forwardRef, useState, useRef, useCallback, useLayoutEffect, useId, useEffect, useContext, useMemo } from 'react';
13
12
  import { createPortal } from 'react-dom';
14
13
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
15
14
 
@@ -251,183 +250,39 @@ function defaultMaxWidth(container) {
251
250
  const base = container instanceof HTMLElement ? container.clientWidth : typeof window !== "undefined" ? window.innerWidth : 1200;
252
251
  return Math.round(base * 0.6);
253
252
  }
254
- var OVERLAY_ROOT_ATTR = "data-overlay-root";
255
- function controlsOf(root) {
256
- if (!root) return "";
257
- const controls = root.querySelectorAll("input, select, textarea, [role='combobox'], [role='radiogroup'], [role='checkbox'], [role='switch'], [role='slider'], [role='spinbutton'], [contenteditable='true']");
258
- return Array.from(controls).map((el) => el.id || el.getAttribute("name") || el.getAttribute("role") || el.tagName).join("|");
259
- }
260
- var OverlayMarkCtx = createContext(null);
261
- function OverlayMarkScope({ mark, children }) {
262
- return /* @__PURE__ */ jsx(OverlayMarkCtx, { value: mark, children: /* @__PURE__ */ jsx(FormChangeCtx, { value: null, children }) });
263
- }
264
- function useOverlayMark(open) {
265
- const forms = useRef(/* @__PURE__ */ new Map());
266
- const count = useRef(0);
267
- const round = useRef(0);
268
- const [changed, setChanged] = useState(false);
269
- const [holds, setHolds] = useState(0);
270
- const [pends, setPends] = useState(0);
271
- const [answers, setAnswers] = useState([]);
272
- const [wasOpen, setWasOpen] = useState(open);
273
- if (open !== wasOpen) {
274
- setWasOpen(open);
275
- if (!open) {
276
- setChanged(false);
277
- setAnswers([]);
278
- }
279
- }
280
- useEffect(() => {
281
- if (open) return;
282
- round.current++;
283
- for (const [id, f] of forms.current) {
284
- if (f.mounted) f.stamp = 0;
285
- else forms.current.delete(id);
286
- }
287
- }, [open]);
288
- const mark = useMemo(() => ({
289
- report: (form) => {
290
- const f = forms.current.get(form) ?? { stamp: 0, mounted: true };
291
- f.stamp = ++count.current;
292
- forms.current.set(form, f);
293
- setChanged(true);
294
- },
295
- attach: (form) => {
296
- const f = forms.current.get(form) ?? { stamp: 0, mounted: true };
297
- f.mounted = true;
298
- forms.current.set(form, f);
299
- return () => {
300
- const gone = forms.current.get(form);
301
- if (!gone) return;
302
- if (gone.stamp > 0) gone.mounted = false;
303
- else forms.current.delete(form);
304
- };
305
- },
306
- submit: (form, from) => {
307
- const root = from.element()?.closest(`[${OVERLAY_ROOT_ATTR}]`) ?? null;
308
- const answer = { form, since: count.current, root, controls: controlsOf(root), ...from };
309
- const at = round.current;
310
- let settled = false;
311
- return {
312
- settle: (how) => {
313
- if (settled || at !== round.current) return;
314
- settled = true;
315
- setAnswers((list) => [...list, { ...answer, how }]);
316
- }
317
- };
318
- },
319
- hold: () => {
320
- setHolds((n) => n + 1);
321
- let released = false;
322
- return () => {
323
- if (released) return;
324
- released = true;
325
- setHolds((n) => n - 1);
326
- };
327
- },
328
- pend: () => {
329
- setPends((n) => n + 1);
330
- let released = false;
331
- return () => {
332
- if (released) return;
333
- released = true;
334
- setPends((n) => n - 1);
335
- };
336
- },
337
- open
338
- }), [open]);
253
+ var DrawerWidthCtx = createContext(null);
254
+ var BulkBarHostCtx = createContext(null);
255
+ var BulkBarPresenceCtx = createContext(null);
256
+ var BULK_BAR_GAP = 16;
257
+ var LAYOUT = "pointer-events-none inset-x-0 bottom-0 z-[var(--ds-z-sticky)] flex flex-col items-center gap-ds-2 px-ds-4 pb-ds-4";
258
+ var BULK_BAR_LAYER = `absolute ${LAYOUT}`;
259
+ var BULK_BAR_WINDOW_LAYER = `fixed ${LAYOUT} pb-[calc(var(--ds-space-4)+env(safe-area-inset-bottom))]`;
260
+ var bulkBarRoom = (height) => height > 0 ? height + BULK_BAR_GAP * 2 : 0;
261
+ function useBulkBarHost() {
262
+ const presence = useContext(BulkBarPresenceCtx);
263
+ const [layer, setLayer] = useState(null);
264
+ const [room, setRoomState] = useState(0);
265
+ const bars = useRef(/* @__PURE__ */ new Map());
266
+ const setRoom = useCallback((bar, height) => {
267
+ if (height > 0) bars.current.set(bar, height);
268
+ else bars.current.delete(bar);
269
+ const heights = [...bars.current.values()];
270
+ const gap = layer ? parseFloat(getComputedStyle(layer).rowGap) || 0 : 0;
271
+ setRoomState(heights.length ? heights.reduce((sum, h) => sum + h, 0) + gap * (heights.length - 1) : 0);
272
+ }, [layer]);
273
+ const [owner] = useState(() => ({}));
339
274
  useEffect(() => {
340
- if (!answers.length) return;
341
- for (const a of answers) {
342
- const form = a.element();
343
- if (form?.isConnected && (form.querySelector("[aria-invalid='true']") || a.failing())) continue;
344
- if (a.how === "returned") {
345
- const now = a.root?.isConnected ? controlsOf(a.root) : "";
346
- if (now !== "" && now !== a.controls) continue;
347
- }
348
- for (const [id, f] of forms.current) {
349
- if (f.stamp === 0 || f.stamp > a.since || id !== a.form && f.mounted) continue;
350
- if (f.mounted) forms.current.set(id, { ...f, stamp: 0 });
351
- else forms.current.delete(id);
352
- }
353
- }
354
- setAnswers([]);
355
- setChanged([...forms.current.values()].some((f) => f.stamp > 0));
356
- }, [answers]);
357
- return { marked: changed || holds > 0, pending: pends > 0, mark };
275
+ presence?.room(owner, bulkBarRoom(room));
276
+ return () => presence?.room(owner, 0);
277
+ }, [presence, owner, room]);
278
+ const value = useMemo(() => ({ layer, setRoom }), [layer, setRoom]);
279
+ return { setLayer, room, value };
358
280
  }
359
- var OverlayCloseCtx = createContext(null);
360
- function useDirtyGuard(dirty, onClose, onDiscard) {
361
- const [wantsConfirm, setWantsConfirm] = useState(false);
362
- const confirming = wantsConfirm && !!dirty;
363
- const pending = useRef(void 0);
364
- const runAfter = useCallback((then) => {
365
- if (typeof then === "function" && then !== onClose) then();
366
- }, [onClose]);
367
- const requestClose = useCallback((then) => {
368
- if (!dirty) {
369
- onClose();
370
- runAfter(then);
371
- return;
372
- }
373
- pending.current = then;
374
- setWantsConfirm((c) => !c);
375
- }, [dirty, onClose, runAfter]);
376
- const keep = useCallback(() => {
377
- pending.current = void 0;
378
- setWantsConfirm(false);
379
- }, []);
380
- const discard = useCallback(() => {
381
- setWantsConfirm(false);
382
- onDiscard?.();
383
- onClose();
384
- const then = pending.current;
385
- pending.current = void 0;
386
- runAfter(then);
387
- }, [onClose, onDiscard, runAfter]);
388
- return { confirming, requestClose, keep, discard };
281
+ function BulkBarSpacer({ room, extra }) {
282
+ const space = bulkBarRoom(room);
283
+ if (!space) return null;
284
+ return /* @__PURE__ */ jsx("div", { "aria-hidden": "true", "data-bulk-bar-spacer": "", style: { height: extra ? `calc(${space}px + ${extra})` : space } });
389
285
  }
390
- function DiscardConfirm({ open, onKeep, onDiscard }) {
391
- const keepRef = useRef(null);
392
- const returnTo = useRef(null);
393
- const overlay = useRef(null);
394
- useEffect(() => {
395
- if (!open) return;
396
- const active = document.activeElement instanceof HTMLElement ? document.activeElement : null;
397
- overlay.current = keepRef.current?.closest("[role='dialog'], [role='alertdialog']") ?? null;
398
- returnTo.current = active && active !== document.body && overlay.current?.contains(active) ? active : null;
399
- keepRef.current?.focus();
400
- return () => {
401
- if (document.activeElement !== document.body) return;
402
- const back = returnTo.current;
403
- if (back && back.isConnected) {
404
- back.focus();
405
- return;
406
- }
407
- const first = overlay.current?.querySelector("button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex='-1'])");
408
- (first ?? overlay.current)?.focus();
409
- };
410
- }, [open]);
411
- if (!open) return null;
412
- return /* @__PURE__ */ jsxs(
413
- "div",
414
- {
415
- role: "group",
416
- "aria-label": "Unsaved changes",
417
- "data-discard-confirm": "",
418
- className: "shrink-0 flex flex-wrap items-center justify-between gap-ds-3 border-t-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)] bg-[var(--ds-status-warning-bg)]",
419
- style: { padding: "var(--ds-space-3) var(--ds-page-gutter)" },
420
- children: [
421
- /* @__PURE__ */ jsx("p", { className: "text-sm text-[var(--ds-text-primary)]", children: "You have unsaved changes." }),
422
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-ds-2", children: [
423
- /* @__PURE__ */ jsx(Button, { type: "button", ref: keepRef, variant: "secondary", size: "sm", onClick: onKeep, children: "Keep editing" }),
424
- /* @__PURE__ */ jsx(Button, { type: "button", variant: "destructive", size: "sm", onClick: onDiscard, children: "Discard changes" })
425
- ] })
426
- ]
427
- }
428
- );
429
- }
430
- var DrawerWidthCtx = createContext(null);
431
286
  function useScrollbarInset() {
432
287
  const observer = useRef(null);
433
288
  return useCallback((el) => {
@@ -447,6 +302,7 @@ function useScrollbarInset() {
447
302
  }, []);
448
303
  }
449
304
  var insetLessScrollbar = (gutter) => `max(0px, calc(${gutter} - var(--gs-scrollbar, 0px)))`;
305
+ var AsideSlotContext = createContext(false);
450
306
  var DrawerFooterCtx = createContext(null);
451
307
  var DrawerScopeCtx = createContext(null);
452
308
  var slideClasses = {
@@ -599,6 +455,7 @@ function DrawerPanel({ form, open, onClose, position = "right", width = "400px",
599
455
  setBodyEl(el);
600
456
  }, [bodyInset]);
601
457
  useKeyboardScroll(bodyEl, typeof title === "string" ? title : "Content", open);
458
+ const { setLayer: setBarLayer, room: barStack, value: barHost } = useBulkBarHost();
602
459
  useCloseSettle(open, panelRef, () => {
603
460
  if (keepMounted) setHidden(true);
604
461
  else setMounted(false);
@@ -771,15 +628,24 @@ function DrawerPanel({ form, open, onClose, position = "right", width = "400px",
771
628
  ]
772
629
  }
773
630
  ),
774
- /* @__PURE__ */ jsx(
775
- "div",
776
- {
777
- ref: bodyRef,
778
- className: cn("flex-1 overflow-y-auto", FOCUS_INSET),
779
- style: flush ? void 0 : { padding: `var(--ds-space-5) ${insetLessScrollbar(INSET)} var(--ds-space-5) ${INSET}` },
780
- children: /* @__PURE__ */ jsx(SurfaceBoundary, { children })
781
- }
782
- ),
631
+ /* @__PURE__ */ jsxs("div", { className: "relative flex-1 min-h-0 flex flex-col", children: [
632
+ /* @__PURE__ */ jsxs(
633
+ "div",
634
+ {
635
+ ref: bodyRef,
636
+ className: cn("flex-1 min-h-0 overflow-y-auto", FOCUS_INSET),
637
+ style: {
638
+ ...flush ? {} : { padding: `var(--ds-space-5) ${insetLessScrollbar(INSET)} var(--ds-space-5) ${INSET}` },
639
+ ...barStack ? { scrollPaddingBottom: bulkBarRoom(barStack) } : {}
640
+ },
641
+ children: [
642
+ /* @__PURE__ */ jsx(BulkBarHostCtx, { value: barHost, children: /* @__PURE__ */ jsx(SurfaceBoundary, { children: /* @__PURE__ */ jsx(AsideSlotContext, { value: true, children }) }) }),
643
+ /* @__PURE__ */ jsx(BulkBarSpacer, { room: barStack })
644
+ ]
645
+ }
646
+ ),
647
+ /* @__PURE__ */ jsx("div", { ref: setBarLayer, "data-bulk-bar-layer": "", className: BULK_BAR_LAYER })
648
+ ] }),
783
649
  confirm,
784
650
  footer && /* @__PURE__ */ jsx(
785
651
  "div",
@@ -859,8 +725,47 @@ var AppShell = forwardRef(function AppShell2({
859
725
  }, ref) {
860
726
  if (REACT_VERSION_PROBLEM) throw new Error(REACT_VERSION_PROBLEM);
861
727
  const [sidebarOpen, setSidebarOpen] = useState(false);
862
- const [asideOpen, setAsideOpen] = useState(false);
863
- const [asideFloating, setAsideFloating] = useState(false);
728
+ const [ownAsideOpen, setAsideOpen] = useState(false);
729
+ const [ownAsideFloating, setAsideFloating] = useState(false);
730
+ const [leftAsideHost, setLeftAsideHost] = useState(null);
731
+ const [rightAsideHost, setRightAsideHost] = useState(null);
732
+ const asides = useRef(/* @__PURE__ */ new Map());
733
+ const opened = useRef([]);
734
+ const [openings, setOpenings] = useState(0);
735
+ const [docked, setDocked] = useState(null);
736
+ const [asked, setAsked] = useState([]);
737
+ const reportAside = useCallback((onClose, state) => {
738
+ if (state) asides.current.set(onClose, state);
739
+ else asides.current.delete(onClose);
740
+ if (state?.opened) {
741
+ opened.current.push(onClose);
742
+ setOpenings((n) => n + 1);
743
+ }
744
+ let next = null;
745
+ for (const s of asides.current.values()) if (s.open && s.docked) next = { side: s.side, floating: s.floating };
746
+ setDocked((d) => d?.side === next?.side && d?.floating === next?.floating ? d : next);
747
+ }, []);
748
+ useLayoutEffect(() => {
749
+ const keys = opened.current.filter((key) => asides.current.get(key)?.open);
750
+ opened.current = [];
751
+ if (keys.length === 0) return;
752
+ let winner2 = keys[keys.length - 1];
753
+ for (const key of keys) if (asides.current.get(key)?.page) winner2 = key;
754
+ const closing = [...asides.current].filter(([key, s]) => key !== winner2 && s.open).map(([key]) => key);
755
+ for (const key of closing) key.current();
756
+ if (closing.length > 0) setAsked(closing);
757
+ }, [openings]);
758
+ useLayoutEffect(() => {
759
+ for (const key of asked) {
760
+ const s = asides.current.get(key);
761
+ if (!s?.open) continue;
762
+ const name = typeof document !== "undefined" && document.getElementById(s.name)?.textContent?.trim() || s.name;
763
+ warnOnce("AsidePanel", `"${name}" stayed open as another opened: one AsidePanel is open at a time in a shell, the last opened, so its onClose must close it (set its open to false).`);
764
+ }
765
+ }, [asked]);
766
+ const asideOpen = docked !== null || ownAsideOpen;
767
+ const asideFloating = docked ? docked.floating : ownAsideFloating;
768
+ const asideFloatingSide = docked ? docked.floating ? docked.side : null : ownAsideOpen && ownAsideFloating ? asideSide : null;
864
769
  const isMobile = useBreakpoint("md");
865
770
  const isSmallMobile = useBreakpoint("sm");
866
771
  const isTablet = isMobile && !isSmallMobile;
@@ -913,6 +818,10 @@ var AppShell = forwardRef(function AppShell2({
913
818
  mainRef.current = el;
914
819
  setMainEl(el);
915
820
  }, []);
821
+ const { setLayer: setBarLayer, room: barStack, value: barHost } = useBulkBarHost();
822
+ const barRoom = bulkBarRoom(barStack);
823
+ const barSafe = isSmallMobile && hasMobileNav ? "0px" : "env(safe-area-inset-bottom)";
824
+ const barSpacer = /* @__PURE__ */ jsx(BulkBarSpacer, { room: barStack, extra: barSafe });
916
825
  useKeyboardScroll(mainEl, "Main content");
917
826
  const navbarRef = useRef(null);
918
827
  const navbarSlot = navbar != null && navbar !== false ? /* @__PURE__ */ jsx("div", { ref: navbarRef, "data-ds-navbar-slot": "", className: "contents", children: navbar }) : navbar;
@@ -941,8 +850,8 @@ var AppShell = forwardRef(function AppShell2({
941
850
  return /* @__PURE__ */ jsx(PreferencesContext, { value: preferences ?? null, children: /* @__PURE__ */ jsx(DrawerWidthCtx, { value: drawerWidth, children: /* @__PURE__ */ jsx(
942
851
  AppShellContext,
943
852
  {
944
- value: { hasSidebar, hasMobileMenu, hasMobileNav, sidebarOpen, setSidebarOpen, isMobile, isTablet, sidebarCollapsed, setSidebarCollapsed: setUserCollapsed, asideOpen, setAsideOpen, asideSide, asideFloating, setAsideFloating, container: shellEl, theme: theme ?? null, isHome, navbarFloating, sidebarFloating, registerChrome, hasLauncher, launcherCorner, registerLauncher, hasFooter, focus: focus ?? null, hasUnsavedChanges: unsavedCount > 0, addUnsavedChanges, drawerWidth, navigate: navigate ?? null, flowLeft: leftCount > 0, markFlowLeft },
945
- children: /* @__PURE__ */ jsxs("div", { ref: (el) => {
853
+ value: { hasSidebar, hasMobileMenu, hasMobileNav, sidebarOpen, setSidebarOpen, isMobile, isTablet, sidebarCollapsed, setSidebarCollapsed: setUserCollapsed, asideOpen, setAsideOpen, asideSide, asideFloating, setAsideFloating, asideFloatingSide, reportAside, asideHosts: { left: leftAsideHost, right: rightAsideHost }, container: shellEl, theme: theme ?? null, isHome, navbarFloating, sidebarFloating, registerChrome, hasLauncher, launcherCorner, registerLauncher, hasFooter, focus: focus ?? null, hasUnsavedChanges: unsavedCount > 0, addUnsavedChanges, drawerWidth, navigate: navigate ?? null, flowLeft: leftCount > 0, markFlowLeft },
854
+ children: /* @__PURE__ */ jsx(AsideSlotContext, { value: false, children: /* @__PURE__ */ jsxs("div", { ref: (el) => {
946
855
  setShellEl(el);
947
856
  if (typeof ref === "function") ref(el);
948
857
  else if (ref) ref.current = el;
@@ -964,11 +873,12 @@ var AppShell = forwardRef(function AppShell2({
964
873
  layout === "multi-column" && navbarSlot,
965
874
  /* @__PURE__ */ jsxs("div", { className: "flex flex-1 min-h-0", children: [
966
875
  hasSidebar && !isMobile && /* @__PURE__ */ jsx("div", { className: "shrink-0 h-full", children: sidebar }),
967
- aside && asideSide === "left" && aside,
968
- /* @__PURE__ */ jsxs("div", { className: "flex-1 flex flex-col min-w-0 min-h-0", children: [
876
+ aside && asideSide === "left" && /* @__PURE__ */ jsx(AsideSlotContext, { value: true, children: aside }),
877
+ /* @__PURE__ */ jsx("div", { ref: setLeftAsideHost, "data-shell-aside-host": "left", className: "contents" }),
878
+ /* @__PURE__ */ jsxs("div", { className: "relative flex-1 flex flex-col min-w-0 min-h-0", children: [
969
879
  layout === "sidebar" && navbarSlot,
970
880
  layout === "stacked" && navbarSlot,
971
- /* @__PURE__ */ jsx(
881
+ /* @__PURE__ */ jsx(BulkBarHostCtx, { value: barHost, children: /* @__PURE__ */ jsx(
972
882
  "main",
973
883
  {
974
884
  ref: setMain,
@@ -992,15 +902,21 @@ var AppShell = forwardRef(function AppShell2({
992
902
  scrollable && "overflow-y-auto",
993
903
  layout === "multi-column" && "flex"
994
904
  ),
995
- style: { containerType: "inline-size" },
905
+ style: { containerType: "inline-size", ...barRoom ? { scrollPaddingBottom: `calc(${barRoom}px + ${barSafe})` } : {} },
996
906
  children: scrollable ? footer ? /* @__PURE__ */ jsxs("div", { className: "min-h-full flex flex-col", children: [
997
907
  /* @__PURE__ */ jsx("div", { className: "flex-1", "data-shell-content": "", children }),
998
- footer
999
- ] }) : children : /* @__PURE__ */ jsx("div", { className: "absolute inset-0", children })
908
+ footer,
909
+ barSpacer
910
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
911
+ children,
912
+ barSpacer
913
+ ] }) : /* @__PURE__ */ jsx("div", { className: "absolute inset-0", children })
1000
914
  }
1001
- )
915
+ ) }),
916
+ /* @__PURE__ */ jsx("div", { ref: setBarLayer, "data-bulk-bar-layer": "", className: BULK_BAR_LAYER, style: { paddingBottom: `calc(${BULK_BAR_GAP}px + ${barSafe})` } })
1002
917
  ] }),
1003
- aside && asideSide === "right" && aside
918
+ /* @__PURE__ */ jsx("div", { ref: setRightAsideHost, "data-shell-aside-host": "right", className: "contents" }),
919
+ aside && asideSide === "right" && /* @__PURE__ */ jsx(AsideSlotContext, { value: true, children: aside })
1004
920
  ] }),
1005
921
  showMobileDrawer && isMobile && // Under the navbar, the menu is modal over the page with the navbar in its scope: the rest inert, Tab cycling the
1006
922
  // two, Escape closing it (the Authoring Practices; Sprint 25.29 item 9). The stacked shell, with no offset, traps
@@ -1025,7 +941,7 @@ var AppShell = forwardRef(function AppShell2({
1025
941
  ) }),
1026
942
  isSmallMobile && hasMobileNav && /* @__PURE__ */ jsx("div", { className: "sticky bottom-0 z-[var(--ds-z-sticky)]", children: mobileNav }),
1027
943
  !isSmallMobile && launcher
1028
- ] })
944
+ ] }) })
1029
945
  }
1030
946
  ) }) });
1031
947
  });
@@ -1328,33 +1244,7 @@ var Breadcrumbs = forwardRef(function Breadcrumbs2({
1328
1244
  dropdown
1329
1245
  ] });
1330
1246
  });
1331
- function useCompact(ref, breakpoint, band = 0) {
1332
- const px = typeof breakpoint === "number" ? breakpoint : readBreakpointPx(breakpoint);
1333
- const width = useElementWidth(ref);
1334
- return useBelow(width, px, band);
1335
- }
1336
- function useBelow(width, threshold, band = 0) {
1337
- const [below, setBelow] = useState(false);
1338
- const next = width > 0 && width < (below ? threshold + band : threshold);
1339
- if (next !== below) setBelow(next);
1340
- return next;
1341
- }
1342
- function useElementWidth(ref) {
1343
- const [width, setWidth] = useState(0);
1344
- const check = useCallback(() => {
1345
- const next = ref.current?.offsetWidth ?? 0;
1346
- if (next > 0) setWidth(next);
1347
- }, [ref]);
1348
- useLayoutEffect(() => {
1349
- check();
1350
- if (!ref.current || typeof ResizeObserver === "undefined") return;
1351
- const observer = new ResizeObserver(check);
1352
- observer.observe(ref.current);
1353
- return () => observer.disconnect();
1354
- }, [check, ref]);
1355
- return width;
1356
- }
1357
1247
 
1358
- export { AppShell, Breadcrumbs, DiscardConfirm, Drawer, DrawerFooterCtx, DrawerPanel, LAUNCHER_INSET_PRIORITY, NAVBAR_INSET_PRIORITY, OVERLAY_ROOT_ATTR, OverlayCloseCtx, OverlayMarkCtx, OverlayMarkScope, PANEL_INSET_PRIORITY, REACT_VERSION_PROBLEM, ResizeEdge, SkipLink, besideRoom, crumbGoes, defaultMaxWidth, insetLessScrollbar, pxOf, useAppShell, useBelow, useCloseSettle, useCompact, useCornerInset, useCrumbFollow, useDirtyGuard, useElementWidth, useOverlayMark, useRememberedWidth, useScrollbarInset, useUnsavedChanges };
1359
- //# sourceMappingURL=chunk-TH272DIC.js.map
1360
- //# sourceMappingURL=chunk-TH272DIC.js.map
1248
+ export { AppShell, AsideSlotContext, BULK_BAR_LAYER, BULK_BAR_WINDOW_LAYER, Breadcrumbs, BulkBarHostCtx, BulkBarPresenceCtx, BulkBarSpacer, Drawer, DrawerFooterCtx, DrawerPanel, LAUNCHER_INSET_PRIORITY, NAVBAR_INSET_PRIORITY, PANEL_INSET_PRIORITY, REACT_VERSION_PROBLEM, ResizeEdge, SkipLink, TOAST_INSET_VARS, besideRoom, bulkBarRoom, crumbGoes, defaultMaxWidth, insetLessScrollbar, pxOf, useAppShell, useBulkBarHost, useCloseSettle, useCornerInset, useCrumbFollow, useRememberedWidth, useScrollbarInset, useUnsavedChanges };
1249
+ //# sourceMappingURL=chunk-AB4UQ4G5.js.map
1250
+ //# sourceMappingURL=chunk-AB4UQ4G5.js.map