@drawnagency/primitives 0.1.66 → 0.1.67

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 (60) hide show
  1. package/dist/chunk-4VMAZZXT.js +968 -0
  2. package/dist/{chunk-VEIDANEU.js → chunk-KM6HOW4H.js} +126 -39
  3. package/dist/{chunk-DQTKWKSK.js → chunk-WJ7RZ6CC.js} +11 -2
  4. package/dist/components/primitives/defineHeadingSection.d.ts +2 -0
  5. package/dist/components/primitives/defineHeadingSection.d.ts.map +1 -1
  6. package/dist/components/primitives/index.js +1 -1
  7. package/dist/components/sections/Button/index.d.ts.map +1 -1
  8. package/dist/components/sections/Colors/index.d.ts.map +1 -1
  9. package/dist/components/sections/Colors/schema.d.ts.map +1 -1
  10. package/dist/components/sections/Container/index.d.ts.map +1 -1
  11. package/dist/components/sections/DoDontMedia/index.d.ts.map +1 -1
  12. package/dist/components/sections/IconList/IconList.d.ts +2 -1
  13. package/dist/components/sections/IconList/IconList.d.ts.map +1 -1
  14. package/dist/components/sections/IconList/IconListSettings.d.ts +2 -1
  15. package/dist/components/sections/IconList/IconListSettings.d.ts.map +1 -1
  16. package/dist/components/sections/IconList/index.d.ts +1 -0
  17. package/dist/components/sections/IconList/index.d.ts.map +1 -1
  18. package/dist/components/sections/LinkHeading/index.d.ts.map +1 -1
  19. package/dist/components/sections/Media/index.d.ts.map +1 -1
  20. package/dist/components/sections/Prose/index.d.ts.map +1 -1
  21. package/dist/components/sections/Spacer/index.d.ts.map +1 -1
  22. package/dist/components/sections/SubHeading/index.d.ts.map +1 -1
  23. package/dist/components/sections/SubSubHeading/index.d.ts.map +1 -1
  24. package/dist/components/sections/all-sections.d.ts +1 -0
  25. package/dist/components/sections/all-sections.d.ts.map +1 -1
  26. package/dist/components/sections/brand-guide.d.ts +1 -0
  27. package/dist/components/sections/brand-guide.d.ts.map +1 -1
  28. package/dist/components/sections/brand-guide.js +1 -1
  29. package/dist/components/sections/register-schemas.js +11 -941
  30. package/dist/components/sections/register.js +36 -0
  31. package/dist/components/shell/EditorShell.d.ts.map +1 -1
  32. package/dist/index.js +20 -20
  33. package/dist/lib/index.js +2 -2
  34. package/dist/lib/insert-menu.d.ts +19 -0
  35. package/dist/lib/insert-menu.d.ts.map +1 -0
  36. package/dist/lib/registry.d.ts +23 -0
  37. package/dist/lib/registry.d.ts.map +1 -1
  38. package/dist/types/database.d.ts +248 -34
  39. package/dist/types/database.d.ts.map +1 -1
  40. package/package.json +5 -1
  41. package/src/components/primitives/defineHeadingSection.tsx +18 -2
  42. package/src/components/sections/Button/index.tsx +10 -3
  43. package/src/components/sections/Colors/index.tsx +15 -7
  44. package/src/components/sections/Colors/schema.ts +5 -2
  45. package/src/components/sections/Container/index.tsx +19 -3
  46. package/src/components/sections/DoDontMedia/index.tsx +18 -7
  47. package/src/components/sections/IconList/IconList.tsx +43 -19
  48. package/src/components/sections/IconList/IconListSettings.tsx +58 -11
  49. package/src/components/sections/IconList/index.tsx +51 -7
  50. package/src/components/sections/LinkHeading/index.tsx +2 -0
  51. package/src/components/sections/Media/index.tsx +15 -6
  52. package/src/components/sections/Prose/index.tsx +9 -1
  53. package/src/components/sections/Spacer/index.tsx +2 -0
  54. package/src/components/sections/SubHeading/index.tsx +2 -0
  55. package/src/components/sections/SubSubHeading/index.tsx +2 -0
  56. package/src/components/shell/EditorShell.tsx +13 -19
  57. package/src/lib/insert-menu.ts +42 -0
  58. package/src/lib/registry.ts +24 -0
  59. package/src/types/database.ts +236 -36
  60. package/dist/{chunk-ZYXLNJ5M.js → chunk-AFK45HX2.js} +3 -3
@@ -1,958 +1,28 @@
1
1
  import {
2
- brandGuideSectionDefs,
3
- pluralize,
4
- stripHtmlToPlainText,
5
- truncate
6
- } from "../../chunk-VEIDANEU.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";
2
+ allSectionDefs
3
+ } from "../../chunk-4VMAZZXT.js";
4
+ import "../../chunk-KM6HOW4H.js";
5
+ import "../../chunk-2BHPWO7M.js";
19
6
  import "../../chunk-5THS4K5D.js";
20
- import {
21
- EditableRichText,
22
- defineHeadingSection
23
- } from "../../chunk-DQTKWKSK.js";
24
- import {
25
- EditablePlainText,
26
- MAX_CONTAINER_COLUMNS,
27
- MediaBlock,
28
- colSpanClass,
29
- containerGridClass,
30
- spacerHiddenClass
31
- } from "../../chunk-XNGG5AGI.js";
7
+ import "../../chunk-WJ7RZ6CC.js";
8
+ import "../../chunk-XNGG5AGI.js";
32
9
  import "../../chunk-4E66N6UV.js";
33
10
  import "../../chunk-I5737FJY.js";
34
11
  import "../../chunk-VU3XR362.js";
35
- import {
36
- DEFAULT_LINK,
37
- LinkValueSchema
38
- } from "../../chunk-3DCG4DNU.js";
12
+ import "../../chunk-3DCG4DNU.js";
39
13
  import "../../chunk-WDSVHKAH.js";
40
- import {
41
- sanitizeHtml
42
- } from "../../chunk-53DKVAB6.js";
14
+ import "../../chunk-53DKVAB6.js";
43
15
  import "../../chunk-ZGFAYZWB.js";
16
+ import "../../chunk-7EOSDFS3.js";
17
+ import "../../chunk-TZQPOR5A.js";
18
+ import "../../chunk-TEYTLQVH.js";
44
19
  import {
45
- cn
46
- } from "../../chunk-7EOSDFS3.js";
47
- import {
48
- getSectionSchema
49
- } from "../../chunk-TZQPOR5A.js";
50
- import {
51
- SingleMediaReferenceSchema
52
- } from "../../chunk-TEYTLQVH.js";
53
- import {
54
- defineSection,
55
- getSection,
56
20
  registerRichText,
57
21
  registerSchema
58
22
  } from "../../chunk-VKAGMEKE.js";
59
23
  import "../../chunk-NSCT3AMV.js";
60
24
  import "../../chunk-JSBRDJBE.js";
61
25
 
62
- // src/components/sections/LinkHeading/index.tsx
63
- import { Heading1 } from "lucide-react";
64
- import { jsx } from "react/jsx-runtime";
65
- var LinkHeading_default = defineHeadingSection({
66
- type: "link_heading",
67
- label: "Link Heading",
68
- icon: /* @__PURE__ */ jsx(Heading1, { size: 18 }),
69
- navRole: "h1",
70
- tag: "h2",
71
- placeholder: "Section heading",
72
- defaultHeading: "New Section"
73
- });
74
-
75
- // src/components/sections/SubHeading/index.tsx
76
- import { Heading2 } from "lucide-react";
77
- import { jsx as jsx2 } from "react/jsx-runtime";
78
- var SubHeading_default = defineHeadingSection({
79
- type: "sub_heading",
80
- label: "Sub Heading",
81
- icon: /* @__PURE__ */ jsx2(Heading2, { size: 18 }),
82
- navRole: "h2",
83
- tag: "h3",
84
- placeholder: "Sub heading",
85
- defaultHeading: "New Sub Heading",
86
- navExclusion: true
87
- });
88
-
89
- // src/components/sections/SubSubHeading/index.tsx
90
- import { Heading3 } from "lucide-react";
91
- import { jsx as jsx3 } from "react/jsx-runtime";
92
- var SubSubHeading_default = defineHeadingSection({
93
- type: "sub_sub_heading",
94
- label: "Sub Sub Heading",
95
- icon: /* @__PURE__ */ jsx3(Heading3, { size: 18 }),
96
- navRole: "h3",
97
- tag: "h4",
98
- placeholder: "Sub sub heading",
99
- defaultHeading: "New Sub Sub Heading",
100
- navExclusion: true
101
- });
102
-
103
- // src/components/sections/Prose/index.tsx
104
- import { z } from "zod";
105
- import { AlignLeft } from "lucide-react";
106
-
107
- // src/components/sections/Prose/Prose.tsx
108
- import { jsx as jsx4 } from "react/jsx-runtime";
109
- function Prose({ body, onChange }) {
110
- if (onChange) {
111
- return /* @__PURE__ */ jsx4("div", { className: "prose-content", children: /* @__PURE__ */ jsx4(
112
- EditableRichText,
113
- {
114
- value: body,
115
- onChange: (html) => onChange({ type: "prose", content: { body: html } }),
116
- isEditMode: true,
117
- preset: "rich",
118
- placeholder: "Start writing..."
119
- }
120
- ) });
121
- }
122
- return /* @__PURE__ */ jsx4(
123
- "div",
124
- {
125
- className: "prose-content",
126
- dangerouslySetInnerHTML: { __html: sanitizeHtml(body) }
127
- }
128
- );
129
- }
130
-
131
- // src/components/sections/Prose/index.tsx
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() })
136
- });
137
- var Prose_default = defineSection({
138
- type: "prose",
139
- label: "Prose",
140
- icon: /* @__PURE__ */ jsx5(AlignLeft, { size: 18 }),
141
- schema,
142
- richTextFields: ["body"],
143
- component: ({ content, onChange }) => /* @__PURE__ */ jsx5(Prose, { body: content.content.body, onChange: onChange ? (c) => onChange(c) : void 0 }),
144
- defaults: () => ({ type: "prose", content: { body: "<p></p>" } }),
145
- getLabel: (content) => truncate(stripHtmlToPlainText(content.content.body), 60)
146
- });
147
-
148
- // src/components/sections/Media/index.tsx
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({
155
- ref: SingleMediaReferenceSchema,
156
- link: LinkValueSchema.optional()
157
- }),
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()
163
- }).default({})
164
- });
165
- var Media_default = defineSection({
166
- type: "media",
167
- label: "Media",
168
- icon: /* @__PURE__ */ jsx6(ImageIcon, { size: 18 }),
169
- schema: schema2,
170
- component: ({ content, options, onChange }) => /* @__PURE__ */ jsx6(
171
- MediaBlock,
172
- {
173
- refValue: content.content.ref,
174
- options: options ?? {},
175
- link: content.content.link,
176
- isEditMode: !!onChange,
177
- onRefChange: onChange ? (ref) => onChange({ ...content, content: { ...content.content, ref } }) : void 0
178
- }
179
- ),
180
- defaults: () => ({
181
- type: "media",
182
- content: { ref: { type: "image", imageId: "" } },
183
- options: {}
184
- }),
185
- getLabel: () => "Media",
186
- getThumbnails: (content) => content.content.ref.imageId ? [{ type: "image", src: content.content.ref.imageId }] : [],
187
- settings: {
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" }
198
- ]
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
- });
208
-
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,
224
- {
225
- tag: "span",
226
- value: text,
227
- onChange: (newText) => onChange({ type: "button", content: { text: newText, link, download } }),
228
- isEditMode: true,
229
- placeholder: "Button text"
230
- }
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",
239
- {
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
- ]
248
- }
249
- );
250
- }
251
- return /* @__PURE__ */ jsx7("button", { className: cn("cursor-pointer", base, variant), children: text });
252
- }
253
-
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
- })
263
- });
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,
271
- {
272
- text: content.content.text,
273
- link: content.content.link,
274
- download: content.content.download,
275
- onChange: onChange ? (c) => onChange(c) : void 0
276
- }
277
- ),
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
- }
284
- });
285
-
286
- // src/components/sections/Container/index.tsx
287
- import { z as z4 } from "zod";
288
- import { LayoutTemplate } from "lucide-react";
289
-
290
- // src/components/sections/Container/Container.tsx
291
- import "react";
292
-
293
- // src/components/editor/ChildBlockWrapper.tsx
294
- import { forwardRef } from "react";
295
- import { CopyPlus } from "lucide-react";
296
-
297
- // src/components/editor/ColSpanControl.tsx
298
- import { useRef, useState } from "react";
299
- import { Columns3 } from "lucide-react";
300
- import { jsx as jsx9, jsxs as jsxs2 } from "react/jsx-runtime";
301
- function ColSpanControl({ colSpan, columns, onChange }) {
302
- const buttonRef = useRef(null);
303
- const [open, setOpen] = useState(false);
304
- if (columns <= 1) return null;
305
- const current = Math.min(Math.max(colSpan, 1), columns);
306
- return /* @__PURE__ */ jsxs2("div", { className: "relative", children: [
307
- /* @__PURE__ */ jsx9(
308
- "button",
309
- {
310
- ref: buttonRef,
311
- type: "button",
312
- "aria-label": "Column span",
313
- "aria-haspopup": "true",
314
- "aria-expanded": open,
315
- onClick: () => setOpen((v) => !v),
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",
317
- children: /* @__PURE__ */ jsx9(Columns3, { size: 14 })
318
- }
319
- ),
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(
321
- PopoverItem,
322
- {
323
- "aria-current": span === current,
324
- onClick: () => {
325
- onChange(span);
326
- setOpen(false);
327
- },
328
- className: cn(span === current && "font-semibold text-primary"),
329
- children: [
330
- "Span ",
331
- span
332
- ]
333
- }
334
- ) }, span)) }) })
335
- ] });
336
- }
337
-
338
- // src/components/editor/ChildBlockWrapper.tsx
339
- import { jsx as jsx10, jsxs as jsxs3 } from "react/jsx-runtime";
340
- var ChildBlockWrapper = forwardRef(
341
- function ChildBlockWrapper2({
342
- label,
343
- columns,
344
- colSpan,
345
- closestEdge,
346
- isDragging,
347
- hasSettings,
348
- dragHandleRef,
349
- onDelete,
350
- onDuplicate,
351
- onColSpanChange,
352
- onOpenSettings,
353
- children
354
- }, ref) {
355
- return /* @__PURE__ */ jsxs3(
356
- "div",
357
- {
358
- ref,
359
- className: cn(
360
- "group/childblock relative h-full rounded-sm",
361
- isDragging && "opacity-50",
362
- "hover:outline hover:outline-2 hover:outline-primary/30"
363
- ),
364
- children: [
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(
371
- "button",
372
- {
373
- type: "button",
374
- "aria-label": "Duplicate block",
375
- onClick: onDuplicate,
376
- className: "cursor-pointer rounded p-1 text-base-contrast-light hover:text-primary",
377
- children: /* @__PURE__ */ jsx10(CopyPlus, { size: 16 })
378
- }
379
- ) }),
380
- /* @__PURE__ */ jsx10("div", { className: "pointer-events-auto", children: /* @__PURE__ */ jsx10(DeleteButton, { onDelete }) })
381
- ] }),
382
- closestEdge === "left" && /* @__PURE__ */ jsx10(
383
- "div",
384
- {
385
- "data-drop-edge": "left",
386
- className: "absolute bottom-0 left-0 top-0 z-10 w-0.5 -translate-x-1 bg-primary"
387
- }
388
- ),
389
- closestEdge === "right" && /* @__PURE__ */ jsx10(
390
- "div",
391
- {
392
- "data-drop-edge": "right",
393
- className: "absolute bottom-0 right-0 top-0 z-10 w-0.5 translate-x-1 bg-primary"
394
- }
395
- ),
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" }),
398
- children
399
- ]
400
- }
401
- );
402
- }
403
- );
404
-
405
- // src/hooks/useBlockDnd.ts
406
- import { useEffect, useRef as useRef2, useState as useState2 } from "react";
407
- function useBlockDnd({ blockId, containerId, index, isContainer, allowedEdges, enabled }) {
408
- const dragRef = useRef2(null);
409
- const handleRef = useRef2(null);
410
- const [closestEdge, setClosestEdge] = useState2(null);
411
- const [isDragging, setIsDragging] = useState2(false);
412
- useEffect(() => {
413
- const element = dragRef.current;
414
- const handle = handleRef.current;
415
- if (!element || !enabled) return;
416
- let cleanup;
417
- let cancelled = false;
418
- Promise.all([
419
- import("../../adapter-HH47ZPGM.js"),
420
- import("../../closest-edge-EBOXL3YW.js")
421
- ]).then(([{ draggable, dropTargetForElements }, { attachClosestEdge, extractClosestEdge }]) => {
422
- if (cancelled) return;
423
- const cleanupDraggable = draggable({
424
- element,
425
- dragHandle: handle ?? void 0,
426
- getInitialData: () => buildBlockDragData({ blockId, containerId, index, isContainer }),
427
- onGenerateDragPreview: () => {
428
- element.style.opacity = "0.4";
429
- requestAnimationFrame(() => {
430
- element.style.opacity = "";
431
- });
432
- },
433
- onDragStart: () => setIsDragging(true),
434
- onDrop: () => setIsDragging(false)
435
- });
436
- const cleanupDrop = dropTargetForElements({
437
- element,
438
- canDrop: ({ source }) => isBlockDragData(source.data) && canDropBlock(source.data, containerId),
439
- getData: ({ input, element: el }) => attachClosestEdge(buildBlockDropData({ dropContainerId: containerId, index }), {
440
- input,
441
- element: el,
442
- allowedEdges
443
- }),
444
- onDrag: ({ self, source, location }) => {
445
- const innermost = location.current.dropTargets[0]?.element === self.element;
446
- if (!innermost || !isBlockDragData(source.data) || source.data.blockId === blockId) {
447
- setClosestEdge(null);
448
- return;
449
- }
450
- setClosestEdge(extractClosestEdge(self.data));
451
- },
452
- onDragLeave: () => setClosestEdge(null),
453
- onDrop: () => setClosestEdge(null)
454
- });
455
- cleanup = () => {
456
- cleanupDraggable();
457
- cleanupDrop();
458
- };
459
- });
460
- return () => {
461
- cancelled = true;
462
- cleanup?.();
463
- };
464
- }, [blockId, containerId, index, isContainer, enabled, allowedEdges]);
465
- return { dragRef, handleRef, closestEdge, isDragging };
466
- }
467
- function useBlockDropZone({ containerId, index, toColumn, allowedEdges, enabled }) {
468
- const dropRef = useRef2(null);
469
- const [closestEdge, setClosestEdge] = useState2(null);
470
- useEffect(() => {
471
- const element = dropRef.current;
472
- if (!element || !enabled) return;
473
- let cleanup;
474
- let cancelled = false;
475
- Promise.all([
476
- import("../../adapter-HH47ZPGM.js"),
477
- import("../../closest-edge-EBOXL3YW.js")
478
- ]).then(([{ dropTargetForElements }, { attachClosestEdge, extractClosestEdge }]) => {
479
- if (cancelled) return;
480
- cleanup = dropTargetForElements({
481
- element,
482
- canDrop: ({ source }) => isBlockDragData(source.data) && canDropBlock(source.data, containerId),
483
- getData: ({ input, element: el }) => attachClosestEdge(buildBlockDropData({ dropContainerId: containerId, index, toColumn }), {
484
- input,
485
- element: el,
486
- allowedEdges
487
- }),
488
- onDrag: ({ self, source, location }) => {
489
- const innermost = location.current.dropTargets[0]?.element === self.element;
490
- if (!innermost || !isBlockDragData(source.data)) {
491
- setClosestEdge(null);
492
- return;
493
- }
494
- setClosestEdge(extractClosestEdge(self.data));
495
- },
496
- onDragLeave: () => setClosestEdge(null),
497
- onDrop: () => setClosestEdge(null)
498
- });
499
- });
500
- return () => {
501
- cancelled = true;
502
- cleanup?.();
503
- };
504
- }, [containerId, index, toColumn, enabled, allowedEdges]);
505
- return { dropRef, closestEdge };
506
- }
507
-
508
- // src/lib/container-ops.ts
509
- function getContainerChildren(block) {
510
- const content = block.content;
511
- return Array.isArray(content?.children) ? content.children : [];
512
- }
513
- function withChildren(container, children) {
514
- return { ...container, content: { ...container.content, children } };
515
- }
516
- function containerColumns(container) {
517
- const n = container.content?.columns;
518
- return typeof n === "number" && n >= 1 ? n : 1;
519
- }
520
- function removeChildAt(container, index) {
521
- const children = getContainerChildren(container);
522
- if (index < 0 || index >= children.length) return { container, removed: null };
523
- const next = [...children];
524
- const [removed] = next.splice(index, 1);
525
- return { container: withChildren(container, next), removed };
526
- }
527
- function duplicateChildAt(container, index, newId2) {
528
- const children = getContainerChildren(container);
529
- if (index < 0 || index >= children.length) return container;
530
- const clone = { ...JSON.parse(JSON.stringify(children[index])), id: newId2() };
531
- const next = [...children];
532
- next.splice(index + 1, 0, clone);
533
- return withChildren(container, next);
534
- }
535
- function setChildColSpan(container, index, span) {
536
- const children = getContainerChildren(container);
537
- if (index < 0 || index >= children.length) return container;
538
- const clamped = Math.max(1, Math.min(span, containerColumns(container)));
539
- const next = children.map((child, i) => {
540
- if (i !== index) return child;
541
- if (clamped <= 1) {
542
- const { layout: _drop, ...rest } = child;
543
- return rest;
544
- }
545
- return { ...child, layout: { ...child.layout, colSpan: clamped } };
546
- });
547
- return withChildren(container, next);
548
- }
549
- function effectiveSpan(child, columns) {
550
- return isSpacer(child) ? 1 : Math.min(child.layout?.colSpan ?? 1, columns);
551
- }
552
- function isSpacer(child) {
553
- return child.type === "spacer";
554
- }
555
- function occupiedColumns(children, columns) {
556
- return children.reduce((sum, c) => sum + effectiveSpan(c, columns), 0);
557
- }
558
- function trimTrailingSpacers(container) {
559
- const children = getContainerChildren(container);
560
- let end = children.length;
561
- while (end > 0 && isSpacer(children[end - 1])) end--;
562
- if (end === children.length) return container;
563
- return withChildren(container, children.slice(0, end));
564
- }
565
-
566
- // src/components/sections/Container/Container.tsx
567
- import { jsx as jsx11, jsxs as jsxs4 } from "react/jsx-runtime";
568
- var ROW_EDGES = ["left", "right"];
569
- var COL_EDGES = ["top", "bottom"];
570
- function ContainerChild({
571
- containerId,
572
- index,
573
- child,
574
- columns,
575
- flow,
576
- cellClass,
577
- label,
578
- hasSettings,
579
- mergedOptions,
580
- isEditMode,
581
- ChildComp,
582
- onDelete,
583
- onDuplicate,
584
- onColSpanChange,
585
- onOpenSettings,
586
- onChildContentChange,
587
- openModal
588
- }) {
589
- const isContainer = Array.isArray(child.content?.children);
590
- const { dragRef, handleRef, closestEdge, isDragging } = useBlockDnd({
591
- blockId: child.id ?? `child-${index}`,
592
- containerId,
593
- index,
594
- isContainer,
595
- allowedEdges: flow === "column" ? COL_EDGES : ROW_EDGES,
596
- enabled: true
597
- });
598
- return /* @__PURE__ */ jsx11(
599
- "div",
600
- {
601
- ref: dragRef,
602
- className: cellClass,
603
- "data-child-index": index,
604
- "data-child-id": child.id,
605
- children: /* @__PURE__ */ jsx11(
606
- ChildBlockWrapper,
607
- {
608
- label,
609
- columns,
610
- colSpan: Math.min(child.layout?.colSpan ?? 1, columns),
611
- closestEdge,
612
- isDragging,
613
- hasSettings,
614
- dragHandleRef: handleRef,
615
- onDelete,
616
- onDuplicate,
617
- onColSpanChange,
618
- onOpenSettings,
619
- children: /* @__PURE__ */ jsx11(
620
- ChildComp,
621
- {
622
- content: child,
623
- options: mergedOptions,
624
- isEditMode,
625
- onChange: onChildContentChange,
626
- openModal
627
- }
628
- )
629
- }
630
- )
631
- }
632
- );
633
- }
634
- function EmptyColumnCell({
635
- containerId,
636
- targetColumn,
637
- testId
638
- }) {
639
- const { dropRef, closestEdge } = useBlockDropZone({
640
- containerId,
641
- index: 0,
642
- toColumn: targetColumn,
643
- allowedEdges: ROW_EDGES,
644
- enabled: true
645
- });
646
- const active = closestEdge !== null;
647
- return /* @__PURE__ */ jsx11(
648
- "div",
649
- {
650
- ref: dropRef,
651
- "data-empty-dropzone": true,
652
- "data-testid": testId,
653
- "data-target-column": targetColumn,
654
- className: cn(
655
- "flex min-h-20 items-center justify-center rounded-md border-2 border-dashed text-xs text-base-contrast-light/50",
656
- active ? "border-primary" : "border-base-200"
657
- ),
658
- children: "Empty"
659
- }
660
- );
661
- }
662
- function newId() {
663
- return typeof crypto !== "undefined" && typeof crypto.randomUUID === "function" ? crypto.randomUUID() : `child-${Math.random().toString(36).slice(2)}-${Date.now()}`;
664
- }
665
- function Container({ content, isEditMode, onChange, openModal }) {
666
- const { columns, flow, childDefaults, children } = content.content;
667
- const editable = isEditMode && !!onChange;
668
- const container = {
669
- id: content.id ?? "container",
670
- type: "container",
671
- content: content.content
672
- };
673
- const emit = (nextSection) => onChange?.({ ...content, content: nextSection.content });
674
- const updateChild = (index, childContent) => {
675
- const next = children.map((c, i) => i === index ? childContent : c);
676
- onChange?.({ ...content, content: { ...content.content, children: next } });
677
- };
678
- const gridClass = cn(
679
- "grid",
680
- "gap-8",
681
- containerGridClass[columns] ?? "grid-cols-1",
682
- flow === "column" && "grid-flow-col"
683
- );
684
- if (!editable) {
685
- return /* @__PURE__ */ jsx11("div", { className: "@container", children: /* @__PURE__ */ jsx11("div", { className: gridClass, children: children.map((child, index) => {
686
- const def = getSection(child.type);
687
- if (!def) return null;
688
- const Child = def.component;
689
- const span = child.layout?.colSpan ?? 1;
690
- const childOptions = "options" in child ? child.options : void 0;
691
- const mergedOptions = { ...childDefaults ?? {}, ...childOptions ?? {} };
692
- const cellClass = cn(
693
- colSpanClass[columns]?.[Math.min(span, columns)] || void 0,
694
- child.type === "spacer" && spacerHiddenClass[columns]
695
- );
696
- return /* @__PURE__ */ jsx11("div", { className: cellClass, children: /* @__PURE__ */ jsx11(Child, { content: child, options: mergedOptions, isEditMode }) }, child.id ?? index);
697
- }) }) });
698
- }
699
- function columnStartOf(index) {
700
- let c = 0;
701
- for (let i = 0; i < index; i++) {
702
- c += isSpacer(children[i]) ? 1 : Math.min(children[i].layout?.colSpan ?? 1, columns);
703
- }
704
- return c + 1;
705
- }
706
- function renderEditChild(child, index) {
707
- const def = getSection(child.type);
708
- if (!def) return null;
709
- if (isSpacer(child)) {
710
- return /* @__PURE__ */ jsx11(
711
- EmptyColumnCell,
712
- {
713
- containerId: container.id,
714
- targetColumn: columnStartOf(index),
715
- testId: "spacer-cell"
716
- },
717
- child.id ?? index
718
- );
719
- }
720
- const span = child.layout?.colSpan ?? 1;
721
- const childOptions = "options" in child ? child.options : void 0;
722
- const mergedOptions = { ...childDefaults ?? {}, ...childOptions ?? {} };
723
- const cellClass = colSpanClass[columns]?.[Math.min(span, columns)] || void 0;
724
- const hasSettings = !!(def.settings || def.settingsForm) && !!openModal;
725
- const onOpenSettings = hasSettings && openModal ? () => {
726
- const childOpts = childOptions ?? {};
727
- const childContent = child.content ?? {};
728
- const seed = { ...childContent, ...childOpts };
729
- const applyResult = (result) => updateChild(index, {
730
- ...child,
731
- content: { ...child.content, ...result.content },
732
- options: { ...childOpts, ...result.options }
733
- });
734
- if (def.settingsForm) {
735
- const CustomForm = def.settingsForm;
736
- openModal(
737
- `${def.label} Settings`,
738
- /* @__PURE__ */ jsx11(CustomForm, { ...seed, onChange: applyResult })
739
- );
740
- } else if (def.settings) {
741
- openModal(
742
- `${def.label} Settings`,
743
- /* @__PURE__ */ jsx11(
744
- SettingsForm,
745
- {
746
- schema: def.settings,
747
- values: seed,
748
- onChange: applyResult,
749
- tabs: def.settingsTabs
750
- }
751
- )
752
- );
753
- }
754
- } : void 0;
755
- return /* @__PURE__ */ jsx11(
756
- ContainerChild,
757
- {
758
- containerId: container.id,
759
- index,
760
- child,
761
- columns,
762
- flow,
763
- cellClass,
764
- label: def.label,
765
- hasSettings,
766
- mergedOptions,
767
- isEditMode,
768
- ChildComp: def.component,
769
- onDelete: () => emit(trimTrailingSpacers(removeChildAt(container, index).container)),
770
- onDuplicate: () => emit(duplicateChildAt(container, index, newId)),
771
- onColSpanChange: (s) => emit(setChildColSpan(container, index, s)),
772
- onOpenSettings,
773
- onChildContentChange: (c) => updateChild(index, c),
774
- openModal
775
- },
776
- child.id ?? index
777
- );
778
- }
779
- const occupied = occupiedColumns(children, columns);
780
- const emptyCols = occupied < columns ? columns - occupied : 0;
781
- return /* @__PURE__ */ jsx11("div", { className: "@container", children: /* @__PURE__ */ jsxs4("div", { className: gridClass, children: [
782
- children.map(renderEditChild),
783
- Array.from({ length: emptyCols }, (_, i) => /* @__PURE__ */ jsx11(
784
- EmptyColumnCell,
785
- {
786
- containerId: container.id,
787
- targetColumn: occupied + i + 1,
788
- testId: "ghost-cell"
789
- },
790
- `ghost-${i}`
791
- ))
792
- ] }) });
793
- }
794
-
795
- // src/components/sections/Container/ContainerSettingsForm.tsx
796
- import { useState as useState3 } from "react";
797
- import { jsx as jsx12, jsxs as jsxs5 } from "react/jsx-runtime";
798
- function inheritableSchema(children) {
799
- const out = {};
800
- for (const child of children) {
801
- const def = getSection(child.type);
802
- if (!def?.settings || !def.inheritableSettings) continue;
803
- for (const key of def.inheritableSettings) {
804
- const field = def.settings[key];
805
- if (field && !out[key]) out[key] = field;
806
- }
807
- }
808
- return out;
809
- }
810
- function ContainerSettingsForm({
811
- columns = 1,
812
- flow = "row",
813
- childDefaults = {},
814
- children = [],
815
- onChange
816
- }) {
817
- const [layout, setLayout] = useState3({ columns, flow });
818
- const [defaults, setDefaults] = useState3(childDefaults);
819
- const childSchema = inheritableSchema(children);
820
- const hasChildDefaults = Object.keys(childSchema).length > 0;
821
- function emit(nextLayout, nextDefaults) {
822
- onChange({ content: { ...nextLayout, childDefaults: nextDefaults }, options: {} });
823
- }
824
- const layoutSchema = {
825
- columns: {
826
- type: "select",
827
- label: "Columns",
828
- default: String(layout.columns),
829
- target: "content",
830
- coerce: "number",
831
- options: Array.from({ length: MAX_CONTAINER_COLUMNS }, (_, i) => ({
832
- label: String(i + 1),
833
- value: String(i + 1)
834
- }))
835
- },
836
- flow: {
837
- type: "select",
838
- label: "Flow",
839
- default: layout.flow,
840
- target: "content",
841
- options: [
842
- { label: "Rows (wrap across)", value: "row" },
843
- { label: "Columns (fill down)", value: "column" }
844
- ]
845
- }
846
- };
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(
851
- SettingsForm,
852
- {
853
- schema: layoutSchema,
854
- values: { columns: String(layout.columns), flow: layout.flow },
855
- onChange: (result) => {
856
- const next = {
857
- columns: Number(result.content.columns ?? layout.columns),
858
- flow: String(result.content.flow ?? layout.flow)
859
- };
860
- setLayout(next);
861
- emit(next, defaults);
862
- }
863
- }
864
- )
865
- ] }),
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(
869
- SettingsForm,
870
- {
871
- schema: childSchema,
872
- values: defaults,
873
- onChange: (result) => {
874
- const next = { ...defaults, ...result.options };
875
- setDefaults(next);
876
- emit(layout, next);
877
- }
878
- }
879
- )
880
- ] })
881
- ] });
882
- }
883
-
884
- // src/components/sections/Container/index.tsx
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(),
892
- // Recursive: children are full blocks. z.lazy defers evaluation to parse
893
- // time, by which point every section schema (incl. container) is registered.
894
- children: z4.array(z4.lazy(() => getSectionSchema())).default([])
895
- }).transform((c) => ({
896
- ...c,
897
- children: c.children.map(
898
- (child) => child.layout?.colSpan && child.layout.colSpan > c.columns ? { ...child, layout: { ...child.layout, colSpan: c.columns } } : child
899
- )
900
- }))
901
- });
902
- var Container_default = defineSection({
903
- type: "container",
904
- label: "Container",
905
- icon: /* @__PURE__ */ jsx13(LayoutTemplate, { size: 18 }),
906
- schema: schema4,
907
- component: ({ content, onChange, isEditMode, openModal }) => /* @__PURE__ */ jsx13(Container, { content, isEditMode: !!isEditMode, onChange, openModal }),
908
- defaults: () => ({
909
- type: "container",
910
- content: { columns: 1, flow: "row", children: [] }
911
- }),
912
- getLabel: (content) => `Container (${pluralize(content.content.children.length, "item")})`,
913
- settingsForm: ContainerSettingsForm
914
- });
915
-
916
- // src/components/sections/Spacer/index.tsx
917
- import { z as z5 } from "zod";
918
- import { SquareDashed } from "lucide-react";
919
-
920
- // src/components/sections/Spacer/Spacer.tsx
921
- import { jsx as jsx14 } from "react/jsx-runtime";
922
- function Spacer() {
923
- return /* @__PURE__ */ jsx14("div", { "data-spacer": true, "aria-hidden": "true", className: "h-full" });
924
- }
925
-
926
- // src/components/sections/Spacer/index.tsx
927
- import { jsx as jsx15 } from "react/jsx-runtime";
928
- var schema5 = z5.object({
929
- type: z5.literal("spacer"),
930
- content: z5.object({}).default({})
931
- });
932
- var Spacer_default = defineSection({
933
- type: "spacer",
934
- label: "Spacer",
935
- icon: /* @__PURE__ */ jsx15(SquareDashed, { size: 18 }),
936
- schema: schema5,
937
- component: () => /* @__PURE__ */ jsx15(Spacer, {}),
938
- defaults: () => ({ type: "spacer", content: {} })
939
- });
940
-
941
- // src/components/sections/core-sections.ts
942
- var coreSectionDefs = [
943
- LinkHeading_default,
944
- SubHeading_default,
945
- SubSubHeading_default,
946
- Prose_default,
947
- Media_default,
948
- Button_default,
949
- Container_default,
950
- Spacer_default
951
- ];
952
-
953
- // src/components/sections/all-sections.ts
954
- var allSectionDefs = [...coreSectionDefs, ...brandGuideSectionDefs];
955
-
956
26
  // src/components/sections/register-schemas.ts
957
27
  var _ensured = false;
958
28
  function ensureSchemasRegistered() {