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