@marktiderman/genesis-forms-studio 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,585 +1,33 @@
1
1
  "use client";
2
- import { SAVE_STATE_LABEL, readinessChip, prefilledUrl, embedSnippet, STATUS_FILTER_LABEL, lowThisWeek, NO_FILTERS, lowScaleAtMost, dayLabel } from './chunk-7XQXES5I.js';
3
- export { NO_FILTERS, RESPONSE_STATUS_LABEL, SAVE_STATE_LABEL, SLUG_PATTERN, STATUS_FILTER_LABEL, answerCell, answerColumns, answersByKey, carryAnswers, comparisonFilter, copyTitle, dayLabel, dropOffByQuestion, dropOffSummary, embedSnippet, filterResponses, followUpOf, formAvailability, formCounts, formNavItems, formStatusChip, freeSlug, hasLowRating, hasLowRatingExcept, headlineCards, headlineNumbers, highestRating, isFiltered, isFull, lowRatingAtMost, lowRatingLabel, lowScaleAtMost, lowThisWeek, lowestRating, matchesStatus, needsFollowUp, offersSubmitAnother, parseInstant, plainLabel, prefilledUrl, presetRange, readinessChip, readinessOf, slugFromTitle, summarizeResponses, takesResponses, toCsv, toResponseFilter, validateSlug, valuesByKey } from './chunk-7XQXES5I.js';
4
- import { StatusBadge, cn, Badge, Breadcrumb, BreadcrumbList, BreadcrumbItem, BreadcrumbLink, BreadcrumbSeparator, BreadcrumbPage, Button, Separator, ToggleGroup, ToggleGroupItem, Skeleton, Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, Tabs, TabsList, TabsTrigger, TabsContent, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, Label, Textarea, Input, EmptyState, toast } from '@marktiderman/genesis-ui';
5
- import { Settings, Inbox, GitBranch, ListChecks, Check, Loader2, TriangleAlert, Undo2, Redo2, Eye, Share2, Rocket, CircleAlert, CircleCheck, Star, ExternalLink, Printer, Copy, CalendarDays, CircleDashed, X, CircleDot, SquareCheck, Gauge, Type, ChartNoAxesColumnDecreasing, ImageOff, Image } from 'lucide-react';
2
+ import { FORM_READINESS_LABELS, STATUS_FILTER_LABEL, lowThisWeek, NO_FILTERS, lowScaleAtMost } from './chunk-KUYYXBEN.js';
3
+ export { FORM_READINESS_LABELS, FORM_READINESS_OPTIONS, NO_FILTERS, RESPONSE_STATUS_LABEL, SAVE_STATE_LABEL, SLUG_PATTERN, STATUS_FILTER_LABEL, answerCell, answerColumns, answersByKey, carryAnswers, comparisonFilter, copyTitle, dayLabel, dropOffByQuestion, dropOffSummary, embedSnippet, filterResponses, followUpOf, formAvailability, formCounts, formNavItems, formStatusChip, freeSlug, hasLowRating, hasLowRatingExcept, headlineCards, headlineNumbers, highestRating, isFiltered, isFull, lowRatingAtMost, lowRatingLabel, lowScaleAtMost, lowThisWeek, lowestRating, matchesStatus, needsFollowUp, offersSubmitAnother, parseInstant, plainLabel, prefilledUrl, presetRange, readinessChip, readinessOf, slugFromTitle, summarizeResponses, takesResponses, toCsv, toResponseFilter, validateSlug, valuesByKey } from './chunk-KUYYXBEN.js';
4
+ import { Chip as Chip$1, FollowUpList as FollowUpList$1, HeadlineNumbers as HeadlineNumbers$1, ResourceHeader as ResourceHeader$1, ResourceSettingsPage as ResourceSettingsPage$1, ResponsesToolbar as ResponsesToolbar$1, SettingsSection as SettingsSection$1, SummaryCard as SummaryCard$1, ReadinessChip as ReadinessChip$1, ShareDialog as ShareDialog$1, dayLabel } from '@marktiderman/genesis-ui/resource';
6
5
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
7
- import { useId, useState, useEffect } from 'react';
6
+ import { Settings, Inbox, GitBranch, ListChecks, CalendarDays, CircleDashed, TriangleAlert, X, CircleDot, CircleCheck, SquareCheck, Gauge, Star, Type, ChartNoAxesColumnDecreasing, ImageOff, Image, ExternalLink } from 'lucide-react';
7
+ import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem, Input, Button, Skeleton, cn, EmptyState } from '@marktiderman/genesis-ui';
8
+ import { useState, useEffect } from 'react';
8
9
 
9
- function Chip({
10
- tone,
11
- icon,
12
- children,
13
- className,
14
- testID
15
- }) {
16
- const inner = /* @__PURE__ */ jsxs(Fragment, { children: [
17
- icon ? /* @__PURE__ */ jsx("span", { className: "mr-1 inline-flex [&_svg]:size-3", "aria-hidden": true, children: icon }) : null,
18
- children
19
- ] });
20
- if (tone === "danger") {
21
- return /* @__PURE__ */ jsx(StatusBadge, { status: "offTrack", className: cn("font-normal", className), "data-testid": testID, children: inner });
22
- }
23
- return /* @__PURE__ */ jsx(Badge, { variant: tone, className: cn("font-normal", className), "data-testid": testID, children: inner });
24
- }
25
- var SAVE_TONE = {
26
- saved: "muted",
27
- saving: "muted",
28
- unsaved: "muted",
29
- failed: "warning",
30
- stopped: "danger",
31
- conflict: "danger"
32
- };
33
- function ResourceHeader({
34
- title,
35
- untitled = "Untitled",
36
- parent,
37
- status,
38
- saveState,
39
- onUndo,
40
- onRedo,
41
- canUndo = true,
42
- canRedo = true,
43
- onPreview,
44
- onShare,
45
- publish,
46
- actions,
47
- className
48
- }) {
49
- const history = onUndo || onRedo;
50
- const name = title.trim() || untitled;
51
- return /* @__PURE__ */ jsxs(
52
- "header",
53
- {
54
- "data-slot": "resource-header",
55
- className: cn("flex flex-col gap-3 md:flex-row md:items-start md:justify-between", className),
56
- children: [
57
- /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1 space-y-1", children: [
58
- parent ? /* @__PURE__ */ jsx(Breadcrumb, { children: /* @__PURE__ */ jsxs(BreadcrumbList, { className: "text-xs", children: [
59
- /* @__PURE__ */ jsx(BreadcrumbItem, { children: /* @__PURE__ */ jsx(BreadcrumbLink, { asChild: true, children: parent }) }),
60
- /* @__PURE__ */ jsx(BreadcrumbSeparator, {}),
61
- /* @__PURE__ */ jsx(BreadcrumbItem, { className: "min-w-0", children: /* @__PURE__ */ jsx(BreadcrumbPage, { className: "truncate", children: name }) })
62
- ] }) }) : null,
63
- /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-x-3 gap-y-1", children: [
64
- /* @__PURE__ */ jsx("h1", { className: "min-w-0 break-words text-xl font-semibold tracking-tight sm:text-2xl", children: name }),
65
- status ? /* @__PURE__ */ jsx(Chip, { tone: status.tone, testID: "resource-status", children: status.label }) : null,
66
- saveState ? /* @__PURE__ */ jsx("span", { "aria-live": "polite", children: /* @__PURE__ */ jsx(
67
- Chip,
68
- {
69
- tone: SAVE_TONE[saveState],
70
- testID: "resource-save-state",
71
- icon: saveState === "saved" ? /* @__PURE__ */ jsx(Check, {}) : saveState === "saving" ? /* @__PURE__ */ jsx(Loader2, { className: "animate-spin" }) : saveState === "unsaved" ? null : /* @__PURE__ */ jsx(TriangleAlert, {}),
72
- children: SAVE_STATE_LABEL[saveState]
73
- }
74
- ) }) : null
75
- ] })
76
- ] }),
77
- /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 flex-wrap items-center gap-2", children: [
78
- history ? /* @__PURE__ */ jsxs(Fragment, { children: [
79
- /* @__PURE__ */ jsx(
80
- Button,
81
- {
82
- variant: "ghost",
83
- size: "icon",
84
- "aria-label": "Undo",
85
- title: "Undo",
86
- disabled: !onUndo || !canUndo,
87
- onClick: onUndo,
88
- "data-testid": "resource-undo",
89
- children: /* @__PURE__ */ jsx(Undo2, { className: "size-4" })
90
- }
91
- ),
92
- /* @__PURE__ */ jsx(
93
- Button,
94
- {
95
- variant: "ghost",
96
- size: "icon",
97
- "aria-label": "Redo",
98
- title: "Redo",
99
- disabled: !onRedo || !canRedo,
100
- onClick: onRedo,
101
- "data-testid": "resource-redo",
102
- children: /* @__PURE__ */ jsx(Redo2, { className: "size-4" })
103
- }
104
- ),
105
- /* @__PURE__ */ jsx(Separator, { orientation: "vertical", className: "mx-1 h-6" })
106
- ] }) : null,
107
- actions,
108
- onPreview ? /* @__PURE__ */ jsx(Button, { variant: "ghost", startIcon: /* @__PURE__ */ jsx(Eye, { className: "size-4" }), onClick: onPreview, "data-testid": "resource-preview", children: "Preview" }) : null,
109
- onShare ? /* @__PURE__ */ jsx(Button, { variant: "outline", startIcon: /* @__PURE__ */ jsx(Share2, { className: "size-4" }), onClick: onShare, "data-testid": "resource-share", children: "Share" }) : null,
110
- publish ? /* @__PURE__ */ jsx(
111
- Button,
112
- {
113
- startIcon: /* @__PURE__ */ jsx(Rocket, { className: "size-4" }),
114
- loading: publish.loading,
115
- disabled: publish.disabled,
116
- onClick: publish.onClick,
117
- "data-testid": "resource-publish",
118
- children: publish.label ?? "Publish"
119
- }
120
- ) : null
121
- ] })
122
- ]
123
- }
124
- );
125
- }
126
- function ReadinessChip({
127
- readiness,
128
- onClick,
129
- className
130
- }) {
131
- const { label, tone } = readinessChip(readiness);
132
- const icon = readiness.state === "ready" ? /* @__PURE__ */ jsx(Check, {}) : readiness.state === "problems" ? /* @__PURE__ */ jsx(CircleAlert, {}) : readiness.state === "warnings" ? /* @__PURE__ */ jsx(TriangleAlert, {}) : null;
133
- const chip = /* @__PURE__ */ jsx(Chip, { tone, icon, testID: "readiness", className, children: label });
134
- if (!onClick) return chip;
135
- return /* @__PURE__ */ jsx(
136
- "button",
137
- {
138
- type: "button",
139
- onClick,
140
- className: "rounded-full focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
141
- children: chip
142
- }
143
- );
144
- }
145
- function ResponsesToolbar({
146
- view,
147
- onViewChange,
148
- filters,
149
- actions,
150
- className
151
- }) {
152
- return /* @__PURE__ */ jsxs(
153
- "div",
154
- {
155
- "data-slot": "responses-toolbar",
156
- className: cn("flex flex-col gap-2 lg:flex-row lg:items-start lg:justify-between", className),
157
- children: [
158
- /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-wrap items-center gap-2", children: [
159
- /* @__PURE__ */ jsxs(
160
- ToggleGroup,
161
- {
162
- type: "single",
163
- variant: "outline",
164
- size: "sm",
165
- className: "h-9",
166
- value: view,
167
- onValueChange: (v) => v && onViewChange(v),
168
- "aria-label": "Show responses as",
169
- children: [
170
- /* @__PURE__ */ jsx(ToggleGroupItem, { value: "summary", className: "h-9 px-3", "data-testid": "responses-view-summary", children: "Summary" }),
171
- /* @__PURE__ */ jsx(ToggleGroupItem, { value: "each", className: "h-9 px-3", "data-testid": "responses-view-each", children: "Each response" })
172
- ]
173
- }
174
- ),
175
- filters
176
- ] }),
177
- actions ? /* @__PURE__ */ jsx("div", { className: "flex shrink-0 flex-wrap items-center gap-2", children: actions }) : null
178
- ]
179
- }
180
- );
181
- }
182
- var TONE = {
183
- positive: "text-emerald-700 dark:text-emerald-300",
184
- negative: "text-red-700 dark:text-red-300",
185
- warning: "text-amber-800 dark:text-amber-200",
186
- muted: "text-muted-foreground"
10
+ var FORM_SHARE_LABELS = {
11
+ noun: "form",
12
+ description: "Anyone with the link can answer."
187
13
  };
188
- function HeadlineNumbers({
189
- numbers,
190
- isLoading = false,
191
- className
192
- }) {
193
- if (isLoading) {
194
- return /* @__PURE__ */ jsx("div", { className: cn("grid grid-cols-2 gap-3 lg:grid-cols-4", className), children: [0, 1, 2, 3].map((i) => /* @__PURE__ */ jsx(Skeleton, { className: "h-28 w-full rounded-lg" }, i)) });
195
- }
196
- if (!numbers.length) return null;
197
- return /* @__PURE__ */ jsx(
198
- "dl",
199
- {
200
- "data-slot": "headline-numbers",
201
- className: cn(
202
- "grid grid-cols-2 gap-3",
203
- numbers.length >= 4 ? "lg:grid-cols-4" : numbers.length === 3 ? "lg:grid-cols-3" : "",
204
- className
205
- ),
206
- children: numbers.map((n) => /* @__PURE__ */ jsxs(
207
- "div",
208
- {
209
- className: "flex min-w-0 flex-col gap-1 rounded-lg border border-border bg-card p-4 text-card-foreground",
210
- "data-testid": `headline-${n.key}`,
211
- children: [
212
- /* @__PURE__ */ jsx("dt", { className: "truncate text-sm text-muted-foreground", children: n.label }),
213
- /* @__PURE__ */ jsx("dd", { className: "text-2xl font-semibold tabular-nums tracking-tight sm:text-3xl", children: n.value }),
214
- n.detail ? /* @__PURE__ */ jsx("dd", { className: cn("text-xs", TONE[n.tone ?? "muted"]), children: n.detail }) : null
215
- ]
216
- },
217
- n.key
218
- ))
219
- }
220
- );
221
- }
222
- function SummaryCard({
223
- icon,
224
- title,
225
- meta,
226
- children,
227
- className,
228
- testID
229
- }) {
230
- return /* @__PURE__ */ jsxs(
231
- "section",
232
- {
233
- "aria-label": title,
234
- "data-testid": testID,
235
- "data-slot": "summary-card",
236
- className: cn("flex flex-col gap-4 rounded-lg border border-border bg-card p-5 text-card-foreground", className),
237
- children: [
238
- /* @__PURE__ */ jsxs("header", { className: "flex flex-wrap items-baseline justify-between gap-x-4 gap-y-1", children: [
239
- /* @__PURE__ */ jsxs("h3", { className: "flex min-w-0 items-center gap-2 font-semibold", children: [
240
- icon ? /* @__PURE__ */ jsx("span", { className: "shrink-0 text-muted-foreground [&_svg]:size-4", "aria-hidden": true, children: icon }) : null,
241
- /* @__PURE__ */ jsx("span", { className: "min-w-0 break-words", children: title })
242
- ] }),
243
- meta ? /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: meta }) : null
244
- ] }),
245
- children
246
- ]
247
- }
248
- );
249
- }
250
- function FollowUpList({
251
- entries,
252
- actions,
253
- title = "Needs a follow-up",
254
- isLoading = false,
255
- empty = "Nothing to follow up right now.",
256
- className
257
- }) {
258
- return /* @__PURE__ */ jsxs(
259
- "section",
260
- {
261
- "aria-label": title,
262
- "data-testid": "follow-up-list",
263
- className: cn("flex flex-col gap-3 rounded-lg border border-border bg-card p-4 text-card-foreground", className),
264
- children: [
265
- /* @__PURE__ */ jsxs("header", { className: "flex items-center justify-between gap-2", children: [
266
- /* @__PURE__ */ jsxs("h3", { className: "flex items-center gap-2 font-semibold", children: [
267
- /* @__PURE__ */ jsx(TriangleAlert, { className: "size-4 text-amber-800 dark:text-amber-200", "aria-hidden": true }),
268
- title
269
- ] }),
270
- !isLoading ? /* @__PURE__ */ jsx("span", { className: "rounded-md bg-muted px-2 py-0.5 text-xs tabular-nums text-muted-foreground", children: entries.length }) : null
271
- ] }),
272
- isLoading ? /* @__PURE__ */ jsxs(Fragment, { children: [
273
- /* @__PURE__ */ jsx(Skeleton, { className: "h-24 w-full rounded-md" }),
274
- /* @__PURE__ */ jsx(Skeleton, { className: "h-24 w-full rounded-md" })
275
- ] }) : entries.length === 0 ? /* @__PURE__ */ jsxs("p", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [
276
- /* @__PURE__ */ jsx(CircleCheck, { className: "size-4 shrink-0", "aria-hidden": true }),
277
- empty
278
- ] }) : /* @__PURE__ */ jsx("ul", { className: "flex flex-col gap-3", children: entries.map((entry) => {
279
- const buttons = actions?.(entry);
280
- return /* @__PURE__ */ jsxs(
281
- "li",
282
- {
283
- className: "flex flex-col gap-1.5 rounded-md border border-border p-3",
284
- "data-testid": `follow-up-${entry.id}`,
285
- children: [
286
- /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-2", children: [
287
- /* @__PURE__ */ jsxs("p", { className: "flex min-w-0 flex-wrap items-center gap-2", children: [
288
- /* @__PURE__ */ jsx("span", { className: "break-words font-medium", children: entry.name }),
289
- entry.tag ? /* @__PURE__ */ jsx(Chip, { tone: entry.tag.tone ?? "muted", children: entry.tag.label }) : null
290
- ] }),
291
- entry.rating ? /* @__PURE__ */ jsxs(
292
- "span",
293
- {
294
- className: "flex shrink-0 items-center gap-0.5 text-sm font-medium tabular-nums text-amber-800 dark:text-amber-200",
295
- role: "img",
296
- "aria-label": `Rated ${entry.rating.value} out of ${entry.rating.max}`,
297
- children: [
298
- entry.rating.value,
299
- /* @__PURE__ */ jsx(Star, { className: "size-3.5 fill-current", "aria-hidden": true })
300
- ]
301
- }
302
- ) : null
303
- ] }),
304
- entry.meta ? /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: entry.meta }) : null,
305
- /* @__PURE__ */ jsx("p", { className: cn("line-clamp-3 break-words text-sm", !entry.comment && "text-muted-foreground"), children: entry.comment || "No comment" }),
306
- buttons ? /* @__PURE__ */ jsx("div", { className: "mt-1 flex flex-wrap gap-2", children: buttons }) : null
307
- ]
308
- },
309
- entry.id
310
- );
311
- }) })
312
- ]
313
- }
314
- );
315
- }
316
- var ANY = "__any";
317
- async function copy(text, what) {
318
- try {
319
- await navigator.clipboard.writeText(text);
320
- toast.success(`${what} copied.`);
321
- } catch {
322
- toast.error("Couldn't copy. Select it and copy it instead.");
323
- }
324
- }
325
- function CopyField({
326
- id,
327
- label,
328
- value,
329
- what
330
- }) {
331
- return /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
332
- /* @__PURE__ */ jsx(Label, { htmlFor: id, children: label }),
333
- /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
334
- /* @__PURE__ */ jsx(Input, { id, readOnly: true, value, onFocus: (e) => e.target.select() }),
335
- /* @__PURE__ */ jsx(
336
- Button,
337
- {
338
- variant: "outline",
339
- startIcon: /* @__PURE__ */ jsx(Copy, { className: "size-4" }),
340
- onClick: () => void copy(value, what),
341
- children: "Copy"
342
- }
343
- )
344
- ] })
345
- ] });
14
+ var ResourceHeader = ResourceHeader$1;
15
+ var Chip = Chip$1;
16
+ var ResponsesToolbar = ResponsesToolbar$1;
17
+ var HeadlineNumbers = HeadlineNumbers$1;
18
+ var SummaryCard = SummaryCard$1;
19
+ var FollowUpList = FollowUpList$1;
20
+ var SettingsSection = SettingsSection$1;
21
+ var ResourceSettingsPage = ResourceSettingsPage$1;
22
+ function ReadinessChip({ labels, ...props }) {
23
+ return /* @__PURE__ */ jsx(ReadinessChip$1, { ...props, labels: { ...FORM_READINESS_LABELS, ...labels } });
346
24
  }
347
25
  function ShareDialog({
348
- open,
349
- onOpenChange,
350
- noun = "form",
351
- title = `Share your ${noun}`,
352
- description = "Anyone with the link can answer.",
353
- url,
354
- posterUrl,
355
- renderQr,
356
- name,
357
- prefill = [],
358
- prefillNote,
359
- defaultTab = "link"
26
+ noun = FORM_SHARE_LABELS.noun,
27
+ description = FORM_SHARE_LABELS.description,
28
+ ...props
360
29
  }) {
361
- const id = useId();
362
- const [values, setValues] = useState({});
363
- const chosen = Object.fromEntries(Object.entries(values).filter(([, v]) => v && v !== ANY));
364
- const prefilled = url && Object.keys(chosen).length ? prefilledUrl(url, chosen) : null;
365
- const poster = url ? posterUrl?.(url, {}) : null;
366
- const prefilledPoster = prefilled && url ? posterUrl?.(url, chosen) : null;
367
- const usable = prefill.filter((p) => p.options.length > 0);
368
- return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs(DialogContent, { className: "max-h-[90dvh] overflow-y-auto sm:max-w-lg", children: [
369
- /* @__PURE__ */ jsxs(DialogHeader, { children: [
370
- /* @__PURE__ */ jsx(DialogTitle, { children: title }),
371
- /* @__PURE__ */ jsx(DialogDescription, { children: description })
372
- ] }),
373
- !url ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "Loading the link\u2026" }) : /* @__PURE__ */ jsxs(Tabs, { defaultValue: defaultTab === "qr" && !renderQr ? "link" : defaultTab, children: [
374
- /* @__PURE__ */ jsxs(TabsList, { children: [
375
- /* @__PURE__ */ jsx(TabsTrigger, { value: "link", children: "Link" }),
376
- renderQr && /* @__PURE__ */ jsx(TabsTrigger, { value: "qr", children: "QR code" }),
377
- /* @__PURE__ */ jsx(TabsTrigger, { value: "embed", children: "Embed" })
378
- ] }),
379
- /* @__PURE__ */ jsxs(TabsContent, { value: "link", className: "space-y-5 pt-2", children: [
380
- /* @__PURE__ */ jsx(CopyField, { id: `${id}-link`, label: "Link", value: url, what: "Link" }),
381
- /* @__PURE__ */ jsx(
382
- Button,
383
- {
384
- asChild: true,
385
- variant: "ghost",
386
- size: "sm",
387
- startIcon: /* @__PURE__ */ jsx(ExternalLink, { className: "size-4" }),
388
- children: /* @__PURE__ */ jsxs("a", { href: url, target: "_blank", rel: "noreferrer", children: [
389
- "Open the ",
390
- noun
391
- ] })
392
- }
393
- ),
394
- usable.length > 0 && /* @__PURE__ */ jsxs("div", { className: "space-y-3 border-t border-border pt-4", children: [
395
- /* @__PURE__ */ jsxs("div", { children: [
396
- /* @__PURE__ */ jsx("p", { className: "text-sm font-medium", children: "Prefilled link" }),
397
- prefillNote && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: prefillNote })
398
- ] }),
399
- /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2", children: usable.map((p) => /* @__PURE__ */ jsxs(
400
- Select,
401
- {
402
- value: values[p.key] ?? ANY,
403
- onValueChange: (v) => setValues((old) => ({ ...old, [p.key]: v })),
404
- children: [
405
- /* @__PURE__ */ jsx(SelectTrigger, { className: "min-w-40 flex-1", "aria-label": p.label, children: /* @__PURE__ */ jsx(SelectValue, {}) }),
406
- /* @__PURE__ */ jsxs(SelectContent, { children: [
407
- /* @__PURE__ */ jsx(SelectItem, { value: ANY, children: p.anyLabel }),
408
- p.options.map((o) => /* @__PURE__ */ jsx(SelectItem, { value: o.value, children: o.label }, o.value))
409
- ] })
410
- ]
411
- },
412
- p.key
413
- )) }),
414
- prefilled && /* @__PURE__ */ jsxs(Fragment, { children: [
415
- /* @__PURE__ */ jsx(
416
- CopyField,
417
- {
418
- id: `${id}-prefilled`,
419
- label: "Prefilled link",
420
- value: prefilled,
421
- what: "Prefilled link"
422
- }
423
- ),
424
- prefilledPoster && /* @__PURE__ */ jsx(
425
- Button,
426
- {
427
- asChild: true,
428
- variant: "outline",
429
- startIcon: /* @__PURE__ */ jsx(Printer, { className: "size-4" }),
430
- children: /* @__PURE__ */ jsx("a", { href: prefilledPoster, target: "_blank", rel: "noreferrer", children: "Print a poster for this link" })
431
- }
432
- )
433
- ] })
434
- ] })
435
- ] }),
436
- renderQr && /* @__PURE__ */ jsxs(TabsContent, { value: "qr", className: "space-y-4 pt-2", children: [
437
- /* @__PURE__ */ jsx("div", { className: "mx-auto w-full max-w-56", children: renderQr(url) }),
438
- /* @__PURE__ */ jsxs("p", { className: "text-center text-sm text-muted-foreground", children: [
439
- "Scan with a phone's camera to open the ",
440
- noun,
441
- "."
442
- ] }),
443
- poster && /* @__PURE__ */ jsx("div", { className: "flex justify-center", children: /* @__PURE__ */ jsx(Button, { asChild: true, variant: "outline", startIcon: /* @__PURE__ */ jsx(Printer, { className: "size-4" }), children: /* @__PURE__ */ jsx("a", { href: poster, target: "_blank", rel: "noreferrer", children: "Print a QR poster" }) }) })
444
- ] }),
445
- /* @__PURE__ */ jsxs(TabsContent, { value: "embed", className: "space-y-3 pt-2", children: [
446
- /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
447
- /* @__PURE__ */ jsx(Label, { htmlFor: `${id}-embed`, children: "Embed on your website" }),
448
- /* @__PURE__ */ jsx(
449
- Textarea,
450
- {
451
- id: `${id}-embed`,
452
- readOnly: true,
453
- rows: 4,
454
- className: "font-mono text-xs",
455
- value: embedSnippet(url, name),
456
- onFocus: (e) => e.target.select()
457
- }
458
- )
459
- ] }),
460
- /* @__PURE__ */ jsx(
461
- Button,
462
- {
463
- variant: "outline",
464
- startIcon: /* @__PURE__ */ jsx(Copy, { className: "size-4" }),
465
- onClick: () => void copy(embedSnippet(url, name), "Embed code"),
466
- children: "Copy embed code"
467
- }
468
- )
469
- ] })
470
- ] })
471
- ] }) });
472
- }
473
- function SettingsSection({
474
- id,
475
- icon,
476
- title,
477
- description,
478
- children,
479
- className
480
- }) {
481
- const headingId = id ? `${id}-title` : void 0;
482
- return /* @__PURE__ */ jsxs(
483
- "section",
484
- {
485
- id,
486
- "aria-labelledby": headingId,
487
- "data-slot": "settings-section",
488
- className: cn("scroll-mt-6 rounded-lg border border-border bg-card text-card-foreground", className),
489
- children: [
490
- /* @__PURE__ */ jsxs("header", { className: "flex gap-3 border-b border-border px-5 py-4", children: [
491
- icon ? /* @__PURE__ */ jsx("span", { className: "mt-0.5 text-primary [&_svg]:size-5", "aria-hidden": true, children: icon }) : null,
492
- /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
493
- /* @__PURE__ */ jsx("h2", { id: headingId, className: "font-semibold", children: title }),
494
- description ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: description }) : null
495
- ] })
496
- ] }),
497
- /* @__PURE__ */ jsx(
498
- "div",
499
- {
500
- className: cn(
501
- "divide-y divide-border",
502
- // Rows: genesis-ui SettingsRow and ToggleRow, padded to the card.
503
- "[&>*]:px-5 [&>[data-slot=settings-row]]:py-3.5",
504
- "[&>[data-slot=settings-row]:not(:has([role=switch]))]:max-sm:flex-col",
505
- "[&>[data-slot=settings-row]:not(:has([role=switch]))]:max-sm:items-stretch",
506
- "[&>[data-slot=settings-row]>div:last-child]:max-sm:flex-wrap"
507
- ),
508
- children
509
- }
510
- )
511
- ]
512
- }
513
- );
514
- }
515
- function ResourceSettingsPage({
516
- sections,
517
- navLabel = "On this page",
518
- className
519
- }) {
520
- const [active, setActive] = useState(sections[0]?.id);
521
- const ids = sections.map((s) => s.id).join(" ");
522
- useEffect(() => {
523
- if (typeof window === "undefined") return;
524
- let frame = 0;
525
- const update = () => {
526
- frame = 0;
527
- const line = window.innerHeight / 3;
528
- let current = sections[0]?.id;
529
- for (const s of sections) {
530
- const el = document.getElementById(s.id);
531
- if (el && el.getBoundingClientRect().top <= line) current = s.id;
532
- }
533
- const bottom = window.innerHeight + window.scrollY >= document.documentElement.scrollHeight - 2;
534
- const last = [...sections].reverse().find((s) => document.getElementById(s.id));
535
- if (bottom && last && window.scrollY > 0) current = last.id;
536
- setActive(current);
537
- };
538
- const onScroll = () => {
539
- if (!frame) frame = window.requestAnimationFrame(update);
540
- };
541
- update();
542
- window.addEventListener("scroll", onScroll, { passive: true, capture: true });
543
- return () => {
544
- window.removeEventListener("scroll", onScroll, { capture: true });
545
- if (frame) window.cancelAnimationFrame(frame);
546
- };
547
- }, [ids]);
548
- return /* @__PURE__ */ jsxs(
549
- "div",
550
- {
551
- "data-slot": "resource-settings-page",
552
- className: cn("grid grid-cols-[minmax(0,1fr)] gap-6 md:grid-cols-[12rem_minmax(0,1fr)] lg:gap-8", className),
553
- children: [
554
- /* @__PURE__ */ jsxs("nav", { "aria-label": navLabel, className: "min-w-0 md:sticky md:top-4 md:self-start", children: [
555
- /* @__PURE__ */ jsx("p", { className: "mb-2 hidden text-xs font-medium uppercase tracking-wide text-muted-foreground md:block", children: navLabel }),
556
- /* @__PURE__ */ jsx("ul", { className: "-mx-1 flex gap-1 overflow-x-auto px-1 md:mx-0 md:flex-col md:overflow-visible md:px-0", children: sections.map((s) => /* @__PURE__ */ jsx("li", { className: "shrink-0", children: /* @__PURE__ */ jsxs(
557
- "a",
558
- {
559
- href: `#${s.id}`,
560
- "aria-current": active === s.id ? "location" : void 0,
561
- onClick: (e) => {
562
- setActive(s.id);
563
- if (s.content === void 0 && s.onSelect) {
564
- e.preventDefault();
565
- s.onSelect();
566
- }
567
- },
568
- className: cn(
569
- "flex items-center gap-2 whitespace-nowrap rounded-md px-3 py-1.5 text-sm text-muted-foreground hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring [&_svg]:size-4",
570
- active === s.id && "bg-muted font-medium text-foreground"
571
- ),
572
- children: [
573
- s.icon ? /* @__PURE__ */ jsx("span", { "aria-hidden": true, children: s.icon }) : null,
574
- s.title
575
- ]
576
- }
577
- ) }, s.id)) })
578
- ] }),
579
- /* @__PURE__ */ jsx("div", { className: "flex min-w-0 flex-col gap-5", children: sections.filter((s) => s.content !== void 0).map((s) => /* @__PURE__ */ jsx(SettingsSection, { id: s.id, icon: s.icon, title: s.title, description: s.description, children: s.content }, s.id)) })
580
- ]
581
- }
582
- );
30
+ return /* @__PURE__ */ jsx(ShareDialog$1, { ...props, noun, description });
583
31
  }
584
32
  var FORM_NAV_ICONS = {
585
33
  questions: ListChecks,
@@ -596,7 +44,7 @@ var WHEN = [
596
44
  { value: "month", label: "This month" },
597
45
  { value: "custom", label: "Pick dates" }
598
46
  ];
599
- var ANY2 = "__any";
47
+ var ANY = "__any";
600
48
  var TRIGGER = "h-9 w-auto min-w-0 gap-2 [&>span]:flex [&>span]:items-center [&>span]:gap-2";
601
49
  function ResponsesFilters({
602
50
  value,
@@ -658,15 +106,15 @@ function ResponsesFilters({
658
106
  choices.filter((c) => c.options.length > 0).map((c) => /* @__PURE__ */ jsxs(
659
107
  Select,
660
108
  {
661
- value: value.match[c.key] || ANY2,
662
- onValueChange: (v) => onChange({ ...value, match: { ...value.match, [c.key]: v === ANY2 ? "" : v } }),
109
+ value: value.match[c.key] || ANY,
110
+ onValueChange: (v) => onChange({ ...value, match: { ...value.match, [c.key]: v === ANY ? "" : v } }),
663
111
  children: [
664
112
  /* @__PURE__ */ jsxs(SelectTrigger, { className: `${TRIGGER} max-w-56`, "aria-label": c.label, children: [
665
113
  c.icon ? /* @__PURE__ */ jsx("span", { className: "text-muted-foreground [&_svg]:size-4", "aria-hidden": true, children: c.icon }) : null,
666
114
  /* @__PURE__ */ jsx(SelectValue, {})
667
115
  ] }),
668
116
  /* @__PURE__ */ jsxs(SelectContent, { children: [
669
- /* @__PURE__ */ jsx(SelectItem, { value: ANY2, children: c.allLabel }),
117
+ /* @__PURE__ */ jsx(SelectItem, { value: ANY, children: c.allLabel }),
670
118
  c.options.map((o) => /* @__PURE__ */ jsx(SelectItem, { value: o, children: o }, o))
671
119
  ] })
672
120
  ]
@@ -676,12 +124,12 @@ function ResponsesFilters({
676
124
  ratings.length > 0 ? /* @__PURE__ */ jsxs(
677
125
  Select,
678
126
  {
679
- value: value.ratingAtMost == null ? ANY2 : String(value.ratingAtMost),
680
- onValueChange: (v) => onChange({ ...value, ratingAtMost: v === ANY2 ? null : Number(v) }),
127
+ value: value.ratingAtMost == null ? ANY : String(value.ratingAtMost),
128
+ onValueChange: (v) => onChange({ ...value, ratingAtMost: v === ANY ? null : Number(v) }),
681
129
  children: [
682
130
  /* @__PURE__ */ jsx(SelectTrigger, { className: TRIGGER, "aria-label": "Rating", children: /* @__PURE__ */ jsx(SelectValue, {}) }),
683
131
  /* @__PURE__ */ jsxs(SelectContent, { children: [
684
- /* @__PURE__ */ jsx(SelectItem, { value: ANY2, children: "Any rating" }),
132
+ /* @__PURE__ */ jsx(SelectItem, { value: ANY, children: "Any rating" }),
685
133
  ratings.map((r) => /* @__PURE__ */ jsx(SelectItem, { value: String(r), children: r === 1 ? "Rated 1" : `Rated ${r} or below` }, r))
686
134
  ] })
687
135
  ]
@@ -853,7 +301,7 @@ function QuestionSummaryCard({
853
301
  const Icon = ICONS[s.fieldType] ?? Type;
854
302
  const average = s.kind === "scale" && s.average !== null ? ` \xB7 average ${s.average.toFixed(1)}` : "";
855
303
  return /* @__PURE__ */ jsx(
856
- SummaryCard,
304
+ SummaryCard$1,
857
305
  {
858
306
  icon: /* @__PURE__ */ jsx(Icon, {}),
859
307
  title: s.label || "Untitled question",
@@ -906,7 +354,7 @@ function DropOffByQuestion({
906
354
  const started = startedCount ?? dropOffs[0]?.views ?? 0;
907
355
  const worst = Math.max(0, ...dropOffs.map((d) => d.stopped));
908
356
  return /* @__PURE__ */ jsx(
909
- SummaryCard,
357
+ SummaryCard$1,
910
358
  {
911
359
  icon: /* @__PURE__ */ jsx(ChartNoAxesColumnDecreasing, {}),
912
360
  title,
@@ -1007,6 +455,6 @@ function PhotoAnswer({
1007
455
  return /* @__PURE__ */ jsx("ul", { className: cn("flex flex-wrap gap-3", className), "aria-label": "Photos", "data-testid": testID, children: photos.map((photo) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(Photo, { photo, resolveUrl }) }, photo.id)) });
1008
456
  }
1009
457
 
1010
- export { Chip, DropOffByQuestion, FORM_NAV_ICONS, FollowUpList, HeadlineNumbers, PhotoAnswer, QuestionSummaryCard, QuestionSummaryCards, ReadinessChip, ResourceHeader, ResourceSettingsPage, ResponsesFilters, ResponsesToolbar, SettingsSection, ShareDialog, SummaryCard };
458
+ export { Chip, DropOffByQuestion, FORM_NAV_ICONS, FORM_SHARE_LABELS, FollowUpList, HeadlineNumbers, PhotoAnswer, QuestionSummaryCard, QuestionSummaryCards, ReadinessChip, ResourceHeader, ResourceSettingsPage, ResponsesFilters, ResponsesToolbar, SettingsSection, ShareDialog, SummaryCard };
1011
459
  //# sourceMappingURL=index.js.map
1012
460
  //# sourceMappingURL=index.js.map