@drawnagency/primitives 0.1.60 → 0.1.62

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/dist/chunk-2BHPWO7M.js +558 -0
  2. package/dist/chunk-4E66N6UV.js +10 -0
  3. package/dist/{chunk-RFZNNCAS.js → chunk-53DKVAB6.js} +1 -61
  4. package/dist/chunk-5THS4K5D.js +123 -0
  5. package/dist/chunk-677NTHAZ.js +27 -0
  6. package/dist/{chunk-TG43X7JO.js → chunk-75P6HWPN.js} +29 -5
  7. package/dist/chunk-7EOSDFS3.js +10 -0
  8. package/dist/{chunk-ODCJQOVO.js → chunk-7IMQVDDJ.js} +1 -1
  9. package/dist/chunk-FJBDGYHE.js +782 -0
  10. package/dist/chunk-I5737FJY.js +62 -0
  11. package/dist/chunk-JRT4KWHU.js +1201 -0
  12. package/dist/chunk-QWDOJUUO.js +648 -0
  13. package/dist/{chunk-BT7STGDW.js → chunk-TEYTLQVH.js} +0 -15
  14. package/dist/chunk-WDSVHKAH.js +54 -0
  15. package/dist/{chunk-Q5EEYBMJ.js → chunk-ZYXLNJ5M.js} +6 -30
  16. package/dist/components/brandguide/ColorSwatchSettings.d.ts +1 -1
  17. package/dist/components/brandguide/ColorSwatchSettings.d.ts.map +1 -1
  18. package/dist/components/brandguide/Colors.d.ts +1 -1
  19. package/dist/components/brandguide/Colors.d.ts.map +1 -1
  20. package/dist/components/editor/index.js +642 -0
  21. package/dist/components/primitives/IconPicker.d.ts +1 -7
  22. package/dist/components/primitives/IconPicker.d.ts.map +1 -1
  23. package/dist/components/primitives/index.d.ts +10 -0
  24. package/dist/components/primitives/index.d.ts.map +1 -0
  25. package/dist/components/primitives/index.js +169 -0
  26. package/dist/components/sections/Colors/index.d.ts.map +1 -1
  27. package/dist/components/sections/Colors/schema.d.ts +19 -0
  28. package/dist/components/sections/Colors/schema.d.ts.map +1 -0
  29. package/dist/components/sections/DoDontMedia/DoDontMediaBlock.d.ts +20 -0
  30. package/dist/components/sections/DoDontMedia/DoDontMediaBlock.d.ts.map +1 -0
  31. package/dist/components/sections/DoDontMedia/index.d.ts +45 -0
  32. package/dist/components/sections/DoDontMedia/index.d.ts.map +1 -0
  33. package/dist/components/sections/IconList/DoDontToggle.d.ts +14 -0
  34. package/dist/components/sections/IconList/DoDontToggle.d.ts.map +1 -0
  35. package/dist/components/sections/IconList/IconList.d.ts.map +1 -1
  36. package/dist/components/sections/IconList/index.d.ts.map +1 -1
  37. package/dist/components/sections/Media/MediaBlock.d.ts +1 -3
  38. package/dist/components/sections/Media/MediaBlock.d.ts.map +1 -1
  39. package/dist/components/sections/Media/index.d.ts +0 -1
  40. package/dist/components/sections/Media/index.d.ts.map +1 -1
  41. package/dist/components/sections/all-sections.d.ts +54 -18
  42. package/dist/components/sections/all-sections.d.ts.map +1 -1
  43. package/dist/components/sections/brand-guide.d.ts +84 -0
  44. package/dist/components/sections/brand-guide.d.ts.map +1 -0
  45. package/dist/components/sections/brand-guide.js +21 -0
  46. package/dist/components/sections/core-sections.d.ts +93 -0
  47. package/dist/components/sections/core-sections.d.ts.map +1 -0
  48. package/dist/components/sections/register-schemas.js +240 -3353
  49. package/dist/index.js +48 -46
  50. package/dist/lib/index.js +17 -11
  51. package/dist/lib/loader.d.ts.map +1 -1
  52. package/dist/lib/migrate-sections-transform.d.ts.map +1 -1
  53. package/dist/lib/migrate-sections-transform.js +1 -1
  54. package/dist/lib/registry.d.ts +6 -1
  55. package/dist/lib/registry.d.ts.map +1 -1
  56. package/dist/media/index.js +1 -1
  57. package/dist/schemas/index.js +9 -13
  58. package/dist/schemas/link.js +13 -0
  59. package/dist/schemas/shared.d.ts +0 -17
  60. package/dist/schemas/shared.d.ts.map +1 -1
  61. package/package.json +13 -1
  62. package/src/components/brandguide/ColorSwatchSettings.tsx +1 -1
  63. package/src/components/brandguide/Colors.tsx +1 -1
  64. package/src/components/primitives/IconPicker.tsx +27 -119
  65. package/src/components/primitives/index.ts +11 -0
  66. package/src/components/sections/Colors/index.tsx +2 -1
  67. package/src/components/sections/Colors/schema.ts +24 -0
  68. package/src/components/sections/DoDontMedia/DoDontMediaBlock.tsx +35 -0
  69. package/src/components/sections/DoDontMedia/index.tsx +84 -0
  70. package/src/components/sections/IconList/DoDontToggle.tsx +76 -0
  71. package/src/components/sections/IconList/IconList.tsx +24 -14
  72. package/src/components/sections/IconList/index.tsx +1 -0
  73. package/src/components/sections/Media/MediaBlock.tsx +1 -11
  74. package/src/components/sections/Media/index.tsx +0 -15
  75. package/src/components/sections/all-sections.ts +7 -35
  76. package/src/components/sections/brand-guide.ts +13 -0
  77. package/src/components/sections/core-sections.ts +26 -0
  78. package/src/lib/loader.ts +10 -4
  79. package/src/lib/migrate-sections-transform.ts +33 -5
  80. package/src/lib/registry.ts +6 -1
  81. package/src/schemas/shared.ts +0 -18
  82. /package/dist/{chunk-ZU2MKPTG.js → chunk-3DCG4DNU.js} +0 -0
@@ -0,0 +1,558 @@
1
+ import {
2
+ Checkbox,
3
+ FormLabel,
4
+ Input
5
+ } from "./chunk-5THS4K5D.js";
6
+ import {
7
+ usePages
8
+ } from "./chunk-4E66N6UV.js";
9
+ import {
10
+ DeleteIcon,
11
+ DragHandle,
12
+ IconButton,
13
+ SettingsIcon
14
+ } from "./chunk-I5737FJY.js";
15
+ import {
16
+ cn
17
+ } from "./chunk-7EOSDFS3.js";
18
+
19
+ // src/components/editor/DragHandle.tsx
20
+ import { forwardRef } from "react";
21
+
22
+ // src/components/shared/Tooltip.tsx
23
+ import { jsx, jsxs } from "react/jsx-runtime";
24
+ function Tooltip({ content, children, className }) {
25
+ return /* @__PURE__ */ jsxs("div", { className: cn("group/tooltip relative inline-flex", className), children: [
26
+ children,
27
+ /* @__PURE__ */ jsx(
28
+ "span",
29
+ {
30
+ className: "pointer-events-none absolute left-1/2 top-full z-50 mt-1.5 -translate-x-1/2 opacity-0 transition-opacity delay-300 group-hover/tooltip:opacity-100",
31
+ role: "tooltip",
32
+ children: /* @__PURE__ */ jsx("span", { className: "block whitespace-nowrap rounded bg-tooltip px-2 py-1 text-center text-xs leading-relaxed text-tooltip-muted", children: content })
33
+ }
34
+ )
35
+ ] });
36
+ }
37
+ function Kbd({ children }) {
38
+ return /* @__PURE__ */ jsx("strong", { className: "text-tooltip-contrast", children });
39
+ }
40
+
41
+ // src/components/editor/DragHandle.tsx
42
+ import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
43
+ var DragHandle2 = forwardRef(function DragHandle3(_, ref) {
44
+ return /* @__PURE__ */ jsx2(Tooltip, { content: /* @__PURE__ */ jsxs2(Fragment, { children: [
45
+ /* @__PURE__ */ jsx2(Kbd, { children: "Drag" }),
46
+ " to move"
47
+ ] }), children: /* @__PURE__ */ jsx2(
48
+ IconButton,
49
+ {
50
+ ref,
51
+ icon: /* @__PURE__ */ jsx2(DragHandle, { size: 18 }),
52
+ label: "Drag to reorder section",
53
+ className: "pointer-events-auto cursor-grab active:cursor-grabbing rounded-md text-base-contrast-light/80 hover:bg-base-contrast-light/10 hover:text-base-contrast",
54
+ tabIndex: -1
55
+ }
56
+ ) });
57
+ });
58
+
59
+ // src/components/editor/DeleteButton.tsx
60
+ import { jsx as jsx3 } from "react/jsx-runtime";
61
+ function DeleteButton({ onDelete }) {
62
+ return /* @__PURE__ */ jsx3(
63
+ IconButton,
64
+ {
65
+ icon: /* @__PURE__ */ jsx3(DeleteIcon, { size: 16 }),
66
+ label: "Delete section",
67
+ intent: "destructive",
68
+ onClick: onDelete,
69
+ className: "pointer-events-auto"
70
+ }
71
+ );
72
+ }
73
+
74
+ // src/components/editor/SettingsButton.tsx
75
+ import { jsx as jsx4 } from "react/jsx-runtime";
76
+ function SettingsButton({ onClick }) {
77
+ return /* @__PURE__ */ jsx4(
78
+ IconButton,
79
+ {
80
+ icon: /* @__PURE__ */ jsx4(SettingsIcon, { size: 16 }),
81
+ label: "Section settings",
82
+ onClick,
83
+ className: "pointer-events-auto"
84
+ }
85
+ );
86
+ }
87
+
88
+ // src/components/editor/SettingsForm.tsx
89
+ import { useId as useId2, useState as useState2 } from "react";
90
+
91
+ // src/components/shared/Select.tsx
92
+ import { useId } from "react";
93
+ import { ChevronDown } from "lucide-react";
94
+ import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
95
+ function Select({
96
+ label,
97
+ value,
98
+ onChange,
99
+ options,
100
+ disabled,
101
+ className,
102
+ selectClassName
103
+ }) {
104
+ const id = useId();
105
+ return /* @__PURE__ */ jsxs3("div", { className, children: [
106
+ label && /* @__PURE__ */ jsx5(FormLabel, { htmlFor: id, children: label }),
107
+ /* @__PURE__ */ jsxs3("div", { className: "relative", children: [
108
+ /* @__PURE__ */ jsx5(
109
+ "select",
110
+ {
111
+ id,
112
+ value,
113
+ onChange: (e) => onChange(e.target.value),
114
+ disabled,
115
+ className: cn(
116
+ "w-full appearance-none rounded border border-base-200 bg-base py-2 pr-8 pl-3 text-sm text-base-contrast",
117
+ "focus:border-base-contrast focus:outline-none focus:ring-1 focus:ring-base-contrast",
118
+ disabled && "cursor-not-allowed opacity-50",
119
+ selectClassName
120
+ ),
121
+ children: options.map((opt) => /* @__PURE__ */ jsx5("option", { value: opt.value, children: opt.label }, opt.value))
122
+ }
123
+ ),
124
+ /* @__PURE__ */ jsx5(
125
+ ChevronDown,
126
+ {
127
+ size: 14,
128
+ className: "pointer-events-none absolute right-2.5 top-1/2 -translate-y-1/2 text-base-contrast-light"
129
+ }
130
+ )
131
+ ] })
132
+ ] });
133
+ }
134
+
135
+ // src/components/shared/LinkField.tsx
136
+ import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
137
+ var TARGET_OPTIONS = [
138
+ { label: "Same tab (_self)", value: "_self" },
139
+ { label: "New tab (_blank)", value: "_blank" }
140
+ ];
141
+ function LinkField({ label, value, onChange }) {
142
+ const { pages, getPageHeadings } = usePages();
143
+ const isInternal = value.kind === "internal";
144
+ function setMode(kind) {
145
+ if (kind === value.kind) return;
146
+ onChange(kind === "external" ? { kind: "external", href: "", target: value.target } : { kind: "internal", pageId: "", anchorSectionId: null, target: "_self" });
147
+ }
148
+ const pageId = isInternal ? value.pageId : "";
149
+ const headings = pageId ? getPageHeadings(pageId) : [];
150
+ return /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-3", children: [
151
+ /* @__PURE__ */ jsx6(FormLabel, { children: label }),
152
+ /* @__PURE__ */ jsx6("div", { className: "flex gap-2", role: "tablist", children: ["external", "internal"].map((kind) => /* @__PURE__ */ jsx6(
153
+ "button",
154
+ {
155
+ type: "button",
156
+ role: "tab",
157
+ "aria-selected": value.kind === kind,
158
+ onClick: () => setMode(kind),
159
+ className: cn(
160
+ "cursor-pointer rounded border px-3 py-1 text-sm capitalize",
161
+ value.kind === kind ? "border-primary bg-primary text-primary-contrast" : "border-base-200 text-base-contrast"
162
+ ),
163
+ children: kind === "external" ? "External URL" : "Internal page"
164
+ },
165
+ kind
166
+ )) }),
167
+ value.kind === "external" ? /* @__PURE__ */ jsx6(
168
+ Input,
169
+ {
170
+ label: "URL",
171
+ value: value.href,
172
+ placeholder: "https://...",
173
+ onChange: (href) => onChange({ kind: "external", href, target: value.target })
174
+ }
175
+ ) : /* @__PURE__ */ jsxs4(Fragment2, { children: [
176
+ /* @__PURE__ */ jsx6(
177
+ Select,
178
+ {
179
+ label: "Page",
180
+ value: value.pageId,
181
+ options: [{ label: "Select a page\u2026", value: "" }, ...pages.map((p) => ({ label: p.title, value: p.id }))],
182
+ onChange: (pid) => onChange({ kind: "internal", pageId: pid, anchorSectionId: null, target: value.target })
183
+ }
184
+ ),
185
+ /* @__PURE__ */ jsx6(
186
+ Select,
187
+ {
188
+ label: "Section",
189
+ value: value.anchorSectionId ?? "",
190
+ disabled: !pageId,
191
+ options: [{ label: "None (top of page)", value: "" }, ...headings.map((h) => ({ label: h.label, value: h.id }))],
192
+ onChange: (anchor) => onChange({ kind: "internal", pageId, anchorSectionId: anchor || null, target: value.target })
193
+ }
194
+ )
195
+ ] }),
196
+ value.kind === "external" && /* @__PURE__ */ jsx6(
197
+ Select,
198
+ {
199
+ label: "Opens in",
200
+ value: value.target,
201
+ options: TARGET_OPTIONS,
202
+ onChange: (t) => onChange({ ...value, target: t })
203
+ }
204
+ )
205
+ ] });
206
+ }
207
+
208
+ // src/components/shared/Tabs.tsx
209
+ import { useState } from "react";
210
+ import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
211
+ function Tabs({ tabs, defaultTabId, activeTabId, onTabChange, fullBleedTabBar }) {
212
+ const [uncontrolledId, setUncontrolledId] = useState(defaultTabId ?? tabs[0]?.id);
213
+ const isControlled = activeTabId !== void 0;
214
+ const activeId = isControlled ? activeTabId : uncontrolledId;
215
+ const activeTab = tabs.find((t) => t.id === activeId) ?? tabs[0];
216
+ function handleSelect(id) {
217
+ if (!isControlled) setUncontrolledId(id);
218
+ onTabChange?.(id);
219
+ }
220
+ return /* @__PURE__ */ jsxs5("div", { className: "flex flex-1 flex-col overflow-hidden", children: [
221
+ /* @__PURE__ */ jsx7("div", { className: cn("border-b border-base-200", fullBleedTabBar && "-mx-6 px-6"), children: /* @__PURE__ */ jsx7("div", { className: "flex", role: "tablist", children: tabs.map((tab) => /* @__PURE__ */ jsx7(
222
+ "button",
223
+ {
224
+ type: "button",
225
+ role: "tab",
226
+ "aria-selected": activeTab?.id === tab.id,
227
+ onClick: () => handleSelect(tab.id),
228
+ className: cn(
229
+ "cursor-pointer px-4 py-2 text-sm font-medium border-b-2 -mb-px",
230
+ activeTab?.id === tab.id ? "border-brand text-brand" : "border-transparent text-base-contrast-light hover:text-base-contrast"
231
+ ),
232
+ children: tab.label
233
+ },
234
+ tab.id
235
+ )) }) }),
236
+ /* @__PURE__ */ jsx7("div", { role: "tabpanel", children: activeTab?.content })
237
+ ] });
238
+ }
239
+
240
+ // src/components/editor/SettingsForm.tsx
241
+ import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
242
+ function resolveValues(schema, values) {
243
+ const resolved = {};
244
+ for (const [key, field] of Object.entries(schema)) {
245
+ resolved[key] = values[key] ?? field.default;
246
+ }
247
+ return resolved;
248
+ }
249
+ function coerceValue(field, value) {
250
+ if (field.type === "select" && "coerce" in field && field.coerce === "number") {
251
+ return Number(value);
252
+ }
253
+ return value;
254
+ }
255
+ function normalizeEmpty(field, value) {
256
+ if (field.type === "select" && "emptyIsUndefined" in field && field.emptyIsUndefined && value === "") {
257
+ return void 0;
258
+ }
259
+ return value;
260
+ }
261
+ function splitByTarget(schema, allValues) {
262
+ const content = {};
263
+ const options = {};
264
+ for (const [key, field] of Object.entries(schema)) {
265
+ const value = normalizeEmpty(field, coerceValue(field, allValues[key]));
266
+ const target = field.target ?? "options";
267
+ if (target === "content") {
268
+ content[key] = value;
269
+ } else {
270
+ options[key] = value;
271
+ }
272
+ }
273
+ return { content, options };
274
+ }
275
+ function RangeField({
276
+ field,
277
+ value,
278
+ onChange
279
+ }) {
280
+ const id = useId2();
281
+ return /* @__PURE__ */ jsxs6("div", { children: [
282
+ /* @__PURE__ */ jsx8(FormLabel, { htmlFor: id, children: field.label }),
283
+ /* @__PURE__ */ jsx8(
284
+ "input",
285
+ {
286
+ id,
287
+ type: "range",
288
+ min: field.min,
289
+ max: field.max,
290
+ step: field.step,
291
+ value,
292
+ onChange: (e) => onChange(Number(e.target.value)),
293
+ className: cn(
294
+ "w-full accent-primary"
295
+ )
296
+ }
297
+ )
298
+ ] });
299
+ }
300
+ function renderField(key, field, value, handleFieldChange) {
301
+ switch (field.type) {
302
+ case "text":
303
+ return /* @__PURE__ */ jsx8(
304
+ Input,
305
+ {
306
+ label: field.label,
307
+ value: String(value ?? ""),
308
+ placeholder: field.placeholder,
309
+ onChange: (v) => handleFieldChange(key, v)
310
+ },
311
+ key
312
+ );
313
+ case "number":
314
+ return /* @__PURE__ */ jsx8(
315
+ Input,
316
+ {
317
+ label: field.label,
318
+ type: "number",
319
+ value: String(value ?? ""),
320
+ onChange: (v) => handleFieldChange(key, v === "" ? "" : Number(v))
321
+ },
322
+ key
323
+ );
324
+ case "checkbox":
325
+ return /* @__PURE__ */ jsx8(
326
+ Checkbox,
327
+ {
328
+ label: field.label,
329
+ checked: Boolean(value),
330
+ onChange: (checked) => handleFieldChange(key, checked)
331
+ },
332
+ key
333
+ );
334
+ case "select":
335
+ return /* @__PURE__ */ jsx8(
336
+ Select,
337
+ {
338
+ label: field.label,
339
+ value: String(value ?? ""),
340
+ options: field.options,
341
+ onChange: (v) => handleFieldChange(key, v)
342
+ },
343
+ key
344
+ );
345
+ case "range":
346
+ return /* @__PURE__ */ jsx8(
347
+ RangeField,
348
+ {
349
+ field,
350
+ value: Number(value ?? field.min),
351
+ onChange: (v) => handleFieldChange(key, v)
352
+ },
353
+ key
354
+ );
355
+ case "link":
356
+ return /* @__PURE__ */ jsx8(
357
+ LinkField,
358
+ {
359
+ label: field.label,
360
+ value: value ?? field.default,
361
+ onChange: (v) => handleFieldChange(key, v)
362
+ },
363
+ key
364
+ );
365
+ default:
366
+ return null;
367
+ }
368
+ }
369
+ function SettingsForm({ schema, values, onChange, tabs }) {
370
+ const [local, setLocal] = useState2(() => resolveValues(schema, values));
371
+ function handleFieldChange(key, newValue) {
372
+ const next = { ...local, [key]: newValue };
373
+ setLocal(next);
374
+ onChange(splitByTarget(schema, next));
375
+ }
376
+ if (tabs) {
377
+ const fields = (keys) => keys.filter((key) => key in schema).map((key) => renderField(key, schema[key], local[key], handleFieldChange));
378
+ const listed = new Set(tabs.flatMap((t) => t.fields));
379
+ const otherKeys = Object.keys(schema).filter((key) => !listed.has(key));
380
+ const tabItems = [
381
+ ...tabs.map((t) => ({ id: t.label, label: t.label, content: /* @__PURE__ */ jsx8("div", { className: "flex flex-col gap-4", children: fields(t.fields) }) })),
382
+ ...otherKeys.length > 0 ? [{ id: "__other__", label: "Other", content: /* @__PURE__ */ jsx8("div", { className: "flex flex-col gap-4", children: fields(otherKeys) }) }] : []
383
+ ];
384
+ return /* @__PURE__ */ jsx8(Tabs, { tabs: tabItems, fullBleedTabBar: true });
385
+ }
386
+ return /* @__PURE__ */ jsx8("div", { className: "flex flex-col gap-4", children: Object.entries(schema).map(([key, field]) => renderField(key, field, local[key], handleFieldChange)) });
387
+ }
388
+
389
+ // src/components/shared/Popover.tsx
390
+ import { useEffect as useEffect2, useLayoutEffect, useRef, useState as useState3 } from "react";
391
+
392
+ // src/hooks/useFocusTrap.ts
393
+ import { useEffect } from "react";
394
+ var FOCUSABLE_SELECTOR = [
395
+ "a[href]",
396
+ "button:not([disabled])",
397
+ "input:not([disabled])",
398
+ "select:not([disabled])",
399
+ "textarea:not([disabled])",
400
+ '[tabindex]:not([tabindex="-1"])'
401
+ ].join(", ");
402
+ function useFocusTrap(ref, active) {
403
+ useEffect(() => {
404
+ if (!active || !ref.current) return;
405
+ const container = ref.current;
406
+ const previouslyFocused = document.activeElement;
407
+ const getFocusable = () => Array.from(container.querySelectorAll(FOCUSABLE_SELECTOR));
408
+ const focusable = getFocusable();
409
+ if (focusable.length > 0) {
410
+ focusable[0].focus();
411
+ }
412
+ const handleKeyDown = (e) => {
413
+ if (e.key !== "Tab") return;
414
+ const elements = getFocusable();
415
+ if (elements.length === 0) return;
416
+ const first = elements[0];
417
+ const last = elements[elements.length - 1];
418
+ if (e.shiftKey) {
419
+ if (document.activeElement === first) {
420
+ e.preventDefault();
421
+ last.focus();
422
+ }
423
+ } else {
424
+ if (document.activeElement === last) {
425
+ e.preventDefault();
426
+ first.focus();
427
+ }
428
+ }
429
+ };
430
+ container.addEventListener("keydown", handleKeyDown);
431
+ return () => {
432
+ container.removeEventListener("keydown", handleKeyDown);
433
+ if (previouslyFocused && document.body.contains(previouslyFocused)) {
434
+ previouslyFocused.focus();
435
+ }
436
+ };
437
+ }, [active, ref]);
438
+ }
439
+
440
+ // src/components/shared/Popover.tsx
441
+ import { jsx as jsx9 } from "react/jsx-runtime";
442
+ function Popover({
443
+ isOpen,
444
+ onClose,
445
+ anchorRef,
446
+ children,
447
+ align = "auto",
448
+ className
449
+ }) {
450
+ const panelRef = useRef(null);
451
+ const [resolvedAlign, setResolvedAlign] = useState3("start");
452
+ useFocusTrap(panelRef, isOpen);
453
+ useLayoutEffect(() => {
454
+ if (!isOpen || align !== "auto") {
455
+ if (!isOpen) setResolvedAlign("start");
456
+ return;
457
+ }
458
+ const panel = panelRef.current;
459
+ if (!panel) return;
460
+ panel.style.left = "0";
461
+ panel.style.right = "auto";
462
+ const rect = panel.getBoundingClientRect();
463
+ if (rect.right > window.innerWidth) {
464
+ setResolvedAlign("end");
465
+ } else {
466
+ setResolvedAlign("start");
467
+ }
468
+ panel.style.left = "";
469
+ panel.style.right = "";
470
+ }, [isOpen, align]);
471
+ useEffect2(() => {
472
+ if (!isOpen) return;
473
+ function onKeyDown(e) {
474
+ if (e.key === "Escape") onClose();
475
+ }
476
+ function onMouseDown(e) {
477
+ const target = e.target;
478
+ if (panelRef.current?.contains(target)) return;
479
+ if (anchorRef.current?.contains(target)) return;
480
+ onClose();
481
+ }
482
+ document.addEventListener("keydown", onKeyDown);
483
+ document.addEventListener("mousedown", onMouseDown);
484
+ return () => {
485
+ document.removeEventListener("keydown", onKeyDown);
486
+ document.removeEventListener("mousedown", onMouseDown);
487
+ };
488
+ }, [isOpen, onClose, anchorRef]);
489
+ if (!isOpen) return null;
490
+ const effectiveAlign = align === "auto" ? resolvedAlign : align;
491
+ return /* @__PURE__ */ jsx9(
492
+ "div",
493
+ {
494
+ ref: panelRef,
495
+ role: "dialog",
496
+ "aria-modal": "false",
497
+ className: cn(
498
+ "absolute top-full z-50 mt-1 rounded-md border border-base-200 bg-base shadow-lg",
499
+ effectiveAlign === "end" ? "right-0" : "left-0",
500
+ className
501
+ ),
502
+ children
503
+ }
504
+ );
505
+ }
506
+
507
+ // src/components/shared/PopoverItem.tsx
508
+ import { forwardRef as forwardRef2 } from "react";
509
+ import { jsx as jsx10 } from "react/jsx-runtime";
510
+ var PopoverItem = forwardRef2(
511
+ function PopoverItem2({ className, children, ...rest }, ref) {
512
+ return /* @__PURE__ */ jsx10(
513
+ "button",
514
+ {
515
+ ref,
516
+ type: "button",
517
+ className: cn(
518
+ "flex w-full cursor-pointer items-center gap-3 px-3 py-1.5 text-left text-xs hover:bg-base-accent",
519
+ className
520
+ ),
521
+ ...rest,
522
+ children
523
+ }
524
+ );
525
+ }
526
+ );
527
+
528
+ // src/lib/block-dnd.ts
529
+ var ROOT_CONTAINER_ID = "__root__";
530
+ function buildBlockDragData(args) {
531
+ return { dragType: "block", ...args };
532
+ }
533
+ function buildBlockDropData(args) {
534
+ return { ...args };
535
+ }
536
+ function isBlockDragData(data) {
537
+ return data.dragType === "block";
538
+ }
539
+ function canDropBlock(source, dropContainerId) {
540
+ if (source.blockId === dropContainerId) return false;
541
+ if (source.isContainer && dropContainerId !== ROOT_CONTAINER_ID) return false;
542
+ return true;
543
+ }
544
+
545
+ export {
546
+ Tooltip,
547
+ Kbd,
548
+ DragHandle2 as DragHandle,
549
+ DeleteButton,
550
+ SettingsButton,
551
+ Popover,
552
+ PopoverItem,
553
+ SettingsForm,
554
+ buildBlockDragData,
555
+ buildBlockDropData,
556
+ isBlockDragData,
557
+ canDropBlock
558
+ };
@@ -0,0 +1,10 @@
1
+ // src/components/shared/PagesContext.tsx
2
+ import { createContext, useContext } from "react";
3
+ var PagesContext = createContext({ pages: [], getPageHeadings: () => [] });
4
+ function usePages() {
5
+ return useContext(PagesContext);
6
+ }
7
+
8
+ export {
9
+ usePages
10
+ };
@@ -2,13 +2,6 @@ import {
2
2
  getSection
3
3
  } from "./chunk-VKAGMEKE.js";
4
4
 
5
- // src/lib/cn.ts
6
- import { clsx } from "clsx";
7
- import { twMerge } from "tailwind-merge";
8
- function cn(...inputs) {
9
- return twMerge(clsx(inputs));
10
- }
11
-
12
5
  // src/lib/nav.ts
13
6
  function toSectionId(text) {
14
7
  const slug = text.normalize("NFKD").replace(/[̀-ͯ]/g, "").toLowerCase().replace(/[^\w\s-]/g, "").replace(/\s+/g, "-").replace(/^-+|-+$/g, "");
@@ -99,62 +92,9 @@ async function ensureSanitizer() {
99
92
  if (!purifier && purifierPromise) await purifierPromise;
100
93
  }
101
94
 
102
- // src/lib/icons.ts
103
- import {
104
- Check,
105
- X,
106
- ArrowRight,
107
- Star,
108
- Info,
109
- AlertTriangle,
110
- Heart,
111
- Lightbulb,
112
- Flag,
113
- Shield,
114
- Zap,
115
- Eye,
116
- Lock,
117
- Globe,
118
- Users,
119
- Target,
120
- Bookmark,
121
- Clock,
122
- ThumbsUp,
123
- ThumbsDown
124
- } from "lucide-react";
125
- var curatedIcons = [
126
- { id: "check", label: "Check", icon: Check },
127
- { id: "x", label: "X", icon: X },
128
- { id: "arrow-right", label: "Arrow Right", icon: ArrowRight },
129
- { id: "star", label: "Star", icon: Star },
130
- { id: "info", label: "Info", icon: Info },
131
- { id: "alert-triangle", label: "Warning", icon: AlertTriangle },
132
- { id: "heart", label: "Heart", icon: Heart },
133
- { id: "lightbulb", label: "Lightbulb", icon: Lightbulb },
134
- { id: "flag", label: "Flag", icon: Flag },
135
- { id: "shield", label: "Shield", icon: Shield },
136
- { id: "zap", label: "Zap", icon: Zap },
137
- { id: "eye", label: "Eye", icon: Eye },
138
- { id: "lock", label: "Lock", icon: Lock },
139
- { id: "globe", label: "Globe", icon: Globe },
140
- { id: "users", label: "Users", icon: Users },
141
- { id: "target", label: "Target", icon: Target },
142
- { id: "bookmark", label: "Bookmark", icon: Bookmark },
143
- { id: "clock", label: "Clock", icon: Clock },
144
- { id: "thumbs-up", label: "Thumbs Up", icon: ThumbsUp },
145
- { id: "thumbs-down", label: "Thumbs Down", icon: ThumbsDown }
146
- ];
147
- var iconMap = new Map(curatedIcons.map((e) => [e.id, e]));
148
- function getIcon(id) {
149
- return iconMap.get(id);
150
- }
151
-
152
95
  export {
153
- cn,
154
96
  toSectionId,
155
97
  generateNavLinks,
156
98
  sanitizeHtml,
157
- ensureSanitizer,
158
- curatedIcons,
159
- getIcon
99
+ ensureSanitizer
160
100
  };