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,17 +1,17 @@
1
1
  "use client";
2
- import { useKeyboardScroll } from './chunk-KSJTMSYA.js';
3
- import { useWholeListCheck, pagerPart, Pagination } from './chunk-IOHGE4EW.js';
2
+ import { useWholeListCheck, pagerPart, Pagination } from './chunk-AVSP6NDX.js';
4
3
  import './chunk-QOMP4D6V.js';
5
- import './chunk-IZW5XHKA.js';
4
+ import './chunk-OWGF362U.js';
6
5
  import './chunk-7QLXYRPD.js';
6
+ import { useScrollsInside, useRunsEdgeToEdge } from './chunk-RS624TD5.js';
7
+ import './chunk-R2IAK3J3.js';
7
8
  import './chunk-72SDAX54.js';
8
- import { useScrollsInside } from './chunk-V73LHNRL.js';
9
- import './chunk-HQFTP7Y5.js';
9
+ import './chunk-FF57N3P7.js';
10
10
  import './chunk-7RKC5C7G.js';
11
- import './chunk-W3GY5ROH.js';
12
- import './chunk-DR6WEI3A.js';
11
+ import { useKeyboardScroll } from './chunk-F2XMZOUP.js';
12
+ import './chunk-HCEHTJ5E.js';
13
13
  import './chunk-53F7GVBQ.js';
14
- import { useLatest, cn, FOCUS_INSET } from './chunk-EZ3ZGFGA.js';
14
+ import { useLatest, cn, FOCUS_INSET } from './chunk-2724SJO2.js';
15
15
  import { useRef, useState, useCallback, useEffect } from 'react';
16
16
  import { useVirtualizer } from '@tanstack/react-virtual';
17
17
  import { jsxs, jsx } from 'react/jsx-runtime';
@@ -28,6 +28,7 @@ function VirtualizedList({
28
28
  "aria-label": ariaLabel = "List"
29
29
  }) {
30
30
  useScrollsInside();
31
+ useRunsEdgeToEdge();
31
32
  const parentRef = useRef(null);
32
33
  const endRef = useRef(null);
33
34
  const [scroller, setScroller] = useState(null);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../showcase/src/components/VirtualizedList.tsx"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AAiCO,SAAS,eAAA,CAAmB;AAAA,EACjC,KAAA,EAAO,QAAA;AAAA,EACP,YAAA;AAAA,EACA,UAAA;AAAA,EACA,QAAA,GAAW,CAAA;AAAA,EACX,MAAA,GAAS,GAAA;AAAA,EACT,SAAA;AAAA,EACA,GAAA;AAAA,EACA,KAAA;AAAA,EACA,cAAc,SAAA,GAAY;AAC5B,CAAA,EAA4B;AAE1B,EAAA,gBAAA,EAAiB;AACjB,EAAA,MAAM,SAAA,GAAY,OAAuB,IAAI,CAAA;AAC7C,EAAA,MAAM,MAAA,GAAS,OAAuB,IAAI,CAAA;AAE1C,EAAA,MAAM,CAAC,QAAA,EAAU,WAAW,CAAA,GAAI,SAAgC,IAAI,CAAA;AAEpE,EAAA,iBAAA,CAAkB,UAAU,SAAS,CAAA;AACrC,EAAA,MAAM,OAAA,GAAU,WAAA,CAAY,CAAC,IAAA,KAAgC;AAC3D,IAAC,UAA4D,OAAA,GAAU,IAAA;AACvE,IAAA,WAAA,CAAY,IAAI,CAAA;AAChB,IAAA,IAAI,OAAO,GAAA,KAAQ,UAAA,EAAY,GAAA,CAAI,IAAI,CAAA;AAAA,SAAA,IAC9B,GAAA,EAAM,GAAA,CAAsD,OAAA,GAAU,IAAA;AAAA,EACjF,CAAA,EAAG,CAAC,GAAG,CAAC,CAAA;AACR,EAAA,iBAAA,CAAkB,iBAAA,EAAmB,QAAA,CAAS,MAAA,EAAQ,KAAK,CAAA;AAC3D,EAAA,MAAM,KAAA,GAAQ,SAAA,CAAU,QAAA,EAAU,KAAK,CAAA;AACvC,EAAA,MAAM,MAAA,GAAS,KAAA,GAAQ,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,MAAM,KAAA,CAAM,CAAC,CAAA,GAAI,CAAC,CAAA,GAAI,CAAA;AAGzD,EAAA,MAAM,cAAc,cAAA,CAAe;AAAA,IACjC,OAAO,KAAA,CAAM,MAAA;AAAA,IACb,gBAAA,EAAkB,MAAM,SAAA,CAAU,OAAA;AAAA,IAClC,YAAA,EAAc,CAAC,KAAA,KAAU,YAAA,CAAa,SAAS,KAAK,CAAA;AAAA;AAAA;AAAA,IAGpD,UAAA,EAAY,CAAC,KAAA,KAAU,MAAA,GAAS,KAAA;AAAA,IAChC;AAAA,GACD,CAAA;AAID,EAAA,MAAM,IAAA,GAAO,KAAA,GAAQ,KAAA,CAAM,IAAA,GAAO,IAAA;AAClC,EAAA,MAAM,UAAA,GAAa,OAAO,IAAI,CAAA;AAC9B,EAAA,MAAM,QAAA,GAAW,OAAO,KAAA,KAAU,YAAA;AAClC,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,MAAM,SAAS,UAAA,CAAW,OAAA;AAC1B,IAAA,UAAA,CAAW,OAAA,GAAU,IAAA;AACrB,IAAA,IAAI,IAAA,KAAS,IAAA,IAAQ,CAAC,SAAA,CAAU,OAAA,EAAS;AACzC,IAAA,IAAI,QAAA,GAAW,SAAS,MAAA,GAAS,MAAA,KAAW,QAAQ,IAAA,GAAO,MAAA,EAAQ,SAAA,CAAU,OAAA,CAAQ,SAAA,GAAY,CAAA;AAAA,EACnG,CAAA,EAAG,CAAC,IAAA,EAAM,QAAQ,CAAC,CAAA;AAInB,EAAA,MAAM,QAAA,GAAW,SAAA,CAAU,KAAA,EAAO,QAAQ,CAAA;AAC1C,EAAA,MAAM,OAAA,GAAU,KAAA,EAAO,KAAA,KAAU,iBAAA,IAAqB,KAAA,CAAM,OAAA;AAC5D,EAAA,MAAM,QAAA,GAAW,OAAO,EAAE,CAAA;AAC1B,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,MAAM,OAAO,SAAA,CAAU,OAAA;AACvB,IAAA,MAAM,MAAM,MAAA,CAAO,OAAA;AAGnB,IAAA,IAAI,QAAA,CAAS,OAAA,KAAY,KAAA,CAAM,MAAA,WAAiB,OAAA,GAAU,EAAA;AAC1D,IAAA,IAAI,CAAC,WAAW,CAAC,IAAA,IAAQ,CAAC,GAAA,IAAO,OAAO,yBAAyB,WAAA,EAAa;AAC9E,IAAA,MAAM,QAAA,GAAW,IAAI,oBAAA,CAAqB,CAAC,OAAA,KAAY;AACrD,MAAA,IAAI,CAAC,OAAA,CAAQ,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,cAAc,CAAA,IAAK,QAAA,CAAS,OAAA,KAAY,KAAA,CAAM,MAAA,EAAQ;AACjF,MAAA,QAAA,CAAS,UAAU,KAAA,CAAM,MAAA;AACzB,MAAA,QAAA,CAAS,OAAA,IAAU;AAAA,IACrB,CAAA,EAAG,EAAE,IAAA,EAAM,UAAA,EAAY,aAAa,CAAA;AACpC,IAAA,QAAA,CAAS,QAAQ,GAAG,CAAA;AACpB,IAAA,OAAO,MAAM,SAAS,UAAA,EAAW;AAAA,EACnC,GAAG,CAAC,OAAA,EAAS,KAAA,CAAM,MAAA,EAAQ,QAAQ,CAAC,CAAA;AAEpC,EAAA,MAAM,IAAA,mBACJ,IAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,GAAA,EAAK,OAAA;AAAA,MACL,cAAA,EAAc,QAAQ,EAAA,GAAK,MAAA;AAAA,MAC3B,SAAA,EAAW,EAAA;AAAA,QACT,8BAAA;AAAA,QACA,WAAA;AAAA,QACA,+BAAA;AAAA,QACA,yEAAA;AAAA,QACA,2BAAA;AAAA,QACA,CAAC,KAAA,IAAS;AAAA,OACZ;AAAA,MACA,KAAA,EAAO,EAAE,MAAA,EAAO;AAAA,MAEhB,QAAA,EAAA;AAAA,wBAAA,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,iBAAA,EAAkB,KAAA,EAAO,EAAE,MAAA,EAAQ,WAAA,CAAY,YAAA,EAAa,IACxE,QAAA,EAAA,WAAA,CAAY,eAAA,EAAgB,CAAE,GAAA,CAAI,CAAC,WAAA,qBAClC,GAAA;AAAA,UAAC,KAAA;AAAA,UAAA;AAAA,YAEC,cAAY,WAAA,CAAY,KAAA;AAAA,YACxB,KAAK,WAAA,CAAY,cAAA;AAAA,YACjB,SAAA,EAAU,8BAAA;AAAA,YACV,OAAO,EAAE,SAAA,EAAW,CAAA,WAAA,EAAc,WAAA,CAAY,KAAK,CAAA,GAAA,CAAA,EAAM;AAAA,YAExD,QAAA,EAAA,UAAA,CAAW,MAAM,WAAA,CAAY,KAAK,GAAG,MAAA,GAAS,WAAA,CAAY,OAAO,WAAW;AAAA,WAAA;AAAA,UANxE,WAAA,CAAY;AAAA,SAQpB,CAAA,EACH,CAAA;AAAA,QACC,OAAA,oBAAW,GAAA,CAAC,KAAA,EAAA,EAAI,GAAA,EAAK,MAAA,EAAQ,0BAAuB,EAAA,EAAG,aAAA,EAAY,MAAA,EAAO,SAAA,EAAU,aAAA,EAAc;AAAA;AAAA;AAAA,GACrG;AAMF,EAAA,IAAI,CAAC,OAAO,OAAO,IAAA;AACnB,EAAA,uBACE,IAAA,CAAC,SAAI,eAAA,EAAc,EAAA,EAAG,WAAW,EAAA,CAAG,uBAAA,EAAyB,SAAS,CAAA,EACnE,QAAA,EAAA;AAAA,IAAA,IAAA;AAAA,IACA,QAAA,CAAS,MAAA,GAAS,CAAA,oBAAK,GAAA,CAAC,cAAY,GAAG,KAAA,CAAM,KAAA,EAAO,IAAA,EAAM,MAAM,KAAA,KAAU,iBAAA,GAAoB,WAAA,GAAc,KAAA,CAAM,MAAM,IAAA,EAAM;AAAA,GAAA,EACjI,CAAA;AAEJ","file":"virtualized.js","sourcesContent":["import { useCallback, useEffect, useRef, useState, type ReactNode, type Ref } from \"react\";\nimport { useVirtualizer, type VirtualItem } from \"@tanstack/react-virtual\";\nimport { cn, FOCUS_INSET } from \"../lib/utils\";\nimport { useLatest } from \"../lib/use-latest\";\nimport { Pagination } from \"./Pagination\";\nimport { pagerPart, useWholeListCheck, type Pager } from \"../lib/use-pagination\";\nimport { useScrollsInside } from \"../lib/surface-context\";\nimport { useKeyboardScroll } from \"../lib/use-keyboard-scroll\";\n\nexport interface VirtualizedListProps<T> {\n items: T[];\n estimateSize: (index: number) => number;\n /** An item, with its index in `items` (the whole list, when the list is paged). */\n renderItem: (item: T, index: number, virtualItem: VirtualItem) => ReactNode;\n overscan?: number;\n height?: number | string;\n className?: string;\n ref?: Ref<HTMLDivElement>;\n /**\n * The list's pager (Sprint 25.13 A49): paging and drawing only the rows on screen combine. Pass every item and\n * `usePagination(items)`'s pager: the list mounts only the rows on screen of the pager's part, and beneath it draws the\n * control in the project's style. A new page starts at its top; with infinite scroll, scrolling inside the list loads\n * more. For a long page (logs, feeds) the user asked to page differently, pass the size to the hook\n * (`usePagination(rows, { pageSize: 100 })`) with its override note.\n */\n pager?: Pager<unknown>;\n /**\n * The list's name, for the Tab stop it becomes while it scrolls with nothing inside to Tab to (Sprint 25.29 item 10):\n * what it lists (\"Activity log\"). Default \"List\".\n */\n \"aria-label\"?: string;\n}\n\nexport function VirtualizedList<T>({\n items: allItems,\n estimateSize,\n renderItem,\n overscan = 5,\n height = 400,\n className,\n ref,\n pager,\n \"aria-label\": ariaLabel = \"List\",\n}: VirtualizedListProps<T>) {\n // Sprint 25.14 A44: a list with no frame of its own: a SplitView's list pane keeps its hairline around it\n useScrollsInside();\n const parentRef = useRef<HTMLDivElement>(null);\n const endRef = useRef<HTMLDivElement>(null);\n // what the keyboard reaches in a row comes into view whole, with its outline (Sprint 30, P3: useKeyboardScroll reveals it)\n const [scroller, setScroller] = useState<HTMLDivElement | null>(null);\n // a list with nothing inside to Tab to, scrolling, is a Tab stop of its own, named (Sprint 25.29 item 10)\n useKeyboardScroll(scroller, ariaLabel);\n const setList = useCallback((node: HTMLDivElement | null) => {\n (parentRef as React.MutableRefObject<HTMLDivElement | null>).current = node;\n setScroller(node);\n if (typeof ref === \"function\") ref(node);\n else if (ref) (ref as React.MutableRefObject<HTMLDivElement | null>).current = node;\n }, [ref]);\n useWholeListCheck(\"VirtualizedList\", allItems.length, pager);\n const items = pagerPart(allItems, pager);\n const offset = pager ? Math.max(0, pager.range[0] - 1) : 0;\n\n // eslint-disable-next-line react-hooks/incompatible-library -- TanStack Virtual is not compiler-compatible (informational; the project does not use the React Compiler)\n const virtualizer = useVirtualizer({\n count: items.length,\n getScrollElement: () => parentRef.current,\n estimateSize: (index) => estimateSize(offset + index),\n // a row is known by its place in the whole list, so page 2's third row isn't page 1's (the review of 30 September:\n // an entry opened on page 1 showed opened on page 2, and page 1's heights carried over)\n getItemKey: (index) => offset + index,\n overscan,\n });\n\n // A49: a new page starts at its top, and so does a growing list that starts again at its first page (a new order, a\n // search): left scrolled to its end, it loaded the next page at once (the review of 30 September)\n const page = pager ? pager.page : null;\n const pageBefore = useRef(page);\n const numbered = pager?.style === \"pagination\";\n useEffect(() => {\n const before = pageBefore.current;\n pageBefore.current = page;\n if (page === null || !parentRef.current) return;\n if (numbered ? page !== before : before !== null && page < before) parentRef.current.scrollTop = 0;\n }, [page, numbered]);\n\n // A49: with infinite scroll, the trigger sits at the end of the list's own content and watches the list's scroller\n // (it watched the viewport, so scrolling inside the list never loaded more); it loads once per growth of the list\n const loadMore = useLatest(pager?.loadMore);\n const growing = pager?.style === \"infinite-scroll\" && pager.hasMore;\n const loadedAt = useRef(-1);\n useEffect(() => {\n const root = parentRef.current;\n const end = endRef.current;\n // the list's length changed since the last load (it grew, or started again at page 1): it may load again (the\n // review of 30 September: after a reset to the first page it never loaded again)\n if (loadedAt.current !== items.length) loadedAt.current = -1;\n if (!growing || !root || !end || typeof IntersectionObserver === \"undefined\") return;\n const observer = new IntersectionObserver((entries) => {\n if (!entries.some((e) => e.isIntersecting) || loadedAt.current === items.length) return;\n loadedAt.current = items.length;\n loadMore.current?.();\n }, { root, rootMargin: \"200px 0px\" });\n observer.observe(end);\n return () => observer.disconnect();\n }, [growing, items.length, loadMore]);\n\n const list = (\n <div\n ref={setList}\n data-ds-list={pager ? \"\" : undefined}\n className={cn(\n \"overflow-auto scroll-p-[4px]\",\n FOCUS_INSET,\n \"rounded-[var(--ds-radius-md)]\",\n \"border-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]\",\n \"bg-[var(--ds-bg-surface)]\",\n !pager && className,\n )}\n style={{ height }}\n >\n <div className=\"relative w-full\" style={{ height: virtualizer.getTotalSize() }}>\n {virtualizer.getVirtualItems().map((virtualItem) => (\n <div\n key={virtualItem.key}\n data-index={virtualItem.index}\n ref={virtualizer.measureElement}\n className=\"absolute top-0 left-0 w-full\"\n style={{ transform: `translateY(${virtualItem.start}px)` }}\n >\n {renderItem(items[virtualItem.index], offset + virtualItem.index, virtualItem)}\n </div>\n ))}\n </div>\n {growing && <div ref={endRef} data-load-more-trigger=\"\" aria-hidden=\"true\" className=\"h-px w-full\" />}\n </div>\n );\n // A3: the pager's control is the list's next sibling, as <Pagination {...pager.props} /> would be. With infinite\n // scroll it is the Load more button alone, for keyboard users: the list's own trigger loads as it scrolls (A49). The\n // two are one element, which takes the className, so in a grid or a flex row they stay one item, and the list keeps\n // its element while its items come and go (the review of 30 September).\n if (!pager) return list;\n return (\n <div data-gs-paged=\"\" className={cn(\"flex flex-col min-w-0\", className)}>\n {list}\n {allItems.length > 0 && <Pagination {...pager.props} mode={pager.style === \"infinite-scroll\" ? \"load-more\" : pager.props.mode} />}\n </div>\n );\n}\n"]}
1
+ {"version":3,"sources":["../../../showcase/src/components/VirtualizedList.tsx"],"names":[],"mappings":"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","file":"virtualized.js","sourcesContent":["import { useCallback, useEffect, useRef, useState, type ReactNode, type Ref } from \"react\";\nimport { useVirtualizer, type VirtualItem } from \"@tanstack/react-virtual\";\nimport { cn, FOCUS_INSET } from \"../lib/utils\";\nimport { useLatest } from \"../lib/use-latest\";\nimport { Pagination } from \"./Pagination\";\nimport { pagerPart, useWholeListCheck, type Pager } from \"../lib/use-pagination\";\nimport { useRunsEdgeToEdge, useScrollsInside } from \"../lib/surface-context\";\nimport { useKeyboardScroll } from \"../lib/use-keyboard-scroll\";\n\nexport interface VirtualizedListProps<T> {\n items: T[];\n estimateSize: (index: number) => number;\n /** An item, with its index in `items` (the whole list, when the list is paged). */\n renderItem: (item: T, index: number, virtualItem: VirtualItem) => ReactNode;\n overscan?: number;\n height?: number | string;\n className?: string;\n ref?: Ref<HTMLDivElement>;\n /**\n * The list's pager (Sprint 25.13 A49): paging and drawing only the rows on screen combine. Pass every item and\n * `usePagination(items)`'s pager: the list mounts only the rows on screen of the pager's part, and beneath it draws the\n * control in the project's style. A new page starts at its top; with infinite scroll, scrolling inside the list loads\n * more. For a long page (logs, feeds) the user asked to page differently, pass the size to the hook\n * (`usePagination(rows, { pageSize: 100 })`) with its override note.\n */\n pager?: Pager<unknown>;\n /**\n * The list's name, for the Tab stop it becomes while it scrolls with nothing inside to Tab to (Sprint 25.29 item 10):\n * what it lists (\"Activity log\"). Default \"List\".\n */\n \"aria-label\"?: string;\n}\n\nexport function VirtualizedList<T>({\n items: allItems,\n estimateSize,\n renderItem,\n overscan = 5,\n height = 400,\n className,\n ref,\n pager,\n \"aria-label\": ariaLabel = \"List\",\n}: VirtualizedListProps<T>) {\n // Sprint 25.14 A44: a list with no frame of its own: a SplitView's list pane keeps its hairline around it\n useScrollsInside();\n // Sprint 25.33 C1: its rows are full-width bands, so the list pane holding it is flush\n useRunsEdgeToEdge();\n const parentRef = useRef<HTMLDivElement>(null);\n const endRef = useRef<HTMLDivElement>(null);\n // what the keyboard reaches in a row comes into view whole, with its outline (Sprint 30, P3: useKeyboardScroll reveals it)\n const [scroller, setScroller] = useState<HTMLDivElement | null>(null);\n // a list with nothing inside to Tab to, scrolling, is a Tab stop of its own, named (Sprint 25.29 item 10)\n useKeyboardScroll(scroller, ariaLabel);\n const setList = useCallback((node: HTMLDivElement | null) => {\n (parentRef as React.MutableRefObject<HTMLDivElement | null>).current = node;\n setScroller(node);\n if (typeof ref === \"function\") ref(node);\n else if (ref) (ref as React.MutableRefObject<HTMLDivElement | null>).current = node;\n }, [ref]);\n useWholeListCheck(\"VirtualizedList\", allItems.length, pager);\n const items = pagerPart(allItems, pager);\n const offset = pager ? Math.max(0, pager.range[0] - 1) : 0;\n\n // eslint-disable-next-line react-hooks/incompatible-library -- TanStack Virtual is not compiler-compatible (informational; the project does not use the React Compiler)\n const virtualizer = useVirtualizer({\n count: items.length,\n getScrollElement: () => parentRef.current,\n estimateSize: (index) => estimateSize(offset + index),\n // a row is known by its place in the whole list, so page 2's third row isn't page 1's (the review of 30 September:\n // an entry opened on page 1 showed opened on page 2, and page 1's heights carried over)\n getItemKey: (index) => offset + index,\n overscan,\n });\n\n // A49: a new page starts at its top, and so does a growing list that starts again at its first page (a new order, a\n // search): left scrolled to its end, it loaded the next page at once (the review of 30 September)\n const page = pager ? pager.page : null;\n const pageBefore = useRef(page);\n const numbered = pager?.style === \"pagination\";\n useEffect(() => {\n const before = pageBefore.current;\n pageBefore.current = page;\n if (page === null || !parentRef.current) return;\n if (numbered ? page !== before : before !== null && page < before) parentRef.current.scrollTop = 0;\n }, [page, numbered]);\n\n // A49: with infinite scroll, the trigger sits at the end of the list's own content and watches the list's scroller\n // (it watched the viewport, so scrolling inside the list never loaded more); it loads once per growth of the list\n const loadMore = useLatest(pager?.loadMore);\n const growing = pager?.style === \"infinite-scroll\" && pager.hasMore;\n const loadedAt = useRef(-1);\n useEffect(() => {\n const root = parentRef.current;\n const end = endRef.current;\n // the list's length changed since the last load (it grew, or started again at page 1): it may load again (the\n // review of 30 September: after a reset to the first page it never loaded again)\n if (loadedAt.current !== items.length) loadedAt.current = -1;\n if (!growing || !root || !end || typeof IntersectionObserver === \"undefined\") return;\n const observer = new IntersectionObserver((entries) => {\n if (!entries.some((e) => e.isIntersecting) || loadedAt.current === items.length) return;\n loadedAt.current = items.length;\n loadMore.current?.();\n }, { root, rootMargin: \"200px 0px\" });\n observer.observe(end);\n return () => observer.disconnect();\n }, [growing, items.length, loadMore]);\n\n const list = (\n <div\n ref={setList}\n data-ds-list={pager ? \"\" : undefined}\n className={cn(\n \"overflow-auto scroll-p-[4px]\",\n FOCUS_INSET,\n \"rounded-[var(--ds-radius-md)]\",\n \"border-[length:var(--ds-border-width)] border-[var(--ds-border-subtle)]\",\n \"bg-[var(--ds-bg-surface)]\",\n !pager && className,\n )}\n style={{ height }}\n >\n <div className=\"relative w-full\" style={{ height: virtualizer.getTotalSize() }}>\n {virtualizer.getVirtualItems().map((virtualItem) => (\n <div\n key={virtualItem.key}\n data-index={virtualItem.index}\n ref={virtualizer.measureElement}\n className=\"absolute top-0 left-0 w-full\"\n style={{ transform: `translateY(${virtualItem.start}px)` }}\n >\n {renderItem(items[virtualItem.index], offset + virtualItem.index, virtualItem)}\n </div>\n ))}\n </div>\n {growing && <div ref={endRef} data-load-more-trigger=\"\" aria-hidden=\"true\" className=\"h-px w-full\" />}\n </div>\n );\n // A3: the pager's control is the list's next sibling, as <Pagination {...pager.props} /> would be. With infinite\n // scroll it is the Load more button alone, for keyboard users: the list's own trigger loads as it scrolls (A49). The\n // two are one element, which takes the className, so in a grid or a flex row they stay one item, and the list keeps\n // its element while its items come and go (the review of 30 September).\n if (!pager) return list;\n return (\n <div data-gs-paged=\"\" className={cn(\"flex flex-col min-w-0\", className)}>\n {list}\n {allItems.length > 0 && <Pagination {...pager.props} mode={pager.style === \"infinite-scroll\" ? \"load-more\" : pager.props.mode} />}\n </div>\n );\n}\n"]}
@@ -151,7 +151,7 @@
151
151
  --ds-nav-h-track-border: var(--ds-border-width);
152
152
  --ds-nav-h-track-gap: 0px;
153
153
  --ds-nav-h-badge-bg: var(--ds-accent-primary);
154
- --ds-nav-v-active-bg: var(--ds-tab-active-bg);
154
+ --ds-nav-v-active-bg: var(--ds-accent-subtle);
155
155
  --ds-nav-v-active-text: var(--ds-accent-ink);
156
156
  --ds-nav-v-active-rail: var(--ds-border-width-thick);
157
157
  --ds-nav-v-item-radius: var(--ds-radius-sm);
@@ -159,7 +159,7 @@
159
159
  --ds-nav-v-track-padding: var(--ds-space-1);
160
160
  --ds-nav-v-active-muted: var(--ds-text-secondary);
161
161
  --ds-nav-v-badge-bg: var(--ds-accent-primary);
162
- --ds-tree-active-bg: var(--ds-tab-active-bg);
162
+ --ds-tree-active-bg: var(--ds-accent-subtle);
163
163
  --ds-tree-active-text: var(--ds-accent-ink);
164
164
  --ds-tree-active-muted: var(--ds-text-muted);
165
165
  --ds-tree-active-rail: var(--ds-border-width-thick);
@@ -184,7 +184,7 @@
184
184
  --ds-nav-h-track-border: var(--ds-border-width);
185
185
  --ds-nav-h-track-gap: 0px;
186
186
  --ds-nav-h-badge-bg: var(--ds-accent-primary);
187
- --ds-nav-v-active-bg: var(--ds-tab-active-bg);
187
+ --ds-nav-v-active-bg: var(--ds-accent-subtle);
188
188
  --ds-nav-v-active-text: var(--ds-accent-ink);
189
189
  --ds-nav-v-active-rail: var(--ds-border-width-thick);
190
190
  --ds-nav-v-item-radius: var(--ds-radius-sm);
@@ -192,7 +192,7 @@
192
192
  --ds-nav-v-track-padding: var(--ds-space-1);
193
193
  --ds-nav-v-active-muted: var(--ds-text-secondary);
194
194
  --ds-nav-v-badge-bg: var(--ds-accent-primary);
195
- --ds-tree-active-bg: var(--ds-tab-active-bg);
195
+ --ds-tree-active-bg: var(--ds-accent-subtle);
196
196
  --ds-tree-active-text: var(--ds-accent-ink);
197
197
  --ds-tree-active-muted: var(--ds-text-muted);
198
198
  --ds-tree-active-rail: var(--ds-border-width-thick);
@@ -469,7 +469,7 @@
469
469
  --ds-nav-h-track-border: var(--ds-border-width);
470
470
  --ds-nav-h-track-gap: 0px;
471
471
  --ds-nav-h-badge-bg: var(--ds-accent-primary);
472
- --ds-nav-v-active-bg: var(--ds-tab-active-bg);
472
+ --ds-nav-v-active-bg: var(--ds-accent-subtle);
473
473
  --ds-nav-v-active-text: var(--ds-accent-ink);
474
474
  --ds-nav-v-active-rail: var(--ds-border-width-thick);
475
475
  --ds-nav-v-item-radius: var(--ds-radius-sm);
@@ -477,7 +477,7 @@
477
477
  --ds-nav-v-track-padding: var(--ds-space-1);
478
478
  --ds-nav-v-active-muted: var(--ds-text-secondary);
479
479
  --ds-nav-v-badge-bg: var(--ds-accent-primary);
480
- --ds-tree-active-bg: var(--ds-tab-active-bg);
480
+ --ds-tree-active-bg: var(--ds-accent-subtle);
481
481
  --ds-tree-active-text: var(--ds-accent-ink);
482
482
  --ds-tree-active-muted: var(--ds-text-muted);
483
483
  --ds-tree-active-rail: var(--ds-border-width-thick);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gridsmith-ui",
3
- "version": "0.31.1",
3
+ "version": "0.33.0",
4
4
  "description": "Gridsmith component library — React components and charts with design tokens and a CSS custom property theming engine",
5
5
  "type": "module",
6
6
  "sideEffects": [
@@ -47,6 +47,11 @@
47
47
  "import": "./dist/kanban.js",
48
48
  "default": "./dist/kanban.js"
49
49
  },
50
+ "./dashboard": {
51
+ "types": "./dist/dashboard.d.ts",
52
+ "import": "./dist/dashboard.js",
53
+ "default": "./dist/dashboard.js"
54
+ },
50
55
  "./data-grid": {
51
56
  "types": "./dist/data-grid.d.ts",
52
57
  "import": "./dist/data-grid.js",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../showcase/src/components/Skeleton.tsx","../../../showcase/src/components/SegmentedControl.tsx","../../../showcase/src/lib/title-text.ts"],"names":["jsx","jsxs"],"mappings":"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","file":"chunk-BHTAVJIG.js","sourcesContent":["import { cn } from \"../lib/utils\";\n\ninterface SkeletonProps {\n variant?: \"text\" | \"avatar\" | \"card\" | \"table-row\" | \"custom\";\n lines?: number;\n width?: string;\n height?: string;\n className?: string;\n}\n\nfunction Bone({ className, style, \"data-gs\": marker }: { className?: string; style?: React.CSSProperties; \"data-gs\"?: string }) {\n return (\n <div\n data-gs={marker}\n className={cn(\"rounded-[var(--ds-radius-sm)] bg-[var(--ds-bg-emphasis)] animate-pulse\", className)}\n style={style}\n />\n );\n}\n\nexport function Skeleton({ variant = \"text\", lines = 3, width, height, className }: SkeletonProps) {\n if (variant === \"custom\") {\n return <Bone data-gs=\"skeleton\" className={className} style={{ width, height }} />;\n }\n\n if (variant === \"avatar\") {\n return <Bone data-gs=\"skeleton\" className={cn(\"h-10 w-10 rounded-[var(--ds-radius-full)]\", className)} />;\n }\n\n if (variant === \"card\") {\n return (\n <div data-gs=\"skeleton\" className={cn(\"space-y-ds-3\", className)}>\n <Bone className=\"h-32 w-full rounded-[var(--ds-radius-md)]\" />\n <Bone className=\"h-4 w-3/4\" />\n <Bone className=\"h-4 w-1/2\" />\n </div>\n );\n }\n\n if (variant === \"table-row\") {\n return (\n <div data-gs=\"skeleton\" className={cn(\"flex gap-ds-4\", className)}>\n <Bone className=\"h-4 flex-[3]\" />\n <Bone className=\"h-4 flex-[2]\" />\n <Bone className=\"h-4 flex-1\" />\n <Bone className=\"h-4 flex-1\" />\n </div>\n );\n }\n\n // text variant\n const widths = [\"w-3/4\", \"w-1/2\", \"w-5/6\", \"w-2/3\", \"w-4/5\"];\n return (\n <div data-gs=\"skeleton\" className={cn(\"space-y-ds-3\", className)}>\n {Array.from({ length: lines }, (_, i) => (\n <Bone key={i} className={cn(\"h-4\", widths[i % widths.length])} />\n ))}\n </div>\n );\n}\n","import { useRef, useState, useLayoutEffect, useEffect, useCallback, useContext } from \"react\";\nimport { cn, FOCUS_RING } from \"../lib/utils\";\nimport { rovingTabStop } from \"../lib/focus\";\nimport { Dropdown } from \"./Dropdown\";\nimport { ControlSizeContext } from \"./Button\";\nimport { useFieldControl, type FieldControlWiring } from \"./Field\";\nimport { useReportedChange } from \"../lib/form-context\";\n\ninterface SegmentedControlOption {\n value: string;\n label: string;\n disabled?: boolean;\n}\n\ntype SegmentedControlSize = \"xs\" | \"sm\" | \"md\" | \"lg\";\n\ninterface SegmentedControlProps extends FieldControlWiring {\n value: string;\n onChange: (value: string) => void;\n options: SegmentedControlOption[];\n /** xs is for dense tool chrome (toolbars, inspectors): 24px tall. */\n size?: SegmentedControlSize;\n variant?: \"default\" | \"ghost\";\n /** Stretch to the container and share the width equally between options. */\n fullWidth?: boolean;\n /**\n * Called with an option's value while the pointer is over it and with null\n * when it leaves, so a picker can preview a choice before committing it.\n */\n onHoverChange?: (value: string | null) => void;\n className?: string;\n /**\n * Accessible name for the radiogroup, for when there is no visible label (e.g. in a toolbar). A control with a visible\n * label sits in a Field instead, which names it and stacks the label above: `<Field label=\"Status\"><SegmentedControl …/></Field>`.\n */\n \"aria-label\"?: string;\n /**\n * Above this many options the control renders a Dropdown with the same value, onChange and options — a row of\n * eleven segments runs off the page (bench round 3). Default 6; raise it only where the row is known to fit. A row\n * that does not fit its container (a four-option range on a phone) renders the Dropdown too, and comes back as a\n * row when the container widens (round 10).\n */\n maxOptions?: number;\n}\n\n/** The options as one string, so the fit re-measures when the labels change. */\nfunction optionsKeyForFit(options: SegmentedControlOption[]): string { return options.map((o) => `${o.value}:${o.label}`).join(\"|\"); }\n\n// Option padding is part of the size (tailwind-merge cannot resolve two ds-\n// spacing classes against each other, so it is not appended as an override).\nconst sizes: Record<SegmentedControlSize, string> = {\n xs: \"text-xs px-ds-2\",\n sm: \"text-xs px-ds-3\",\n md: \"text-sm px-ds-3\",\n lg: \"text-base px-ds-3\",\n};\n\n// xs uses a 2px inset so the whole control stays 24px tall.\nconst containerPadding: Record<SegmentedControlSize, string> = {\n xs: \"p-[2px] gap-[2px]\",\n sm: \"p-ds-1 gap-ds-0.5\",\n md: \"p-ds-1 gap-ds-0.5\",\n lg: \"p-ds-1 gap-ds-0.5\",\n};\n\nconst indicatorInset: Record<SegmentedControlSize, string> = {\n xs: \"2px\",\n sm: \"4px\",\n md: \"4px\",\n lg: \"4px\",\n};\n\n/**\n * The segments' corner: the frame's (a field's, --ds-radius-md) less the gap between the two edges, the selected\n * segment's inset and the frame's border when it draws one, so the curves sit evenly inside each other; never sharper\n * than the theme's small radius, so on a theme of small corners the segments stay slightly rounded, not square (a\n * square theme stays square).\n */\nfunction segmentRadius(inset: string, bordered: boolean): string {\n return `max(var(--ds-radius-sm), calc(var(--ds-radius-md) - ${inset}${bordered ? \" - var(--ds-border-width)\" : \"\"}))`;\n}\n\nexport function SegmentedControl({\n value,\n onChange: onChangeProp,\n options,\n size: sizeProp,\n variant = \"default\",\n fullWidth = false,\n onHoverChange,\n className,\n \"aria-label\": ariaLabel,\n maxOptions = 6,\n ...wiring\n}: SegmentedControlProps) {\n // Sprint 25.12 A15: a new value the user gives this control marks the form's overlay; the page's onChange runs as given\n const onChange = useReportedChange(onChangeProp, value);\n const contextSize = useContext(ControlSizeContext);\n // Inside a Field it takes the Field's label (round 27, R27-10): a radio group is named by aria-labelledby, since\n // <label for> cannot name it, and the Field stacks its label above. It takes the Field's id, hint, error and\n // required state too (round 27 review: without them the summary had no link and focus skipped it); a radio group\n // carries aria-invalid and aria-required itself. As a Dropdown, the Dropdown reads the same Field. The name follows the\n // one rule (useFieldControl; the final review of Sprint 25.31, 2): the Field's label on screen leads it, where a\n // page's aria-label won before (\"Calendar view\" over \"View\"), and a page's name beside it is left out, with a warning\n const control = useFieldControl({ ...wiring, \"aria-label\": ariaLabel }, { component: \"SegmentedControl\" });\n // as a Dropdown, only the names the rule left: the page gave them to this control, not to a Dropdown, which would warn\n const { \"aria-labelledby\": _pageLabelledBy, ...dropdownWiring } = wiring;\n const size: SegmentedControlSize = sizeProp ?? (contextSize as SegmentedControlSize | null) ?? \"md\";\n const containerRef = useRef<HTMLDivElement>(null);\n const [indicator, setIndicator] = useState({ left: 0, width: 0 });\n\n // Round 10 (the owner's review of the analytics overview on a phone): a row that does not fit its container renders\n // the Dropdown too, not only a row of too many options. The row's natural width is remembered from the last time\n // it was laid out; the container (the filters row, the toolbar) is observed in both states, so the row comes back\n // when the container widens. 8 px of hysteresis keeps a row sitting on the edge from flapping.\n const parentRef = useRef<HTMLElement | null>(null);\n const neededRef = useRef(0);\n const [overflows, setOverflows] = useState(false);\n const measureFit = useCallback(() => {\n const row = containerRef.current;\n if (row) {\n parentRef.current = row.parentElement;\n if (row.scrollWidth > 0) neededRef.current = row.scrollWidth;\n }\n const parent = parentRef.current;\n const needed = neededRef.current;\n if (!parent || !needed) return;\n const style = getComputedStyle(parent);\n const available = parent.clientWidth - (parseFloat(style.paddingLeft) || 0) - (parseFloat(style.paddingRight) || 0);\n if (available <= 0) return;\n setOverflows((prev) => (prev ? needed > available - 8 : needed > available));\n }, []);\n useLayoutEffect(() => {\n measureFit();\n const parent = parentRef.current;\n if (!parent || typeof ResizeObserver === \"undefined\") return;\n const observer = new ResizeObserver(measureFit);\n observer.observe(parent);\n return () => observer.disconnect();\n }, [measureFit, optionsKeyForFit(options)]); // eslint-disable-line react-hooks/exhaustive-deps\n const collapsed = options.length > maxOptions || overflows;\n\n const updateIndicator = useCallback(() => {\n if (!containerRef.current) return;\n const active = containerRef.current.querySelector<HTMLElement>(\"[data-active='true']\");\n if (active) {\n setIndicator({\n left: active.offsetLeft,\n width: active.offsetWidth,\n });\n }\n }, []);\n\n // The option set can change under the control (a configurator hiding an\n // option), which moves the active segment without changing `value`: re-measure\n // on that too, and watch every segment, not just the container, so a label\n // that changes width also moves the thumb.\n const optionsKey = options.map((o) => `${o.value}:${o.label}`).join(\"|\");\n useLayoutEffect(() => {\n updateIndicator();\n }, [value, optionsKey, updateIndicator]);\n\n // The web font can land after the first measurement and widen the segments (round 5: the thumb was caught\n // mid-slide on every load). Re-measure when the fonts are ready, and animate only after the user's first change:\n // the initial position and the font re-measure snap into place.\n useEffect(() => {\n const fonts = (document as Document & { fonts?: { ready?: Promise<unknown> } }).fonts;\n if (!fonts?.ready) return;\n let live = true;\n fonts.ready.then(() => { if (live) updateIndicator(); });\n return () => { live = false; };\n }, [updateIndicator]);\n const [animate, setAnimate] = useState(false);\n const select = useCallback((v: string) => { setAnimate(true); onChange(v); }, [onChange]);\n\n useLayoutEffect(() => {\n if (collapsed || !containerRef.current || typeof ResizeObserver === \"undefined\") return;\n const observer = new ResizeObserver(updateIndicator);\n observer.observe(containerRef.current);\n containerRef.current.querySelectorAll<HTMLElement>(\"button\").forEach((b) => observer.observe(b));\n return () => observer.disconnect();\n }, [updateIndicator, optionsKey, collapsed]);\n\n const containerHeight: Record<SegmentedControlSize, string> = {\n xs: \"h-ds-6\",\n sm: \"h-[var(--ds-control-height-sm)]\",\n md: \"h-[var(--ds-control-height-md)]\",\n lg: \"h-[var(--ds-control-height-lg)]\",\n };\n\n // Radio-group keyboard pattern: one tab stop (the checked option, or the\n // first enabled one when nothing is checked), arrows move selection between\n // enabled options, Home/End jump to the ends.\n const tabStop = rovingTabStop(options, (o) => o.value === value, (o) => !!o.disabled);\n const handleKeyDown = (e: React.KeyboardEvent<HTMLDivElement>) => {\n const enabled = options.filter((o) => !o.disabled);\n if (enabled.length === 0) return;\n const idx = Math.max(0, enabled.findIndex((o) => o.value === value));\n let next: number | null = null;\n if (e.key === \"ArrowRight\" || e.key === \"ArrowDown\") next = (idx + 1) % enabled.length;\n else if (e.key === \"ArrowLeft\" || e.key === \"ArrowUp\") next = (idx - 1 + enabled.length) % enabled.length;\n else if (e.key === \"Home\") next = 0;\n else if (e.key === \"End\") next = enabled.length - 1;\n if (next === null) return;\n e.preventDefault();\n select(enabled[next].value);\n const buttons = containerRef.current?.querySelectorAll<HTMLButtonElement>('[role=\"radio\"]');\n buttons?.forEach((b) => { if (b.dataset.value === enabled[next!].value) b.focus(); });\n };\n\n if (collapsed) {\n return (\n <Dropdown\n {...dropdownWiring}\n aria-label={control.shown ? undefined : control.label}\n aria-labelledby={control.shown ? undefined : wiring[\"aria-labelledby\"]}\n size={size === \"md\" || size === \"lg\" ? \"md\" : \"sm\"}\n options={options.filter((o) => !o.disabled).map((o) => ({ id: o.value, label: o.label }))}\n value={value}\n onChange={(v) => onChange(v as string)}\n className={cn(\"inline-block\", fullWidth ? \"w-full\" : \"w-auto\", className)}\n />\n );\n }\n\n return (\n <div\n ref={containerRef}\n role=\"radiogroup\"\n id={control.id}\n aria-label={control.labelId ? undefined : control.label}\n aria-labelledby={control.labelId}\n aria-describedby={control.describedBy}\n aria-invalid={control.invalid || undefined}\n aria-required={control.required || undefined}\n onKeyDown={handleKeyDown}\n className={cn(\n \"relative items-center\",\n // Sprint 25.14 A21: in a Field's column a track that isn't full width keeps its own width; w-fit, not self-start,\n // so a row or a centred column still aligns it (the review of 30 September)\n fullWidth ? \"flex w-full\" : \"inline-flex w-fit\",\n containerPadding[size],\n containerHeight[size],\n \"bg-[var(--ds-bg-muted)]\",\n // The owner's review of 1 October 2026: the frame's corner is a field's, so the two change together with the\n // theme's radius; the segments inside sit concentric with it (SEGMENT_RADIUS)\n \"rounded-[var(--ds-radius-md)]\",\n // The frame's edge is a button's: each segment is a labelled button, so the frame isn't what identifies the\n // control, and WCAG 1.4.11 asks no 3:1 of it\n variant === \"default\" && \"border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]\",\n control.invalid && \"border-[length:var(--ds-border-width)] border-[var(--ds-status-error)]\",\n className,\n )}\n style={{ \"--ds-segment-radius\": segmentRadius(indicatorInset[size], variant === \"default\" || !!control.invalid) } as React.CSSProperties}\n >\n <div\n className={cn(\n \"absolute rounded-[var(--ds-segment-radius)]\",\n \"bg-[var(--ds-bg-surface)]\",\n // The selected segment is a kind of button, so it takes a secondary button's surface and edge, as the frame\n // takes a button's edge (the owner, 3 October 2026: the 3:1 strong edge it kept from Sprint 25.14 drew the\n // darker outline 25.19 took off the fields). It shows its state as the raised surface on the muted track, with\n // the default variant's shadow and its label's stronger colour. While its segment has keyboard focus the shadow\n // steps aside, as a button's does, so the segment's outline sits clear (Sprint 30, D7)\n \"border-[length:var(--ds-border-width)] border-[var(--ds-border-default)]\",\n variant === \"default\" && \"shadow-[var(--ds-shadow-sm)]\",\n \"[[role=radiogroup]:has([aria-checked=true]:focus-visible)_&]:shadow-none\",\n \"transition-all motion-reduce:transition-none\",\n )}\n data-animate={animate ? \"true\" : \"false\"}\n style={{\n left: indicator.left,\n width: indicator.width,\n top: indicatorInset[size],\n bottom: indicatorInset[size],\n transitionDuration: animate ? \"var(--ds-transition-speed)\" : \"0ms\",\n transitionTimingFunction: \"var(--ds-transition-ease)\",\n }}\n />\n {options.map((opt, i) => (\n <button\n key={opt.value}\n type=\"button\"\n role=\"radio\"\n aria-checked={opt.value === value}\n data-active={opt.value === value}\n data-value={opt.value}\n tabIndex={i === tabStop ? 0 : -1}\n disabled={opt.disabled}\n onClick={() => !opt.disabled && select(opt.value)}\n onMouseEnter={onHoverChange && !opt.disabled ? () => onHoverChange(opt.value) : undefined}\n onMouseLeave={onHoverChange ? () => onHoverChange(null) : undefined}\n className={cn(\n \"relative z-10 inline-flex items-center justify-center self-stretch font-medium whitespace-nowrap\",\n fullWidth && \"flex-1\",\n \"rounded-[var(--ds-segment-radius)]\",\n \"transition-colors cursor-pointer\",\n FOCUS_RING,\n sizes[size],\n opt.value === value\n ? \"text-[var(--ds-text-primary)]\"\n : \"text-[var(--ds-text-muted)] hover:text-[var(--ds-text-secondary)]\",\n opt.disabled && \"opacity-disabled cursor-not-allowed\",\n )}\n >\n {opt.label}\n </button>\n ))}\n </div>\n );\n}\n","/**\n * A title's text classes: CardTitle's, which a titled Table's band and a ChartContainer's title share (Sprint 25.9, A1),\n * one definition so they cannot drift. A word or a name longer than its line breaks at the line's edge (round 27,\n * R27-16: run 70's \"primary-logo-navy.svg\" ran out of its card); break-word, not anywhere, so a title's words still\n * set its minimum and the actions beside it wrap under it before a word is broken (HEADER_LAYOUT.floor).\n */\nexport const TITLE_TEXT = \"font-[family-name:var(--ds-font-display)] text-lg font-[var(--ds-weight-heading)] text-[var(--ds-text-primary)] break-words\";\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../showcase/src/lib/use-fill-height.ts"],"names":[],"mappings":"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","file":"chunk-DBHVJ7Z3.js","sourcesContent":["import { createContext, useContext, useLayoutEffect, useState, type RefObject } from \"react\";\n\n/**\n * Sprint 25.16 A42 (P31): a page led by a chart, a board or a split view fills the height under its header, and its\n * content scrolls inside it; the page doesn't. SplitView (frame=\"page\"), Gantt and KanbanBoard use this one mechanism.\n *\n * The element takes the rest of the shell's <main> height (main's height less what sits above it and the padding below\n * it) when all of these hold:\n * - it leads the page: above it there is only the page's header, its chrome (notices, tabs, a toolbar, a search or\n * filters row, however tall) and short rows, so a board in a dashboard's last card keeps its own height;\n * - it is the page's last content: nothing after it, up to <main>, takes room, so nothing below it is cut off (the\n * shell's footer is not the page's: it sits below the fold, as it does under a SplitView frame=\"fill\");\n * - its height follows its content: neither its own class nor a box around it gives it one (a fixed height, a flex\n * column that already gives it the rest), and given more, the page grows;\n * - the rest is tall enough to use (a phone on its side keeps the natural height, and the page scrolls).\n * Outside the shell's <main> (AppShell marks it), or inside a view that fills (a Gantt in a SplitView's pane), it keeps\n * its own height. Returns the height in pixels, or null for the natural height.\n */\nexport function useFillHeight(ref: RefObject<HTMLElement | null>, enabled = true): number | null {\n const insideFill = useContext(FillContext);\n const active = enabled && !insideFill;\n const [height, setHeight] = useState<number | null>(null);\n useLayoutEffect(() => {\n const main = ref.current?.closest<HTMLElement>(\"main[data-shell-main]\");\n if (!active || !main || typeof ResizeObserver === \"undefined\") {\n setHeight(null);\n return;\n }\n let frame = 0;\n // the element is read on each measure: a view that changes its layout (stacked or side by side) changes its root\n const measure = () => {\n frame = 0;\n const el = ref.current;\n setHeight(el && main.contains(el) ? fillHeight(el, main) : null);\n };\n const schedule = () => {\n if (!frame) frame = requestAnimationFrame(measure);\n };\n measure();\n // main's height (the window, a phone's toolbar), and what sits above or after the element at the page's top level (a\n // header's actions wrapping, a banner or a note arriving): the boxes there now, and any that arrive\n const ro = new ResizeObserver(schedule);\n const watch = (box: Element) => {\n ro.observe(box);\n for (const child of Array.from(box.children)) ro.observe(child);\n };\n const page = main.querySelector<HTMLElement>(\"[data-shell-content]\");\n watch(main);\n if (page) watch(page);\n const mo = typeof MutationObserver === \"undefined\" ? null : new MutationObserver((records) => {\n for (const record of records) record.addedNodes.forEach((node) => { if (node instanceof Element) ro.observe(node); });\n schedule();\n });\n mo?.observe(main, { childList: true });\n if (page) mo?.observe(page, { childList: true });\n return () => {\n ro.disconnect();\n mo?.disconnect();\n if (frame) cancelAnimationFrame(frame);\n };\n }, [ref, active]);\n return active ? height : null;\n}\n\n/** Content inside a view that fills: it takes the view's height (h-full), never the page's. */\nexport const FillContext = createContext(false);\n\n/** Below this the rest of the screen is too short to scroll inside (about six rows of a chart). */\nconst MIN_FILL = 240;\n/** A row that may sit between the header and the view: tabs, a toolbar, a filters row (PageContainer's ROW_MAX). */\nconst ROW_MAX = 56;\n\nfunction fillHeight(el: HTMLElement, main: HTMLElement): number | null {\n // the page's own content: the shell's wrapper above its footer, else main\n const page = el.closest<HTMLElement>(\"[data-shell-content]\") ?? main;\n if (!leadsPage(el, page) || !isLastContent(el, page) || keepScroll(el, () => hasOwnHeight(el) || !followsContent(el, main))) return null;\n const mainBox = main.getBoundingClientRect();\n const top = el.getBoundingClientRect().top - mainBox.top - main.clientTop + main.scrollTop;\n const height = Math.floor(main.clientHeight - top - spaceBelow(el, main));\n return height >= MIN_FILL ? height : null;\n}\n\n/**\n * Runs a probe that changes the element's height for a moment, and puts back where everything inside it was scrolled:\n * at its natural height a region that scrolls inside it stops scrolling, and the browser drops its position (Sprint\n * 25.18: the week's hours opened at the working day, then the next measure sent them back to midnight).\n */\nfunction keepScroll<T>(el: HTMLElement, probe: () => T): T {\n const scrolled: Array<[HTMLElement, number, number]> = [];\n for (const node of [el, ...Array.from(el.querySelectorAll<HTMLElement>(\"*\"))]) {\n if (node.scrollTop || node.scrollLeft) scrolled.push([node, node.scrollTop, node.scrollLeft]);\n }\n try {\n return probe();\n } finally {\n for (const [node, top, left] of scrolled) {\n node.scrollTop = top;\n node.scrollLeft = left;\n }\n }\n}\n\n/** Whether a box takes room on the page: an overlay, a portal's anchor, a hidden or an empty node doesn't. */\nfunction takesRoom(box: Element): boolean {\n const style = getComputedStyle(box);\n if (style.display === \"none\" || style.position === \"absolute\" || style.position === \"fixed\") return false;\n const rect = box.getBoundingClientRect();\n return rect.height > 0 && rect.width > 0;\n}\n\n/**\n * The page's chrome, at any height: a notice (an Alert, a banner that wraps to two lines on a phone), tabs, a toolbar, a\n * search or filters row (the review of 1 October: a two-line banner turned the fill off). Content of the page's own (a\n * row of stats, cards, text) is not chrome.\n */\nconst CHROME = '[role=\"alert\"], [role=\"status\"], [role=\"tablist\"], [role=\"toolbar\"], [role=\"search\"]';\nconst isChrome = (box: Element) =>\n box.matches(CHROME) || (box.children.length > 0 && Array.from(box.children).every((child) => !takesRoom(child) || child.matches(CHROME)));\n\n/** Above the element, up to the page's root: only the page's header (its title), its chrome, and rows no taller than ROW_MAX. */\nfunction leadsPage(el: HTMLElement, page: HTMLElement): boolean {\n for (let node: HTMLElement | null = el; node && node !== page; node = node.parentElement) {\n for (let prev = node.previousElementSibling; prev; prev = prev.previousElementSibling) {\n if (!takesRoom(prev)) continue;\n if (prev.matches('[data-gs=\"page-header\"]') || prev.querySelector('[data-gs=\"page-header\"], h1')) continue;\n if (isChrome(prev)) continue;\n if (prev.getBoundingClientRect().height > ROW_MAX) return false;\n }\n }\n return true;\n}\n\n/** Nothing after the element, up to the page's root, takes room. */\nfunction isLastContent(el: HTMLElement, page: HTMLElement): boolean {\n for (let node: HTMLElement | null = el; node && node !== page; node = node.parentElement) {\n for (let next = node.nextElementSibling; next; next = next.nextElementSibling) {\n if (takesRoom(next)) return false;\n }\n }\n return true;\n}\n\n/**\n * The element's own CSS gives it a height that isn't its content's: a fixed height of its own (`h-[420px]`), or `h-full`\n * that resolves against a box which already has one. It keeps it. Read with the fill's own height set aside.\n */\nfunction hasOwnHeight(el: HTMLElement): boolean {\n const inline = el.style.height;\n el.style.height = \"\";\n const own = el.offsetHeight;\n el.style.height = \"auto\";\n const natural = el.offsetHeight;\n el.style.height = inline;\n return Math.abs(own - natural) > 1;\n}\n\n/**\n * Given more height, does the page grow? Probed once: the element is made a screen taller and main's top-level box\n * measured. A box of fixed height around it, or a flex column that already gives it its height, absorbs it.\n */\nfunction followsContent(el: HTMLElement, main: HTMLElement): boolean {\n let top: HTMLElement = el;\n while (top.parentElement && top.parentElement !== main) top = top.parentElement;\n const before = top.offsetHeight;\n const inline = el.style.height;\n el.style.height = `${el.offsetHeight + main.clientHeight}px`;\n const grows = top.offsetHeight > before;\n el.style.height = inline;\n return grows;\n}\n\n/** The padding, borders and margins under the element, up to main's own padding: they stay below it. */\nfunction spaceBelow(el: HTMLElement, main: HTMLElement): number {\n const px = (value: string) => parseFloat(value) || 0;\n let space = px(getComputedStyle(main).paddingBottom);\n for (let node: HTMLElement | null = el; node && node !== main; node = node.parentElement) {\n const style = getComputedStyle(node);\n space += px(style.marginBottom);\n if (node !== el) space += px(style.paddingBottom) + px(style.borderBottomWidth);\n }\n return space;\n}\n"]}