@sevenfold/setto-client 0.4.0 → 0.6.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/README.md +32 -24
- package/dist/SettoAnimation.d.ts +22 -0
- package/dist/SettoForm.d.ts +18 -0
- package/dist/SettoVideo.d.ts +16 -0
- package/dist/admin/App.d.ts +2 -3
- package/dist/block-tree/SettoBlockTree.d.ts +24 -0
- package/dist/block-tree/extract-keys.d.ts +30 -0
- package/dist/block-tree/registry.d.ts +24 -0
- package/dist/block-tree/schema.d.ts +120 -0
- package/dist/block-tree/validate.d.ts +28 -0
- package/dist/edit-mode/constants.d.ts +4 -2
- package/dist/edit-mode/document-layout.d.ts +4 -2
- package/dist/edit-mode/mount.d.ts +2 -2
- package/dist/edit-mode/setto-mark.d.ts +16 -0
- package/dist/index.d.ts +13 -0
- package/dist/lib/theme-store.d.ts +2 -0
- package/dist/lib/urls.d.ts +8 -4
- package/dist/lib/use-site-access.d.ts +16 -0
- package/dist/provider.d.ts +10 -5
- package/dist/setto-client.js +1026 -190
- package/dist/setto-client.js.map +1 -1
- package/package.json +7 -4
package/dist/setto-client.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx, jsxs, Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { useEffect, useSyncExternalStore,
|
|
2
|
+
import { useState, useEffect, useSyncExternalStore, useRef, useLayoutEffect, createContext, useContext, useCallback, useMemo, useId, forwardRef } from "react";
|
|
3
3
|
import { useTranslation } from "react-i18next";
|
|
4
4
|
import { createPortal } from "react-dom";
|
|
5
5
|
function __rest(s, e) {
|
|
@@ -21129,6 +21129,15 @@ class ThemeStore {
|
|
|
21129
21129
|
this.drafts.clear();
|
|
21130
21130
|
this.bump();
|
|
21131
21131
|
}
|
|
21132
|
+
/** Resets every draft back to its original value. */
|
|
21133
|
+
revertAll() {
|
|
21134
|
+
for (const draft of this.drafts.values()) {
|
|
21135
|
+
if (!this.data[draft.sectionId]) this.data[draft.sectionId] = {};
|
|
21136
|
+
this.data[draft.sectionId][draft.field] = draft.original;
|
|
21137
|
+
}
|
|
21138
|
+
this.drafts.clear();
|
|
21139
|
+
this.bump();
|
|
21140
|
+
}
|
|
21132
21141
|
commit() {
|
|
21133
21142
|
this.baseline = structuredClone(this.data);
|
|
21134
21143
|
this.drafts.clear();
|
|
@@ -21146,14 +21155,63 @@ class ThemeStore {
|
|
|
21146
21155
|
for (const fn of this.listeners) fn(this.cachedSnapshot);
|
|
21147
21156
|
}
|
|
21148
21157
|
}
|
|
21149
|
-
const
|
|
21158
|
+
const EMPTY = {
|
|
21159
|
+
sites: null,
|
|
21160
|
+
currentSite: null,
|
|
21161
|
+
loading: true,
|
|
21162
|
+
error: null
|
|
21163
|
+
};
|
|
21164
|
+
function useSiteAccess(supabase, session, siteId) {
|
|
21165
|
+
const [state, setState] = useState(EMPTY);
|
|
21166
|
+
useEffect(() => {
|
|
21167
|
+
if (!session) {
|
|
21168
|
+
setState({ sites: null, currentSite: null, loading: false, error: null });
|
|
21169
|
+
return;
|
|
21170
|
+
}
|
|
21171
|
+
let cancelled = false;
|
|
21172
|
+
setState((prev) => ({ ...prev, loading: true, error: null }));
|
|
21173
|
+
supabase.from("sites").select("*").then(({ data, error }) => {
|
|
21174
|
+
if (cancelled) return;
|
|
21175
|
+
if (error) {
|
|
21176
|
+
setState({ sites: [], currentSite: null, loading: false, error: error.message });
|
|
21177
|
+
return;
|
|
21178
|
+
}
|
|
21179
|
+
const rows = data ?? [];
|
|
21180
|
+
const current = rows.find((s) => s.id === siteId) ?? null;
|
|
21181
|
+
setState({ sites: rows, currentSite: current, loading: false, error: null });
|
|
21182
|
+
});
|
|
21183
|
+
return () => {
|
|
21184
|
+
cancelled = true;
|
|
21185
|
+
};
|
|
21186
|
+
}, [supabase, session, siteId]);
|
|
21187
|
+
return state;
|
|
21188
|
+
}
|
|
21189
|
+
const SETTO_BASE = "/setto";
|
|
21190
|
+
function siteHomeUrl(pathname = "/") {
|
|
21191
|
+
const u = new URL(window.location.href);
|
|
21192
|
+
u.pathname = pathname;
|
|
21193
|
+
u.search = "";
|
|
21194
|
+
return u.toString();
|
|
21195
|
+
}
|
|
21196
|
+
function isAdminRoute() {
|
|
21197
|
+
return window.location.pathname.startsWith(SETTO_BASE);
|
|
21198
|
+
}
|
|
21199
|
+
function isAdminDeploymentView() {
|
|
21200
|
+
return isAdminRoute() && new URLSearchParams(window.location.search).has("deployment");
|
|
21201
|
+
}
|
|
21202
|
+
function adminRedirectUrl() {
|
|
21203
|
+
return `${window.location.origin}${SETTO_BASE}`;
|
|
21204
|
+
}
|
|
21205
|
+
const FAB_SIZE = 52;
|
|
21206
|
+
const FAB_INSET = 16;
|
|
21150
21207
|
const STYLE_ID$1 = "setto-edit-layout";
|
|
21151
21208
|
const HTML_CLASS = "setto-edit-mode";
|
|
21152
21209
|
function useSettoDocumentLayout(active) {
|
|
21153
21210
|
useEffect(() => {
|
|
21154
21211
|
const html = document.documentElement;
|
|
21155
21212
|
html.classList.add(HTML_CLASS);
|
|
21156
|
-
html.style.setProperty("--setto-
|
|
21213
|
+
html.style.setProperty("--setto-fab-size", `${FAB_SIZE}px`);
|
|
21214
|
+
html.style.setProperty("--setto-fab-inset", `${FAB_INSET}px`);
|
|
21157
21215
|
let styleEl = document.getElementById(STYLE_ID$1);
|
|
21158
21216
|
if (!styleEl) {
|
|
21159
21217
|
styleEl = document.createElement("style");
|
|
@@ -21164,23 +21222,10 @@ function useSettoDocumentLayout(active) {
|
|
|
21164
21222
|
overflow-x: clip;
|
|
21165
21223
|
}
|
|
21166
21224
|
|
|
21167
|
-
html.setto-edit-mode body {
|
|
21168
|
-
padding-top: var(--setto-toolbar-height);
|
|
21169
|
-
}
|
|
21170
|
-
|
|
21171
|
-
html.setto-edit-mode #root {
|
|
21172
|
-
width: 100%;
|
|
21173
|
-
min-height: calc(100dvh - var(--setto-toolbar-height));
|
|
21174
|
-
/* Containing block for position:fixed descendants so they sit below the toolbar. */
|
|
21175
|
-
transform: translateZ(0);
|
|
21176
|
-
}
|
|
21177
|
-
|
|
21178
21225
|
html.setto-edit-mode #setto-toolbar-root {
|
|
21179
21226
|
position: fixed;
|
|
21180
|
-
|
|
21181
|
-
|
|
21182
|
-
right: 0;
|
|
21183
|
-
height: var(--setto-toolbar-height);
|
|
21227
|
+
left: var(--setto-fab-inset);
|
|
21228
|
+
bottom: var(--setto-fab-inset);
|
|
21184
21229
|
z-index: 2147483647;
|
|
21185
21230
|
}
|
|
21186
21231
|
|
|
@@ -21207,19 +21252,13 @@ function useSettoDocumentLayout(active) {
|
|
|
21207
21252
|
}
|
|
21208
21253
|
|
|
21209
21254
|
html.setto-edit-mode [data-setto-key]:hover {
|
|
21210
|
-
outline: 1px dashed rgba(
|
|
21255
|
+
outline: 1px dashed rgba(196, 80, 42, 0.55);
|
|
21211
21256
|
outline-offset: 2px;
|
|
21212
21257
|
border-radius: 2px;
|
|
21213
21258
|
}
|
|
21214
21259
|
|
|
21215
21260
|
html.setto-edit-mode [data-setto-section]:hover {
|
|
21216
|
-
box-shadow: inset 0 0 0 1px rgba(
|
|
21217
|
-
}
|
|
21218
|
-
|
|
21219
|
-
@media (max-width: 480px) {
|
|
21220
|
-
html.setto-edit-mode [data-setto-draft-count] {
|
|
21221
|
-
display: none;
|
|
21222
|
-
}
|
|
21261
|
+
box-shadow: inset 0 0 0 1px rgba(196, 80, 42, 0.3);
|
|
21223
21262
|
}
|
|
21224
21263
|
`;
|
|
21225
21264
|
document.head.appendChild(styleEl);
|
|
@@ -21227,7 +21266,8 @@ function useSettoDocumentLayout(active) {
|
|
|
21227
21266
|
notifyLayoutChange();
|
|
21228
21267
|
return () => {
|
|
21229
21268
|
html.classList.remove(HTML_CLASS);
|
|
21230
|
-
html.style.removeProperty("--setto-
|
|
21269
|
+
html.style.removeProperty("--setto-fab-size");
|
|
21270
|
+
html.style.removeProperty("--setto-fab-inset");
|
|
21231
21271
|
styleEl?.remove();
|
|
21232
21272
|
notifyLayoutChange();
|
|
21233
21273
|
};
|
|
@@ -21395,8 +21435,7 @@ function computePosition(targetEl, toolbarW, toolbarH) {
|
|
|
21395
21435
|
const rect = targetEl.getBoundingClientRect();
|
|
21396
21436
|
let left = rect.left + (rect.width - toolbarW) / 2;
|
|
21397
21437
|
left = Math.max(MARGIN$1, Math.min(left, window.innerWidth - toolbarW - MARGIN$1));
|
|
21398
|
-
const
|
|
21399
|
-
const top = Math.max(minTop, rect.top - toolbarH - GAP);
|
|
21438
|
+
const top = Math.max(MARGIN$1, rect.top - toolbarH - GAP);
|
|
21400
21439
|
return { top, left };
|
|
21401
21440
|
}
|
|
21402
21441
|
function BrandColorPicker({
|
|
@@ -21432,7 +21471,7 @@ function BrandColorPicker({
|
|
|
21432
21471
|
left = window.innerWidth - popoverW - 8;
|
|
21433
21472
|
}
|
|
21434
21473
|
if (left < 8) left = 8;
|
|
21435
|
-
if (top <
|
|
21474
|
+
if (top < 8) top = rect.bottom + 6;
|
|
21436
21475
|
if (top + popoverH > window.innerHeight - 8) {
|
|
21437
21476
|
top = rect.top - popoverH - 6;
|
|
21438
21477
|
}
|
|
@@ -22150,23 +22189,43 @@ function useEditBaseline() {
|
|
|
22150
22189
|
};
|
|
22151
22190
|
}, [session, store, config.siteId]);
|
|
22152
22191
|
}
|
|
22153
|
-
|
|
22154
|
-
|
|
22155
|
-
|
|
22156
|
-
|
|
22157
|
-
|
|
22158
|
-
|
|
22159
|
-
return
|
|
22160
|
-
|
|
22161
|
-
|
|
22162
|
-
|
|
22163
|
-
|
|
22164
|
-
|
|
22165
|
-
|
|
22166
|
-
|
|
22167
|
-
|
|
22168
|
-
|
|
22192
|
+
function SettoMark({
|
|
22193
|
+
size = 24,
|
|
22194
|
+
letterColor = "#F2EBDF",
|
|
22195
|
+
dotColor = "#C4502A",
|
|
22196
|
+
style
|
|
22197
|
+
}) {
|
|
22198
|
+
return /* @__PURE__ */ jsxs(
|
|
22199
|
+
"svg",
|
|
22200
|
+
{
|
|
22201
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
22202
|
+
viewBox: "0 0 64 64",
|
|
22203
|
+
width: size,
|
|
22204
|
+
height: size,
|
|
22205
|
+
"aria-hidden": true,
|
|
22206
|
+
style: { display: "block", ...style },
|
|
22207
|
+
children: [
|
|
22208
|
+
/* @__PURE__ */ jsx(
|
|
22209
|
+
"text",
|
|
22210
|
+
{
|
|
22211
|
+
x: "32",
|
|
22212
|
+
y: "46",
|
|
22213
|
+
textAnchor: "middle",
|
|
22214
|
+
fontFamily: "Georgia, serif",
|
|
22215
|
+
fontStyle: "italic",
|
|
22216
|
+
fontSize: "42",
|
|
22217
|
+
fill: letterColor,
|
|
22218
|
+
children: "S"
|
|
22219
|
+
}
|
|
22220
|
+
),
|
|
22221
|
+
/* @__PURE__ */ jsx("circle", { cx: "49", cy: "17", r: "5", fill: dotColor })
|
|
22222
|
+
]
|
|
22223
|
+
}
|
|
22224
|
+
);
|
|
22169
22225
|
}
|
|
22226
|
+
const BLEKK = "#211711";
|
|
22227
|
+
const CREMA = "#F2EBDF";
|
|
22228
|
+
const TERRACOTTA = "#C4502A";
|
|
22170
22229
|
function EditToolbar() {
|
|
22171
22230
|
const { store, themeStore, assetStore, api, config, supabase } = useSetto();
|
|
22172
22231
|
const { i18n } = useTranslation();
|
|
@@ -22195,6 +22254,7 @@ function EditToolbar() {
|
|
|
22195
22254
|
const [error, setError] = useState(null);
|
|
22196
22255
|
const [publishedNotice, setPublishedNotice] = useState(false);
|
|
22197
22256
|
const [menuOpen, setMenuOpen] = useState(false);
|
|
22257
|
+
const [expanded, setExpanded] = useState(false);
|
|
22198
22258
|
const menuRef = useRef(null);
|
|
22199
22259
|
const publishedNoticeTimer = useRef(null);
|
|
22200
22260
|
const { row: deploymentRow, status: deploymentStatus } = useDeploymentStatus(
|
|
@@ -22225,6 +22285,18 @@ function EditToolbar() {
|
|
|
22225
22285
|
const hasDrafts = draftCount > 0;
|
|
22226
22286
|
const showToolbarProgress = activeDeployment !== null && !publishDialogOpen && deploymentRow !== null && isDeploymentInProgress(deploymentRow.status) && !isDeploymentStale(deploymentRow);
|
|
22227
22287
|
const showPublishedNotice = publishedNotice && !publishDialogOpen;
|
|
22288
|
+
useEffect(() => {
|
|
22289
|
+
if (error || showToolbarProgress || showPublishedNotice) {
|
|
22290
|
+
setExpanded(true);
|
|
22291
|
+
}
|
|
22292
|
+
}, [error, showToolbarProgress, showPublishedNotice]);
|
|
22293
|
+
const toggleExpanded = () => {
|
|
22294
|
+
setExpanded((prev) => {
|
|
22295
|
+
const next = !prev;
|
|
22296
|
+
if (!next) setMenuOpen(false);
|
|
22297
|
+
return next;
|
|
22298
|
+
});
|
|
22299
|
+
};
|
|
22228
22300
|
useEffect(() => {
|
|
22229
22301
|
return () => {
|
|
22230
22302
|
if (publishedNoticeTimer.current) clearTimeout(publishedNoticeTimer.current);
|
|
@@ -22375,6 +22447,14 @@ function EditToolbar() {
|
|
|
22375
22447
|
setPublishedNotice(false);
|
|
22376
22448
|
if (publishedNoticeTimer.current) clearTimeout(publishedNoticeTimer.current);
|
|
22377
22449
|
};
|
|
22450
|
+
const cancelDrafts = () => {
|
|
22451
|
+
setMenuOpen(false);
|
|
22452
|
+
store?.revertAll();
|
|
22453
|
+
themeStore?.revertAll();
|
|
22454
|
+
assetStore?.revertAll();
|
|
22455
|
+
setError(null);
|
|
22456
|
+
setExpanded(false);
|
|
22457
|
+
};
|
|
22378
22458
|
const signOut = () => {
|
|
22379
22459
|
setMenuOpen(false);
|
|
22380
22460
|
void supabase.auth.signOut();
|
|
@@ -22384,67 +22464,92 @@ function EditToolbar() {
|
|
|
22384
22464
|
window.location.href = SETTO_BASE;
|
|
22385
22465
|
};
|
|
22386
22466
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
22387
|
-
/* @__PURE__ */ jsxs(
|
|
22388
|
-
|
|
22389
|
-
|
|
22390
|
-
|
|
22391
|
-
|
|
22392
|
-
|
|
22393
|
-
|
|
22394
|
-
|
|
22395
|
-
|
|
22396
|
-
|
|
22397
|
-
/* @__PURE__ */ jsx("button", { type: "button", onClick: publish, style: publishBtnStyle, children: "Publiser" })
|
|
22398
|
-
] }) : null,
|
|
22399
|
-
showPublishedNotice ? /* @__PURE__ */ jsxs(
|
|
22400
|
-
"button",
|
|
22401
|
-
{
|
|
22402
|
-
type: "button",
|
|
22403
|
-
onClick: dismissPublishedNotice,
|
|
22404
|
-
style: publishedNoticeStyle,
|
|
22405
|
-
title: "Lukk",
|
|
22406
|
-
children: [
|
|
22407
|
-
/* @__PURE__ */ jsx(PublishedCheckIcon, {}),
|
|
22408
|
-
"Publisert"
|
|
22409
|
-
]
|
|
22410
|
-
}
|
|
22411
|
-
) : null,
|
|
22412
|
-
showToolbarProgress && deploymentRow ? /* @__PURE__ */ jsx(
|
|
22413
|
-
PublishProgressBar,
|
|
22414
|
-
{
|
|
22415
|
-
status: deploymentRow.status,
|
|
22416
|
-
compact: true,
|
|
22417
|
-
href: `${SETTO_BASE}?deployment=${encodeURIComponent(activeDeployment)}`
|
|
22418
|
-
}
|
|
22419
|
-
) : null,
|
|
22420
|
-
/* @__PURE__ */ jsxs("div", { ref: menuRef, style: { position: "relative" }, children: [
|
|
22467
|
+
/* @__PURE__ */ jsxs(
|
|
22468
|
+
"div",
|
|
22469
|
+
{
|
|
22470
|
+
role: "toolbar",
|
|
22471
|
+
"aria-label": "Setto editor",
|
|
22472
|
+
style: {
|
|
22473
|
+
...containerStyleFor(hasDrafts),
|
|
22474
|
+
...expanded ? expandedContainerStyle : {}
|
|
22475
|
+
},
|
|
22476
|
+
children: [
|
|
22421
22477
|
/* @__PURE__ */ jsx(
|
|
22422
22478
|
"button",
|
|
22423
22479
|
{
|
|
22424
22480
|
type: "button",
|
|
22425
|
-
|
|
22426
|
-
"aria-
|
|
22427
|
-
"
|
|
22428
|
-
style:
|
|
22429
|
-
|
|
22430
|
-
|
|
22431
|
-
},
|
|
22432
|
-
onMouseEnter: (e) => {
|
|
22433
|
-
e.currentTarget.style.background = "#f0f0f0";
|
|
22434
|
-
},
|
|
22435
|
-
onMouseLeave: (e) => {
|
|
22436
|
-
e.currentTarget.style.background = menuOpen ? "#f0f0f0" : "transparent";
|
|
22437
|
-
},
|
|
22438
|
-
children: "⋮"
|
|
22481
|
+
"aria-label": expanded ? "Lukk Setto-meny" : "Åpne Setto-meny",
|
|
22482
|
+
"aria-expanded": expanded,
|
|
22483
|
+
title: "Setto",
|
|
22484
|
+
style: markButtonStyle,
|
|
22485
|
+
onClick: toggleExpanded,
|
|
22486
|
+
children: /* @__PURE__ */ jsx(SettoMark, { size: 28 })
|
|
22439
22487
|
}
|
|
22440
22488
|
),
|
|
22441
|
-
|
|
22442
|
-
/* @__PURE__ */ jsx(
|
|
22443
|
-
/* @__PURE__ */ jsx(
|
|
22489
|
+
expanded ? /* @__PURE__ */ jsxs("div", { style: contentStyle, children: [
|
|
22490
|
+
error ? /* @__PURE__ */ jsx("span", { style: errorStyle, children: error }) : null,
|
|
22491
|
+
hasDrafts && !publishing && activeDeployment === null ? /* @__PURE__ */ jsx("button", { type: "button", onClick: publish, style: publishBtnStyle, children: "Publiser" }) : null,
|
|
22492
|
+
showPublishedNotice ? /* @__PURE__ */ jsxs(
|
|
22493
|
+
"button",
|
|
22494
|
+
{
|
|
22495
|
+
type: "button",
|
|
22496
|
+
onClick: dismissPublishedNotice,
|
|
22497
|
+
style: publishedNoticeStyle,
|
|
22498
|
+
title: "Lukk",
|
|
22499
|
+
children: [
|
|
22500
|
+
/* @__PURE__ */ jsx(PublishedCheckIcon, {}),
|
|
22501
|
+
"Publisert"
|
|
22502
|
+
]
|
|
22503
|
+
}
|
|
22504
|
+
) : null,
|
|
22505
|
+
showToolbarProgress && deploymentRow ? /* @__PURE__ */ jsx(
|
|
22506
|
+
PublishProgressBar,
|
|
22507
|
+
{
|
|
22508
|
+
status: deploymentRow.status,
|
|
22509
|
+
compact: true,
|
|
22510
|
+
href: `${SETTO_BASE}?deployment=${encodeURIComponent(activeDeployment)}`
|
|
22511
|
+
}
|
|
22512
|
+
) : null,
|
|
22513
|
+
!hasDrafts && !error && !showPublishedNotice && !showToolbarProgress && !publishing ? /* @__PURE__ */ jsx("span", { style: emptyStateStyle, children: "Ingen endringer" }) : null,
|
|
22514
|
+
/* @__PURE__ */ jsxs("div", { ref: menuRef, style: { position: "relative" }, children: [
|
|
22515
|
+
/* @__PURE__ */ jsx(
|
|
22516
|
+
"button",
|
|
22517
|
+
{
|
|
22518
|
+
type: "button",
|
|
22519
|
+
onClick: () => setMenuOpen((o) => !o),
|
|
22520
|
+
"aria-label": "Meny",
|
|
22521
|
+
"aria-expanded": menuOpen,
|
|
22522
|
+
style: {
|
|
22523
|
+
...menuBtnStyle,
|
|
22524
|
+
background: menuOpen ? "rgba(242, 235, 223, 0.16)" : "transparent"
|
|
22525
|
+
},
|
|
22526
|
+
onMouseEnter: (e) => {
|
|
22527
|
+
e.currentTarget.style.background = "rgba(242, 235, 223, 0.16)";
|
|
22528
|
+
},
|
|
22529
|
+
onMouseLeave: (e) => {
|
|
22530
|
+
e.currentTarget.style.background = menuOpen ? "rgba(242, 235, 223, 0.16)" : "transparent";
|
|
22531
|
+
},
|
|
22532
|
+
children: "⋮"
|
|
22533
|
+
}
|
|
22534
|
+
),
|
|
22535
|
+
menuOpen ? /* @__PURE__ */ jsxs("div", { style: dropdownStyle, role: "menu", children: [
|
|
22536
|
+
/* @__PURE__ */ jsx(
|
|
22537
|
+
MenuItem,
|
|
22538
|
+
{
|
|
22539
|
+
onClick: cancelDrafts,
|
|
22540
|
+
icon: /* @__PURE__ */ jsx(UndoIcon, {}),
|
|
22541
|
+
disabled: !hasDrafts,
|
|
22542
|
+
children: "Avbryt"
|
|
22543
|
+
}
|
|
22544
|
+
),
|
|
22545
|
+
/* @__PURE__ */ jsx(MenuItem, { onClick: goToHistory, icon: /* @__PURE__ */ jsx(HistoryIcon, {}), children: "Historikk" }),
|
|
22546
|
+
/* @__PURE__ */ jsx(MenuItem, { onClick: signOut, icon: /* @__PURE__ */ jsx(LogOutIcon, {}), children: "Logg ut" })
|
|
22547
|
+
] }) : null
|
|
22548
|
+
] })
|
|
22444
22549
|
] }) : null
|
|
22445
|
-
]
|
|
22446
|
-
|
|
22447
|
-
|
|
22550
|
+
]
|
|
22551
|
+
}
|
|
22552
|
+
),
|
|
22448
22553
|
/* @__PURE__ */ jsx(EditOnboardingDialog, {}),
|
|
22449
22554
|
publishDialogOpen && (publishing || activeDeployment !== null) ? /* @__PURE__ */ jsx(
|
|
22450
22555
|
PublishDialog,
|
|
@@ -22459,82 +22564,112 @@ function EditToolbar() {
|
|
|
22459
22564
|
) : null
|
|
22460
22565
|
] });
|
|
22461
22566
|
}
|
|
22462
|
-
const
|
|
22463
|
-
|
|
22464
|
-
|
|
22465
|
-
|
|
22466
|
-
|
|
22467
|
-
|
|
22468
|
-
|
|
22469
|
-
|
|
22470
|
-
|
|
22471
|
-
|
|
22472
|
-
|
|
22473
|
-
|
|
22474
|
-
|
|
22567
|
+
const EASE_OUT_EXPO = "cubic-bezier(0.16, 1, 0.3, 1)";
|
|
22568
|
+
const FAB_SHADOW = "0 8px 24px rgba(33, 23, 17, 0.35), 0 2px 6px rgba(33, 23, 17, 0.2)";
|
|
22569
|
+
function containerStyleFor(hasDrafts) {
|
|
22570
|
+
return {
|
|
22571
|
+
display: "inline-flex",
|
|
22572
|
+
alignItems: "center",
|
|
22573
|
+
height: FAB_SIZE,
|
|
22574
|
+
width: FAB_SIZE,
|
|
22575
|
+
padding: 0,
|
|
22576
|
+
background: BLEKK,
|
|
22577
|
+
color: CREMA,
|
|
22578
|
+
borderRadius: FAB_SIZE / 2,
|
|
22579
|
+
boxShadow: hasDrafts ? `0 0 0 2px ${TERRACOTTA}, ${FAB_SHADOW}` : FAB_SHADOW,
|
|
22580
|
+
fontFamily: 'system-ui, -apple-system, "Segoe UI", sans-serif',
|
|
22581
|
+
fontSize: 13,
|
|
22582
|
+
overflow: "visible",
|
|
22583
|
+
transition: `width 500ms ${EASE_OUT_EXPO}, padding 500ms ${EASE_OUT_EXPO}, box-shadow 300ms ${EASE_OUT_EXPO}`
|
|
22584
|
+
};
|
|
22585
|
+
}
|
|
22586
|
+
const expandedContainerStyle = {
|
|
22587
|
+
width: "auto",
|
|
22588
|
+
paddingRight: 10
|
|
22475
22589
|
};
|
|
22476
|
-
const
|
|
22477
|
-
display: "flex",
|
|
22590
|
+
const markButtonStyle = {
|
|
22591
|
+
display: "inline-flex",
|
|
22478
22592
|
alignItems: "center",
|
|
22479
|
-
|
|
22593
|
+
justifyContent: "center",
|
|
22594
|
+
flexShrink: 0,
|
|
22595
|
+
width: FAB_SIZE,
|
|
22596
|
+
height: FAB_SIZE,
|
|
22597
|
+
background: "transparent",
|
|
22598
|
+
border: "none",
|
|
22599
|
+
padding: 0,
|
|
22600
|
+
cursor: "pointer",
|
|
22601
|
+
color: "inherit"
|
|
22480
22602
|
};
|
|
22481
|
-
const
|
|
22603
|
+
const contentStyle = {
|
|
22482
22604
|
display: "flex",
|
|
22483
22605
|
alignItems: "center",
|
|
22484
|
-
gap:
|
|
22485
|
-
|
|
22606
|
+
gap: 6,
|
|
22607
|
+
paddingLeft: 4,
|
|
22608
|
+
whiteSpace: "nowrap"
|
|
22486
22609
|
};
|
|
22487
|
-
const
|
|
22488
|
-
color: "#
|
|
22610
|
+
const errorStyle = {
|
|
22611
|
+
color: "#ffb4a8",
|
|
22489
22612
|
fontSize: 12,
|
|
22490
|
-
|
|
22613
|
+
maxWidth: 240,
|
|
22614
|
+
overflow: "hidden",
|
|
22615
|
+
textOverflow: "ellipsis"
|
|
22491
22616
|
};
|
|
22492
|
-
const errorStyle = { color: "#dc2626", fontSize: 12 };
|
|
22493
22617
|
const publishedNoticeStyle = {
|
|
22494
22618
|
display: "inline-flex",
|
|
22495
22619
|
alignItems: "center",
|
|
22496
22620
|
gap: 5,
|
|
22497
22621
|
padding: "4px 10px",
|
|
22498
|
-
borderRadius:
|
|
22499
|
-
border: "1px solid
|
|
22500
|
-
background: "
|
|
22501
|
-
color:
|
|
22622
|
+
borderRadius: 999,
|
|
22623
|
+
border: "1px solid rgba(242, 235, 223, 0.25)",
|
|
22624
|
+
background: "rgba(242, 235, 223, 0.08)",
|
|
22625
|
+
color: CREMA,
|
|
22502
22626
|
fontSize: 12,
|
|
22503
22627
|
fontWeight: 500,
|
|
22504
22628
|
cursor: "pointer",
|
|
22505
22629
|
whiteSpace: "nowrap"
|
|
22506
22630
|
};
|
|
22507
22631
|
const publishBtnStyle = {
|
|
22508
|
-
background:
|
|
22509
|
-
color:
|
|
22632
|
+
background: TERRACOTTA,
|
|
22633
|
+
color: CREMA,
|
|
22510
22634
|
border: "none",
|
|
22511
|
-
borderRadius:
|
|
22635
|
+
borderRadius: 999,
|
|
22512
22636
|
padding: "6px 14px",
|
|
22513
22637
|
fontSize: 13,
|
|
22514
|
-
fontWeight:
|
|
22638
|
+
fontWeight: 600,
|
|
22515
22639
|
cursor: "pointer",
|
|
22516
22640
|
whiteSpace: "nowrap"
|
|
22517
22641
|
};
|
|
22518
22642
|
const menuBtnStyle = {
|
|
22519
22643
|
background: "transparent",
|
|
22520
|
-
color:
|
|
22644
|
+
color: CREMA,
|
|
22521
22645
|
border: "none",
|
|
22522
|
-
borderRadius:
|
|
22523
|
-
|
|
22524
|
-
|
|
22646
|
+
borderRadius: 999,
|
|
22647
|
+
width: 32,
|
|
22648
|
+
height: 32,
|
|
22649
|
+
display: "inline-flex",
|
|
22650
|
+
alignItems: "center",
|
|
22651
|
+
justifyContent: "center",
|
|
22652
|
+
fontSize: 22,
|
|
22525
22653
|
lineHeight: 1,
|
|
22526
|
-
cursor: "pointer"
|
|
22654
|
+
cursor: "pointer",
|
|
22655
|
+
fontWeight: 700
|
|
22656
|
+
};
|
|
22657
|
+
const emptyStateStyle = {
|
|
22658
|
+
color: "rgba(242, 235, 223, 0.55)",
|
|
22659
|
+
fontSize: 13,
|
|
22660
|
+
whiteSpace: "nowrap",
|
|
22661
|
+
padding: "0 4px"
|
|
22527
22662
|
};
|
|
22528
22663
|
const dropdownStyle = {
|
|
22529
22664
|
position: "absolute",
|
|
22530
|
-
|
|
22531
|
-
|
|
22532
|
-
|
|
22665
|
+
bottom: "100%",
|
|
22666
|
+
left: 0,
|
|
22667
|
+
marginBottom: 8,
|
|
22533
22668
|
background: "#fff",
|
|
22534
22669
|
border: "1px solid #e8e8e8",
|
|
22535
|
-
borderRadius:
|
|
22536
|
-
boxShadow: "0
|
|
22537
|
-
minWidth:
|
|
22670
|
+
borderRadius: 10,
|
|
22671
|
+
boxShadow: "0 12px 32px rgba(33, 23, 17, 0.18)",
|
|
22672
|
+
minWidth: 160,
|
|
22538
22673
|
overflow: "hidden",
|
|
22539
22674
|
zIndex: 1
|
|
22540
22675
|
};
|
|
@@ -22554,17 +22689,19 @@ const menuItemStyle = {
|
|
|
22554
22689
|
function MenuItem({
|
|
22555
22690
|
onClick,
|
|
22556
22691
|
icon,
|
|
22557
|
-
children
|
|
22692
|
+
children,
|
|
22693
|
+
disabled = false
|
|
22558
22694
|
}) {
|
|
22559
22695
|
return /* @__PURE__ */ jsxs(
|
|
22560
22696
|
"button",
|
|
22561
22697
|
{
|
|
22562
22698
|
type: "button",
|
|
22563
22699
|
onClick,
|
|
22564
|
-
|
|
22700
|
+
disabled,
|
|
22701
|
+
style: { ...menuItemStyle, opacity: disabled ? 0.4 : 1, cursor: disabled ? "not-allowed" : "pointer" },
|
|
22565
22702
|
role: "menuitem",
|
|
22566
22703
|
onMouseEnter: (e) => {
|
|
22567
|
-
e.currentTarget.style.background = "#f5f5f5";
|
|
22704
|
+
if (!disabled) e.currentTarget.style.background = "#f5f5f5";
|
|
22568
22705
|
},
|
|
22569
22706
|
onMouseLeave: (e) => {
|
|
22570
22707
|
e.currentTarget.style.background = "transparent";
|
|
@@ -22613,6 +22750,12 @@ function LogOutIcon() {
|
|
|
22613
22750
|
/* @__PURE__ */ jsx("line", { x1: "21", y1: "12", x2: "9", y2: "12" })
|
|
22614
22751
|
] });
|
|
22615
22752
|
}
|
|
22753
|
+
function UndoIcon() {
|
|
22754
|
+
return /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
22755
|
+
/* @__PURE__ */ jsx("path", { d: "M3 7v6h6" }),
|
|
22756
|
+
/* @__PURE__ */ jsx("path", { d: "M21 17a9 9 0 0 0-15-6.7L3 13" })
|
|
22757
|
+
] });
|
|
22758
|
+
}
|
|
22616
22759
|
function EditModeShell({ children, themeStore }) {
|
|
22617
22760
|
useSettoDocumentLayout(true);
|
|
22618
22761
|
const [toolbarRoot, setToolbarRoot] = useState(null);
|
|
@@ -22652,7 +22795,9 @@ function SettoProvider({ config, children }) {
|
|
|
22652
22795
|
const api = useMemo(() => createApi({ apiUrl: config.apiUrl, supabase }), [config.apiUrl, supabase]);
|
|
22653
22796
|
const [session, setSession] = useState(null);
|
|
22654
22797
|
const [authLoading, setAuthLoading] = useState(true);
|
|
22655
|
-
const [
|
|
22798
|
+
const [onAdminRoute, setOnAdminRoute] = useState(
|
|
22799
|
+
() => typeof window === "undefined" ? false : isAdminRoute()
|
|
22800
|
+
);
|
|
22656
22801
|
useEffect(() => {
|
|
22657
22802
|
let cancelled = false;
|
|
22658
22803
|
supabase.auth.getSession().then(({ data }) => {
|
|
@@ -22669,18 +22814,12 @@ function SettoProvider({ config, children }) {
|
|
|
22669
22814
|
};
|
|
22670
22815
|
}, [supabase]);
|
|
22671
22816
|
useEffect(() => {
|
|
22672
|
-
const compute = () =>
|
|
22673
|
-
try {
|
|
22674
|
-
const params = new URLSearchParams(window.location.search);
|
|
22675
|
-
setEditParam(params.get("setto") === "edit");
|
|
22676
|
-
} catch {
|
|
22677
|
-
setEditParam(false);
|
|
22678
|
-
}
|
|
22679
|
-
};
|
|
22817
|
+
const compute = () => setOnAdminRoute(isAdminRoute());
|
|
22680
22818
|
compute();
|
|
22681
22819
|
window.addEventListener("popstate", compute);
|
|
22682
22820
|
return () => window.removeEventListener("popstate", compute);
|
|
22683
22821
|
}, []);
|
|
22822
|
+
const access = useSiteAccess(supabase, session, config.siteId);
|
|
22684
22823
|
const { i18n } = useTranslation();
|
|
22685
22824
|
const storeRef = useRef(null);
|
|
22686
22825
|
const themeStoreRef = useRef(null);
|
|
@@ -22693,7 +22832,7 @@ function SettoProvider({ config, children }) {
|
|
|
22693
22832
|
assetStoreRef.current = new AssetStore();
|
|
22694
22833
|
setStoreReady(true);
|
|
22695
22834
|
}, [i18n, config.theme]);
|
|
22696
|
-
const editMode = !!session &&
|
|
22835
|
+
const editMode = !!session && !!access.currentSite && !onAdminRoute && !authLoading && !access.loading;
|
|
22697
22836
|
const value = {
|
|
22698
22837
|
config,
|
|
22699
22838
|
supabase,
|
|
@@ -22701,6 +22840,8 @@ function SettoProvider({ config, children }) {
|
|
|
22701
22840
|
session,
|
|
22702
22841
|
authLoading,
|
|
22703
22842
|
editMode,
|
|
22843
|
+
currentSite: access.currentSite,
|
|
22844
|
+
sites: access.sites,
|
|
22704
22845
|
store: storeReady ? storeRef.current : null,
|
|
22705
22846
|
themeStore: storeReady ? themeStoreRef.current : null,
|
|
22706
22847
|
assetStore: storeReady ? assetStoreRef.current : null
|
|
@@ -23140,7 +23281,7 @@ function FloatingPopover({
|
|
|
23140
23281
|
if (top + popoverH > window.innerHeight - 8) {
|
|
23141
23282
|
top = rect.top - popoverH - 6;
|
|
23142
23283
|
}
|
|
23143
|
-
if (top <
|
|
23284
|
+
if (top < 8) top = rect.bottom + 6;
|
|
23144
23285
|
setPos({ top, left });
|
|
23145
23286
|
};
|
|
23146
23287
|
update();
|
|
@@ -23645,6 +23786,236 @@ const removeBtnStyle = {
|
|
|
23645
23786
|
boxShadow: "0 1px 4px rgba(0,0,0,0.18)",
|
|
23646
23787
|
fontFamily: 'system-ui, -apple-system, "Segoe UI", sans-serif'
|
|
23647
23788
|
};
|
|
23789
|
+
function SettoForm({
|
|
23790
|
+
formId,
|
|
23791
|
+
fields,
|
|
23792
|
+
endpoint,
|
|
23793
|
+
endpointKey,
|
|
23794
|
+
className
|
|
23795
|
+
}) {
|
|
23796
|
+
const { t } = useTranslation();
|
|
23797
|
+
const [status, setStatus] = useState("idle");
|
|
23798
|
+
const [values, setValues] = useState(
|
|
23799
|
+
() => Object.fromEntries(fields.map((f) => [f.name, ""]))
|
|
23800
|
+
);
|
|
23801
|
+
const setField = useCallback(
|
|
23802
|
+
(name, value) => setValues((prev) => ({ ...prev, [name]: value })),
|
|
23803
|
+
[]
|
|
23804
|
+
);
|
|
23805
|
+
const resolveEndpoint = () => {
|
|
23806
|
+
if (endpoint) return endpoint;
|
|
23807
|
+
if (endpointKey) {
|
|
23808
|
+
const resolved = t(endpointKey, { defaultValue: "" });
|
|
23809
|
+
return resolved && resolved !== endpointKey ? resolved : null;
|
|
23810
|
+
}
|
|
23811
|
+
return null;
|
|
23812
|
+
};
|
|
23813
|
+
const onSubmit = async (e) => {
|
|
23814
|
+
e.preventDefault();
|
|
23815
|
+
const url = resolveEndpoint();
|
|
23816
|
+
if (!url) {
|
|
23817
|
+
setStatus("error");
|
|
23818
|
+
return;
|
|
23819
|
+
}
|
|
23820
|
+
setStatus("submitting");
|
|
23821
|
+
try {
|
|
23822
|
+
const res = await fetch(url, {
|
|
23823
|
+
method: "POST",
|
|
23824
|
+
headers: { "Content-Type": "application/json" },
|
|
23825
|
+
body: JSON.stringify(values)
|
|
23826
|
+
});
|
|
23827
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
23828
|
+
setStatus("success");
|
|
23829
|
+
setValues(
|
|
23830
|
+
(prev) => Object.fromEntries(Object.keys(prev).map((k) => [k, ""]))
|
|
23831
|
+
);
|
|
23832
|
+
} catch {
|
|
23833
|
+
setStatus("error");
|
|
23834
|
+
}
|
|
23835
|
+
};
|
|
23836
|
+
if (status === "success") {
|
|
23837
|
+
return /* @__PURE__ */ jsx("div", { className, "data-setto-form-status": "success", children: /* @__PURE__ */ jsx("p", { children: /* @__PURE__ */ jsx(T, { k: `form.${formId}.success` }) }) });
|
|
23838
|
+
}
|
|
23839
|
+
return /* @__PURE__ */ jsxs("form", { className, onSubmit, "data-setto-form": formId, noValidate: true, children: [
|
|
23840
|
+
fields.map((field) => /* @__PURE__ */ jsxs("label", { "data-setto-form-field": field.name, children: [
|
|
23841
|
+
/* @__PURE__ */ jsx("span", { children: /* @__PURE__ */ jsx(T, { k: field.labelKey }) }),
|
|
23842
|
+
field.type === "textarea" ? /* @__PURE__ */ jsx(
|
|
23843
|
+
"textarea",
|
|
23844
|
+
{
|
|
23845
|
+
name: field.name,
|
|
23846
|
+
required: field.required,
|
|
23847
|
+
value: values[field.name] ?? "",
|
|
23848
|
+
placeholder: field.placeholderKey ? t(field.placeholderKey, { defaultValue: "" }) : void 0,
|
|
23849
|
+
onChange: (e) => setField(field.name, e.target.value)
|
|
23850
|
+
}
|
|
23851
|
+
) : /* @__PURE__ */ jsx(
|
|
23852
|
+
"input",
|
|
23853
|
+
{
|
|
23854
|
+
type: field.type,
|
|
23855
|
+
name: field.name,
|
|
23856
|
+
required: field.required,
|
|
23857
|
+
value: values[field.name] ?? "",
|
|
23858
|
+
placeholder: field.placeholderKey ? t(field.placeholderKey, { defaultValue: "" }) : void 0,
|
|
23859
|
+
onChange: (e) => setField(field.name, e.target.value)
|
|
23860
|
+
}
|
|
23861
|
+
)
|
|
23862
|
+
] }, field.name)),
|
|
23863
|
+
/* @__PURE__ */ jsx("button", { type: "submit", disabled: status === "submitting", children: /* @__PURE__ */ jsx(T, { k: `form.${formId}.submit` }) }),
|
|
23864
|
+
status === "error" ? /* @__PURE__ */ jsx("p", { "data-setto-form-status": "error", role: "alert", children: /* @__PURE__ */ jsx(T, { k: `form.${formId}.error` }) }) : null
|
|
23865
|
+
] });
|
|
23866
|
+
}
|
|
23867
|
+
function SettoVideo({ srcKey, posterKey, title, className }) {
|
|
23868
|
+
const { t, i18n } = useTranslation();
|
|
23869
|
+
const { store } = useSetto();
|
|
23870
|
+
const src = store ? store.get(srcKey, i18n.language) : t(srcKey, { defaultValue: "" });
|
|
23871
|
+
const poster = posterKey ? store ? store.get(posterKey, i18n.language) : t(posterKey, { defaultValue: "" }) : void 0;
|
|
23872
|
+
if (!src) {
|
|
23873
|
+
return /* @__PURE__ */ jsx(
|
|
23874
|
+
"div",
|
|
23875
|
+
{
|
|
23876
|
+
className,
|
|
23877
|
+
"data-setto-video": srcKey,
|
|
23878
|
+
"data-setto-video-empty": "1"
|
|
23879
|
+
}
|
|
23880
|
+
);
|
|
23881
|
+
}
|
|
23882
|
+
const embed = toEmbedUrl(src);
|
|
23883
|
+
if (embed) {
|
|
23884
|
+
return /* @__PURE__ */ jsx(
|
|
23885
|
+
"iframe",
|
|
23886
|
+
{
|
|
23887
|
+
className,
|
|
23888
|
+
src: embed,
|
|
23889
|
+
title: title ?? "Video",
|
|
23890
|
+
loading: "lazy",
|
|
23891
|
+
allow: "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",
|
|
23892
|
+
allowFullScreen: true,
|
|
23893
|
+
"data-setto-video": srcKey
|
|
23894
|
+
}
|
|
23895
|
+
);
|
|
23896
|
+
}
|
|
23897
|
+
return /* @__PURE__ */ jsx(
|
|
23898
|
+
"video",
|
|
23899
|
+
{
|
|
23900
|
+
className,
|
|
23901
|
+
src,
|
|
23902
|
+
poster,
|
|
23903
|
+
controls: true,
|
|
23904
|
+
preload: "metadata",
|
|
23905
|
+
"data-setto-video": srcKey
|
|
23906
|
+
}
|
|
23907
|
+
);
|
|
23908
|
+
}
|
|
23909
|
+
function toEmbedUrl(url) {
|
|
23910
|
+
try {
|
|
23911
|
+
const u = new URL(url);
|
|
23912
|
+
if (u.hostname === "youtu.be") {
|
|
23913
|
+
return `https://www.youtube.com/embed/${u.pathname.slice(1)}`;
|
|
23914
|
+
}
|
|
23915
|
+
if (u.hostname.endsWith("youtube.com")) {
|
|
23916
|
+
const v = u.searchParams.get("v");
|
|
23917
|
+
if (v) return `https://www.youtube.com/embed/${v}`;
|
|
23918
|
+
if (u.pathname.startsWith("/embed/")) return url;
|
|
23919
|
+
}
|
|
23920
|
+
if (u.hostname.endsWith("vimeo.com")) {
|
|
23921
|
+
const id = u.pathname.split("/").filter(Boolean).pop();
|
|
23922
|
+
if (id && /^\d+$/.test(id)) return `https://player.vimeo.com/video/${id}`;
|
|
23923
|
+
}
|
|
23924
|
+
return null;
|
|
23925
|
+
} catch {
|
|
23926
|
+
return null;
|
|
23927
|
+
}
|
|
23928
|
+
}
|
|
23929
|
+
const DEFAULT_DURATION = {
|
|
23930
|
+
fadeIn: 600,
|
|
23931
|
+
slideUp: 700,
|
|
23932
|
+
stagger: 700,
|
|
23933
|
+
scrollReveal: 700
|
|
23934
|
+
};
|
|
23935
|
+
function SettoAnimation({
|
|
23936
|
+
preset,
|
|
23937
|
+
delay = 0,
|
|
23938
|
+
duration,
|
|
23939
|
+
className,
|
|
23940
|
+
children
|
|
23941
|
+
}) {
|
|
23942
|
+
const ref = useRef(null);
|
|
23943
|
+
const [visible, setVisible] = useState(preset === "fadeIn" || preset === "slideUp");
|
|
23944
|
+
useEffect(() => {
|
|
23945
|
+
if (preset === "fadeIn" || preset === "slideUp") {
|
|
23946
|
+
const t = window.setTimeout(() => setVisible(true), delay);
|
|
23947
|
+
return () => window.clearTimeout(t);
|
|
23948
|
+
}
|
|
23949
|
+
const el = ref.current;
|
|
23950
|
+
if (!el || typeof IntersectionObserver === "undefined") {
|
|
23951
|
+
setVisible(true);
|
|
23952
|
+
return;
|
|
23953
|
+
}
|
|
23954
|
+
const io = new IntersectionObserver(
|
|
23955
|
+
(entries) => {
|
|
23956
|
+
for (const entry of entries) {
|
|
23957
|
+
if (entry.isIntersecting) {
|
|
23958
|
+
setVisible(true);
|
|
23959
|
+
io.disconnect();
|
|
23960
|
+
}
|
|
23961
|
+
}
|
|
23962
|
+
},
|
|
23963
|
+
{ threshold: 0.15 }
|
|
23964
|
+
);
|
|
23965
|
+
io.observe(el);
|
|
23966
|
+
return () => io.disconnect();
|
|
23967
|
+
}, [preset, delay]);
|
|
23968
|
+
const reduced = typeof window !== "undefined" && window.matchMedia?.("(prefers-reduced-motion: reduce)").matches;
|
|
23969
|
+
const ms = duration ?? DEFAULT_DURATION[preset];
|
|
23970
|
+
const transition = reduced ? void 0 : `opacity ${ms}ms ease-out ${delay}ms, transform ${ms}ms ease-out ${delay}ms`;
|
|
23971
|
+
const hiddenTransform = preset === "slideUp" || preset === "scrollReveal" ? "translateY(24px)" : "none";
|
|
23972
|
+
const style = reduced ? void 0 : {
|
|
23973
|
+
opacity: visible ? 1 : 0,
|
|
23974
|
+
transform: visible ? "none" : hiddenTransform,
|
|
23975
|
+
transition,
|
|
23976
|
+
willChange: "opacity, transform"
|
|
23977
|
+
};
|
|
23978
|
+
if (preset === "stagger") {
|
|
23979
|
+
return /* @__PURE__ */ jsx(
|
|
23980
|
+
"div",
|
|
23981
|
+
{
|
|
23982
|
+
ref,
|
|
23983
|
+
className,
|
|
23984
|
+
"data-setto-animation": preset,
|
|
23985
|
+
"data-setto-animation-visible": visible ? "1" : "0",
|
|
23986
|
+
children: wrapChildrenForStagger(children, visible, ms, delay, reduced)
|
|
23987
|
+
}
|
|
23988
|
+
);
|
|
23989
|
+
}
|
|
23990
|
+
return /* @__PURE__ */ jsx(
|
|
23991
|
+
"div",
|
|
23992
|
+
{
|
|
23993
|
+
ref,
|
|
23994
|
+
className,
|
|
23995
|
+
style,
|
|
23996
|
+
"data-setto-animation": preset,
|
|
23997
|
+
"data-setto-animation-visible": visible ? "1" : "0",
|
|
23998
|
+
children
|
|
23999
|
+
}
|
|
24000
|
+
);
|
|
24001
|
+
}
|
|
24002
|
+
function wrapChildrenForStagger(children, visible, ms, baseDelay, reduced) {
|
|
24003
|
+
if (reduced) return children;
|
|
24004
|
+
const arr = Array.isArray(children) ? children : [children];
|
|
24005
|
+
const step = 80;
|
|
24006
|
+
return arr.map((child, i) => /* @__PURE__ */ jsx(
|
|
24007
|
+
"div",
|
|
24008
|
+
{
|
|
24009
|
+
style: {
|
|
24010
|
+
opacity: visible ? 1 : 0,
|
|
24011
|
+
transform: visible ? "none" : "translateY(16px)",
|
|
24012
|
+
transition: `opacity ${ms}ms ease-out ${baseDelay + i * step}ms, transform ${ms}ms ease-out ${baseDelay + i * step}ms`
|
|
24013
|
+
},
|
|
24014
|
+
children: child
|
|
24015
|
+
},
|
|
24016
|
+
i
|
|
24017
|
+
));
|
|
24018
|
+
}
|
|
23648
24019
|
function authCallbackType() {
|
|
23649
24020
|
const hash = window.location.hash.replace(/^#/, "");
|
|
23650
24021
|
if (!hash) return null;
|
|
@@ -23747,7 +24118,7 @@ function AuthGate({ children }) {
|
|
|
23747
24118
|
clearAuthHashFromUrl();
|
|
23748
24119
|
const { data: sessionData } = await supabase.auth.getSession();
|
|
23749
24120
|
if (sessionData.session) {
|
|
23750
|
-
window.location.replace(
|
|
24121
|
+
window.location.replace(siteHomeUrl());
|
|
23751
24122
|
return;
|
|
23752
24123
|
}
|
|
23753
24124
|
setMode("signin");
|
|
@@ -23937,8 +24308,7 @@ function SettoAdminApp() {
|
|
|
23937
24308
|
return /* @__PURE__ */ jsx(AuthGate, { children: /* @__PURE__ */ jsx(SiteList, {}) });
|
|
23938
24309
|
}
|
|
23939
24310
|
function SiteList() {
|
|
23940
|
-
const { supabase, session, config } = useSetto();
|
|
23941
|
-
const [sites, setSites] = useState(null);
|
|
24311
|
+
const { supabase, session, sites, currentSite, config } = useSetto();
|
|
23942
24312
|
const [error, setError] = useState(null);
|
|
23943
24313
|
const redirectAfterSignIn = useRef(false);
|
|
23944
24314
|
useEffect(() => {
|
|
@@ -23949,33 +24319,13 @@ function SiteList() {
|
|
|
23949
24319
|
});
|
|
23950
24320
|
return () => sub.subscription.unsubscribe();
|
|
23951
24321
|
}, [supabase]);
|
|
23952
|
-
useEffect(() => {
|
|
23953
|
-
if (!session) return;
|
|
23954
|
-
let cancelled = false;
|
|
23955
|
-
supabase.from("sites").select("*").then(({ data, error: err }) => {
|
|
23956
|
-
if (cancelled) return;
|
|
23957
|
-
if (err) {
|
|
23958
|
-
setError(err.message);
|
|
23959
|
-
setSites([]);
|
|
23960
|
-
return;
|
|
23961
|
-
}
|
|
23962
|
-
setSites(data ?? []);
|
|
23963
|
-
});
|
|
23964
|
-
return () => {
|
|
23965
|
-
cancelled = true;
|
|
23966
|
-
};
|
|
23967
|
-
}, [supabase, session]);
|
|
23968
|
-
const currentSite = useMemo(
|
|
23969
|
-
() => sites?.find((s) => s.id === config.siteId) ?? null,
|
|
23970
|
-
[sites, config.siteId]
|
|
23971
|
-
);
|
|
23972
24322
|
useEffect(() => {
|
|
23973
24323
|
if (!redirectAfterSignIn.current || !session || sites === null || isAdminDeploymentView()) {
|
|
23974
24324
|
return;
|
|
23975
24325
|
}
|
|
23976
24326
|
if (currentSite) {
|
|
23977
24327
|
redirectAfterSignIn.current = false;
|
|
23978
|
-
window.location.replace(
|
|
24328
|
+
window.location.replace(siteHomeUrl());
|
|
23979
24329
|
return;
|
|
23980
24330
|
}
|
|
23981
24331
|
redirectAfterSignIn.current = false;
|
|
@@ -24000,8 +24350,8 @@ function SiteList() {
|
|
|
24000
24350
|
" · branch ",
|
|
24001
24351
|
currentSite.branch
|
|
24002
24352
|
] }),
|
|
24003
|
-
/* @__PURE__ */ jsx("div", { style: { display: "flex", gap: 8 }, children: /* @__PURE__ */ jsx("a", { href:
|
|
24004
|
-
/* @__PURE__ */ jsx(DeploymentList, { siteId: currentSite.id })
|
|
24353
|
+
/* @__PURE__ */ jsx("div", { style: { display: "flex", gap: 8 }, children: /* @__PURE__ */ jsx("a", { href: siteHomeUrl(), style: primaryBtnLinkStyle, children: "Begynn å redigere" }) }),
|
|
24354
|
+
/* @__PURE__ */ jsx(DeploymentList, { siteId: currentSite.id, onError: setError })
|
|
24005
24355
|
] }) : sites && sites.length === 0 ? /* @__PURE__ */ jsx("p", { style: mutedStyle, children: "Du er ikke medlem av noen sider ennå. Be administratoren legge deg til." }) : null,
|
|
24006
24356
|
sites && !currentSite && sites.length > 0 ? /* @__PURE__ */ jsxs("section", { style: cardStyle, children: [
|
|
24007
24357
|
/* @__PURE__ */ jsx("h2", { style: { margin: 0, fontSize: 16 }, children: "Andre sider du har tilgang til" }),
|
|
@@ -24019,7 +24369,10 @@ function SiteList() {
|
|
|
24019
24369
|
] })
|
|
24020
24370
|
] });
|
|
24021
24371
|
}
|
|
24022
|
-
function DeploymentList({
|
|
24372
|
+
function DeploymentList({
|
|
24373
|
+
siteId,
|
|
24374
|
+
onError
|
|
24375
|
+
}) {
|
|
24023
24376
|
const { supabase, api, config } = useSetto();
|
|
24024
24377
|
const [rows, setRows] = useState(null);
|
|
24025
24378
|
const [focusId, setFocusId] = useState(focusedDeploymentId);
|
|
@@ -24051,8 +24404,10 @@ function DeploymentList({ siteId }) {
|
|
|
24051
24404
|
}, [api, config.siteId]);
|
|
24052
24405
|
useEffect(() => {
|
|
24053
24406
|
let cancelled = false;
|
|
24054
|
-
supabase.from("deployments").select("*").eq("site_id", siteId).order("started_at", { ascending: false }).limit(10).then(({ data }) => {
|
|
24055
|
-
if (
|
|
24407
|
+
supabase.from("deployments").select("*").eq("site_id", siteId).order("started_at", { ascending: false }).limit(10).then(({ data, error }) => {
|
|
24408
|
+
if (cancelled) return;
|
|
24409
|
+
if (error) onError?.(error.message);
|
|
24410
|
+
setRows(data ?? []);
|
|
24056
24411
|
});
|
|
24057
24412
|
const channel = supabase.channel(`setto-site-deps-${siteId}`).on(
|
|
24058
24413
|
"postgres_changes",
|
|
@@ -24071,7 +24426,7 @@ function DeploymentList({ siteId }) {
|
|
|
24071
24426
|
cancelled = true;
|
|
24072
24427
|
supabase.removeChannel(channel);
|
|
24073
24428
|
};
|
|
24074
|
-
}, [supabase, siteId]);
|
|
24429
|
+
}, [supabase, siteId, onError]);
|
|
24075
24430
|
const showFocusPanel = focusId !== null && (focusRow === null || isDeploymentInProgress(focusRow.status));
|
|
24076
24431
|
if (!rows) return /* @__PURE__ */ jsx("p", { style: mutedStyle, children: "Laster deploys…" });
|
|
24077
24432
|
return /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 12 }, children: [
|
|
@@ -24156,9 +24511,6 @@ function SignOutButton() {
|
|
|
24156
24511
|
const { supabase } = useSetto();
|
|
24157
24512
|
return /* @__PURE__ */ jsx("button", { onClick: () => supabase.auth.signOut(), style: signOutBtnStyle, children: "Logg ut" });
|
|
24158
24513
|
}
|
|
24159
|
-
function editUrl() {
|
|
24160
|
-
return editModeUrl("/");
|
|
24161
|
-
}
|
|
24162
24514
|
const loadingRedirectStyle = {
|
|
24163
24515
|
minHeight: "100dvh",
|
|
24164
24516
|
display: "flex",
|
|
@@ -24264,20 +24616,504 @@ function depDotStyle(status) {
|
|
|
24264
24616
|
flexShrink: 0
|
|
24265
24617
|
};
|
|
24266
24618
|
}
|
|
24619
|
+
const BLOCK_REGISTRY = {
|
|
24620
|
+
section: { allowedChildren: "any", themed: true, selectable: true },
|
|
24621
|
+
block: { allowedChildren: "any", themed: true, selectable: true },
|
|
24622
|
+
container: { allowedChildren: "any", themed: false, selectable: false },
|
|
24623
|
+
animation: { allowedChildren: "any", themed: false, selectable: false },
|
|
24624
|
+
repeater: { allowedChildren: "any", themed: false, selectable: false },
|
|
24625
|
+
text: { allowedChildren: null, themed: false, selectable: false },
|
|
24626
|
+
image: { allowedChildren: null, themed: false, selectable: false },
|
|
24627
|
+
icon: { allowedChildren: null, themed: false, selectable: false },
|
|
24628
|
+
form: { allowedChildren: null, themed: false, selectable: false },
|
|
24629
|
+
video: { allowedChildren: null, themed: false, selectable: false }
|
|
24630
|
+
};
|
|
24631
|
+
const BLOCK_TYPES = Object.keys(
|
|
24632
|
+
BLOCK_REGISTRY
|
|
24633
|
+
);
|
|
24634
|
+
function nodeChildren(node) {
|
|
24635
|
+
switch (node.type) {
|
|
24636
|
+
case "section":
|
|
24637
|
+
case "block":
|
|
24638
|
+
case "container":
|
|
24639
|
+
case "animation":
|
|
24640
|
+
return node.children;
|
|
24641
|
+
case "repeater":
|
|
24642
|
+
return node.itemTemplate;
|
|
24643
|
+
default:
|
|
24644
|
+
return [];
|
|
24645
|
+
}
|
|
24646
|
+
}
|
|
24647
|
+
const FIELD_TYPES = ["text", "email", "tel", "textarea"];
|
|
24648
|
+
const ANIMATION_PRESETS = ["fadeIn", "slideUp", "stagger", "scrollReveal"];
|
|
24649
|
+
function validateBlockTree(input) {
|
|
24650
|
+
const issues = [];
|
|
24651
|
+
if (!isRecord(input)) {
|
|
24652
|
+
return fail("$", "Block tree must be an object");
|
|
24653
|
+
}
|
|
24654
|
+
if (input.version !== "1") {
|
|
24655
|
+
issues.push({ path: "$.version", message: 'Expected version "1"' });
|
|
24656
|
+
}
|
|
24657
|
+
if (!Array.isArray(input.root)) {
|
|
24658
|
+
return fail("$.root", "root must be an array of block nodes");
|
|
24659
|
+
}
|
|
24660
|
+
const seenIds = /* @__PURE__ */ new Set();
|
|
24661
|
+
for (let i = 0; i < input.root.length; i++) {
|
|
24662
|
+
validateNode(input.root[i], `root[${i}]`, seenIds, issues);
|
|
24663
|
+
}
|
|
24664
|
+
return issues.length ? { ok: false, issues } : { ok: true };
|
|
24665
|
+
}
|
|
24666
|
+
function validateNode(raw, path, seenIds, issues) {
|
|
24667
|
+
if (!isRecord(raw)) {
|
|
24668
|
+
issues.push({ path, message: "Node must be an object" });
|
|
24669
|
+
return;
|
|
24670
|
+
}
|
|
24671
|
+
const type = raw.type;
|
|
24672
|
+
if (typeof type !== "string" || !(type in BLOCK_REGISTRY)) {
|
|
24673
|
+
issues.push({ path, message: `Unknown block type "${String(type)}"` });
|
|
24674
|
+
return;
|
|
24675
|
+
}
|
|
24676
|
+
if (typeof raw.id !== "string" || raw.id.length === 0) {
|
|
24677
|
+
issues.push({ path, message: "Node is missing a non-empty string id" });
|
|
24678
|
+
} else if (seenIds.has(raw.id)) {
|
|
24679
|
+
issues.push({ path, message: `Duplicate node id "${raw.id}"` });
|
|
24680
|
+
} else {
|
|
24681
|
+
seenIds.add(raw.id);
|
|
24682
|
+
}
|
|
24683
|
+
if (raw.className !== void 0 && typeof raw.className !== "string") {
|
|
24684
|
+
issues.push({ path: `${path}.className`, message: "className must be a string" });
|
|
24685
|
+
}
|
|
24686
|
+
validateTypeSpecific(raw, type, path, issues);
|
|
24687
|
+
const meta = BLOCK_REGISTRY[type];
|
|
24688
|
+
if (meta.allowedChildren === null && Array.isArray(raw.children) && raw.children.length > 0) {
|
|
24689
|
+
issues.push({ path, message: `Type "${type}" cannot have children` });
|
|
24690
|
+
return;
|
|
24691
|
+
}
|
|
24692
|
+
const children = nodeChildren(raw);
|
|
24693
|
+
if (meta.allowedChildren === null && children.length > 0) {
|
|
24694
|
+
issues.push({ path, message: `Type "${type}" cannot have children` });
|
|
24695
|
+
return;
|
|
24696
|
+
}
|
|
24697
|
+
for (let i = 0; i < children.length; i++) {
|
|
24698
|
+
const child = children[i];
|
|
24699
|
+
const childPath = type === "repeater" ? `${path}.itemTemplate[${i}]` : `${path}.children[${i}]`;
|
|
24700
|
+
validateNode(child, childPath, seenIds, issues);
|
|
24701
|
+
if (meta.allowedChildren !== "any" && meta.allowedChildren !== null && isRecord(child) && typeof child.type === "string" && !meta.allowedChildren.includes(child.type)) {
|
|
24702
|
+
issues.push({
|
|
24703
|
+
path: childPath,
|
|
24704
|
+
message: `Type "${child.type}" not allowed inside "${type}"`
|
|
24705
|
+
});
|
|
24706
|
+
}
|
|
24707
|
+
}
|
|
24708
|
+
}
|
|
24709
|
+
function validateTypeSpecific(raw, type, path, issues) {
|
|
24710
|
+
switch (type) {
|
|
24711
|
+
case "section":
|
|
24712
|
+
requireString(raw, "sectionId", path, issues);
|
|
24713
|
+
requireArray(raw, "children", path, issues);
|
|
24714
|
+
break;
|
|
24715
|
+
case "block":
|
|
24716
|
+
requireString(raw, "blockId", path, issues);
|
|
24717
|
+
requireArray(raw, "children", path, issues);
|
|
24718
|
+
break;
|
|
24719
|
+
case "container":
|
|
24720
|
+
requireArray(raw, "children", path, issues);
|
|
24721
|
+
break;
|
|
24722
|
+
case "text":
|
|
24723
|
+
requireString(raw, "k", path, issues);
|
|
24724
|
+
break;
|
|
24725
|
+
case "image":
|
|
24726
|
+
requireString(raw, "srcKey", path, issues);
|
|
24727
|
+
break;
|
|
24728
|
+
case "icon":
|
|
24729
|
+
requireString(raw, "k", path, issues);
|
|
24730
|
+
break;
|
|
24731
|
+
case "repeater":
|
|
24732
|
+
requireString(raw, "itemsKey", path, issues);
|
|
24733
|
+
requireArray(raw, "itemTemplate", path, issues);
|
|
24734
|
+
if (!isRecord(raw.defaultItem)) {
|
|
24735
|
+
issues.push({
|
|
24736
|
+
path: `${path}.defaultItem`,
|
|
24737
|
+
message: "defaultItem must be an object mapping field names to default strings"
|
|
24738
|
+
});
|
|
24739
|
+
} else {
|
|
24740
|
+
for (const [field, value] of Object.entries(raw.defaultItem)) {
|
|
24741
|
+
if (typeof value !== "string") {
|
|
24742
|
+
issues.push({
|
|
24743
|
+
path: `${path}.defaultItem.${field}`,
|
|
24744
|
+
message: "defaultItem values must be strings"
|
|
24745
|
+
});
|
|
24746
|
+
}
|
|
24747
|
+
}
|
|
24748
|
+
}
|
|
24749
|
+
break;
|
|
24750
|
+
case "form":
|
|
24751
|
+
requireString(raw, "formId", path, issues);
|
|
24752
|
+
if (!Array.isArray(raw.fields) || raw.fields.length === 0) {
|
|
24753
|
+
issues.push({ path: `${path}.fields`, message: "form must declare at least one field" });
|
|
24754
|
+
break;
|
|
24755
|
+
}
|
|
24756
|
+
for (let i = 0; i < raw.fields.length; i++) {
|
|
24757
|
+
validateFormField(raw.fields[i], `${path}.fields[${i}]`, issues);
|
|
24758
|
+
}
|
|
24759
|
+
if (raw.endpoint === void 0 && raw.endpointKey === void 0) {
|
|
24760
|
+
issues.push({
|
|
24761
|
+
path,
|
|
24762
|
+
message: "form must declare either endpoint or endpointKey"
|
|
24763
|
+
});
|
|
24764
|
+
}
|
|
24765
|
+
break;
|
|
24766
|
+
case "video":
|
|
24767
|
+
requireString(raw, "srcKey", path, issues);
|
|
24768
|
+
break;
|
|
24769
|
+
case "animation":
|
|
24770
|
+
requireArray(raw, "children", path, issues);
|
|
24771
|
+
if (typeof raw.preset !== "string" || !ANIMATION_PRESETS.includes(raw.preset)) {
|
|
24772
|
+
issues.push({
|
|
24773
|
+
path: `${path}.preset`,
|
|
24774
|
+
message: `preset must be one of ${ANIMATION_PRESETS.join(", ")}`
|
|
24775
|
+
});
|
|
24776
|
+
}
|
|
24777
|
+
break;
|
|
24778
|
+
}
|
|
24779
|
+
}
|
|
24780
|
+
function validateFormField(raw, path, issues) {
|
|
24781
|
+
if (!isRecord(raw)) {
|
|
24782
|
+
issues.push({ path, message: "field must be an object" });
|
|
24783
|
+
return;
|
|
24784
|
+
}
|
|
24785
|
+
if (typeof raw.name !== "string" || raw.name.length === 0) {
|
|
24786
|
+
issues.push({ path: `${path}.name`, message: "field.name must be a non-empty string" });
|
|
24787
|
+
}
|
|
24788
|
+
if (typeof raw.type !== "string" || !FIELD_TYPES.includes(raw.type)) {
|
|
24789
|
+
issues.push({
|
|
24790
|
+
path: `${path}.type`,
|
|
24791
|
+
message: `field.type must be one of ${FIELD_TYPES.join(", ")}`
|
|
24792
|
+
});
|
|
24793
|
+
}
|
|
24794
|
+
requireString(raw, "labelKey", path, issues);
|
|
24795
|
+
}
|
|
24796
|
+
function requireString(raw, field, path, issues) {
|
|
24797
|
+
const value = raw[field];
|
|
24798
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
24799
|
+
issues.push({ path: `${path}.${field}`, message: `${field} must be a non-empty string` });
|
|
24800
|
+
}
|
|
24801
|
+
}
|
|
24802
|
+
function requireArray(raw, field, path, issues) {
|
|
24803
|
+
if (!Array.isArray(raw[field])) {
|
|
24804
|
+
issues.push({ path: `${path}.${field}`, message: `${field} must be an array` });
|
|
24805
|
+
}
|
|
24806
|
+
}
|
|
24807
|
+
function isRecord(value) {
|
|
24808
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
24809
|
+
}
|
|
24810
|
+
function fail(path, message) {
|
|
24811
|
+
return { ok: false, issues: [{ path, message }] };
|
|
24812
|
+
}
|
|
24813
|
+
function parseBlockTree(input) {
|
|
24814
|
+
const result = validateBlockTree(input);
|
|
24815
|
+
if (!result.ok) {
|
|
24816
|
+
const first = result.issues[0];
|
|
24817
|
+
const detail = first ? `${first.path}: ${first.message}` : "unknown";
|
|
24818
|
+
throw new Error(`Invalid block tree at ${detail}`);
|
|
24819
|
+
}
|
|
24820
|
+
return input;
|
|
24821
|
+
}
|
|
24822
|
+
const IconRegistryContext = createContext(null);
|
|
24823
|
+
const RepeaterScopeContext = createContext(null);
|
|
24824
|
+
function SettoBlockTree({ tree, icons, validate = true }) {
|
|
24825
|
+
if (validate) {
|
|
24826
|
+
const result = validateBlockTree(tree);
|
|
24827
|
+
if (!result.ok) {
|
|
24828
|
+
const first = result.issues[0];
|
|
24829
|
+
const detail = first ? `${first.path}: ${first.message}` : "unknown";
|
|
24830
|
+
throw new Error(`Invalid block tree at ${detail}`);
|
|
24831
|
+
}
|
|
24832
|
+
}
|
|
24833
|
+
return /* @__PURE__ */ jsx(IconRegistryContext.Provider, { value: { icons }, children: tree.root.map((node) => /* @__PURE__ */ jsx(RenderNode, { node }, node.id)) });
|
|
24834
|
+
}
|
|
24835
|
+
function RenderNode({ node }) {
|
|
24836
|
+
switch (node.type) {
|
|
24837
|
+
case "section":
|
|
24838
|
+
return /* @__PURE__ */ jsx(SettoSection, { sectionId: node.sectionId, className: node.className, children: renderChildren(node.children) });
|
|
24839
|
+
case "block":
|
|
24840
|
+
return /* @__PURE__ */ jsx(SettoBlock, { blockId: node.blockId, className: node.className, children: renderChildren(node.children) });
|
|
24841
|
+
case "container":
|
|
24842
|
+
return renderContainer(node.tag ?? "div", node.className, renderChildren(node.children));
|
|
24843
|
+
case "animation":
|
|
24844
|
+
return /* @__PURE__ */ jsx(
|
|
24845
|
+
SettoAnimation,
|
|
24846
|
+
{
|
|
24847
|
+
preset: node.preset,
|
|
24848
|
+
delay: node.delay,
|
|
24849
|
+
duration: node.duration,
|
|
24850
|
+
className: node.className,
|
|
24851
|
+
children: renderChildren(node.children)
|
|
24852
|
+
}
|
|
24853
|
+
);
|
|
24854
|
+
case "repeater":
|
|
24855
|
+
return /* @__PURE__ */ jsx(
|
|
24856
|
+
SettoRepeater,
|
|
24857
|
+
{
|
|
24858
|
+
itemsKey: node.itemsKey,
|
|
24859
|
+
defaultItem: node.defaultItem,
|
|
24860
|
+
itemLabel: node.itemLabel,
|
|
24861
|
+
className: node.className,
|
|
24862
|
+
children: (itemKey) => /* @__PURE__ */ jsx(
|
|
24863
|
+
RepeaterScopeContext.Provider,
|
|
24864
|
+
{
|
|
24865
|
+
value: { itemsKey: node.itemsKey, itemKey },
|
|
24866
|
+
children: renderChildren(node.itemTemplate)
|
|
24867
|
+
}
|
|
24868
|
+
)
|
|
24869
|
+
}
|
|
24870
|
+
);
|
|
24871
|
+
case "text":
|
|
24872
|
+
return /* @__PURE__ */ jsx(TextLeaf, { node });
|
|
24873
|
+
case "image":
|
|
24874
|
+
return /* @__PURE__ */ jsx(ImageLeaf, { node });
|
|
24875
|
+
case "icon":
|
|
24876
|
+
return /* @__PURE__ */ jsx(IconLeaf, { node });
|
|
24877
|
+
case "form":
|
|
24878
|
+
return /* @__PURE__ */ jsx(
|
|
24879
|
+
SettoForm,
|
|
24880
|
+
{
|
|
24881
|
+
formId: node.formId,
|
|
24882
|
+
fields: node.fields,
|
|
24883
|
+
endpoint: node.endpoint,
|
|
24884
|
+
endpointKey: node.endpointKey,
|
|
24885
|
+
className: node.className
|
|
24886
|
+
}
|
|
24887
|
+
);
|
|
24888
|
+
case "video":
|
|
24889
|
+
return /* @__PURE__ */ jsx(VideoLeaf, { node });
|
|
24890
|
+
}
|
|
24891
|
+
}
|
|
24892
|
+
function renderChildren(children) {
|
|
24893
|
+
return children.map((child) => /* @__PURE__ */ jsx(RenderNode, { node: child }, child.id));
|
|
24894
|
+
}
|
|
24895
|
+
function TextLeaf({ node }) {
|
|
24896
|
+
const k = useScopedKey(node.k);
|
|
24897
|
+
const inner = /* @__PURE__ */ jsx(T, { k });
|
|
24898
|
+
if (!node.as) {
|
|
24899
|
+
return node.className ? /* @__PURE__ */ jsx("span", { className: node.className, children: inner }) : inner;
|
|
24900
|
+
}
|
|
24901
|
+
return renderTag(node.as, node.className, inner);
|
|
24902
|
+
}
|
|
24903
|
+
function ImageLeaf({ node }) {
|
|
24904
|
+
const srcKey = useScopedKey(node.srcKey);
|
|
24905
|
+
return /* @__PURE__ */ jsx(SettoImage, { srcKey, alt: node.alt, className: node.className });
|
|
24906
|
+
}
|
|
24907
|
+
function VideoLeaf({ node }) {
|
|
24908
|
+
const srcKey = useScopedKey(node.srcKey);
|
|
24909
|
+
const posterKey = useScopedKey(node.posterKey ?? "");
|
|
24910
|
+
return /* @__PURE__ */ jsx(
|
|
24911
|
+
SettoVideo,
|
|
24912
|
+
{
|
|
24913
|
+
srcKey,
|
|
24914
|
+
posterKey: node.posterKey ? posterKey : void 0,
|
|
24915
|
+
title: node.title,
|
|
24916
|
+
className: node.className
|
|
24917
|
+
}
|
|
24918
|
+
);
|
|
24919
|
+
}
|
|
24920
|
+
function IconLeaf({ node }) {
|
|
24921
|
+
const registry = useContext(IconRegistryContext);
|
|
24922
|
+
const k = useScopedKey(node.k);
|
|
24923
|
+
if (!registry) {
|
|
24924
|
+
throw new Error("SettoBlockTree must be mounted with an `icons` prop for icon nodes");
|
|
24925
|
+
}
|
|
24926
|
+
return /* @__PURE__ */ jsx(
|
|
24927
|
+
SettoIcon,
|
|
24928
|
+
{
|
|
24929
|
+
k,
|
|
24930
|
+
icons: registry.icons,
|
|
24931
|
+
size: node.size,
|
|
24932
|
+
className: node.className
|
|
24933
|
+
}
|
|
24934
|
+
);
|
|
24935
|
+
}
|
|
24936
|
+
function useScopedKey(key) {
|
|
24937
|
+
const scope = useContext(RepeaterScopeContext);
|
|
24938
|
+
if (!scope) return key;
|
|
24939
|
+
return `${scope.itemsKey}.${scope.itemKey}.${key}`;
|
|
24940
|
+
}
|
|
24941
|
+
function renderTag(tag, className, children) {
|
|
24942
|
+
switch (tag) {
|
|
24943
|
+
case "h1":
|
|
24944
|
+
return /* @__PURE__ */ jsx("h1", { className, children });
|
|
24945
|
+
case "h2":
|
|
24946
|
+
return /* @__PURE__ */ jsx("h2", { className, children });
|
|
24947
|
+
case "h3":
|
|
24948
|
+
return /* @__PURE__ */ jsx("h3", { className, children });
|
|
24949
|
+
case "h4":
|
|
24950
|
+
return /* @__PURE__ */ jsx("h4", { className, children });
|
|
24951
|
+
case "h5":
|
|
24952
|
+
return /* @__PURE__ */ jsx("h5", { className, children });
|
|
24953
|
+
case "h6":
|
|
24954
|
+
return /* @__PURE__ */ jsx("h6", { className, children });
|
|
24955
|
+
case "p":
|
|
24956
|
+
return /* @__PURE__ */ jsx("p", { className, children });
|
|
24957
|
+
case "span":
|
|
24958
|
+
return /* @__PURE__ */ jsx("span", { className, children });
|
|
24959
|
+
case "div":
|
|
24960
|
+
return /* @__PURE__ */ jsx("div", { className, children });
|
|
24961
|
+
case "label":
|
|
24962
|
+
return /* @__PURE__ */ jsx("label", { className, children });
|
|
24963
|
+
case "em":
|
|
24964
|
+
return /* @__PURE__ */ jsx("em", { className, children });
|
|
24965
|
+
case "strong":
|
|
24966
|
+
return /* @__PURE__ */ jsx("strong", { className, children });
|
|
24967
|
+
}
|
|
24968
|
+
}
|
|
24969
|
+
function renderContainer(tag, className, children) {
|
|
24970
|
+
switch (tag) {
|
|
24971
|
+
case "div":
|
|
24972
|
+
return /* @__PURE__ */ jsx("div", { className, children });
|
|
24973
|
+
case "section":
|
|
24974
|
+
return /* @__PURE__ */ jsx("section", { className, children });
|
|
24975
|
+
case "header":
|
|
24976
|
+
return /* @__PURE__ */ jsx("header", { className, children });
|
|
24977
|
+
case "footer":
|
|
24978
|
+
return /* @__PURE__ */ jsx("footer", { className, children });
|
|
24979
|
+
case "main":
|
|
24980
|
+
return /* @__PURE__ */ jsx("main", { className, children });
|
|
24981
|
+
case "aside":
|
|
24982
|
+
return /* @__PURE__ */ jsx("aside", { className, children });
|
|
24983
|
+
case "nav":
|
|
24984
|
+
return /* @__PURE__ */ jsx("nav", { className, children });
|
|
24985
|
+
case "article":
|
|
24986
|
+
return /* @__PURE__ */ jsx("article", { className, children });
|
|
24987
|
+
case "ul":
|
|
24988
|
+
return /* @__PURE__ */ jsx("ul", { className, children });
|
|
24989
|
+
case "ol":
|
|
24990
|
+
return /* @__PURE__ */ jsx("ol", { className, children });
|
|
24991
|
+
case "li":
|
|
24992
|
+
return /* @__PURE__ */ jsx("li", { className, children });
|
|
24993
|
+
case "p":
|
|
24994
|
+
return /* @__PURE__ */ jsx("p", { className, children });
|
|
24995
|
+
case "span":
|
|
24996
|
+
return /* @__PURE__ */ jsx("span", { className, children });
|
|
24997
|
+
}
|
|
24998
|
+
}
|
|
24999
|
+
function extractContentKeys(tree) {
|
|
25000
|
+
const out = {
|
|
25001
|
+
keys: /* @__PURE__ */ new Set(),
|
|
25002
|
+
themeIds: /* @__PURE__ */ new Set(),
|
|
25003
|
+
repeaters: []
|
|
25004
|
+
};
|
|
25005
|
+
for (const node of tree.root) {
|
|
25006
|
+
walk(node, out, null);
|
|
25007
|
+
}
|
|
25008
|
+
return out;
|
|
25009
|
+
}
|
|
25010
|
+
function walk(node, out, repeaterCtx) {
|
|
25011
|
+
switch (node.type) {
|
|
25012
|
+
case "section":
|
|
25013
|
+
out.themeIds.add(node.sectionId);
|
|
25014
|
+
for (const child of node.children) walk(child, out, repeaterCtx);
|
|
25015
|
+
return;
|
|
25016
|
+
case "block":
|
|
25017
|
+
out.themeIds.add(node.blockId);
|
|
25018
|
+
for (const child of node.children) walk(child, out, repeaterCtx);
|
|
25019
|
+
return;
|
|
25020
|
+
case "container":
|
|
25021
|
+
case "animation":
|
|
25022
|
+
for (const child of node.children) walk(child, out, repeaterCtx);
|
|
25023
|
+
return;
|
|
25024
|
+
case "text":
|
|
25025
|
+
out.keys.add(scopedKey(node.k, repeaterCtx));
|
|
25026
|
+
return;
|
|
25027
|
+
case "image":
|
|
25028
|
+
out.keys.add(scopedKey(node.srcKey, repeaterCtx));
|
|
25029
|
+
return;
|
|
25030
|
+
case "icon":
|
|
25031
|
+
out.keys.add(scopedKey(node.k, repeaterCtx));
|
|
25032
|
+
return;
|
|
25033
|
+
case "video":
|
|
25034
|
+
out.keys.add(scopedKey(node.srcKey, repeaterCtx));
|
|
25035
|
+
if (node.posterKey) out.keys.add(scopedKey(node.posterKey, repeaterCtx));
|
|
25036
|
+
return;
|
|
25037
|
+
case "form":
|
|
25038
|
+
out.keys.add(`form.${node.formId}.submit`);
|
|
25039
|
+
out.keys.add(`form.${node.formId}.success`);
|
|
25040
|
+
if (node.endpointKey) out.keys.add(node.endpointKey);
|
|
25041
|
+
for (const field of node.fields) {
|
|
25042
|
+
out.keys.add(field.labelKey);
|
|
25043
|
+
if (field.placeholderKey) out.keys.add(field.placeholderKey);
|
|
25044
|
+
}
|
|
25045
|
+
return;
|
|
25046
|
+
case "repeater":
|
|
25047
|
+
recordRepeater(node, out);
|
|
25048
|
+
for (const child of node.itemTemplate) {
|
|
25049
|
+
walk(child, out, { itemsKey: node.itemsKey });
|
|
25050
|
+
}
|
|
25051
|
+
return;
|
|
25052
|
+
}
|
|
25053
|
+
}
|
|
25054
|
+
function scopedKey(key, repeaterCtx) {
|
|
25055
|
+
if (!repeaterCtx) return key;
|
|
25056
|
+
return `${repeaterCtx.itemsKey}.*.${key}`;
|
|
25057
|
+
}
|
|
25058
|
+
function recordRepeater(node, out) {
|
|
25059
|
+
const fields = Object.keys(node.defaultItem);
|
|
25060
|
+
const itemKeys = [];
|
|
25061
|
+
collectRelativeKeys(node.itemTemplate, "", itemKeys);
|
|
25062
|
+
out.repeaters.push({
|
|
25063
|
+
itemsKey: node.itemsKey,
|
|
25064
|
+
fields,
|
|
25065
|
+
itemKeys
|
|
25066
|
+
});
|
|
25067
|
+
}
|
|
25068
|
+
function collectRelativeKeys(nodes, prefix, out) {
|
|
25069
|
+
for (const node of nodes) {
|
|
25070
|
+
switch (node.type) {
|
|
25071
|
+
case "text":
|
|
25072
|
+
case "icon":
|
|
25073
|
+
out.push(node.k);
|
|
25074
|
+
break;
|
|
25075
|
+
case "image":
|
|
25076
|
+
out.push(node.srcKey);
|
|
25077
|
+
break;
|
|
25078
|
+
case "video":
|
|
25079
|
+
out.push(node.srcKey);
|
|
25080
|
+
if (node.posterKey) {
|
|
25081
|
+
out.push(node.posterKey);
|
|
25082
|
+
}
|
|
25083
|
+
break;
|
|
25084
|
+
case "section":
|
|
25085
|
+
case "block":
|
|
25086
|
+
case "container":
|
|
25087
|
+
case "animation":
|
|
25088
|
+
collectRelativeKeys(node.children, prefix, out);
|
|
25089
|
+
break;
|
|
25090
|
+
}
|
|
25091
|
+
}
|
|
25092
|
+
}
|
|
24267
25093
|
export {
|
|
24268
25094
|
AuthGate,
|
|
25095
|
+
BLOCK_REGISTRY,
|
|
25096
|
+
BLOCK_TYPES,
|
|
24269
25097
|
GuestEditProvider,
|
|
24270
25098
|
SETTO_BASE,
|
|
24271
25099
|
SettoAdminApp,
|
|
25100
|
+
SettoAnimation,
|
|
24272
25101
|
SettoBlock,
|
|
25102
|
+
SettoBlockTree,
|
|
25103
|
+
SettoForm,
|
|
24273
25104
|
SettoIcon,
|
|
24274
25105
|
SettoImage,
|
|
24275
25106
|
SettoProvider,
|
|
24276
25107
|
SettoRepeater,
|
|
24277
25108
|
SettoSection,
|
|
25109
|
+
SettoVideo,
|
|
24278
25110
|
T,
|
|
25111
|
+
extractContentKeys,
|
|
25112
|
+
nodeChildren,
|
|
25113
|
+
parseBlockTree,
|
|
24279
25114
|
useGuestEdit,
|
|
24280
25115
|
useSectionTheme,
|
|
24281
|
-
useSetto
|
|
25116
|
+
useSetto,
|
|
25117
|
+
validateBlockTree
|
|
24282
25118
|
};
|
|
24283
25119
|
//# sourceMappingURL=setto-client.js.map
|