@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.
- package/dist/chunk-6FX7WZZO.mjs +1822 -0
- package/dist/{chunk-LH2KMMXT.mjs → chunk-YP7RL2MC.mjs} +995 -299
- package/dist/client.d.mts +271 -10
- package/dist/client.d.ts +271 -10
- package/dist/client.js +2036 -503
- package/dist/client.mjs +86 -4
- package/dist/index.d.mts +3 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.js +2036 -503
- package/dist/index.mjs +86 -4
- package/dist/{server-BSuY9Qx6.d.mts → server-DCXU_K9q.d.mts} +248 -9
- package/dist/{server-BSuY9Qx6.d.ts → server-DCXU_K9q.d.ts} +248 -9
- package/dist/server.d.mts +1 -1
- package/dist/server.d.ts +1 -1
- package/dist/server.js +1048 -330
- package/dist/server.mjs +41 -1
- package/package.json +2 -2
- package/dist/chunk-IUYPELGQ.mjs +0 -1028
|
@@ -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
|
+
};
|