@byscott-io/pagelet 0.1.0 → 0.2.0
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/CHANGELOG.md +38 -0
- package/README.md +20 -2
- package/dist/editor.cjs.map +1 -1
- package/dist/editor.js.map +1 -1
- package/dist/index.cjs +387 -40
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +381 -41
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
2
2
|
import { migrate, Render } from '@puckeditor/core/rsc';
|
|
3
|
+
import { useState } from 'react';
|
|
3
4
|
|
|
4
5
|
const DOCUMENT_VERSION = 1;
|
|
5
6
|
function emptyData() {
|
|
@@ -34,6 +35,10 @@ function needsPuckMigration(data) {
|
|
|
34
35
|
const root = data.root ?? {};
|
|
35
36
|
return Object.keys(root).some((key) => key !== "props" && key !== "readOnly");
|
|
36
37
|
}
|
|
38
|
+
function pageSettings(document) {
|
|
39
|
+
const props = upgradeDocument(document)?.data?.root?.props ?? {};
|
|
40
|
+
return { title: props.title || "", background: props.background ?? "site" };
|
|
41
|
+
}
|
|
37
42
|
|
|
38
43
|
function PageletPage({ document, palette, theme }) {
|
|
39
44
|
if (!palette) throw new Error("pagelet: <PageletPage> needs a palette (see createPalette)");
|
|
@@ -41,6 +46,63 @@ function PageletPage({ document, palette, theme }) {
|
|
|
41
46
|
return /* @__PURE__ */ jsx(Render, { config: palette.config, data, metadata: { pageletTheme: theme } });
|
|
42
47
|
}
|
|
43
48
|
|
|
49
|
+
const outline = { fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round" };
|
|
50
|
+
const PATHS = {
|
|
51
|
+
location: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
52
|
+
/* @__PURE__ */ jsx("path", { d: "M12 22s7-6.2 7-12a7 7 0 1 0-14 0c0 5.8 7 12 7 12z" }),
|
|
53
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "10", r: "2.5" })
|
|
54
|
+
] }),
|
|
55
|
+
phone: /* @__PURE__ */ jsx("path", { d: "M6.5 3h3l1.5 4.5-2 1.5a12 12 0 0 0 6 6l1.5-2L21 14.5v3a2 2 0 0 1-2 2A16 16 0 0 1 4.5 5a2 2 0 0 1 2-2z" }),
|
|
56
|
+
email: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
57
|
+
/* @__PURE__ */ jsx("rect", { x: "3", y: "5", width: "18", height: "14", rx: "2" }),
|
|
58
|
+
/* @__PURE__ */ jsx("path", { d: "M3 7l9 6 9-6" })
|
|
59
|
+
] }),
|
|
60
|
+
video: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
61
|
+
/* @__PURE__ */ jsx("rect", { x: "2.5", y: "6", width: "13", height: "12", rx: "2" }),
|
|
62
|
+
/* @__PURE__ */ jsx("path", { d: "M15.5 10.5 21.5 7v10l-6-3.5z" })
|
|
63
|
+
] }),
|
|
64
|
+
calendar: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
65
|
+
/* @__PURE__ */ jsx("rect", { x: "3", y: "5", width: "18", height: "16", rx: "2" }),
|
|
66
|
+
/* @__PURE__ */ jsx("path", { d: "M3 10h18M8 3v4M16 3v4" })
|
|
67
|
+
] }),
|
|
68
|
+
clock: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
69
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "9" }),
|
|
70
|
+
/* @__PURE__ */ jsx("path", { d: "M12 7v5l3 2" })
|
|
71
|
+
] }),
|
|
72
|
+
link: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
73
|
+
/* @__PURE__ */ jsx("path", { d: "M10 14a4 4 0 0 0 5.7 0l3-3a4 4 0 0 0-5.7-5.7l-1 1" }),
|
|
74
|
+
/* @__PURE__ */ jsx("path", { d: "M14 10a4 4 0 0 0-5.7 0l-3 3a4 4 0 0 0 5.7 5.7l1-1" })
|
|
75
|
+
] }),
|
|
76
|
+
facebook: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
77
|
+
/* @__PURE__ */ jsx("rect", { x: "3", y: "3", width: "18", height: "18", rx: "3" }),
|
|
78
|
+
/* @__PURE__ */ jsx("path", { d: "M15.5 8H14a2 2 0 0 0-2 2v11M9.5 13h5" })
|
|
79
|
+
] }),
|
|
80
|
+
youtube: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
81
|
+
/* @__PURE__ */ jsx("rect", { x: "2.5", y: "5.5", width: "19", height: "13", rx: "4" }),
|
|
82
|
+
/* @__PURE__ */ jsx("path", { d: "M10.5 9.5v5l4-2.5z" })
|
|
83
|
+
] })
|
|
84
|
+
};
|
|
85
|
+
const iconNames = Object.keys(PATHS);
|
|
86
|
+
const iconOptions = [{ label: "None", value: "" }].concat(
|
|
87
|
+
iconNames.map((name) => ({ label: name[0].toUpperCase() + name.slice(1), value: name }))
|
|
88
|
+
);
|
|
89
|
+
function Icon({ name, size = "1.1em", style }) {
|
|
90
|
+
const glyph = PATHS[name];
|
|
91
|
+
if (!glyph) return null;
|
|
92
|
+
return /* @__PURE__ */ jsx(
|
|
93
|
+
"svg",
|
|
94
|
+
{
|
|
95
|
+
viewBox: "0 0 24 24",
|
|
96
|
+
width: size,
|
|
97
|
+
height: size,
|
|
98
|
+
"aria-hidden": "true",
|
|
99
|
+
focusable: "false",
|
|
100
|
+
style: { ...outline, verticalAlign: "-0.15em", flexShrink: 0, ...style },
|
|
101
|
+
children: glyph
|
|
102
|
+
}
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
44
106
|
const SAFE_SCHEMES = ["http:", "https:", "mailto:", "tel:"];
|
|
45
107
|
function safeUrl(value) {
|
|
46
108
|
if (typeof value !== "string") return void 0;
|
|
@@ -92,8 +154,10 @@ function tokenVar(token) {
|
|
|
92
154
|
|
|
93
155
|
const alignOptions = [
|
|
94
156
|
{ label: "Left", value: "left" },
|
|
95
|
-
{ label: "Center", value: "center" }
|
|
157
|
+
{ label: "Center", value: "center" },
|
|
158
|
+
{ label: "Right", value: "right" }
|
|
96
159
|
];
|
|
160
|
+
const blockMargin = (align) => align === "center" ? { marginInline: "auto" } : align === "right" ? { marginLeft: "auto" } : {};
|
|
97
161
|
function paragraphs(text) {
|
|
98
162
|
return String(text ?? "").split(/\n\s*\n/).map((paragraph) => paragraph.trim()).filter(Boolean);
|
|
99
163
|
}
|
|
@@ -155,7 +219,7 @@ const Text = {
|
|
|
155
219
|
align: { type: "radio", options: alignOptions }
|
|
156
220
|
},
|
|
157
221
|
defaultProps: { text: "Write something here.", align: "left" },
|
|
158
|
-
render: ({ text, align }) => /* @__PURE__ */ jsx("div", { style: { textAlign: align, maxWidth: "68ch",
|
|
222
|
+
render: ({ text, align }) => /* @__PURE__ */ jsx("div", { style: { textAlign: align, maxWidth: "68ch", ...blockMargin(align) }, children: /* @__PURE__ */ jsx(Paragraphs, { text }) })
|
|
159
223
|
};
|
|
160
224
|
const Button = {
|
|
161
225
|
label: "Button",
|
|
@@ -268,6 +332,79 @@ const Faq = {
|
|
|
268
332
|
/* @__PURE__ */ jsx("div", { style: { paddingTop: "8px" }, children: /* @__PURE__ */ jsx(Paragraphs, { text: item.answer }) })
|
|
269
333
|
] }, index)) })
|
|
270
334
|
};
|
|
335
|
+
const Quote = {
|
|
336
|
+
label: "Quote",
|
|
337
|
+
category: "content",
|
|
338
|
+
fields: {
|
|
339
|
+
text: { type: "textarea" },
|
|
340
|
+
citation: { type: "text", label: "Citation (e.g. Matthew 28:19-20 ESV)" },
|
|
341
|
+
citationAlign: { type: "radio", label: "Citation alignment", options: alignOptions }
|
|
342
|
+
},
|
|
343
|
+
defaultProps: { text: "Quotation", citation: "", citationAlign: "right" },
|
|
344
|
+
render: ({ text, citation, citationAlign }) => /* @__PURE__ */ jsxs("figure", { style: { margin: "0 0 1em", maxWidth: "68ch" }, children: [
|
|
345
|
+
/* @__PURE__ */ jsx("blockquote", { style: { margin: 0, fontStyle: "italic" }, children: /* @__PURE__ */ jsx(Paragraphs, { text, style: { margin: "0 0 0.5em" } }) }),
|
|
346
|
+
citation && /* @__PURE__ */ jsx("figcaption", { style: { textAlign: citationAlign ?? "right" }, children: citation })
|
|
347
|
+
] })
|
|
348
|
+
};
|
|
349
|
+
const IconList = {
|
|
350
|
+
label: "Icon list",
|
|
351
|
+
category: "content",
|
|
352
|
+
fields: {
|
|
353
|
+
items: {
|
|
354
|
+
type: "array",
|
|
355
|
+
getItemSummary: (item) => item.text?.split("\n")[0] || "Item",
|
|
356
|
+
arrayFields: {
|
|
357
|
+
icon: { type: "select", options: iconOptions },
|
|
358
|
+
text: { type: "textarea", label: "Text (line breaks are kept)" },
|
|
359
|
+
href: { type: "text", label: "Link (optional): https://, tel:, mailto:" }
|
|
360
|
+
},
|
|
361
|
+
defaultItemProps: { icon: "link", text: "", href: "" }
|
|
362
|
+
},
|
|
363
|
+
align: { type: "radio", options: alignOptions }
|
|
364
|
+
},
|
|
365
|
+
defaultProps: { items: [{ icon: "phone", text: "(555) 555-0100", href: "tel:+15555550100" }], align: "left" },
|
|
366
|
+
render: ({ items = [], align }) => /* @__PURE__ */ jsx("ul", { style: { listStyle: "none", padding: 0, margin: "0 0 1em", textAlign: align }, children: items.map((item, index) => {
|
|
367
|
+
const href = safeUrl(item.href);
|
|
368
|
+
const content = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
369
|
+
/* @__PURE__ */ jsx(Icon, { name: item.icon, style: { marginRight: "0.4em" } }),
|
|
370
|
+
/* @__PURE__ */ jsx("span", { style: { whiteSpace: "pre-line" }, children: item.text })
|
|
371
|
+
] });
|
|
372
|
+
return /* @__PURE__ */ jsx("li", { style: { margin: "0.35em 0" }, children: href ? /* @__PURE__ */ jsx("a", { href, style: { color: tokenVar("colorAccent") }, children: content }) : content }, index);
|
|
373
|
+
}) })
|
|
374
|
+
};
|
|
375
|
+
const Schedule = {
|
|
376
|
+
label: "Schedule",
|
|
377
|
+
category: "content",
|
|
378
|
+
fields: {
|
|
379
|
+
rows: {
|
|
380
|
+
type: "array",
|
|
381
|
+
getItemSummary: (row) => [row.day, row.time, row.what].filter(Boolean).join(" ") || "Row",
|
|
382
|
+
arrayFields: { day: { type: "text" }, time: { type: "text" }, what: { type: "text" } },
|
|
383
|
+
defaultItemProps: { day: "Sunday", time: "10:00", what: "Worship" }
|
|
384
|
+
},
|
|
385
|
+
align: { type: "radio", options: alignOptions }
|
|
386
|
+
},
|
|
387
|
+
defaultProps: { rows: [{ day: "Sunday", time: "10:00", what: "Worship" }], align: "center" },
|
|
388
|
+
render: ({ rows = [], align }) => /* @__PURE__ */ jsx("table", { style: { borderCollapse: "collapse", margin: "0 0 1em", ...blockMargin(align ?? "center") }, children: /* @__PURE__ */ jsx("tbody", { children: rows.map((row, index) => /* @__PURE__ */ jsxs("tr", { children: [
|
|
389
|
+
/* @__PURE__ */ jsx("td", { style: { padding: "4px 12px", textAlign: "right", fontWeight: 600 }, children: row.day }),
|
|
390
|
+
/* @__PURE__ */ jsx("td", { style: { padding: "4px 12px", textAlign: "right", fontVariantNumeric: "tabular-nums" }, children: row.time }),
|
|
391
|
+
/* @__PURE__ */ jsx("td", { style: { padding: "4px 12px" }, children: row.what })
|
|
392
|
+
] }, index)) }) })
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
function overlayColor(hex, opacityPercent) {
|
|
396
|
+
let value = String(hex ?? "").trim().replace(/^#/, "");
|
|
397
|
+
if (/^[0-9a-f]{3}$/i.test(value)) value = value.split("").map((c) => c + c).join("");
|
|
398
|
+
if (!/^[0-9a-f]{6}$/i.test(value)) value = "ffffff";
|
|
399
|
+
const [r, g, b] = [0, 2, 4].map((i) => parseInt(value.slice(i, i + 2), 16));
|
|
400
|
+
const opacity = Math.min(100, Math.max(0, Number(opacityPercent) || 0)) / 100;
|
|
401
|
+
return `rgba(${r}, ${g}, ${b}, ${opacity})`;
|
|
402
|
+
}
|
|
403
|
+
function cssImage(url) {
|
|
404
|
+
const safe = safeImageUrl(url);
|
|
405
|
+
return safe ? `url("${encodeURI(safe)}")` : void 0;
|
|
406
|
+
}
|
|
407
|
+
const percentField = (label) => ({ type: "number", label, min: 0, max: 100 });
|
|
271
408
|
|
|
272
409
|
const SPACING = { none: "0", small: "16px", medium: "40px", large: "80px" };
|
|
273
410
|
const spacingOptions = Object.keys(SPACING).map((value) => ({ label: value, value }));
|
|
@@ -288,21 +425,27 @@ const Section = {
|
|
|
288
425
|
{ label: "Accent", value: "accent" }
|
|
289
426
|
]
|
|
290
427
|
},
|
|
428
|
+
backgroundImage: { type: "text", label: "Background image URL (optional)" },
|
|
429
|
+
overlayColor: { type: "text", label: "Overlay color over the image (hex, e.g. #ffffff)" },
|
|
430
|
+
overlayOpacity: percentField("Overlay opacity %"),
|
|
291
431
|
padding: { type: "select", options: spacingOptions },
|
|
292
432
|
content: { type: "slot" }
|
|
293
433
|
},
|
|
294
|
-
defaultProps: { background: "none", padding: "medium" },
|
|
295
|
-
render: ({ background, padding, content: Content }) =>
|
|
296
|
-
|
|
297
|
-
{
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
434
|
+
defaultProps: { background: "none", backgroundImage: "", overlayColor: "#ffffff", overlayOpacity: 85, padding: "medium" },
|
|
435
|
+
render: ({ background, backgroundImage, overlayColor: color, overlayOpacity, padding, content: Content }) => {
|
|
436
|
+
const image = cssImage(backgroundImage);
|
|
437
|
+
const inner = /* @__PURE__ */ jsx("div", { style: { maxWidth: tokenVar("maxWidth"), margin: "0 auto", paddingInline: "16px" }, children: /* @__PURE__ */ jsx(Content, {}) });
|
|
438
|
+
return /* @__PURE__ */ jsx(
|
|
439
|
+
"section",
|
|
440
|
+
{
|
|
441
|
+
style: {
|
|
442
|
+
background: image ? `${image} center / cover` : BACKGROUNDS[background] ?? BACKGROUNDS.none,
|
|
443
|
+
color: !image && background === "accent" ? tokenVar("colorAccentText") : void 0
|
|
444
|
+
},
|
|
445
|
+
children: /* @__PURE__ */ jsx("div", { style: { paddingBlock: SPACING[padding] ?? SPACING.medium, background: image ? overlayColor(color, overlayOpacity ?? 85) : void 0 }, children: inner })
|
|
446
|
+
}
|
|
447
|
+
);
|
|
448
|
+
}
|
|
306
449
|
};
|
|
307
450
|
const Columns = {
|
|
308
451
|
label: "Columns",
|
|
@@ -355,15 +498,27 @@ const Image = {
|
|
|
355
498
|
fields: {
|
|
356
499
|
url: { type: "text", label: "Image URL" },
|
|
357
500
|
alt: { type: "text", label: "Description (for screen readers)" },
|
|
358
|
-
caption: { type: "text" }
|
|
501
|
+
caption: { type: "text" },
|
|
502
|
+
align: {
|
|
503
|
+
type: "radio",
|
|
504
|
+
options: [
|
|
505
|
+
{ label: "Left", value: "left" },
|
|
506
|
+
{ label: "Center", value: "center" },
|
|
507
|
+
{ label: "Right", value: "right" }
|
|
508
|
+
]
|
|
509
|
+
},
|
|
510
|
+
width: { ...percentField("Width (% of the column)"), min: 10 }
|
|
359
511
|
},
|
|
360
|
-
defaultProps: { url: "", alt: "", caption: "" },
|
|
361
|
-
render: ({ url, alt, caption }) => {
|
|
512
|
+
defaultProps: { url: "", alt: "", caption: "", align: "left", width: 100 },
|
|
513
|
+
render: ({ url, alt, caption, align, width }) => {
|
|
362
514
|
const src = safeImageUrl(url);
|
|
363
515
|
if (!src) return null;
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
516
|
+
const requested = Number.isFinite(Number(width)) && width !== "" && width !== null ? Number(width) : 100;
|
|
517
|
+
const percent = Math.min(100, Math.max(10, requested));
|
|
518
|
+
const side = align === "center" ? { marginInline: "auto" } : align === "right" ? { marginLeft: "auto" } : {};
|
|
519
|
+
return /* @__PURE__ */ jsxs("figure", { style: { margin: "0 0 1em", width: `${percent}%`, maxWidth: "100%", ...side }, children: [
|
|
520
|
+
/* @__PURE__ */ jsx("img", { src, alt: alt ?? "", style: { width: "100%", height: "auto", display: "block", borderRadius: tokenVar("radius") } }),
|
|
521
|
+
caption && /* @__PURE__ */ jsx("figcaption", { style: { fontSize: "0.9em", opacity: 0.75, marginTop: "6px", textAlign: align }, children: caption })
|
|
367
522
|
] });
|
|
368
523
|
}
|
|
369
524
|
};
|
|
@@ -445,6 +600,48 @@ const VideoEmbed = {
|
|
|
445
600
|
) });
|
|
446
601
|
}
|
|
447
602
|
};
|
|
603
|
+
function embedAllowed(value, hosts) {
|
|
604
|
+
let url;
|
|
605
|
+
try {
|
|
606
|
+
url = new URL(String(value ?? "").trim());
|
|
607
|
+
} catch {
|
|
608
|
+
return void 0;
|
|
609
|
+
}
|
|
610
|
+
if (url.protocol !== "https:") return void 0;
|
|
611
|
+
const host = url.hostname.toLowerCase();
|
|
612
|
+
const ok = hosts.some((allowed) => {
|
|
613
|
+
const name = String(allowed).toLowerCase();
|
|
614
|
+
return host === name || host.endsWith(`.${name}`);
|
|
615
|
+
});
|
|
616
|
+
return ok ? url.href : void 0;
|
|
617
|
+
}
|
|
618
|
+
function makeEmbed(hosts) {
|
|
619
|
+
return {
|
|
620
|
+
label: "Embedded page",
|
|
621
|
+
category: "media",
|
|
622
|
+
fields: {
|
|
623
|
+
url: { type: "text", label: `Page address (https, from: ${hosts.join(", ")})` },
|
|
624
|
+
title: { type: "text", label: "Title (for screen readers)" },
|
|
625
|
+
height: { type: "number", label: "Height (pixels)", min: 200, max: 4e3 }
|
|
626
|
+
},
|
|
627
|
+
defaultProps: { url: "", title: "Embedded page", height: 800 },
|
|
628
|
+
render: ({ url, title, height }) => {
|
|
629
|
+
const src = embedAllowed(url, hosts);
|
|
630
|
+
if (!src) return null;
|
|
631
|
+
return /* @__PURE__ */ jsx(
|
|
632
|
+
"iframe",
|
|
633
|
+
{
|
|
634
|
+
src,
|
|
635
|
+
title: title || "Embedded page",
|
|
636
|
+
loading: "lazy",
|
|
637
|
+
sandbox: "allow-scripts allow-same-origin allow-popups",
|
|
638
|
+
referrerPolicy: "no-referrer",
|
|
639
|
+
style: { width: "100%", height: `${Math.min(4e3, Math.max(200, Number(height) || 800))}px`, border: 0, borderRadius: tokenVar("radius"), background: "#fff" }
|
|
640
|
+
}
|
|
641
|
+
);
|
|
642
|
+
}
|
|
643
|
+
};
|
|
644
|
+
}
|
|
448
645
|
|
|
449
646
|
const builtInBlocks = {
|
|
450
647
|
Section,
|
|
@@ -454,6 +651,9 @@ const builtInBlocks = {
|
|
|
454
651
|
Hero,
|
|
455
652
|
Heading,
|
|
456
653
|
Text,
|
|
654
|
+
Quote,
|
|
655
|
+
IconList,
|
|
656
|
+
Schedule,
|
|
457
657
|
Button,
|
|
458
658
|
CallToAction,
|
|
459
659
|
Cards,
|
|
@@ -468,18 +668,64 @@ const builtInCategories = {
|
|
|
468
668
|
media: { title: "Media" }
|
|
469
669
|
};
|
|
470
670
|
|
|
471
|
-
|
|
472
|
-
|
|
671
|
+
const SITE_BACKGROUND_DEFAULTS = { image: "", overlayColor: "#ffffff", overlayOpacity: 85, rounded: true, fit: "cover", frame: "none" };
|
|
672
|
+
function resolveBackground(theme = {}, page = {}) {
|
|
673
|
+
const site = { ...SITE_BACKGROUND_DEFAULTS, ...theme.background ?? {} };
|
|
674
|
+
const mode = page.background ?? "site";
|
|
675
|
+
if (mode === "none") return null;
|
|
676
|
+
const chosen = mode === "custom" ? {
|
|
677
|
+
image: page.backgroundImage,
|
|
678
|
+
overlayColor: page.overlayColor ?? site.overlayColor,
|
|
679
|
+
overlayOpacity: page.overlayOpacity ?? site.overlayOpacity,
|
|
680
|
+
rounded: site.rounded,
|
|
681
|
+
fit: site.fit,
|
|
682
|
+
frame: site.frame
|
|
683
|
+
} : {
|
|
684
|
+
...site,
|
|
685
|
+
// A page may keep the site's photo but change how much shows through.
|
|
686
|
+
overlayOpacity: page.overlayOpacity ?? site.overlayOpacity
|
|
687
|
+
};
|
|
688
|
+
const image = cssImage(chosen.image);
|
|
689
|
+
return image ? { ...chosen, image, imageUrl: safeImageUrl(chosen.image) } : null;
|
|
690
|
+
}
|
|
691
|
+
function ThemeRoot({ theme, page, children }) {
|
|
692
|
+
const background = resolveBackground(theme ?? {}, page ?? {});
|
|
693
|
+
if (!background) {
|
|
694
|
+
return /* @__PURE__ */ jsx("div", { className: "pagelet", style: themeToStyle(theme), children });
|
|
695
|
+
}
|
|
696
|
+
const radius = background.rounded ? "var(--pagelet-radius)" : 0;
|
|
697
|
+
const panel = { background: overlayColor(background.overlayColor, background.overlayOpacity), padding: "calc(var(--pagelet-space) * 2)" };
|
|
698
|
+
const area = background.fit === "photo" ? /* @__PURE__ */ jsxs("div", { className: "pagelet-background", style: { display: "grid", borderRadius: radius, overflow: "hidden" }, children: [
|
|
699
|
+
/* @__PURE__ */ jsx("img", { src: background.imageUrl, alt: "", "aria-hidden": "true", style: { gridArea: "1 / 1", width: "100%", height: "100%", objectFit: "cover", display: "block" } }),
|
|
700
|
+
/* @__PURE__ */ jsx("div", { className: "pagelet-panel", style: { gridArea: "1 / 1", ...panel }, children })
|
|
701
|
+
] }) : /* @__PURE__ */ jsx("div", { className: "pagelet-background", style: { background: `${background.image} center / cover`, borderRadius: radius, overflow: "hidden" }, children: /* @__PURE__ */ jsx("div", { className: "pagelet-panel", style: { ...panel, minHeight: "60vh" }, children }) });
|
|
702
|
+
return /* @__PURE__ */ jsx("div", { className: "pagelet", style: themeToStyle(theme), children: background.frame === "card" ? /* @__PURE__ */ jsx("div", { className: "pagelet-frame", style: { border: "1px solid rgba(0, 0, 0, 0.15)", borderRadius: radius, padding: "var(--pagelet-space)", background: "var(--pagelet-color-background)" }, children: area }) : area });
|
|
473
703
|
}
|
|
474
704
|
|
|
475
|
-
|
|
705
|
+
const pageFields = {
|
|
706
|
+
title: { type: "text", label: "Page title (browser tab)" },
|
|
707
|
+
background: {
|
|
708
|
+
type: "radio",
|
|
709
|
+
label: "Background",
|
|
710
|
+
options: [
|
|
711
|
+
{ label: "Site's", value: "site" },
|
|
712
|
+
{ label: "Custom", value: "custom" },
|
|
713
|
+
{ label: "None", value: "none" }
|
|
714
|
+
]
|
|
715
|
+
},
|
|
716
|
+
backgroundImage: { type: "text", label: "Custom background image URL" },
|
|
717
|
+
overlayColor: { type: "text", label: "Custom overlay color (hex)" },
|
|
718
|
+
overlayOpacity: percentField("Overlay opacity % (blank uses the site setting)")
|
|
719
|
+
};
|
|
720
|
+
function createPalette({ blocks = {}, hide = [], categories = {}, root = {}, embedHosts = [] } = {}) {
|
|
721
|
+
const builtIns = embedHosts.length ? { ...builtInBlocks, Embed: makeEmbed(embedHosts) } : builtInBlocks;
|
|
476
722
|
const hidden = new Set(hide);
|
|
477
723
|
for (const name of hidden) {
|
|
478
|
-
if (!(name in
|
|
724
|
+
if (!(name in builtIns) && !(name in blocks)) {
|
|
479
725
|
throw new Error(`pagelet: cannot hide unknown block "${name}"`);
|
|
480
726
|
}
|
|
481
727
|
}
|
|
482
|
-
const all = { ...
|
|
728
|
+
const all = { ...builtIns, ...blocks };
|
|
483
729
|
const components = {};
|
|
484
730
|
const membership = {};
|
|
485
731
|
for (const [name, block] of Object.entries(all)) {
|
|
@@ -504,15 +750,28 @@ function createPalette({ blocks = {}, hide = [], categories = {}, root = {} } =
|
|
|
504
750
|
components,
|
|
505
751
|
categories: puckCategories,
|
|
506
752
|
root: {
|
|
753
|
+
defaultProps: { title: "", background: "site", backgroundImage: "", overlayColor: "#ffffff" },
|
|
507
754
|
...root,
|
|
508
|
-
|
|
509
|
-
//
|
|
510
|
-
//
|
|
511
|
-
|
|
755
|
+
fields: { ...pageFields, ...root.fields ?? {} },
|
|
756
|
+
// The page's root render applies the theme and the background. It is
|
|
757
|
+
// the root of the editor's preview iframe as well as of a rendered
|
|
758
|
+
// page, so both reach the editor. The site theme arrives through
|
|
759
|
+
// Puck's metadata; the page's own settings are this render's props.
|
|
760
|
+
render: ({ children, puck, title, background, backgroundImage, overlayColor, overlayOpacity }) => /* @__PURE__ */ jsx(
|
|
761
|
+
ThemeRoot,
|
|
762
|
+
{
|
|
763
|
+
theme: puck?.metadata?.pageletTheme,
|
|
764
|
+
page: { title, background, backgroundImage, overlayColor, overlayOpacity: blankToUndefined(overlayOpacity) },
|
|
765
|
+
children
|
|
766
|
+
}
|
|
767
|
+
)
|
|
512
768
|
}
|
|
513
769
|
}
|
|
514
770
|
};
|
|
515
771
|
}
|
|
772
|
+
function blankToUndefined(value) {
|
|
773
|
+
return value === "" || value === null || value === void 0 ? void 0 : value;
|
|
774
|
+
}
|
|
516
775
|
|
|
517
776
|
class PageletApiError extends Error {
|
|
518
777
|
constructor(message, { status, errors } = {}) {
|
|
@@ -555,28 +814,109 @@ function createPageletClient({ baseUrl = "/pagelet", csrfToken, headers = {}, fe
|
|
|
555
814
|
});
|
|
556
815
|
}
|
|
557
816
|
const pagePath = (id) => `/pages/${encodeURIComponent(id)}`;
|
|
817
|
+
async function getPublishedPage(path) {
|
|
818
|
+
try {
|
|
819
|
+
const data = await request("GET", `/published?path=${encodeURIComponent(path)}`);
|
|
820
|
+
return { document: data.document, pagePath: data.page_path, rest: data.rest ?? "" };
|
|
821
|
+
} catch (error) {
|
|
822
|
+
if (error instanceof PageletApiError && error.status === 404) return null;
|
|
823
|
+
throw error;
|
|
824
|
+
}
|
|
825
|
+
}
|
|
558
826
|
return {
|
|
559
827
|
listPages: () => request("GET", "/pages"),
|
|
560
828
|
getPage: (id) => request("GET", pagePath(id)),
|
|
561
|
-
createPage: ({ path, title }) => request("POST", "/pages", { page: { path, title } }),
|
|
562
|
-
updatePage: (id, { path, title }) => request("PATCH", pagePath(id), { page: { path, title } }),
|
|
829
|
+
createPage: ({ path, title, matchesSubpaths }) => request("POST", "/pages", { page: { path, title, matches_subpaths: matchesSubpaths } }),
|
|
830
|
+
updatePage: (id, { path, title, matchesSubpaths }) => request("PATCH", pagePath(id), { page: { path, title, matches_subpaths: matchesSubpaths } }),
|
|
563
831
|
saveDraft: (id, document2) => request("PATCH", pagePath(id), { page: { draft_document: document2 } }),
|
|
564
832
|
deletePage: (id) => request("DELETE", pagePath(id)),
|
|
565
833
|
publish: (id, { publishedBy } = {}) => request("POST", `${pagePath(id)}/publish`, publishedBy ? { published_by: publishedBy } : {}),
|
|
566
834
|
listVersions: (id) => request("GET", `${pagePath(id)}/versions`),
|
|
567
835
|
restoreVersion: (id, number) => request("POST", `${pagePath(id)}/versions/${encodeURIComponent(number)}/restore`, {}),
|
|
568
836
|
// Public: the published document at a path, or null when there is none.
|
|
569
|
-
getPublished: async (path) =>
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
}
|
|
837
|
+
getPublished: async (path) => (await getPublishedPage(path))?.document ?? null,
|
|
838
|
+
// Public: { document, pagePath, rest } for a path, or null. rest is what
|
|
839
|
+
// follows a page that answers for sub-paths: /messages/43 -> "43".
|
|
840
|
+
getPublishedPage,
|
|
841
|
+
// Public: the site's theme and menu.
|
|
842
|
+
getSite: async () => (await request("GET", "/site")).settings,
|
|
843
|
+
// Needs publish permission: changes are live at once.
|
|
844
|
+
updateSite: async (settings) => (await request("PATCH", "/site", { settings })).settings
|
|
578
845
|
};
|
|
579
846
|
}
|
|
580
847
|
|
|
581
|
-
|
|
848
|
+
const CSS = `
|
|
849
|
+
.pagelet-menu{display:flex;align-items:center;flex-wrap:wrap;gap:4px 16px;padding:8px 16px;background:var(--pagelet-menu-background,#f8f9fa)}
|
|
850
|
+
.pagelet-menu--centered{flex-direction:column}
|
|
851
|
+
.pagelet-menu__groups{display:flex;flex:1;align-items:center;gap:4px 16px;flex-wrap:wrap}
|
|
852
|
+
.pagelet-menu--centered .pagelet-menu__groups{justify-content:center;flex:none}
|
|
853
|
+
.pagelet-menu__group{display:flex;gap:4px 16px;flex-wrap:wrap;align-items:center}
|
|
854
|
+
.pagelet-menu__group--right{margin-left:auto}
|
|
855
|
+
.pagelet-menu--centered .pagelet-menu__group--right{margin-left:0}
|
|
856
|
+
.pagelet-menu__link{text-decoration:none;color:rgba(0,0,0,.65);padding:6px 0;display:inline-flex;align-items:center;gap:6px}
|
|
857
|
+
.pagelet-menu__link:hover{color:rgba(0,0,0,.9)}
|
|
858
|
+
.pagelet-menu__link--current{color:rgba(0,0,0,.9)}
|
|
859
|
+
.pagelet-menu__toggle{display:none;margin-left:auto;background:none;border:1px solid rgba(0,0,0,.2);border-radius:6px;padding:6px 10px;cursor:pointer;font:inherit}
|
|
860
|
+
@media (max-width: 767px){
|
|
861
|
+
.pagelet-menu__toggle{display:inline-block}
|
|
862
|
+
.pagelet-menu__groups{display:none;flex-basis:100%;flex-direction:column;align-items:flex-start}
|
|
863
|
+
.pagelet-menu__groups--open{display:flex}
|
|
864
|
+
.pagelet-menu__group{flex-direction:column;align-items:flex-start}
|
|
865
|
+
.pagelet-menu__group--right{margin-left:0}
|
|
866
|
+
}`;
|
|
867
|
+
function isExternal(href) {
|
|
868
|
+
return /^https?:\/\//i.test(href);
|
|
869
|
+
}
|
|
870
|
+
function needsPlainAnchor(href, LinkComponent) {
|
|
871
|
+
return !LinkComponent || isExternal(href) || /^(mailto|tel):/i.test(href);
|
|
872
|
+
}
|
|
873
|
+
function MenuLink({ item, linkComponent: LinkComponent, currentPath, onNavigate }) {
|
|
874
|
+
const href = safeUrl(item.href);
|
|
875
|
+
if (!href) return null;
|
|
876
|
+
const current = !isExternal(href) && currentPath === href;
|
|
877
|
+
const className = `pagelet-menu__link${current ? " pagelet-menu__link--current" : ""}`;
|
|
878
|
+
const style = item.highlight ? { color: tokenVar("colorAccent") } : void 0;
|
|
879
|
+
const body = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
880
|
+
item.label,
|
|
881
|
+
item.icon && /* @__PURE__ */ jsx(Icon, { name: item.icon })
|
|
882
|
+
] });
|
|
883
|
+
if (needsPlainAnchor(href, LinkComponent)) {
|
|
884
|
+
const external = isExternal(href) ? { target: "_blank", rel: "noopener noreferrer" } : {};
|
|
885
|
+
return /* @__PURE__ */ jsx("a", { className, style, href, ...external, children: body });
|
|
886
|
+
}
|
|
887
|
+
return /* @__PURE__ */ jsx(LinkComponent, { className, style, to: href, onClick: onNavigate, "aria-current": current ? "page" : void 0, children: body });
|
|
888
|
+
}
|
|
889
|
+
function PageletMenu({ menu = {}, linkComponent, currentPath }) {
|
|
890
|
+
const [open, setOpen] = useState(false);
|
|
891
|
+
const items = Array.isArray(menu.items) ? menu.items : [];
|
|
892
|
+
const brand = menu.brand ?? {};
|
|
893
|
+
const logo = safeImageUrl(brand.logo);
|
|
894
|
+
const brandHref = safeUrl(brand.href) ?? "/";
|
|
895
|
+
const close = () => setOpen(false);
|
|
896
|
+
const group = (name) => items.filter((item) => (item.group ?? "left") === name);
|
|
897
|
+
const linkProps = { linkComponent, currentPath, onNavigate: close };
|
|
898
|
+
const BrandLink = needsPlainAnchor(brandHref, linkComponent) ? "a" : linkComponent;
|
|
899
|
+
const brandTarget = BrandLink === "a" ? { href: brandHref } : { to: brandHref, onClick: close };
|
|
900
|
+
return /* @__PURE__ */ jsxs("nav", { className: `pagelet-menu${menu.style === "centered" ? " pagelet-menu--centered" : ""}`, "aria-label": "Site", children: [
|
|
901
|
+
/* @__PURE__ */ jsx("style", { children: CSS }),
|
|
902
|
+
(logo || brand.label) && /* @__PURE__ */ jsxs(BrandLink, { className: "pagelet-menu__brand", ...brandTarget, style: { display: "inline-flex", alignItems: "center", gap: "8px", textDecoration: "none", color: "inherit" }, children: [
|
|
903
|
+
logo && /* @__PURE__ */ jsx("img", { src: logo, alt: brand.label || "Home", style: { height: "40px", width: "auto", display: "block" } }),
|
|
904
|
+
!logo && brand.label
|
|
905
|
+
] }),
|
|
906
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "pagelet-menu__toggle", "aria-expanded": open, onClick: () => setOpen((value) => !value), children: "Menu" }),
|
|
907
|
+
/* @__PURE__ */ jsxs("div", { className: `pagelet-menu__groups${open ? " pagelet-menu__groups--open" : ""}`, children: [
|
|
908
|
+
/* @__PURE__ */ jsx("div", { className: "pagelet-menu__group", children: group("left").map((item, i) => /* @__PURE__ */ jsx(MenuLink, { item, ...linkProps }, i)) }),
|
|
909
|
+
/* @__PURE__ */ jsx("div", { className: "pagelet-menu__group pagelet-menu__group--right", children: group("right").map((item, i) => /* @__PURE__ */ jsx(MenuLink, { item, ...linkProps }, i)) })
|
|
910
|
+
] })
|
|
911
|
+
] });
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
function PageletLayout({ site = {}, linkComponent, currentPath, children }) {
|
|
915
|
+
return /* @__PURE__ */ jsxs("div", { className: "pagelet-layout", style: { ...themeToStyle(site.theme), maxWidth: tokenVar("maxWidth"), margin: "0 auto" }, children: [
|
|
916
|
+
/* @__PURE__ */ jsx(PageletMenu, { menu: site.menu, linkComponent, currentPath }),
|
|
917
|
+
/* @__PURE__ */ jsx("main", { style: { marginTop: "var(--pagelet-space)" }, children })
|
|
918
|
+
] });
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
export { DOCUMENT_VERSION, Icon, PageletApiError, PageletLayout, PageletMenu, PageletPage, builtInBlocks, builtInCategories, createDocument, createPageletClient, createPalette, defaultTheme, embedAllowed, embedUrl, emptyData, iconNames, overlayColor, pageSettings, safeImageUrl, safeUrl, themeToStyle, tokenVar, upgradeDocument };
|
|
582
922
|
//# sourceMappingURL=index.js.map
|