gridsmith-ui 0.29.0 → 0.30.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 (103) hide show
  1. package/CHANGELOG.md +79 -0
  2. package/README.md +5 -1
  3. package/dist/DropdownMenu-DqT7ZQ0D.d.ts +39 -0
  4. package/dist/charts.d.ts +5 -0
  5. package/dist/charts.js +5 -3
  6. package/dist/charts.js.map +1 -1
  7. package/dist/chat.js +5 -5
  8. package/dist/{chunk-EIWXVICU.js → chunk-6A5RHW2W.js} +3 -3
  9. package/dist/{chunk-EIWXVICU.js.map → chunk-6A5RHW2W.js.map} +1 -1
  10. package/dist/{chunk-XVB55M3N.js → chunk-72SDAX54.js} +2 -2
  11. package/dist/chunk-72SDAX54.js.map +1 -0
  12. package/dist/{chunk-OYNUU346.js → chunk-7QLXYRPD.js} +3 -12
  13. package/dist/chunk-7QLXYRPD.js.map +1 -0
  14. package/dist/chunk-F2BNJSON.js +1348 -0
  15. package/dist/chunk-F2BNJSON.js.map +1 -0
  16. package/dist/{chunk-GXMTS3YD.js → chunk-GQVBZ4GW.js} +54 -16
  17. package/dist/chunk-GQVBZ4GW.js.map +1 -0
  18. package/dist/{chunk-2ROVDVLZ.js → chunk-INGF2CPW.js} +4 -23
  19. package/dist/chunk-INGF2CPW.js.map +1 -0
  20. package/dist/{chunk-66JJCQU3.js → chunk-JTU7OBYJ.js} +5 -3
  21. package/dist/chunk-JTU7OBYJ.js.map +1 -0
  22. package/dist/{chunk-DQCHCLEJ.js → chunk-KTPGMJUK.js} +6 -6
  23. package/dist/{chunk-DQCHCLEJ.js.map → chunk-KTPGMJUK.js.map} +1 -1
  24. package/dist/{chunk-TJEV7YAY.js → chunk-L6HWACK7.js} +7 -7
  25. package/dist/chunk-L6HWACK7.js.map +1 -0
  26. package/dist/chunk-LJYSVZMI.js +25 -0
  27. package/dist/chunk-LJYSVZMI.js.map +1 -0
  28. package/dist/{chunk-YUTF2X6B.js → chunk-NI5542ZM.js} +42 -38
  29. package/dist/chunk-NI5542ZM.js.map +1 -0
  30. package/dist/chunk-NKUCS67T.js +13 -0
  31. package/dist/chunk-NKUCS67T.js.map +1 -0
  32. package/dist/{chunk-TRDXWHD2.js → chunk-NLXQ323J.js} +6 -6
  33. package/dist/chunk-NLXQ323J.js.map +1 -0
  34. package/dist/{chunk-ZI3OQDET.js → chunk-OIOII5L6.js} +93 -4
  35. package/dist/chunk-OIOII5L6.js.map +1 -0
  36. package/dist/{chunk-R4GXKH4U.js → chunk-Q36JVJX4.js} +52 -93
  37. package/dist/chunk-Q36JVJX4.js.map +1 -0
  38. package/dist/{chunk-FD4YED7O.js → chunk-RHWTMTU5.js} +3 -3
  39. package/dist/{chunk-FD4YED7O.js.map → chunk-RHWTMTU5.js.map} +1 -1
  40. package/dist/chunk-TYNDXMBV.js +105 -0
  41. package/dist/chunk-TYNDXMBV.js.map +1 -0
  42. package/dist/{chunk-OPFJEL3Z.js → chunk-UNS4TGGK.js} +110 -20
  43. package/dist/chunk-UNS4TGGK.js.map +1 -0
  44. package/dist/chunk-Z4MCD73B.js +316 -0
  45. package/dist/chunk-Z4MCD73B.js.map +1 -0
  46. package/dist/{chunk-CRQ6KCRD.js → chunk-ZHUVLBCC.js} +7 -4
  47. package/dist/chunk-ZHUVLBCC.js.map +1 -0
  48. package/dist/data-grid.d.ts +26 -7
  49. package/dist/data-grid.js +179 -57
  50. package/dist/data-grid.js.map +1 -1
  51. package/dist/editor-mentions.js +3 -3
  52. package/dist/editor.js +5 -5
  53. package/dist/{feather-FNBQ74RR.js → feather-XFFEFLTV.js} +5 -3
  54. package/dist/feather-XFFEFLTV.js.map +1 -0
  55. package/dist/{heroicons-DODCSYWD.js → heroicons-L6DOHBEA.js} +5 -3
  56. package/dist/heroicons-L6DOHBEA.js.map +1 -0
  57. package/dist/icons.d.ts +10 -1
  58. package/dist/icons.js +1 -1
  59. package/dist/index.d.ts +174 -50
  60. package/dist/index.js +810 -1731
  61. package/dist/index.js.map +1 -1
  62. package/dist/kanban.d.ts +26 -8
  63. package/dist/kanban.js +203 -79
  64. package/dist/kanban.js.map +1 -1
  65. package/dist/{lucide-ZWE3EPGP.js → lucide-JOORDTO6.js} +5 -3
  66. package/dist/lucide-JOORDTO6.js.map +1 -0
  67. package/dist/{phosphor-DIHPA4AJ.js → phosphor-TKGDYNGR.js} +5 -3
  68. package/dist/phosphor-TKGDYNGR.js.map +1 -0
  69. package/dist/resizable.js +12 -11
  70. package/dist/resizable.js.map +1 -1
  71. package/dist/roots.css +14 -8
  72. package/dist/{table-utils-BkjNEWts.d.ts → table-utils-BXkP-2Hx.d.ts} +8 -5
  73. package/dist/{tabler-D3VYTSGI.js → tabler-YGETLYRU.js} +5 -3
  74. package/dist/tabler-YGETLYRU.js.map +1 -0
  75. package/dist/tokens.css +14 -0
  76. package/dist/{use-pagination-BxcRlNQG.d.ts → use-pagination-QykGSREv.d.ts} +7 -0
  77. package/dist/utils.js.map +1 -1
  78. package/dist/virtualized.d.ts +1 -1
  79. package/dist/virtualized.js +10 -9
  80. package/dist/virtualized.js.map +1 -1
  81. package/dist-static/tokens.css +14 -0
  82. package/package.json +1 -1
  83. package/dist/chunk-2ROVDVLZ.js.map +0 -1
  84. package/dist/chunk-66JJCQU3.js.map +0 -1
  85. package/dist/chunk-BRAXJEEP.js +0 -117
  86. package/dist/chunk-BRAXJEEP.js.map +0 -1
  87. package/dist/chunk-CRQ6KCRD.js.map +0 -1
  88. package/dist/chunk-GXMTS3YD.js.map +0 -1
  89. package/dist/chunk-OPFJEL3Z.js.map +0 -1
  90. package/dist/chunk-OYNUU346.js.map +0 -1
  91. package/dist/chunk-QM4SJKWV.js +0 -51
  92. package/dist/chunk-QM4SJKWV.js.map +0 -1
  93. package/dist/chunk-R4GXKH4U.js.map +0 -1
  94. package/dist/chunk-TJEV7YAY.js.map +0 -1
  95. package/dist/chunk-TRDXWHD2.js.map +0 -1
  96. package/dist/chunk-XVB55M3N.js.map +0 -1
  97. package/dist/chunk-YUTF2X6B.js.map +0 -1
  98. package/dist/chunk-ZI3OQDET.js.map +0 -1
  99. package/dist/feather-FNBQ74RR.js.map +0 -1
  100. package/dist/heroicons-DODCSYWD.js.map +0 -1
  101. package/dist/lucide-ZWE3EPGP.js.map +0 -1
  102. package/dist/phosphor-DIHPA4AJ.js.map +0 -1
  103. package/dist/tabler-D3VYTSGI.js.map +0 -1
@@ -0,0 +1,105 @@
1
+ "use client";
2
+ import { formatLocale } from './chunk-7QLXYRPD.js';
3
+ import { warnOnce } from './chunk-L6HWACK7.js';
4
+ import { createContext, useMemo, useContext } from 'react';
5
+
6
+ // ../../showcase/src/lib/relative-time.ts
7
+ var MINUTE = 6e4;
8
+ var HOUR = 60 * MINUTE;
9
+ var DAY = 24 * HOUR;
10
+ var HOUSE_RELATIVE_THRESHOLD_HOURS = 24;
11
+ function toMillis(time) {
12
+ if (time instanceof Date) return time.getTime();
13
+ if (typeof time === "number") return time;
14
+ if (typeof time !== "string") return NaN;
15
+ const day = /^(\d{4})-(\d{2})-(\d{2})$/.exec(time.trim());
16
+ if (day) return new Date(Number(day[1]), Number(day[2]) - 1, Number(day[3])).getTime();
17
+ return Date.parse(time);
18
+ }
19
+ var ISO_8601 = /^(\d{4})-(\d{2})-(\d{2})(?:T\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?(?:Z|[+-]\d{2}:\d{2})?)?$/;
20
+ function datedMillis(time) {
21
+ if (typeof time !== "string") return toMillis(time);
22
+ const iso = ISO_8601.exec(time);
23
+ if (!iso) return NaN;
24
+ const [year, month, day] = [Number(iso[1]), Number(iso[2]) - 1, Number(iso[3])];
25
+ const check = /* @__PURE__ */ new Date(0);
26
+ check.setUTCFullYear(year, month, day);
27
+ if (check.getUTCFullYear() !== year || check.getUTCMonth() !== month || check.getUTCDate() !== day) return NaN;
28
+ return toMillis(time);
29
+ }
30
+ function thresholdOf(hours, from) {
31
+ if (typeof hours === "number" && Number.isFinite(hours) && hours >= 0) return hours;
32
+ const house = HOUSE_RELATIVE_THRESHOLD_HOURS;
33
+ warnOnce(from, `a threshold of ${String(hours)} isn't a number of hours, so the house's ${house} is used.`);
34
+ return house;
35
+ }
36
+ function midnight(t) {
37
+ const d = new Date(t);
38
+ d.setHours(0, 0, 0, 0);
39
+ return d.getTime();
40
+ }
41
+ function formatRelativeTime(time, options) {
42
+ const at = toMillis(time);
43
+ if (!Number.isFinite(at)) return "";
44
+ const now = options.now ?? Date.now();
45
+ const locale = options.locale ?? formatLocale();
46
+ const diff = at - now;
47
+ const age = Math.abs(diff);
48
+ if (age > thresholdOf(options.thresholdHours, "formatRelativeTime") * HOUR) return new Intl.DateTimeFormat(locale, { dateStyle: "medium", timeStyle: "short" }).format(at);
49
+ const rtf = new Intl.RelativeTimeFormat(locale, { numeric: "auto" });
50
+ if (age < MINUTE) return rtf.format(0, "second");
51
+ if (age < HOUR) return rtf.format(Math.trunc(diff / MINUTE), "minute");
52
+ if (age < DAY) return rtf.format(Math.trunc(diff / HOUR), "hour");
53
+ return rtf.format(Math.round((midnight(at) - midnight(now)) / DAY), "day");
54
+ }
55
+ function formatFullTime(time, locale) {
56
+ const at = toMillis(time);
57
+ return Number.isFinite(at) ? new Intl.DateTimeFormat(locale ?? formatLocale(), { year: "numeric", month: "long", day: "numeric", hour: "numeric", minute: "2-digit", timeZoneName: "short" }).format(at) : "";
58
+ }
59
+ var PreferencesContext = createContext(null);
60
+ var HOUSE_DEFAULTS = {
61
+ validationTiming: "onBlur",
62
+ formActions: { align: "end", placement: "auto" },
63
+ itemsPerPage: 25,
64
+ paginationStyle: "pagination",
65
+ tableToCardsBreakpoint: "md",
66
+ emptyStyle: "illustration-cta",
67
+ loadingStyle: "skeleton",
68
+ destructiveMode: "dialog",
69
+ highSeverityActions: ["delete_account", "remove_team_member"],
70
+ typeToConfirmActions: ["delete_account"],
71
+ undoWindowSeconds: 5,
72
+ relativeThresholdHours: HOUSE_RELATIVE_THRESHOLD_HOURS,
73
+ canNavigateBack: true
74
+ };
75
+ var PREFERENCE_HOUSE_DEFAULTS = Object.freeze(HOUSE_DEFAULTS);
76
+ function usePreferences() {
77
+ const raw = useShellPreferences();
78
+ return useMemo(() => resolvePreferences(raw), [raw]);
79
+ }
80
+ function resolvePreferences(raw) {
81
+ const d = PREFERENCE_HOUSE_DEFAULTS;
82
+ const out = { ...d, formActions: { ...d.formActions }, highSeverityActions: [...d.highSeverityActions], typeToConfirmActions: [...d.typeToConfirmActions] };
83
+ for (const [key, value] of Object.entries(raw)) {
84
+ if (value === void 0) continue;
85
+ if (key === "formActions") {
86
+ const actions = value;
87
+ if (actions?.align !== void 0) out.formActions.align = actions.align;
88
+ if (actions?.placement !== void 0) out.formActions.placement = actions.placement;
89
+ continue;
90
+ }
91
+ out[key] = value;
92
+ }
93
+ return out;
94
+ }
95
+ function useShellPreferences() {
96
+ return useContext(PreferencesContext) ?? EMPTY;
97
+ }
98
+ var EMPTY = Object.freeze({});
99
+ function preferenceActionKey(label) {
100
+ return label.trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_|_$/g, "");
101
+ }
102
+
103
+ export { DAY, HOUR, MINUTE, PREFERENCE_HOUSE_DEFAULTS, PreferencesContext, datedMillis, formatFullTime, formatRelativeTime, preferenceActionKey, thresholdOf, toMillis, usePreferences, useShellPreferences };
104
+ //# sourceMappingURL=chunk-TYNDXMBV.js.map
105
+ //# sourceMappingURL=chunk-TYNDXMBV.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../showcase/src/lib/relative-time.ts","../../../showcase/src/lib/preferences.ts"],"names":[],"mappings":"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","file":"chunk-TYNDXMBV.js","sourcesContent":["import { warnOnce } from \"./dev-warn\";\nimport { formatLocale } from \"./format-locale\";\n\n/**\n * Times as people read them, relative or in full (Sprint 25.17 A29), apart from the RelativeTime component since Sprint\n * 25.22 (nothing here needs React). Not on `gridsmith-ui/utils` (that sprint's review): a relative time and a full one\n * depend on the visitor's clock and time zone, which a server doesn't know, so a server page hands RelativeTime the time.\n */\n\n/** A time: an ISO string, a Date, or milliseconds since the epoch. */\nexport type TimeValue = string | Date | number;\n\nexport const MINUTE = 60_000;\nexport const HOUR = 60 * MINUTE;\nexport const DAY = 24 * HOUR;\n\n/** The house's threshold, dateTime.relativeThresholdHours: within this many hours a time reads relative. The shell's preferences default to it. */\nexport const HOUSE_RELATIVE_THRESHOLD_HOURS = 24;\n\nexport function toMillis(time: TimeValue): number {\n if (time instanceof Date) return time.getTime();\n if (typeof time === \"number\") return time;\n if (typeof time !== \"string\") return NaN;\n // a date alone (\"2026-09-01\") is that day on the calendar here, as people read it: Date.parse reads it as midnight in\n // UTC, which New York showed as the evening before (the 25.17 second review)\n const day = /^(\\d{4})-(\\d{2})-(\\d{2})$/.exec(time.trim());\n if (day) return new Date(Number(day[1]), Number(day[2]) - 1, Number(day[3])).getTime();\n return Date.parse(time);\n}\n\n/**\n * ISO 8601's extended form, as ECMAScript's date-time format writes it: a calendar date, then perhaps a time (hours and\n * minutes, perhaps seconds and a fraction) and perhaps its zone, Z or ±hh:mm (±hhmm is ISO 8601 too, but outside the\n * format every browser must read, so it stays text).\n */\nconst ISO_8601 = /^(\\d{4})-(\\d{2})-(\\d{2})(?:T\\d{2}:\\d{2}(?::\\d{2}(?:\\.\\d+)?)?(?:Z|[+-]\\d{2}:\\d{2})?)?$/;\n\n/**\n * A time's milliseconds when it can be dated, else NaN (Sprint 25.8.7 item 10): a Date or a number as it is, a string\n * only in ISO 8601's form and on a day the calendar has. Date.parse guesses at other text (\"March 3\" is 3 March 2001,\n * \"12\" December 2001) and carries a day past its month's end into the next (\"2026-02-30\" is 2 March), so text that\n * only reads like a time (\"2 days ago\", \"Yesterday\") is never given a date.\n */\nexport function datedMillis(time: TimeValue): number {\n if (typeof time !== \"string\") return toMillis(time);\n const iso = ISO_8601.exec(time);\n if (!iso) return NaN;\n const [year, month, day] = [Number(iso[1]), Number(iso[2]) - 1, Number(iso[3])];\n // in UTC, so no zone moves the day; setUTCFullYear, since Date.UTC reads the years 0–99 as 1900–1999\n const check = new Date(0);\n check.setUTCFullYear(year, month, day);\n if (check.getUTCFullYear() !== year || check.getUTCMonth() !== month || check.getUTCDate() !== day) return NaN;\n return toMillis(time);\n}\n\n/**\n * A threshold as given, or the house's when it isn't a number of hours (a missing setting read as `Number(undefined)`\n * re-rendered every time in a loop: the 25.17 second review), which development builds are told about.\n */\nexport function thresholdOf(hours: unknown, from: string): number {\n if (typeof hours === \"number\" && Number.isFinite(hours) && hours >= 0) return hours;\n const house = HOUSE_RELATIVE_THRESHOLD_HOURS;\n warnOnce(from, `a threshold of ${String(hours)} isn't a number of hours, so the house's ${house} is used.`);\n return house;\n}\n\n/** Local midnight before a moment: days are counted on the calendar, so Monday 23:30 is two days before Wednesday 00:30. */\nfunction midnight(t: number): number {\n const d = new Date(t);\n d.setHours(0, 0, 0, 0);\n return d.getTime();\n}\n\n/**\n * A time as people read it (Sprint 25.17 A29, R28-48; date_time#1, #11 and #13): within `thresholdHours` of now,\n * relative (\"now\", \"5 minutes ago\", \"in 2 hours\", \"yesterday\", counting days on the calendar), in the page's language;\n * beyond it, the date and the time. The threshold is given: the project's is the shell's\n * (dateTime.relativeThresholdHours), which RelativeTime and useRelativeTimeFormat read for you.\n */\nexport function formatRelativeTime(time: TimeValue, options: { thresholdHours: number; now?: number; locale?: string }): string {\n const at = toMillis(time);\n if (!Number.isFinite(at)) return \"\";\n const now = options.now ?? Date.now();\n // the locale given (a component's useFormatLocale(), Sprint 25.20), else the browser's reading\n const locale = options.locale ?? formatLocale();\n const diff = at - now;\n const age = Math.abs(diff);\n // past the threshold the moment itself: a log or a table keeps its time of day (the review: a log read \"1 Oct 2026\"\n // on every row)\n if (age > thresholdOf(options.thresholdHours, \"formatRelativeTime\") * HOUR) return new Intl.DateTimeFormat(locale, { dateStyle: \"medium\", timeStyle: \"short\" }).format(at);\n // whole units, counted as they pass (\"5 minutes ago\" until the sixth minute), so the text changes when the shown time\n // refreshes, on the minute or the hour\n const rtf = new Intl.RelativeTimeFormat(locale, { numeric: \"auto\" });\n if (age < MINUTE) return rtf.format(0, \"second\");\n if (age < HOUR) return rtf.format(Math.trunc(diff / MINUTE), \"minute\");\n if (age < DAY) return rtf.format(Math.trunc(diff / HOUR), \"hour\");\n return rtf.format(Math.round((midnight(at) - midnight(now)) / DAY), \"day\");\n}\n\n/**\n * The full timestamp, as the tooltip and a screen reader give it: the date, the time and its zone, in the locale given\n * (a component's useFormatLocale()), else the browser's reading.\n */\nexport function formatFullTime(time: TimeValue, locale?: string): string {\n const at = toMillis(time);\n return Number.isFinite(at)\n ? new Intl.DateTimeFormat(locale ?? formatLocale(), { year: \"numeric\", month: \"long\", day: \"numeric\", hour: \"numeric\", minute: \"2-digit\", timeZoneName: \"short\" }).format(at)\n : \"\";\n}\n","/**\n * The customer's UX preferences, carried once by AppShell the way the theme is (Sprint 25.6 B9).\n *\n * The generated shell passes the object the template emits from the profile's rules; the builder preview passes the\n * same values from the theme state. Components default from it when no prop is passed — FormWrapper's validation mode\n * and actions alignment, Table and DataGrid's mobile breakpoint, Pagination's page size, EmptyState's style, the\n * destructive-action helper's mode, lists and undo window, and RelativeTime's threshold (Sprint 25.17 A29) and a flow's going back (useFlowSteps, Sprint 25.19 S3) — so a page\n * gets the customer's value by doing nothing. A\n * prop always wins; a component outside any shell keeps the default it always had (components read\n * `useShellPreferences`). App code reads `usePreferences`, which fills every field (round 21); a paged list reads its\n * page size through `usePagination`.\n */\nimport { createContext, useContext, useMemo } from \"react\";\nimport { HOUSE_RELATIVE_THRESHOLD_HOURS } from \"./relative-time\";\n\nexport type PreferenceValidationTiming = \"onBlur\" | \"onSubmit\" | \"onChange\";\nexport type PreferenceBreakpoint = \"sm\" | \"md\" | \"lg\";\nexport type PreferenceEmptyStyle = \"illustration-cta\" | \"text-cta\" | \"redirect\";\nexport type PreferenceLoadingStyle = \"skeleton\" | \"spinner\" | \"progress-bar\";\nexport type PreferenceDestructiveMode = \"dialog\" | \"undo\";\n/** dataDisplay.paginationStyle — how a long list shows more: page numbers, a Load more button, or loading as the user scrolls. */\nexport type PreferencePaginationStyle = \"pagination\" | \"load-more\" | \"infinite-scroll\";\n\nexport interface AppShellPreferences {\n /** forms.validationTiming — when a field first shows its error. */\n validationTiming?: PreferenceValidationTiming;\n /** forms.actions — where a form's buttons sit and how they align. */\n formActions?: { align?: \"start\" | \"end\"; placement?: \"auto\" | \"inline\" };\n /** dataDisplay.itemsPerPage — the page size of a paged or load-more list. */\n itemsPerPage?: number;\n /** dataDisplay.paginationStyle — page numbers, a Load more button, or loading on scroll; usePagination and Pagination follow it (round 22). */\n paginationStyle?: PreferencePaginationStyle;\n /** responsive.tableToCardsBreakpoint — below this width a Table becomes stacked cards. */\n tableToCardsBreakpoint?: PreferenceBreakpoint;\n /** emptyStates.emptyStyle — illustration with an action, text with an action, or redirect to creation. */\n emptyStyle?: PreferenceEmptyStyle;\n /** emptyStates.loadingStyle — what a page shows while data loads. */\n loadingStyle?: PreferenceLoadingStyle;\n /** destructiveActions.mode — confirm in a dialog, or act now with an Undo toast. */\n destructiveMode?: PreferenceDestructiveMode;\n /** destructiveActions.highSeverityActions — always confirm in a dialog, even in undo mode. */\n highSeverityActions?: string[];\n /** destructiveActions.typeToConfirmActions — the user types the name before the confirm enables. */\n typeToConfirmActions?: string[];\n /** destructiveActions.undoWindowSeconds — how long the Undo toast stays. */\n undoWindowSeconds?: number;\n /** dateTime.relativeThresholdHours — within this many hours a time reads relative (\"2 hours ago\"), older the date (RelativeTime). */\n relativeThresholdHours?: number;\n /**\n * forms.canNavigateBack — a flow with steps lets the user go back (Sprint 25.19 S3). On, its step list opens any step\n * already reached and it has a Back; off, the list only shows progress and there is no Back. useFlowSteps reads it.\n */\n canNavigateBack?: boolean;\n}\n\nexport const PreferencesContext = createContext<AppShellPreferences | null>(null);\n\n/** Every preference in force, the shell's value or the house default — what app code reads. */\nexport interface ResolvedPreferences {\n validationTiming: PreferenceValidationTiming;\n formActions: { align: \"start\" | \"end\"; placement: \"auto\" | \"inline\" };\n itemsPerPage: number;\n paginationStyle: PreferencePaginationStyle;\n tableToCardsBreakpoint: PreferenceBreakpoint;\n emptyStyle: PreferenceEmptyStyle;\n loadingStyle: PreferenceLoadingStyle;\n destructiveMode: PreferenceDestructiveMode;\n highSeverityActions: string[];\n typeToConfirmActions: string[];\n undoWindowSeconds: number;\n relativeThresholdHours: number;\n canNavigateBack: boolean;\n}\n\n/**\n * The house defaults for the fields the shell carries: the platform's preference-defaults.ts, mirrored in\n * decisions-schema.ts (DEFAULT_PATTERN_DECISIONS); a test keeps the three equal.\n */\nconst HOUSE_DEFAULTS: ResolvedPreferences = {\n validationTiming: \"onBlur\",\n formActions: { align: \"end\", placement: \"auto\" },\n itemsPerPage: 25,\n paginationStyle: \"pagination\",\n tableToCardsBreakpoint: \"md\",\n emptyStyle: \"illustration-cta\",\n loadingStyle: \"skeleton\",\n destructiveMode: \"dialog\",\n highSeverityActions: [\"delete_account\", \"remove_team_member\"],\n typeToConfirmActions: [\"delete_account\"],\n undoWindowSeconds: 5,\n relativeThresholdHours: HOUSE_RELATIVE_THRESHOLD_HOURS,\n canNavigateBack: true,\n};\nexport const PREFERENCE_HOUSE_DEFAULTS: Readonly<ResolvedPreferences> = Object.freeze(HOUSE_DEFAULTS);\n\n/**\n * For app code: every preference in force — the shell's value, else the house default — so a page never writes a\n * fallback of its own. Round 21 (run 58): with every field optional, TypeScript made the builder write\n * `const { itemsPerPage = 20 } = usePreferences()`, a second copy of the preference it then edited by hand when the\n * rule changed. Paged lists use `usePagination`, which reads the page size itself.\n */\nexport function usePreferences(): ResolvedPreferences {\n const raw = useShellPreferences();\n return useMemo(() => resolvePreferences(raw), [raw]);\n}\n\n/** The shell's values over the house defaults; a field the shell leaves undefined keeps its default. */\nexport function resolvePreferences(raw: AppShellPreferences): ResolvedPreferences {\n const d = PREFERENCE_HOUSE_DEFAULTS;\n const out: ResolvedPreferences = { ...d, formActions: { ...d.formActions }, highSeverityActions: [...d.highSeverityActions], typeToConfirmActions: [...d.typeToConfirmActions] };\n for (const [key, value] of Object.entries(raw) as Array<[keyof AppShellPreferences, unknown]>) {\n if (value === undefined) continue;\n if (key === \"formActions\") {\n const actions = value as AppShellPreferences[\"formActions\"];\n if (actions?.align !== undefined) out.formActions.align = actions.align;\n if (actions?.placement !== undefined) out.formActions.placement = actions.placement;\n continue;\n }\n (out as unknown as Record<string, unknown>)[key] = value;\n }\n return out;\n}\n\n/**\n * For components: only what the shell passed, or an empty object outside any shell — a component with no prop takes\n * the shell's value, and a bare component keeps the default it always had.\n */\nexport function useShellPreferences(): AppShellPreferences {\n return useContext(PreferencesContext) ?? EMPTY;\n}\n\nconst EMPTY: AppShellPreferences = Object.freeze({});\n\n/**\n * \"Remove range\" → \"remove_range\": the key the high-severity and type-to-confirm lists use. The engine's `actionKey`\n * (platform/src/lib/destructive-mode.ts) is the same rule for compliance; a platform test keeps the two identical.\n */\nexport function preferenceActionKey(label: string): string {\n return label.trim().toLowerCase().replace(/[^a-z0-9]+/g, \"_\").replace(/^_|_$/g, \"\");\n}\n"]}
@@ -1,16 +1,16 @@
1
1
  "use client";
2
- import { PreferencesContext, Button } from './chunk-R4GXKH4U.js';
3
- import { useBreakpoint, readBreakpointPx } from './chunk-CRQ6KCRD.js';
4
- import { FormChangeCtx } from './chunk-XVB55M3N.js';
5
- import { SurfaceBoundary } from './chunk-QM4SJKWV.js';
2
+ import { PreferencesContext } from './chunk-TYNDXMBV.js';
3
+ import { FormChangeCtx } from './chunk-72SDAX54.js';
4
+ import { SurfaceBoundary, Button } from './chunk-Q36JVJX4.js';
5
+ import { useBreakpoint, readBreakpointPx } from './chunk-ZHUVLBCC.js';
6
6
  import { useRevealFocus } from './chunk-HTXP5BMM.js';
7
- import { usePopupFocus, useOverlayLayer, useReturnFocus, useFocusTrap, focusInitial, useNonModalDialog, OVERLAY_CLOSE_ATTR } from './chunk-ZI3OQDET.js';
8
- import { useTextOverflow, Tooltip } from './chunk-GXMTS3YD.js';
9
- import { Icon } from './chunk-TJEV7YAY.js';
7
+ import { usePopupFocus, useOverlayLayer, useReturnFocus, useFocusTrap, focusInitial, useNonModalDialog, OVERLAY_CLOSE_ATTR } from './chunk-OIOII5L6.js';
8
+ import { useTextOverflow, AutoTooltip } from './chunk-GQVBZ4GW.js';
9
+ import { Icon } from './chunk-L6HWACK7.js';
10
10
  import { usePosition } from './chunk-HP7WUZPK.js';
11
11
  import { useLatest } from './chunk-WH2FUFAC.js';
12
- import { cn, FOCUS_RING, FOCUS_INSET, lockBodyScroll, unlockBodyScroll, OVERLAY_SURFACE, GLASS_OVERLAY, GROUP_FOCUS_RING } from './chunk-66JJCQU3.js';
13
- import { createContext, version, forwardRef, useState, useCallback, useRef, useEffect, isValidElement, useContext, useMemo, useId, useLayoutEffect } from 'react';
12
+ import { cn, FOCUS_RING, FOCUS_INSET, lockBodyScroll, unlockBodyScroll, OVERLAY_SURFACE, GLASS_OVERLAY, GROUP_FOCUS_RING } from './chunk-JTU7OBYJ.js';
13
+ import { createContext, version, forwardRef, useState, useCallback, useRef, useEffect, useContext, useMemo, useId, useLayoutEffect } from 'react';
14
14
  import { createPortal } from 'react-dom';
15
15
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
16
16
 
@@ -59,6 +59,62 @@ function useCloseSettle(open, elRef, onSettled) {
59
59
  };
60
60
  }, [open, elRef, cb]);
61
61
  }
62
+ var TOAST_INSET_VARS = {
63
+ "bottom-right": "--ds-toast-inset-bottom-right",
64
+ "bottom-left": "--ds-toast-inset-bottom-left",
65
+ "bottom-center": "--ds-toast-inset-bottom-center",
66
+ right: "--ds-toast-inset-right",
67
+ left: "--ds-toast-inset-left",
68
+ top: "--ds-toast-inset-top"
69
+ };
70
+ function besideRoom(room) {
71
+ return `min(${room}, calc(100% - 280px - var(--ds-space-4)))`;
72
+ }
73
+ var LAUNCHER_INSET_PRIORITY = 1;
74
+ var PANEL_INSET_PRIORITY = 2;
75
+ var NAVBAR_INSET_PRIORITY = 1;
76
+ var registry = /* @__PURE__ */ new WeakMap();
77
+ var seq = 0;
78
+ function claimsFor(target) {
79
+ let entry = registry.get(target);
80
+ if (!entry) {
81
+ entry = { "bottom-right": [], "bottom-left": [], "bottom-center": [], right: [], left: [], top: [] };
82
+ registry.set(target, entry);
83
+ }
84
+ return entry;
85
+ }
86
+ function winner(claims) {
87
+ let best = null;
88
+ for (const c of claims) {
89
+ if (!best || c.priority > best.priority || c.priority === best.priority && c.seq > best.seq) best = c;
90
+ }
91
+ return best;
92
+ }
93
+ function apply(target, corner) {
94
+ const style = target.style;
95
+ if (!style) return;
96
+ const best = winner(claimsFor(target)[corner]);
97
+ if (best) style.setProperty(TOAST_INSET_VARS[corner], best.inset);
98
+ else style.removeProperty(TOAST_INSET_VARS[corner]);
99
+ }
100
+ function claimCornerInset(corner, inset, priority, target) {
101
+ const claim = { inset, priority, seq: ++seq };
102
+ const list = claimsFor(target)[corner];
103
+ list.push(claim);
104
+ apply(target, corner);
105
+ return () => {
106
+ const i = list.indexOf(claim);
107
+ if (i >= 0) list.splice(i, 1);
108
+ apply(target, corner);
109
+ };
110
+ }
111
+ function useCornerInset(corner, inset, priority, target) {
112
+ useEffect(() => {
113
+ if (!corner || !inset) return;
114
+ const el = target ?? document.documentElement;
115
+ return claimCornerInset(corner, inset, priority, el);
116
+ }, [corner, inset, priority, target]);
117
+ }
62
118
  var STEP = 16;
63
119
  var BIG_STEP = 64;
64
120
  function clamp(v, min, max) {
@@ -409,7 +465,7 @@ function Drawer(props) {
409
465
  const { position = "right", mobile, open, onClose, footer, dirty, onDiscard } = props;
410
466
  const shellWidth = useContext(DrawerWidthCtx) ?? void 0;
411
467
  const isPhone = useBreakpoint("sm");
412
- const mobileMode = mobile ?? (position === "left" ? "panel" : "sheet");
468
+ const mobileMode = mobile ?? (position === "left" && props.modal !== false ? "panel" : "sheet");
413
469
  const { marked, mark } = useOverlayMark(open);
414
470
  const guard = useDirtyGuard(dirty ?? marked, onClose, onDiscard);
415
471
  const confirm = /* @__PURE__ */ jsx(DiscardConfirm, { open: guard.confirming, onKeep: guard.keep, onDiscard: guard.discard });
@@ -489,6 +545,26 @@ function DrawerPanel({ form, open, onClose, position = "right", width = "400px",
489
545
  return () => cancelAnimationFrame(raf);
490
546
  }, [open, mounted, nonModal]);
491
547
  useNonModalDialog(panelRef, open && mounted && nonModal, onClose, isTopLayer);
548
+ const holdsRoom = open && mounted && nonModal && !sheet;
549
+ const horizontal = position === "left" || position === "right";
550
+ const [panelHeight, setPanelHeight] = useState(null);
551
+ useEffect(() => {
552
+ const el = panelRef.current;
553
+ if (!holdsRoom || horizontal || !el || typeof ResizeObserver === "undefined") return;
554
+ const ro = new ResizeObserver(([entry]) => {
555
+ const height = (entry?.target ?? el).offsetHeight;
556
+ setPanelHeight(height > 0 ? height : null);
557
+ });
558
+ ro.observe(el);
559
+ return () => ro.disconnect();
560
+ }, [holdsRoom, horizontal]);
561
+ const claimTarget = container instanceof Element ? container : null;
562
+ const sideRoom = holdsRoom && horizontal ? besideRoom(`calc(${effectiveWidth} + var(--ds-space-4))`) : null;
563
+ const bottomRoom = holdsRoom && !horizontal && panelHeight ? `calc(${panelHeight}px + var(--ds-space-3))` : null;
564
+ useCornerInset(sideRoom ? position : null, sideRoom, PANEL_INSET_PRIORITY, claimTarget);
565
+ useCornerInset(bottomRoom ? "bottom-right" : null, bottomRoom, PANEL_INSET_PRIORITY, claimTarget);
566
+ useCornerInset(bottomRoom ? "bottom-left" : null, bottomRoom, PANEL_INSET_PRIORITY, claimTarget);
567
+ useCornerInset(bottomRoom ? "bottom-center" : null, bottomRoom, PANEL_INSET_PRIORITY, claimTarget);
492
568
  const wasBlocking = useRef(blocking);
493
569
  useEffect(() => {
494
570
  const was = wasBlocking.current;
@@ -654,7 +730,7 @@ function DrawerPanel({ form, open, onClose, position = "right", width = "400px",
654
730
  minHeight: "calc(var(--ds-space-unit) * 6 + 1.75rem + 2px + var(--ds-border-width))"
655
731
  },
656
732
  children: [
657
- !sheet && titleTruncated ? /* @__PURE__ */ jsx(Tooltip, { content: title, children: /* @__PURE__ */ jsx("h2", { ref: titleOverflowRef, id: titleId, className: heading, children: title }) }) : /* @__PURE__ */ jsx("h2", { ref: titleOverflowRef, id: titleId, className: heading, children: title }),
733
+ !sheet && titleTruncated ? /* @__PURE__ */ jsx(AutoTooltip, { content: title, children: /* @__PURE__ */ jsx("h2", { ref: titleOverflowRef, id: titleId, className: heading, children: title }) }) : /* @__PURE__ */ jsx("h2", { ref: titleOverflowRef, id: titleId, className: heading, children: title }),
658
734
  /* @__PURE__ */ jsx(
659
735
  "button",
660
736
  {
@@ -820,12 +896,17 @@ var AppShell = forwardRef(function AppShell2({
820
896
  };
821
897
  }, [hasFooter, shellEl]);
822
898
  const hasLauncher = !!launcher && !isSmallMobile;
823
- const launcherProps = isValidElement(launcher) ? launcher.props : null;
824
- const launcherCorner = !hasLauncher ? null : launcherProps?.variant === "ribbon" ? null : launcherProps?.edge === "bottom-left" ? "bottom-left" : launcherProps?.edge === "right" || launcherProps?.edge === "left" || launcherProps?.edge === "bottom" ? null : "bottom-right";
899
+ const [launcherClaim, setLauncherClaim] = useState(null);
900
+ const registerLauncher = useCallback((corner) => {
901
+ const claim = { corner };
902
+ setLauncherClaim(claim);
903
+ return () => setLauncherClaim((c) => c === claim ? null : c);
904
+ }, []);
905
+ const launcherCorner = hasLauncher ? launcherClaim?.corner ?? null : null;
825
906
  return /* @__PURE__ */ jsx(PreferencesContext, { value: preferences ?? null, children: /* @__PURE__ */ jsx(DrawerWidthCtx, { value: drawerWidth, children: /* @__PURE__ */ jsx(
826
907
  AppShellContext,
827
908
  {
828
- value: { hasSidebar, hasMobileMenu, hasMobileNav, sidebarOpen, setSidebarOpen, isMobile, isTablet, sidebarCollapsed, setSidebarCollapsed: setUserCollapsed, asideOpen, setAsideOpen, asideSide, asideFloating, setAsideFloating, container: shellEl, theme: theme ?? null, isHome, navbarFloating, sidebarFloating, registerChrome, hasLauncher, launcherCorner, hasFooter, focus: focus ?? null, hasUnsavedChanges: unsavedCount > 0, addUnsavedChanges, drawerWidth, navigate: navigate ?? null, flowLeft: leftCount > 0, markFlowLeft },
909
+ value: { hasSidebar, hasMobileMenu, hasMobileNav, sidebarOpen, setSidebarOpen, isMobile, isTablet, sidebarCollapsed, setSidebarCollapsed: setUserCollapsed, asideOpen, setAsideOpen, asideSide, asideFloating, setAsideFloating, container: shellEl, theme: theme ?? null, isHome, navbarFloating, sidebarFloating, registerChrome, hasLauncher, launcherCorner, registerLauncher, hasFooter, focus: focus ?? null, hasUnsavedChanges: unsavedCount > 0, addUnsavedChanges, drawerWidth, navigate: navigate ?? null, flowLeft: leftCount > 0, markFlowLeft },
829
910
  children: /* @__PURE__ */ jsxs("div", { ref: (el) => {
830
911
  setShellEl(el);
831
912
  if (typeof ref === "function") ref(el);
@@ -835,7 +916,12 @@ var AppShell = forwardRef(function AppShell2({
835
916
  // with the toolbar hidden). Sprint 25.21 S1: h-dvh alone, which an app's own height class replaces through cn();
836
917
  // the supports-[…] variant that stood beside it survived that merge and beat the app's class (the builder's
837
918
  // preview ran under its footer). Every browser that draws the themes' oklch() colours has dvh.
838
- "relative h-dvh flex flex-col bg-[var(--ds-bg-canvas)] overflow-hidden",
919
+ // Sprint 25.8.7 item 31: clipped, not hidden. A box that hides its overflow is still a scroll container, which
920
+ // the browser scrolls to show a focused element past its edge: on run 73's roles page at 390 px it had moved
921
+ // 424 px after 15 Tab stops, and the next 50 stops sat in a blank band below <main>. Nothing can scroll a
922
+ // clipped box. The page scrolls in <main>, never here, and holds what it positions (below), so in a browser
923
+ // without overflow: clip (Safari 15) nothing of the library's runs past this box to need clipping.
924
+ "relative h-dvh flex flex-col bg-[var(--ds-bg-canvas)] overflow-clip",
839
925
  className
840
926
  ), children: [
841
927
  skipLink && /* @__PURE__ */ jsx(SkipLink, { href: `#${mainId}` }),
@@ -855,8 +941,12 @@ var AppShell = forwardRef(function AppShell2({
855
941
  "data-shell-main": "",
856
942
  tabIndex: -1,
857
943
  className: cn(
858
- "flex-1 min-h-0 focus:outline-none",
859
- scrollable ? "overflow-y-auto" : "relative",
944
+ // Sprint 25.8.7 item 31: <main> is the page's containing block, so what the page positions with no
945
+ // positioned box of its own (a Checkbox's visually hidden input) scrolls with the page, and Tab scrolls
946
+ // <main> to it. Placed against the shell instead, it stayed where the unscrolled page had put it, out of
947
+ // <main>'s reach, and focus scrolled the shell's outer box to show it
948
+ "relative flex-1 min-h-0 focus:outline-none",
949
+ scrollable && "overflow-y-auto",
860
950
  layout === "multi-column" && "flex"
861
951
  ),
862
952
  style: { containerType: "inline-size" },
@@ -1215,6 +1305,6 @@ function useElementWidth(ref) {
1215
1305
  return width;
1216
1306
  }
1217
1307
 
1218
- export { AppShell, Breadcrumbs, DiscardConfirm, Drawer, DrawerFooterCtx, DrawerPanel, OVERLAY_ROOT_ATTR, OverlayCloseCtx, OverlayMarkCtx, OverlayMarkScope, REACT_VERSION_PROBLEM, ResizeEdge, SkipLink, crumbGoes, defaultMaxWidth, insetLessScrollbar, pxOf, useAppShell, useBelow, useCloseSettle, useCompact, useCrumbFollow, useDirtyGuard, useElementWidth, useOverlayMark, useRememberedWidth, useScrollbarInset, useUnsavedChanges };
1219
- //# sourceMappingURL=chunk-OPFJEL3Z.js.map
1220
- //# sourceMappingURL=chunk-OPFJEL3Z.js.map
1308
+ export { AppShell, Breadcrumbs, DiscardConfirm, Drawer, DrawerFooterCtx, DrawerPanel, LAUNCHER_INSET_PRIORITY, NAVBAR_INSET_PRIORITY, OVERLAY_ROOT_ATTR, OverlayCloseCtx, OverlayMarkCtx, OverlayMarkScope, PANEL_INSET_PRIORITY, REACT_VERSION_PROBLEM, ResizeEdge, SkipLink, besideRoom, crumbGoes, defaultMaxWidth, insetLessScrollbar, pxOf, useAppShell, useBelow, useCloseSettle, useCompact, useCornerInset, useCrumbFollow, useDirtyGuard, useElementWidth, useOverlayMark, useRememberedWidth, useScrollbarInset, useUnsavedChanges };
1309
+ //# sourceMappingURL=chunk-UNS4TGGK.js.map
1310
+ //# sourceMappingURL=chunk-UNS4TGGK.js.map