gridsmith-ui 0.22.0 → 0.24.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 (43) hide show
  1. package/dist/Breadcrumbs-CGS8GKl-.d.ts +33 -0
  2. package/dist/chat.js +1 -1
  3. package/dist/chunk-2PWGJ4PK.js +45 -0
  4. package/dist/chunk-2PWGJ4PK.js.map +1 -0
  5. package/dist/chunk-2YSEME33.js +118 -0
  6. package/dist/chunk-2YSEME33.js.map +1 -0
  7. package/dist/chunk-3BRQRKDO.js +72 -0
  8. package/dist/chunk-3BRQRKDO.js.map +1 -0
  9. package/dist/{chunk-2RV23OBK.js → chunk-6XAU44U4.js} +25 -10
  10. package/dist/chunk-6XAU44U4.js.map +1 -0
  11. package/dist/{chunk-RT7RYGC3.js → chunk-JXANCXK6.js} +4 -39
  12. package/dist/chunk-JXANCXK6.js.map +1 -0
  13. package/dist/{chunk-AAYCQXZQ.js → chunk-MV7JXWXK.js} +630 -411
  14. package/dist/chunk-MV7JXWXK.js.map +1 -0
  15. package/dist/{chunk-EYMGLK4M.js → chunk-SVP2IG4S.js} +2 -2
  16. package/dist/{chunk-EYMGLK4M.js.map → chunk-SVP2IG4S.js.map} +1 -1
  17. package/dist/chunk-WOVYHFUD.js +50 -0
  18. package/dist/chunk-WOVYHFUD.js.map +1 -0
  19. package/dist/chunk-XVB55M3N.js +42 -0
  20. package/dist/chunk-XVB55M3N.js.map +1 -0
  21. package/dist/data-grid.js +5 -3
  22. package/dist/data-grid.js.map +1 -1
  23. package/dist/editor.js +11 -3
  24. package/dist/editor.js.map +1 -1
  25. package/dist/index.d.ts +227 -45
  26. package/dist/index.js +843 -586
  27. package/dist/index.js.map +1 -1
  28. package/dist/kanban.js +23 -4
  29. package/dist/kanban.js.map +1 -1
  30. package/dist/resizable.d.ts +17 -2
  31. package/dist/resizable.js +102 -24
  32. package/dist/resizable.js.map +1 -1
  33. package/dist/roots.css +19 -4
  34. package/dist/tokens.css +20 -3
  35. package/dist/virtualized.js +5 -2
  36. package/dist/virtualized.js.map +1 -1
  37. package/dist-static/tokens.css +20 -3
  38. package/package.json +1 -1
  39. package/dist/chunk-2RV23OBK.js.map +0 -1
  40. package/dist/chunk-AAYCQXZQ.js.map +0 -1
  41. package/dist/chunk-RG4KUCRS.js +0 -9
  42. package/dist/chunk-RG4KUCRS.js.map +0 -1
  43. package/dist/chunk-RT7RYGC3.js.map +0 -1
@@ -0,0 +1,33 @@
1
+ import * as react from 'react';
2
+ import { IconName } from './icons.js';
3
+
4
+ interface BreadcrumbItem {
5
+ label: string;
6
+ href?: string;
7
+ onClick?: () => void;
8
+ icon?: IconName;
9
+ }
10
+ type BreadcrumbsVariant = "inline" | "contained" | "pill";
11
+ type BreadcrumbsSeparator = "slash" | "chevron" | "dot" | "arrow";
12
+ type BreadcrumbsSize = "sm" | "md";
13
+ interface BreadcrumbsProps {
14
+ items: BreadcrumbItem[];
15
+ separator?: BreadcrumbsSeparator;
16
+ variant?: BreadcrumbsVariant;
17
+ /** Visual treatment of individual breadcrumb items — independent of layout variant. */
18
+ itemVariant?: "default" | "pill";
19
+ size?: BreadcrumbsSize;
20
+ showIcons?: boolean;
21
+ maxItems?: number;
22
+ /** Full-bleed: contained background extends edge-to-edge, breaking out of parent padding. Text stays aligned with sibling content. */
23
+ bleed?: boolean;
24
+ /** Max-width for bleed content alignment. Defaults to --ds-bp-xl. */
25
+ contentMaxWidth?: string;
26
+ className?: string;
27
+ container?: Element | DocumentFragment;
28
+ /** The landmark's name. Default "Breadcrumb"; a second trail on the page names itself (SplitView's path: "Path"). */
29
+ "aria-label"?: string;
30
+ }
31
+ declare const Breadcrumbs: react.ForwardRefExoticComponent<BreadcrumbsProps & react.RefAttributes<HTMLElement>>;
32
+
33
+ export { type BreadcrumbItem as B, Breadcrumbs as a, type BreadcrumbsSeparator as b, type BreadcrumbsSize as c, type BreadcrumbsVariant as d };
package/dist/chat.js CHANGED
@@ -1,4 +1,4 @@
1
- import { Divider, CodeBlock, Link } from './chunk-EYMGLK4M.js';
1
+ import { Divider, CodeBlock, Link } from './chunk-SVP2IG4S.js';
2
2
  import './chunk-SBTBMAIU.js';
3
3
  import './chunk-CXMV7SXJ.js';
4
4
  import './chunk-7Q46JXSK.js';
@@ -0,0 +1,45 @@
1
+ import { createContext, createElement, useContext, useLayoutEffect } from 'react';
2
+
3
+ // ../../showcase/src/lib/page-actions.ts
4
+ var PageActionsCtx = createContext(null);
5
+ var CardSurfaceCtx = createContext(false);
6
+ var SplitPaneCtx = createContext(false);
7
+ var TableCardCtx = createContext(false);
8
+ var SplitListFramerCtx = createContext(null);
9
+ function useReportToList(kind) {
10
+ const report = useContext(SplitListFramerCtx);
11
+ useLayoutEffect(() => {
12
+ if (!report) return;
13
+ report(kind, true);
14
+ return () => report(kind, false);
15
+ }, [report, kind]);
16
+ }
17
+ function useFramesItself() {
18
+ useReportToList("framed");
19
+ }
20
+ function useScrollsInside() {
21
+ useReportToList("scrolls");
22
+ }
23
+ function SurfaceBoundary({ children, pane = false, framer = null }) {
24
+ return createElement(
25
+ CardSurfaceCtx,
26
+ { value: false },
27
+ createElement(
28
+ SplitPaneCtx,
29
+ { value: pane },
30
+ createElement(
31
+ TableCardCtx,
32
+ { value: false },
33
+ createElement(
34
+ PageActionsCtx,
35
+ { value: null },
36
+ createElement(SplitListFramerCtx, { value: framer }, children)
37
+ )
38
+ )
39
+ )
40
+ );
41
+ }
42
+
43
+ export { CardSurfaceCtx, PageActionsCtx, SplitPaneCtx, SurfaceBoundary, TableCardCtx, useFramesItself, useScrollsInside };
44
+ //# sourceMappingURL=chunk-2PWGJ4PK.js.map
45
+ //# sourceMappingURL=chunk-2PWGJ4PK.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../showcase/src/lib/page-actions.ts","../../../showcase/src/lib/surface-context.ts"],"names":["createContext"],"mappings":";;;AAwBO,IAAM,cAAA,GAAiB,cAAsC,IAAI;AClBjE,IAAM,cAAA,GAAiBA,cAAc,KAAK;AAS1C,IAAM,YAAA,GAAeA,cAA6B,KAAK;AAOvD,IAAM,YAAA,GAAeA,cAAc,KAAK;AAWxC,IAAM,kBAAA,GAAqBA,cAAsC,IAAI,CAAA;AAE5E,SAAS,gBAAgB,IAAA,EAA4B;AACnD,EAAA,MAAM,MAAA,GAAS,WAAW,kBAAkB,CAAA;AAC5C,EAAA,eAAA,CAAgB,MAAM;AACpB,IAAA,IAAI,CAAC,MAAA,EAAQ;AACb,IAAA,MAAA,CAAO,MAAM,IAAI,CAAA;AACjB,IAAA,OAAO,MAAM,MAAA,CAAO,IAAA,EAAM,KAAK,CAAA;AAAA,EACjC,CAAA,EAAG,CAAC,MAAA,EAAQ,IAAI,CAAC,CAAA;AACnB;AAGO,SAAS,eAAA,GAAkB;AAChC,EAAA,eAAA,CAAgB,QAAQ,CAAA;AAC1B;AAGO,SAAS,gBAAA,GAAmB;AACjC,EAAA,eAAA,CAAgB,SAAS,CAAA;AAC3B;AASO,SAAS,gBAAgB,EAAE,QAAA,EAAU,OAAO,KAAA,EAAO,MAAA,GAAS,MAAK,EAAmF;AAGzJ,EAAA,OAAO,aAAA;AAAA,IAAc,cAAA;AAAA,IAAgB,EAAE,OAAO,KAAA,EAAM;AAAA,IAClD,aAAA;AAAA,MAAc,YAAA;AAAA,MAAc,EAAE,OAAO,IAAA,EAAK;AAAA,MACxC,aAAA;AAAA,QAAc,YAAA;AAAA,QAAc,EAAE,OAAO,KAAA,EAAM;AAAA,QACzC,aAAA;AAAA,UAAc,cAAA;AAAA,UAAgB,EAAE,OAAO,IAAA,EAAK;AAAA,UAC1C,cAAc,kBAAA,EAAoB,EAAE,KAAA,EAAO,MAAA,IAAU,QAAQ;AAAA;AAAC;AAAC;AAAC,GAAC;AAC3E","file":"chunk-2PWGJ4PK.js","sourcesContent":["import { createContext } from \"react\";\nimport type { ControlSize } from \"../components/Button\";\n\n/**\n * Sprint 25.16 A4 (R28-7): the slot in a page's own PageHeader for its actions (PageActions). PageContainer provides it,\n * its header draws it while a page asks, and the page's PageActions render into it. The review of 1 October:\n * - content that starts afresh (an overlay's body, a SplitView's pane: SurfaceBoundary) has no slot, so a header there\n * (a message's own header, a drawer's) never takes the page's actions;\n * - the page's header is the first slot in the page, so a second header that comes and goes hands nothing on;\n * - a container with no header of its own (a page that wraps itself in one under an area layout) passes its page's\n * actions to the container around it;\n * - the actions take the header's control size (a minimal or snapping header draws its own actions small).\n */\nexport interface PageActionsSlot {\n /** A page below asks for the slot: a PageActions is drawn. */\n wanted: boolean;\n /** Where the actions go: the slot of the page's header, at the size its controls are drawn. */\n target: { el: HTMLElement; size: ControlSize | null } | null;\n /** A header's slot, while drawn, with its controls' size; returns its release. */\n register: (el: HTMLElement, size: ControlSize | null) => () => void;\n /** A PageActions asks for the slot while drawn; returns its release. */\n add: () => () => void;\n}\n\nexport const PageActionsCtx = createContext<PageActionsSlot | null>(null);\n","import { createContext, createElement, useContext, useLayoutEffect, type ReactNode } from \"react\";\nimport { PageActionsCtx } from \"./page-actions\";\n\n/**\n * True inside a Card's surface. A StackedList reads it to inset its rows to the card's header (px-ds-5).\n */\nexport const CardSurfaceCtx = createContext(false);\n\n/**\n * The SplitView pane content sits in (\"list\", \"detail\", \"aside\"), false outside one. A StackedList reads it to take its\n * inset from the pane (Sprint 25.4 C-5): rows keep their padding so the selection rail sits at the pane's edge and never\n * touches content. A pane header (SectionHeading variant=\"pane\") in the detail insets at the page's gutter, as the item's\n * path above it does, and in the list at its rows' inset (the owner's review of 1 October).\n */\nexport type SplitPaneKind = false | \"list\" | \"detail\" | \"aside\";\nexport const SplitPaneCtx = createContext<SplitPaneKind>(false);\n\n/**\n * True inside a Table or DataGrid card (the phone layout below mobileBreakpoint). An EntityCell reads it to keep\n * the avatar beside the text and align to the value column (Sprint 25.4, round 7: the stacked cell made the card\n * three lines taller than it needed to be).\n */\nexport const TableCardCtx = createContext(false);\n\n/**\n * Sprint 25.14 A44: what a SplitView's list pane holds, as its content reports it while mounted. \"framed\": content that\n * draws its own frame or cards (a KanbanBoard, a CardGrid, a Table, a DataGrid). \"scrolls\": content with no frame of its\n * own that scrolls inside the pane (a StackedList, a Gantt, a VirtualizedList), which the view's hairline frames. The\n * view drops its frame and surface when the pane holds framed content and nothing that scrolls (P32: no frame around\n * what frames itself; a hairline around what scrolls inside). Here, not in SplitView, so a board or a card grid never\n * imports the resizable panels. Null outside a list pane: overlays and the view's other panes reset it (SurfaceBoundary).\n */\nexport type SplitListReport = (kind: \"framed\" | \"scrolls\", present: boolean) => void;\nexport const SplitListFramerCtx = createContext<SplitListReport | null>(null);\n\nfunction useReportToList(kind: \"framed\" | \"scrolls\") {\n const report = useContext(SplitListFramerCtx);\n useLayoutEffect(() => {\n if (!report) return;\n report(kind, true);\n return () => report(kind, false);\n }, [report, kind]);\n}\n\n/** Tell a SplitView whose list pane this sits in that it draws its own frame or cards (A44). */\nexport function useFramesItself() {\n useReportToList(\"framed\");\n}\n\n/** Tell a SplitView whose list pane this sits in that it scrolls inside the pane with no frame of its own (A44). */\nexport function useScrollsInside() {\n useReportToList(\"scrolls\");\n}\n\n/**\n * Where content starts afresh: an overlay's body, an opened table row's panel, a SplitView's pane. What a Card, a pane\n * or a table card told the content around it stops here (the review of 30 September: a StackedList in a Drawer opened\n * from a Card took the card's inset, and one in a Dialog declared in a list pane the pane's, so neither lined up with\n * the overlay's gutter), and so does the page's actions slot. A pane passes `pane` (which pane its content is in) and\n * the list pane its `framer`.\n */\nexport function SurfaceBoundary({ children, pane = false, framer = null }: { children: ReactNode; pane?: SplitPaneKind; framer?: SplitListReport | null }) {\n // the page's actions belong to the page's own header (Sprint 25.16's review): a header in a pane or an overlay never\n // takes them, and PageActions there renders nothing\n return createElement(CardSurfaceCtx, { value: false },\n createElement(SplitPaneCtx, { value: pane },\n createElement(TableCardCtx, { value: false },\n createElement(PageActionsCtx, { value: null },\n createElement(SplitListFramerCtx, { value: framer }, children)))));\n}\n"]}
@@ -0,0 +1,118 @@
1
+ import { createContext, useContext, useState, useLayoutEffect } from 'react';
2
+
3
+ // ../../showcase/src/lib/use-fill-height.ts
4
+ function useFillHeight(ref, enabled = true) {
5
+ const insideFill = useContext(FillContext);
6
+ const active = enabled && !insideFill;
7
+ const [height, setHeight] = useState(null);
8
+ useLayoutEffect(() => {
9
+ const main = ref.current?.closest("main[data-shell-main]");
10
+ if (!active || !main || typeof ResizeObserver === "undefined") {
11
+ setHeight(null);
12
+ return;
13
+ }
14
+ let frame = 0;
15
+ const measure = () => {
16
+ frame = 0;
17
+ const el = ref.current;
18
+ setHeight(el && main.contains(el) ? fillHeight(el, main) : null);
19
+ };
20
+ const schedule = () => {
21
+ if (!frame) frame = requestAnimationFrame(measure);
22
+ };
23
+ measure();
24
+ const ro = new ResizeObserver(schedule);
25
+ const watch = (box) => {
26
+ ro.observe(box);
27
+ for (const child of Array.from(box.children)) ro.observe(child);
28
+ };
29
+ const page = main.querySelector("[data-shell-content]");
30
+ watch(main);
31
+ if (page) watch(page);
32
+ const mo = typeof MutationObserver === "undefined" ? null : new MutationObserver((records) => {
33
+ for (const record of records) record.addedNodes.forEach((node) => {
34
+ if (node instanceof Element) ro.observe(node);
35
+ });
36
+ schedule();
37
+ });
38
+ mo?.observe(main, { childList: true });
39
+ if (page) mo?.observe(page, { childList: true });
40
+ return () => {
41
+ ro.disconnect();
42
+ mo?.disconnect();
43
+ if (frame) cancelAnimationFrame(frame);
44
+ };
45
+ }, [ref, active]);
46
+ return active ? height : null;
47
+ }
48
+ var FillContext = createContext(false);
49
+ var MIN_FILL = 240;
50
+ var ROW_MAX = 56;
51
+ function fillHeight(el, main) {
52
+ const page = el.closest("[data-shell-content]") ?? main;
53
+ if (!leadsPage(el, page) || !isLastContent(el, page) || hasOwnHeight(el) || !followsContent(el, main)) return null;
54
+ const mainBox = main.getBoundingClientRect();
55
+ const top = el.getBoundingClientRect().top - mainBox.top - main.clientTop + main.scrollTop;
56
+ const height = Math.floor(main.clientHeight - top - spaceBelow(el, main));
57
+ return height >= MIN_FILL ? height : null;
58
+ }
59
+ function takesRoom(box) {
60
+ const style = getComputedStyle(box);
61
+ if (style.display === "none" || style.position === "absolute" || style.position === "fixed") return false;
62
+ const rect = box.getBoundingClientRect();
63
+ return rect.height > 0 && rect.width > 0;
64
+ }
65
+ var CHROME = '[role="alert"], [role="status"], [role="tablist"], [role="toolbar"], [role="search"]';
66
+ var isChrome = (box) => box.matches(CHROME) || box.children.length > 0 && Array.from(box.children).every((child) => !takesRoom(child) || child.matches(CHROME));
67
+ function leadsPage(el, page) {
68
+ for (let node = el; node && node !== page; node = node.parentElement) {
69
+ for (let prev = node.previousElementSibling; prev; prev = prev.previousElementSibling) {
70
+ if (!takesRoom(prev)) continue;
71
+ if (prev.matches('[data-gs="page-header"]') || prev.querySelector('[data-gs="page-header"], h1')) continue;
72
+ if (isChrome(prev)) continue;
73
+ if (prev.getBoundingClientRect().height > ROW_MAX) return false;
74
+ }
75
+ }
76
+ return true;
77
+ }
78
+ function isLastContent(el, page) {
79
+ for (let node = el; node && node !== page; node = node.parentElement) {
80
+ for (let next = node.nextElementSibling; next; next = next.nextElementSibling) {
81
+ if (takesRoom(next)) return false;
82
+ }
83
+ }
84
+ return true;
85
+ }
86
+ function hasOwnHeight(el) {
87
+ const inline = el.style.height;
88
+ el.style.height = "";
89
+ const own = el.offsetHeight;
90
+ el.style.height = "auto";
91
+ const natural = el.offsetHeight;
92
+ el.style.height = inline;
93
+ return Math.abs(own - natural) > 1;
94
+ }
95
+ function followsContent(el, main) {
96
+ let top = el;
97
+ while (top.parentElement && top.parentElement !== main) top = top.parentElement;
98
+ const before = top.offsetHeight;
99
+ const inline = el.style.height;
100
+ el.style.height = `${el.offsetHeight + main.clientHeight}px`;
101
+ const grows = top.offsetHeight > before;
102
+ el.style.height = inline;
103
+ return grows;
104
+ }
105
+ function spaceBelow(el, main) {
106
+ const px = (value) => parseFloat(value) || 0;
107
+ let space = px(getComputedStyle(main).paddingBottom);
108
+ for (let node = el; node && node !== main; node = node.parentElement) {
109
+ const style = getComputedStyle(node);
110
+ space += px(style.marginBottom);
111
+ if (node !== el) space += px(style.paddingBottom) + px(style.borderBottomWidth);
112
+ }
113
+ return space;
114
+ }
115
+
116
+ export { FillContext, useFillHeight };
117
+ //# sourceMappingURL=chunk-2YSEME33.js.map
118
+ //# sourceMappingURL=chunk-2YSEME33.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../showcase/src/lib/use-fill-height.ts"],"names":[],"mappings":"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","file":"chunk-2YSEME33.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) || 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/** 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"]}
@@ -0,0 +1,72 @@
1
+ import { useRef, useState, useCallback } from 'react';
2
+
3
+ // ../../showcase/src/lib/use-scroll-edges.ts
4
+ var EDGE_FADE = 24;
5
+ var NONE = { left: false, right: false, vertical: false, gutterRight: 0, gutterBottom: 0 };
6
+ var same = (a, b) => a.left === b.left && a.right === b.right && a.vertical === b.vertical && a.gutterRight === b.gutterRight && a.gutterBottom === b.gutterBottom;
7
+ function useScrollEdges(options = {}) {
8
+ const { enabled = true, fade = EDGE_FADE, noFade } = options;
9
+ const elRef = useRef(null);
10
+ const observer = useRef(null);
11
+ const borders = useRef({ x: 0, y: 0 });
12
+ const [state, setState] = useState(NONE);
13
+ const measure = useCallback(() => {
14
+ const el = elRef.current;
15
+ if (!el) return;
16
+ const next = {
17
+ left: el.scrollLeft > 1,
18
+ right: el.scrollLeft + el.clientWidth < el.scrollWidth - 1,
19
+ vertical: el.scrollHeight > el.clientHeight + 1,
20
+ gutterRight: Math.max(0, el.offsetWidth - el.clientWidth - borders.current.x),
21
+ gutterBottom: Math.max(0, el.offsetHeight - el.clientHeight - borders.current.y)
22
+ };
23
+ setState((prev) => same(prev, next) ? prev : next);
24
+ }, []);
25
+ const ref = useCallback((el) => {
26
+ observer.current?.disconnect();
27
+ observer.current = null;
28
+ elRef.current = el;
29
+ if (!el || !enabled) return;
30
+ const style = getComputedStyle(el);
31
+ const px = (v) => parseFloat(v) || 0;
32
+ borders.current = { x: px(style.borderLeftWidth) + px(style.borderRightWidth), y: px(style.borderTopWidth) + px(style.borderBottomWidth) };
33
+ measure();
34
+ if (typeof ResizeObserver === "undefined") return;
35
+ const o = new ResizeObserver(measure);
36
+ o.observe(el);
37
+ for (const child of Array.from(el.children)) o.observe(child);
38
+ observer.current = o;
39
+ }, [enabled, measure]);
40
+ const fadeLeft = state.left && !noFade?.left;
41
+ const fadeRight = state.right && !noFade?.right;
42
+ const shown = enabled && (fadeLeft || fadeRight);
43
+ const { gutterRight: gr, gutterBottom: gb } = state;
44
+ const fadeLayer = `linear-gradient(to right, ${fadeLeft ? `transparent 0, black ${fade}px` : "black 0"}, ${fadeRight ? `black calc(100% - ${gr + fade}px), transparent calc(100% - ${gr}px)` : "black 100%"})`;
45
+ const gutters = [gb > 0 && { size: `100% ${gb}px`, position: "0 100%" }, gr > 0 && { size: `${gr}px 100%`, position: "100% 0" }].filter(Boolean);
46
+ const mask = shown ? {
47
+ image: [fadeLayer, ...gutters.map(() => "linear-gradient(black, black)")].join(", "),
48
+ size: ["100% 100%", ...gutters.map((g) => g.size)].join(", "),
49
+ position: ["0 0", ...gutters.map((g) => g.position)].join(", ")
50
+ } : void 0;
51
+ return {
52
+ /** The scroller's ref (a callback). */
53
+ ref,
54
+ /** The scroller itself, for code that moves it. */
55
+ elRef,
56
+ measure,
57
+ /** Content hides past a side edge: the scroller moves sideways, and its edges fade (but those in `noFade`). */
58
+ overflowing: enabled && (state.left || state.right),
59
+ /** It scrolls either way, sideways or up and down: a keyboard needs to reach it. */
60
+ scrollable: enabled && (state.left || state.right || state.vertical),
61
+ edges: { left: state.left, right: state.right },
62
+ /** For the scroller: `data-fade` names the faded edges ("left", "right" or "left right"), the style masks them. */
63
+ fadeProps: {
64
+ "data-fade": shown ? [fadeLeft && "left", fadeRight && "right"].filter(Boolean).join(" ") : void 0,
65
+ style: mask ? { maskImage: mask.image, WebkitMaskImage: mask.image, maskSize: mask.size, WebkitMaskSize: mask.size, maskPosition: mask.position, WebkitMaskPosition: mask.position, maskRepeat: "no-repeat", WebkitMaskRepeat: "no-repeat" } : void 0
66
+ }
67
+ };
68
+ }
69
+
70
+ export { EDGE_FADE, useScrollEdges };
71
+ //# sourceMappingURL=chunk-3BRQRKDO.js.map
72
+ //# sourceMappingURL=chunk-3BRQRKDO.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../showcase/src/lib/use-scroll-edges.ts"],"names":[],"mappings":"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","file":"chunk-3BRQRKDO.js","sourcesContent":["import { useCallback, useRef, useState } from \"react\";\n\n/** Which edges of a horizontal scroller have content past them. */\nexport interface ScrollEdges { left: boolean; right: boolean }\n\n/** The width of an edge fade, in px (Sprint 25.4 chunk 5's, on the Tabs strip). */\nexport const EDGE_FADE = 24;\n\n/** What a measure reads: the edges with more, whether it scrolls up and down, and the room its scrollbars take. */\ninterface ScrollState extends ScrollEdges { vertical: boolean; gutterRight: number; gutterBottom: number }\n\nconst NONE: ScrollState = { left: false, right: false, vertical: false, gutterRight: 0, gutterBottom: 0 };\nconst same = (a: ScrollState, b: ScrollState) => a.left === b.left && a.right === b.right && a.vertical === b.vertical && a.gutterRight === b.gutterRight && a.gutterBottom === b.gutterBottom;\n\n/**\n * A horizontal scroller's edge fades — one mechanism for the Tabs strip (Sprint 25.4 chunk 5) and the Table's scroll\n * area (Sprint 25.10 T2, round 25's R25-2: a four-column table in half a row scrolled sideways, and on a Mac, where the\n * scrollbar hides until you scroll, nothing showed there was more). It reports which edges hide content and masks the\n * scroller on those edges only: none at either end, none when everything fits. A mask fades what is under it, so it\n * follows whatever surface the scroller sits on, and it takes no clicks. Scrollbars that take room (Windows, Linux, a\n * Mac set to always show them) stay whole: the mask leaves their gutters opaque and the fade ends where they start\n * (Sprint 25.10 review). Measured on scroll (pass `measure` as onScroll) and whenever the scroller or its content\n * changes size. Left to right only, as the library is.\n */\nexport function useScrollEdges<T extends HTMLElement = HTMLDivElement>(options: { enabled?: boolean; fade?: number; noFade?: { left?: boolean; right?: boolean } } = {}) {\n // `noFade`: an edge that shows what it hides some other way, as a table's pinned columns do with a shadow at their inner\n // edge (Sprint 25.13 A35); the other edge still fades (the review of 30 September: one pinned column dropped both)\n const { enabled = true, fade = EDGE_FADE, noFade } = options;\n const elRef = useRef<T | null>(null);\n const observer = useRef<ResizeObserver | null>(null);\n // the scroller's own borders, read once when it attaches: what offsetWidth counts beyond clientWidth and a scrollbar\n const borders = useRef({ x: 0, y: 0 });\n const [state, setState] = useState<ScrollState>(NONE);\n const measure = useCallback(() => {\n const el = elRef.current;\n if (!el) return;\n const next: ScrollState = {\n left: el.scrollLeft > 1,\n right: el.scrollLeft + el.clientWidth < el.scrollWidth - 1,\n vertical: el.scrollHeight > el.clientHeight + 1,\n gutterRight: Math.max(0, el.offsetWidth - el.clientWidth - borders.current.x),\n gutterBottom: Math.max(0, el.offsetHeight - el.clientHeight - borders.current.y),\n };\n setState((prev) => (same(prev, next) ? prev : next));\n }, []);\n // A callback ref, so the observer follows the element (a Table's scroll area mounts only on desktop, and again after\n // a switch from cards) and nothing re-renders when it attaches: state changes only when what it measures does.\n const ref = useCallback((el: T | null) => {\n observer.current?.disconnect();\n observer.current = null;\n elRef.current = el;\n if (!el || !enabled) return;\n const style = getComputedStyle(el);\n const px = (v: string) => parseFloat(v) || 0;\n borders.current = { x: px(style.borderLeftWidth) + px(style.borderRightWidth), y: px(style.borderTopWidth) + px(style.borderBottomWidth) };\n measure();\n if (typeof ResizeObserver === \"undefined\") return;\n // the content too: a table's rows arriving widen it without the scroller changing size\n const o = new ResizeObserver(measure);\n o.observe(el);\n for (const child of Array.from(el.children)) o.observe(child);\n observer.current = o;\n }, [enabled, measure]);\n const fadeLeft = state.left && !noFade?.left;\n const fadeRight = state.right && !noFade?.right;\n const shown = enabled && (fadeLeft || fadeRight);\n const { gutterRight: gr, gutterBottom: gb } = state;\n const fadeLayer = `linear-gradient(to right, ${fadeLeft ? `transparent 0, black ${fade}px` : \"black 0\"}, ${fadeRight ? `black calc(100% - ${gr + fade}px), transparent calc(100% - ${gr}px)` : \"black 100%\"})`;\n // an opaque layer over each scrollbar gutter; mask layers add up, so a scrollbar shows whole over the fade\n const gutters = [gb > 0 && { size: `100% ${gb}px`, position: \"0 100%\" }, gr > 0 && { size: `${gr}px 100%`, position: \"100% 0\" }].filter(Boolean) as Array<{ size: string; position: string }>;\n const mask = shown\n ? {\n image: [fadeLayer, ...gutters.map(() => \"linear-gradient(black, black)\")].join(\", \"),\n size: [\"100% 100%\", ...gutters.map((g) => g.size)].join(\", \"),\n position: [\"0 0\", ...gutters.map((g) => g.position)].join(\", \"),\n }\n : undefined;\n return {\n /** The scroller's ref (a callback). */\n ref,\n /** The scroller itself, for code that moves it. */\n elRef,\n measure,\n /** Content hides past a side edge: the scroller moves sideways, and its edges fade (but those in `noFade`). */\n overflowing: enabled && (state.left || state.right),\n /** It scrolls either way, sideways or up and down: a keyboard needs to reach it. */\n scrollable: enabled && (state.left || state.right || state.vertical),\n edges: { left: state.left, right: state.right } as ScrollEdges,\n /** For the scroller: `data-fade` names the faded edges (\"left\", \"right\" or \"left right\"), the style masks them. */\n fadeProps: {\n \"data-fade\": shown ? [fadeLeft && \"left\", fadeRight && \"right\"].filter(Boolean).join(\" \") : undefined,\n style: mask\n ? { maskImage: mask.image, WebkitMaskImage: mask.image, maskSize: mask.size, WebkitMaskSize: mask.size, maskPosition: mask.position, WebkitMaskPosition: mask.position, maskRepeat: \"no-repeat\", WebkitMaskRepeat: \"no-repeat\" }\n : undefined,\n },\n };\n}\n"]}
@@ -1,5 +1,6 @@
1
1
  import { ControlSizeContext, triggerType, Button, useShellPreferences, PREFERENCE_HOUSE_DEFAULTS } from './chunk-7TOJ4G7S.js';
2
- import { useReportedChange, useOptionalFormContext, usePopupFocus, focusAfterRemoval, usePressMarks, focusInitial, FormChangeCtx } from './chunk-RT7RYGC3.js';
2
+ import { useReportedChange, useOptionalFormContext, FormChangeCtx } from './chunk-XVB55M3N.js';
3
+ import { usePopupFocus, focusAfterRemoval, usePressMarks, focusInitial } from './chunk-JXANCXK6.js';
3
4
  import { useTextOverflow, Tooltip, usePosition } from './chunk-CXMV7SXJ.js';
4
5
  import { Slot } from './chunk-7Q46JXSK.js';
5
6
  import { useLatest } from './chunk-WH2FUFAC.js';
@@ -9,6 +10,15 @@ import { createContext, useContext, useRef, useCallback, useEffect, useId, useSt
9
10
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
10
11
  import { createPortal } from 'react-dom';
11
12
 
13
+ // ../../showcase/src/lib/control-looks.ts
14
+ var CHECK_FILL = "bg-[var(--ds-check-bg)] [background-image:var(--ds-check-gradient,var(--ds-gradient))]";
15
+ var SWITCH_TRACK_ON = "bg-[var(--ds-check-bg)] border-transparent";
16
+ var SWITCH_TRACK_OFF = "bg-[var(--ds-switch-off-fill,var(--ds-bg-muted))] border-[var(--ds-switch-off-edge,var(--ds-border-default))]";
17
+ var SWITCH_KNOB_ON = "bg-[var(--ds-bg-surface)] supports-[color:oklch(from_red_l_c_h)]:bg-[oklch(from_var(--ds-bg-surface)_l_c_h_/_max(alpha,min(1,alpha*1000)))]";
18
+ var SWITCH_KNOB_OFF = "bg-[var(--ds-switch-off-knob,var(--ds-bg-surface))]";
19
+ var SWITCH_KNOB_MOTION = "transition-[transform,background-color] duration-[var(--ds-transition-speed)]";
20
+ var FilterRowContext = createContext(false);
21
+ var FILTER_FIELD_WIDTH = "w-[280px] max-w-full";
12
22
  var UNCONTROLLED = /* @__PURE__ */ Symbol("uncontrolled");
13
23
  var valueOf = (e) => {
14
24
  const t = e.target;
@@ -28,6 +38,7 @@ var INPUT_TEXT = {
28
38
  function Input({ className, size: sizeProp, error = false, prefix, suffix, ...props }) {
29
39
  const contextSize = useContext(ControlSizeContext);
30
40
  const size = sizeProp ?? contextSize ?? "md";
41
+ const width = useContext(FilterRowContext) ? FILTER_FIELD_WIDTH : "w-full";
31
42
  const onChange = useReportedChange(props.onChange, props.value === void 0 ? UNCONTROLLED : props.value, valueOf);
32
43
  const borderColor = error ? "border-[var(--ds-status-error)]" : "border-[var(--ds-input-border)]";
33
44
  const hoverBorder = !error && !props.disabled && !props.readOnly ? "hover:border-[var(--ds-input-border-hover)]" : "";
@@ -36,7 +47,8 @@ function Input({ className, size: sizeProp, error = false, prefix, suffix, ...pr
36
47
  "div",
37
48
  {
38
49
  className: cn(
39
- "flex items-center w-full",
50
+ "flex items-center",
51
+ width,
40
52
  INPUT_HEIGHT[size],
41
53
  "bg-[var(--ds-input-bg)]",
42
54
  "border-[length:var(--ds-border-width)]",
@@ -78,7 +90,8 @@ function Input({ className, size: sizeProp, error = false, prefix, suffix, ...pr
78
90
  {
79
91
  "aria-invalid": error || void 0,
80
92
  className: cn(
81
- "flex w-full",
93
+ "flex",
94
+ width,
82
95
  INPUT_TEXT[size],
83
96
  INPUT_HEIGHT[size],
84
97
  "bg-[var(--ds-input-bg)] text-[var(--ds-text-primary)]",
@@ -185,8 +198,9 @@ function Toggle({ checked, onChange, label, disabled, error = false, layout = "f
185
198
  "transition-colors duration-[var(--ds-transition-speed)]",
186
199
  "focus-visible:outline-none focus-visible:shadow-[var(--ds-shadow-focus)]",
187
200
  disabled ? "cursor-not-allowed" : "cursor-pointer",
188
- checked ? "bg-[var(--ds-accent-primary)]" : "bg-[var(--ds-bg-muted)]",
189
- error ? "border-[var(--ds-status-error)]" : checked ? "border-transparent" : "border-[var(--ds-border-default)]"
201
+ // Sprint 25.14 A20: on, the checked colour, as a checkbox's; off, the outline in the edge colour (lib/control-looks.ts)
202
+ checked ? SWITCH_TRACK_ON : SWITCH_TRACK_OFF,
203
+ error && "border-[var(--ds-status-error)]"
190
204
  ),
191
205
  style: { width: dims.w, height: dims.h },
192
206
  children: /* @__PURE__ */ jsx(
@@ -194,10 +208,11 @@ function Toggle({ checked, onChange, label, disabled, error = false, layout = "f
194
208
  {
195
209
  className: cn(
196
210
  "pointer-events-none inline-block shrink-0",
197
- "bg-[var(--ds-bg-surface)]",
211
+ // on: the surface made opaque; off: the edge colour, beside the outline
212
+ checked ? SWITCH_KNOB_ON : SWITCH_KNOB_OFF,
198
213
  "border-[length:var(--ds-border-width)] border-transparent",
199
214
  "rounded-[var(--ds-radius-full)]",
200
- "transition-transform"
215
+ SWITCH_KNOB_MOTION
201
216
  ),
202
217
  style: {
203
218
  width: dims.thumb,
@@ -1344,6 +1359,6 @@ function useWholeListCheck(component, length, pager) {
1344
1359
  }, [component, length, total]);
1345
1360
  }
1346
1361
 
1347
- export { AddAnotherRowCtx, Dropdown, Field, InlineFieldError, Input, Label, NumberInput, Pagination, Popover, Textarea, Toggle, pagerPart, useField, useFieldControl, usePagination, useWholeListCheck, warnOnce };
1348
- //# sourceMappingURL=chunk-2RV23OBK.js.map
1349
- //# sourceMappingURL=chunk-2RV23OBK.js.map
1362
+ export { AddAnotherRowCtx, CHECK_FILL, Dropdown, Field, FilterRowContext, InlineFieldError, Input, Label, NumberInput, Pagination, Popover, SWITCH_KNOB_MOTION, SWITCH_KNOB_OFF, SWITCH_KNOB_ON, SWITCH_TRACK_OFF, SWITCH_TRACK_ON, Textarea, Toggle, pagerPart, useField, useFieldControl, usePagination, useWholeListCheck, warnOnce };
1363
+ //# sourceMappingURL=chunk-6XAU44U4.js.map
1364
+ //# sourceMappingURL=chunk-6XAU44U4.js.map