@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
@@ -1,207 +1,71 @@
1
1
  import {
2
- getMediaProvider
3
- } from "../../chunk-CBWK3KPV.js";
2
+ brandGuideSectionDefs,
3
+ pluralize,
4
+ stripHtmlToPlainText,
5
+ truncate
6
+ } from "../../chunk-JRT4KWHU.js";
7
+ import {
8
+ DeleteButton,
9
+ DragHandle,
10
+ Popover,
11
+ PopoverItem,
12
+ SettingsButton,
13
+ SettingsForm,
14
+ buildBlockDragData,
15
+ buildBlockDropData,
16
+ canDropBlock,
17
+ isBlockDragData
18
+ } from "../../chunk-2BHPWO7M.js";
19
+ import "../../chunk-5THS4K5D.js";
20
+ import {
21
+ EditableRichText,
22
+ defineHeadingSection
23
+ } from "../../chunk-FJBDGYHE.js";
24
+ import {
25
+ EditablePlainText,
26
+ MAX_CONTAINER_COLUMNS,
27
+ MediaBlock,
28
+ colSpanClass,
29
+ containerGridClass,
30
+ spacerHiddenClass
31
+ } from "../../chunk-QWDOJUUO.js";
32
+ import "../../chunk-4E66N6UV.js";
33
+ import "../../chunk-I5737FJY.js";
34
+ import "../../chunk-CBWK3KPV.js";
4
35
  import {
5
36
  DEFAULT_LINK,
6
37
  LinkValueSchema
7
- } from "../../chunk-ZU2MKPTG.js";
38
+ } from "../../chunk-3DCG4DNU.js";
39
+ import "../../chunk-WDSVHKAH.js";
8
40
  import {
9
- cn,
10
- curatedIcons,
11
- getIcon,
12
- sanitizeHtml,
13
- toSectionId
14
- } from "../../chunk-RFZNNCAS.js";
41
+ sanitizeHtml
42
+ } from "../../chunk-53DKVAB6.js";
15
43
  import "../../chunk-ZGFAYZWB.js";
44
+ import {
45
+ cn
46
+ } from "../../chunk-7EOSDFS3.js";
16
47
  import {
17
48
  getSectionSchema
18
49
  } from "../../chunk-TZQPOR5A.js";
50
+ import {
51
+ SingleMediaReferenceSchema
52
+ } from "../../chunk-TEYTLQVH.js";
19
53
  import {
20
54
  defineSection,
21
55
  getSection,
22
56
  registerRichText,
23
57
  registerSchema
24
58
  } from "../../chunk-VKAGMEKE.js";
25
- import {
26
- ColorItemSchema,
27
- SingleMediaReferenceSchema
28
- } from "../../chunk-BT7STGDW.js";
29
59
  import "../../chunk-NSCT3AMV.js";
30
60
  import "../../chunk-JSBRDJBE.js";
31
61
 
32
62
  // src/components/sections/LinkHeading/index.tsx
33
63
  import { Heading1 } from "lucide-react";
34
-
35
- // src/components/primitives/defineHeadingSection.tsx
36
- import { z } from "zod";
37
-
38
- // src/components/primitives/EditablePlainText.tsx
39
- import {
40
- useRef as useRef2,
41
- useEffect as useEffect2,
42
- useCallback as useCallback2,
43
- createElement
44
- } from "react";
45
-
46
- // src/components/primitives/useEditablePlainText.ts
47
- import { useState, useEffect, useRef, useCallback } from "react";
48
- function useEditablePlainText({
49
- value: propValue,
50
- onChange,
51
- multiline = false
52
- }) {
53
- const [value, setValue] = useState(propValue);
54
- const lastPropValue = useRef(propValue);
55
- useEffect(() => {
56
- if (propValue !== lastPropValue.current) {
57
- setValue(propValue);
58
- lastPropValue.current = propValue;
59
- }
60
- }, [propValue]);
61
- const handleInput = useCallback((text) => {
62
- setValue(text);
63
- }, []);
64
- const handleBlur = useCallback(() => {
65
- if (value !== propValue) {
66
- onChange(value);
67
- lastPropValue.current = value;
68
- }
69
- }, [value, propValue, onChange]);
70
- return {
71
- value,
72
- handleInput,
73
- handleBlur,
74
- shouldPreventEnter: !multiline
75
- };
76
- }
77
-
78
- // src/components/primitives/EditablePlainText.tsx
79
- function EditablePlainText({
80
- tag,
81
- value,
82
- onChange,
83
- isEditMode,
84
- multiline = false,
85
- placeholder,
86
- className,
87
- ...htmlAttrs
88
- }) {
89
- const ref = useRef2(null);
90
- const {
91
- value: localValue,
92
- handleInput,
93
- handleBlur,
94
- shouldPreventEnter
95
- } = useEditablePlainText({ value, onChange, multiline });
96
- useEffect2(() => {
97
- if (ref.current && ref.current.textContent !== localValue) {
98
- ref.current.textContent = localValue;
99
- }
100
- }, [localValue]);
101
- const onInput = useCallback2(() => {
102
- if (ref.current) {
103
- handleInput(ref.current.textContent || "");
104
- }
105
- }, [handleInput]);
106
- const onKeyDown = useCallback2(
107
- (e) => {
108
- if (e.key === "Enter" && shouldPreventEnter) {
109
- e.preventDefault();
110
- }
111
- },
112
- [shouldPreventEnter]
113
- );
114
- const onPaste = useCallback2(
115
- (e) => {
116
- e.preventDefault();
117
- const text = e.clipboardData.getData("text/plain");
118
- const selection = window.getSelection();
119
- if (selection && selection.rangeCount > 0) {
120
- const range = selection.getRangeAt(0);
121
- range.deleteContents();
122
- range.insertNode(document.createTextNode(text));
123
- range.collapse(false);
124
- selection.removeAllRanges();
125
- selection.addRange(range);
126
- }
127
- if (ref.current) {
128
- handleInput(ref.current.textContent || "");
129
- }
130
- },
131
- [handleInput]
132
- );
133
- return createElement(
134
- tag,
135
- {
136
- ref,
137
- className: isEditMode && placeholder ? `editable-placeholder ${className ?? ""}` : className,
138
- contentEditable: isEditMode ? "true" : void 0,
139
- suppressContentEditableWarning: isEditMode || void 0,
140
- onInput: isEditMode ? onInput : void 0,
141
- onBlur: isEditMode ? handleBlur : void 0,
142
- onKeyDown: isEditMode ? onKeyDown : void 0,
143
- onPaste: isEditMode ? onPaste : void 0,
144
- "data-placeholder": isEditMode && placeholder ? placeholder : void 0,
145
- ...htmlAttrs
146
- },
147
- value
148
- );
149
- }
150
-
151
- // src/components/primitives/HeadingSection.tsx
152
64
  import { jsx } from "react/jsx-runtime";
153
- function HeadingSection({ heading, tag, placeholder, className, onChange }) {
154
- return /* @__PURE__ */ jsx(
155
- EditablePlainText,
156
- {
157
- tag,
158
- value: heading,
159
- onChange: onChange ?? (() => {
160
- }),
161
- isEditMode: !!onChange,
162
- placeholder,
163
- id: toSectionId(heading),
164
- className: cn("scroll-mt-20", className)
165
- }
166
- );
167
- }
168
-
169
- // src/components/primitives/defineHeadingSection.tsx
170
- import { jsx as jsx2 } from "react/jsx-runtime";
171
- function defineHeadingSection(config) {
172
- const content = config.navExclusion ? z.object({ heading: z.string(), excludeFromNav: z.boolean().optional() }) : z.object({ heading: z.string() });
173
- const schema8 = z.object({ type: z.literal(config.type), content });
174
- return defineSection({
175
- type: config.type,
176
- label: config.label,
177
- icon: config.icon,
178
- schema: schema8,
179
- navRole: config.navRole,
180
- component: ({ content: content2, onChange }) => /* @__PURE__ */ jsx2(
181
- HeadingSection,
182
- {
183
- heading: content2.content.heading,
184
- tag: config.tag,
185
- placeholder: config.placeholder,
186
- onChange: onChange ? (heading) => onChange({ ...content2, content: { ...content2.content, heading } }) : void 0
187
- }
188
- ),
189
- defaults: () => ({ type: config.type, content: { heading: config.defaultHeading } }),
190
- getLabel: (content2) => content2.content.heading,
191
- ...config.navExclusion ? {
192
- settings: {
193
- excludeFromNav: { type: "checkbox", label: "Exclude from navigation", default: false, target: "content" }
194
- }
195
- } : {}
196
- });
197
- }
198
-
199
- // src/components/sections/LinkHeading/index.tsx
200
- import { jsx as jsx3 } from "react/jsx-runtime";
201
65
  var LinkHeading_default = defineHeadingSection({
202
66
  type: "link_heading",
203
67
  label: "Link Heading",
204
- icon: /* @__PURE__ */ jsx3(Heading1, { size: 18 }),
68
+ icon: /* @__PURE__ */ jsx(Heading1, { size: 18 }),
205
69
  navRole: "h1",
206
70
  tag: "h2",
207
71
  placeholder: "Section heading",
@@ -210,11 +74,11 @@ var LinkHeading_default = defineHeadingSection({
210
74
 
211
75
  // src/components/sections/SubHeading/index.tsx
212
76
  import { Heading2 } from "lucide-react";
213
- import { jsx as jsx4 } from "react/jsx-runtime";
77
+ import { jsx as jsx2 } from "react/jsx-runtime";
214
78
  var SubHeading_default = defineHeadingSection({
215
79
  type: "sub_heading",
216
80
  label: "Sub Heading",
217
- icon: /* @__PURE__ */ jsx4(Heading2, { size: 18 }),
81
+ icon: /* @__PURE__ */ jsx2(Heading2, { size: 18 }),
218
82
  navRole: "h2",
219
83
  tag: "h3",
220
84
  placeholder: "Sub heading",
@@ -224,11 +88,11 @@ var SubHeading_default = defineHeadingSection({
224
88
 
225
89
  // src/components/sections/SubSubHeading/index.tsx
226
90
  import { Heading3 } from "lucide-react";
227
- import { jsx as jsx5 } from "react/jsx-runtime";
91
+ import { jsx as jsx3 } from "react/jsx-runtime";
228
92
  var SubSubHeading_default = defineHeadingSection({
229
93
  type: "sub_sub_heading",
230
94
  label: "Sub Sub Heading",
231
- icon: /* @__PURE__ */ jsx5(Heading3, { size: 18 }),
95
+ icon: /* @__PURE__ */ jsx3(Heading3, { size: 18 }),
232
96
  navRole: "h3",
233
97
  tag: "h4",
234
98
  placeholder: "Sub sub heading",
@@ -237,732 +101,14 @@ var SubSubHeading_default = defineHeadingSection({
237
101
  });
238
102
 
239
103
  // src/components/sections/Prose/index.tsx
240
- import { z as z2 } from "zod";
104
+ import { z } from "zod";
241
105
  import { AlignLeft } from "lucide-react";
242
106
 
243
- // src/components/primitives/EditableRichText.tsx
244
- import { useRef as useRef4, useCallback as useCallback4, useEffect as useEffect4 } from "react";
245
- import { EditorContent } from "@tiptap/react";
246
- import { BubbleMenu } from "@tiptap/react/menus";
247
-
248
- // src/components/primitives/useEditableRichText.ts
249
- import { useState as useState2, useRef as useRef3, useCallback as useCallback3, useEffect as useEffect3 } from "react";
250
- import { Editor } from "@tiptap/core";
251
- import Placeholder from "@tiptap/extension-placeholder";
252
-
253
- // src/components/primitives/tiptap-presets.ts
254
- import StarterKit from "@tiptap/starter-kit";
255
- import Underline from "@tiptap/extension-underline";
256
- import Link from "@tiptap/extension-link";
257
-
258
- // ../../node_modules/.pnpm/@tiptap+extension-paragraph@3.22.4_@tiptap+core@3.22.4_@tiptap+pm@3.22.4_/node_modules/@tiptap/extension-paragraph/dist/index.js
259
- import { mergeAttributes, Node } from "@tiptap/core";
260
- var EMPTY_PARAGRAPH_MARKDOWN = " ";
261
- var NBSP_CHAR = "\xA0";
262
- var Paragraph = Node.create({
263
- name: "paragraph",
264
- priority: 1e3,
265
- addOptions() {
266
- return {
267
- HTMLAttributes: {}
268
- };
269
- },
270
- group: "block",
271
- content: "inline*",
272
- parseHTML() {
273
- return [{ tag: "p" }];
274
- },
275
- renderHTML({ HTMLAttributes }) {
276
- return ["p", mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0];
277
- },
278
- parseMarkdown: (token, helpers) => {
279
- const tokens = token.tokens || [];
280
- if (tokens.length === 1 && tokens[0].type === "image") {
281
- return helpers.parseChildren([tokens[0]]);
282
- }
283
- const content = helpers.parseInline(tokens);
284
- const hasExplicitEmptyParagraphMarker = tokens.length === 1 && tokens[0].type === "text" && (tokens[0].raw === EMPTY_PARAGRAPH_MARKDOWN || tokens[0].text === EMPTY_PARAGRAPH_MARKDOWN || tokens[0].raw === NBSP_CHAR || tokens[0].text === NBSP_CHAR);
285
- if (hasExplicitEmptyParagraphMarker && content.length === 1 && content[0].type === "text" && (content[0].text === EMPTY_PARAGRAPH_MARKDOWN || content[0].text === NBSP_CHAR)) {
286
- return helpers.createNode("paragraph", void 0, []);
287
- }
288
- return helpers.createNode("paragraph", void 0, content);
289
- },
290
- renderMarkdown: (node, h, ctx) => {
291
- var _a, _b;
292
- if (!node) {
293
- return "";
294
- }
295
- const content = Array.isArray(node.content) ? node.content : [];
296
- if (content.length === 0) {
297
- const previousContent = Array.isArray((_a = ctx == null ? void 0 : ctx.previousNode) == null ? void 0 : _a.content) ? ctx.previousNode.content : [];
298
- const previousNodeIsEmptyParagraph = ((_b = ctx == null ? void 0 : ctx.previousNode) == null ? void 0 : _b.type) === "paragraph" && previousContent.length === 0;
299
- return previousNodeIsEmptyParagraph ? EMPTY_PARAGRAPH_MARKDOWN : "";
300
- }
301
- return h.renderChildren(content);
302
- },
303
- addCommands() {
304
- return {
305
- setParagraph: () => ({ commands }) => {
306
- return commands.setNode(this.name);
307
- }
308
- };
309
- },
310
- addKeyboardShortcuts() {
311
- return {
312
- "Mod-Alt-0": () => this.editor.commands.setParagraph()
313
- };
314
- }
315
- });
316
- var index_default = Paragraph;
317
-
318
- // src/components/primitives/CustomParagraph.ts
319
- var ALLOWED_CLASSES = ["large", "lead-in"];
320
- var CustomParagraph = index_default.extend({
321
- addAttributes() {
322
- return {
323
- ...this.parent?.(),
324
- class: {
325
- default: null,
326
- parseHTML: (element) => {
327
- const cls = element.getAttribute("class");
328
- if (cls && ALLOWED_CLASSES.includes(cls)) {
329
- return cls;
330
- }
331
- return null;
332
- },
333
- renderHTML: (attributes) => {
334
- if (!attributes.class) return {};
335
- return { class: attributes.class };
336
- }
337
- }
338
- };
339
- }
340
- });
341
-
342
- // src/components/primitives/tiptap-presets.ts
343
- var basic = [
344
- StarterKit.configure({
345
- paragraph: false,
346
- heading: false,
347
- blockquote: false,
348
- bulletList: false,
349
- orderedList: false,
350
- listItem: false,
351
- codeBlock: false,
352
- horizontalRule: false,
353
- code: false,
354
- strike: false
355
- }),
356
- CustomParagraph,
357
- Underline
358
- ];
359
- var rich = [
360
- StarterKit.configure({
361
- paragraph: false,
362
- heading: { levels: [3, 4] },
363
- blockquote: false,
364
- codeBlock: false,
365
- horizontalRule: false,
366
- code: false,
367
- strike: false
368
- }),
369
- CustomParagraph,
370
- Underline,
371
- // `page` protocol: stable internal-page hrefs (page://{pageId}#{sectionId})
372
- // resolved to real routes at SSR — see lib/links.ts.
373
- Link.configure({ openOnClick: false, protocols: ["page"] })
374
- ];
375
- var presets = { basic, rich };
376
-
377
- // src/components/primitives/useEditableRichText.ts
378
- function useEditableRichText({
379
- value,
380
- onChange,
381
- preset,
382
- placeholder
383
- }) {
384
- const editorRef = useRef3(null);
385
- const [editor, setEditor] = useState2(null);
386
- const valueRef = useRef3(value);
387
- const onChangeRef = useRef3(onChange);
388
- useEffect3(() => {
389
- valueRef.current = value;
390
- }, [value]);
391
- useEffect3(() => {
392
- onChangeRef.current = onChange;
393
- }, [onChange]);
394
- const activate = useCallback3(() => {
395
- if (editorRef.current) return;
396
- const extensions = placeholder ? [...presets[preset], Placeholder.configure({ placeholder })] : presets[preset];
397
- const ed = new Editor({
398
- extensions,
399
- content: valueRef.current,
400
- // Commit edits to onChange on every transaction, not only on deactivate().
401
- // Without this, content lives only inside TipTap until blur; clicking a
402
- // Save button while the editor is still focused flushes persistence before
403
- // the (150ms-delayed) blur->deactivate commit fires, silently dropping the
404
- // in-progress edit. Live-committing keeps persistence in sync continuously.
405
- onUpdate: ({ editor: updated }) => {
406
- const html = updated.getHTML();
407
- if (html !== valueRef.current) {
408
- valueRef.current = html;
409
- onChangeRef.current(html);
410
- }
411
- }
412
- });
413
- editorRef.current = ed;
414
- setEditor(ed);
415
- }, [preset, placeholder]);
416
- const deactivate = useCallback3(() => {
417
- const ed = editorRef.current;
418
- if (!ed) return;
419
- const html = ed.getHTML();
420
- ed.destroy();
421
- editorRef.current = null;
422
- setEditor(null);
423
- if (html !== valueRef.current) {
424
- onChangeRef.current(html);
425
- }
426
- }, []);
427
- useEffect3(() => {
428
- return () => {
429
- editorRef.current?.destroy();
430
- editorRef.current = null;
431
- };
432
- }, []);
433
- return {
434
- isEditorActive: editor !== null,
435
- editor,
436
- activate,
437
- deactivate
438
- };
439
- }
440
-
441
- // src/components/primitives/RichTextToolbar.tsx
442
- import { useState as useState4 } from "react";
443
- import { useEditorState } from "@tiptap/react";
444
-
445
- // src/components/primitives/LinkPopover.tsx
446
- import { useState as useState3 } from "react";
447
-
448
- // src/components/shared/Button.tsx
449
- import { forwardRef } from "react";
450
- import { jsx as jsx6 } from "react/jsx-runtime";
451
- var boxSize = {
452
- sm: "rounded px-3 py-1.5 text-xs font-medium",
453
- md: "rounded px-4 py-2 text-sm font-medium"
454
- };
455
- var ghostSize = {
456
- sm: "text-xs font-medium",
457
- md: "text-sm font-medium"
458
- };
459
- var variantClasses = {
460
- primary: "bg-base-contrast text-base-accent hover:bg-base-contrast/90",
461
- brand: "bg-primary text-primary-contrast hover:opacity-90",
462
- secondary: "border border-base-200 text-base-contrast hover:bg-base-accent",
463
- destructive: "border border-red-600 text-red-600 hover:bg-red-600 hover:text-white"
464
- };
465
- var ghostToneClasses = {
466
- neutral: "text-base-contrast-light hover:text-base-contrast",
467
- destructive: "text-red-600 hover:text-red-800"
468
- };
469
- var Button = forwardRef(function Button2({
470
- variant = "primary",
471
- size = "sm",
472
- tone = "neutral",
473
- fullWidth = false,
474
- isLoading = false,
475
- loadingLabel,
476
- disabled,
477
- className,
478
- children,
479
- ...rest
480
- }, ref) {
481
- const isGhost = variant === "ghost";
482
- const resolvedDisabled = disabled || isLoading;
483
- const label = isLoading && loadingLabel ? loadingLabel : children;
484
- return /* @__PURE__ */ jsx6(
485
- "button",
486
- {
487
- ref,
488
- disabled: resolvedDisabled,
489
- className: cn(
490
- "inline-flex items-center justify-center transition-colors",
491
- isGhost ? ghostSize[size] : boxSize[size],
492
- isGhost ? ghostToneClasses[tone] : variantClasses[variant],
493
- fullWidth && "w-full",
494
- resolvedDisabled ? "cursor-not-allowed opacity-50" : "cursor-pointer",
495
- className
496
- ),
497
- ...rest,
498
- children: label
499
- }
500
- );
501
- });
502
-
503
- // src/components/shared/PagesContext.tsx
504
- import { createContext, useContext } from "react";
505
- var PagesContext = createContext({ pages: [], getPageHeadings: () => [] });
506
- function usePages() {
507
- return useContext(PagesContext);
508
- }
509
-
510
- // src/lib/links.ts
511
- function internalHref(pageId, anchorSectionId) {
512
- return `page://${pageId}${anchorSectionId ? `#${anchorSectionId}` : ""}`;
513
- }
514
- function parseInternalHref(href) {
515
- const m = /^page:\/\/([^#]+)(?:#(.+))?$/.exec(href);
516
- return m ? { pageId: m[1], anchorSectionId: m[2] ?? null } : null;
517
- }
518
-
519
- // src/components/primitives/LinkPopover.tsx
520
- import { Fragment, jsx as jsx7, jsxs } from "react/jsx-runtime";
521
- var selectClasses = "w-full rounded border border-base-contrast/20 bg-base px-2 py-1 text-sm text-base-contrast focus:outline-none focus:ring-1 focus:ring-primary";
522
- function LinkPopover({ editor, onClose }) {
523
- const { pages, getPageHeadings } = usePages();
524
- const existingAttrs = editor.getAttributes("link");
525
- const existingInternal = parseInternalHref(existingAttrs.href ?? "");
526
- const [mode, setMode] = useState3(existingInternal ? "internal" : "external");
527
- const [url, setUrl] = useState3(existingInternal ? "" : existingAttrs.href ?? "");
528
- const [pageId, setPageId] = useState3(existingInternal?.pageId ?? "");
529
- const [anchorId, setAnchorId] = useState3(existingInternal?.anchorSectionId ?? "");
530
- const [openInNewTab, setOpenInNewTab] = useState3(
531
- existingAttrs.target === "_blank"
532
- );
533
- const hasLink = Boolean(existingAttrs.href);
534
- const headings = pageId ? getPageHeadings(pageId) : [];
535
- const handleApply = () => {
536
- if (mode === "internal") {
537
- if (pageId) {
538
- editor.chain().focus().extendMarkRange("link").setLink({ href: internalHref(pageId, anchorId || null), target: null }).run();
539
- }
540
- } else if (url.trim()) {
541
- editor.chain().focus().extendMarkRange("link").setLink({ href: url.trim(), target: openInNewTab ? "_blank" : null }).run();
542
- }
543
- onClose();
544
- };
545
- const handleRemove = () => {
546
- editor.chain().focus().extendMarkRange("link").unsetLink().run();
547
- onClose();
548
- };
549
- const handleKeyDown = (e) => {
550
- if (e.key === "Enter") {
551
- e.preventDefault();
552
- handleApply();
553
- } else if (e.key === "Escape") {
554
- e.preventDefault();
555
- onClose();
556
- }
557
- };
558
- return /* @__PURE__ */ jsxs(
559
- "div",
560
- {
561
- className: "flex w-64 flex-col gap-2 rounded bg-base-accent p-3 shadow-lg",
562
- onMouseDown: (e) => e.stopPropagation(),
563
- onKeyDown: handleKeyDown,
564
- children: [
565
- /* @__PURE__ */ jsx7("div", { className: "flex gap-2", role: "tablist", children: ["external", "internal"].map((kind) => /* @__PURE__ */ jsx7(
566
- "button",
567
- {
568
- type: "button",
569
- role: "tab",
570
- "aria-selected": mode === kind,
571
- onClick: () => setMode(kind),
572
- className: cn(
573
- "cursor-pointer rounded border px-2 py-0.5 text-xs",
574
- mode === kind ? "border-primary bg-primary text-primary-contrast" : "border-base-200 text-base-contrast"
575
- ),
576
- children: kind === "external" ? "External URL" : "Internal page"
577
- },
578
- kind
579
- )) }),
580
- mode === "external" ? /* @__PURE__ */ jsxs(Fragment, { children: [
581
- /* @__PURE__ */ jsx7(
582
- "input",
583
- {
584
- autoFocus: true,
585
- type: "url",
586
- value: url,
587
- onChange: (e) => setUrl(e.target.value),
588
- placeholder: "https://example.com",
589
- className: "rounded border border-base-contrast/20 bg-base px-2 py-1 text-sm text-base-contrast placeholder:text-base-contrast/40 focus:outline-none focus:ring-1 focus:ring-primary"
590
- }
591
- ),
592
- /* @__PURE__ */ jsxs("label", { className: "flex cursor-pointer items-center gap-2 text-sm text-base-contrast", children: [
593
- /* @__PURE__ */ jsx7(
594
- "input",
595
- {
596
- type: "checkbox",
597
- checked: openInNewTab,
598
- onChange: (e) => setOpenInNewTab(e.target.checked),
599
- className: "accent-primary"
600
- }
601
- ),
602
- "Open in new tab"
603
- ] })
604
- ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
605
- /* @__PURE__ */ jsxs(
606
- "select",
607
- {
608
- autoFocus: true,
609
- "aria-label": "Page",
610
- value: pageId,
611
- onChange: (e) => {
612
- setPageId(e.target.value);
613
- setAnchorId("");
614
- },
615
- className: selectClasses,
616
- children: [
617
- /* @__PURE__ */ jsx7("option", { value: "", children: "Select a page\u2026" }),
618
- pages.map((p) => /* @__PURE__ */ jsx7("option", { value: p.id, children: p.title }, p.id))
619
- ]
620
- }
621
- ),
622
- /* @__PURE__ */ jsxs(
623
- "select",
624
- {
625
- "aria-label": "Section",
626
- value: anchorId,
627
- disabled: !pageId,
628
- onChange: (e) => setAnchorId(e.target.value),
629
- className: cn(selectClasses, !pageId && "cursor-not-allowed opacity-50"),
630
- children: [
631
- /* @__PURE__ */ jsx7("option", { value: "", children: "None (top of page)" }),
632
- headings.map((h) => /* @__PURE__ */ jsx7("option", { value: h.id, children: h.label }, h.id))
633
- ]
634
- }
635
- )
636
- ] }),
637
- /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
638
- /* @__PURE__ */ jsx7(
639
- Button,
640
- {
641
- variant: "brand",
642
- size: "sm",
643
- onMouseDown: (e) => {
644
- e.preventDefault();
645
- handleApply();
646
- },
647
- children: "Apply"
648
- }
649
- ),
650
- hasLink && /* @__PURE__ */ jsx7(
651
- Button,
652
- {
653
- variant: "secondary",
654
- size: "sm",
655
- onMouseDown: (e) => {
656
- e.preventDefault();
657
- handleRemove();
658
- },
659
- children: "Remove"
660
- }
661
- ),
662
- /* @__PURE__ */ jsx7(
663
- Button,
664
- {
665
- variant: "secondary",
666
- size: "sm",
667
- onMouseDown: (e) => {
668
- e.preventDefault();
669
- onClose();
670
- },
671
- children: "Cancel"
672
- }
673
- )
674
- ] })
675
- ]
676
- }
677
- );
678
- }
679
-
680
- // src/components/primitives/RichTextToolbar.tsx
681
- import { Fragment as Fragment2, jsx as jsx8, jsxs as jsxs2 } from "react/jsx-runtime";
682
- function ToolbarButton({ isActive, onClick, title, children }) {
683
- return /* @__PURE__ */ jsx8(
684
- "button",
685
- {
686
- type: "button",
687
- title,
688
- "aria-label": title,
689
- "aria-pressed": isActive,
690
- onMouseDown: (e) => {
691
- e.preventDefault();
692
- onClick();
693
- },
694
- className: cn(
695
- "cursor-pointer rounded px-2 py-1 text-sm font-medium transition-colors",
696
- isActive ? "bg-primary text-primary-contrast" : "bg-base-accent text-base-contrast hover:opacity-80"
697
- ),
698
- children
699
- }
700
- );
701
- }
702
- function Separator() {
703
- return /* @__PURE__ */ jsx8("div", { className: "mx-1 h-5 w-px bg-base-contrast/20" });
704
- }
705
- function RichTextToolbar({ editor, preset }) {
706
- const [showLinkPopover, setShowLinkPopover] = useState4(false);
707
- const state = useEditorState({
708
- editor,
709
- selector: ({ editor: ed }) => ({
710
- isBold: ed.isActive("bold"),
711
- isItalic: ed.isActive("italic"),
712
- isUnderline: ed.isActive("underline"),
713
- isLink: ed.isActive("link"),
714
- isBulletList: ed.isActive("bulletList"),
715
- isOrderedList: ed.isActive("orderedList"),
716
- isH3: ed.isActive("heading", { level: 3 }),
717
- isH4: ed.isActive("heading", { level: 4 }),
718
- isLarge: ed.isActive("paragraph", { class: "large" }),
719
- isLeadIn: ed.isActive("paragraph", { class: "lead-in" })
720
- })
721
- });
722
- const toggleParagraphVariant = (cls, isCurrentlyActive) => {
723
- if (isCurrentlyActive) {
724
- editor.chain().focus().setNode("paragraph").run();
725
- } else {
726
- editor.chain().focus().setNode("paragraph", { class: cls }).run();
727
- }
728
- };
729
- return /* @__PURE__ */ jsxs2(
730
- "div",
731
- {
732
- "data-testid": "rich-text-toolbar",
733
- className: "flex items-center gap-1 rounded border border-base-contrast/10 bg-base-accent p-1 shadow-md",
734
- children: [
735
- /* @__PURE__ */ jsx8(
736
- ToolbarButton,
737
- {
738
- isActive: state.isBold,
739
- onClick: () => editor.chain().focus().toggleBold().run(),
740
- title: "Bold",
741
- children: /* @__PURE__ */ jsx8("strong", { children: "B" })
742
- }
743
- ),
744
- /* @__PURE__ */ jsx8(
745
- ToolbarButton,
746
- {
747
- isActive: state.isItalic,
748
- onClick: () => editor.chain().focus().toggleItalic().run(),
749
- title: "Italic",
750
- children: /* @__PURE__ */ jsx8("em", { children: "I" })
751
- }
752
- ),
753
- /* @__PURE__ */ jsx8(
754
- ToolbarButton,
755
- {
756
- isActive: state.isUnderline,
757
- onClick: () => editor.chain().focus().toggleUnderline().run(),
758
- title: "Underline",
759
- children: /* @__PURE__ */ jsx8("span", { className: "underline", children: "U" })
760
- }
761
- ),
762
- preset === "rich" && /* @__PURE__ */ jsxs2(Fragment2, { children: [
763
- /* @__PURE__ */ jsx8(Separator, {}),
764
- /* @__PURE__ */ jsxs2("div", { className: "relative", children: [
765
- /* @__PURE__ */ jsx8(
766
- ToolbarButton,
767
- {
768
- isActive: state.isLink || showLinkPopover,
769
- onClick: () => setShowLinkPopover((prev) => !prev),
770
- title: "Link",
771
- children: "\u{1F517}"
772
- }
773
- ),
774
- showLinkPopover && /* @__PURE__ */ jsx8("div", { className: "absolute left-0 top-full z-50 mt-1", children: /* @__PURE__ */ jsx8(
775
- LinkPopover,
776
- {
777
- editor,
778
- onClose: () => setShowLinkPopover(false)
779
- }
780
- ) })
781
- ] }),
782
- /* @__PURE__ */ jsx8(Separator, {}),
783
- /* @__PURE__ */ jsx8(
784
- ToolbarButton,
785
- {
786
- isActive: state.isBulletList,
787
- onClick: () => editor.chain().focus().toggleBulletList().run(),
788
- title: "Bullet List",
789
- children: "\u2022\u2261"
790
- }
791
- ),
792
- /* @__PURE__ */ jsx8(
793
- ToolbarButton,
794
- {
795
- isActive: state.isOrderedList,
796
- onClick: () => editor.chain().focus().toggleOrderedList().run(),
797
- title: "Ordered List",
798
- children: "1\u2261"
799
- }
800
- ),
801
- /* @__PURE__ */ jsx8(Separator, {}),
802
- /* @__PURE__ */ jsx8(
803
- ToolbarButton,
804
- {
805
- isActive: state.isH3,
806
- onClick: () => editor.chain().focus().toggleHeading({ level: 3 }).run(),
807
- title: "Heading 3",
808
- children: "H3"
809
- }
810
- ),
811
- /* @__PURE__ */ jsx8(
812
- ToolbarButton,
813
- {
814
- isActive: state.isH4,
815
- onClick: () => editor.chain().focus().toggleHeading({ level: 4 }).run(),
816
- title: "Heading 4",
817
- children: "H4"
818
- }
819
- ),
820
- /* @__PURE__ */ jsx8(
821
- ToolbarButton,
822
- {
823
- isActive: state.isLarge,
824
- onClick: () => toggleParagraphVariant("large", state.isLarge),
825
- title: "Large Paragraph",
826
- children: "Lg"
827
- }
828
- ),
829
- /* @__PURE__ */ jsx8(
830
- ToolbarButton,
831
- {
832
- isActive: state.isLeadIn,
833
- onClick: () => toggleParagraphVariant("lead-in", state.isLeadIn),
834
- title: "Lead-In",
835
- children: "Li"
836
- }
837
- )
838
- ] })
839
- ]
840
- }
841
- );
842
- }
843
-
844
- // src/components/primitives/EditableRichText.tsx
845
- import { jsx as jsx9, jsxs as jsxs3 } from "react/jsx-runtime";
846
- function isContentEmpty(html) {
847
- return html.replace(/<[^>]*>/g, "").trim().length === 0;
848
- }
849
- var DEFAULT_PLACEHOLDER = "Click to edit\u2026";
850
- function EditableRichText({
851
- value,
852
- onChange,
853
- isEditMode,
854
- preset = "rich",
855
- className,
856
- placeholder = DEFAULT_PLACEHOLDER
857
- }) {
858
- const { isEditorActive, editor, activate, deactivate } = useEditableRichText({ value, onChange, preset, placeholder });
859
- const wrapperRef = useRef4(null);
860
- const blurTimeoutRef = useRef4(null);
861
- const clickCoordsRef = useRef4(null);
862
- useEffect4(() => {
863
- return () => {
864
- if (blurTimeoutRef.current) {
865
- clearTimeout(blurTimeoutRef.current);
866
- }
867
- };
868
- }, []);
869
- useEffect4(() => {
870
- if (!isEditorActive || !editor) return;
871
- requestAnimationFrame(() => {
872
- if (editor.isDestroyed) return;
873
- const coords = clickCoordsRef.current;
874
- if (coords) {
875
- try {
876
- const pos = editor.view.posAtCoords(coords);
877
- if (pos) {
878
- editor.commands.focus();
879
- editor.commands.setTextSelection(pos.pos);
880
- } else {
881
- editor.commands.focus("end");
882
- }
883
- } catch {
884
- editor.commands.focus("end");
885
- } finally {
886
- clickCoordsRef.current = null;
887
- }
888
- } else {
889
- editor.commands.focus("end");
890
- }
891
- });
892
- }, [isEditorActive, editor]);
893
- const handleClick = useCallback4((e) => {
894
- if (isEditMode && !isEditorActive) {
895
- clickCoordsRef.current = { left: e.clientX, top: e.clientY };
896
- activate();
897
- }
898
- }, [isEditMode, isEditorActive, activate]);
899
- const handleFocus = useCallback4(() => {
900
- if (blurTimeoutRef.current) {
901
- clearTimeout(blurTimeoutRef.current);
902
- blurTimeoutRef.current = null;
903
- }
904
- }, []);
905
- const handleBlur = useCallback4(() => {
906
- blurTimeoutRef.current = setTimeout(() => {
907
- if (wrapperRef.current && !wrapperRef.current.contains(document.activeElement)) {
908
- deactivate();
909
- }
910
- }, 150);
911
- }, [deactivate]);
912
- if (!isEditMode) {
913
- return /* @__PURE__ */ jsx9(
914
- "div",
915
- {
916
- className,
917
- dangerouslySetInnerHTML: { __html: sanitizeHtml(value) }
918
- }
919
- );
920
- }
921
- if (!isEditorActive || !editor) {
922
- const empty = isContentEmpty(value);
923
- if (empty) {
924
- return /* @__PURE__ */ jsx9("div", { className, onClick: handleClick, children: /* @__PURE__ */ jsx9("p", { className: "tiptap-placeholder", children: placeholder }) });
925
- }
926
- return /* @__PURE__ */ jsx9(
927
- "div",
928
- {
929
- className,
930
- onClick: handleClick,
931
- dangerouslySetInnerHTML: { __html: sanitizeHtml(value) }
932
- }
933
- );
934
- }
935
- return /* @__PURE__ */ jsxs3(
936
- "div",
937
- {
938
- ref: wrapperRef,
939
- className,
940
- onFocus: handleFocus,
941
- onBlur: handleBlur,
942
- children: [
943
- /* @__PURE__ */ jsx9(
944
- BubbleMenu,
945
- {
946
- editor,
947
- className: "z-50",
948
- shouldShow: ({ editor: ed, state }) => {
949
- const { from, to } = state.selection;
950
- return ed.isFocused && from !== to;
951
- },
952
- children: /* @__PURE__ */ jsx9(RichTextToolbar, { editor, preset })
953
- }
954
- ),
955
- /* @__PURE__ */ jsx9(EditorContent, { editor })
956
- ]
957
- }
958
- );
959
- }
960
-
961
107
  // src/components/sections/Prose/Prose.tsx
962
- import { jsx as jsx10 } from "react/jsx-runtime";
108
+ import { jsx as jsx4 } from "react/jsx-runtime";
963
109
  function Prose({ body, onChange }) {
964
110
  if (onChange) {
965
- return /* @__PURE__ */ jsx10("div", { className: "prose-content", children: /* @__PURE__ */ jsx10(
111
+ return /* @__PURE__ */ jsx4("div", { className: "prose-content", children: /* @__PURE__ */ jsx4(
966
112
  EditableRichText,
967
113
  {
968
114
  value: body,
@@ -973,7 +119,7 @@ function Prose({ body, onChange }) {
973
119
  }
974
120
  ) });
975
121
  }
976
- return /* @__PURE__ */ jsx10(
122
+ return /* @__PURE__ */ jsx4(
977
123
  "div",
978
124
  {
979
125
  className: "prose-content",
@@ -982,254 +128,51 @@ function Prose({ body, onChange }) {
982
128
  );
983
129
  }
984
130
 
985
- // src/lib/text.ts
986
- function stripHtmlToPlainText(html) {
987
- return html.replace(/<(script|style)\b[^>]*>[\s\S]*?<\/\1>/gi, "").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
988
- }
989
- function truncate(text, maxLength) {
990
- if (text.length <= maxLength) return text;
991
- return text.slice(0, maxLength) + "...";
992
- }
993
- function pluralize(n, word) {
994
- return `${n} ${word}${n === 1 ? "" : "s"}`;
995
- }
996
-
997
131
  // src/components/sections/Prose/index.tsx
998
- import { jsx as jsx11 } from "react/jsx-runtime";
999
- var schema = z2.object({
1000
- type: z2.literal("prose"),
1001
- content: z2.object({ body: z2.string() })
132
+ import { jsx as jsx5 } from "react/jsx-runtime";
133
+ var schema = z.object({
134
+ type: z.literal("prose"),
135
+ content: z.object({ body: z.string() })
1002
136
  });
1003
137
  var Prose_default = defineSection({
1004
138
  type: "prose",
1005
139
  label: "Prose",
1006
- icon: /* @__PURE__ */ jsx11(AlignLeft, { size: 18 }),
140
+ icon: /* @__PURE__ */ jsx5(AlignLeft, { size: 18 }),
1007
141
  schema,
1008
142
  richTextFields: ["body"],
1009
- component: ({ content, onChange }) => /* @__PURE__ */ jsx11(Prose, { body: content.content.body, onChange: onChange ? (c) => onChange(c) : void 0 }),
143
+ component: ({ content, onChange }) => /* @__PURE__ */ jsx5(Prose, { body: content.content.body, onChange: onChange ? (c) => onChange(c) : void 0 }),
1010
144
  defaults: () => ({ type: "prose", content: { body: "<p></p>" } }),
1011
145
  getLabel: (content) => truncate(stripHtmlToPlainText(content.content.body), 60)
1012
146
  });
1013
147
 
1014
148
  // src/components/sections/Media/index.tsx
1015
- import { z as z3 } from "zod";
1016
- import { Image as ImageIcon2 } from "lucide-react";
1017
-
1018
- // src/components/sections/Media/MediaBlock.tsx
1019
- import { Check, X } from "lucide-react";
1020
-
1021
- // src/components/primitives/ResolvedMedia.tsx
1022
- import { ImageIcon } from "lucide-react";
1023
-
1024
- // src/hooks/useResolvedMedia.ts
1025
- import { useMemo } from "react";
1026
-
1027
- // src/components/shell/MediaLibraryContext.tsx
1028
- import { createContext as createContext2, useContext as useContext2 } from "react";
1029
- var MediaLibraryContext = createContext2(null);
1030
- function useMediaLibrary() {
1031
- return useContext2(MediaLibraryContext);
1032
- }
1033
-
1034
- // src/hooks/useResolvedMedia.ts
1035
- function useResolvedMedia(imageId) {
1036
- const ctx = useMediaLibrary();
1037
- return useMemo(() => {
1038
- if (!imageId || !ctx) {
1039
- return { src: void 0, srcset: void 0, poster: void 0, alt: "", kind: "image" };
1040
- }
1041
- const { manifest, pendingItems, pendingLocalUrls, siteConfig } = ctx;
1042
- const sizes = siteConfig?.media?.sizes ?? [640, 1080, 1920];
1043
- const manifestItem = manifest.images[imageId];
1044
- const pendingItem = pendingItems.find((i) => i.id === imageId);
1045
- const item = manifestItem ?? pendingItem;
1046
- const alt = item?.alt ?? "";
1047
- const kind = item?.kind ?? "image";
1048
- const localUrl = pendingLocalUrls[imageId];
1049
- if (localUrl) {
1050
- return { src: localUrl, srcset: void 0, poster: void 0, alt, kind };
1051
- }
1052
- if (manifestItem) {
1053
- const provider = getMediaProvider();
1054
- const resolved = provider.resolve(manifestItem, sizes);
1055
- if (resolved && resolved.tag === "img") {
1056
- return {
1057
- src: resolved.src,
1058
- srcset: "srcSet" in resolved ? resolved.srcSet : void 0,
1059
- poster: void 0,
1060
- alt,
1061
- kind
1062
- };
1063
- }
1064
- if (resolved && resolved.tag === "video") {
1065
- return { src: resolved.src, srcset: void 0, poster: resolved.poster, alt, kind };
1066
- }
1067
- }
1068
- return { src: void 0, srcset: void 0, poster: void 0, alt, kind };
1069
- }, [imageId, ctx]);
1070
- }
1071
-
1072
- // src/components/primitives/ResolvedMedia.tsx
1073
- import { jsx as jsx12, jsxs as jsxs4 } from "react/jsx-runtime";
1074
- function ResolvedMedia({
1075
- imageId,
1076
- src: propSrc,
1077
- srcset: propSrcset,
1078
- alt: propAlt,
1079
- className,
1080
- imgClassName,
1081
- invertFrom
1082
- }) {
1083
- const resolved = useResolvedMedia(imageId);
1084
- const src = propSrc || resolved.src;
1085
- const srcset = propSrcset || resolved.srcset;
1086
- const poster = resolved.poster;
1087
- const alt = propAlt ?? resolved.alt;
1088
- const kind = resolved.kind;
1089
- const invertClass = invertFrom === "light" ? "invert dark:invert-0" : invertFrom === "dark" ? "dark:invert" : void 0;
1090
- const mediaClass = cn("h-full w-full", imgClassName, invertClass);
1091
- return /* @__PURE__ */ jsx12("div", { className, children: src ? kind === "video" ? /* @__PURE__ */ jsx12("video", { src, poster, muted: true, playsInline: true, loop: true, autoPlay: true, className: mediaClass }) : /* @__PURE__ */ jsx12("img", { src, srcSet: srcset, alt, className: mediaClass }) : /* @__PURE__ */ jsxs4("div", { className: "flex aspect-video h-full w-full flex-col items-center justify-center gap-1 rounded bg-base-accent text-base-contrast-light/50", children: [
1092
- /* @__PURE__ */ jsx12(ImageIcon, { size: 24 }),
1093
- /* @__PURE__ */ jsx12("span", { className: "text-sm", children: "No Image" })
1094
- ] }) });
1095
- }
1096
-
1097
- // src/components/primitives/ImageDropZone.tsx
1098
- import { useState as useState5 } from "react";
1099
- import { Fragment as Fragment3, jsx as jsx13, jsxs as jsxs5 } from "react/jsx-runtime";
1100
- var ACCEPTED = /^(image|video)\//;
1101
- function ImageDropZone({ onImageSelected, className, children }) {
1102
- const mediaLibrary = useMediaLibrary();
1103
- const [dragging, setDragging] = useState5(false);
1104
- if (!mediaLibrary) return /* @__PURE__ */ jsx13(Fragment3, { children });
1105
- const maxFileSize = mediaLibrary.siteConfig?.media.maxFileSize;
1106
- const hasFiles = (e) => e.dataTransfer.types.includes("Files");
1107
- const handleDragOver = (e) => {
1108
- if (!hasFiles(e)) return;
1109
- e.preventDefault();
1110
- e.dataTransfer.dropEffect = "copy";
1111
- setDragging(true);
1112
- };
1113
- const handleDragLeave = (e) => {
1114
- if (!hasFiles(e)) return;
1115
- const related = e.relatedTarget;
1116
- if (related && e.currentTarget.contains(related)) return;
1117
- setDragging(false);
1118
- };
1119
- const handleDrop = (e) => {
1120
- if (!hasFiles(e)) return;
1121
- e.preventDefault();
1122
- e.stopPropagation();
1123
- setDragging(false);
1124
- const file = e.dataTransfer.files[0];
1125
- if (!file) return;
1126
- if (!ACCEPTED.test(file.type)) {
1127
- mediaLibrary.reportRejectedUploads([{ name: file.name, reason: "type" }]);
1128
- return;
1129
- }
1130
- if (maxFileSize && file.size > maxFileSize) {
1131
- mediaLibrary.reportRejectedUploads([{ name: file.name, reason: "size" }]);
1132
- return;
1133
- }
1134
- mediaLibrary.uploadFileWithCallback(file, onImageSelected);
1135
- };
1136
- return /* @__PURE__ */ jsxs5(
1137
- "div",
1138
- {
1139
- className: cn("relative", className),
1140
- onDragOver: handleDragOver,
1141
- onDragLeave: handleDragLeave,
1142
- onDrop: handleDrop,
1143
- children: [
1144
- children,
1145
- dragging && /* @__PURE__ */ jsx13("div", { className: "pointer-events-none absolute inset-0 z-10 rounded-md border-2 border-primary bg-primary/5" })
1146
- ]
1147
- }
1148
- );
1149
- }
1150
-
1151
- // src/components/sections/Media/MediaBlock.tsx
1152
- import { jsx as jsx14, jsxs as jsxs6 } from "react/jsx-runtime";
1153
- function MediaBlock({ refValue, options, link, dodont, isEditMode, onRefChange }) {
1154
- const mediaLibrary = useMediaLibrary();
1155
- const fit = refValue.objectFit ?? options.objectFit;
1156
- const fitClass = fit === "cover" ? "object-cover" : "object-contain";
1157
- const showBorder = refValue.border ?? options.border;
1158
- const captionStr = refValue.caption ? Array.isArray(refValue.caption) ? refValue.caption.join("\n") : refValue.caption : "";
1159
- const replace = onRefChange ? (imageId) => onRefChange({ ...refValue, imageId }) : void 0;
1160
- const resolved = /* @__PURE__ */ jsx14(
1161
- ResolvedMedia,
1162
- {
1163
- imageId: refValue.imageId || void 0,
1164
- className: "h-full w-full",
1165
- imgClassName: fitClass,
1166
- invertFrom: refValue.invertFrom
1167
- }
1168
- );
1169
- let media = isEditMode && replace ? /* @__PURE__ */ jsx14(ImageDropZone, { onImageSelected: replace, className: "h-full w-full", children: resolved }) : resolved;
1170
- if (!isEditMode && link && link.kind === "external" && link.href) {
1171
- media = /* @__PURE__ */ jsx14("a", { href: link.href, target: link.target, className: "group block", children: media });
1172
- }
1173
- const onImageClick = isEditMode && replace && mediaLibrary ? () => mediaLibrary.openSelectModal((imageId) => replace(imageId)) : void 0;
1174
- return /* @__PURE__ */ jsxs6("figure", { children: [
1175
- dodont && /* @__PURE__ */ jsx14("div", { className: "flex h-10 items-center justify-center pb-1", children: dodont === "do" ? /* @__PURE__ */ jsx14(Check, { size: 28, className: "text-green-600" }) : /* @__PURE__ */ jsx14(X, { size: 28, className: "text-red-600" }) }),
1176
- /* @__PURE__ */ jsx14(
1177
- "div",
1178
- {
1179
- "data-testid": "media-open-surface",
1180
- className: cn(
1181
- "overflow-hidden rounded-md",
1182
- showBorder && "border border-base-200",
1183
- options.square && "aspect-square",
1184
- onImageClick && "cursor-pointer"
1185
- ),
1186
- onClick: onImageClick,
1187
- onPointerDown: onImageClick ? (e) => e.stopPropagation() : void 0,
1188
- children: media
1189
- }
1190
- ),
1191
- options.showCaption && /* @__PURE__ */ jsx14("figcaption", { className: "mt-2 min-h-[1em] text-sm text-base-contrast-light", children: isEditMode && onRefChange ? /* @__PURE__ */ jsx14(
1192
- EditablePlainText,
1193
- {
1194
- tag: "span",
1195
- value: captionStr,
1196
- onChange: (caption) => onRefChange({ ...refValue, caption: caption || void 0 }),
1197
- isEditMode: true,
1198
- placeholder: "Caption",
1199
- className: "block min-h-[1em]"
1200
- }
1201
- ) : captionStr || " " })
1202
- ] });
1203
- }
1204
-
1205
- // src/components/sections/Media/index.tsx
1206
- import { jsx as jsx15 } from "react/jsx-runtime";
1207
- var schema2 = z3.object({
1208
- type: z3.literal("media"),
1209
- content: z3.object({
149
+ import { z as z2 } from "zod";
150
+ import { Image as ImageIcon } from "lucide-react";
151
+ import { jsx as jsx6 } from "react/jsx-runtime";
152
+ var schema2 = z2.object({
153
+ type: z2.literal("media"),
154
+ content: z2.object({
1210
155
  ref: SingleMediaReferenceSchema,
1211
- link: LinkValueSchema.optional(),
1212
- dodont: z3.enum(["do", "dont"]).optional()
156
+ link: LinkValueSchema.optional()
1213
157
  }),
1214
- options: z3.object({
1215
- square: z3.boolean().optional(),
1216
- showCaption: z3.boolean().optional(),
1217
- border: z3.boolean().optional(),
1218
- objectFit: z3.enum(["cover", "contain"]).optional()
158
+ options: z2.object({
159
+ square: z2.boolean().optional(),
160
+ showCaption: z2.boolean().optional(),
161
+ border: z2.boolean().optional(),
162
+ objectFit: z2.enum(["cover", "contain"]).optional()
1219
163
  }).default({})
1220
164
  });
1221
165
  var Media_default = defineSection({
1222
166
  type: "media",
1223
167
  label: "Media",
1224
- icon: /* @__PURE__ */ jsx15(ImageIcon2, { size: 18 }),
168
+ icon: /* @__PURE__ */ jsx6(ImageIcon, { size: 18 }),
1225
169
  schema: schema2,
1226
- component: ({ content, options, onChange }) => /* @__PURE__ */ jsx15(
170
+ component: ({ content, options, onChange }) => /* @__PURE__ */ jsx6(
1227
171
  MediaBlock,
1228
172
  {
1229
173
  refValue: content.content.ref,
1230
174
  options: options ?? {},
1231
175
  link: content.content.link,
1232
- dodont: content.content.dodont,
1233
176
  isEditMode: !!onChange,
1234
177
  onRefChange: onChange ? (ref) => onChange({ ...content, content: { ...content.content, ref } }) : void 0
1235
178
  }
@@ -1242,1863 +185,126 @@ var Media_default = defineSection({
1242
185
  getLabel: () => "Media",
1243
186
  getThumbnails: (content) => content.content.ref.imageId ? [{ type: "image", src: content.content.ref.imageId }] : [],
1244
187
  settings: {
1245
- square: { type: "checkbox", label: "Square aspect ratio", default: false },
1246
- showCaption: { type: "checkbox", label: "Show caption", default: false },
1247
- border: { type: "checkbox", label: "Border", default: false },
1248
- objectFit: {
1249
- type: "select",
1250
- label: "Image fit",
1251
- default: "contain",
1252
- options: [
1253
- { label: "Contain", value: "contain" },
1254
- { label: "Cover", value: "cover" }
1255
- ]
1256
- },
1257
- link: { type: "link", label: "Link", default: DEFAULT_LINK, target: "content" },
1258
- dodont: {
1259
- type: "select",
1260
- label: "Do / Don't",
1261
- target: "content",
1262
- default: "",
1263
- emptyIsUndefined: true,
1264
- options: [
1265
- { label: "None", value: "" },
1266
- { label: "Do", value: "do" },
1267
- { label: "Don't", value: "dont" }
1268
- ]
1269
- }
1270
- },
1271
- inheritableSettings: ["square", "showCaption", "border", "objectFit"],
1272
- settingsTabs: [
1273
- { label: "Display", fields: ["square", "showCaption", "border", "objectFit"] },
1274
- { label: "Link", fields: ["link"] },
1275
- { label: "Do / Don't", fields: ["dodont"] }
1276
- ]
1277
- });
1278
-
1279
- // src/components/sections/Button/index.tsx
1280
- import { z as z4 } from "zod";
1281
- import { RectangleHorizontal } from "lucide-react";
1282
-
1283
- // src/components/sections/Button/CTAButton.tsx
1284
- import { Download } from "lucide-react";
1285
- import { jsx as jsx16, jsxs as jsxs7 } from "react/jsx-runtime";
1286
- function Button3({ text, link, download, onChange }) {
1287
- const base = "inline-block rounded-md px-6 py-3 font-bold transition-colors";
1288
- const variant = "border-2 border-primary text-primary hover:bg-primary hover:text-primary-contrast";
1289
- if (onChange) {
1290
- return /* @__PURE__ */ jsxs7("span", { className: cn(base, variant, "inline-flex items-center gap-2"), children: [
1291
- download && /* @__PURE__ */ jsx16(Download, { size: 16, className: "shrink-0" }),
1292
- /* @__PURE__ */ jsx16(
1293
- EditablePlainText,
1294
- {
1295
- tag: "span",
1296
- value: text,
1297
- onChange: (newText) => onChange({ type: "button", content: { text: newText, link, download } }),
1298
- isEditMode: true,
1299
- placeholder: "Button text"
1300
- }
1301
- )
1302
- ] });
1303
- }
1304
- const href = link && link.kind === "external" ? link.href : "";
1305
- const target = link?.target;
1306
- if (href) {
1307
- return /* @__PURE__ */ jsxs7(
1308
- "a",
1309
- {
1310
- href,
1311
- target,
1312
- download: download ? "" : void 0,
1313
- className: cn(base, variant, download && "inline-flex items-center gap-2"),
1314
- children: [
1315
- download && /* @__PURE__ */ jsx16(Download, { size: 16, className: "shrink-0" }),
1316
- text
1317
- ]
1318
- }
1319
- );
1320
- }
1321
- return /* @__PURE__ */ jsx16("button", { className: cn("cursor-pointer", base, variant), children: text });
1322
- }
1323
-
1324
- // src/components/sections/Button/index.tsx
1325
- import { jsx as jsx17 } from "react/jsx-runtime";
1326
- var schema3 = z4.object({
1327
- type: z4.literal("button"),
1328
- content: z4.object({
1329
- text: z4.string(),
1330
- link: LinkValueSchema.optional(),
1331
- download: z4.boolean().optional()
1332
- })
1333
- });
1334
- var Button_default = defineSection({
1335
- type: "button",
1336
- label: "Button",
1337
- icon: /* @__PURE__ */ jsx17(RectangleHorizontal, { size: 18 }),
1338
- schema: schema3,
1339
- component: ({ content, onChange }) => /* @__PURE__ */ jsx17(
1340
- Button3,
1341
- {
1342
- text: content.content.text,
1343
- link: content.content.link,
1344
- download: content.content.download,
1345
- onChange: onChange ? (c) => onChange(c) : void 0
1346
- }
1347
- ),
1348
- defaults: () => ({ type: "button", content: { text: "Button" } }),
1349
- getLabel: (content) => content.content.text,
1350
- settings: {
1351
- link: { type: "link", label: "Link", default: DEFAULT_LINK, target: "content" },
1352
- download: { type: "checkbox", label: "Download link", default: false, target: "content" }
1353
- }
1354
- });
1355
-
1356
- // src/components/sections/Colors/index.tsx
1357
- import { z as z5 } from "zod";
1358
- import { Palette } from "lucide-react";
1359
-
1360
- // src/components/brandguide/Colors.tsx
1361
- import { useState as useState9, useEffect as useEffect7 } from "react";
1362
- import { Eye, EyeOff } from "lucide-react";
1363
-
1364
- // src/lib/container-grid.ts
1365
- var MAX_CONTAINER_COLUMNS = 6;
1366
- var containerGridClass = {
1367
- 1: "grid-cols-1",
1368
- 2: "grid-cols-1 @sm:grid-cols-2",
1369
- 3: "grid-cols-1 @sm:grid-cols-2 @lg:grid-cols-3",
1370
- 4: "grid-cols-1 @xs:grid-cols-2 @lg:grid-cols-4",
1371
- 5: "grid-cols-1 @xs:grid-cols-2 @md:grid-cols-3 @xl:grid-cols-5",
1372
- 6: "grid-cols-1 @xs:grid-cols-2 @md:grid-cols-3 @2xl:grid-cols-6"
1373
- };
1374
- var colSpanClass = {
1375
- 1: { 1: "" },
1376
- 2: { 1: "", 2: "col-span-1 @sm:col-span-2" },
1377
- 3: { 1: "", 2: "col-span-1 @sm:col-span-2", 3: "col-span-1 @sm:col-span-2 @lg:col-span-3" },
1378
- 4: { 1: "", 2: "col-span-1 @xs:col-span-2", 3: "col-span-1 @xs:col-span-2 @lg:col-span-3", 4: "col-span-1 @xs:col-span-2 @lg:col-span-4" },
1379
- 5: { 1: "", 2: "col-span-1 @xs:col-span-2", 3: "col-span-1 @xs:col-span-2 @md:col-span-3", 4: "col-span-1 @xs:col-span-2 @md:col-span-3 @xl:col-span-4", 5: "col-span-1 @xs:col-span-2 @md:col-span-3 @xl:col-span-5" },
1380
- 6: { 1: "", 2: "col-span-1 @xs:col-span-2", 3: "col-span-1 @xs:col-span-2 @md:col-span-3", 4: "col-span-1 @xs:col-span-2 @md:col-span-3 @2xl:col-span-4", 5: "col-span-1 @xs:col-span-2 @md:col-span-3 @2xl:col-span-5", 6: "col-span-1 @xs:col-span-2 @md:col-span-3 @2xl:col-span-6" }
1381
- };
1382
- var spacerHiddenClass = {
1383
- 1: "hidden",
1384
- 2: "hidden @sm:block",
1385
- 3: "hidden @sm:block",
1386
- 4: "hidden @xs:block",
1387
- 5: "hidden @xs:block",
1388
- 6: "hidden @xs:block"
1389
- };
1390
-
1391
- // src/components/primitives/EditableGrid.tsx
1392
- import { useRef as useRef6, useEffect as useEffect6, useState as useState7 } from "react";
1393
- import { ImageIcon as ImageIcon3 } from "lucide-react";
1394
-
1395
- // src/components/shared/icons.tsx
1396
- import { GripVertical, Plus, Trash2, X as X2, Settings } from "lucide-react";
1397
- import { GripVertical as GripVertical2, Plus as Plus2, Trash2 as Trash22, X as X3, Settings as Settings2 } from "lucide-react";
1398
- import { jsx as jsx18 } from "react/jsx-runtime";
1399
- function DragHandle({ size = 16 }) {
1400
- return /* @__PURE__ */ jsx18(GripVertical, { size });
1401
- }
1402
- function AddIcon({ size = 14 }) {
1403
- return /* @__PURE__ */ jsx18(Plus, { size });
1404
- }
1405
- function DeleteIcon({ size = 14 }) {
1406
- return /* @__PURE__ */ jsx18(Trash2, { size });
1407
- }
1408
- function SettingsIcon({ size = 14 }) {
1409
- return /* @__PURE__ */ jsx18(Settings, { size });
1410
- }
1411
-
1412
- // src/components/shared/IconButton.tsx
1413
- import { forwardRef as forwardRef2 } from "react";
1414
- import { jsx as jsx19 } from "react/jsx-runtime";
1415
- var sizeClasses = {
1416
- sm: "h-chrome-sm w-chrome-sm",
1417
- md: "h-chrome-md w-chrome-md",
1418
- lg: "h-chrome-lg w-chrome-lg"
1419
- };
1420
- var intentClasses = {
1421
- default: "text-base-contrast-light hover:text-base-contrast",
1422
- destructive: "text-destructive hover:text-destructive-hover",
1423
- primary: "text-primary/60 hover:text-primary"
1424
- };
1425
- var IconButton = forwardRef2(
1426
- function IconButton2({ icon, label, size = "md", intent = "default", className, ...rest }, ref) {
1427
- return /* @__PURE__ */ jsx19(
1428
- "button",
1429
- {
1430
- ref,
1431
- type: "button",
1432
- className: cn(
1433
- "cursor-pointer flex items-center justify-center rounded transition-colors",
1434
- sizeClasses[size],
1435
- intentClasses[intent],
1436
- className
1437
- ),
1438
- "aria-label": label,
1439
- ...rest,
1440
- children: icon
1441
- }
1442
- );
1443
- }
1444
- );
1445
-
1446
- // src/components/primitives/useEditableCollection.ts
1447
- import { useState as useState6, useCallback as useCallback5, useRef as useRef5, useEffect as useEffect5 } from "react";
1448
- var nextId = 0;
1449
- function uid() {
1450
- return typeof crypto !== "undefined" && typeof crypto.randomUUID === "function" ? crypto.randomUUID() : `_id_${nextId++}_${Date.now()}`;
1451
- }
1452
- function wrapItems(items) {
1453
- return items.map((data) => ({ id: uid(), data }));
1454
- }
1455
- function useEditableCollection({
1456
- items,
1457
- onChange,
1458
- createItem
1459
- }) {
1460
- const [wrappedItems, setWrappedItems] = useState6(
1461
- () => wrapItems(items)
1462
- );
1463
- const prevItemsRef = useRef5(items);
1464
- useEffect5(() => {
1465
- if (items === prevItemsRef.current) return;
1466
- prevItemsRef.current = items;
1467
- setWrappedItems((prev) => {
1468
- return items.map((data, i) => {
1469
- if (i < prev.length) {
1470
- return { id: prev[i].id, data };
1471
- }
1472
- return { id: uid(), data };
1473
- });
1474
- });
1475
- }, [items]);
1476
- const onReorder = useCallback5(
1477
- (fromIndex, toIndex) => {
1478
- setWrappedItems((prev) => {
1479
- const next = [...prev];
1480
- const [moved] = next.splice(fromIndex, 1);
1481
- next.splice(toIndex, 0, moved);
1482
- onChange(next.map((w) => w.data));
1483
- return next;
1484
- });
1485
- },
1486
- [onChange]
1487
- );
1488
- const onAdd = useCallback5(() => {
1489
- const newItem = createItem();
1490
- setWrappedItems((prev) => {
1491
- const next = [...prev, { id: uid(), data: newItem }];
1492
- onChange(next.map((w) => w.data));
1493
- return next;
1494
- });
1495
- }, [createItem, onChange]);
1496
- const onInsert = useCallback5(
1497
- (index) => {
1498
- const newItem = createItem();
1499
- setWrappedItems((prev) => {
1500
- const next = [...prev];
1501
- next.splice(index, 0, { id: uid(), data: newItem });
1502
- onChange(next.map((w) => w.data));
1503
- return next;
1504
- });
1505
- },
1506
- [createItem, onChange]
1507
- );
1508
- const onRemove = useCallback5(
1509
- (id) => {
1510
- setWrappedItems((prev) => {
1511
- const next = prev.filter((w) => w.id !== id);
1512
- onChange(next.map((w) => w.data));
1513
- return next;
1514
- });
1515
- },
1516
- [onChange]
1517
- );
1518
- return { wrappedItems, onReorder, onAdd, onInsert, onRemove };
1519
- }
1520
-
1521
- // src/components/primitives/EditableGrid.tsx
1522
- import { jsx as jsx20, jsxs as jsxs8 } from "react/jsx-runtime";
1523
- function EditableGrid({
1524
- items,
1525
- columns,
1526
- onChange,
1527
- createItem,
1528
- isEditMode,
1529
- renderItem,
1530
- onItemSettings,
1531
- onItemImageClick,
1532
- chromeTopClass,
1533
- className
1534
- }) {
1535
- const { wrappedItems, onReorder, onAdd, onInsert, onRemove } = useEditableCollection({
1536
- items,
1537
- onChange,
1538
- createItem
1539
- });
1540
- const [dragState, setDragState] = useState7({ sourceId: null, closestEdge: null });
1541
- const [isDragging, setIsDragging] = useState7(false);
1542
- useEffect6(() => {
1543
- let cleanup;
1544
- let cancelled = false;
1545
- import("../../adapter-HH47ZPGM.js").then(({ monitorForElements }) => {
1546
- if (cancelled) return;
1547
- cleanup = monitorForElements({
1548
- onDragStart: ({ source }) => {
1549
- if (source.data.dragType === "grid-cell") setIsDragging(true);
1550
- },
1551
- onDrop: ({ source }) => {
1552
- if (source.data.dragType === "grid-cell") setIsDragging(false);
1553
- }
1554
- });
1555
- });
1556
- return () => {
1557
- cancelled = true;
1558
- cleanup?.();
1559
- };
1560
- }, []);
1561
- if (!isEditMode) {
1562
- return /* @__PURE__ */ jsx20("div", { className: "@container", children: /* @__PURE__ */ jsx20("div", { className: cn("grid gap-4", containerGridClass[columns] || containerGridClass[1], className), children: wrappedItems.map((wrapped, index) => /* @__PURE__ */ jsx20("div", { children: renderItem(wrapped.data, { isEditMode: false, index }) }, wrapped.id)) }) });
1563
- }
1564
- return /* @__PURE__ */ jsx20("div", { className: "@container", children: /* @__PURE__ */ jsxs8("div", { className: cn("group/grid relative grid gap-4", containerGridClass[columns] || containerGridClass[1], className), children: [
1565
- wrappedItems.map((wrapped, index) => /* @__PURE__ */ jsx20(
1566
- GridCell,
1567
- {
1568
- id: wrapped.id,
1569
- index,
1570
- isLast: index === wrappedItems.length - 1,
1571
- isDragging,
1572
- onReorder,
1573
- onRemove,
1574
- onInsert,
1575
- onSettings: onItemSettings ? () => onItemSettings(index) : void 0,
1576
- onImageClick: onItemImageClick ? () => onItemImageClick(index) : void 0,
1577
- chromeTopClass,
1578
- dragState,
1579
- setDragState,
1580
- children: renderItem(wrapped.data, { isEditMode: true, index })
1581
- },
1582
- wrapped.id
1583
- )),
1584
- /* @__PURE__ */ jsx20(
1585
- IconButton,
1586
- {
1587
- icon: /* @__PURE__ */ jsx20(AddIcon, { size: 16 }),
1588
- label: "Add item",
1589
- size: "lg",
1590
- intent: "primary",
1591
- onClick: onAdd,
1592
- className: "absolute -bottom-6 left-1/2 z-20 -translate-x-1/2 rounded-full border border-base-200 bg-base opacity-0 transition-opacity group-hover/grid:opacity-100"
1593
- }
1594
- )
1595
- ] }) });
1596
- }
1597
- function GridCell({
1598
- id,
1599
- index,
1600
- isLast,
1601
- isDragging,
1602
- onReorder,
1603
- onRemove,
1604
- onInsert,
1605
- onSettings,
1606
- onImageClick,
1607
- chromeTopClass,
1608
- dragState,
1609
- setDragState,
1610
- children
1611
- }) {
1612
- const cellRef = useRef6(null);
1613
- const handleRef = useRef6(null);
1614
- useEffect6(() => {
1615
- const cell = cellRef.current;
1616
- const handle = handleRef.current;
1617
- if (!cell || !handle) return;
1618
- let cleanup;
1619
- let cancelled = false;
1620
- Promise.all([
1621
- import("../../adapter-HH47ZPGM.js"),
1622
- import("../../closest-edge-EBOXL3YW.js")
1623
- ]).then(([{ draggable, dropTargetForElements }, { attachClosestEdge, extractClosestEdge }]) => {
1624
- if (cancelled) return;
1625
- const cleanupDraggable = draggable({
1626
- element: handle,
1627
- getInitialData: () => ({ dragType: "grid-cell", id, index }),
1628
- onDragStart: () => setDragState({ sourceId: id, closestEdge: null }),
1629
- onDrop: () => setDragState({ sourceId: null, closestEdge: null })
1630
- });
1631
- const cleanupDropTarget = dropTargetForElements({
1632
- element: cell,
1633
- canDrop: ({ source }) => source.data.dragType === "grid-cell",
1634
- getData: ({ input, element }) => attachClosestEdge(
1635
- { id, index },
1636
- { input, element, allowedEdges: ["left", "right"] }
1637
- ),
1638
- onDragEnter: ({ self }) => {
1639
- const edge = extractClosestEdge(self.data);
1640
- if (edge) setDragState((prev) => ({ ...prev, closestEdge: { id, edge } }));
1641
- },
1642
- onDrag: ({ self }) => {
1643
- const edge = extractClosestEdge(self.data);
1644
- if (edge) setDragState((prev) => ({ ...prev, closestEdge: { id, edge } }));
1645
- },
1646
- onDragLeave: () => {
1647
- setDragState((prev) => prev.closestEdge?.id === id ? { ...prev, closestEdge: null } : prev);
1648
- },
1649
- onDrop: ({ source, self }) => {
1650
- const fromIndex = source.data.index;
1651
- const edge = extractClosestEdge(self.data);
1652
- let toIndex = index;
1653
- if (edge === "right") toIndex = index + 1;
1654
- if (fromIndex < toIndex) toIndex--;
1655
- if (fromIndex !== toIndex) {
1656
- onReorder(fromIndex, toIndex);
1657
- }
1658
- }
1659
- });
1660
- cleanup = () => {
1661
- cleanupDraggable();
1662
- cleanupDropTarget();
1663
- };
1664
- });
1665
- return () => {
1666
- cancelled = true;
1667
- cleanup?.();
1668
- };
1669
- }, [id, index, onReorder, setDragState]);
1670
- const isCellDragging = dragState.sourceId === id;
1671
- const showLeftEdge = dragState.closestEdge?.id === id && dragState.closestEdge.edge === "left" && dragState.sourceId !== id;
1672
- const showRightEdge = dragState.closestEdge?.id === id && dragState.closestEdge.edge === "right" && dragState.sourceId !== id;
1673
- return /* @__PURE__ */ jsxs8(
1674
- "div",
1675
- {
1676
- ref: cellRef,
1677
- className: cn(
1678
- "group/cell relative",
1679
- isCellDragging && "opacity-50"
1680
- ),
1681
- children: [
1682
- showLeftEdge && /* @__PURE__ */ jsx20("div", { className: "absolute top-0 bottom-0 left-0 z-10 w-0.5 -translate-x-2 bg-primary" }),
1683
- showRightEdge && /* @__PURE__ */ jsx20("div", { className: "absolute top-0 right-0 bottom-0 z-10 w-0.5 translate-x-2 bg-primary" }),
1684
- !isDragging && !isLast && /* @__PURE__ */ jsx20(
1685
- IconButton,
1686
- {
1687
- icon: /* @__PURE__ */ jsx20(AddIcon, { size: 12 }),
1688
- label: "Insert item",
1689
- size: "sm",
1690
- intent: "primary",
1691
- onClick: () => onInsert(index + 1),
1692
- className: "absolute top-1/2 -right-2 z-20 -translate-y-1/2 translate-x-1/2 rounded-full border border-base-200 bg-base opacity-0 group-hover/cell:opacity-100"
1693
- }
1694
- ),
1695
- /* @__PURE__ */ jsx20(
1696
- IconButton,
1697
- {
1698
- ref: handleRef,
1699
- icon: /* @__PURE__ */ jsx20(DragHandle, { size: 16 }),
1700
- label: "Drag to reorder",
1701
- className: cn("absolute left-1.5 z-10 cursor-grab bg-base/80 opacity-0 shadow-sm group-hover/cell:opacity-100 no-hover:opacity-100", chromeTopClass || "top-1.5"),
1702
- tabIndex: -1
1703
- }
1704
- ),
1705
- /* @__PURE__ */ jsxs8("div", { className: cn("absolute right-1.5 z-10 flex items-center gap-1 opacity-0 group-hover/cell:opacity-100 no-hover:opacity-100", chromeTopClass || "top-1.5"), children: [
1706
- onImageClick && /* @__PURE__ */ jsx20(
1707
- IconButton,
1708
- {
1709
- icon: /* @__PURE__ */ jsx20(ImageIcon3, { size: 16 }),
1710
- label: "Change image",
1711
- onClick: onImageClick,
1712
- className: "bg-base/80 shadow-sm"
1713
- }
1714
- ),
1715
- onSettings && /* @__PURE__ */ jsx20(
1716
- IconButton,
1717
- {
1718
- icon: /* @__PURE__ */ jsx20(SettingsIcon, { size: 16 }),
1719
- label: "Item settings",
1720
- onClick: onSettings,
1721
- className: "bg-base/80 shadow-sm"
1722
- }
1723
- ),
1724
- /* @__PURE__ */ jsx20(
1725
- IconButton,
1726
- {
1727
- icon: /* @__PURE__ */ jsx20(DeleteIcon, { size: 16 }),
1728
- label: "Delete item",
1729
- intent: "destructive",
1730
- onClick: () => onRemove(id),
1731
- className: "bg-base/80 shadow-sm"
1732
- }
1733
- )
1734
- ] }),
1735
- children
1736
- ]
1737
- }
1738
- );
1739
- }
1740
-
1741
- // src/components/brandguide/ColorSwatchSettings.tsx
1742
- import { useState as useState8 } from "react";
1743
-
1744
- // src/components/shared/Input.tsx
1745
- import { forwardRef as forwardRef3, useId } from "react";
1746
-
1747
- // src/components/shared/FormLabel.tsx
1748
- import { jsx as jsx21 } from "react/jsx-runtime";
1749
- function FormLabel({ htmlFor, children, className }) {
1750
- return /* @__PURE__ */ jsx21(
1751
- "label",
1752
- {
1753
- htmlFor,
1754
- className: cn("mb-1.5 block text-sm font-medium text-base-contrast", className),
1755
- children
1756
- }
1757
- );
1758
- }
1759
-
1760
- // src/components/shared/Input.tsx
1761
- import { jsx as jsx22, jsxs as jsxs9 } from "react/jsx-runtime";
1762
- var Input = forwardRef3(function Input2({ label, value, onChange, type = "text", className, disabled, error, ...rest }, ref) {
1763
- const id = useId();
1764
- const errorId = `${id}-error`;
1765
- return /* @__PURE__ */ jsxs9("div", { className, children: [
1766
- /* @__PURE__ */ jsx22(FormLabel, { htmlFor: id, children: label }),
1767
- /* @__PURE__ */ jsx22(
1768
- "input",
1769
- {
1770
- ref,
1771
- id,
1772
- type,
1773
- value,
1774
- onChange: (e) => onChange(e.target.value),
1775
- disabled,
1776
- "aria-invalid": error ? true : void 0,
1777
- "aria-describedby": error ? errorId : void 0,
1778
- className: cn(
1779
- "w-full rounded border bg-base px-3 py-2 text-sm text-base-contrast focus:outline-none focus:ring-1",
1780
- error ? "border-red-600 focus:border-red-600 focus:ring-red-600" : "border-base-200 focus:border-base-contrast focus:ring-base-contrast",
1781
- disabled && "cursor-not-allowed opacity-50"
1782
- ),
1783
- ...rest
1784
- }
1785
- ),
1786
- error && /* @__PURE__ */ jsx22("p", { id: errorId, className: "mt-1 text-xs text-red-600", children: error })
1787
- ] });
1788
- });
1789
-
1790
- // src/components/brandguide/ColorSwatchSettings.tsx
1791
- import { jsx as jsx23, jsxs as jsxs10 } from "react/jsx-runtime";
1792
- function mergeSpaces(spaces) {
1793
- return Object.assign({}, ...spaces);
1794
- }
1795
- function ColorSwatchSettings({ color, onChange }) {
1796
- const [localColor, setLocalColor] = useState8(() => ({
1797
- ...color,
1798
- spaces: [mergeSpaces(color.spaces)]
1799
- }));
1800
- const space = localColor.spaces[0] || {};
1801
- const update = (updated) => {
1802
- setLocalColor(updated);
1803
- onChange(updated);
1804
- };
1805
- const updateSpace = (key, value) => {
1806
- const newSpace = { ...space, [key]: value || void 0 };
1807
- update({ ...localColor, spaces: [newSpace] });
1808
- };
1809
- return /* @__PURE__ */ jsxs10("div", { className: "space-y-3", children: [
1810
- /* @__PURE__ */ jsx23(
1811
- Input,
1812
- {
1813
- label: "Name",
1814
- value: localColor.name || "",
1815
- onChange: (name) => update({ ...localColor, name })
1816
- }
1817
- ),
1818
- ["hex", "rgb", "cmyk", "pantone"].map((key) => /* @__PURE__ */ jsx23(
1819
- Input,
1820
- {
1821
- label: key.toUpperCase(),
1822
- value: space[key] || "",
1823
- onChange: (value) => updateSpace(key, value)
1824
- },
1825
- key
1826
- ))
1827
- ] });
1828
- }
1829
-
1830
- // src/components/brandguide/Colors.tsx
1831
- import { jsx as jsx24, jsxs as jsxs11 } from "react/jsx-runtime";
1832
- function getContrastClass(hex) {
1833
- if (!hex) return "text-white";
1834
- const h = hex.replace("#", "");
1835
- if (h.length < 6) return "text-white";
1836
- const r = parseInt(h.substring(0, 2), 16);
1837
- const g = parseInt(h.substring(2, 4), 16);
1838
- const b = parseInt(h.substring(4, 6), 16);
1839
- const yiq = (r * 299 + g * 587 + b * 114) / 1e3;
1840
- return yiq >= 128 ? "text-black" : "text-white";
1841
- }
1842
- var createColorItem = () => ({
1843
- name: "",
1844
- spaces: [{ hex: "#cccccc" }]
1845
- });
1846
- function buildContent(colors, options) {
1847
- const { label, columns, collapsing, showLabel } = options;
1848
- return {
1849
- type: "colors",
1850
- content: { colors },
1851
- ...label || columns || collapsing !== void 0 || showLabel !== void 0 ? { options: { label, columns, collapsing, showLabel } } : {}
1852
- };
1853
- }
1854
- function Colors({ colors, columns = 3, label, collapsing, showLabel = true, onChange, openModal }) {
1855
- if (onChange && openModal) {
1856
- return /* @__PURE__ */ jsx24(
1857
- ColorsEditable,
1858
- {
1859
- colors,
1860
- columns,
1861
- label,
1862
- collapsing,
1863
- showLabel,
1864
- onChange,
1865
- openModal
1866
- }
1867
- );
1868
- }
1869
- return /* @__PURE__ */ jsx24(ColorsView, { colors, columns, label, collapsing, showLabel });
1870
- }
1871
- function ColorsView({
1872
- colors,
1873
- columns,
1874
- label,
1875
- collapsing,
1876
- showLabel
1877
- }) {
1878
- const [expanded, setExpanded] = useState9(!collapsing);
1879
- const [copiedIndex, setCopiedIndex] = useState9(null);
1880
- const handleCopy = (value, colorIndex) => {
1881
- if (navigator.clipboard?.writeText) {
1882
- navigator.clipboard.writeText(value);
1883
- } else {
1884
- const ta = document.createElement("textarea");
1885
- ta.value = value;
1886
- ta.style.position = "fixed";
1887
- ta.style.opacity = "0";
1888
- document.body.appendChild(ta);
1889
- ta.select();
1890
- document.execCommand("copy");
1891
- document.body.removeChild(ta);
1892
- }
1893
- setCopiedIndex(colorIndex);
1894
- setTimeout(() => setCopiedIndex(null), 1500);
1895
- };
1896
- return /* @__PURE__ */ jsxs11("div", { children: [
1897
- (showLabel && !!label || collapsing) && /* @__PURE__ */ jsxs11("div", { className: "mb-4 flex items-center gap-2", children: [
1898
- showLabel && !!label && /* @__PURE__ */ jsx24("span", { className: "text-lg font-bold", children: label }),
1899
- collapsing && /* @__PURE__ */ jsxs11(
1900
- "button",
1901
- {
1902
- onClick: () => setExpanded(!expanded),
1903
- className: "cursor-pointer ml-auto flex items-center gap-1.5 text-sm text-base-contrast-light hover:text-base-contrast",
1904
- children: [
1905
- /* @__PURE__ */ jsx24("span", { children: expanded ? "Hide Details" : "Show Details" }),
1906
- expanded ? /* @__PURE__ */ jsx24(EyeOff, { size: 16 }) : /* @__PURE__ */ jsx24(Eye, { size: 16 })
1907
- ]
1908
- }
1909
- )
1910
- ] }),
1911
- /* @__PURE__ */ jsx24("div", { className: "@container", children: /* @__PURE__ */ jsx24("div", { className: cn("grid gap-4", containerGridClass[columns] || containerGridClass[3]), children: colors.map((color, i) => {
1912
- const hex = color.spaces[0]?.hex;
1913
- const contrast = getContrastClass(hex);
1914
- return /* @__PURE__ */ jsxs11("div", { className: "overflow-hidden rounded-md border border-base-200", children: [
1915
- /* @__PURE__ */ jsxs11("div", { className: cn("relative flex min-h-[80px] items-end p-3", contrast), style: { backgroundColor: hex || "#ccc" }, children: [
1916
- color.name && /* @__PURE__ */ jsx24("span", { className: "text-sm font-bold", children: color.name }),
1917
- copiedIndex === i && /* @__PURE__ */ jsx24("span", { className: cn(
1918
- "absolute top-2 right-2 rounded-full px-2.5 py-0.5 text-xs font-medium",
1919
- getContrastClass(hex) === "text-black" ? "bg-black/15 text-black" : "bg-white/25 text-white"
1920
- ), children: "Copied!" })
1921
- ] }),
1922
- expanded && /* @__PURE__ */ jsx24("div", { className: "space-y-1 bg-base-accent p-3 text-sm", children: color.spaces.map(
1923
- (space, j) => Object.entries(space).map(
1924
- ([key, value]) => value ? /* @__PURE__ */ jsxs11("button", { onClick: () => handleCopy(value, i), className: "cursor-pointer flex w-full justify-between hover:text-primary", children: [
1925
- /* @__PURE__ */ jsx24("span", { className: "font-medium uppercase", children: key }),
1926
- /* @__PURE__ */ jsx24("span", { children: value })
1927
- ] }, `${j}-${key}`) : null
1928
- )
1929
- ) })
1930
- ] }, i);
1931
- }) }) })
1932
- ] });
1933
- }
1934
- function ColorsEditable({
1935
- colors,
1936
- columns,
1937
- label,
1938
- collapsing,
1939
- showLabel,
1940
- onChange,
1941
- openModal
1942
- }) {
1943
- const [expanded, setExpanded] = useState9(!collapsing);
1944
- const options = { label, columns, collapsing, showLabel };
1945
- useEffect7(() => {
1946
- setExpanded(!collapsing);
1947
- }, [collapsing]);
1948
- const handleItemSettings = (index) => {
1949
- const color = colors[index];
1950
- openModal(
1951
- "Color Settings",
1952
- /* @__PURE__ */ jsx24(ColorSwatchSettings, { color, onChange: (updated) => {
1953
- const newColors = colors.map((c, i) => i === index ? updated : c);
1954
- onChange(buildContent(newColors, options));
1955
- } })
1956
- );
1957
- };
1958
- return /* @__PURE__ */ jsxs11("div", { children: [
1959
- (showLabel && !!label || collapsing) && /* @__PURE__ */ jsxs11("div", { className: "mb-4 flex items-center gap-2", children: [
1960
- showLabel && !!label && /* @__PURE__ */ jsx24(
1961
- EditablePlainText,
1962
- {
1963
- tag: "span",
1964
- value: label,
1965
- onChange: (newLabel) => onChange(buildContent(colors, { ...options, label: newLabel })),
1966
- isEditMode: true,
1967
- className: "text-lg font-bold"
1968
- }
1969
- ),
1970
- collapsing && /* @__PURE__ */ jsxs11(
1971
- "button",
1972
- {
1973
- onClick: () => setExpanded(!expanded),
1974
- className: "cursor-pointer ml-auto flex items-center gap-1.5 text-sm text-base-contrast-light hover:text-base-contrast",
1975
- children: [
1976
- /* @__PURE__ */ jsx24("span", { children: expanded ? "Hide Details" : "Show Details" }),
1977
- expanded ? /* @__PURE__ */ jsx24(EyeOff, { size: 16 }) : /* @__PURE__ */ jsx24(Eye, { size: 16 })
1978
- ]
1979
- }
1980
- )
1981
- ] }),
1982
- /* @__PURE__ */ jsx24(
1983
- EditableGrid,
1984
- {
1985
- items: colors,
1986
- columns,
1987
- onChange: (newColors) => onChange(buildContent(newColors, options)),
1988
- createItem: createColorItem,
1989
- isEditMode: true,
1990
- onItemSettings: handleItemSettings,
1991
- renderItem: (color, { index }) => /* @__PURE__ */ jsx24(
1992
- ColorSwatchEditable,
1993
- {
1994
- color,
1995
- index,
1996
- colors,
1997
- options,
1998
- onChange,
1999
- expanded
2000
- }
2001
- )
2002
- }
2003
- )
2004
- ] });
2005
- }
2006
- function ColorSwatchEditable({
2007
- color,
2008
- index,
2009
- colors,
2010
- options,
2011
- onChange,
2012
- expanded
2013
- }) {
2014
- const hex = color.spaces[0]?.hex;
2015
- const contrast = getContrastClass(hex);
2016
- const updateColor = (updated) => {
2017
- const newColors = colors.map((c, i) => i === index ? updated : c);
2018
- onChange(buildContent(newColors, options));
2019
- };
2020
- return /* @__PURE__ */ jsxs11("div", { className: "overflow-hidden rounded-md border border-base-200", children: [
2021
- /* @__PURE__ */ jsx24("div", { className: cn("flex min-h-[80px] items-end p-3", contrast), style: { backgroundColor: hex || "#ccc" }, children: /* @__PURE__ */ jsx24(
2022
- EditablePlainText,
2023
- {
2024
- tag: "span",
2025
- value: color.name || "",
2026
- onChange: (name) => updateColor({ ...color, name }),
2027
- isEditMode: true,
2028
- placeholder: "Color name",
2029
- className: "text-sm font-bold"
2030
- }
2031
- ) }),
2032
- expanded && /* @__PURE__ */ jsx24("div", { className: "space-y-1 bg-base-accent p-3 text-sm", children: color.spaces.map(
2033
- (space, j) => Object.entries(space).map(
2034
- ([key, value]) => value ? /* @__PURE__ */ jsxs11("div", { className: "flex w-full justify-between", children: [
2035
- /* @__PURE__ */ jsx24("span", { className: "font-medium uppercase", children: key }),
2036
- /* @__PURE__ */ jsx24("span", { children: value })
2037
- ] }, `${j}-${key}`) : null
2038
- )
2039
- ) })
2040
- ] });
2041
- }
2042
-
2043
- // src/components/sections/Colors/index.tsx
2044
- import { jsx as jsx25 } from "react/jsx-runtime";
2045
- var schema4 = z5.object({
2046
- type: z5.literal("colors"),
2047
- content: z5.object({ colors: z5.array(ColorItemSchema) }),
2048
- options: z5.object({
2049
- label: z5.string().optional(),
2050
- columns: z5.number().int().min(2).max(4).optional(),
2051
- collapsing: z5.boolean().optional(),
2052
- showLabel: z5.boolean().optional()
2053
- }).optional()
2054
- });
2055
- var Colors_default = defineSection({
2056
- type: "colors",
2057
- label: "Colors",
2058
- icon: /* @__PURE__ */ jsx25(Palette, { size: 18 }),
2059
- schema: schema4,
2060
- component: ({ content, options, onChange, openModal }) => /* @__PURE__ */ jsx25(
2061
- Colors,
2062
- {
2063
- colors: content.content.colors,
2064
- columns: options?.columns,
2065
- label: options?.label,
2066
- collapsing: options?.collapsing,
2067
- showLabel: options?.showLabel,
2068
- onChange: onChange ? (c) => onChange(c) : void 0,
2069
- openModal
2070
- }
2071
- ),
2072
- defaults: () => ({
2073
- type: "colors",
2074
- content: { colors: [{ spaces: [{ hex: "#000000" }] }] }
2075
- }),
2076
- getLabel: (content) => pluralize(content.content.colors.length, "color"),
2077
- getThumbnails: (content) => content.content.colors.filter((c) => c.spaces[0]?.hex).map((c) => ({ type: "color", value: c.spaces[0].hex })),
2078
- settings: {
2079
- columns: {
2080
- type: "select",
2081
- label: "Columns",
2082
- default: "3",
2083
- coerce: "number",
2084
- options: [{ label: "2", value: "2" }, { label: "3", value: "3" }, { label: "4", value: "4" }]
2085
- },
2086
- label: { type: "text", label: "Label", default: "", placeholder: "Color Section Label" },
2087
- showLabel: { type: "checkbox", label: "Show label", default: true },
2088
- collapsing: { type: "checkbox", label: "Collapsing layout", default: false }
2089
- }
2090
- });
2091
-
2092
- // src/components/sections/IconList/index.tsx
2093
- import { z as z6 } from "zod";
2094
- import { List } from "lucide-react";
2095
-
2096
- // src/components/sections/IconList/IconList.tsx
2097
- import { useRef as useRef8, useEffect as useEffect9, useState as useState11, useCallback as useCallback6 } from "react";
2098
- import { CopyPlus, Pencil } from "lucide-react";
2099
-
2100
- // src/components/primitives/IconPicker.tsx
2101
- import { useEffect as useEffect8, useRef as useRef7, useState as useState10 } from "react";
2102
- import { Check as Check2, X as X4 } from "lucide-react";
2103
- import { Fragment as Fragment4, jsx as jsx26, jsxs as jsxs12 } from "react/jsx-runtime";
2104
- function IconPicker({
2105
- selected,
2106
- onSelect,
2107
- onClose,
2108
- showRemove = true,
2109
- allowDoDont = false,
2110
- dodont,
2111
- onSelectDoDont
2112
- }) {
2113
- const panelRef = useRef7(null);
2114
- const [mode, setMode] = useState10(dodont ? "dodont" : "none");
2115
- useEffect8(() => {
2116
- function handleMouseDown(e) {
2117
- if (panelRef.current && !panelRef.current.contains(e.target)) {
2118
- onClose();
2119
- }
2120
- }
2121
- document.addEventListener("mousedown", handleMouseDown);
2122
- return () => document.removeEventListener("mousedown", handleMouseDown);
2123
- }, [onClose]);
2124
- const showDoDont = allowDoDont && mode === "dodont";
2125
- return /* @__PURE__ */ jsxs12(
2126
- "div",
2127
- {
2128
- ref: panelRef,
2129
- className: "absolute z-50 w-56 rounded-lg border border-base-200 bg-base p-2 shadow-lg",
2130
- children: [
2131
- allowDoDont && /* @__PURE__ */ jsxs12("div", { role: "tablist", "aria-label": "Icon mode", className: "mb-2 flex gap-1 rounded-md bg-base-200 p-0.5", children: [
2132
- /* @__PURE__ */ jsx26(
2133
- "button",
2134
- {
2135
- type: "button",
2136
- role: "tab",
2137
- "aria-label": "None",
2138
- "aria-selected": mode === "none",
2139
- className: cn(
2140
- "cursor-pointer flex-1 rounded px-2 py-1 text-xs font-medium transition-colors",
2141
- mode === "none" ? "bg-base text-base-contrast shadow-sm" : "text-base-contrast-light hover:text-base-contrast"
2142
- ),
2143
- onClick: () => {
2144
- setMode("none");
2145
- onSelectDoDont?.(void 0);
2146
- },
2147
- children: "None"
2148
- }
2149
- ),
2150
- /* @__PURE__ */ jsx26(
2151
- "button",
2152
- {
2153
- type: "button",
2154
- role: "tab",
2155
- "aria-label": "Do / Don't",
2156
- "aria-selected": mode === "dodont",
2157
- className: cn(
2158
- "cursor-pointer flex-1 rounded px-2 py-1 text-xs font-medium transition-colors",
2159
- mode === "dodont" ? "bg-base text-base-contrast shadow-sm" : "text-base-contrast-light hover:text-base-contrast"
2160
- ),
2161
- onClick: () => setMode("dodont"),
2162
- children: "Do / Don't"
2163
- }
2164
- )
2165
- ] }),
2166
- showDoDont ? /* @__PURE__ */ jsxs12("div", { className: "grid grid-cols-2 gap-2", children: [
2167
- /* @__PURE__ */ jsxs12(
2168
- "button",
2169
- {
2170
- type: "button",
2171
- "aria-label": "Do",
2172
- className: cn(
2173
- "cursor-pointer flex flex-col items-center justify-center gap-1 rounded py-2 transition-colors",
2174
- "text-green-600 hover:bg-base-accent",
2175
- dodont === "do" && "ring-2 ring-primary bg-base-accent"
2176
- ),
2177
- onClick: () => onSelectDoDont?.("do"),
2178
- children: [
2179
- /* @__PURE__ */ jsx26(Check2, { size: 20 }),
2180
- /* @__PURE__ */ jsx26("span", { className: "text-xs font-medium", children: "Do" })
2181
- ]
2182
- }
2183
- ),
2184
- /* @__PURE__ */ jsxs12(
2185
- "button",
2186
- {
2187
- type: "button",
2188
- "aria-label": "Don't",
2189
- className: cn(
2190
- "cursor-pointer flex flex-col items-center justify-center gap-1 rounded py-2 transition-colors",
2191
- "text-red-600 hover:bg-base-accent",
2192
- dodont === "dont" && "ring-2 ring-primary bg-base-accent"
2193
- ),
2194
- onClick: () => onSelectDoDont?.("dont"),
2195
- children: [
2196
- /* @__PURE__ */ jsx26(X4, { size: 20 }),
2197
- /* @__PURE__ */ jsx26("span", { className: "text-xs font-medium", children: "Don't" })
2198
- ]
2199
- }
2200
- )
2201
- ] }) : /* @__PURE__ */ jsxs12(Fragment4, { children: [
2202
- /* @__PURE__ */ jsx26("div", { className: "grid grid-cols-5 gap-1", children: curatedIcons.map((entry) => {
2203
- const Icon = entry.icon;
2204
- const isSelected = selected === entry.id;
2205
- return /* @__PURE__ */ jsx26(
2206
- "button",
2207
- {
2208
- "aria-label": entry.label,
2209
- className: cn(
2210
- "cursor-pointer flex h-9 w-9 items-center justify-center rounded transition-colors",
2211
- "text-base-contrast-light hover:bg-base-accent hover:text-base-contrast",
2212
- isSelected && "ring-2 ring-primary bg-base-accent text-primary"
2213
- ),
2214
- onClick: () => onSelect(entry.id),
2215
- children: /* @__PURE__ */ jsx26(Icon, { size: 18 })
2216
- },
2217
- entry.id
2218
- );
2219
- }) }),
2220
- showRemove && /* @__PURE__ */ jsx26(
2221
- "button",
2222
- {
2223
- className: "cursor-pointer mt-2 w-full rounded px-2 py-1.5 text-center text-xs text-base-contrast-light hover:bg-base-accent hover:text-base-contrast",
2224
- onClick: () => onSelect(null),
2225
- children: "Remove icon"
2226
- }
2227
- )
2228
- ] })
2229
- ]
2230
- }
2231
- );
2232
- }
2233
-
2234
- // src/components/sections/IconList/IconList.tsx
2235
- import { jsx as jsx27, jsxs as jsxs13 } from "react/jsx-runtime";
2236
- function IconList({
2237
- items,
2238
- icon = null,
2239
- showLabel = true,
2240
- stackText = false,
2241
- labelClassName = "font-bold text-base-contrast",
2242
- textClassName = "text-base-contrast-light",
2243
- iconClassName = "text-primary",
2244
- onChange,
2245
- onItemsChange
2246
- }) {
2247
- const isEditMode = !!onChange || !!onItemsChange;
2248
- const emitItems = useCallback6(
2249
- (newItems) => {
2250
- onChange?.({
2251
- type: "icon_list",
2252
- content: { items: newItems },
2253
- options: { icon, showLabel, stackText }
2254
- });
2255
- onItemsChange?.(newItems);
2256
- },
2257
- [onChange, onItemsChange, icon, showLabel, stackText]
2258
- );
2259
- if (!isEditMode) {
2260
- return /* @__PURE__ */ jsx27(
2261
- ViewIconList,
2262
- {
2263
- items,
2264
- defaultIcon: icon,
2265
- showLabel,
2266
- stackText,
2267
- labelClassName,
2268
- textClassName,
2269
- iconClassName
2270
- }
2271
- );
2272
- }
2273
- return /* @__PURE__ */ jsx27(
2274
- EditIconList,
2275
- {
2276
- items,
2277
- defaultIcon: icon,
2278
- showLabel,
2279
- stackText,
2280
- labelClassName,
2281
- textClassName,
2282
- iconClassName,
2283
- onItemsChange: emitItems
2284
- }
2285
- );
2286
- }
2287
- function resolveIcon(item, defaultIcon) {
2288
- if (item.dodont === "do") return getIcon("check") ?? null;
2289
- if (item.dodont === "dont") return getIcon("x") ?? null;
2290
- const id = item.icon ?? defaultIcon;
2291
- return id ? getIcon(id) : null;
2292
- }
2293
- function resolveIconColor(item, iconClassName) {
2294
- if (item.dodont === "do") return "text-green-600";
2295
- if (item.dodont === "dont") return "text-red-600";
2296
- return iconClassName;
2297
- }
2298
- function ViewIconList({
2299
- items,
2300
- defaultIcon,
2301
- showLabel,
2302
- stackText,
2303
- labelClassName,
2304
- textClassName,
2305
- iconClassName
2306
- }) {
2307
- const hasAnyIcon = items.some((item) => resolveIcon(item, defaultIcon));
2308
- return /* @__PURE__ */ jsx27("div", { className: "grid gap-4 pb-4", children: items.map((item, i) => {
2309
- const iconEntry = resolveIcon(item, defaultIcon);
2310
- const iconColor = resolveIconColor(item, iconClassName);
2311
- return /* @__PURE__ */ jsxs13(
2312
- "div",
2313
- {
2314
- className: cn(
2315
- "grid gap-x-3",
2316
- hasAnyIcon ? "grid-cols-[24px_1fr]" : "grid-cols-1"
2317
- ),
2318
- children: [
2319
- hasAnyIcon && /* @__PURE__ */ jsx27(
2320
- "div",
2321
- {
2322
- "data-testid": "icon-list-icon",
2323
- className: cn("flex items-start pt-0.5", iconColor),
2324
- children: iconEntry && /* @__PURE__ */ jsx27(iconEntry.icon, { size: 18 })
2325
- }
2326
- ),
2327
- /* @__PURE__ */ jsxs13(
2328
- "div",
2329
- {
2330
- "data-testid": "icon-list-content",
2331
- className: stackText ? "flex flex-col" : void 0,
2332
- children: [
2333
- showLabel && /* @__PURE__ */ jsxs13("span", { className: labelClassName, children: [
2334
- item.label,
2335
- !stackText && " "
2336
- ] }),
2337
- /* @__PURE__ */ jsx27("span", { className: textClassName, children: item.text })
2338
- ]
2339
- }
2340
- )
2341
- ]
2342
- },
2343
- i
2344
- );
2345
- }) });
2346
- }
2347
- function EditIconList({
2348
- items,
2349
- defaultIcon,
2350
- showLabel,
2351
- stackText,
2352
- labelClassName,
2353
- textClassName,
2354
- iconClassName,
2355
- onItemsChange
2356
- }) {
2357
- const { wrappedItems, onReorder, onAdd, onRemove } = useEditableCollection({
2358
- items,
2359
- onChange: onItemsChange,
2360
- createItem: () => ({ label: "", text: "" })
2361
- });
2362
- const [dragState, setDragState] = useState11({ sourceId: null, targetId: null });
2363
- const updateItem = useCallback6(
2364
- (index, patch) => {
2365
- const updated = wrappedItems.map(
2366
- (w, i) => i === index ? { ...w.data, ...patch } : w.data
2367
- );
2368
- onItemsChange(updated);
2369
- },
2370
- [wrappedItems, onItemsChange]
2371
- );
2372
- const duplicateItem = useCallback6(
2373
- (index) => {
2374
- const copy = { ...wrappedItems[index].data };
2375
- const next = [
2376
- ...wrappedItems.slice(0, index + 1).map((w) => w.data),
2377
- copy,
2378
- ...wrappedItems.slice(index + 1).map((w) => w.data)
2379
- ];
2380
- onItemsChange(next);
2381
- },
2382
- [wrappedItems, onItemsChange]
2383
- );
2384
- const hasAnyIcon = true;
2385
- return /* @__PURE__ */ jsxs13("div", { className: "group/list relative grid gap-4 pb-4", children: [
2386
- wrappedItems.map((wrapped, index) => /* @__PURE__ */ jsx27(
2387
- EditableRow,
2388
- {
2389
- id: wrapped.id,
2390
- index,
2391
- item: wrapped.data,
2392
- defaultIcon,
2393
- hasAnyIcon,
2394
- showLabel,
2395
- stackText,
2396
- labelClassName,
2397
- textClassName,
2398
- iconClassName,
2399
- dragState,
2400
- setDragState,
2401
- onReorder,
2402
- onRemove,
2403
- onUpdateItem: (patch) => updateItem(index, patch),
2404
- onDuplicate: () => duplicateItem(index)
2405
- },
2406
- wrapped.id
2407
- )),
2408
- /* @__PURE__ */ jsx27(
2409
- TrailingDropZone,
2410
- {
2411
- index: wrappedItems.length,
2412
- dragState,
2413
- setDragState,
2414
- onReorder
2415
- }
2416
- ),
2417
- /* @__PURE__ */ jsx27(
2418
- IconButton,
2419
- {
2420
- icon: /* @__PURE__ */ jsx27(AddIcon, { size: 16 }),
2421
- label: "Add item",
2422
- size: "lg",
2423
- intent: "primary",
2424
- onClick: onAdd,
2425
- className: "absolute -bottom-6 left-1/2 z-20 -translate-x-1/2 rounded-full border border-base-200 bg-base opacity-0 transition-opacity group-hover/list:opacity-100"
2426
- }
2427
- )
2428
- ] });
2429
- }
2430
- function EditableRow({
2431
- id,
2432
- index,
2433
- item,
2434
- defaultIcon,
2435
- hasAnyIcon,
2436
- showLabel,
2437
- stackText,
2438
- labelClassName,
2439
- textClassName,
2440
- iconClassName,
2441
- dragState,
2442
- setDragState,
2443
- onReorder,
2444
- onRemove,
2445
- onUpdateItem,
2446
- onDuplicate
2447
- }) {
2448
- const rowRef = useRef8(null);
2449
- const handleRef = useRef8(null);
2450
- const [iconHover, setIconHover] = useState11(false);
2451
- const [pickerOpen, setPickerOpen] = useState11(false);
2452
- const [editingSuppressed, setEditingSuppressed] = useState11(false);
2453
- const iconEntry = resolveIcon(item, defaultIcon);
2454
- const effectiveIconId = item.icon ?? defaultIcon ?? null;
2455
- const iconColor = resolveIconColor(item, iconClassName);
2456
- useEffect9(() => {
2457
- const row = rowRef.current;
2458
- const handle = handleRef.current;
2459
- if (!row || !handle) return;
2460
- let cleanup;
2461
- let cancelled = false;
2462
- import("../../adapter-HH47ZPGM.js").then(({ draggable, dropTargetForElements }) => {
2463
- if (cancelled) return;
2464
- const cleanupDraggable = draggable({
2465
- element: handle,
2466
- getInitialData: () => ({ dragType: "icon-list-row", id, index }),
2467
- onDragStart: () => setDragState({ sourceId: id, targetId: null }),
2468
- onDrop: () => setDragState({ sourceId: null, targetId: null })
2469
- });
2470
- const cleanupDropTarget = dropTargetForElements({
2471
- element: row,
2472
- canDrop: ({ source }) => source.data.dragType === "icon-list-row",
2473
- getData: () => ({ id, index }),
2474
- onDragEnter: () => setDragState((prev) => ({ ...prev, targetId: id })),
2475
- onDragLeave: () => setDragState(
2476
- (prev) => prev.targetId === id ? { ...prev, targetId: null } : prev
2477
- ),
2478
- onDrop: ({ source }) => {
2479
- const fromIndex = source.data.index;
2480
- if (fromIndex !== index) onReorder(fromIndex, index);
2481
- }
2482
- });
2483
- cleanup = () => {
2484
- cleanupDraggable();
2485
- cleanupDropTarget();
2486
- };
2487
- });
2488
- return () => {
2489
- cancelled = true;
2490
- cleanup?.();
2491
- };
2492
- }, [id, index, onReorder, setDragState]);
2493
- const isDropTarget = dragState.targetId === id && dragState.sourceId !== id;
2494
- const onEditableActivity = (e) => {
2495
- const t = e.target;
2496
- if (t.matches?.("input, textarea, [contenteditable], [contenteditable='true']")) {
2497
- setEditingSuppressed(true);
2498
- }
2499
- };
2500
- return /* @__PURE__ */ jsxs13(
2501
- "div",
2502
- {
2503
- ref: rowRef,
2504
- className: cn(
2505
- "group/row relative grid gap-x-3",
2506
- hasAnyIcon ? "grid-cols-[24px_1fr]" : "grid-cols-1",
2507
- isDropTarget && "border-t-2 border-primary"
2508
- ),
2509
- onFocusCapture: onEditableActivity,
2510
- onInput: onEditableActivity,
2511
- onMouseMove: () => setEditingSuppressed(false),
2512
- children: [
2513
- hasAnyIcon && /* @__PURE__ */ jsxs13(
2514
- "div",
2515
- {
2516
- "data-testid": "icon-list-icon",
2517
- className: cn("relative flex items-start pt-0.5", iconColor),
2518
- onMouseEnter: () => setIconHover(true),
2519
- onMouseLeave: () => setIconHover(false),
2520
- children: [
2521
- /* @__PURE__ */ jsx27(
2522
- "button",
2523
- {
2524
- className: "cursor-pointer transition-opacity hover:opacity-70",
2525
- "aria-label": "Change icon",
2526
- onClick: () => setPickerOpen((prev) => !prev),
2527
- children: iconHover && !pickerOpen ? /* @__PURE__ */ jsx27(Pencil, { size: 18, className: "text-base-contrast-light" }) : iconEntry ? /* @__PURE__ */ jsx27(iconEntry.icon, { size: 18 }) : /* @__PURE__ */ jsx27("span", { className: "flex h-[18px] w-[18px] items-center justify-center rounded-sm border border-dashed border-base-contrast-light/40" })
2528
- }
2529
- ),
2530
- pickerOpen && /* @__PURE__ */ jsx27("div", { className: "absolute top-full left-0 z-50 mt-1", children: /* @__PURE__ */ jsx27(
2531
- IconPicker,
2532
- {
2533
- selected: effectiveIconId,
2534
- showRemove: true,
2535
- allowDoDont: true,
2536
- dodont: item.dodont,
2537
- onSelect: (newIcon) => {
2538
- onUpdateItem({ icon: newIcon ?? void 0, dodont: void 0 });
2539
- setPickerOpen(false);
2540
- setIconHover(false);
2541
- },
2542
- onSelectDoDont: (v) => {
2543
- onUpdateItem({ dodont: v });
2544
- if (v !== void 0) {
2545
- setPickerOpen(false);
2546
- setIconHover(false);
2547
- }
2548
- },
2549
- onClose: () => {
2550
- setPickerOpen(false);
2551
- setIconHover(false);
2552
- }
2553
- }
2554
- ) })
2555
- ]
2556
- }
2557
- ),
2558
- /* @__PURE__ */ jsxs13("div", { className: stackText ? "flex flex-col" : void 0, children: [
2559
- showLabel && /* @__PURE__ */ jsx27(
2560
- EditablePlainText,
2561
- {
2562
- tag: "span",
2563
- value: item.label,
2564
- onChange: (label) => onUpdateItem({ label }),
2565
- isEditMode: true,
2566
- placeholder: "Label",
2567
- className: cn(labelClassName, !stackText && "mr-1")
2568
- }
2569
- ),
2570
- /* @__PURE__ */ jsx27(
2571
- EditablePlainText,
2572
- {
2573
- tag: "span",
2574
- value: item.text,
2575
- onChange: (text) => onUpdateItem({ text }),
2576
- isEditMode: true,
2577
- placeholder: "Text",
2578
- className: textClassName
2579
- }
2580
- )
2581
- ] }),
2582
- /* @__PURE__ */ jsxs13(
2583
- "div",
2584
- {
2585
- className: cn(
2586
- "absolute right-1 top-1 z-10 flex items-center gap-1",
2587
- editingSuppressed ? "opacity-0 pointer-events-none" : "opacity-0 group-hover/row:opacity-100 no-hover:opacity-100"
2588
- ),
2589
- children: [
2590
- /* @__PURE__ */ jsx27(
2591
- IconButton,
2592
- {
2593
- ref: handleRef,
2594
- icon: /* @__PURE__ */ jsx27(DragHandle, { size: 14 }),
2595
- label: "Drag to reorder",
2596
- size: "sm",
2597
- className: "cursor-grab rounded bg-base/80 p-1 shadow-sm text-base-contrast-light/80 hover:text-base-contrast active:cursor-grabbing",
2598
- tabIndex: -1
2599
- }
2600
- ),
2601
- /* @__PURE__ */ jsx27(
2602
- "button",
2603
- {
2604
- "aria-label": "Duplicate item",
2605
- onClick: onDuplicate,
2606
- className: "cursor-pointer rounded bg-base/80 p-1 shadow-sm text-base-contrast-light hover:text-primary",
2607
- children: /* @__PURE__ */ jsx27(CopyPlus, { size: 14 })
2608
- }
2609
- ),
2610
- /* @__PURE__ */ jsx27(
2611
- IconButton,
2612
- {
2613
- icon: /* @__PURE__ */ jsx27(DeleteIcon, { size: 14 }),
2614
- label: "Delete item",
2615
- size: "sm",
2616
- intent: "destructive",
2617
- onClick: () => onRemove(id),
2618
- className: "bg-base/80 shadow-sm"
2619
- }
2620
- )
2621
- ]
2622
- }
2623
- )
2624
- ]
2625
- }
2626
- );
2627
- }
2628
- function TrailingDropZone({
2629
- index,
2630
- dragState,
2631
- setDragState,
2632
- onReorder
2633
- }) {
2634
- const ref = useRef8(null);
2635
- const trailingId = "__trailing__";
2636
- useEffect9(() => {
2637
- const el = ref.current;
2638
- if (!el) return;
2639
- let cleanup;
2640
- let cancelled = false;
2641
- import("../../adapter-HH47ZPGM.js").then(({ dropTargetForElements }) => {
2642
- if (cancelled) return;
2643
- cleanup = dropTargetForElements({
2644
- element: el,
2645
- canDrop: ({ source }) => source.data.dragType === "icon-list-row",
2646
- getData: () => ({ id: trailingId, index }),
2647
- onDragEnter: () => setDragState((prev) => ({ ...prev, targetId: trailingId })),
2648
- onDragLeave: () => setDragState(
2649
- (prev) => prev.targetId === trailingId ? { ...prev, targetId: null } : prev
2650
- ),
2651
- onDrop: ({ source }) => {
2652
- const fromIndex = source.data.index;
2653
- if (fromIndex !== index - 1) onReorder(fromIndex, index - 1);
2654
- }
2655
- });
2656
- });
2657
- return () => {
2658
- cancelled = true;
2659
- cleanup?.();
2660
- };
2661
- }, [index, onReorder, setDragState]);
2662
- const isDropTarget = dragState.targetId === trailingId && dragState.sourceId !== null;
2663
- return /* @__PURE__ */ jsx27(
2664
- "div",
2665
- {
2666
- ref,
2667
- className: cn("absolute bottom-0 left-0 right-0 h-4", isDropTarget && "border-t-2 border-primary")
2668
- }
2669
- );
2670
- }
2671
-
2672
- // src/components/sections/IconList/IconListSettings.tsx
2673
- import { useState as useState12 } from "react";
2674
-
2675
- // src/components/shared/Checkbox.tsx
2676
- import { useId as useId2 } from "react";
2677
- import { Check as Check3 } from "lucide-react";
2678
- import { jsx as jsx28, jsxs as jsxs14 } from "react/jsx-runtime";
2679
- function Checkbox({
2680
- checked,
2681
- onChange,
2682
- label,
2683
- description,
2684
- disabled,
2685
- className,
2686
- startAdornment,
2687
- align = "start"
2688
- }) {
2689
- const id = useId2();
2690
- return /* @__PURE__ */ jsxs14(
2691
- "label",
2692
- {
2693
- htmlFor: id,
2694
- className: cn(
2695
- "flex cursor-pointer gap-3 select-none hover:opacity-80",
2696
- align === "start" ? "items-start" : "items-center",
2697
- disabled && "cursor-not-allowed opacity-50",
2698
- className
2699
- ),
2700
- children: [
2701
- /* @__PURE__ */ jsxs14(
2702
- "span",
2703
- {
2704
- className: cn(
2705
- "relative flex h-5 w-5 shrink-0 items-center justify-center",
2706
- align === "start" && "mt-0.5"
2707
- ),
2708
- children: [
2709
- /* @__PURE__ */ jsx28(
2710
- "input",
2711
- {
2712
- id,
2713
- type: "checkbox",
2714
- checked,
2715
- disabled,
2716
- onChange: (e) => onChange(e.target.checked),
2717
- className: "sr-only"
2718
- }
2719
- ),
2720
- /* @__PURE__ */ jsx28(
2721
- "span",
2722
- {
2723
- "aria-hidden": "true",
2724
- className: cn(
2725
- "flex h-5 w-5 items-center justify-center rounded border transition-colors",
2726
- checked ? "border-primary bg-primary text-primary-contrast" : "border-base-300 bg-base hover:border-primary",
2727
- disabled && "pointer-events-none"
2728
- ),
2729
- children: checked && /* @__PURE__ */ jsx28(Check3, { size: 14, strokeWidth: 3 })
2730
- }
2731
- )
2732
- ]
2733
- }
2734
- ),
2735
- startAdornment,
2736
- /* @__PURE__ */ jsxs14("div", { className: "text-sm", children: [
2737
- /* @__PURE__ */ jsx28("span", { className: "font-medium text-base-contrast", children: label }),
2738
- description && /* @__PURE__ */ jsx28("span", { className: "block text-base-contrast-light", children: description })
2739
- ] })
188
+ square: { type: "checkbox", label: "Square aspect ratio", default: false },
189
+ showCaption: { type: "checkbox", label: "Show caption", default: false },
190
+ border: { type: "checkbox", label: "Border", default: false },
191
+ objectFit: {
192
+ type: "select",
193
+ label: "Image fit",
194
+ default: "contain",
195
+ options: [
196
+ { label: "Contain", value: "contain" },
197
+ { label: "Cover", value: "cover" }
2740
198
  ]
2741
- }
2742
- );
2743
- }
199
+ },
200
+ link: { type: "link", label: "Link", default: DEFAULT_LINK, target: "content" }
201
+ },
202
+ inheritableSettings: ["square", "showCaption", "border", "objectFit"],
203
+ settingsTabs: [
204
+ { label: "Display", fields: ["square", "showCaption", "border", "objectFit"] },
205
+ { label: "Link", fields: ["link"] }
206
+ ]
207
+ });
2744
208
 
2745
- // src/components/sections/IconList/IconListSettings.tsx
2746
- import { jsx as jsx29, jsxs as jsxs15 } from "react/jsx-runtime";
2747
- function IconListSettings({
2748
- icon: initialIcon,
2749
- showLabel: initialShowLabel,
2750
- stackText: initialStackText,
2751
- onChange
2752
- }) {
2753
- const [icon, setIcon] = useState12(initialIcon);
2754
- const [showLabel, setShowLabel] = useState12(initialShowLabel);
2755
- const [stackText, setStackText] = useState12(initialStackText);
2756
- const [pickerOpen, setPickerOpen] = useState12(false);
2757
- const emit = (overrides) => onChange({ content: {}, options: { icon, showLabel, stackText, ...overrides } });
2758
- const showIcons = icon !== null;
2759
- const iconEntry = icon ? getIcon(icon) : null;
2760
- return /* @__PURE__ */ jsxs15("div", { className: "space-y-4", children: [
2761
- /* @__PURE__ */ jsxs15("div", { className: "relative", children: [
2762
- /* @__PURE__ */ jsx29(FormLabel, { htmlFor: "icon-picker-btn", children: "Default Icon" }),
2763
- /* @__PURE__ */ jsxs15("div", { className: "flex items-center gap-2", children: [
2764
- /* @__PURE__ */ jsxs15(
2765
- "button",
2766
- {
2767
- id: "icon-picker-btn",
2768
- className: "cursor-pointer flex items-center gap-2 rounded border border-base-200 px-3 py-1.5 text-sm text-base-contrast-light hover:border-primary hover:text-primary",
2769
- onClick: () => setPickerOpen((prev) => !prev),
2770
- children: [
2771
- iconEntry && /* @__PURE__ */ jsx29(iconEntry.icon, { size: 16 }),
2772
- /* @__PURE__ */ jsx29("span", { children: iconEntry?.label ?? "None" })
2773
- ]
2774
- }
2775
- ),
2776
- icon && /* @__PURE__ */ jsx29(
2777
- "button",
2778
- {
2779
- className: "cursor-pointer text-xs text-base-contrast-light hover:text-base-contrast",
2780
- onClick: () => {
2781
- setIcon(null);
2782
- emit({ icon: null });
2783
- },
2784
- children: "Clear"
2785
- }
2786
- )
2787
- ] }),
2788
- pickerOpen && /* @__PURE__ */ jsx29("div", { className: "absolute top-full left-0 z-50 mt-1", children: /* @__PURE__ */ jsx29(
2789
- IconPicker,
209
+ // src/components/sections/Button/index.tsx
210
+ import { z as z3 } from "zod";
211
+ import { RectangleHorizontal } from "lucide-react";
212
+
213
+ // src/components/sections/Button/CTAButton.tsx
214
+ import { Download } from "lucide-react";
215
+ import { jsx as jsx7, jsxs } from "react/jsx-runtime";
216
+ function Button({ text, link, download, onChange }) {
217
+ const base = "inline-block rounded-md px-6 py-3 font-bold transition-colors";
218
+ const variant = "border-2 border-primary text-primary hover:bg-primary hover:text-primary-contrast";
219
+ if (onChange) {
220
+ return /* @__PURE__ */ jsxs("span", { className: cn(base, variant, "inline-flex items-center gap-2"), children: [
221
+ download && /* @__PURE__ */ jsx7(Download, { size: 16, className: "shrink-0" }),
222
+ /* @__PURE__ */ jsx7(
223
+ EditablePlainText,
2790
224
  {
2791
- selected: icon,
2792
- onSelect: (newIcon) => {
2793
- if (newIcon !== null) {
2794
- setIcon(newIcon);
2795
- emit({ icon: newIcon });
2796
- }
2797
- setPickerOpen(false);
2798
- },
2799
- onClose: () => setPickerOpen(false)
225
+ tag: "span",
226
+ value: text,
227
+ onChange: (newText) => onChange({ type: "button", content: { text: newText, link, download } }),
228
+ isEditMode: true,
229
+ placeholder: "Button text"
2800
230
  }
2801
- ) })
2802
- ] }),
2803
- /* @__PURE__ */ jsx29(
2804
- Checkbox,
2805
- {
2806
- checked: showLabel,
2807
- onChange: (v) => {
2808
- setShowLabel(v);
2809
- emit({ showLabel: v });
2810
- },
2811
- label: "Show labels"
2812
- }
2813
- ),
2814
- showLabel && /* @__PURE__ */ jsx29(
2815
- Checkbox,
231
+ )
232
+ ] });
233
+ }
234
+ const href = link && link.kind === "external" ? link.href : "";
235
+ const target = link?.target;
236
+ if (href) {
237
+ return /* @__PURE__ */ jsxs(
238
+ "a",
2816
239
  {
2817
- checked: stackText,
2818
- onChange: (v) => {
2819
- setStackText(v);
2820
- emit({ stackText: v });
2821
- },
2822
- label: "Stack label above text"
240
+ href,
241
+ target,
242
+ download: download ? "" : void 0,
243
+ className: cn(base, variant, download && "inline-flex items-center gap-2"),
244
+ children: [
245
+ download && /* @__PURE__ */ jsx7(Download, { size: 16, className: "shrink-0" }),
246
+ text
247
+ ]
2823
248
  }
2824
- )
2825
- ] });
249
+ );
250
+ }
251
+ return /* @__PURE__ */ jsx7("button", { className: cn("cursor-pointer", base, variant), children: text });
2826
252
  }
2827
253
 
2828
- // src/components/sections/IconList/index.tsx
2829
- import { jsx as jsx30 } from "react/jsx-runtime";
2830
- var IconListItemSchema = z6.object({
2831
- label: z6.string(),
2832
- text: z6.string(),
2833
- icon: z6.string().optional(),
2834
- dodont: z6.enum(["do", "dont"]).optional()
2835
- });
2836
- var schema5 = z6.object({
2837
- type: z6.literal("icon_list"),
2838
- content: z6.object({
2839
- items: z6.array(IconListItemSchema)
2840
- }),
2841
- options: z6.object({
2842
- icon: z6.string().nullable().optional(),
2843
- showLabel: z6.boolean().optional(),
2844
- stackText: z6.boolean().optional()
2845
- }).optional()
254
+ // src/components/sections/Button/index.tsx
255
+ import { jsx as jsx8 } from "react/jsx-runtime";
256
+ var schema3 = z3.object({
257
+ type: z3.literal("button"),
258
+ content: z3.object({
259
+ text: z3.string(),
260
+ link: LinkValueSchema.optional(),
261
+ download: z3.boolean().optional()
262
+ })
2846
263
  });
2847
- var IconList_default = defineSection({
2848
- type: "icon_list",
2849
- label: "Icon List",
2850
- icon: /* @__PURE__ */ jsx30(List, { size: 18 }),
2851
- schema: schema5,
2852
- component: ({ content, options, onChange }) => /* @__PURE__ */ jsx30(
2853
- IconList,
264
+ var Button_default = defineSection({
265
+ type: "button",
266
+ label: "Button",
267
+ icon: /* @__PURE__ */ jsx8(RectangleHorizontal, { size: 18 }),
268
+ schema: schema3,
269
+ component: ({ content, onChange }) => /* @__PURE__ */ jsx8(
270
+ Button,
2854
271
  {
2855
- items: content.content.items,
2856
- icon: options?.icon,
2857
- showLabel: options?.showLabel,
2858
- stackText: options?.stackText,
272
+ text: content.content.text,
273
+ link: content.content.link,
274
+ download: content.content.download,
2859
275
  onChange: onChange ? (c) => onChange(c) : void 0
2860
276
  }
2861
277
  ),
2862
- defaults: () => ({
2863
- type: "icon_list",
2864
- content: { items: [{ label: "", text: "" }] }
2865
- }),
2866
- getLabel: (content) => pluralize(content.content.items.length, "icon"),
2867
- settingsForm: IconListSettings
278
+ defaults: () => ({ type: "button", content: { text: "Button" } }),
279
+ getLabel: (content) => content.content.text,
280
+ settings: {
281
+ link: { type: "link", label: "Link", default: DEFAULT_LINK, target: "content" },
282
+ download: { type: "checkbox", label: "Download link", default: false, target: "content" }
283
+ }
2868
284
  });
2869
285
 
2870
286
  // src/components/sections/Container/index.tsx
2871
- import { z as z7 } from "zod";
287
+ import { z as z4 } from "zod";
2872
288
  import { LayoutTemplate } from "lucide-react";
2873
289
 
2874
290
  // src/components/sections/Container/Container.tsx
2875
291
  import "react";
2876
292
 
2877
293
  // src/components/editor/ChildBlockWrapper.tsx
2878
- import { forwardRef as forwardRef6 } from "react";
2879
- import { CopyPlus as CopyPlus2 } from "lucide-react";
2880
-
2881
- // src/components/editor/DragHandle.tsx
2882
- import { forwardRef as forwardRef4 } from "react";
2883
-
2884
- // src/components/shared/Tooltip.tsx
2885
- import { jsx as jsx31, jsxs as jsxs16 } from "react/jsx-runtime";
2886
- function Tooltip({ content, children, className }) {
2887
- return /* @__PURE__ */ jsxs16("div", { className: cn("group/tooltip relative inline-flex", className), children: [
2888
- children,
2889
- /* @__PURE__ */ jsx31(
2890
- "span",
2891
- {
2892
- 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",
2893
- role: "tooltip",
2894
- children: /* @__PURE__ */ jsx31("span", { className: "block whitespace-nowrap rounded bg-tooltip px-2 py-1 text-center text-xs leading-relaxed text-tooltip-muted", children: content })
2895
- }
2896
- )
2897
- ] });
2898
- }
2899
- function Kbd({ children }) {
2900
- return /* @__PURE__ */ jsx31("strong", { className: "text-tooltip-contrast", children });
2901
- }
2902
-
2903
- // src/components/editor/DragHandle.tsx
2904
- import { Fragment as Fragment5, jsx as jsx32, jsxs as jsxs17 } from "react/jsx-runtime";
2905
- var DragHandle2 = forwardRef4(function DragHandle3(_, ref) {
2906
- return /* @__PURE__ */ jsx32(Tooltip, { content: /* @__PURE__ */ jsxs17(Fragment5, { children: [
2907
- /* @__PURE__ */ jsx32(Kbd, { children: "Drag" }),
2908
- " to move"
2909
- ] }), children: /* @__PURE__ */ jsx32(
2910
- IconButton,
2911
- {
2912
- ref,
2913
- icon: /* @__PURE__ */ jsx32(DragHandle, { size: 18 }),
2914
- label: "Drag to reorder section",
2915
- 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",
2916
- tabIndex: -1
2917
- }
2918
- ) });
2919
- });
2920
-
2921
- // src/components/editor/DeleteButton.tsx
2922
- import { jsx as jsx33 } from "react/jsx-runtime";
2923
- function DeleteButton({ onDelete }) {
2924
- return /* @__PURE__ */ jsx33(
2925
- IconButton,
2926
- {
2927
- icon: /* @__PURE__ */ jsx33(DeleteIcon, { size: 16 }),
2928
- label: "Delete section",
2929
- intent: "destructive",
2930
- onClick: onDelete,
2931
- className: "pointer-events-auto"
2932
- }
2933
- );
2934
- }
2935
-
2936
- // src/components/editor/SettingsButton.tsx
2937
- import { jsx as jsx34 } from "react/jsx-runtime";
2938
- function SettingsButton({ onClick }) {
2939
- return /* @__PURE__ */ jsx34(
2940
- IconButton,
2941
- {
2942
- icon: /* @__PURE__ */ jsx34(SettingsIcon, { size: 16 }),
2943
- label: "Section settings",
2944
- onClick,
2945
- className: "pointer-events-auto"
2946
- }
2947
- );
2948
- }
294
+ import { forwardRef } from "react";
295
+ import { CopyPlus } from "lucide-react";
2949
296
 
2950
297
  // src/components/editor/ColSpanControl.tsx
2951
- import { useRef as useRef10, useState as useState14 } from "react";
298
+ import { useRef, useState } from "react";
2952
299
  import { Columns3 } from "lucide-react";
2953
-
2954
- // src/components/shared/Popover.tsx
2955
- import { useEffect as useEffect11, useLayoutEffect, useRef as useRef9, useState as useState13 } from "react";
2956
-
2957
- // src/hooks/useFocusTrap.ts
2958
- import { useEffect as useEffect10 } from "react";
2959
- var FOCUSABLE_SELECTOR = [
2960
- "a[href]",
2961
- "button:not([disabled])",
2962
- "input:not([disabled])",
2963
- "select:not([disabled])",
2964
- "textarea:not([disabled])",
2965
- '[tabindex]:not([tabindex="-1"])'
2966
- ].join(", ");
2967
- function useFocusTrap(ref, active) {
2968
- useEffect10(() => {
2969
- if (!active || !ref.current) return;
2970
- const container = ref.current;
2971
- const previouslyFocused = document.activeElement;
2972
- const getFocusable = () => Array.from(container.querySelectorAll(FOCUSABLE_SELECTOR));
2973
- const focusable = getFocusable();
2974
- if (focusable.length > 0) {
2975
- focusable[0].focus();
2976
- }
2977
- const handleKeyDown = (e) => {
2978
- if (e.key !== "Tab") return;
2979
- const elements = getFocusable();
2980
- if (elements.length === 0) return;
2981
- const first = elements[0];
2982
- const last = elements[elements.length - 1];
2983
- if (e.shiftKey) {
2984
- if (document.activeElement === first) {
2985
- e.preventDefault();
2986
- last.focus();
2987
- }
2988
- } else {
2989
- if (document.activeElement === last) {
2990
- e.preventDefault();
2991
- first.focus();
2992
- }
2993
- }
2994
- };
2995
- container.addEventListener("keydown", handleKeyDown);
2996
- return () => {
2997
- container.removeEventListener("keydown", handleKeyDown);
2998
- if (previouslyFocused && document.body.contains(previouslyFocused)) {
2999
- previouslyFocused.focus();
3000
- }
3001
- };
3002
- }, [active, ref]);
3003
- }
3004
-
3005
- // src/components/shared/Popover.tsx
3006
- import { jsx as jsx35 } from "react/jsx-runtime";
3007
- function Popover({
3008
- isOpen,
3009
- onClose,
3010
- anchorRef,
3011
- children,
3012
- align = "auto",
3013
- className
3014
- }) {
3015
- const panelRef = useRef9(null);
3016
- const [resolvedAlign, setResolvedAlign] = useState13("start");
3017
- useFocusTrap(panelRef, isOpen);
3018
- useLayoutEffect(() => {
3019
- if (!isOpen || align !== "auto") {
3020
- if (!isOpen) setResolvedAlign("start");
3021
- return;
3022
- }
3023
- const panel = panelRef.current;
3024
- if (!panel) return;
3025
- panel.style.left = "0";
3026
- panel.style.right = "auto";
3027
- const rect = panel.getBoundingClientRect();
3028
- if (rect.right > window.innerWidth) {
3029
- setResolvedAlign("end");
3030
- } else {
3031
- setResolvedAlign("start");
3032
- }
3033
- panel.style.left = "";
3034
- panel.style.right = "";
3035
- }, [isOpen, align]);
3036
- useEffect11(() => {
3037
- if (!isOpen) return;
3038
- function onKeyDown(e) {
3039
- if (e.key === "Escape") onClose();
3040
- }
3041
- function onMouseDown(e) {
3042
- const target = e.target;
3043
- if (panelRef.current?.contains(target)) return;
3044
- if (anchorRef.current?.contains(target)) return;
3045
- onClose();
3046
- }
3047
- document.addEventListener("keydown", onKeyDown);
3048
- document.addEventListener("mousedown", onMouseDown);
3049
- return () => {
3050
- document.removeEventListener("keydown", onKeyDown);
3051
- document.removeEventListener("mousedown", onMouseDown);
3052
- };
3053
- }, [isOpen, onClose, anchorRef]);
3054
- if (!isOpen) return null;
3055
- const effectiveAlign = align === "auto" ? resolvedAlign : align;
3056
- return /* @__PURE__ */ jsx35(
3057
- "div",
3058
- {
3059
- ref: panelRef,
3060
- role: "dialog",
3061
- "aria-modal": "false",
3062
- className: cn(
3063
- "absolute top-full z-50 mt-1 rounded-md border border-base-200 bg-base shadow-lg",
3064
- effectiveAlign === "end" ? "right-0" : "left-0",
3065
- className
3066
- ),
3067
- children
3068
- }
3069
- );
3070
- }
3071
-
3072
- // src/components/shared/PopoverItem.tsx
3073
- import { forwardRef as forwardRef5 } from "react";
3074
- import { jsx as jsx36 } from "react/jsx-runtime";
3075
- var PopoverItem = forwardRef5(
3076
- function PopoverItem2({ className, children, ...rest }, ref) {
3077
- return /* @__PURE__ */ jsx36(
3078
- "button",
3079
- {
3080
- ref,
3081
- type: "button",
3082
- className: cn(
3083
- "flex w-full cursor-pointer items-center gap-3 px-3 py-1.5 text-left text-xs hover:bg-base-accent",
3084
- className
3085
- ),
3086
- ...rest,
3087
- children
3088
- }
3089
- );
3090
- }
3091
- );
3092
-
3093
- // src/components/editor/ColSpanControl.tsx
3094
- import { jsx as jsx37, jsxs as jsxs18 } from "react/jsx-runtime";
300
+ import { jsx as jsx9, jsxs as jsxs2 } from "react/jsx-runtime";
3095
301
  function ColSpanControl({ colSpan, columns, onChange }) {
3096
- const buttonRef = useRef10(null);
3097
- const [open, setOpen] = useState14(false);
302
+ const buttonRef = useRef(null);
303
+ const [open, setOpen] = useState(false);
3098
304
  if (columns <= 1) return null;
3099
305
  const current = Math.min(Math.max(colSpan, 1), columns);
3100
- return /* @__PURE__ */ jsxs18("div", { className: "relative", children: [
3101
- /* @__PURE__ */ jsx37(
306
+ return /* @__PURE__ */ jsxs2("div", { className: "relative", children: [
307
+ /* @__PURE__ */ jsx9(
3102
308
  "button",
3103
309
  {
3104
310
  ref: buttonRef,
@@ -3108,10 +314,10 @@ function ColSpanControl({ colSpan, columns, onChange }) {
3108
314
  "aria-expanded": open,
3109
315
  onClick: () => setOpen((v) => !v),
3110
316
  className: "pointer-events-auto inline-flex cursor-pointer items-center gap-1 rounded p-1 text-xs text-base-contrast-light hover:text-primary",
3111
- children: /* @__PURE__ */ jsx37(Columns3, { size: 14 })
317
+ children: /* @__PURE__ */ jsx9(Columns3, { size: 14 })
3112
318
  }
3113
319
  ),
3114
- /* @__PURE__ */ jsx37(Popover, { isOpen: open, onClose: () => setOpen(false), anchorRef: buttonRef, className: "min-w-28", children: /* @__PURE__ */ jsx37("ul", { role: "list", className: "py-1", children: Array.from({ length: columns }, (_, i) => i + 1).map((span) => /* @__PURE__ */ jsx37("li", { children: /* @__PURE__ */ jsxs18(
320
+ /* @__PURE__ */ jsx9(Popover, { isOpen: open, onClose: () => setOpen(false), anchorRef: buttonRef, className: "min-w-28", children: /* @__PURE__ */ jsx9("ul", { role: "list", className: "py-1", children: Array.from({ length: columns }, (_, i) => i + 1).map((span) => /* @__PURE__ */ jsx9("li", { children: /* @__PURE__ */ jsxs2(
3115
321
  PopoverItem,
3116
322
  {
3117
323
  "aria-current": span === current,
@@ -3130,8 +336,8 @@ function ColSpanControl({ colSpan, columns, onChange }) {
3130
336
  }
3131
337
 
3132
338
  // src/components/editor/ChildBlockWrapper.tsx
3133
- import { jsx as jsx38, jsxs as jsxs19 } from "react/jsx-runtime";
3134
- var ChildBlockWrapper = forwardRef6(
339
+ import { jsx as jsx10, jsxs as jsxs3 } from "react/jsx-runtime";
340
+ var ChildBlockWrapper = forwardRef(
3135
341
  function ChildBlockWrapper2({
3136
342
  label,
3137
343
  columns,
@@ -3146,7 +352,7 @@ var ChildBlockWrapper = forwardRef6(
3146
352
  onOpenSettings,
3147
353
  children
3148
354
  }, ref) {
3149
- return /* @__PURE__ */ jsxs19(
355
+ return /* @__PURE__ */ jsxs3(
3150
356
  "div",
3151
357
  {
3152
358
  ref,
@@ -3156,39 +362,39 @@ var ChildBlockWrapper = forwardRef6(
3156
362
  "hover:outline hover:outline-2 hover:outline-primary/30"
3157
363
  ),
3158
364
  children: [
3159
- /* @__PURE__ */ jsxs19("div", { className: "absolute top-0 left-0 -translate-x-full z-20 flex flex-col items-center gap-1 opacity-0 transition-opacity group-hover/childblock:opacity-100 group-focus-within/childblock:opacity-100 pointer-events-none", children: [
3160
- /* @__PURE__ */ jsx38("span", { className: "sr-only", children: label }),
3161
- /* @__PURE__ */ jsx38("div", { className: "pointer-events-auto", children: /* @__PURE__ */ jsx38(DragHandle2, { ref: dragHandleRef }) }),
3162
- /* @__PURE__ */ jsx38("div", { className: "pointer-events-auto", children: /* @__PURE__ */ jsx38(ColSpanControl, { colSpan, columns, onChange: onColSpanChange }) }),
3163
- hasSettings && onOpenSettings && /* @__PURE__ */ jsx38("div", { className: "pointer-events-auto", children: /* @__PURE__ */ jsx38(SettingsButton, { onClick: onOpenSettings }) }),
3164
- /* @__PURE__ */ jsx38("div", { className: "pointer-events-auto", children: /* @__PURE__ */ jsx38(
365
+ /* @__PURE__ */ jsxs3("div", { className: "absolute top-0 left-0 -translate-x-full z-20 flex flex-col items-center gap-1 opacity-0 transition-opacity group-hover/childblock:opacity-100 group-focus-within/childblock:opacity-100 pointer-events-none", children: [
366
+ /* @__PURE__ */ jsx10("span", { className: "sr-only", children: label }),
367
+ /* @__PURE__ */ jsx10("div", { className: "pointer-events-auto", children: /* @__PURE__ */ jsx10(DragHandle, { ref: dragHandleRef }) }),
368
+ /* @__PURE__ */ jsx10("div", { className: "pointer-events-auto", children: /* @__PURE__ */ jsx10(ColSpanControl, { colSpan, columns, onChange: onColSpanChange }) }),
369
+ hasSettings && onOpenSettings && /* @__PURE__ */ jsx10("div", { className: "pointer-events-auto", children: /* @__PURE__ */ jsx10(SettingsButton, { onClick: onOpenSettings }) }),
370
+ /* @__PURE__ */ jsx10("div", { className: "pointer-events-auto", children: /* @__PURE__ */ jsx10(
3165
371
  "button",
3166
372
  {
3167
373
  type: "button",
3168
374
  "aria-label": "Duplicate block",
3169
375
  onClick: onDuplicate,
3170
376
  className: "cursor-pointer rounded p-1 text-base-contrast-light hover:text-primary",
3171
- children: /* @__PURE__ */ jsx38(CopyPlus2, { size: 16 })
377
+ children: /* @__PURE__ */ jsx10(CopyPlus, { size: 16 })
3172
378
  }
3173
379
  ) }),
3174
- /* @__PURE__ */ jsx38("div", { className: "pointer-events-auto", children: /* @__PURE__ */ jsx38(DeleteButton, { onDelete }) })
380
+ /* @__PURE__ */ jsx10("div", { className: "pointer-events-auto", children: /* @__PURE__ */ jsx10(DeleteButton, { onDelete }) })
3175
381
  ] }),
3176
- closestEdge === "left" && /* @__PURE__ */ jsx38(
382
+ closestEdge === "left" && /* @__PURE__ */ jsx10(
3177
383
  "div",
3178
384
  {
3179
385
  "data-drop-edge": "left",
3180
386
  className: "absolute bottom-0 left-0 top-0 z-10 w-0.5 -translate-x-1 bg-primary"
3181
387
  }
3182
388
  ),
3183
- closestEdge === "right" && /* @__PURE__ */ jsx38(
389
+ closestEdge === "right" && /* @__PURE__ */ jsx10(
3184
390
  "div",
3185
391
  {
3186
392
  "data-drop-edge": "right",
3187
393
  className: "absolute bottom-0 right-0 top-0 z-10 w-0.5 translate-x-1 bg-primary"
3188
394
  }
3189
395
  ),
3190
- closestEdge === "top" && /* @__PURE__ */ jsx38("div", { "data-drop-edge": "top", className: "absolute left-0 right-0 top-0 z-10 h-0.5 -translate-y-1 bg-primary" }),
3191
- closestEdge === "bottom" && /* @__PURE__ */ jsx38("div", { "data-drop-edge": "bottom", className: "absolute left-0 right-0 bottom-0 z-10 h-0.5 translate-y-1 bg-primary" }),
396
+ closestEdge === "top" && /* @__PURE__ */ jsx10("div", { "data-drop-edge": "top", className: "absolute left-0 right-0 top-0 z-10 h-0.5 -translate-y-1 bg-primary" }),
397
+ closestEdge === "bottom" && /* @__PURE__ */ jsx10("div", { "data-drop-edge": "bottom", className: "absolute left-0 right-0 bottom-0 z-10 h-0.5 translate-y-1 bg-primary" }),
3192
398
  children
3193
399
  ]
3194
400
  }
@@ -3196,334 +402,14 @@ var ChildBlockWrapper = forwardRef6(
3196
402
  }
3197
403
  );
3198
404
 
3199
- // src/components/editor/SettingsForm.tsx
3200
- import { useId as useId4, useState as useState16 } from "react";
3201
-
3202
- // src/components/shared/Select.tsx
3203
- import { useId as useId3 } from "react";
3204
- import { ChevronDown } from "lucide-react";
3205
- import { jsx as jsx39, jsxs as jsxs20 } from "react/jsx-runtime";
3206
- function Select({
3207
- label,
3208
- value,
3209
- onChange,
3210
- options,
3211
- disabled,
3212
- className,
3213
- selectClassName
3214
- }) {
3215
- const id = useId3();
3216
- return /* @__PURE__ */ jsxs20("div", { className, children: [
3217
- label && /* @__PURE__ */ jsx39(FormLabel, { htmlFor: id, children: label }),
3218
- /* @__PURE__ */ jsxs20("div", { className: "relative", children: [
3219
- /* @__PURE__ */ jsx39(
3220
- "select",
3221
- {
3222
- id,
3223
- value,
3224
- onChange: (e) => onChange(e.target.value),
3225
- disabled,
3226
- className: cn(
3227
- "w-full appearance-none rounded border border-base-200 bg-base py-2 pr-8 pl-3 text-sm text-base-contrast",
3228
- "focus:border-base-contrast focus:outline-none focus:ring-1 focus:ring-base-contrast",
3229
- disabled && "cursor-not-allowed opacity-50",
3230
- selectClassName
3231
- ),
3232
- children: options.map((opt) => /* @__PURE__ */ jsx39("option", { value: opt.value, children: opt.label }, opt.value))
3233
- }
3234
- ),
3235
- /* @__PURE__ */ jsx39(
3236
- ChevronDown,
3237
- {
3238
- size: 14,
3239
- className: "pointer-events-none absolute right-2.5 top-1/2 -translate-y-1/2 text-base-contrast-light"
3240
- }
3241
- )
3242
- ] })
3243
- ] });
3244
- }
3245
-
3246
- // src/components/shared/LinkField.tsx
3247
- import { Fragment as Fragment6, jsx as jsx40, jsxs as jsxs21 } from "react/jsx-runtime";
3248
- var TARGET_OPTIONS = [
3249
- { label: "Same tab (_self)", value: "_self" },
3250
- { label: "New tab (_blank)", value: "_blank" }
3251
- ];
3252
- function LinkField({ label, value, onChange }) {
3253
- const { pages, getPageHeadings } = usePages();
3254
- const isInternal = value.kind === "internal";
3255
- function setMode(kind) {
3256
- if (kind === value.kind) return;
3257
- onChange(kind === "external" ? { kind: "external", href: "", target: value.target } : { kind: "internal", pageId: "", anchorSectionId: null, target: "_self" });
3258
- }
3259
- const pageId = isInternal ? value.pageId : "";
3260
- const headings = pageId ? getPageHeadings(pageId) : [];
3261
- return /* @__PURE__ */ jsxs21("div", { className: "flex flex-col gap-3", children: [
3262
- /* @__PURE__ */ jsx40(FormLabel, { children: label }),
3263
- /* @__PURE__ */ jsx40("div", { className: "flex gap-2", role: "tablist", children: ["external", "internal"].map((kind) => /* @__PURE__ */ jsx40(
3264
- "button",
3265
- {
3266
- type: "button",
3267
- role: "tab",
3268
- "aria-selected": value.kind === kind,
3269
- onClick: () => setMode(kind),
3270
- className: cn(
3271
- "cursor-pointer rounded border px-3 py-1 text-sm capitalize",
3272
- value.kind === kind ? "border-primary bg-primary text-primary-contrast" : "border-base-200 text-base-contrast"
3273
- ),
3274
- children: kind === "external" ? "External URL" : "Internal page"
3275
- },
3276
- kind
3277
- )) }),
3278
- value.kind === "external" ? /* @__PURE__ */ jsx40(
3279
- Input,
3280
- {
3281
- label: "URL",
3282
- value: value.href,
3283
- placeholder: "https://...",
3284
- onChange: (href) => onChange({ kind: "external", href, target: value.target })
3285
- }
3286
- ) : /* @__PURE__ */ jsxs21(Fragment6, { children: [
3287
- /* @__PURE__ */ jsx40(
3288
- Select,
3289
- {
3290
- label: "Page",
3291
- value: value.pageId,
3292
- options: [{ label: "Select a page\u2026", value: "" }, ...pages.map((p) => ({ label: p.title, value: p.id }))],
3293
- onChange: (pid) => onChange({ kind: "internal", pageId: pid, anchorSectionId: null, target: value.target })
3294
- }
3295
- ),
3296
- /* @__PURE__ */ jsx40(
3297
- Select,
3298
- {
3299
- label: "Section",
3300
- value: value.anchorSectionId ?? "",
3301
- disabled: !pageId,
3302
- options: [{ label: "None (top of page)", value: "" }, ...headings.map((h) => ({ label: h.label, value: h.id }))],
3303
- onChange: (anchor) => onChange({ kind: "internal", pageId, anchorSectionId: anchor || null, target: value.target })
3304
- }
3305
- )
3306
- ] }),
3307
- value.kind === "external" && /* @__PURE__ */ jsx40(
3308
- Select,
3309
- {
3310
- label: "Opens in",
3311
- value: value.target,
3312
- options: TARGET_OPTIONS,
3313
- onChange: (t) => onChange({ ...value, target: t })
3314
- }
3315
- )
3316
- ] });
3317
- }
3318
-
3319
- // src/components/shared/Tabs.tsx
3320
- import { useState as useState15 } from "react";
3321
- import { jsx as jsx41, jsxs as jsxs22 } from "react/jsx-runtime";
3322
- function Tabs({ tabs, defaultTabId, activeTabId, onTabChange, fullBleedTabBar }) {
3323
- const [uncontrolledId, setUncontrolledId] = useState15(defaultTabId ?? tabs[0]?.id);
3324
- const isControlled = activeTabId !== void 0;
3325
- const activeId = isControlled ? activeTabId : uncontrolledId;
3326
- const activeTab = tabs.find((t) => t.id === activeId) ?? tabs[0];
3327
- function handleSelect(id) {
3328
- if (!isControlled) setUncontrolledId(id);
3329
- onTabChange?.(id);
3330
- }
3331
- return /* @__PURE__ */ jsxs22("div", { className: "flex flex-1 flex-col overflow-hidden", children: [
3332
- /* @__PURE__ */ jsx41("div", { className: cn("border-b border-base-200", fullBleedTabBar && "-mx-6 px-6"), children: /* @__PURE__ */ jsx41("div", { className: "flex", role: "tablist", children: tabs.map((tab) => /* @__PURE__ */ jsx41(
3333
- "button",
3334
- {
3335
- type: "button",
3336
- role: "tab",
3337
- "aria-selected": activeTab?.id === tab.id,
3338
- onClick: () => handleSelect(tab.id),
3339
- className: cn(
3340
- "cursor-pointer px-4 py-2 text-sm font-medium border-b-2 -mb-px",
3341
- activeTab?.id === tab.id ? "border-brand text-brand" : "border-transparent text-base-contrast-light hover:text-base-contrast"
3342
- ),
3343
- children: tab.label
3344
- },
3345
- tab.id
3346
- )) }) }),
3347
- /* @__PURE__ */ jsx41("div", { role: "tabpanel", children: activeTab?.content })
3348
- ] });
3349
- }
3350
-
3351
- // src/components/editor/SettingsForm.tsx
3352
- import { jsx as jsx42, jsxs as jsxs23 } from "react/jsx-runtime";
3353
- function resolveValues(schema8, values) {
3354
- const resolved = {};
3355
- for (const [key, field] of Object.entries(schema8)) {
3356
- resolved[key] = values[key] ?? field.default;
3357
- }
3358
- return resolved;
3359
- }
3360
- function coerceValue(field, value) {
3361
- if (field.type === "select" && "coerce" in field && field.coerce === "number") {
3362
- return Number(value);
3363
- }
3364
- return value;
3365
- }
3366
- function normalizeEmpty(field, value) {
3367
- if (field.type === "select" && "emptyIsUndefined" in field && field.emptyIsUndefined && value === "") {
3368
- return void 0;
3369
- }
3370
- return value;
3371
- }
3372
- function splitByTarget(schema8, allValues) {
3373
- const content = {};
3374
- const options = {};
3375
- for (const [key, field] of Object.entries(schema8)) {
3376
- const value = normalizeEmpty(field, coerceValue(field, allValues[key]));
3377
- const target = field.target ?? "options";
3378
- if (target === "content") {
3379
- content[key] = value;
3380
- } else {
3381
- options[key] = value;
3382
- }
3383
- }
3384
- return { content, options };
3385
- }
3386
- function RangeField({
3387
- field,
3388
- value,
3389
- onChange
3390
- }) {
3391
- const id = useId4();
3392
- return /* @__PURE__ */ jsxs23("div", { children: [
3393
- /* @__PURE__ */ jsx42(FormLabel, { htmlFor: id, children: field.label }),
3394
- /* @__PURE__ */ jsx42(
3395
- "input",
3396
- {
3397
- id,
3398
- type: "range",
3399
- min: field.min,
3400
- max: field.max,
3401
- step: field.step,
3402
- value,
3403
- onChange: (e) => onChange(Number(e.target.value)),
3404
- className: cn(
3405
- "w-full accent-primary"
3406
- )
3407
- }
3408
- )
3409
- ] });
3410
- }
3411
- function renderField(key, field, value, handleFieldChange) {
3412
- switch (field.type) {
3413
- case "text":
3414
- return /* @__PURE__ */ jsx42(
3415
- Input,
3416
- {
3417
- label: field.label,
3418
- value: String(value ?? ""),
3419
- placeholder: field.placeholder,
3420
- onChange: (v) => handleFieldChange(key, v)
3421
- },
3422
- key
3423
- );
3424
- case "number":
3425
- return /* @__PURE__ */ jsx42(
3426
- Input,
3427
- {
3428
- label: field.label,
3429
- type: "number",
3430
- value: String(value ?? ""),
3431
- onChange: (v) => handleFieldChange(key, v === "" ? "" : Number(v))
3432
- },
3433
- key
3434
- );
3435
- case "checkbox":
3436
- return /* @__PURE__ */ jsx42(
3437
- Checkbox,
3438
- {
3439
- label: field.label,
3440
- checked: Boolean(value),
3441
- onChange: (checked) => handleFieldChange(key, checked)
3442
- },
3443
- key
3444
- );
3445
- case "select":
3446
- return /* @__PURE__ */ jsx42(
3447
- Select,
3448
- {
3449
- label: field.label,
3450
- value: String(value ?? ""),
3451
- options: field.options,
3452
- onChange: (v) => handleFieldChange(key, v)
3453
- },
3454
- key
3455
- );
3456
- case "range":
3457
- return /* @__PURE__ */ jsx42(
3458
- RangeField,
3459
- {
3460
- field,
3461
- value: Number(value ?? field.min),
3462
- onChange: (v) => handleFieldChange(key, v)
3463
- },
3464
- key
3465
- );
3466
- case "link":
3467
- return /* @__PURE__ */ jsx42(
3468
- LinkField,
3469
- {
3470
- label: field.label,
3471
- value: value ?? field.default,
3472
- onChange: (v) => handleFieldChange(key, v)
3473
- },
3474
- key
3475
- );
3476
- default:
3477
- return null;
3478
- }
3479
- }
3480
- function SettingsForm({ schema: schema8, values, onChange, tabs }) {
3481
- const [local, setLocal] = useState16(() => resolveValues(schema8, values));
3482
- function handleFieldChange(key, newValue) {
3483
- const next = { ...local, [key]: newValue };
3484
- setLocal(next);
3485
- onChange(splitByTarget(schema8, next));
3486
- }
3487
- if (tabs) {
3488
- const fields = (keys) => keys.filter((key) => key in schema8).map((key) => renderField(key, schema8[key], local[key], handleFieldChange));
3489
- const listed = new Set(tabs.flatMap((t) => t.fields));
3490
- const otherKeys = Object.keys(schema8).filter((key) => !listed.has(key));
3491
- const tabItems = [
3492
- ...tabs.map((t) => ({ id: t.label, label: t.label, content: /* @__PURE__ */ jsx42("div", { className: "flex flex-col gap-4", children: fields(t.fields) }) })),
3493
- ...otherKeys.length > 0 ? [{ id: "__other__", label: "Other", content: /* @__PURE__ */ jsx42("div", { className: "flex flex-col gap-4", children: fields(otherKeys) }) }] : []
3494
- ];
3495
- return /* @__PURE__ */ jsx42(Tabs, { tabs: tabItems, fullBleedTabBar: true });
3496
- }
3497
- return /* @__PURE__ */ jsx42("div", { className: "flex flex-col gap-4", children: Object.entries(schema8).map(([key, field]) => renderField(key, field, local[key], handleFieldChange)) });
3498
- }
3499
-
3500
- // src/hooks/useBlockDnd.ts
3501
- import { useEffect as useEffect12, useRef as useRef11, useState as useState17 } from "react";
3502
-
3503
- // src/lib/block-dnd.ts
3504
- var ROOT_CONTAINER_ID = "__root__";
3505
- function buildBlockDragData(args) {
3506
- return { dragType: "block", ...args };
3507
- }
3508
- function buildBlockDropData(args) {
3509
- return { ...args };
3510
- }
3511
- function isBlockDragData(data) {
3512
- return data.dragType === "block";
3513
- }
3514
- function canDropBlock(source, dropContainerId) {
3515
- if (source.blockId === dropContainerId) return false;
3516
- if (source.isContainer && dropContainerId !== ROOT_CONTAINER_ID) return false;
3517
- return true;
3518
- }
3519
-
3520
405
  // src/hooks/useBlockDnd.ts
406
+ import { useEffect, useRef as useRef2, useState as useState2 } from "react";
3521
407
  function useBlockDnd({ blockId, containerId, index, isContainer, allowedEdges, enabled }) {
3522
- const dragRef = useRef11(null);
3523
- const handleRef = useRef11(null);
3524
- const [closestEdge, setClosestEdge] = useState17(null);
3525
- const [isDragging, setIsDragging] = useState17(false);
3526
- useEffect12(() => {
408
+ const dragRef = useRef2(null);
409
+ const handleRef = useRef2(null);
410
+ const [closestEdge, setClosestEdge] = useState2(null);
411
+ const [isDragging, setIsDragging] = useState2(false);
412
+ useEffect(() => {
3527
413
  const element = dragRef.current;
3528
414
  const handle = handleRef.current;
3529
415
  if (!element || !enabled) return;
@@ -3579,9 +465,9 @@ function useBlockDnd({ blockId, containerId, index, isContainer, allowedEdges, e
3579
465
  return { dragRef, handleRef, closestEdge, isDragging };
3580
466
  }
3581
467
  function useBlockDropZone({ containerId, index, toColumn, allowedEdges, enabled }) {
3582
- const dropRef = useRef11(null);
3583
- const [closestEdge, setClosestEdge] = useState17(null);
3584
- useEffect12(() => {
468
+ const dropRef = useRef2(null);
469
+ const [closestEdge, setClosestEdge] = useState2(null);
470
+ useEffect(() => {
3585
471
  const element = dropRef.current;
3586
472
  if (!element || !enabled) return;
3587
473
  let cleanup;
@@ -3678,7 +564,7 @@ function trimTrailingSpacers(container) {
3678
564
  }
3679
565
 
3680
566
  // src/components/sections/Container/Container.tsx
3681
- import { jsx as jsx43, jsxs as jsxs24 } from "react/jsx-runtime";
567
+ import { jsx as jsx11, jsxs as jsxs4 } from "react/jsx-runtime";
3682
568
  var ROW_EDGES = ["left", "right"];
3683
569
  var COL_EDGES = ["top", "bottom"];
3684
570
  function ContainerChild({
@@ -3709,14 +595,14 @@ function ContainerChild({
3709
595
  allowedEdges: flow === "column" ? COL_EDGES : ROW_EDGES,
3710
596
  enabled: true
3711
597
  });
3712
- return /* @__PURE__ */ jsx43(
598
+ return /* @__PURE__ */ jsx11(
3713
599
  "div",
3714
600
  {
3715
601
  ref: dragRef,
3716
602
  className: cellClass,
3717
603
  "data-child-index": index,
3718
604
  "data-child-id": child.id,
3719
- children: /* @__PURE__ */ jsx43(
605
+ children: /* @__PURE__ */ jsx11(
3720
606
  ChildBlockWrapper,
3721
607
  {
3722
608
  label,
@@ -3730,7 +616,7 @@ function ContainerChild({
3730
616
  onDuplicate,
3731
617
  onColSpanChange,
3732
618
  onOpenSettings,
3733
- children: /* @__PURE__ */ jsx43(
619
+ children: /* @__PURE__ */ jsx11(
3734
620
  ChildComp,
3735
621
  {
3736
622
  content: child,
@@ -3758,7 +644,7 @@ function EmptyColumnCell({
3758
644
  enabled: true
3759
645
  });
3760
646
  const active = closestEdge !== null;
3761
- return /* @__PURE__ */ jsx43(
647
+ return /* @__PURE__ */ jsx11(
3762
648
  "div",
3763
649
  {
3764
650
  ref: dropRef,
@@ -3796,7 +682,7 @@ function Container({ content, isEditMode, onChange, openModal }) {
3796
682
  flow === "column" && "grid-flow-col"
3797
683
  );
3798
684
  if (!editable) {
3799
- return /* @__PURE__ */ jsx43("div", { className: "@container", children: /* @__PURE__ */ jsx43("div", { className: gridClass, children: children.map((child, index) => {
685
+ return /* @__PURE__ */ jsx11("div", { className: "@container", children: /* @__PURE__ */ jsx11("div", { className: gridClass, children: children.map((child, index) => {
3800
686
  const def = getSection(child.type);
3801
687
  if (!def) return null;
3802
688
  const Child = def.component;
@@ -3807,7 +693,7 @@ function Container({ content, isEditMode, onChange, openModal }) {
3807
693
  colSpanClass[columns]?.[Math.min(span, columns)] || void 0,
3808
694
  child.type === "spacer" && spacerHiddenClass[columns]
3809
695
  );
3810
- return /* @__PURE__ */ jsx43("div", { className: cellClass, children: /* @__PURE__ */ jsx43(Child, { content: child, options: mergedOptions, isEditMode }) }, child.id ?? index);
696
+ return /* @__PURE__ */ jsx11("div", { className: cellClass, children: /* @__PURE__ */ jsx11(Child, { content: child, options: mergedOptions, isEditMode }) }, child.id ?? index);
3811
697
  }) }) });
3812
698
  }
3813
699
  function columnStartOf(index) {
@@ -3821,7 +707,7 @@ function Container({ content, isEditMode, onChange, openModal }) {
3821
707
  const def = getSection(child.type);
3822
708
  if (!def) return null;
3823
709
  if (isSpacer(child)) {
3824
- return /* @__PURE__ */ jsx43(
710
+ return /* @__PURE__ */ jsx11(
3825
711
  EmptyColumnCell,
3826
712
  {
3827
713
  containerId: container.id,
@@ -3849,12 +735,12 @@ function Container({ content, isEditMode, onChange, openModal }) {
3849
735
  const CustomForm = def.settingsForm;
3850
736
  openModal(
3851
737
  `${def.label} Settings`,
3852
- /* @__PURE__ */ jsx43(CustomForm, { ...seed, onChange: applyResult })
738
+ /* @__PURE__ */ jsx11(CustomForm, { ...seed, onChange: applyResult })
3853
739
  );
3854
740
  } else if (def.settings) {
3855
741
  openModal(
3856
742
  `${def.label} Settings`,
3857
- /* @__PURE__ */ jsx43(
743
+ /* @__PURE__ */ jsx11(
3858
744
  SettingsForm,
3859
745
  {
3860
746
  schema: def.settings,
@@ -3866,7 +752,7 @@ function Container({ content, isEditMode, onChange, openModal }) {
3866
752
  );
3867
753
  }
3868
754
  } : void 0;
3869
- return /* @__PURE__ */ jsx43(
755
+ return /* @__PURE__ */ jsx11(
3870
756
  ContainerChild,
3871
757
  {
3872
758
  containerId: container.id,
@@ -3892,9 +778,9 @@ function Container({ content, isEditMode, onChange, openModal }) {
3892
778
  }
3893
779
  const occupied = occupiedColumns(children, columns);
3894
780
  const emptyCols = occupied < columns ? columns - occupied : 0;
3895
- return /* @__PURE__ */ jsx43("div", { className: "@container", children: /* @__PURE__ */ jsxs24("div", { className: gridClass, children: [
781
+ return /* @__PURE__ */ jsx11("div", { className: "@container", children: /* @__PURE__ */ jsxs4("div", { className: gridClass, children: [
3896
782
  children.map(renderEditChild),
3897
- Array.from({ length: emptyCols }, (_, i) => /* @__PURE__ */ jsx43(
783
+ Array.from({ length: emptyCols }, (_, i) => /* @__PURE__ */ jsx11(
3898
784
  EmptyColumnCell,
3899
785
  {
3900
786
  containerId: container.id,
@@ -3907,8 +793,8 @@ function Container({ content, isEditMode, onChange, openModal }) {
3907
793
  }
3908
794
 
3909
795
  // src/components/sections/Container/ContainerSettingsForm.tsx
3910
- import { useState as useState18 } from "react";
3911
- import { jsx as jsx44, jsxs as jsxs25 } from "react/jsx-runtime";
796
+ import { useState as useState3 } from "react";
797
+ import { jsx as jsx12, jsxs as jsxs5 } from "react/jsx-runtime";
3912
798
  function inheritableSchema(children) {
3913
799
  const out = {};
3914
800
  for (const child of children) {
@@ -3928,8 +814,8 @@ function ContainerSettingsForm({
3928
814
  children = [],
3929
815
  onChange
3930
816
  }) {
3931
- const [layout, setLayout] = useState18({ columns, flow });
3932
- const [defaults, setDefaults] = useState18(childDefaults);
817
+ const [layout, setLayout] = useState3({ columns, flow });
818
+ const [defaults, setDefaults] = useState3(childDefaults);
3933
819
  const childSchema = inheritableSchema(children);
3934
820
  const hasChildDefaults = Object.keys(childSchema).length > 0;
3935
821
  function emit(nextLayout, nextDefaults) {
@@ -3958,10 +844,10 @@ function ContainerSettingsForm({
3958
844
  ]
3959
845
  }
3960
846
  };
3961
- return /* @__PURE__ */ jsxs25("div", { className: "flex flex-col gap-6", children: [
3962
- /* @__PURE__ */ jsxs25("fieldset", { className: "flex flex-col gap-4", children: [
3963
- /* @__PURE__ */ jsx44("legend", { className: "mb-2 text-sm font-semibold text-base-contrast", children: "Layout" }),
3964
- /* @__PURE__ */ jsx44(
847
+ return /* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-6", children: [
848
+ /* @__PURE__ */ jsxs5("fieldset", { className: "flex flex-col gap-4", children: [
849
+ /* @__PURE__ */ jsx12("legend", { className: "mb-2 text-sm font-semibold text-base-contrast", children: "Layout" }),
850
+ /* @__PURE__ */ jsx12(
3965
851
  SettingsForm,
3966
852
  {
3967
853
  schema: layoutSchema,
@@ -3977,9 +863,9 @@ function ContainerSettingsForm({
3977
863
  }
3978
864
  )
3979
865
  ] }),
3980
- hasChildDefaults && /* @__PURE__ */ jsxs25("fieldset", { className: "flex flex-col gap-4 border-t border-base-200 pt-4", children: [
3981
- /* @__PURE__ */ jsx44("legend", { className: "mb-2 text-sm font-semibold text-base-contrast", children: "Apply to all items" }),
3982
- /* @__PURE__ */ jsx44(
866
+ hasChildDefaults && /* @__PURE__ */ jsxs5("fieldset", { className: "flex flex-col gap-4 border-t border-base-200 pt-4", children: [
867
+ /* @__PURE__ */ jsx12("legend", { className: "mb-2 text-sm font-semibold text-base-contrast", children: "Apply to all items" }),
868
+ /* @__PURE__ */ jsx12(
3983
869
  SettingsForm,
3984
870
  {
3985
871
  schema: childSchema,
@@ -3996,16 +882,16 @@ function ContainerSettingsForm({
3996
882
  }
3997
883
 
3998
884
  // src/components/sections/Container/index.tsx
3999
- import { jsx as jsx45 } from "react/jsx-runtime";
4000
- var schema6 = z7.object({
4001
- type: z7.literal("container"),
4002
- content: z7.object({
4003
- columns: z7.number().int().min(1).max(MAX_CONTAINER_COLUMNS).default(1),
4004
- flow: z7.enum(["row", "column"]).default("row"),
4005
- childDefaults: z7.record(z7.string(), z7.unknown()).optional(),
885
+ import { jsx as jsx13 } from "react/jsx-runtime";
886
+ var schema4 = z4.object({
887
+ type: z4.literal("container"),
888
+ content: z4.object({
889
+ columns: z4.number().int().min(1).max(MAX_CONTAINER_COLUMNS).default(1),
890
+ flow: z4.enum(["row", "column"]).default("row"),
891
+ childDefaults: z4.record(z4.string(), z4.unknown()).optional(),
4006
892
  // Recursive: children are full blocks. z.lazy defers evaluation to parse
4007
893
  // time, by which point every section schema (incl. container) is registered.
4008
- children: z7.array(z7.lazy(() => getSectionSchema())).default([])
894
+ children: z4.array(z4.lazy(() => getSectionSchema())).default([])
4009
895
  }).transform((c) => ({
4010
896
  ...c,
4011
897
  children: c.children.map(
@@ -4016,9 +902,9 @@ var schema6 = z7.object({
4016
902
  var Container_default = defineSection({
4017
903
  type: "container",
4018
904
  label: "Container",
4019
- icon: /* @__PURE__ */ jsx45(LayoutTemplate, { size: 18 }),
4020
- schema: schema6,
4021
- component: ({ content, onChange, isEditMode, openModal }) => /* @__PURE__ */ jsx45(Container, { content, isEditMode: !!isEditMode, onChange, openModal }),
905
+ icon: /* @__PURE__ */ jsx13(LayoutTemplate, { size: 18 }),
906
+ schema: schema4,
907
+ component: ({ content, onChange, isEditMode, openModal }) => /* @__PURE__ */ jsx13(Container, { content, isEditMode: !!isEditMode, onChange, openModal }),
4022
908
  defaults: () => ({
4023
909
  type: "container",
4024
910
  content: { columns: 1, flow: "row", children: [] }
@@ -4028,44 +914,45 @@ var Container_default = defineSection({
4028
914
  });
4029
915
 
4030
916
  // src/components/sections/Spacer/index.tsx
4031
- import { z as z8 } from "zod";
917
+ import { z as z5 } from "zod";
4032
918
  import { SquareDashed } from "lucide-react";
4033
919
 
4034
920
  // src/components/sections/Spacer/Spacer.tsx
4035
- import { jsx as jsx46 } from "react/jsx-runtime";
921
+ import { jsx as jsx14 } from "react/jsx-runtime";
4036
922
  function Spacer() {
4037
- return /* @__PURE__ */ jsx46("div", { "data-spacer": true, "aria-hidden": "true", className: "h-full" });
923
+ return /* @__PURE__ */ jsx14("div", { "data-spacer": true, "aria-hidden": "true", className: "h-full" });
4038
924
  }
4039
925
 
4040
926
  // src/components/sections/Spacer/index.tsx
4041
- import { jsx as jsx47 } from "react/jsx-runtime";
4042
- var schema7 = z8.object({
4043
- type: z8.literal("spacer"),
4044
- content: z8.object({}).default({})
927
+ import { jsx as jsx15 } from "react/jsx-runtime";
928
+ var schema5 = z5.object({
929
+ type: z5.literal("spacer"),
930
+ content: z5.object({}).default({})
4045
931
  });
4046
932
  var Spacer_default = defineSection({
4047
933
  type: "spacer",
4048
934
  label: "Spacer",
4049
- icon: /* @__PURE__ */ jsx47(SquareDashed, { size: 18 }),
4050
- schema: schema7,
4051
- component: () => /* @__PURE__ */ jsx47(Spacer, {}),
935
+ icon: /* @__PURE__ */ jsx15(SquareDashed, { size: 18 }),
936
+ schema: schema5,
937
+ component: () => /* @__PURE__ */ jsx15(Spacer, {}),
4052
938
  defaults: () => ({ type: "spacer", content: {} })
4053
939
  });
4054
940
 
4055
- // src/components/sections/all-sections.ts
4056
- var allSectionDefs = [
941
+ // src/components/sections/core-sections.ts
942
+ var coreSectionDefs = [
4057
943
  LinkHeading_default,
4058
944
  SubHeading_default,
4059
945
  SubSubHeading_default,
4060
946
  Prose_default,
4061
947
  Media_default,
4062
948
  Button_default,
4063
- Colors_default,
4064
- IconList_default,
4065
949
  Container_default,
4066
950
  Spacer_default
4067
951
  ];
4068
952
 
953
+ // src/components/sections/all-sections.ts
954
+ var allSectionDefs = [...coreSectionDefs, ...brandGuideSectionDefs];
955
+
4069
956
  // src/components/sections/register-schemas.ts
4070
957
  var _ensured = false;
4071
958
  function ensureSchemasRegistered() {