@drawnagency/primitives 0.1.56 → 0.1.58

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 (165) hide show
  1. package/dist/adapter-HH47ZPGM.js +1779 -0
  2. package/dist/auth/index.js +1 -0
  3. package/dist/{chunk-EU6NZ4GS.js → chunk-AN62WPW7.js} +22 -163
  4. package/dist/chunk-ESE5UBQI.js +73 -0
  5. package/dist/{chunk-KGYWQDBB.js → chunk-ICLXLWQ5.js} +9 -72
  6. package/dist/chunk-JSBRDJBE.js +30 -0
  7. package/dist/chunk-NSCT3AMV.js +32 -0
  8. package/dist/chunk-RFZNNCAS.js +160 -0
  9. package/dist/chunk-TG43X7JO.js +123 -0
  10. package/dist/{chunk-7IAWF7LE.js → chunk-V7JN2DDU.js} +2 -19
  11. package/dist/chunk-VKAGMEKE.js +90 -0
  12. package/dist/chunk-ZU2MKPTG.js +29 -0
  13. package/dist/closest-edge-EBOXL3YW.js +72 -0
  14. package/dist/components/editor/ChildBlockWrapper.d.ts +19 -0
  15. package/dist/components/editor/ChildBlockWrapper.d.ts.map +1 -0
  16. package/dist/components/editor/ColSpanControl.d.ts +9 -0
  17. package/dist/components/editor/ColSpanControl.d.ts.map +1 -0
  18. package/dist/components/editor/SectionWrapper.d.ts +1 -1
  19. package/dist/components/editor/SectionWrapper.d.ts.map +1 -1
  20. package/dist/components/editor/SettingsForm.d.ts +5 -1
  21. package/dist/components/editor/SettingsForm.d.ts.map +1 -1
  22. package/dist/components/primitives/EditableGrid.d.ts.map +1 -1
  23. package/dist/components/primitives/IconPicker.d.ts +7 -1
  24. package/dist/components/primitives/IconPicker.d.ts.map +1 -1
  25. package/dist/components/sections/Container/Container.d.ts +20 -0
  26. package/dist/components/sections/Container/Container.d.ts.map +1 -0
  27. package/dist/components/sections/Container/ContainerSettingsForm.d.ts +17 -0
  28. package/dist/components/sections/Container/ContainerSettingsForm.d.ts.map +1 -0
  29. package/dist/components/sections/Container/index.d.ts +11 -0
  30. package/dist/components/sections/Container/index.d.ts.map +1 -0
  31. package/dist/components/sections/IconList/IconList.d.ts +1 -0
  32. package/dist/components/sections/IconList/IconList.d.ts.map +1 -1
  33. package/dist/components/sections/IconList/IconListSettings.d.ts +3 -4
  34. package/dist/components/sections/IconList/IconListSettings.d.ts.map +1 -1
  35. package/dist/components/sections/IconList/index.d.ts +1 -0
  36. package/dist/components/sections/IconList/index.d.ts.map +1 -1
  37. package/dist/components/sections/Media/MediaBlock.d.ts +19 -0
  38. package/dist/components/sections/Media/MediaBlock.d.ts.map +1 -0
  39. package/dist/components/sections/{MediaGrid → Media}/index.d.ts +15 -25
  40. package/dist/components/sections/Media/index.d.ts.map +1 -0
  41. package/dist/components/sections/Prose/index.d.ts.map +1 -1
  42. package/dist/components/sections/Spacer/Spacer.d.ts +2 -0
  43. package/dist/components/sections/Spacer/Spacer.d.ts.map +1 -0
  44. package/dist/components/sections/Spacer/index.d.ts +6 -0
  45. package/dist/components/sections/Spacer/index.d.ts.map +1 -0
  46. package/dist/components/sections/all-sections.d.ts +29 -103
  47. package/dist/components/sections/all-sections.d.ts.map +1 -1
  48. package/dist/components/sections/register-schemas.d.ts.map +1 -1
  49. package/dist/components/sections/register-schemas.js +4094 -0
  50. package/dist/components/shared/Tabs.d.ts +24 -0
  51. package/dist/components/shared/Tabs.d.ts.map +1 -0
  52. package/dist/components/shell/EditorShell.d.ts.map +1 -1
  53. package/dist/components/shell/SiteSettingsModal.d.ts.map +1 -1
  54. package/dist/components/shell/blockMoveDispatch.d.ts +21 -0
  55. package/dist/components/shell/blockMoveDispatch.d.ts.map +1 -0
  56. package/dist/hooks/useBlockDnd.d.ts +48 -0
  57. package/dist/hooks/useBlockDnd.d.ts.map +1 -0
  58. package/dist/index.js +69 -56
  59. package/dist/lib/block-dnd.d.ts +42 -0
  60. package/dist/lib/block-dnd.d.ts.map +1 -0
  61. package/dist/lib/block-move.d.ts +31 -0
  62. package/dist/lib/block-move.d.ts.map +1 -0
  63. package/dist/lib/container-grid.d.ts +29 -0
  64. package/dist/lib/container-grid.d.ts.map +1 -0
  65. package/dist/lib/container-ops.d.ts +44 -0
  66. package/dist/lib/container-ops.d.ts.map +1 -0
  67. package/dist/lib/dexie.d.ts.map +1 -1
  68. package/dist/lib/dexie.js +15 -0
  69. package/dist/lib/env.js +1 -0
  70. package/dist/lib/index.js +19 -13
  71. package/dist/lib/loader.d.ts.map +1 -1
  72. package/dist/lib/migrate-sections-transform.d.ts +12 -0
  73. package/dist/lib/migrate-sections-transform.d.ts.map +1 -0
  74. package/dist/lib/migrate-sections-transform.js +7 -0
  75. package/dist/lib/registry.d.ts +39 -0
  76. package/dist/lib/registry.d.ts.map +1 -1
  77. package/dist/lib/registry.js +27 -0
  78. package/dist/media/index.js +1 -0
  79. package/dist/schemas/auth.js +1 -0
  80. package/dist/schemas/block.d.ts +20 -0
  81. package/dist/schemas/block.d.ts.map +1 -0
  82. package/dist/schemas/block.js +15 -0
  83. package/dist/schemas/index.js +13 -4
  84. package/dist/schemas/link.d.ts +7 -0
  85. package/dist/schemas/link.d.ts.map +1 -1
  86. package/dist/schemas/rich-text.d.ts +9 -0
  87. package/dist/schemas/rich-text.d.ts.map +1 -0
  88. package/dist/schemas/sections.d.ts +2 -0
  89. package/dist/schemas/sections.d.ts.map +1 -1
  90. package/dist/schemas/shared.d.ts +30 -0
  91. package/dist/schemas/shared.d.ts.map +1 -1
  92. package/dist/types/database.js +2 -0
  93. package/package.json +17 -1
  94. package/src/components/brandguide/Colors.tsx +35 -33
  95. package/src/components/editor/ChildBlockWrapper.tsx +108 -0
  96. package/src/components/editor/ColSpanControl.tsx +56 -0
  97. package/src/components/editor/SectionWrapper.tsx +44 -20
  98. package/src/components/editor/SettingsForm.tsx +100 -73
  99. package/src/components/primitives/EditableGrid.tsx +40 -36
  100. package/src/components/primitives/IconPicker.tsx +116 -26
  101. package/src/components/sections/Container/Container.tsx +354 -0
  102. package/src/components/sections/Container/ContainerSettingsForm.tsx +113 -0
  103. package/src/components/sections/Container/index.tsx +51 -0
  104. package/src/components/sections/IconList/IconList.tsx +113 -43
  105. package/src/components/sections/IconList/IconListSettings.tsx +2 -2
  106. package/src/components/sections/IconList/index.tsx +1 -1
  107. package/src/components/sections/Media/MediaBlock.tsx +103 -0
  108. package/src/components/sections/Media/index.tsx +85 -0
  109. package/src/components/sections/Prose/index.tsx +1 -0
  110. package/src/components/sections/Spacer/Spacer.tsx +6 -0
  111. package/src/components/sections/Spacer/index.tsx +18 -0
  112. package/src/components/sections/all-sections.ts +10 -8
  113. package/src/components/sections/register-schemas.ts +5 -2
  114. package/src/components/shared/Tabs.tsx +63 -0
  115. package/src/components/shell/EditorShell.tsx +105 -13
  116. package/src/components/shell/SiteSettingsModal.tsx +41 -51
  117. package/src/components/shell/blockMoveDispatch.ts +40 -0
  118. package/src/hooks/useBlockDnd.ts +144 -0
  119. package/src/lib/block-dnd.ts +58 -0
  120. package/src/lib/block-move.ts +236 -0
  121. package/src/lib/container-grid.ts +58 -0
  122. package/src/lib/container-ops.ts +159 -0
  123. package/src/lib/dexie.ts +22 -0
  124. package/src/lib/loader.ts +16 -4
  125. package/src/lib/migrate-sections-transform.ts +147 -0
  126. package/src/lib/registry.ts +48 -0
  127. package/src/schemas/block.ts +40 -0
  128. package/src/schemas/link.ts +19 -1
  129. package/src/schemas/rich-text.ts +11 -0
  130. package/src/schemas/sections.ts +5 -1
  131. package/src/schemas/shared.ts +6 -0
  132. package/dist/components/brandguide/DoDontList.d.ts +0 -16
  133. package/dist/components/brandguide/DoDontList.d.ts.map +0 -1
  134. package/dist/components/brandguide/DoDontMediaGrid.d.ts +0 -16
  135. package/dist/components/brandguide/DoDontMediaGrid.d.ts.map +0 -1
  136. package/dist/components/primitives/MediaSettingsForms.d.ts +0 -23
  137. package/dist/components/primitives/MediaSettingsForms.d.ts.map +0 -1
  138. package/dist/components/sections/DoDontList/index.d.ts +0 -21
  139. package/dist/components/sections/DoDontList/index.d.ts.map +0 -1
  140. package/dist/components/sections/DoDontMediaGrid/index.d.ts +0 -55
  141. package/dist/components/sections/DoDontMediaGrid/index.d.ts.map +0 -1
  142. package/dist/components/sections/MediaGrid/MediaGrid.d.ts +0 -17
  143. package/dist/components/sections/MediaGrid/MediaGrid.d.ts.map +0 -1
  144. package/dist/components/sections/MediaGrid/index.d.ts.map +0 -1
  145. package/dist/components/sections/SplitContent/SplitContent.d.ts +0 -14
  146. package/dist/components/sections/SplitContent/SplitContent.d.ts.map +0 -1
  147. package/dist/components/sections/SplitContent/index.d.ts +0 -13
  148. package/dist/components/sections/SplitContent/index.d.ts.map +0 -1
  149. package/src/components/brandguide/DoDontList.d.ts.map +0 -1
  150. package/src/components/brandguide/DoDontList.tsx +0 -67
  151. package/src/components/brandguide/DoDontMediaGrid.d.ts.map +0 -1
  152. package/src/components/brandguide/DoDontMediaGrid.tsx +0 -19
  153. package/src/components/primitives/MediaSettingsForms.tsx +0 -128
  154. package/src/components/sections/DoDontList/index.d.ts.map +0 -1
  155. package/src/components/sections/DoDontList/index.tsx +0 -45
  156. package/src/components/sections/DoDontMediaGrid/index.d.ts.map +0 -1
  157. package/src/components/sections/DoDontMediaGrid/index.tsx +0 -63
  158. package/src/components/sections/MediaGrid/MediaGrid.d.ts.map +0 -1
  159. package/src/components/sections/MediaGrid/MediaGrid.tsx +0 -239
  160. package/src/components/sections/MediaGrid/index.d.ts.map +0 -1
  161. package/src/components/sections/MediaGrid/index.tsx +0 -57
  162. package/src/components/sections/SplitContent/SplitContent.d.ts.map +0 -1
  163. package/src/components/sections/SplitContent/SplitContent.tsx +0 -84
  164. package/src/components/sections/SplitContent/index.d.ts.map +0 -1
  165. package/src/components/sections/SplitContent/index.tsx +0 -55
@@ -1,128 +0,0 @@
1
- import { useState, useRef } from "react";
2
- import { cn } from "../../lib/cn";
3
- import { Check, X } from "lucide-react";
4
- import { Checkbox } from "../shared/Checkbox";
5
- import { Select } from "../shared/Select";
6
-
7
- export function ImageSettingsForm({
8
- border,
9
- objectFit,
10
- invertFrom: initialInvertFrom,
11
- onChange,
12
- }: {
13
- border?: boolean;
14
- objectFit?: "cover" | "contain";
15
- invertFrom?: string;
16
- onChange: (update: { border?: boolean; objectFit?: "cover" | "contain"; invertFrom?: string }) => void;
17
- }) {
18
- const [itemBorder, setItemBorder] = useState(border);
19
- const [fit, setFit] = useState(objectFit);
20
- const [invertFrom, setInvertFrom] = useState(initialInvertFrom ?? "");
21
-
22
- return (
23
- <div className="space-y-4">
24
- <Select
25
- label="Object fit"
26
- value={fit ?? ""}
27
- onChange={(v) => {
28
- const val = (v || undefined) as "cover" | "contain" | undefined;
29
- setFit(val);
30
- onChange({ border: itemBorder, objectFit: val, invertFrom: invertFrom || undefined });
31
- }}
32
- options={[
33
- { value: "", label: "Default (inherit from grid)" },
34
- { value: "contain", label: "Contain" },
35
- { value: "cover", label: "Crop to fill" },
36
- ]}
37
- />
38
- <Select
39
- label="Invert colors"
40
- value={invertFrom}
41
- onChange={(v) => {
42
- setInvertFrom(v);
43
- onChange({ border: itemBorder, objectFit: fit, invertFrom: v || undefined });
44
- }}
45
- options={[
46
- { value: "", label: "None" },
47
- { value: "light", label: "Invert on light theme" },
48
- { value: "dark", label: "Invert on dark theme" },
49
- ]}
50
- />
51
- <Checkbox
52
- checked={itemBorder ?? false}
53
- onChange={(v) => {
54
- const val = v || undefined;
55
- setItemBorder(val);
56
- onChange({ border: val, objectFit: fit, invertFrom: invertFrom || undefined });
57
- }}
58
- label="Override border settings"
59
- />
60
- </div>
61
- );
62
- }
63
-
64
- export function DoDontImageSettingsForm({
65
- border,
66
- objectFit,
67
- invertFrom,
68
- doDont: initialDoDont,
69
- onChange,
70
- }: {
71
- border?: boolean;
72
- objectFit?: "cover" | "contain";
73
- invertFrom?: string;
74
- doDont: "do" | "dont";
75
- onChange: (update: { border?: boolean; objectFit?: "cover" | "contain"; invertFrom?: string; doDont: "do" | "dont" }) => void;
76
- }) {
77
- const [doDont, setDoDont] = useState<"do" | "dont">(initialDoDont);
78
- const latestRef = useRef<{ border?: boolean; objectFit?: "cover" | "contain"; invertFrom?: string }>({ border, objectFit, invertFrom });
79
-
80
- const handleBaseChange = (updated: { border?: boolean; objectFit?: "cover" | "contain"; invertFrom?: string }) => {
81
- latestRef.current = updated;
82
- onChange({ ...updated, doDont });
83
- };
84
-
85
- return (
86
- <div className="space-y-4">
87
- <ImageSettingsForm
88
- border={border}
89
- objectFit={objectFit}
90
- invertFrom={invertFrom}
91
- onChange={handleBaseChange}
92
- />
93
- <hr className="border-base-200" />
94
- <div className="space-y-2">
95
- <button
96
- className={cn(
97
- "cursor-pointer flex w-full items-center gap-3 rounded-md border px-3 py-2.5 text-sm transition-colors",
98
- doDont === "do"
99
- ? "border-green-600 bg-base-accent text-base-contrast"
100
- : "border-base-200 text-base-contrast-light hover:bg-base-accent",
101
- )}
102
- onClick={() => {
103
- setDoDont("do");
104
- onChange({ ...latestRef.current, doDont: "do" });
105
- }}
106
- >
107
- <Check size={16} className="text-green-600" />
108
- Do
109
- </button>
110
- <button
111
- className={cn(
112
- "cursor-pointer flex w-full items-center gap-3 rounded-md border px-3 py-2.5 text-sm transition-colors",
113
- doDont === "dont"
114
- ? "border-red-600 bg-base-accent text-base-contrast"
115
- : "border-base-200 text-base-contrast-light hover:bg-base-accent",
116
- )}
117
- onClick={() => {
118
- setDoDont("dont");
119
- onChange({ ...latestRef.current, doDont: "dont" });
120
- }}
121
- >
122
- <X size={16} className="text-red-600" />
123
- Don't
124
- </button>
125
- </div>
126
- </div>
127
- );
128
- }
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.tsx"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;AAkBA,wBAwBG"}
@@ -1,45 +0,0 @@
1
- import { defineSection } from "../../../lib/registry";
2
- import { z } from "zod";
3
- import { ListChecks } from "lucide-react";
4
- import DoDontList from "../../brandguide/DoDontList";
5
-
6
- const DoDontItemSchema = z.object({ label: z.string(), text: z.string(), icon: z.string().optional() });
7
-
8
- const schema = z.object({
9
- type: z.literal("do_dont"),
10
- content: z.object({
11
- doItems: z.array(DoDontItemSchema),
12
- dontItems: z.array(DoDontItemSchema),
13
- }),
14
- options: z.object({
15
- showLabel: z.boolean().optional(),
16
- stackText: z.boolean().optional(),
17
- }).optional(),
18
- });
19
-
20
- export default defineSection({
21
- type: "do_dont",
22
- label: "Do / Don't",
23
- icon: <ListChecks size={18} />,
24
- schema,
25
- component: ({ content, options, onChange }) => (
26
- <DoDontList
27
- doItems={content.content.doItems}
28
- dontItems={content.content.dontItems}
29
- showLabel={options?.showLabel as boolean}
30
- stackText={options?.stackText as boolean}
31
- onChange={onChange ? (c) => onChange(c as typeof content) : undefined}
32
- />
33
- ),
34
- defaults: () => ({
35
- type: "do_dont" as const,
36
- content: {
37
- doItems: [{ label: "Do", text: "" }],
38
- dontItems: [{ label: "Don't", text: "" }],
39
- },
40
- }),
41
- settings: {
42
- showLabel: { type: "checkbox", label: "Show labels", default: true },
43
- stackText: { type: "checkbox", label: "Stack label above text", default: false },
44
- },
45
- });
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.tsx"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAeA,wBAqCG"}
@@ -1,63 +0,0 @@
1
- import { defineSection } from "../../../lib/registry";
2
- import { z } from "zod";
3
- import { GalleryVerticalEnd } from "lucide-react";
4
- import { MediaReferenceSchema } from "../../../schemas/shared";
5
- import { MediaGridOptionsSchema } from "../../../schemas/media-grid-options";
6
- import DoDontMediaGrid from "../../brandguide/DoDontMediaGrid";
7
-
8
- const schema = z.object({
9
- type: z.literal("do_dont_grid"),
10
- content: z.object({
11
- columns: z.number().int().min(1).max(5),
12
- media: z.array(MediaReferenceSchema),
13
- }),
14
- options: MediaGridOptionsSchema,
15
- });
16
-
17
- export default defineSection({
18
- type: "do_dont_grid",
19
- label: "Do / Don't Grid",
20
- icon: <GalleryVerticalEnd size={18} />,
21
- schema,
22
- component: ({ content, options, onChange, openModal }) => (
23
- <DoDontMediaGrid
24
- media={content.content.media}
25
- columns={content.content.columns}
26
- square={options?.square as boolean}
27
- border={options?.border as boolean}
28
- crop={options?.crop as boolean}
29
- showCaptions={options?.showCaptions as boolean}
30
- onChange={onChange ? (c) => onChange(c as typeof content) : undefined}
31
- openModal={openModal}
32
- />
33
- ),
34
- defaults: () => ({
35
- type: "do_dont_grid" as const,
36
- options: {},
37
- content: {
38
- columns: 2,
39
- media: [
40
- { type: "doDontImage" as const, imageId: "", doDont: "do" as const },
41
- { type: "doDontImage" as const, imageId: "", doDont: "dont" as const },
42
- ],
43
- },
44
- }),
45
- getLabel: (content) => {
46
- const n = content.content.media.length;
47
- return `${n} item${n === 1 ? "" : "s"}`;
48
- },
49
- getThumbnails: (content) =>
50
- content.content.media
51
- .filter((m) => m.imageId)
52
- .map((m) => ({ type: "image" as const, src: m.imageId })),
53
- settings: {
54
- columns: {
55
- type: "select", label: "Columns", default: "2", target: "content", coerce: "number",
56
- options: [{ label: "1", value: "1" }, { label: "2", value: "2" }, { label: "3", value: "3" }, { label: "4", value: "4" }],
57
- },
58
- border: { type: "checkbox", label: "Show border", default: false },
59
- square: { type: "checkbox", label: "Square aspect ratio", default: false },
60
- crop: { type: "checkbox", label: "Crop to fill", default: false },
61
- showCaptions: { type: "checkbox", label: "Show captions", default: false },
62
- },
63
- });
@@ -1 +0,0 @@
1
- {"version":3,"file":"MediaGrid.d.ts","sourceRoot":"","sources":["MediaGrid.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAOhE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAIvC,UAAU,KAAK;IACb,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAC;IAC7C,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,KAAK,IAAI,CAAC;CACzD;AAED,MAAM,CAAC,OAAO,UAAU,SAAS,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,EAAE,WAAW,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,KAAK,2CAgChI"}
@@ -1,239 +0,0 @@
1
- import type { MediaReference } from "../../../schemas/shared";
2
- import type { SectionContent } from "../../../schemas/sections";
3
- import { cn } from "../../../lib/cn";
4
- import { gridColsClass } from "../../../lib/grid";
5
- import { EditableGrid } from "../../primitives/EditableGrid";
6
- import { ResolvedMedia } from "../../primitives/ResolvedMedia";
7
- import { ImageSettingsForm, DoDontImageSettingsForm } from "../../primitives/MediaSettingsForms";
8
- import { EditablePlainText } from "../../primitives/EditablePlainText";
9
- import type { ReactNode } from "react";
10
- import { Check, X } from "lucide-react";
11
- import { useMediaLibrary } from "../../shell/MediaLibraryContext";
12
- import { ImageDropZone } from "../../primitives/ImageDropZone";
13
-
14
- interface Props {
15
- media: MediaReference[];
16
- columns: number;
17
- square?: boolean;
18
- border?: boolean;
19
- crop?: boolean;
20
- showCaptions?: boolean;
21
- sectionType?: string;
22
- onChange?: (content: SectionContent) => void;
23
- openModal?: (title: string, content: ReactNode) => void;
24
- }
25
-
26
- export default function MediaGrid({ media, columns, square, border, crop, showCaptions, sectionType, onChange, openModal }: Props) {
27
- if (onChange && openModal) {
28
- return (
29
- <MediaGridEditable
30
- media={media}
31
- columns={columns}
32
- square={square}
33
- border={border}
34
- crop={crop}
35
- showCaptions={showCaptions}
36
- sectionType={sectionType}
37
- onChange={onChange}
38
- openModal={openModal}
39
- />
40
- );
41
- }
42
-
43
- return (
44
- <div className={cn("grid gap-4", gridColsClass[columns] || "grid-cols-1")}>
45
- {media.map((item, i) => (
46
- <MediaGridItem
47
- key={i}
48
- item={item}
49
- isEditMode={false}
50
- square={square}
51
- border={border}
52
- crop={crop}
53
- showCaptions={showCaptions}
54
- />
55
- ))}
56
- </div>
57
- );
58
- }
59
-
60
- function MediaGridEditable({ media, columns, square, border, crop, showCaptions, sectionType = "media_grid", onChange, openModal }: Props & { onChange: (content: SectionContent) => void; openModal: (title: string, content: ReactNode) => void }) {
61
- const mediaLibrary = useMediaLibrary();
62
-
63
- const opts = square || border || crop || showCaptions
64
- ? { options: { square, border, crop, showCaptions } }
65
- : {};
66
-
67
- const createItem = (): MediaReference =>
68
- sectionType === "do_dont_grid"
69
- ? { type: "doDontImage", imageId: "", doDont: "do" }
70
- : { type: "image", imageId: "" };
71
-
72
- const handleItemSettings = (index: number) => {
73
- const item = media[index];
74
-
75
- if (item.type === "doDontImage") {
76
- openModal(
77
- "Image Settings",
78
- <DoDontImageSettingsForm
79
- border={item.border}
80
- objectFit={item.objectFit}
81
- invertFrom={item.invertFrom}
82
- doDont={item.doDont}
83
- onChange={(updated) => {
84
- const newMedia = media.map((m, i) => i === index ? { ...m, ...updated } : m);
85
- onChange({ type: sectionType, content: { columns, media: newMedia }, ...opts } as SectionContent);
86
- }}
87
- />,
88
- );
89
- } else {
90
- openModal(
91
- "Image Settings",
92
- <ImageSettingsForm
93
- border={item.border}
94
- objectFit={item.objectFit}
95
- invertFrom={item.invertFrom}
96
- onChange={(updated) => {
97
- const newMedia = media.map((m, i) => i === index ? { ...m, ...updated } : m);
98
- onChange({ type: sectionType, content: { columns, media: newMedia }, ...opts } as SectionContent);
99
- }}
100
- />,
101
- );
102
- }
103
- };
104
-
105
- const setItemImage = (index: number, imageId: string) => {
106
- const newMedia = media.map((m, i) => i === index ? { ...m, imageId } : m);
107
- onChange({ type: sectionType, content: { columns, media: newMedia }, ...opts } as SectionContent);
108
- };
109
-
110
- const handleItemImageClick = (index: number) => {
111
- mediaLibrary?.openSelectModal((imageId) => setItemImage(index, imageId));
112
- };
113
-
114
- return (
115
- <EditableGrid
116
- items={media}
117
- columns={columns}
118
- onChange={(newMedia) =>
119
- onChange({ type: sectionType, content: { columns, media: newMedia as MediaReference[] }, ...opts } as SectionContent)
120
- }
121
- createItem={createItem}
122
- isEditMode={true}
123
- onItemSettings={handleItemSettings}
124
- onItemImageClick={mediaLibrary ? handleItemImageClick : undefined}
125
- chromeTopClass={sectionType === "do_dont_grid" ? "top-[50px]" : undefined}
126
- renderItem={(item, { isEditMode, index }) => (
127
- <MediaGridItem
128
- item={item}
129
- isEditMode={isEditMode}
130
- square={square}
131
- border={border}
132
- crop={crop}
133
- showCaptions={showCaptions}
134
- onImageReplace={mediaLibrary ? (imageId) => setItemImage(index, imageId) : undefined}
135
- onCaptionChange={(caption) => {
136
- const newMedia = media.map((m, i) => i === index ? { ...m, caption: caption || undefined } : m);
137
- onChange({ type: sectionType, content: { columns, media: newMedia }, ...opts } as SectionContent);
138
- }}
139
- />
140
- )}
141
- />
142
- );
143
- }
144
-
145
- function MediaGridItem({
146
- item,
147
- isEditMode,
148
- square,
149
- border,
150
- crop,
151
- showCaptions,
152
- onCaptionChange,
153
- onImageReplace,
154
- }: {
155
- item: MediaReference;
156
- isEditMode: boolean;
157
- square?: boolean;
158
- border?: boolean;
159
- crop?: boolean;
160
- showCaptions?: boolean;
161
- onCaptionChange?: (caption: string) => void;
162
- onImageReplace?: (imageId: string) => void;
163
- }) {
164
- const isDoDont = item.type === "doDontImage";
165
-
166
- const showBorder = item.border ?? border;
167
- const itemFit = item.objectFit ?? (crop ? "cover" : undefined);
168
-
169
- const fitClass = itemFit === "cover" ? "object-cover" : "object-contain";
170
-
171
- const captionStr = item.caption
172
- ? (Array.isArray(item.caption) ? item.caption.join("\n") : item.caption)
173
- : "";
174
-
175
- const itemAny = item as Record<string, unknown>;
176
- const resolvedMedia = (
177
- <ResolvedMedia
178
- imageId={item.imageId || undefined}
179
- src={itemAny.src as string | undefined}
180
- srcset={itemAny.srcset as string | undefined}
181
- alt={itemAny.alt as string | undefined}
182
- className="h-full w-full"
183
- imgClassName={fitClass}
184
- invertFrom={item.invertFrom}
185
- />
186
- );
187
- const media = onImageReplace ? (
188
- <ImageDropZone onImageSelected={onImageReplace} className="h-full w-full">
189
- {resolvedMedia}
190
- </ImageDropZone>
191
- ) : (
192
- resolvedMedia
193
- );
194
-
195
- return (
196
- <figure>
197
- {isDoDont && (
198
- <div className="flex h-10 items-center justify-center pb-1">
199
- {item.doDont === "do"
200
- ? <Check size={28} className="text-green-600" />
201
- : <X size={28} className="text-red-600" />
202
- }
203
- </div>
204
- )}
205
-
206
- <div className={cn(
207
- "overflow-hidden rounded-md",
208
- showBorder && "border border-base-200",
209
- square && "aspect-square",
210
- )}>
211
- {item.type === "linkedImage" && !isEditMode ? (
212
- <a href={item.href} target={item.target} className="group block">
213
- {media}
214
- {item.linkText && (
215
- <span className="mt-2 block text-sm text-primary group-hover:underline">{item.linkText}</span>
216
- )}
217
- </a>
218
- ) : media}
219
- </div>
220
-
221
- {showCaptions && (
222
- <figcaption className="mt-2 min-h-[1em] text-sm text-base-contrast-light">
223
- {isEditMode && onCaptionChange ? (
224
- <EditablePlainText
225
- tag="span"
226
- value={captionStr}
227
- onChange={onCaptionChange}
228
- isEditMode={true}
229
- placeholder="Caption"
230
- className="block min-h-[1em]"
231
- />
232
- ) : (
233
- captionStr || " "
234
- )}
235
- </figcaption>
236
- )}
237
- </figure>
238
- );
239
- }
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.tsx"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAeA,wBA+BG"}
@@ -1,57 +0,0 @@
1
- import { defineSection } from "../../../lib/registry";
2
- import { z } from "zod";
3
- import { LayoutGrid } from "lucide-react";
4
- import { MediaReferenceSchema } from "../../../schemas/shared";
5
- import { MediaGridOptionsSchema } from "../../../schemas/media-grid-options";
6
- import MediaGrid from "./MediaGrid";
7
-
8
- const schema = z.object({
9
- type: z.literal("media_grid"),
10
- content: z.object({
11
- columns: z.number().int().min(1).max(5),
12
- media: z.array(MediaReferenceSchema),
13
- }),
14
- options: MediaGridOptionsSchema,
15
- });
16
-
17
- export default defineSection({
18
- type: "media_grid",
19
- label: "Media Grid",
20
- icon: <LayoutGrid size={18} />,
21
- schema,
22
- component: ({ content, options, onChange, openModal }) => (
23
- <MediaGrid
24
- media={content.content.media}
25
- columns={content.content.columns}
26
- square={options?.square as boolean}
27
- border={options?.border as boolean}
28
- crop={options?.crop as boolean}
29
- showCaptions={options?.showCaptions as boolean}
30
- onChange={onChange ? (c) => onChange(c as typeof content) : undefined}
31
- openModal={openModal}
32
- />
33
- ),
34
- defaults: () => ({
35
- type: "media_grid" as const,
36
- content: { columns: 2, media: [{ type: "image" as const, imageId: "" }] },
37
- options: {},
38
- }),
39
- getLabel: (content) => {
40
- const n = content.content.media.length;
41
- return `${n} image${n === 1 ? "" : "s"}`;
42
- },
43
- getThumbnails: (content) =>
44
- content.content.media
45
- .filter((m) => m.imageId)
46
- .map((m) => ({ type: "image" as const, src: m.imageId })),
47
- settings: {
48
- columns: {
49
- type: "select", label: "Columns", default: "2", target: "content", coerce: "number",
50
- options: [{ label: "1", value: "1" }, { label: "2", value: "2" }, { label: "3", value: "3" }, { label: "4", value: "4" }],
51
- },
52
- border: { type: "checkbox", label: "Show border", default: false },
53
- square: { type: "checkbox", label: "Square aspect ratio", default: false },
54
- crop: { type: "checkbox", label: "Crop to fill", default: false },
55
- showCaptions: { type: "checkbox", label: "Show captions", default: false },
56
- },
57
- });
@@ -1 +0,0 @@
1
- {"version":3,"file":"SplitContent.d.ts","sourceRoot":"","sources":["SplitContent.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAIhE,UAAU,KAAK;IACb,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACjC,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAC;CAC9C;AAED,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,aAAsB,EAAE,QAAQ,EAAE,EAAE,KAAK,2CA2DtG"}
@@ -1,84 +0,0 @@
1
- import { cn } from "../../../lib/cn";
2
- import { sanitizeHtml } from "../../../lib/sanitize";
3
- import { ResolvedMedia } from "../../primitives/ResolvedMedia";
4
- import { EditableRichText } from "../../primitives/EditableRichText";
5
- import { IconButton } from "../../shared/IconButton";
6
- import { ImageDropZone } from "../../primitives/ImageDropZone";
7
- import type { SectionContent } from "../../../schemas/sections";
8
- import { useMediaLibrary } from "../../shell/MediaLibraryContext";
9
- import { ImageIcon } from "lucide-react";
10
-
11
- interface Props {
12
- imageId: string | undefined;
13
- src?: string;
14
- srcset?: string;
15
- alt?: string;
16
- body: string;
17
- border?: boolean;
18
- imagePosition?: "left" | "right";
19
- onChange?: (content: SectionContent) => void;
20
- }
21
-
22
- export default function SplitContent({ imageId, src, srcset, alt, body, border, imagePosition = "left", onChange }: Props) {
23
- const mediaLibrary = useMediaLibrary();
24
-
25
- const handleImageChange = (newImageId: string) => {
26
- onChange?.({
27
- type: "split_content",
28
- content: { imageId: newImageId, body },
29
- options: { border, imagePosition },
30
- });
31
- };
32
-
33
- const handleBodyChange = (html: string) => {
34
- onChange?.({
35
- type: "split_content",
36
- content: { imageId, body: html, src, srcset, alt },
37
- options: { border, imagePosition },
38
- });
39
- };
40
-
41
- const handleImageClick = () => {
42
- mediaLibrary?.openSelectModal(handleImageChange);
43
- };
44
-
45
- return (
46
- <div className={cn(
47
- "flex flex-col gap-6 lg:flex-row lg:items-start",
48
- imagePosition === "right" && "lg:flex-row-reverse",
49
- )}>
50
- <div className={cn("group/img relative flex-shrink-0 lg:w-1/2", border && "overflow-hidden rounded-md border border-base-200")}>
51
- {onChange && mediaLibrary ? (
52
- <ImageDropZone onImageSelected={handleImageChange}>
53
- <ResolvedMedia imageId={imageId} src={src} srcset={srcset} alt={alt} className="w-full" />
54
- </ImageDropZone>
55
- ) : (
56
- <ResolvedMedia imageId={imageId} src={src} srcset={srcset} alt={alt} className="w-full" />
57
- )}
58
- {onChange && mediaLibrary && (
59
- <div className="absolute top-1.5 right-1.5 z-10 opacity-0 transition-opacity group-hover/img:opacity-100">
60
- <IconButton
61
- icon={<ImageIcon size={16} />}
62
- label="Change image"
63
- onClick={handleImageClick}
64
- className="bg-base/80 shadow-sm"
65
- />
66
- </div>
67
- )}
68
- </div>
69
- <div className="prose-content lg:w-1/2">
70
- {onChange ? (
71
- <EditableRichText
72
- value={body}
73
- onChange={handleBodyChange}
74
- isEditMode={true}
75
- preset="rich"
76
- />
77
- ) : (
78
- // eslint-disable-next-line react/no-danger
79
- <div dangerouslySetInnerHTML={{ __html: sanitizeHtml(body) }} />
80
- )}
81
- </div>
82
- </div>
83
- );
84
- }
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.tsx"],"names":[],"mappings":";;;;;;;;;;;AAiBA,wBAkBG"}