@doneisbetter/gds-core 2.6.7 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1822 @@
1
+ import {
2
+ ActionBar,
3
+ FormField,
4
+ GdsIcons,
5
+ ListingCard,
6
+ NotificationCenterView,
7
+ ReferenceSection,
8
+ StateBlock,
9
+ getSemanticActionLabel,
10
+ resolveSemanticActionConfig
11
+ } from "./chunk-YP7RL2MC.mjs";
12
+
13
+ // src/SemanticButton.tsx
14
+ import { useEffect, useState } from "react";
15
+ import { Button } from "@mantine/core";
16
+ import { useGdsTranslation } from "@doneisbetter/gds-theme";
17
+ import { IconCheck, IconX } from "@tabler/icons-react";
18
+ import { jsx } from "react/jsx-runtime";
19
+ function SemanticButton({
20
+ action,
21
+ loading,
22
+ feedbackState,
23
+ feedbackText,
24
+ prerenderLabelOnly = true,
25
+ vocabularyPacks = [],
26
+ ...props
27
+ }) {
28
+ const { t } = useGdsTranslation();
29
+ const config = resolveSemanticActionConfig(action, vocabularyPacks);
30
+ const [mounted, setMounted] = useState(!prerenderLabelOnly);
31
+ const [internalFeedback, setInternalFeedback] = useState(null);
32
+ useEffect(() => {
33
+ if (prerenderLabelOnly) {
34
+ setMounted(true);
35
+ }
36
+ }, [prerenderLabelOnly]);
37
+ useEffect(() => {
38
+ if (feedbackState) {
39
+ setInternalFeedback(feedbackState);
40
+ const timer = setTimeout(() => setInternalFeedback(null), 2e3);
41
+ return () => clearTimeout(timer);
42
+ }
43
+ }, [feedbackState]);
44
+ let Icon = config.icon;
45
+ let label = getSemanticActionLabel(action, t, vocabularyPacks);
46
+ let color = props.color;
47
+ if (!mounted) {
48
+ const { leftSection, ...buttonProps } = props;
49
+ return /* @__PURE__ */ jsx(Button, { loading, color, ...buttonProps, children: getSemanticActionLabel(action, void 0, vocabularyPacks) });
50
+ }
51
+ if (internalFeedback === "success") {
52
+ const defaultFeedback = config.feedback ?? { icon: IconCheck, color: "teal", messageId: "gds.feedback.saved" };
53
+ Icon = defaultFeedback.icon;
54
+ label = feedbackText || t(defaultFeedback.messageId, "Success");
55
+ color = defaultFeedback.color;
56
+ } else if (internalFeedback === "error") {
57
+ Icon = IconX;
58
+ label = feedbackText || t("gds.feedback.error", "Something went wrong");
59
+ color = "red";
60
+ }
61
+ return /* @__PURE__ */ jsx(
62
+ Button,
63
+ {
64
+ leftSection: /* @__PURE__ */ jsx(Icon, { size: "1rem" }),
65
+ loading,
66
+ color,
67
+ ...props,
68
+ children: label
69
+ }
70
+ );
71
+ }
72
+
73
+ // src/ConfirmDialog.tsx
74
+ import { Modal, Group, Text } from "@mantine/core";
75
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
76
+ function ConfirmDialog({
77
+ opened,
78
+ onClose,
79
+ onConfirm,
80
+ title,
81
+ children,
82
+ confirmAction = "confirm",
83
+ cancelAction = "cancel",
84
+ isDanger = true,
85
+ loading = false
86
+ }) {
87
+ return /* @__PURE__ */ jsxs(Modal, { opened, onClose, title, centered: true, trapFocus: true, children: [
88
+ /* @__PURE__ */ jsx2(Text, { size: "sm", mb: "xl", children }),
89
+ /* @__PURE__ */ jsxs(Group, { justify: "flex-end", children: [
90
+ /* @__PURE__ */ jsx2(SemanticButton, { action: cancelAction, variant: "default", onClick: onClose, disabled: loading }),
91
+ /* @__PURE__ */ jsx2(SemanticButton, { action: confirmAction, color: isDanger ? "red" : "violet", onClick: onConfirm, loading })
92
+ ] })
93
+ ] });
94
+ }
95
+
96
+ // src/ThemeToggle.tsx
97
+ import { ActionIcon, useMantineColorScheme, useComputedColorScheme } from "@mantine/core";
98
+ import { useGdsTranslation as useGdsTranslation2 } from "@doneisbetter/gds-theme";
99
+ import { jsx as jsx3 } from "react/jsx-runtime";
100
+ function ThemeToggle({ size = "md", onColorSchemeChange }) {
101
+ const { setColorScheme } = useMantineColorScheme();
102
+ const computedColorScheme = useComputedColorScheme("light", { getInitialValueInEffect: true });
103
+ const { t } = useGdsTranslation2();
104
+ const toggleColorScheme = () => {
105
+ const nextScheme = computedColorScheme === "dark" ? "light" : "dark";
106
+ setColorScheme(nextScheme);
107
+ onColorSchemeChange?.(nextScheme);
108
+ };
109
+ const isDark = computedColorScheme === "dark";
110
+ return /* @__PURE__ */ jsx3(
111
+ ActionIcon,
112
+ {
113
+ onClick: toggleColorScheme,
114
+ variant: "default",
115
+ size,
116
+ "aria-label": t("gds.aria.themeToggle", "Toggle color scheme"),
117
+ children: isDark ? /* @__PURE__ */ jsx3(GdsIcons.Sun, { size: "1.2rem" }) : /* @__PURE__ */ jsx3(GdsIcons.Moon, { size: "1.2rem" })
118
+ }
119
+ );
120
+ }
121
+
122
+ // src/ReferenceThemeExplorer.tsx
123
+ import { useEffect as useEffect2, useMemo, useState as useState2 } from "react";
124
+ import {
125
+ Badge,
126
+ Button as Button2,
127
+ Checkbox,
128
+ Code,
129
+ Group as Group2,
130
+ NativeSelect,
131
+ Paper,
132
+ SimpleGrid,
133
+ Stack,
134
+ Text as Text2,
135
+ TextInput,
136
+ Title
137
+ } from "@mantine/core";
138
+ import {
139
+ GdsProvider,
140
+ applyGdsFontLane,
141
+ getGdsFontLanes,
142
+ getGdsThemePresets,
143
+ resolveGdsThemePreset
144
+ } from "@doneisbetter/gds-theme";
145
+ import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
146
+ function resolvePreviewColorScheme(presetId, requestedScheme) {
147
+ if (presetId === "dark-public" || presetId === "neon-night") {
148
+ return "dark";
149
+ }
150
+ return requestedScheme;
151
+ }
152
+ var presetCatalog = getGdsThemePresets();
153
+ var themePresetCatalog = Object.fromEntries(
154
+ presetCatalog.map((preset) => [
155
+ preset.id,
156
+ {
157
+ label: preset.label,
158
+ bestFor: `Apps aligned with ${preset.label.toLowerCase()}.`,
159
+ summary: preset.description,
160
+ themeKey: preset.runtimeLane,
161
+ supportedUse: `General product adoption with ${preset.label}.`,
162
+ avoidFor: "Avoid creating a local non-canonical theme authority."
163
+ }
164
+ ])
165
+ );
166
+ var colorSchemeProof = [
167
+ {
168
+ id: "light",
169
+ label: "Light",
170
+ description: "Validates readable default surfaces, controls, badges, and focus states against light backgrounds."
171
+ },
172
+ {
173
+ id: "dark",
174
+ label: "Dark",
175
+ description: "Validates contrast for public, operational, and feedback surfaces when dark mode is active."
176
+ },
177
+ {
178
+ id: "auto",
179
+ label: "Auto",
180
+ description: "Documents the adopter path for OS-controlled schemes while keeping the provider contract unchanged."
181
+ }
182
+ ];
183
+ function ThemePreviewSurface({
184
+ preset,
185
+ colorScheme,
186
+ requestedColorScheme
187
+ }) {
188
+ const forcedScheme = requestedColorScheme && requestedColorScheme !== colorScheme;
189
+ return /* @__PURE__ */ jsx4(Paper, { withBorder: true, radius: "xl", p: "lg", children: /* @__PURE__ */ jsxs2(Stack, { gap: "lg", children: [
190
+ /* @__PURE__ */ jsxs2(Group2, { justify: "space-between", align: "flex-start", wrap: "wrap", children: [
191
+ /* @__PURE__ */ jsxs2(Stack, { gap: 4, children: [
192
+ /* @__PURE__ */ jsx4(Text2, { fw: 700, children: preset.label }),
193
+ /* @__PURE__ */ jsx4(Text2, { size: "sm", c: "dimmed", children: preset.summary }),
194
+ /* @__PURE__ */ jsxs2(Text2, { size: "sm", children: [
195
+ /* @__PURE__ */ jsx4("strong", { children: "Best for:" }),
196
+ " ",
197
+ preset.bestFor
198
+ ] }),
199
+ /* @__PURE__ */ jsxs2(Text2, { size: "sm", children: [
200
+ /* @__PURE__ */ jsx4("strong", { children: "Color scheme:" }),
201
+ " ",
202
+ colorScheme
203
+ ] }),
204
+ /* @__PURE__ */ jsxs2(Text2, { size: "sm", children: [
205
+ /* @__PURE__ */ jsx4("strong", { children: "Accessibility proof:" }),
206
+ " status uses text, badge label, and placement, not color alone."
207
+ ] }),
208
+ forcedScheme ? /* @__PURE__ */ jsx4(Text2, { size: "sm", c: "dimmed", children: "This lane always previews in dark mode so the runtime stays inside its sanctioned contrast contract." }) : null
209
+ ] }),
210
+ /* @__PURE__ */ jsx4(ThemeToggle, {})
211
+ ] }),
212
+ /* @__PURE__ */ jsx4(
213
+ ActionBar,
214
+ {
215
+ primary: { action: "save", size: "sm" },
216
+ secondary: [{ action: "cancel", size: "sm" }],
217
+ tertiary: [{ action: "preview", size: "sm" }]
218
+ }
219
+ ),
220
+ /* @__PURE__ */ jsxs2(SimpleGrid, { cols: { base: 1, md: 2 }, spacing: "md", children: [
221
+ /* @__PURE__ */ jsx4(Paper, { withBorder: true, radius: "lg", p: "md", children: /* @__PURE__ */ jsxs2(Stack, { gap: "sm", children: [
222
+ /* @__PURE__ */ jsx4(Text2, { fw: 700, size: "sm", children: "Token-backed controls" }),
223
+ /* @__PURE__ */ jsx4(FormField, { label: "Reference input", description: "Inputs stay inside the theme lane instead of drifting into route-local styling.", children: /* @__PURE__ */ jsx4(TextInput, { placeholder: "Search or type a route name" }) }),
224
+ /* @__PURE__ */ jsxs2(Group2, { gap: "xs", wrap: "wrap", children: [
225
+ /* @__PURE__ */ jsx4(Badge, { color: "teal", variant: "light", children: "Success" }),
226
+ /* @__PURE__ */ jsx4(Badge, { color: "orange", variant: "light", children: "Warning" }),
227
+ /* @__PURE__ */ jsx4(Badge, { color: "red", variant: "light", children: "Critical" })
228
+ ] })
229
+ ] }) }),
230
+ /* @__PURE__ */ jsx4(
231
+ ListingCard,
232
+ {
233
+ title: "Reference site proof surface",
234
+ description: "This preview uses the real shipped design-system runtime rather than a docs-only styling lane.",
235
+ metadata: [
236
+ { id: "runtime", label: "Runtime lane", value: preset.themeKey },
237
+ { id: "scheme", label: "Color scheme", value: colorScheme },
238
+ { id: "focus", label: "A11y proof", value: "Keyboard + readable states" }
239
+ ],
240
+ primaryAction: /* @__PURE__ */ jsx4(Button2, { size: "sm", children: "Inspect route" })
241
+ }
242
+ )
243
+ ] })
244
+ ] }) });
245
+ }
246
+ function ReferenceThemeExplorer({
247
+ initialSelection,
248
+ onSelectionChange
249
+ }) {
250
+ const [preset, setPreset] = useState2(initialSelection?.preset ?? "default");
251
+ const [colorScheme, setColorScheme] = useState2(initialSelection?.colorScheme ?? "light");
252
+ const [brandPrimary, setBrandPrimary] = useState2(initialSelection?.brandPrimary ?? "blue");
253
+ const [brandFlatSurfaces, setBrandFlatSurfaces] = useState2(initialSelection?.brandFlatSurfaces ?? true);
254
+ const [brandEditorialSerif, setBrandEditorialSerif] = useState2(initialSelection?.brandEditorialSerif ?? false);
255
+ const [fontLane, setFontLane] = useState2(initialSelection?.fontLane ?? "inter");
256
+ const [comparisonEnabled, setComparisonEnabled] = useState2(false);
257
+ const [comparisonPreset, setComparisonPreset] = useState2("editorial");
258
+ const availableComparisonPresets = presetCatalog.filter((item) => item.id !== preset).map((item) => item.id);
259
+ const resolveTheme = (presetId) => applyGdsFontLane(resolveGdsThemePreset(presetId, {
260
+ brandPrimary,
261
+ brandFlatSurfaces,
262
+ brandEditorialSerif
263
+ }), fontLane);
264
+ const selectionSummary = themePresetCatalog[preset];
265
+ const comparisonSummary = themePresetCatalog[comparisonPreset];
266
+ const selectedTheme = useMemo(() => resolveTheme(preset), [preset, brandPrimary, brandFlatSurfaces, brandEditorialSerif, fontLane]);
267
+ const comparedTheme = useMemo(() => resolveTheme(comparisonPreset), [comparisonPreset, brandPrimary, brandFlatSurfaces, brandEditorialSerif, fontLane]);
268
+ const effectiveColorScheme = resolvePreviewColorScheme(preset, colorScheme);
269
+ const effectiveComparisonScheme = resolvePreviewColorScheme(comparisonPreset, colorScheme);
270
+ const previewKey = `${preset}-${effectiveColorScheme}-${brandPrimary}-${brandFlatSurfaces}-${brandEditorialSerif}-${fontLane}`;
271
+ const comparisonPreviewKey = `${comparisonPreset}-${effectiveComparisonScheme}-${brandPrimary}-${brandFlatSurfaces}-${brandEditorialSerif}-${fontLane}`;
272
+ useEffect2(() => {
273
+ onSelectionChange?.({
274
+ preset,
275
+ colorScheme: effectiveColorScheme,
276
+ theme: selectedTheme,
277
+ fontLane,
278
+ runtimeKey: previewKey,
279
+ brandPrimary,
280
+ brandFlatSurfaces,
281
+ brandEditorialSerif
282
+ });
283
+ }, [onSelectionChange, preset, effectiveColorScheme, selectedTheme, fontLane, previewKey, brandPrimary, brandFlatSurfaces, brandEditorialSerif]);
284
+ useEffect2(() => {
285
+ if (comparisonPreset !== preset) {
286
+ return;
287
+ }
288
+ setComparisonPreset(availableComparisonPresets[0] ?? "editorial");
289
+ }, [comparisonPreset, preset, availableComparisonPresets]);
290
+ const reset = () => {
291
+ setPreset("default");
292
+ setColorScheme("light");
293
+ setBrandPrimary("blue");
294
+ setBrandFlatSurfaces(true);
295
+ setBrandEditorialSerif(false);
296
+ setComparisonEnabled(false);
297
+ setComparisonPreset("editorial");
298
+ setFontLane("inter");
299
+ };
300
+ return /* @__PURE__ */ jsxs2(Stack, { gap: "xl", children: [
301
+ /* @__PURE__ */ jsx4(
302
+ ReferenceSection,
303
+ {
304
+ title: "Theme Lab",
305
+ description: "Test the actual shipped GDS theme presets, color-scheme behavior, and the governed brand-theme generator. This page is part of the live demo, not a separate styling experiment.",
306
+ children: /* @__PURE__ */ jsxs2(SimpleGrid, { cols: { base: 1, md: 2, xl: 3 }, spacing: "lg", children: [
307
+ /* @__PURE__ */ jsx4(Paper, { withBorder: true, radius: "xl", p: "lg", children: /* @__PURE__ */ jsxs2(Stack, { gap: "md", children: [
308
+ /* @__PURE__ */ jsx4(Title, { order: 4, children: "Theme preset" }),
309
+ /* @__PURE__ */ jsx4(FormField, { label: "Preset", children: /* @__PURE__ */ jsx4(
310
+ NativeSelect,
311
+ {
312
+ "aria-label": "Preset",
313
+ value: preset,
314
+ onChange: (event) => setPreset(event.currentTarget.value || "default"),
315
+ data: Object.entries(themePresetCatalog).map(([value, item]) => ({ value, label: item.label }))
316
+ }
317
+ ) }),
318
+ /* @__PURE__ */ jsx4(FormField, { label: "Preview color scheme", children: /* @__PURE__ */ jsx4(
319
+ NativeSelect,
320
+ {
321
+ "aria-label": "Preview color scheme",
322
+ value: colorScheme,
323
+ onChange: (event) => setColorScheme(event.currentTarget.value || "light"),
324
+ data: [
325
+ { value: "light", label: "Light" },
326
+ { value: "dark", label: "Dark" },
327
+ { value: "auto", label: "Auto" }
328
+ ]
329
+ }
330
+ ) }),
331
+ /* @__PURE__ */ jsx4(FormField, { label: "Webfont lane", children: /* @__PURE__ */ jsx4(
332
+ NativeSelect,
333
+ {
334
+ "aria-label": "Webfont lane",
335
+ value: fontLane,
336
+ onChange: (event) => setFontLane(event.currentTarget.value || "inter"),
337
+ data: getGdsFontLanes().map((lane) => ({ value: lane.id, label: lane.label }))
338
+ }
339
+ ) }),
340
+ /* @__PURE__ */ jsx4(Button2, { variant: "default", size: "sm", onClick: reset, children: "Reset theme lab" }),
341
+ /* @__PURE__ */ jsx4(Text2, { size: "sm", c: "dimmed", children: "Theme changes apply to the official site shell so visitors can validate real whole-page runtime behavior." })
342
+ ] }) }),
343
+ /* @__PURE__ */ jsx4(Paper, { withBorder: true, radius: "xl", p: "lg", children: /* @__PURE__ */ jsxs2(Stack, { gap: "md", children: [
344
+ /* @__PURE__ */ jsx4(Title, { order: 4, children: "Brand builder options" }),
345
+ /* @__PURE__ */ jsx4(FormField, { label: "Brand primary color", children: /* @__PURE__ */ jsx4(
346
+ NativeSelect,
347
+ {
348
+ "aria-label": "Brand primary color",
349
+ value: brandPrimary,
350
+ onChange: (event) => setBrandPrimary(event.currentTarget.value || "blue"),
351
+ data: ["blue", "violet", "teal", "grape", "indigo", "orange"],
352
+ disabled: preset !== "brand"
353
+ }
354
+ ) }),
355
+ /* @__PURE__ */ jsx4(
356
+ Checkbox,
357
+ {
358
+ label: "Use flat surfaces",
359
+ checked: brandFlatSurfaces,
360
+ onChange: (event) => setBrandFlatSurfaces(event.currentTarget.checked),
361
+ disabled: preset !== "brand"
362
+ }
363
+ ),
364
+ /* @__PURE__ */ jsx4(
365
+ Checkbox,
366
+ {
367
+ label: "Use editorial serif headings",
368
+ checked: brandEditorialSerif,
369
+ onChange: (event) => setBrandEditorialSerif(event.currentTarget.checked),
370
+ disabled: preset !== "brand"
371
+ }
372
+ ),
373
+ /* @__PURE__ */ jsx4(Text2, { size: "sm", c: "dimmed", children: "The generator composes shipped helpers instead of creating a second theme authority inside the website." })
374
+ ] }) }),
375
+ /* @__PURE__ */ jsx4(Paper, { withBorder: true, radius: "xl", p: "lg", children: /* @__PURE__ */ jsxs2(Stack, { gap: "md", role: "status", "aria-live": "polite", children: [
376
+ /* @__PURE__ */ jsx4(Title, { order: 4, children: "Current selection summary" }),
377
+ /* @__PURE__ */ jsxs2(Stack, { gap: 6, children: [
378
+ /* @__PURE__ */ jsx4(Text2, { fw: 700, children: selectionSummary.label }),
379
+ /* @__PURE__ */ jsx4(Text2, { size: "sm", c: "dimmed", children: selectionSummary.summary }),
380
+ /* @__PURE__ */ jsxs2(Text2, { size: "sm", children: [
381
+ /* @__PURE__ */ jsx4("strong", { children: "Best for:" }),
382
+ " ",
383
+ selectionSummary.bestFor
384
+ ] }),
385
+ /* @__PURE__ */ jsxs2(Text2, { size: "sm", children: [
386
+ /* @__PURE__ */ jsx4("strong", { children: "Runtime lane:" }),
387
+ " ",
388
+ /* @__PURE__ */ jsx4(Code, { children: selectionSummary.themeKey })
389
+ ] }),
390
+ /* @__PURE__ */ jsxs2(Text2, { size: "sm", children: [
391
+ /* @__PURE__ */ jsx4("strong", { children: "Color scheme:" }),
392
+ " ",
393
+ colorScheme
394
+ ] }),
395
+ (preset === "dark-public" || preset === "neon-night") && colorScheme !== effectiveColorScheme ? /* @__PURE__ */ jsx4(Text2, { size: "sm", c: "dimmed", children: "This dark-forward preset always renders in dark mode inside the live preview." }) : null
396
+ ] }),
397
+ /* @__PURE__ */ jsx4(
398
+ Checkbox,
399
+ {
400
+ "aria-label": "Compare against a second shipped preset",
401
+ label: "Compare against a second shipped preset",
402
+ checked: comparisonEnabled,
403
+ onChange: (event) => setComparisonEnabled(event.currentTarget.checked)
404
+ }
405
+ ),
406
+ /* @__PURE__ */ jsx4(FormField, { label: "Comparison preset", children: /* @__PURE__ */ jsx4(
407
+ NativeSelect,
408
+ {
409
+ "aria-label": "Comparison preset",
410
+ value: comparisonPreset,
411
+ onChange: (event) => setComparisonPreset(event.currentTarget.value || "editorial"),
412
+ disabled: !comparisonEnabled,
413
+ data: availableComparisonPresets.map((value) => ({ value, label: themePresetCatalog[value].label }))
414
+ }
415
+ ) })
416
+ ] }) })
417
+ ] })
418
+ }
419
+ ),
420
+ /* @__PURE__ */ jsx4(
421
+ ReferenceSection,
422
+ {
423
+ title: "Shipped theme lanes",
424
+ description: "Every lane below is part of the supported system. The website uses these exact helpers as its live runtime proof.",
425
+ children: /* @__PURE__ */ jsx4(SimpleGrid, { cols: { base: 1, md: 2, xl: 5 }, spacing: "md", children: Object.values(themePresetCatalog).map((lane) => /* @__PURE__ */ jsx4(Paper, { withBorder: true, radius: "lg", p: "md", children: /* @__PURE__ */ jsxs2(Stack, { gap: 6, children: [
426
+ /* @__PURE__ */ jsx4(Text2, { fw: 700, size: "sm", children: lane.label }),
427
+ /* @__PURE__ */ jsx4(Text2, { size: "sm", c: "dimmed", children: lane.summary }),
428
+ /* @__PURE__ */ jsxs2(Text2, { size: "xs", children: [
429
+ /* @__PURE__ */ jsx4("strong", { children: "Best for:" }),
430
+ " ",
431
+ lane.supportedUse
432
+ ] }),
433
+ /* @__PURE__ */ jsx4(Code, { block: true, fz: "10px", children: lane.themeKey }),
434
+ /* @__PURE__ */ jsxs2(Text2, { size: "xs", c: "dimmed", children: [
435
+ /* @__PURE__ */ jsx4("strong", { children: "Avoid for:" }),
436
+ " ",
437
+ lane.avoidFor ?? "No special exclusion noted for this lane."
438
+ ] })
439
+ ] }) }, lane.themeKey)) })
440
+ }
441
+ ),
442
+ /* @__PURE__ */ jsx4(
443
+ ReferenceSection,
444
+ {
445
+ title: "Light, dark, and auto proof",
446
+ description: "Every official lane must remain usable across explicit light, explicit dark, and OS-controlled auto modes. The dark-public lane is intentionally forced to dark in preview to preserve its contrast contract.",
447
+ children: /* @__PURE__ */ jsx4(SimpleGrid, { cols: { base: 1, md: 3 }, spacing: "md", children: colorSchemeProof.map((item) => /* @__PURE__ */ jsx4(Paper, { withBorder: true, radius: "lg", p: "md", children: /* @__PURE__ */ jsxs2(Stack, { gap: 6, children: [
448
+ /* @__PURE__ */ jsx4(Badge, { variant: "light", color: item.id === "dark" ? "violet" : item.id === "auto" ? "teal" : "blue", w: "fit-content", children: item.label }),
449
+ /* @__PURE__ */ jsx4(Text2, { size: "sm", children: item.description }),
450
+ /* @__PURE__ */ jsx4(Text2, { size: "xs", c: "dimmed", children: "Required proof: semantic text, visible focus, and contrast-safe state treatment." })
451
+ ] }) }, item.id)) })
452
+ }
453
+ ),
454
+ /* @__PURE__ */ jsx4(
455
+ ReferenceSection,
456
+ {
457
+ title: "Live Theme Preview",
458
+ description: "Visitors can test the shipped presets, compare lanes, and inspect actual GDS surfaces under each theme.",
459
+ children: /* @__PURE__ */ jsxs2(SimpleGrid, { cols: { base: 1, xl: comparisonEnabled ? 2 : 1 }, spacing: "lg", children: [
460
+ /* @__PURE__ */ jsx4(GdsProvider, { theme: selectedTheme, defaultColorScheme: effectiveColorScheme, children: /* @__PURE__ */ jsx4(
461
+ ThemePreviewSurface,
462
+ {
463
+ preset: selectionSummary,
464
+ colorScheme: effectiveColorScheme,
465
+ requestedColorScheme: colorScheme
466
+ }
467
+ ) }, previewKey),
468
+ comparisonEnabled ? /* @__PURE__ */ jsx4(GdsProvider, { theme: comparedTheme, defaultColorScheme: effectiveComparisonScheme, children: /* @__PURE__ */ jsx4(Paper, { withBorder: true, radius: "xl", p: "lg", children: /* @__PURE__ */ jsxs2(Stack, { gap: "md", children: [
469
+ /* @__PURE__ */ jsxs2(Group2, { justify: "space-between", align: "flex-start", wrap: "wrap", children: [
470
+ /* @__PURE__ */ jsxs2(Stack, { gap: 4, children: [
471
+ /* @__PURE__ */ jsx4(Text2, { fw: 700, children: "Comparison Preview Surface" }),
472
+ /* @__PURE__ */ jsx4(Text2, { size: "sm", c: "dimmed", children: "Compare another shipped theme against the current selection before adopting it downstream." })
473
+ ] }),
474
+ /* @__PURE__ */ jsx4(Badge, { color: "violet", variant: "light", children: comparisonSummary.label })
475
+ ] }),
476
+ /* @__PURE__ */ jsx4(
477
+ ThemePreviewSurface,
478
+ {
479
+ preset: comparisonSummary,
480
+ colorScheme: effectiveComparisonScheme,
481
+ requestedColorScheme: colorScheme
482
+ }
483
+ )
484
+ ] }) }) }, comparisonPreviewKey) : null
485
+ ] })
486
+ }
487
+ ),
488
+ /* @__PURE__ */ jsx4(
489
+ ReferenceSection,
490
+ {
491
+ title: "Unsupported lane boundary",
492
+ description: "Unsupported local theme lanes are blocked by policy and compliance because they create parallel design-system authority.",
493
+ tone: "supporting",
494
+ children: /* @__PURE__ */ jsxs2(Stack, { gap: "md", children: [
495
+ /* @__PURE__ */ jsx4(
496
+ StateBlock,
497
+ {
498
+ variant: "permission",
499
+ title: "Do not create local branding-layer helpers",
500
+ description: "If a consumer needs brand expression, use createPublicBrandTheme(...). If a lane is missing, request it for GDS instead of building extendGdsTheme(...), createTheme(...), or mergeMantineTheme(...) ownership locally.",
501
+ compact: true
502
+ }
503
+ ),
504
+ /* @__PURE__ */ jsxs2(SimpleGrid, { cols: { base: 1, md: 2 }, spacing: "md", children: [
505
+ /* @__PURE__ */ jsx4(Paper, { withBorder: true, radius: "lg", p: "md", children: /* @__PURE__ */ jsxs2(Stack, { gap: 6, children: [
506
+ /* @__PURE__ */ jsx4(Text2, { fw: 700, size: "sm", children: "Approved remediation" }),
507
+ /* @__PURE__ */ jsxs2(Code, { block: true, children: [
508
+ "createPublicBrandTheme(",
509
+ `{ flatSurfaces: true, overrides: { primaryColor: 'blue' } }`,
510
+ ")"
511
+ ] })
512
+ ] }) }),
513
+ /* @__PURE__ */ jsx4(Paper, { withBorder: true, radius: "lg", p: "md", children: /* @__PURE__ */ jsxs2(Stack, { gap: 6, children: [
514
+ /* @__PURE__ */ jsx4(Text2, { fw: 700, size: "sm", children: "Prohibited ownership" }),
515
+ /* @__PURE__ */ jsx4(Code, { block: true, children: "extendGdsTheme(...) / createTheme(...) / mergeMantineTheme(...)" })
516
+ ] }) })
517
+ ] })
518
+ ] })
519
+ }
520
+ )
521
+ ] });
522
+ }
523
+
524
+ // src/GameBoardTile.tsx
525
+ import { Center, Paper as Paper2, Text as Text3, UnstyledButton, useMantineTheme } from "@mantine/core";
526
+ import { useMediaQuery } from "@mantine/hooks";
527
+ import { jsx as jsx5 } from "react/jsx-runtime";
528
+ function GameBoardTile({
529
+ face,
530
+ revealed,
531
+ matched,
532
+ disabled,
533
+ onPress,
534
+ highlightColor
535
+ }) {
536
+ const theme = useMantineTheme();
537
+ const reduceMotion = useMediaQuery("(prefers-reduced-motion: reduce)");
538
+ const highlighted = revealed && !matched;
539
+ const revealBg = highlightColor ?? (typeof theme.primaryColor === "string" ? `${theme.primaryColor}.5` : "violet.5");
540
+ return /* @__PURE__ */ jsx5(UnstyledButton, { w: "100%", disabled, onClick: onPress, "aria-pressed": revealed, children: /* @__PURE__ */ jsx5(
541
+ Paper2,
542
+ {
543
+ withBorder: true,
544
+ radius: "md",
545
+ p: "md",
546
+ bg: revealed ? revealBg : "dark.6",
547
+ styles: {
548
+ root: {
549
+ aspectRatio: "1",
550
+ opacity: matched ? 0.55 : 1,
551
+ cursor: disabled ? "not-allowed" : "pointer",
552
+ transition: reduceMotion ? "opacity 0.2s ease" : "transform 0.25s ease, background-color 0.25s ease, opacity 0.25s ease",
553
+ transform: reduceMotion || !highlighted ? "scale(1)" : "scale(1.02)"
554
+ }
555
+ },
556
+ children: /* @__PURE__ */ jsx5(Center, { h: "100%", children: /* @__PURE__ */ jsx5(Text3, { size: "xl", fw: 700, children: face }) })
557
+ }
558
+ ) });
559
+ }
560
+
561
+ // src/ListingState.client.tsx
562
+ import { createContext, useContext, useMemo as useMemo2, useReducer } from "react";
563
+ import { jsx as jsx6 } from "react/jsx-runtime";
564
+ function createInitialState(config = {}) {
565
+ return {
566
+ search: "",
567
+ sort: config.defaultSort ?? "newest",
568
+ filters: [],
569
+ page: 1,
570
+ pageSize: config.defaultPageSize ?? 25,
571
+ selection: []
572
+ };
573
+ }
574
+ function listingQueryReducer(state, action) {
575
+ if (action.type === "set-search") {
576
+ return { ...state, search: action.value, page: 1, selection: [] };
577
+ }
578
+ if (action.type === "set-sort") {
579
+ return { ...state, sort: action.value, page: 1, selection: [] };
580
+ }
581
+ if (action.type === "toggle-filter") {
582
+ const exists = state.filters.includes(action.value);
583
+ return {
584
+ ...state,
585
+ filters: exists ? state.filters.filter((item) => item !== action.value) : [...state.filters, action.value],
586
+ page: 1,
587
+ selection: []
588
+ };
589
+ }
590
+ if (action.type === "clear-filters") {
591
+ return { ...state, filters: [], page: 1, selection: [] };
592
+ }
593
+ if (action.type === "set-page") {
594
+ return { ...state, page: Math.max(1, Math.floor(action.value)) };
595
+ }
596
+ if (action.type === "set-page-size") {
597
+ const nextPageSize = Math.max(1, Math.floor(action.value));
598
+ return { ...state, pageSize: nextPageSize, page: 1, selection: [] };
599
+ }
600
+ if (action.type === "toggle-selection") {
601
+ const exists = state.selection.includes(action.value);
602
+ return {
603
+ ...state,
604
+ selection: exists ? state.selection.filter((item) => item !== action.value) : [...state.selection, action.value]
605
+ };
606
+ }
607
+ if (action.type === "clear-selection") {
608
+ return { ...state, selection: [] };
609
+ }
610
+ return createInitialState({ defaultSort: state.sort, defaultPageSize: state.pageSize });
611
+ }
612
+ var ListingStateContext = createContext(null);
613
+ function ListingProvider({
614
+ children,
615
+ config
616
+ }) {
617
+ const [state, dispatch] = useReducer(listingQueryReducer, config, createInitialState);
618
+ const value = useMemo2(() => ({ state, dispatch }), [state]);
619
+ return /* @__PURE__ */ jsx6(ListingStateContext.Provider, { value, children });
620
+ }
621
+ function useListingState() {
622
+ const context = useContext(ListingStateContext);
623
+ if (!context) {
624
+ throw new Error("useListingState must be used within ListingProvider.");
625
+ }
626
+ return context;
627
+ }
628
+
629
+ // src/DiscoveryShell.tsx
630
+ import { useEffect as useEffect3, useState as useState3 } from "react";
631
+ import { AppShell as MantineAppShell, Box, Burger, Group as Group3, ScrollArea } from "@mantine/core";
632
+ import { useMediaQuery as useMediaQuery2 } from "@mantine/hooks";
633
+ import { jsx as jsx7, jsxs as jsxs3 } from "react/jsx-runtime";
634
+ function useDiscoveryShellState({
635
+ defaultSidebarOpen = false,
636
+ sidebarStorageKey,
637
+ onSidebarOpenedChange
638
+ } = {}) {
639
+ const [opened, setOpenedState] = useState3(defaultSidebarOpen);
640
+ useEffect3(() => {
641
+ if (!sidebarStorageKey || typeof window === "undefined") {
642
+ return;
643
+ }
644
+ const stored = window.localStorage.getItem(sidebarStorageKey);
645
+ if (stored === "open") {
646
+ setOpenedState(true);
647
+ }
648
+ if (stored === "closed") {
649
+ setOpenedState(false);
650
+ }
651
+ }, [sidebarStorageKey]);
652
+ const persistAndNotify = (next) => {
653
+ if (sidebarStorageKey && typeof window !== "undefined") {
654
+ window.localStorage.setItem(sidebarStorageKey, next ? "open" : "closed");
655
+ }
656
+ onSidebarOpenedChange?.(next);
657
+ };
658
+ const setOpened = (next) => {
659
+ setOpenedState(next);
660
+ persistAndNotify(next);
661
+ };
662
+ const open = () => setOpened(true);
663
+ const close = () => setOpened(false);
664
+ const toggle = () => {
665
+ setOpenedState((current) => {
666
+ const next = !current;
667
+ persistAndNotify(next);
668
+ return next;
669
+ });
670
+ };
671
+ return { opened, open, close, toggle, setOpened };
672
+ }
673
+ function DiscoveryShell({
674
+ header,
675
+ sidebar,
676
+ footer,
677
+ children,
678
+ mobileNavigationLabel = "Toggle navigation",
679
+ defaultSidebarOpen = false,
680
+ sidebarStorageKey,
681
+ sidebarOpened,
682
+ onSidebarOpenedChange,
683
+ stickySidebar = true,
684
+ desktopCollapsible = false,
685
+ desktopNavigationLabel = "Toggle sidebar",
686
+ sidebarWidth = 280,
687
+ headerHeight = 60,
688
+ shellPadding = "md",
689
+ collapseBreakpoint = "sm"
690
+ }) {
691
+ const breakpointByAlias = {
692
+ xs: "36em",
693
+ sm: "48em",
694
+ md: "62em",
695
+ lg: "75em",
696
+ xl: "88em"
697
+ };
698
+ const isMobile = useMediaQuery2(`(max-width: ${breakpointByAlias[collapseBreakpoint]})`);
699
+ const shellState = useDiscoveryShellState({ defaultSidebarOpen, sidebarStorageKey, onSidebarOpenedChange });
700
+ const opened = sidebarOpened ?? shellState.opened;
701
+ const close = onSidebarOpenedChange ? () => onSidebarOpenedChange(false) : shellState.close;
702
+ const toggle = onSidebarOpenedChange ? () => onSidebarOpenedChange(!opened) : shellState.toggle;
703
+ return /* @__PURE__ */ jsxs3(
704
+ MantineAppShell,
705
+ {
706
+ header: { height: headerHeight },
707
+ footer: footer ? { height: 68 } : void 0,
708
+ navbar: {
709
+ width: sidebarWidth,
710
+ breakpoint: collapseBreakpoint,
711
+ collapsed: {
712
+ mobile: !opened,
713
+ desktop: desktopCollapsible ? !opened : false
714
+ }
715
+ },
716
+ padding: shellPadding,
717
+ children: [
718
+ /* @__PURE__ */ jsx7(MantineAppShell.Header, { children: /* @__PURE__ */ jsxs3(Group3, { h: "100%", px: "md", gap: "sm", wrap: "nowrap", children: [
719
+ /* @__PURE__ */ jsx7(
720
+ Burger,
721
+ {
722
+ opened,
723
+ onClick: toggle,
724
+ hiddenFrom: collapseBreakpoint,
725
+ size: "sm",
726
+ "aria-label": mobileNavigationLabel
727
+ }
728
+ ),
729
+ desktopCollapsible ? /* @__PURE__ */ jsx7(
730
+ Burger,
731
+ {
732
+ opened,
733
+ onClick: toggle,
734
+ visibleFrom: collapseBreakpoint,
735
+ size: "sm",
736
+ "aria-label": desktopNavigationLabel
737
+ }
738
+ ) : null,
739
+ /* @__PURE__ */ jsx7(Box, { style: { flex: 1, minWidth: 0 }, children: header })
740
+ ] }) }),
741
+ /* @__PURE__ */ jsx7(MantineAppShell.Navbar, { p: "md", "data-sticky-sidebar": stickySidebar || void 0, children: /* @__PURE__ */ jsx7(ScrollArea, { h: "100%", type: "auto", children: /* @__PURE__ */ jsx7(
742
+ Box,
743
+ {
744
+ h: "100%",
745
+ style: stickySidebar ? {
746
+ display: "flex",
747
+ flexDirection: "column"
748
+ } : void 0,
749
+ children: sidebar
750
+ }
751
+ ) }) }),
752
+ footer ? /* @__PURE__ */ jsx7(MantineAppShell.Footer, { children: /* @__PURE__ */ jsx7(Group3, { h: "100%", px: "md", justify: "space-around", wrap: "nowrap", children: footer }) }) : null,
753
+ /* @__PURE__ */ jsx7(
754
+ MantineAppShell.Main,
755
+ {
756
+ onClick: () => {
757
+ if (isMobile) {
758
+ close();
759
+ }
760
+ },
761
+ children
762
+ }
763
+ )
764
+ ]
765
+ }
766
+ );
767
+ }
768
+
769
+ // src/DocsShell.tsx
770
+ import { useState as useState4 } from "react";
771
+ import { Box as Box2, Burger as Burger2, Container, Divider, Group as Group4, Stack as Stack2, Text as Text4, Transition } from "@mantine/core";
772
+ import { Fragment, jsx as jsx8, jsxs as jsxs4 } from "react/jsx-runtime";
773
+ function DocsShellSidebar({ primaryNavigation, secondaryNavigation }) {
774
+ return /* @__PURE__ */ jsxs4(Stack2, { gap: "md", h: "100%", children: [
775
+ primaryNavigation ? /* @__PURE__ */ jsxs4(Stack2, { gap: "xs", children: [
776
+ /* @__PURE__ */ jsx8(Text4, { size: "xs", fw: 700, c: "dimmed", children: "Primary" }),
777
+ primaryNavigation
778
+ ] }) : null,
779
+ secondaryNavigation ? /* @__PURE__ */ jsxs4(Fragment, { children: [
780
+ /* @__PURE__ */ jsx8(Divider, {}),
781
+ /* @__PURE__ */ jsxs4(Stack2, { gap: "xs", children: [
782
+ /* @__PURE__ */ jsx8(Text4, { size: "xs", fw: 700, c: "dimmed", children: "More" }),
783
+ secondaryNavigation
784
+ ] })
785
+ ] }) : null
786
+ ] });
787
+ }
788
+ function resolveContentContainerSize(value) {
789
+ if (value === "full") {
790
+ return "100%";
791
+ }
792
+ return value ?? "lg";
793
+ }
794
+ function DocsShell({
795
+ brand,
796
+ primaryNavigation,
797
+ secondaryNavigation,
798
+ headerContext,
799
+ actions,
800
+ mobileNavigation,
801
+ mobileNavigationMode = "drawer",
802
+ footer,
803
+ children,
804
+ contentWidth = "full"
805
+ }) {
806
+ const [mobileNavOpen, setMobileNavOpen] = useState4(false);
807
+ return /* @__PURE__ */ jsx8(
808
+ DiscoveryShell,
809
+ {
810
+ header: /* @__PURE__ */ jsxs4(Group4, { h: "100%", justify: "space-between", align: "center", wrap: "nowrap", gap: "md", children: [
811
+ /* @__PURE__ */ jsxs4(Group4, { gap: "sm", align: "center", wrap: "nowrap", style: { minWidth: 0 }, children: [
812
+ mobileNavigationMode === "inline-collapse" && mobileNavigation ? /* @__PURE__ */ jsxs4(Fragment, { children: [
813
+ /* @__PURE__ */ jsx8(
814
+ Burger2,
815
+ {
816
+ opened: mobileNavOpen,
817
+ onClick: () => setMobileNavOpen((value) => !value),
818
+ hiddenFrom: "sm",
819
+ size: "sm",
820
+ "aria-label": "Toggle docs navigation"
821
+ }
822
+ ),
823
+ /* @__PURE__ */ jsx8(Transition, { mounted: mobileNavOpen, transition: "pop", duration: 120, children: (styles) => /* @__PURE__ */ jsx8(Box2, { style: styles, children: /* @__PURE__ */ jsx8(Box2, { mt: "xs", p: "sm", style: { borderRadius: 8, border: "1px solid var(--mantine-color-default-border)" }, children: mobileNavigation }) }) })
824
+ ] }) : null,
825
+ /* @__PURE__ */ jsx8(Box2, { style: { minWidth: 0 }, children: brand }),
826
+ headerContext ? /* @__PURE__ */ jsx8(Text4, { size: "sm", c: "dimmed", lineClamp: 1, children: headerContext }) : null
827
+ ] }),
828
+ /* @__PURE__ */ jsx8(Group4, { gap: "sm", wrap: "nowrap", style: { minWidth: 0 }, children: actions })
829
+ ] }),
830
+ sidebar: /* @__PURE__ */ jsx8(
831
+ DocsShellSidebar,
832
+ {
833
+ primaryNavigation,
834
+ secondaryNavigation
835
+ }
836
+ ),
837
+ footer,
838
+ mobileNavigationLabel: "Open docs navigation",
839
+ headerHeight: 72,
840
+ children: /* @__PURE__ */ jsx8(Container, { size: resolveContentContainerSize(contentWidth), px: "md", py: "lg", w: "100%", children })
841
+ }
842
+ );
843
+ }
844
+
845
+ // src/SidebarNav.tsx
846
+ import { forwardRef } from "react";
847
+ import { Box as Box3, NavLink, Stack as Stack3, Text as Text5, createPolymorphicComponent } from "@mantine/core";
848
+ import { useGdsTranslation as useGdsTranslation3 } from "@doneisbetter/gds-theme";
849
+ import { jsx as jsx9, jsxs as jsxs5 } from "react/jsx-runtime";
850
+ function SidebarNav({ children, ariaLabel = "Primary navigation", gap = "md" }) {
851
+ return /* @__PURE__ */ jsx9(Stack3, { component: "nav", "aria-label": ariaLabel, gap, h: "100%", children });
852
+ }
853
+ function SidebarNavSection({ label, children, pushToBottom = false }) {
854
+ return /* @__PURE__ */ jsxs5(Stack3, { gap: "xs", mt: pushToBottom ? "auto" : void 0, children: [
855
+ label ? /* @__PURE__ */ jsx9(Text5, { size: "xs", fw: 700, c: "dimmed", children: label }) : null,
856
+ /* @__PURE__ */ jsx9(Stack3, { gap: 4, children })
857
+ ] });
858
+ }
859
+ var _SidebarNavItem = forwardRef(
860
+ ({
861
+ action,
862
+ label,
863
+ description,
864
+ badge,
865
+ icon,
866
+ "aria-label": ariaLabel,
867
+ "aria-current": ariaCurrent,
868
+ vocabularyPacks = [],
869
+ ...props
870
+ }, ref) => {
871
+ const { t } = useGdsTranslation3();
872
+ const config = action ? resolveSemanticActionConfig(action, vocabularyPacks) : null;
873
+ const Icon = config?.icon;
874
+ const resolvedLabel = label ?? (action ? getSemanticActionLabel(action, t, vocabularyPacks) : void 0);
875
+ return /* @__PURE__ */ jsx9(
876
+ NavLink,
877
+ {
878
+ ref,
879
+ label: resolvedLabel,
880
+ description,
881
+ leftSection: icon ?? (Icon ? /* @__PURE__ */ jsx9(Icon, { size: "1rem", stroke: 1.5 }) : void 0),
882
+ rightSection: badge ? /* @__PURE__ */ jsx9(Box3, { children: badge }) : props.rightSection,
883
+ "aria-label": ariaLabel ?? (typeof resolvedLabel === "string" ? resolvedLabel : void 0),
884
+ "aria-current": props.active ? "page" : ariaCurrent,
885
+ ...props
886
+ }
887
+ );
888
+ }
889
+ );
890
+ var SidebarNavItem = createPolymorphicComponent(_SidebarNavItem);
891
+
892
+ // src/DocsCodeBlock.tsx
893
+ import { useState as useState5 } from "react";
894
+ import { ActionIcon as ActionIcon2, Code as Code2, Group as Group5, Paper as Paper3, Stack as Stack4, Text as Text6 } from "@mantine/core";
895
+ import { jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
896
+ function DocsCodeBlock({ code, language, title, withCopy = true }) {
897
+ const [copied, setCopied] = useState5(false);
898
+ const handleCopy = async () => {
899
+ if (!navigator?.clipboard) {
900
+ return;
901
+ }
902
+ await navigator.clipboard.writeText(code);
903
+ setCopied(true);
904
+ window.setTimeout(() => setCopied(false), 1200);
905
+ };
906
+ return /* @__PURE__ */ jsx10(Paper3, { withBorder: true, radius: "lg", p: "md", children: /* @__PURE__ */ jsxs6(Stack4, { gap: "sm", children: [
907
+ title || withCopy ? /* @__PURE__ */ jsxs6(Group5, { justify: "space-between", align: "center", children: [
908
+ /* @__PURE__ */ jsxs6(Stack4, { gap: 0, children: [
909
+ title ? /* @__PURE__ */ jsx10(Text6, { fw: 600, children: title }) : null,
910
+ language ? /* @__PURE__ */ jsx10(Text6, { size: "xs", c: "dimmed", children: language }) : null
911
+ ] }),
912
+ withCopy ? /* @__PURE__ */ jsx10(
913
+ ActionIcon2,
914
+ {
915
+ variant: "subtle",
916
+ "aria-label": copied ? "Copied code block" : "Copy code block",
917
+ onClick: () => {
918
+ void handleCopy();
919
+ },
920
+ children: /* @__PURE__ */ jsx10(GdsIcons.Copy, { size: "1rem" })
921
+ }
922
+ ) : null
923
+ ] }) : null,
924
+ /* @__PURE__ */ jsx10(Code2, { block: true, children: code })
925
+ ] }) });
926
+ }
927
+
928
+ // src/ShareButtonGroup.tsx
929
+ import { useState as useState6 } from "react";
930
+ import { ActionIcon as ActionIcon3, Button as Button3, Group as Group6, Stack as Stack5, Text as Text7 } from "@mantine/core";
931
+ import { jsx as jsx11, jsxs as jsxs7 } from "react/jsx-runtime";
932
+ var channelLabels = {
933
+ native: "Share",
934
+ copy: "Copy link",
935
+ mail: "Email",
936
+ message: "Message",
937
+ x: "Share on X",
938
+ facebook: "Share on Facebook",
939
+ linkedin: "Share on LinkedIn",
940
+ whatsapp: "Share on WhatsApp",
941
+ telegram: "Share on Telegram"
942
+ };
943
+ function encodeShare(url, title, text) {
944
+ const safeUrl = encodeURIComponent(url);
945
+ const safeTitle = encodeURIComponent(title ?? "");
946
+ const safeText = encodeURIComponent(text ?? title ?? "");
947
+ return {
948
+ mail: `mailto:?subject=${safeTitle}&body=${safeText ? `${safeText}%0A%0A` : ""}${safeUrl}`,
949
+ message: `sms:?&body=${safeText ? `${safeText}%20` : ""}${safeUrl}`,
950
+ x: `https://x.com/intent/tweet?text=${safeText ? `${safeText}%20` : ""}${safeUrl}`,
951
+ facebook: `https://www.facebook.com/sharer/sharer.php?u=${safeUrl}`,
952
+ linkedin: `https://www.linkedin.com/sharing/share-offsite/?url=${safeUrl}`,
953
+ whatsapp: `https://wa.me/?text=${safeText ? `${safeText}%20` : ""}${safeUrl}`,
954
+ telegram: `https://t.me/share/url?url=${safeUrl}&text=${safeText}`
955
+ };
956
+ }
957
+ function ShareAction({
958
+ channel,
959
+ compact,
960
+ href,
961
+ onClick
962
+ }) {
963
+ const label = channelLabels[channel];
964
+ const Icon = channel === "copy" ? GdsIcons.Copy : channel === "mail" ? GdsIcons.Mail : channel === "message" ? GdsIcons.Message : GdsIcons.Refer;
965
+ if (compact) {
966
+ return href ? /* @__PURE__ */ jsx11(ActionIcon3, { component: "a", href, target: "_blank", rel: "noreferrer", variant: "subtle", size: "lg", "aria-label": label, children: /* @__PURE__ */ jsx11(Icon, { size: "1rem", stroke: 1.75 }) }) : /* @__PURE__ */ jsx11(ActionIcon3, { variant: "subtle", size: "lg", "aria-label": label, onClick, children: /* @__PURE__ */ jsx11(Icon, { size: "1rem", stroke: 1.75 }) });
967
+ }
968
+ return href ? /* @__PURE__ */ jsx11(Button3, { component: "a", href, target: "_blank", rel: "noreferrer", variant: "default", leftSection: /* @__PURE__ */ jsx11(Icon, { size: "1rem", stroke: 1.75 }), children: label }) : /* @__PURE__ */ jsx11(Button3, { variant: "default", leftSection: /* @__PURE__ */ jsx11(Icon, { size: "1rem", stroke: 1.75 }), onClick, children: label });
969
+ }
970
+ function ShareButtonGroup({
971
+ url,
972
+ title,
973
+ text,
974
+ channels = ["native", "copy", "mail"],
975
+ compact = false,
976
+ label = "Share this",
977
+ description
978
+ }) {
979
+ const [copied, setCopied] = useState6(false);
980
+ const [shared, setShared] = useState6(false);
981
+ const hrefs = encodeShare(url, title, text);
982
+ async function handleCopy() {
983
+ await navigator.clipboard?.writeText?.(url);
984
+ setCopied(true);
985
+ setTimeout(() => setCopied(false), 1800);
986
+ }
987
+ async function handleNativeShare() {
988
+ if (navigator.share) {
989
+ await navigator.share({ url, title, text });
990
+ setShared(true);
991
+ setTimeout(() => setShared(false), 1800);
992
+ return;
993
+ }
994
+ await handleCopy();
995
+ }
996
+ return /* @__PURE__ */ jsxs7(Stack5, { gap: "sm", children: [
997
+ /* @__PURE__ */ jsxs7(Stack5, { gap: 2, children: [
998
+ /* @__PURE__ */ jsx11(Text7, { fw: 600, children: label }),
999
+ description ? /* @__PURE__ */ jsx11(Text7, { size: "sm", c: "dimmed", children: description }) : null
1000
+ ] }),
1001
+ /* @__PURE__ */ jsx11(Group6, { gap: "sm", wrap: "wrap", children: channels.map((channel) => {
1002
+ if (channel === "copy") {
1003
+ return /* @__PURE__ */ jsx11(ShareAction, { channel, compact, onClick: () => void handleCopy() }, channel);
1004
+ }
1005
+ if (channel === "native") {
1006
+ return /* @__PURE__ */ jsx11(ShareAction, { channel, compact, onClick: () => void handleNativeShare() }, channel);
1007
+ }
1008
+ return /* @__PURE__ */ jsx11(ShareAction, { channel, compact, href: hrefs[channel] }, channel);
1009
+ }) }),
1010
+ copied ? /* @__PURE__ */ jsx11(Text7, { size: "sm", c: "teal", children: "Link copied." }) : null,
1011
+ shared ? /* @__PURE__ */ jsx11(Text7, { size: "sm", c: "teal", children: "Share sheet opened." }) : null
1012
+ ] });
1013
+ }
1014
+
1015
+ // src/UploadDropzone.tsx
1016
+ import { useRef, useState as useState7 } from "react";
1017
+ import { Badge as Badge2, Box as Box4, Button as Button4, Group as Group7, Stack as Stack6, Text as Text8 } from "@mantine/core";
1018
+ import { jsx as jsx12, jsxs as jsxs8 } from "react/jsx-runtime";
1019
+ function UploadDropzone({
1020
+ title,
1021
+ description,
1022
+ onFilesSelected,
1023
+ accept,
1024
+ acceptedTypesLabel,
1025
+ maxSizeLabel,
1026
+ multiple = true,
1027
+ actionLabel = "Choose files",
1028
+ mode = "panel",
1029
+ state = "idle",
1030
+ selectedFiles = [],
1031
+ error,
1032
+ policyText,
1033
+ retryAction,
1034
+ removeAction,
1035
+ readonly = false
1036
+ }) {
1037
+ const inputRef = useRef(null);
1038
+ const [dragging, setDragging] = useState7(false);
1039
+ const UploadIcon = GdsIcons.Upload;
1040
+ const effectiveState = readonly ? "readonly" : dragging ? "drag-active" : state;
1041
+ const isDisabled = readonly || effectiveState === "upload-pending";
1042
+ const isError = ["upload-failed", "unsupported-type", "too-large"].includes(effectiveState);
1043
+ const forwardFiles = (files) => {
1044
+ if (isDisabled || !files?.length || !onFilesSelected) {
1045
+ return;
1046
+ }
1047
+ onFilesSelected(Array.from(files));
1048
+ };
1049
+ return /* @__PURE__ */ jsxs8(
1050
+ Box4,
1051
+ {
1052
+ onDragOver: (event) => {
1053
+ event.preventDefault();
1054
+ if (isDisabled) {
1055
+ return;
1056
+ }
1057
+ setDragging(true);
1058
+ },
1059
+ onDragLeave: () => setDragging(false),
1060
+ onDrop: (event) => {
1061
+ event.preventDefault();
1062
+ setDragging(false);
1063
+ forwardFiles(event.dataTransfer.files);
1064
+ },
1065
+ p: mode === "inline" ? "md" : "xl",
1066
+ style: {
1067
+ border: `1px dashed var(${effectiveState === "drag-active" ? "--mantine-color-violet-6" : isError ? "--mantine-color-red-6" : "--mantine-color-default-border"})`,
1068
+ borderRadius: "var(--mantine-radius-lg)",
1069
+ background: effectiveState === "drag-active" ? "var(--mantine-color-violet-light)" : "transparent"
1070
+ },
1071
+ "aria-invalid": isError || void 0,
1072
+ children: [
1073
+ /* @__PURE__ */ jsx12(
1074
+ "input",
1075
+ {
1076
+ ref: inputRef,
1077
+ type: "file",
1078
+ hidden: true,
1079
+ accept,
1080
+ multiple,
1081
+ disabled: isDisabled,
1082
+ onChange: (event) => forwardFiles(event.currentTarget.files)
1083
+ }
1084
+ ),
1085
+ /* @__PURE__ */ jsxs8(Stack6, { align: mode === "inline" ? "flex-start" : "center", ta: mode === "inline" ? "left" : "center", gap: "sm", children: [
1086
+ /* @__PURE__ */ jsx12(UploadIcon, { size: "1.5rem" }),
1087
+ /* @__PURE__ */ jsx12(Badge2, { variant: "light", color: isError ? "red" : effectiveState === "selected" ? "blue" : effectiveState === "upload-pending" ? "violet" : "gray", children: effectiveState.replace("-", " ") }),
1088
+ /* @__PURE__ */ jsx12(Text8, { fw: 600, children: title }),
1089
+ description ? /* @__PURE__ */ jsx12(Text8, { size: "sm", c: "dimmed", children: description }) : null,
1090
+ acceptedTypesLabel || maxSizeLabel ? /* @__PURE__ */ jsxs8(Group7, { gap: "xs", justify: mode === "inline" ? "flex-start" : "center", children: [
1091
+ acceptedTypesLabel ? /* @__PURE__ */ jsx12(Badge2, { variant: "outline", color: "gray", children: acceptedTypesLabel }) : null,
1092
+ maxSizeLabel ? /* @__PURE__ */ jsx12(Badge2, { variant: "outline", color: "gray", children: maxSizeLabel }) : null
1093
+ ] }) : null,
1094
+ selectedFiles.length ? /* @__PURE__ */ jsxs8(Text8, { size: "sm", children: [
1095
+ "Selected: ",
1096
+ selectedFiles.join(", ")
1097
+ ] }) : null,
1098
+ policyText ? /* @__PURE__ */ jsx12(Text8, { size: "sm", c: isError ? "red.7" : "dimmed", children: policyText }) : null,
1099
+ error ? /* @__PURE__ */ jsx12(Text8, { size: "sm", c: "red.7", role: "alert", children: error }) : null,
1100
+ /* @__PURE__ */ jsxs8(Group7, { children: [
1101
+ /* @__PURE__ */ jsx12(Button4, { variant: "light", onClick: () => inputRef.current?.click(), disabled: isDisabled, children: actionLabel }),
1102
+ retryAction,
1103
+ removeAction
1104
+ ] })
1105
+ ] })
1106
+ ]
1107
+ }
1108
+ );
1109
+ }
1110
+
1111
+ // src/AccessRecoveryPanel.tsx
1112
+ import { Group as Group8 } from "@mantine/core";
1113
+ import { useGdsTranslation as useGdsTranslation4 } from "@doneisbetter/gds-theme";
1114
+ import { jsx as jsx13 } from "react/jsx-runtime";
1115
+ var stateBlockVariantByState = {
1116
+ unauthenticated: "permission",
1117
+ "expired-session": "info",
1118
+ timeout: "error",
1119
+ forbidden: "permission",
1120
+ missing: "error",
1121
+ unavailable: "error"
1122
+ };
1123
+ var defaultCopyByState = {
1124
+ unauthenticated: {
1125
+ title: "Sign in required",
1126
+ description: "Please sign in to continue to this content."
1127
+ },
1128
+ "expired-session": {
1129
+ title: "Session expired",
1130
+ description: "Sign in again or retry to continue where you left off."
1131
+ },
1132
+ timeout: {
1133
+ title: "Request timed out",
1134
+ description: "The recovery action took too long. Retry or choose a safe destination."
1135
+ },
1136
+ forbidden: {
1137
+ title: "You do not have access",
1138
+ description: "This content is outside your current permissions or scope."
1139
+ },
1140
+ missing: {
1141
+ title: "Content not found",
1142
+ description: "The resource may have moved, been deleted, or never existed in this scope."
1143
+ },
1144
+ unavailable: {
1145
+ title: "Content is temporarily unavailable",
1146
+ description: "Try again in a moment or return to a safe destination."
1147
+ }
1148
+ };
1149
+ function defaultActionsForState(state, {
1150
+ onRetry,
1151
+ onSignIn,
1152
+ onBack,
1153
+ supportAction
1154
+ }) {
1155
+ const signInAction = onSignIn ? { action: "login", onClick: onSignIn } : null;
1156
+ const retryAction = onRetry ? { action: "refresh", onClick: onRetry, variant: "light" } : null;
1157
+ const backAction = onBack ? { action: "back", onClick: onBack, variant: "default" } : null;
1158
+ switch (state) {
1159
+ case "unauthenticated":
1160
+ return { primary: signInAction, secondary: backAction, tertiary: supportAction ?? null };
1161
+ case "expired-session":
1162
+ return {
1163
+ primary: signInAction ?? retryAction,
1164
+ secondary: retryAction && signInAction ? retryAction : backAction,
1165
+ tertiary: supportAction ?? null
1166
+ };
1167
+ case "timeout":
1168
+ return {
1169
+ primary: retryAction ?? backAction,
1170
+ secondary: retryAction && backAction ? backAction : supportAction ?? null,
1171
+ tertiary: retryAction && backAction ? supportAction ?? null : null
1172
+ };
1173
+ case "forbidden":
1174
+ return { primary: backAction, secondary: supportAction ?? null, tertiary: null };
1175
+ case "missing":
1176
+ return { primary: backAction, secondary: supportAction ?? null, tertiary: null };
1177
+ case "unavailable":
1178
+ return {
1179
+ primary: retryAction ?? backAction,
1180
+ secondary: retryAction && backAction ? backAction : supportAction ?? null,
1181
+ tertiary: retryAction && backAction ? supportAction ?? null : null
1182
+ };
1183
+ }
1184
+ }
1185
+ function ActionGroup({
1186
+ primaryAction,
1187
+ secondaryAction,
1188
+ tertiaryAction
1189
+ }) {
1190
+ const actions = [primaryAction, secondaryAction, tertiaryAction].filter(Boolean);
1191
+ if (actions.length === 0) {
1192
+ return null;
1193
+ }
1194
+ return /* @__PURE__ */ jsx13(Group8, { gap: "sm", justify: "center", wrap: "wrap", children: actions.map((actionConfig, index) => /* @__PURE__ */ jsx13(
1195
+ SemanticButton,
1196
+ {
1197
+ action: actionConfig.action,
1198
+ onClick: actionConfig.onClick,
1199
+ loading: actionConfig.loading,
1200
+ disabled: actionConfig.disabled,
1201
+ color: actionConfig.color,
1202
+ variant: actionConfig.variant ?? (index === 0 ? "filled" : "default")
1203
+ },
1204
+ `${actionConfig.action}-${index}`
1205
+ )) });
1206
+ }
1207
+ function AccessRecoveryPanel({
1208
+ state,
1209
+ title,
1210
+ description,
1211
+ primaryAction,
1212
+ secondaryAction,
1213
+ tertiaryAction,
1214
+ onRetry,
1215
+ onSignIn,
1216
+ onBack,
1217
+ supportAction,
1218
+ compact = false
1219
+ }) {
1220
+ const { t } = useGdsTranslation4();
1221
+ const defaultCopy = defaultCopyByState[state];
1222
+ const defaults = defaultActionsForState(state, {
1223
+ onRetry,
1224
+ onSignIn,
1225
+ onBack,
1226
+ supportAction
1227
+ });
1228
+ return /* @__PURE__ */ jsx13(
1229
+ StateBlock,
1230
+ {
1231
+ variant: stateBlockVariantByState[state],
1232
+ compact,
1233
+ title: title ?? t(`gds.accessRecovery.${state}.title`, defaultCopy.title),
1234
+ description: description ?? t(`gds.accessRecovery.${state}.description`, defaultCopy.description),
1235
+ action: /* @__PURE__ */ jsx13(
1236
+ ActionGroup,
1237
+ {
1238
+ primaryAction: primaryAction ?? defaults.primary,
1239
+ secondaryAction: secondaryAction ?? defaults.secondary,
1240
+ tertiaryAction: tertiaryAction ?? defaults.tertiary
1241
+ }
1242
+ )
1243
+ }
1244
+ );
1245
+ }
1246
+
1247
+ // src/GdsForm.client.tsx
1248
+ import { createContext as createContext2, useContext as useContext2, useMemo as useMemo3, useReducer as useReducer2 } from "react";
1249
+ import { Alert, Anchor, Stack as Stack7, Text as Text9 } from "@mantine/core";
1250
+ import { jsx as jsx14 } from "react/jsx-runtime";
1251
+ function createFieldState(value) {
1252
+ return { value, touched: false, dirty: false };
1253
+ }
1254
+ function createSnapshot(values) {
1255
+ const fields = Object.entries(values).reduce((acc, [field, value]) => {
1256
+ acc[field] = createFieldState(value);
1257
+ return acc;
1258
+ }, {});
1259
+ return { fields, issues: [], submitState: "idle" };
1260
+ }
1261
+ function gdsFormReducer(state, action) {
1262
+ switch (action.type) {
1263
+ case "set-field": {
1264
+ const current = state.fields[action.field] ?? createFieldState("");
1265
+ return {
1266
+ ...state,
1267
+ fields: {
1268
+ ...state.fields,
1269
+ [action.field]: {
1270
+ value: action.value,
1271
+ touched: current.touched,
1272
+ dirty: true
1273
+ }
1274
+ }
1275
+ };
1276
+ }
1277
+ case "touch-field": {
1278
+ const current = state.fields[action.field] ?? createFieldState("");
1279
+ return {
1280
+ ...state,
1281
+ fields: {
1282
+ ...state.fields,
1283
+ [action.field]: { ...current, touched: true }
1284
+ }
1285
+ };
1286
+ }
1287
+ case "set-issues":
1288
+ return { ...state, issues: [...action.issues] };
1289
+ case "set-submit-state":
1290
+ return { ...state, submitState: action.submitState, submitError: action.submitError };
1291
+ case "reset":
1292
+ return createSnapshot(action.values ?? {});
1293
+ default:
1294
+ return state;
1295
+ }
1296
+ }
1297
+ function sortIssues(issues) {
1298
+ const weight = { blocking: 0, warning: 1, info: 2 };
1299
+ return [...issues].sort((a, b) => weight[a.severity] - weight[b.severity]);
1300
+ }
1301
+ function useGdsForm({
1302
+ initialValues,
1303
+ validate,
1304
+ validateAsync,
1305
+ onSubmit
1306
+ }) {
1307
+ const [snapshot, dispatch] = useReducer2(gdsFormReducer, initialValues, createSnapshot);
1308
+ const submit = async () => {
1309
+ dispatch({ type: "set-submit-state", submitState: "validating" });
1310
+ const syncIssues = sortIssues(validate ? validate(snapshot) : []);
1311
+ let mergedIssues = syncIssues;
1312
+ if (syncIssues.filter((issue) => issue.severity === "blocking").length === 0 && validateAsync) {
1313
+ const asyncIssues = sortIssues(await validateAsync(snapshot));
1314
+ mergedIssues = sortIssues([...syncIssues, ...asyncIssues]);
1315
+ }
1316
+ dispatch({ type: "set-issues", issues: mergedIssues });
1317
+ if (mergedIssues.some((issue) => issue.severity === "blocking")) {
1318
+ dispatch({ type: "set-submit-state", submitState: "error", submitError: "Please resolve blocking validation issues." });
1319
+ return false;
1320
+ }
1321
+ dispatch({ type: "set-submit-state", submitState: "submitting" });
1322
+ try {
1323
+ await onSubmit(
1324
+ Object.entries(snapshot.fields).reduce((acc, [field, state]) => {
1325
+ acc[field] = state.value;
1326
+ return acc;
1327
+ }, {})
1328
+ );
1329
+ dispatch({ type: "set-submit-state", submitState: "success" });
1330
+ return true;
1331
+ } catch (error) {
1332
+ dispatch({
1333
+ type: "set-submit-state",
1334
+ submitState: "error",
1335
+ submitError: error instanceof Error ? error.message : "Submission failed."
1336
+ });
1337
+ return false;
1338
+ }
1339
+ };
1340
+ return useMemo3(
1341
+ () => ({
1342
+ snapshot,
1343
+ setFieldValue: (field, value) => dispatch({ type: "set-field", field, value }),
1344
+ touchField: (field) => dispatch({ type: "touch-field", field }),
1345
+ submit,
1346
+ retrySubmit: submit
1347
+ }),
1348
+ [snapshot]
1349
+ );
1350
+ }
1351
+ var GdsFormContext = createContext2(null);
1352
+ function GdsFormProvider({ snapshot, children }) {
1353
+ return /* @__PURE__ */ jsx14(GdsFormContext.Provider, { value: { snapshot }, children });
1354
+ }
1355
+ function useGdsFormSnapshot() {
1356
+ const context = useContext2(GdsFormContext);
1357
+ if (!context) {
1358
+ throw new Error("useGdsFormSnapshot must be used within GdsFormProvider.");
1359
+ }
1360
+ return context.snapshot;
1361
+ }
1362
+ function FormErrorSummary({ title = "Please review the following issues." }) {
1363
+ const snapshot = useGdsFormSnapshot();
1364
+ const blocking = snapshot.issues.filter((issue) => issue.severity === "blocking");
1365
+ if (blocking.length === 0) {
1366
+ return null;
1367
+ }
1368
+ return /* @__PURE__ */ jsx14(Alert, { color: "red", title, children: /* @__PURE__ */ jsx14(Stack7, { gap: 4, children: blocking.map((issue) => /* @__PURE__ */ jsx14(Anchor, { href: `#${issue.field}`, children: issue.message }, `${issue.field}-${issue.message}`)) }) });
1369
+ }
1370
+ function ValidatedFieldMessage({ field }) {
1371
+ const snapshot = useGdsFormSnapshot();
1372
+ const issue = snapshot.issues.find((item) => item.field === field && item.severity === "blocking");
1373
+ if (!issue) {
1374
+ return null;
1375
+ }
1376
+ return /* @__PURE__ */ jsx14(Text9, { size: "xs", c: "red.7", id: `${field}-error`, children: issue.message });
1377
+ }
1378
+
1379
+ // src/AdvancedDataTable.client.tsx
1380
+ import { useMemo as useMemo4, useState as useState8 } from "react";
1381
+ import { Checkbox as Checkbox2, Group as Group9, ScrollArea as ScrollArea2, SegmentedControl, Stack as Stack8, Table, Text as Text10 } from "@mantine/core";
1382
+ import { jsx as jsx15, jsxs as jsxs9 } from "react/jsx-runtime";
1383
+ function compareValues(a, b) {
1384
+ if (typeof a === "number" && typeof b === "number") {
1385
+ return a - b;
1386
+ }
1387
+ return String(a).localeCompare(String(b), void 0, { numeric: true, sensitivity: "base" });
1388
+ }
1389
+ function AdvancedDataTable({
1390
+ rows,
1391
+ columns,
1392
+ rowId,
1393
+ loading = false,
1394
+ error = null,
1395
+ density: densityProp,
1396
+ stickyHeader = true,
1397
+ stickyHeaderOffset = 0,
1398
+ selectedRowIds,
1399
+ onSelectedRowIdsChange,
1400
+ sortBy: sortByProp,
1401
+ sortDirection: sortDirectionProp,
1402
+ onSortChange,
1403
+ responsiveFallback = "stacked-cards"
1404
+ }) {
1405
+ const [densityState, setDensityState] = useState8(densityProp ?? "comfortable");
1406
+ const [sortState, setSortState] = useState8({ key: sortByProp ?? null, direction: sortDirectionProp ?? "asc" });
1407
+ const [selectionState, setSelectionState] = useState8([]);
1408
+ const density = densityProp ?? densityState;
1409
+ const sortBy = sortByProp ?? sortState.key;
1410
+ const sortDirection = sortDirectionProp ?? sortState.direction;
1411
+ const selection = selectedRowIds ?? selectionState;
1412
+ const sortedRows = useMemo4(() => {
1413
+ if (!sortBy) {
1414
+ return rows;
1415
+ }
1416
+ const column = columns.find((item) => item.key === sortBy);
1417
+ if (!column) {
1418
+ return rows;
1419
+ }
1420
+ const next = [...rows].sort((left, right) => {
1421
+ const leftValue = column.sortAccessor ? column.sortAccessor(left) : String(left[column.key] ?? "");
1422
+ const rightValue = column.sortAccessor ? column.sortAccessor(right) : String(right[column.key] ?? "");
1423
+ const result = compareValues(leftValue, rightValue);
1424
+ return sortDirection === "asc" ? result : -result;
1425
+ });
1426
+ return next;
1427
+ }, [rows, columns, sortBy, sortDirection]);
1428
+ const allIds = useMemo4(() => sortedRows.map((row, index) => rowId(row, index)), [sortedRows, rowId]);
1429
+ const allSelected = allIds.length > 0 && allIds.every((id) => selection.includes(id));
1430
+ if (error) {
1431
+ return /* @__PURE__ */ jsx15(StateBlock, { variant: "error", title: "Unable to load table", description: error, compact: true });
1432
+ }
1433
+ if (loading) {
1434
+ return /* @__PURE__ */ jsx15(StateBlock, { variant: "loading", title: "Loading table", description: "Preparing enterprise data grid.", compact: true });
1435
+ }
1436
+ if (!rows.length) {
1437
+ return /* @__PURE__ */ jsx15(
1438
+ StateBlock,
1439
+ {
1440
+ variant: "empty",
1441
+ title: "No rows available",
1442
+ description: "Adjust filters or broaden scope to populate this table.",
1443
+ compact: true
1444
+ }
1445
+ );
1446
+ }
1447
+ const horizontalSpacing = density === "compact" ? "xs" : "md";
1448
+ const verticalSpacing = density === "compact" ? 6 : 10;
1449
+ return /* @__PURE__ */ jsxs9(Stack8, { gap: "sm", children: [
1450
+ /* @__PURE__ */ jsxs9(Group9, { justify: "space-between", align: "center", children: [
1451
+ /* @__PURE__ */ jsxs9(Text10, { size: "sm", fw: 600, children: [
1452
+ rows.length,
1453
+ " rows"
1454
+ ] }),
1455
+ /* @__PURE__ */ jsx15(
1456
+ SegmentedControl,
1457
+ {
1458
+ size: "xs",
1459
+ value: density,
1460
+ onChange: (value) => setDensityState(value),
1461
+ data: [
1462
+ { label: "Compact", value: "compact" },
1463
+ { label: "Comfortable", value: "comfortable" }
1464
+ ]
1465
+ }
1466
+ )
1467
+ ] }),
1468
+ /* @__PURE__ */ jsx15(ScrollArea2, { children: /* @__PURE__ */ jsxs9(
1469
+ Table,
1470
+ {
1471
+ stickyHeader,
1472
+ stickyHeaderOffset,
1473
+ withTableBorder: true,
1474
+ highlightOnHover: true,
1475
+ striped: true,
1476
+ horizontalSpacing,
1477
+ verticalSpacing,
1478
+ children: [
1479
+ /* @__PURE__ */ jsx15(Table.Thead, { children: /* @__PURE__ */ jsxs9(Table.Tr, { children: [
1480
+ /* @__PURE__ */ jsx15(Table.Th, { children: /* @__PURE__ */ jsx15(
1481
+ Checkbox2,
1482
+ {
1483
+ "aria-label": "Select all rows",
1484
+ checked: allSelected,
1485
+ indeterminate: !allSelected && selection.length > 0,
1486
+ onChange: (event) => {
1487
+ const next = event.currentTarget.checked ? allIds : [];
1488
+ if (onSelectedRowIdsChange) {
1489
+ onSelectedRowIdsChange(next);
1490
+ } else {
1491
+ setSelectionState(next);
1492
+ }
1493
+ }
1494
+ }
1495
+ ) }),
1496
+ columns.map((column) => /* @__PURE__ */ jsx15(Table.Th, { style: column.width ? { width: column.width } : void 0, children: column.sortable ? /* @__PURE__ */ jsx15(
1497
+ "button",
1498
+ {
1499
+ type: "button",
1500
+ "aria-label": `Sort by ${column.label}`,
1501
+ onClick: () => {
1502
+ const nextDirection = sortBy === column.key && sortDirection === "asc" ? "desc" : "asc";
1503
+ if (onSortChange) {
1504
+ onSortChange(column.key, nextDirection);
1505
+ } else {
1506
+ setSortState({ key: column.key, direction: nextDirection });
1507
+ }
1508
+ },
1509
+ children: column.label
1510
+ }
1511
+ ) : column.label }, column.key))
1512
+ ] }) }),
1513
+ /* @__PURE__ */ jsx15(Table.Tbody, { children: sortedRows.map((row, index) => {
1514
+ const id = rowId(row, index);
1515
+ const checked = selection.includes(id);
1516
+ return /* @__PURE__ */ jsxs9(Table.Tr, { children: [
1517
+ /* @__PURE__ */ jsx15(Table.Td, { children: /* @__PURE__ */ jsx15(
1518
+ Checkbox2,
1519
+ {
1520
+ "aria-label": `Select row ${id}`,
1521
+ checked,
1522
+ onChange: () => {
1523
+ const next = checked ? selection.filter((item) => item !== id) : [...selection, id];
1524
+ if (onSelectedRowIdsChange) {
1525
+ onSelectedRowIdsChange(next);
1526
+ } else {
1527
+ setSelectionState(next);
1528
+ }
1529
+ }
1530
+ }
1531
+ ) }),
1532
+ columns.map((column) => /* @__PURE__ */ jsx15(Table.Td, { children: column.render ? column.render(row) : String(row[column.key] ?? "") }, column.key))
1533
+ ] }, id);
1534
+ }) })
1535
+ ]
1536
+ }
1537
+ ) }),
1538
+ responsiveFallback === "stacked-cards" ? /* @__PURE__ */ jsx15(Stack8, { gap: "xs", children: sortedRows.slice(0, 3).map((row, index) => {
1539
+ const id = rowId(row, index);
1540
+ return /* @__PURE__ */ jsx15(
1541
+ StateBlock,
1542
+ {
1543
+ variant: "info",
1544
+ compact: true,
1545
+ title: String(row[columns[0]?.key] ?? id),
1546
+ description: columns.slice(1, 3).map((column) => `${column.label}: ${String(row[column.key] ?? "")}`).join(" | ")
1547
+ },
1548
+ `card-${id}`
1549
+ );
1550
+ }) }) : null
1551
+ ] });
1552
+ }
1553
+
1554
+ // src/OverlayManager.client.tsx
1555
+ import { createContext as createContext3, useContext as useContext3, useMemo as useMemo5, useState as useState9 } from "react";
1556
+ import { jsx as jsx16 } from "react/jsx-runtime";
1557
+ var OverlayManagerContext = createContext3(null);
1558
+ function OverlayManagerProvider({ children }) {
1559
+ const [stack, setStack] = useState9([]);
1560
+ const value = useMemo5(() => ({
1561
+ stack,
1562
+ registerOverlay: (overlay) => {
1563
+ setStack((current) => {
1564
+ const without = current.filter((item) => item.id !== overlay.id);
1565
+ return [...without, overlay];
1566
+ });
1567
+ },
1568
+ unregisterOverlay: (id) => {
1569
+ setStack((current) => current.filter((item) => item.id !== id));
1570
+ },
1571
+ isTopMost: (id) => stack.length > 0 && stack[stack.length - 1]?.id === id,
1572
+ requestClose: (id, reason) => {
1573
+ if (stack.length === 0 || stack[stack.length - 1]?.id !== id) {
1574
+ return null;
1575
+ }
1576
+ return reason;
1577
+ }
1578
+ }), [stack]);
1579
+ return /* @__PURE__ */ jsx16(OverlayManagerContext.Provider, { value, children });
1580
+ }
1581
+ function useOverlayManager() {
1582
+ const context = useContext3(OverlayManagerContext);
1583
+ if (!context) {
1584
+ throw new Error("useOverlayManager must be used within OverlayManagerProvider.");
1585
+ }
1586
+ return context;
1587
+ }
1588
+
1589
+ // src/CommandPalette.client.tsx
1590
+ import { createContext as createContext4, useContext as useContext4, useEffect as useEffect4, useMemo as useMemo6, useState as useState10 } from "react";
1591
+ import { ActionIcon as ActionIcon4, Group as Group10, Modal as Modal2, Stack as Stack9, Text as Text11, TextInput as TextInput2 } from "@mantine/core";
1592
+ import { jsx as jsx17, jsxs as jsxs10 } from "react/jsx-runtime";
1593
+ var CommandPaletteContext = createContext4(null);
1594
+ function normalize(text) {
1595
+ return text.trim().toLowerCase();
1596
+ }
1597
+ function scoreCommand(command, query, recentUse) {
1598
+ const q = normalize(query);
1599
+ if (!q) {
1600
+ return 0;
1601
+ }
1602
+ const label = normalize(command.label);
1603
+ const keywords = command.keywords?.map(normalize) ?? [];
1604
+ const prefix = label.startsWith(q) ? 1 : 0;
1605
+ const keyword = keywords.some((item) => item.includes(q)) ? 1 : 0;
1606
+ const recent = recentUse[command.id] ? 1 : 0;
1607
+ return 0.6 * prefix + 0.3 * keyword + 0.1 * recent;
1608
+ }
1609
+ function CommandRegistryProvider({ children }) {
1610
+ const [commands, setCommands] = useState10([]);
1611
+ const [opened, setOpened] = useState10(false);
1612
+ const value = useMemo6(() => ({
1613
+ commands,
1614
+ registerCommands: (next) => setCommands(next),
1615
+ open: () => setOpened(true),
1616
+ close: () => setOpened(false)
1617
+ }), [commands]);
1618
+ return /* @__PURE__ */ jsxs10(CommandPaletteContext.Provider, { value, children: [
1619
+ children,
1620
+ /* @__PURE__ */ jsx17(CommandPalette, { opened, onClose: () => setOpened(false) })
1621
+ ] });
1622
+ }
1623
+ function useCommandLauncher() {
1624
+ const context = useContext4(CommandPaletteContext);
1625
+ if (!context) {
1626
+ throw new Error("useCommandLauncher must be used within CommandRegistryProvider.");
1627
+ }
1628
+ return {
1629
+ open: context.open,
1630
+ close: context.close,
1631
+ registerCommands: context.registerCommands
1632
+ };
1633
+ }
1634
+ function CommandPalette({ opened, onClose }) {
1635
+ const contextValue = useContext4(CommandPaletteContext);
1636
+ if (!contextValue) {
1637
+ throw new Error("CommandPalette must be used within CommandRegistryProvider.");
1638
+ }
1639
+ const registry = contextValue;
1640
+ const [query, setQuery] = useState10("");
1641
+ const [recentUse, setRecentUse] = useState10({});
1642
+ const enabledCommands = registry.commands.filter((command) => command.enabledWhen ? command.enabledWhen() : true);
1643
+ const filteredCommands = [...enabledCommands].map((command) => ({ command, score: scoreCommand(command, query, recentUse) })).filter(({ command, score }) => normalize(query) === "" || score > 0 || normalize(command.label).includes(normalize(query))).sort((a, b) => b.score - a.score).map((item) => item.command);
1644
+ useEffect4(() => {
1645
+ function onKeyDown(event) {
1646
+ if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === "k") {
1647
+ event.preventDefault();
1648
+ registry.open();
1649
+ }
1650
+ if (event.key === "Escape" && opened) {
1651
+ onClose();
1652
+ }
1653
+ }
1654
+ window.addEventListener("keydown", onKeyDown);
1655
+ return () => window.removeEventListener("keydown", onKeyDown);
1656
+ }, [registry, opened, onClose]);
1657
+ return /* @__PURE__ */ jsx17(Modal2, { opened, onClose, title: "Quick actions", centered: true, children: /* @__PURE__ */ jsxs10(Stack9, { gap: "sm", children: [
1658
+ /* @__PURE__ */ jsx17(
1659
+ TextInput2,
1660
+ {
1661
+ "aria-label": "Command search",
1662
+ placeholder: "Search commands",
1663
+ value: query,
1664
+ onChange: (event) => setQuery(event.currentTarget.value)
1665
+ }
1666
+ ),
1667
+ filteredCommands.length === 0 ? /* @__PURE__ */ jsx17(Text11, { size: "sm", c: "dimmed", children: "No matching commands." }) : filteredCommands.map((command) => /* @__PURE__ */ jsxs10(Group10, { justify: "space-between", wrap: "nowrap", children: [
1668
+ /* @__PURE__ */ jsx17(
1669
+ "button",
1670
+ {
1671
+ type: "button",
1672
+ onClick: async () => {
1673
+ await command.run();
1674
+ setRecentUse((current) => ({ ...current, [command.id]: Date.now() }));
1675
+ onClose();
1676
+ },
1677
+ "aria-label": command.label,
1678
+ children: command.label
1679
+ }
1680
+ ),
1681
+ command.shortcut ? /* @__PURE__ */ jsx17(Text11, { size: "xs", c: "dimmed", children: command.shortcut }) : null
1682
+ ] }, command.id)),
1683
+ /* @__PURE__ */ jsx17(Group10, { justify: "flex-end", children: /* @__PURE__ */ jsx17(ActionIcon4, { variant: "subtle", "aria-label": "Close command palette", onClick: onClose, children: /* @__PURE__ */ jsx17(GdsIcons.Close, { size: "1rem" }) }) })
1684
+ ] }) });
1685
+ }
1686
+
1687
+ // src/Notifications.client.tsx
1688
+ import { createContext as createContext5, useContext as useContext5, useMemo as useMemo7, useState as useState11 } from "react";
1689
+ import { jsx as jsx18 } from "react/jsx-runtime";
1690
+ var GdsNotificationContext = createContext5(null);
1691
+ function GdsNotificationProvider({ children }) {
1692
+ const [notifications, setNotifications] = useState11([]);
1693
+ const value = useMemo7(() => ({
1694
+ notifications,
1695
+ notify: (message) => {
1696
+ setNotifications((current) => {
1697
+ const rest = current.filter((item) => item.id !== message.id);
1698
+ return [...rest, message];
1699
+ });
1700
+ if (typeof message.autoCloseMs === "number" && message.autoCloseMs > 0) {
1701
+ window.setTimeout(() => {
1702
+ setNotifications((current) => current.filter((item) => item.id !== message.id));
1703
+ }, message.autoCloseMs);
1704
+ }
1705
+ },
1706
+ dismiss: (id) => {
1707
+ setNotifications((current) => current.filter((item) => item.id !== id));
1708
+ },
1709
+ clear: () => {
1710
+ setNotifications([]);
1711
+ }
1712
+ }), [notifications]);
1713
+ return /* @__PURE__ */ jsx18(GdsNotificationContext.Provider, { value, children });
1714
+ }
1715
+ function useGdsNotifications() {
1716
+ const context = useContext5(GdsNotificationContext);
1717
+ if (!context) {
1718
+ throw new Error("useGdsNotifications must be used within GdsNotificationProvider.");
1719
+ }
1720
+ return context;
1721
+ }
1722
+ function NotificationCenter({
1723
+ title = "Notifications",
1724
+ emptyMessage = "No active notifications."
1725
+ }) {
1726
+ const { notifications, dismiss, clear } = useGdsNotifications();
1727
+ return /* @__PURE__ */ jsx18(
1728
+ NotificationCenterView,
1729
+ {
1730
+ title,
1731
+ emptyMessage,
1732
+ notifications,
1733
+ onDismiss: dismiss,
1734
+ onClear: clear
1735
+ }
1736
+ );
1737
+ }
1738
+
1739
+ // src/Telemetry.client.tsx
1740
+ import { createContext as createContext6, useContext as useContext6, useMemo as useMemo8 } from "react";
1741
+ import { jsx as jsx19 } from "react/jsx-runtime";
1742
+ var GdsTelemetryContext = createContext6(null);
1743
+ function hashToUnit(value) {
1744
+ let hash = 0;
1745
+ for (let index = 0; index < value.length; index += 1) {
1746
+ hash = (hash << 5) - hash + value.charCodeAt(index);
1747
+ hash |= 0;
1748
+ }
1749
+ return Math.abs(hash % 1e3) / 1e3;
1750
+ }
1751
+ function redactContext(context) {
1752
+ if (!context) {
1753
+ return context;
1754
+ }
1755
+ return Object.entries(context).reduce((acc, [key, value]) => {
1756
+ if (key.toLowerCase().includes("email") || key.toLowerCase().includes("name") || key.toLowerCase().includes("phone")) {
1757
+ return acc;
1758
+ }
1759
+ acc[key] = value;
1760
+ return acc;
1761
+ }, {});
1762
+ }
1763
+ function GdsTelemetryProvider({ children, sink, sampleRate = 1 }) {
1764
+ const value = useMemo8(() => ({
1765
+ emit: (event) => {
1766
+ if (hashToUnit(event.correlationId) > sampleRate) {
1767
+ return;
1768
+ }
1769
+ sink?.({
1770
+ ...event,
1771
+ context: redactContext(event.context),
1772
+ ts: Date.now()
1773
+ });
1774
+ }
1775
+ }), [sampleRate, sink]);
1776
+ return /* @__PURE__ */ jsx19(GdsTelemetryContext.Provider, { value, children });
1777
+ }
1778
+ function useGdsTelemetry() {
1779
+ const context = useContext6(GdsTelemetryContext);
1780
+ if (!context) {
1781
+ throw new Error("useGdsTelemetry must be used within GdsTelemetryProvider.");
1782
+ }
1783
+ return context;
1784
+ }
1785
+
1786
+ export {
1787
+ SemanticButton,
1788
+ ConfirmDialog,
1789
+ ThemeToggle,
1790
+ ReferenceThemeExplorer,
1791
+ GameBoardTile,
1792
+ listingQueryReducer,
1793
+ ListingProvider,
1794
+ useListingState,
1795
+ useDiscoveryShellState,
1796
+ DiscoveryShell,
1797
+ DocsShell,
1798
+ SidebarNav,
1799
+ SidebarNavSection,
1800
+ SidebarNavItem,
1801
+ DocsCodeBlock,
1802
+ ShareButtonGroup,
1803
+ UploadDropzone,
1804
+ AccessRecoveryPanel,
1805
+ gdsFormReducer,
1806
+ useGdsForm,
1807
+ GdsFormProvider,
1808
+ useGdsFormSnapshot,
1809
+ FormErrorSummary,
1810
+ ValidatedFieldMessage,
1811
+ AdvancedDataTable,
1812
+ OverlayManagerProvider,
1813
+ useOverlayManager,
1814
+ CommandRegistryProvider,
1815
+ useCommandLauncher,
1816
+ CommandPalette,
1817
+ GdsNotificationProvider,
1818
+ useGdsNotifications,
1819
+ NotificationCenter,
1820
+ GdsTelemetryProvider,
1821
+ useGdsTelemetry
1822
+ };