@sevenfold/setto-client 0.15.0 → 0.17.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/dist/LanguageSwitcher.d.ts +40 -0
- package/dist/chrome.d.ts +3 -3
- package/dist/index.d.ts +5 -3
- package/dist/lib/languages.d.ts +43 -0
- package/dist/modules/forms/field-tools.d.ts +35 -0
- package/dist/modules/forms/index.d.ts +1 -1
- package/dist/setto-client.js +266 -690
- package/dist/setto-client.js.map +1 -1
- package/dist/types.d.ts +8 -0
- package/package.json +1 -1
- package/dist/edit-mode/build-status.d.ts +0 -13
- package/dist/edit-mode/deployment-status.d.ts +0 -37
- package/dist/modules/forms/field-editor.d.ts +0 -29
- package/dist/preview-bar.d.ts +0 -29
package/dist/setto-client.js
CHANGED
|
@@ -1,9 +1,242 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { jsxs, jsx, Fragment } from "react/jsx-runtime";
|
|
2
2
|
import * as React from "react";
|
|
3
3
|
import { useState, useRef, useEffect, useMemo, createContext, useContext, useCallback, useLayoutEffect, useSyncExternalStore, forwardRef, Fragment as Fragment$1, useId, useInsertionEffect } from "react";
|
|
4
4
|
import { useTranslation } from "react-i18next";
|
|
5
5
|
import * as ReactDOM from "react-dom";
|
|
6
6
|
import { createPortal } from "react-dom";
|
|
7
|
+
const STORAGE_KEY$1 = "setto.lang";
|
|
8
|
+
const DEFAULT_LANGUAGE = "no";
|
|
9
|
+
const ENDONYMS = {
|
|
10
|
+
no: "Norsk",
|
|
11
|
+
nb: "Norsk",
|
|
12
|
+
nn: "Nynorsk",
|
|
13
|
+
en: "English",
|
|
14
|
+
sv: "Svenska",
|
|
15
|
+
da: "Dansk",
|
|
16
|
+
de: "Deutsch",
|
|
17
|
+
fr: "Français",
|
|
18
|
+
es: "Español",
|
|
19
|
+
it: "Italiano",
|
|
20
|
+
nl: "Nederlands",
|
|
21
|
+
pl: "Polski",
|
|
22
|
+
pt: "Português",
|
|
23
|
+
fi: "Suomi",
|
|
24
|
+
is: "Íslenska",
|
|
25
|
+
lt: "Lietuvių",
|
|
26
|
+
uk: "Українська",
|
|
27
|
+
ru: "Русский",
|
|
28
|
+
ar: "العربية",
|
|
29
|
+
tr: "Türkçe",
|
|
30
|
+
so: "Soomaali",
|
|
31
|
+
ti: "ትግርኛ",
|
|
32
|
+
th: "ไทย",
|
|
33
|
+
vi: "Tiếng Việt",
|
|
34
|
+
zh: "中文",
|
|
35
|
+
ja: "日本語"
|
|
36
|
+
};
|
|
37
|
+
function languageName(tag) {
|
|
38
|
+
const base = tag.toLowerCase().split("-")[0] ?? tag;
|
|
39
|
+
return ENDONYMS[base] ?? tag.toUpperCase();
|
|
40
|
+
}
|
|
41
|
+
function siteLanguages(configured, primary = DEFAULT_LANGUAGE) {
|
|
42
|
+
const out = [primary];
|
|
43
|
+
for (const tag of configured ?? []) {
|
|
44
|
+
if (tag && !out.includes(tag)) out.push(tag);
|
|
45
|
+
}
|
|
46
|
+
return out;
|
|
47
|
+
}
|
|
48
|
+
function readStoredLanguage() {
|
|
49
|
+
try {
|
|
50
|
+
return window.localStorage.getItem(STORAGE_KEY$1);
|
|
51
|
+
} catch {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function storeLanguage(tag) {
|
|
56
|
+
try {
|
|
57
|
+
window.localStorage.setItem(STORAGE_KEY$1, tag);
|
|
58
|
+
} catch {
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function pickInitialLanguage(available, browserLanguages = typeof navigator === "undefined" ? [] : navigator.languages ?? [navigator.language], stored = null) {
|
|
62
|
+
const fallback = available[0] ?? DEFAULT_LANGUAGE;
|
|
63
|
+
if (stored && available.includes(stored)) return stored;
|
|
64
|
+
for (const wanted of browserLanguages) {
|
|
65
|
+
const base = wanted.toLowerCase().split("-")[0];
|
|
66
|
+
if (!base) continue;
|
|
67
|
+
const hit = available.find((tag) => tag.toLowerCase().split("-")[0] === base);
|
|
68
|
+
if (hit) return hit;
|
|
69
|
+
if ((base === "nb" || base === "nn" || base === "no") && available.includes("no")) {
|
|
70
|
+
return "no";
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return fallback;
|
|
74
|
+
}
|
|
75
|
+
const INLINE_MARKER = "data-setto-languages-inline";
|
|
76
|
+
function SettoLanguages({ languages, inline = false }) {
|
|
77
|
+
const { i18n } = useTranslation();
|
|
78
|
+
const [open, setOpen] = useState(false);
|
|
79
|
+
const ref = useRef(null);
|
|
80
|
+
useEffect(() => {
|
|
81
|
+
if (!inline) return;
|
|
82
|
+
const root = document.documentElement;
|
|
83
|
+
const count = Number(root.getAttribute(INLINE_MARKER) ?? "0") + 1;
|
|
84
|
+
root.setAttribute(INLINE_MARKER, String(count));
|
|
85
|
+
return () => {
|
|
86
|
+
const left = Number(root.getAttribute(INLINE_MARKER) ?? "1") - 1;
|
|
87
|
+
if (left > 0) root.setAttribute(INLINE_MARKER, String(left));
|
|
88
|
+
else root.removeAttribute(INLINE_MARKER);
|
|
89
|
+
};
|
|
90
|
+
}, [inline]);
|
|
91
|
+
useEffect(() => {
|
|
92
|
+
if (!open) return;
|
|
93
|
+
const onDown = (e) => {
|
|
94
|
+
if (!ref.current?.contains(e.target)) setOpen(false);
|
|
95
|
+
};
|
|
96
|
+
const onKey = (e) => {
|
|
97
|
+
if (e.key === "Escape") setOpen(false);
|
|
98
|
+
};
|
|
99
|
+
document.addEventListener("mousedown", onDown);
|
|
100
|
+
document.addEventListener("keydown", onKey);
|
|
101
|
+
return () => {
|
|
102
|
+
document.removeEventListener("mousedown", onDown);
|
|
103
|
+
document.removeEventListener("keydown", onKey);
|
|
104
|
+
};
|
|
105
|
+
}, [open]);
|
|
106
|
+
if (languages.length < 2) return null;
|
|
107
|
+
const current = i18n.language;
|
|
108
|
+
const choose = (tag) => {
|
|
109
|
+
setOpen(false);
|
|
110
|
+
if (tag === current) return;
|
|
111
|
+
storeLanguage(tag);
|
|
112
|
+
void i18n.changeLanguage(tag);
|
|
113
|
+
document.documentElement.lang = tag;
|
|
114
|
+
};
|
|
115
|
+
return /* @__PURE__ */ jsxs("div", { ref, style: inline ? inlineWrapStyle : floatingWrapStyle, children: [
|
|
116
|
+
/* @__PURE__ */ jsxs(
|
|
117
|
+
"button",
|
|
118
|
+
{
|
|
119
|
+
type: "button",
|
|
120
|
+
onClick: () => setOpen((o) => !o),
|
|
121
|
+
"aria-expanded": open,
|
|
122
|
+
"aria-haspopup": "listbox",
|
|
123
|
+
"aria-label": `Språk: ${languageName(current)}`,
|
|
124
|
+
style: triggerStyle,
|
|
125
|
+
children: [
|
|
126
|
+
/* @__PURE__ */ jsx(GlobeIcon, {}),
|
|
127
|
+
languageName(current)
|
|
128
|
+
]
|
|
129
|
+
}
|
|
130
|
+
),
|
|
131
|
+
open ? /* @__PURE__ */ jsx("ul", { role: "listbox", style: inline ? inlineListStyle : floatingListStyle, children: languages.map((tag) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(
|
|
132
|
+
"button",
|
|
133
|
+
{
|
|
134
|
+
type: "button",
|
|
135
|
+
role: "option",
|
|
136
|
+
"aria-selected": tag === current,
|
|
137
|
+
onClick: () => choose(tag),
|
|
138
|
+
style: {
|
|
139
|
+
...optionStyle,
|
|
140
|
+
fontWeight: tag === current ? 600 : 400
|
|
141
|
+
},
|
|
142
|
+
children: languageName(tag)
|
|
143
|
+
}
|
|
144
|
+
) }, tag)) }) : null
|
|
145
|
+
] });
|
|
146
|
+
}
|
|
147
|
+
function AutoLanguageSwitcher({ languages }) {
|
|
148
|
+
const [inlineElsewhere, setInlineElsewhere] = useState(false);
|
|
149
|
+
useEffect(() => {
|
|
150
|
+
const check = () => setInlineElsewhere(document.documentElement.hasAttribute(INLINE_MARKER));
|
|
151
|
+
check();
|
|
152
|
+
const observer = new MutationObserver(check);
|
|
153
|
+
observer.observe(document.documentElement, {
|
|
154
|
+
attributes: true,
|
|
155
|
+
attributeFilter: [INLINE_MARKER]
|
|
156
|
+
});
|
|
157
|
+
return () => observer.disconnect();
|
|
158
|
+
}, []);
|
|
159
|
+
if (inlineElsewhere || languages.length < 2) return null;
|
|
160
|
+
return /* @__PURE__ */ jsx(SettoLanguages, { languages });
|
|
161
|
+
}
|
|
162
|
+
function GlobeIcon() {
|
|
163
|
+
return /* @__PURE__ */ jsxs(
|
|
164
|
+
"svg",
|
|
165
|
+
{
|
|
166
|
+
width: "15",
|
|
167
|
+
height: "15",
|
|
168
|
+
viewBox: "0 0 24 24",
|
|
169
|
+
fill: "none",
|
|
170
|
+
stroke: "currentColor",
|
|
171
|
+
strokeWidth: "1.8",
|
|
172
|
+
strokeLinecap: "round",
|
|
173
|
+
strokeLinejoin: "round",
|
|
174
|
+
"aria-hidden": "true",
|
|
175
|
+
children: [
|
|
176
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "9" }),
|
|
177
|
+
/* @__PURE__ */ jsx("path", { d: "M3 12h18M12 3c2.4 2.6 3.6 5.6 3.6 9s-1.2 6.4-3.6 9c-2.4-2.6-3.6-5.6-3.6-9S9.6 5.6 12 3z" })
|
|
178
|
+
]
|
|
179
|
+
}
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
const floatingWrapStyle = {
|
|
183
|
+
position: "fixed",
|
|
184
|
+
left: "max(16px, env(safe-area-inset-left))",
|
|
185
|
+
bottom: "max(16px, env(safe-area-inset-bottom))",
|
|
186
|
+
zIndex: 2147482e3,
|
|
187
|
+
fontFamily: 'system-ui, -apple-system, "Segoe UI", sans-serif'
|
|
188
|
+
};
|
|
189
|
+
const inlineWrapStyle = {
|
|
190
|
+
position: "relative",
|
|
191
|
+
display: "inline-block",
|
|
192
|
+
fontFamily: "inherit"
|
|
193
|
+
};
|
|
194
|
+
const triggerStyle = {
|
|
195
|
+
display: "inline-flex",
|
|
196
|
+
alignItems: "center",
|
|
197
|
+
gap: 7,
|
|
198
|
+
minHeight: 40,
|
|
199
|
+
padding: "8px 14px",
|
|
200
|
+
borderRadius: 999,
|
|
201
|
+
border: "1px solid rgba(127, 127, 127, 0.32)",
|
|
202
|
+
// Translucent over whatever the site put behind it, and legible either way.
|
|
203
|
+
background: "rgba(255, 255, 255, 0.82)",
|
|
204
|
+
backdropFilter: "blur(8px)",
|
|
205
|
+
WebkitBackdropFilter: "blur(8px)",
|
|
206
|
+
color: "#1a1a1a",
|
|
207
|
+
fontSize: 13.5,
|
|
208
|
+
fontWeight: 500,
|
|
209
|
+
lineHeight: 1,
|
|
210
|
+
cursor: "pointer",
|
|
211
|
+
boxShadow: "0 4px 14px rgba(0, 0, 0, 0.12)"
|
|
212
|
+
};
|
|
213
|
+
const listBase = {
|
|
214
|
+
position: "absolute",
|
|
215
|
+
margin: 0,
|
|
216
|
+
padding: 4,
|
|
217
|
+
listStyle: "none",
|
|
218
|
+
minWidth: 150,
|
|
219
|
+
borderRadius: 12,
|
|
220
|
+
border: "1px solid rgba(127, 127, 127, 0.24)",
|
|
221
|
+
background: "#ffffff",
|
|
222
|
+
boxShadow: "0 12px 32px rgba(0, 0, 0, 0.18)",
|
|
223
|
+
zIndex: 1
|
|
224
|
+
};
|
|
225
|
+
const floatingListStyle = { ...listBase, left: 0, bottom: "calc(100% + 8px)" };
|
|
226
|
+
const inlineListStyle = { ...listBase, left: 0, top: "calc(100% + 8px)" };
|
|
227
|
+
const optionStyle = {
|
|
228
|
+
display: "block",
|
|
229
|
+
width: "100%",
|
|
230
|
+
minHeight: 40,
|
|
231
|
+
padding: "9px 12px",
|
|
232
|
+
border: 0,
|
|
233
|
+
borderRadius: 8,
|
|
234
|
+
background: "transparent",
|
|
235
|
+
color: "#1a1a1a",
|
|
236
|
+
fontSize: 13.5,
|
|
237
|
+
textAlign: "left",
|
|
238
|
+
cursor: "pointer"
|
|
239
|
+
};
|
|
7
240
|
function __rest(s, e) {
|
|
8
241
|
var t = {};
|
|
9
242
|
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
@@ -21431,11 +21664,11 @@ function useSettoChromeSlot(name) {
|
|
|
21431
21664
|
slotListeners.delete(listener);
|
|
21432
21665
|
};
|
|
21433
21666
|
}, []);
|
|
21434
|
-
const Offered = slots.get(name)
|
|
21667
|
+
const Offered = slots.get(name);
|
|
21435
21668
|
return useMemo(() => {
|
|
21436
21669
|
if (!Offered) return null;
|
|
21437
|
-
return function SettoChromeSlot() {
|
|
21438
|
-
return /* @__PURE__ */ jsx(ChromeBoundary, { onError: () => retireSlot(name), children:
|
|
21670
|
+
return function SettoChromeSlot(props) {
|
|
21671
|
+
return /* @__PURE__ */ jsx(ChromeBoundary, { onError: () => retireSlot(name), children: React.createElement(Offered, props) });
|
|
21439
21672
|
};
|
|
21440
21673
|
}, [Offered, name]);
|
|
21441
21674
|
}
|
|
@@ -21552,7 +21785,7 @@ function withoutCheckoutIntent() {
|
|
|
21552
21785
|
}
|
|
21553
21786
|
const FAB_SIZE = 52;
|
|
21554
21787
|
const FAB_INSET = 16;
|
|
21555
|
-
const STYLE_ID$
|
|
21788
|
+
const STYLE_ID$2 = "setto-edit-layout";
|
|
21556
21789
|
const HTML_CLASS = "setto-edit-mode";
|
|
21557
21790
|
function useSettoDocumentLayout(active) {
|
|
21558
21791
|
useEffect(() => {
|
|
@@ -21561,10 +21794,10 @@ function useSettoDocumentLayout(active) {
|
|
|
21561
21794
|
html.classList.add(HTML_CLASS);
|
|
21562
21795
|
html.style.setProperty("--setto-fab-size", `${FAB_SIZE}px`);
|
|
21563
21796
|
html.style.setProperty("--setto-fab-inset", `${FAB_INSET}px`);
|
|
21564
|
-
let styleEl = document.getElementById(STYLE_ID$
|
|
21797
|
+
let styleEl = document.getElementById(STYLE_ID$2);
|
|
21565
21798
|
if (!styleEl) {
|
|
21566
21799
|
styleEl = document.createElement("style");
|
|
21567
|
-
styleEl.id = STYLE_ID$
|
|
21800
|
+
styleEl.id = STYLE_ID$2;
|
|
21568
21801
|
styleEl.textContent = `
|
|
21569
21802
|
html.setto-edit-mode {
|
|
21570
21803
|
width: 100%;
|
|
@@ -21767,6 +22000,16 @@ function SettoProvider({ config, children }) {
|
|
|
21767
22000
|
}, []);
|
|
21768
22001
|
const access = useSiteAccess(supabase, session, config.siteId);
|
|
21769
22002
|
const { i18n } = useTranslation();
|
|
22003
|
+
const languages = useMemo(
|
|
22004
|
+
() => siteLanguages(config.languages, config.languages?.[0]),
|
|
22005
|
+
[config.languages]
|
|
22006
|
+
);
|
|
22007
|
+
useEffect(() => {
|
|
22008
|
+
if (!i18n || languages.length < 2) return;
|
|
22009
|
+
const wanted = pickInitialLanguage(languages, void 0, readStoredLanguage());
|
|
22010
|
+
if (wanted && wanted !== i18n.language) void i18n.changeLanguage(wanted);
|
|
22011
|
+
document.documentElement.lang = wanted;
|
|
22012
|
+
}, [i18n, languages]);
|
|
21770
22013
|
const storeRef = useRef(null);
|
|
21771
22014
|
const themeStoreRef = useRef(null);
|
|
21772
22015
|
const assetStoreRef = useRef(null);
|
|
@@ -21809,7 +22052,8 @@ function SettoProvider({ config, children }) {
|
|
|
21809
22052
|
const editShellActive = editMode && !!value.store;
|
|
21810
22053
|
return /* @__PURE__ */ jsx(SettoContext.Provider, { value, children: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
21811
22054
|
/* @__PURE__ */ jsx(EditModeShell, { active: editShellActive, themeStore: value.themeStore, children }),
|
|
21812
|
-
/* @__PURE__ */ jsx(SettoChrome, {})
|
|
22055
|
+
/* @__PURE__ */ jsx(SettoChrome, {}),
|
|
22056
|
+
/* @__PURE__ */ jsx(AutoLanguageSwitcher, { languages })
|
|
21813
22057
|
] }) });
|
|
21814
22058
|
}
|
|
21815
22059
|
function useSetto() {
|
|
@@ -21838,202 +22082,6 @@ function useGuestEdit() {
|
|
|
21838
22082
|
}
|
|
21839
22083
|
};
|
|
21840
22084
|
}
|
|
21841
|
-
const PREVIEW_HOST = /(^|\.)site\.setto\.no$/i;
|
|
21842
|
-
function isPreviewHost(hostname) {
|
|
21843
|
-
return PREVIEW_HOST.test(hostname);
|
|
21844
|
-
}
|
|
21845
|
-
function previewBarState(input) {
|
|
21846
|
-
if (!input.onPreview || input.authLoading) return "hidden";
|
|
21847
|
-
if (input.hasSession && !input.sitesLoaded) return "hidden";
|
|
21848
|
-
if (!input.hasSession && input.soldPublic === null) return "hidden";
|
|
21849
|
-
const bought = input.paidMarker || input.currentSiteIsLive || input.soldPublic === true;
|
|
21850
|
-
if (bought) return "hidden";
|
|
21851
|
-
return input.hasSession && input.hasCurrentSite ? "owner" : "visitor";
|
|
21852
|
-
}
|
|
21853
|
-
function SettoPreviewBar() {
|
|
21854
|
-
const { supabase, api, config, session, authLoading, currentSite, sites, editMode } = useSetto();
|
|
21855
|
-
const [onPreview, setOnPreview] = useState(false);
|
|
21856
|
-
const [busy, setBusy] = useState(false);
|
|
21857
|
-
const [error, setError] = useState(null);
|
|
21858
|
-
const [paid, setPaid] = useState(false);
|
|
21859
|
-
const [soldPublic, setSoldPublic] = useState(null);
|
|
21860
|
-
useEffect(() => {
|
|
21861
|
-
setOnPreview(isPreviewHost(window.location.hostname));
|
|
21862
|
-
setPaid(new URLSearchParams(window.location.search).get("betaling") === "ok");
|
|
21863
|
-
}, []);
|
|
21864
|
-
useEffect(() => {
|
|
21865
|
-
if (!onPreview || session) return;
|
|
21866
|
-
let cancelled = false;
|
|
21867
|
-
fetch(`${config.apiUrl}/onboarding/sites/${config.siteId}/sold`, {
|
|
21868
|
-
headers: { Accept: "application/json" }
|
|
21869
|
-
}).then((r) => r.ok ? r.json() : null).then((d) => {
|
|
21870
|
-
if (!cancelled && d) setSoldPublic(d.sold === true);
|
|
21871
|
-
}).catch(() => {
|
|
21872
|
-
});
|
|
21873
|
-
return () => {
|
|
21874
|
-
cancelled = true;
|
|
21875
|
-
};
|
|
21876
|
-
}, [onPreview, session, config.apiUrl, config.siteId]);
|
|
21877
|
-
const signIn = () => {
|
|
21878
|
-
window.location.href = checkoutIntentUrl();
|
|
21879
|
-
};
|
|
21880
|
-
const next = () => {
|
|
21881
|
-
if (window.parent !== window) {
|
|
21882
|
-
window.parent.postMessage({ type: "setto:next", siteId: config.siteId }, "*");
|
|
21883
|
-
return;
|
|
21884
|
-
}
|
|
21885
|
-
signIn();
|
|
21886
|
-
};
|
|
21887
|
-
const publish = async () => {
|
|
21888
|
-
setBusy(true);
|
|
21889
|
-
setError(null);
|
|
21890
|
-
try {
|
|
21891
|
-
const token = (await supabase.auth.getSession()).data.session?.access_token;
|
|
21892
|
-
if (!token) {
|
|
21893
|
-
signIn();
|
|
21894
|
-
return;
|
|
21895
|
-
}
|
|
21896
|
-
const data = await api.request(
|
|
21897
|
-
`/onboarding/sites/${config.siteId}/checkout`,
|
|
21898
|
-
{
|
|
21899
|
-
method: "POST",
|
|
21900
|
-
// Back to the page they were looking at, not to a chat window.
|
|
21901
|
-
body: { returnTo: window.location.origin + window.location.pathname }
|
|
21902
|
-
},
|
|
21903
|
-
"checkout"
|
|
21904
|
-
);
|
|
21905
|
-
if (!data.url) throw new Error("checkout_failed");
|
|
21906
|
-
window.location.href = data.url;
|
|
21907
|
-
} catch (err) {
|
|
21908
|
-
const detail = err instanceof Error ? err.message : "";
|
|
21909
|
-
setError(
|
|
21910
|
-
detail.includes("already_paid") ? "Denne siden er allerede betalt." : detail.includes("not_found") ? "Fant ingen bestilling for denne siden på kontoen din." : detail.includes("stripe_not_configured") ? "Betaling er ikke satt opp ennå." : "Klarte ikke starte betalingen. Prøv igjen."
|
|
21911
|
-
);
|
|
21912
|
-
setBusy(false);
|
|
21913
|
-
}
|
|
21914
|
-
};
|
|
21915
|
-
const resumed = useRef(false);
|
|
21916
|
-
useEffect(() => {
|
|
21917
|
-
if (resumed.current || !onPreview || !session || !currentSite) return;
|
|
21918
|
-
if (!wantsCheckout()) return;
|
|
21919
|
-
if (paid || currentSite.status === "live") return;
|
|
21920
|
-
resumed.current = true;
|
|
21921
|
-
window.history.replaceState(null, "", withoutCheckoutIntent());
|
|
21922
|
-
void publish();
|
|
21923
|
-
}, [onPreview, session, currentSite, paid]);
|
|
21924
|
-
const state = previewBarState({
|
|
21925
|
-
onPreview,
|
|
21926
|
-
authLoading,
|
|
21927
|
-
hasSession: !!session,
|
|
21928
|
-
sitesLoaded: sites !== null,
|
|
21929
|
-
hasCurrentSite: !!currentSite,
|
|
21930
|
-
currentSiteIsLive: currentSite?.status === "live",
|
|
21931
|
-
soldPublic,
|
|
21932
|
-
paidMarker: paid
|
|
21933
|
-
});
|
|
21934
|
-
if (state === "hidden") return null;
|
|
21935
|
-
const owner = state === "owner";
|
|
21936
|
-
const name = currentSite?.display_name?.trim() || config.displayName?.trim() || config.siteId;
|
|
21937
|
-
return createPortal(
|
|
21938
|
-
/*
|
|
21939
|
-
* Lifted above the editor's floating button when edit mode is on, which
|
|
21940
|
-
* sits in the same corner band. Sideways would collide again the moment
|
|
21941
|
-
* its toolbar opens, since that expands rightward.
|
|
21942
|
-
*/
|
|
21943
|
-
/* @__PURE__ */ jsx(
|
|
21944
|
-
"div",
|
|
21945
|
-
{
|
|
21946
|
-
style: editMode ? { ...barStyle, bottom: "calc(max(16px, env(safe-area-inset-bottom, 0px)) + 68px)" } : barStyle,
|
|
21947
|
-
"data-setto-preview-bar": "",
|
|
21948
|
-
children: owner ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
21949
|
-
/* @__PURE__ */ jsx("span", { style: dotStyle, "aria-hidden": true }),
|
|
21950
|
-
/* @__PURE__ */ jsxs("span", { style: labelStyle, children: [
|
|
21951
|
-
"Du forhåndsviser nå ",
|
|
21952
|
-
/* @__PURE__ */ jsx("strong", { style: { fontWeight: 600 }, children: name })
|
|
21953
|
-
] }),
|
|
21954
|
-
error && /* @__PURE__ */ jsx("span", { style: errorStyle, children: error }),
|
|
21955
|
-
/* @__PURE__ */ jsx("button", { type: "button", style: primaryStyle, onClick: publish, disabled: busy, children: busy ? "Åpner betaling …" : "Publiser" })
|
|
21956
|
-
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
21957
|
-
/* @__PURE__ */ jsx("span", { style: dotStyle, "aria-hidden": true }),
|
|
21958
|
-
/* @__PURE__ */ jsx("span", { style: labelStyle, children: "Forhåndsvisning" }),
|
|
21959
|
-
/* @__PURE__ */ jsx("button", { type: "button", style: primaryStyle, onClick: next, children: "Neste" })
|
|
21960
|
-
] })
|
|
21961
|
-
}
|
|
21962
|
-
),
|
|
21963
|
-
document.body
|
|
21964
|
-
);
|
|
21965
|
-
}
|
|
21966
|
-
const barStyle = {
|
|
21967
|
-
position: "fixed",
|
|
21968
|
-
left: "50%",
|
|
21969
|
-
bottom: "max(16px, env(safe-area-inset-bottom, 0px))",
|
|
21970
|
-
transform: "translateX(-50%)",
|
|
21971
|
-
zIndex: 2147483e3,
|
|
21972
|
-
display: "flex",
|
|
21973
|
-
alignItems: "center",
|
|
21974
|
-
gap: 14,
|
|
21975
|
-
maxWidth: "calc(100vw - 24px)",
|
|
21976
|
-
padding: "10px 10px 10px 18px",
|
|
21977
|
-
borderRadius: 999,
|
|
21978
|
-
background: "#211711",
|
|
21979
|
-
color: "#fffdf8",
|
|
21980
|
-
fontFamily: 'system-ui, -apple-system, "Segoe UI", sans-serif',
|
|
21981
|
-
fontSize: 14,
|
|
21982
|
-
lineHeight: 1.3,
|
|
21983
|
-
boxShadow: "0 18px 40px -18px rgba(0, 0, 0, 0.65)",
|
|
21984
|
-
/*
|
|
21985
|
-
* A frosted rim, so the pill reads as an object on any page.
|
|
21986
|
-
*
|
|
21987
|
-
* The bar sits on sites Setto did not choose the colours for: on a dark hero
|
|
21988
|
-
* a dark pill disappears, and on a pale section its shadow is all that holds
|
|
21989
|
-
* it. A translucent white border with `padding-box` clipping lets the page
|
|
21990
|
-
* show through the rim itself, which separates the pill from either ground
|
|
21991
|
-
* without picking a side.
|
|
21992
|
-
*/
|
|
21993
|
-
border: "5px solid rgba(255, 255, 255, 0.22)",
|
|
21994
|
-
backgroundClip: "padding-box",
|
|
21995
|
-
backdropFilter: "blur(10px)",
|
|
21996
|
-
WebkitBackdropFilter: "blur(10px)"
|
|
21997
|
-
};
|
|
21998
|
-
const dotStyle = {
|
|
21999
|
-
width: 7,
|
|
22000
|
-
height: 7,
|
|
22001
|
-
borderRadius: "50%",
|
|
22002
|
-
background: "#c4502a",
|
|
22003
|
-
flex: "none"
|
|
22004
|
-
};
|
|
22005
|
-
const labelStyle = {
|
|
22006
|
-
whiteSpace: "nowrap",
|
|
22007
|
-
overflow: "hidden",
|
|
22008
|
-
textOverflow: "ellipsis"
|
|
22009
|
-
};
|
|
22010
|
-
const primaryStyle = {
|
|
22011
|
-
flex: "none",
|
|
22012
|
-
padding: "9px 18px",
|
|
22013
|
-
border: 0,
|
|
22014
|
-
borderRadius: 999,
|
|
22015
|
-
background: "#c4502a",
|
|
22016
|
-
color: "#fffdf8",
|
|
22017
|
-
font: "inherit",
|
|
22018
|
-
fontWeight: 600,
|
|
22019
|
-
cursor: "pointer"
|
|
22020
|
-
};
|
|
22021
|
-
const errorStyle = {
|
|
22022
|
-
color: "#ffb4a2",
|
|
22023
|
-
fontSize: 13,
|
|
22024
|
-
/*
|
|
22025
|
-
* Shrinkable, unlike the label beside it.
|
|
22026
|
-
*
|
|
22027
|
-
* `nowrap` on a flex child whose `min-width` is `auto` cannot shrink below
|
|
22028
|
-
* its own text, so on a 320px phone the error shouldered «Publiser» 19px
|
|
22029
|
-
* past the right edge of the screen and the bar clipped it — the button
|
|
22030
|
-
* that takes the money, unreachable, exactly when something had gone wrong.
|
|
22031
|
-
*/
|
|
22032
|
-
minWidth: 0,
|
|
22033
|
-
overflow: "hidden",
|
|
22034
|
-
textOverflow: "ellipsis",
|
|
22035
|
-
whiteSpace: "nowrap"
|
|
22036
|
-
};
|
|
22037
22085
|
function isLinkContext(el) {
|
|
22038
22086
|
const key = el.getAttribute("data-setto-key");
|
|
22039
22087
|
if (key?.startsWith("lang.")) return false;
|
|
@@ -22839,12 +22887,12 @@ function SettoImage({ srcKey, alt, className, style, ...rest }) {
|
|
|
22839
22887
|
)
|
|
22840
22888
|
] });
|
|
22841
22889
|
}
|
|
22842
|
-
const STYLE_ID$
|
|
22890
|
+
const STYLE_ID$1 = "setto-repeater-styles";
|
|
22843
22891
|
function useRepeaterStyles() {
|
|
22844
22892
|
useEffect(() => {
|
|
22845
|
-
if (document.getElementById(STYLE_ID$
|
|
22893
|
+
if (document.getElementById(STYLE_ID$1)) return;
|
|
22846
22894
|
const el = document.createElement("style");
|
|
22847
|
-
el.id = STYLE_ID$
|
|
22895
|
+
el.id = STYLE_ID$1;
|
|
22848
22896
|
el.textContent = `
|
|
22849
22897
|
.setto-repeater-remove {
|
|
22850
22898
|
opacity: 0;
|
|
@@ -23129,276 +23177,6 @@ function buildEnvelope(fields, values, extra) {
|
|
|
23129
23177
|
function settoSubmissionUrl(apiUrl, siteId, formId) {
|
|
23130
23178
|
return `${apiUrl.replace(/\/+$/, "")}/sites/${encodeURIComponent(siteId)}/forms/${encodeURIComponent(formId)}/submissions`;
|
|
23131
23179
|
}
|
|
23132
|
-
const STYLE_ID$1 = "setto-form-edit-styles";
|
|
23133
|
-
function useFormEditStyles() {
|
|
23134
|
-
useEffect(() => {
|
|
23135
|
-
if (document.getElementById(STYLE_ID$1)) return;
|
|
23136
|
-
const el = document.createElement("style");
|
|
23137
|
-
el.id = STYLE_ID$1;
|
|
23138
|
-
el.textContent = `
|
|
23139
|
-
.setto-form-field-tools {
|
|
23140
|
-
opacity: 0;
|
|
23141
|
-
transition: opacity 120ms ease;
|
|
23142
|
-
}
|
|
23143
|
-
.setto-form__field:hover .setto-form-field-tools,
|
|
23144
|
-
.setto-form__field:focus-within .setto-form-field-tools,
|
|
23145
|
-
.setto-form-field-tools[data-open] {
|
|
23146
|
-
opacity: 1;
|
|
23147
|
-
}
|
|
23148
|
-
@media (hover: none) {
|
|
23149
|
-
.setto-form-field-tools { opacity: 1; }
|
|
23150
|
-
}
|
|
23151
|
-
`;
|
|
23152
|
-
document.head.appendChild(el);
|
|
23153
|
-
}, []);
|
|
23154
|
-
}
|
|
23155
|
-
function FormFieldTools({
|
|
23156
|
-
field,
|
|
23157
|
-
index,
|
|
23158
|
-
count,
|
|
23159
|
-
autoOpen,
|
|
23160
|
-
onMove,
|
|
23161
|
-
onRemove,
|
|
23162
|
-
onChange
|
|
23163
|
-
}) {
|
|
23164
|
-
useFormEditStyles();
|
|
23165
|
-
const [open, setOpen] = useState(autoOpen === true);
|
|
23166
|
-
const gearRef = useRef(null);
|
|
23167
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
23168
|
-
/* @__PURE__ */ jsxs(
|
|
23169
|
-
"div",
|
|
23170
|
-
{
|
|
23171
|
-
"data-setto-ui": true,
|
|
23172
|
-
className: "setto-form-field-tools",
|
|
23173
|
-
style: toolsStyle,
|
|
23174
|
-
...open ? { "data-open": "" } : {},
|
|
23175
|
-
onMouseDown: (e) => e.stopPropagation(),
|
|
23176
|
-
onClick: (e) => e.stopPropagation(),
|
|
23177
|
-
children: [
|
|
23178
|
-
/* @__PURE__ */ jsx(ToolButton, { label: "Flytt opp", disabled: index === 0, onClick: () => onMove(-1), children: /* @__PURE__ */ jsx(ArrowIcon, { direction: "up" }) }),
|
|
23179
|
-
/* @__PURE__ */ jsx(
|
|
23180
|
-
ToolButton,
|
|
23181
|
-
{
|
|
23182
|
-
label: "Flytt ned",
|
|
23183
|
-
disabled: index === count - 1,
|
|
23184
|
-
onClick: () => onMove(1),
|
|
23185
|
-
children: /* @__PURE__ */ jsx(ArrowIcon, { direction: "down" })
|
|
23186
|
-
}
|
|
23187
|
-
),
|
|
23188
|
-
/* @__PURE__ */ jsx(
|
|
23189
|
-
ToolButton,
|
|
23190
|
-
{
|
|
23191
|
-
ref: gearRef,
|
|
23192
|
-
label: "Feltinnstillinger",
|
|
23193
|
-
active: open,
|
|
23194
|
-
onClick: () => setOpen((o) => !o),
|
|
23195
|
-
children: /* @__PURE__ */ jsx(GearIcon, {})
|
|
23196
|
-
}
|
|
23197
|
-
),
|
|
23198
|
-
/* @__PURE__ */ jsx(ToolButton, { label: "Fjern felt", disabled: count <= 1, onClick: onRemove, danger: true, children: "×" })
|
|
23199
|
-
]
|
|
23200
|
-
}
|
|
23201
|
-
),
|
|
23202
|
-
/* @__PURE__ */ jsx(
|
|
23203
|
-
FloatingPopover,
|
|
23204
|
-
{
|
|
23205
|
-
open,
|
|
23206
|
-
anchorRef: gearRef,
|
|
23207
|
-
onClose: () => setOpen(false),
|
|
23208
|
-
ariaLabel: "Feltinnstillinger",
|
|
23209
|
-
minWidth: 300,
|
|
23210
|
-
style: { width: 300, padding: 14 },
|
|
23211
|
-
children: /* @__PURE__ */ jsx(FieldSettings, { field, onChange })
|
|
23212
|
-
}
|
|
23213
|
-
)
|
|
23214
|
-
] });
|
|
23215
|
-
}
|
|
23216
|
-
function FieldSettings({ field, onChange }) {
|
|
23217
|
-
const [label, setLabel] = useState(field.label);
|
|
23218
|
-
const [placeholder, setPlaceholder] = useState(field.placeholder);
|
|
23219
|
-
const [options, setOptions] = useState(field.options.join("\n"));
|
|
23220
|
-
const [name, setName] = useState(field.name);
|
|
23221
|
-
useEffect(() => setLabel(field.label), [field.label]);
|
|
23222
|
-
useEffect(() => setPlaceholder(field.placeholder), [field.placeholder]);
|
|
23223
|
-
useEffect(() => setName(field.name), [field.name]);
|
|
23224
|
-
return /* @__PURE__ */ jsxs("div", { style: { display: "grid", gap: 10, fontSize: 13, color: "#222" }, children: [
|
|
23225
|
-
/* @__PURE__ */ jsx(Row, { label: "Type", children: /* @__PURE__ */ jsx(
|
|
23226
|
-
"select",
|
|
23227
|
-
{
|
|
23228
|
-
value: field.type,
|
|
23229
|
-
onChange: (e) => onChange({ type: e.target.value }),
|
|
23230
|
-
style: inputStyle$1,
|
|
23231
|
-
children: FORM_FIELD_TYPES.map((type) => /* @__PURE__ */ jsx("option", { value: type, children: FORM_FIELD_TYPE_LABELS[type] }, type))
|
|
23232
|
-
}
|
|
23233
|
-
) }),
|
|
23234
|
-
/* @__PURE__ */ jsx(Row, { label: "Etikett", children: /* @__PURE__ */ jsx(
|
|
23235
|
-
"input",
|
|
23236
|
-
{
|
|
23237
|
-
type: "text",
|
|
23238
|
-
value: label,
|
|
23239
|
-
onChange: (e) => setLabel(e.target.value),
|
|
23240
|
-
onBlur: () => label !== field.label && onChange({ label }),
|
|
23241
|
-
style: inputStyle$1
|
|
23242
|
-
}
|
|
23243
|
-
) }),
|
|
23244
|
-
fieldTypeHasPlaceholder(field.type) ? /* @__PURE__ */ jsx(Row, { label: "Plassholder", children: /* @__PURE__ */ jsx(
|
|
23245
|
-
"input",
|
|
23246
|
-
{
|
|
23247
|
-
type: "text",
|
|
23248
|
-
value: placeholder,
|
|
23249
|
-
onChange: (e) => setPlaceholder(e.target.value),
|
|
23250
|
-
onBlur: () => placeholder !== field.placeholder && onChange({ placeholder }),
|
|
23251
|
-
style: inputStyle$1
|
|
23252
|
-
}
|
|
23253
|
-
) }) : null,
|
|
23254
|
-
fieldTypeHasOptions(field.type) ? /* @__PURE__ */ jsx(Row, { label: "Alternativer", hint: "Ett per linje", children: /* @__PURE__ */ jsx(
|
|
23255
|
-
"textarea",
|
|
23256
|
-
{
|
|
23257
|
-
value: options,
|
|
23258
|
-
rows: 4,
|
|
23259
|
-
onChange: (e) => setOptions(e.target.value),
|
|
23260
|
-
onBlur: () => options !== field.options.join("\n") && onChange({ options }),
|
|
23261
|
-
style: { ...inputStyle$1, resize: "vertical", fontFamily: "inherit" }
|
|
23262
|
-
}
|
|
23263
|
-
) }) : null,
|
|
23264
|
-
/* @__PURE__ */ jsxs("label", { style: { display: "flex", alignItems: "center", gap: 8, cursor: "pointer" }, children: [
|
|
23265
|
-
/* @__PURE__ */ jsx(
|
|
23266
|
-
"input",
|
|
23267
|
-
{
|
|
23268
|
-
type: "checkbox",
|
|
23269
|
-
checked: field.required,
|
|
23270
|
-
onChange: (e) => onChange({ required: e.target.checked })
|
|
23271
|
-
}
|
|
23272
|
-
),
|
|
23273
|
-
"Må fylles ut"
|
|
23274
|
-
] }),
|
|
23275
|
-
/* @__PURE__ */ jsx(Row, { label: "Teknisk navn", hint: "Brukes i e-posten og i historikken", children: /* @__PURE__ */ jsx(
|
|
23276
|
-
"input",
|
|
23277
|
-
{
|
|
23278
|
-
type: "text",
|
|
23279
|
-
value: name,
|
|
23280
|
-
onChange: (e) => setName(e.target.value),
|
|
23281
|
-
onBlur: () => {
|
|
23282
|
-
const next = name.trim();
|
|
23283
|
-
if (next && next !== field.name) onChange({ name: next });
|
|
23284
|
-
else setName(field.name);
|
|
23285
|
-
},
|
|
23286
|
-
style: { ...inputStyle$1, fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace" }
|
|
23287
|
-
}
|
|
23288
|
-
) })
|
|
23289
|
-
] });
|
|
23290
|
-
}
|
|
23291
|
-
function Row({ label, hint, children }) {
|
|
23292
|
-
return /* @__PURE__ */ jsxs("label", { style: { display: "grid", gap: 4 }, children: [
|
|
23293
|
-
/* @__PURE__ */ jsxs("span", { style: { fontSize: 12, color: "#666" }, children: [
|
|
23294
|
-
label,
|
|
23295
|
-
hint ? /* @__PURE__ */ jsxs("span", { style: { color: "#999" }, children: [
|
|
23296
|
-
" · ",
|
|
23297
|
-
hint
|
|
23298
|
-
] }) : null
|
|
23299
|
-
] }),
|
|
23300
|
-
children
|
|
23301
|
-
] });
|
|
23302
|
-
}
|
|
23303
|
-
function ToolButton({ label, onClick, disabled, active, danger, children, ref }) {
|
|
23304
|
-
return /* @__PURE__ */ jsx(
|
|
23305
|
-
"button",
|
|
23306
|
-
{
|
|
23307
|
-
ref,
|
|
23308
|
-
type: "button",
|
|
23309
|
-
"data-setto-ui": true,
|
|
23310
|
-
"aria-label": label,
|
|
23311
|
-
title: label,
|
|
23312
|
-
disabled,
|
|
23313
|
-
onClick,
|
|
23314
|
-
style: {
|
|
23315
|
-
...toolBtnStyle,
|
|
23316
|
-
...active ? { background: "#640AFF", color: "#fff", borderColor: "#640AFF" } : {},
|
|
23317
|
-
...danger ? { fontSize: 16 } : {},
|
|
23318
|
-
...disabled ? { opacity: 0.35, cursor: "default" } : {}
|
|
23319
|
-
},
|
|
23320
|
-
children
|
|
23321
|
-
}
|
|
23322
|
-
);
|
|
23323
|
-
}
|
|
23324
|
-
function ArrowIcon({ direction }) {
|
|
23325
|
-
return /* @__PURE__ */ jsx(
|
|
23326
|
-
"svg",
|
|
23327
|
-
{
|
|
23328
|
-
width: "12",
|
|
23329
|
-
height: "12",
|
|
23330
|
-
viewBox: "0 0 24 24",
|
|
23331
|
-
fill: "none",
|
|
23332
|
-
stroke: "currentColor",
|
|
23333
|
-
strokeWidth: "2.5",
|
|
23334
|
-
strokeLinecap: "round",
|
|
23335
|
-
strokeLinejoin: "round",
|
|
23336
|
-
"aria-hidden": "true",
|
|
23337
|
-
style: direction === "down" ? { transform: "rotate(180deg)" } : void 0,
|
|
23338
|
-
children: /* @__PURE__ */ jsx("path", { d: "M12 19V5M5 12l7-7 7 7" })
|
|
23339
|
-
}
|
|
23340
|
-
);
|
|
23341
|
-
}
|
|
23342
|
-
function GearIcon() {
|
|
23343
|
-
return /* @__PURE__ */ jsxs(
|
|
23344
|
-
"svg",
|
|
23345
|
-
{
|
|
23346
|
-
width: "13",
|
|
23347
|
-
height: "13",
|
|
23348
|
-
viewBox: "0 0 24 24",
|
|
23349
|
-
fill: "none",
|
|
23350
|
-
stroke: "currentColor",
|
|
23351
|
-
strokeWidth: "2",
|
|
23352
|
-
strokeLinecap: "round",
|
|
23353
|
-
strokeLinejoin: "round",
|
|
23354
|
-
"aria-hidden": "true",
|
|
23355
|
-
children: [
|
|
23356
|
-
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "3" }),
|
|
23357
|
-
/* @__PURE__ */ jsx("path", { d: "M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09a1.65 1.65 0 0 0-1-1.51 1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09a1.65 1.65 0 0 0 1.51-1 1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z" })
|
|
23358
|
-
]
|
|
23359
|
-
}
|
|
23360
|
-
);
|
|
23361
|
-
}
|
|
23362
|
-
const toolsStyle = {
|
|
23363
|
-
position: "absolute",
|
|
23364
|
-
top: -12,
|
|
23365
|
-
right: 0,
|
|
23366
|
-
zIndex: 2,
|
|
23367
|
-
display: "flex",
|
|
23368
|
-
gap: 4,
|
|
23369
|
-
padding: 3,
|
|
23370
|
-
background: "#ffffff",
|
|
23371
|
-
border: "1px solid #e0e0e0",
|
|
23372
|
-
borderRadius: 999,
|
|
23373
|
-
boxShadow: "0 1px 4px rgba(0,0,0,0.18)",
|
|
23374
|
-
fontFamily: 'system-ui, -apple-system, "Segoe UI", sans-serif'
|
|
23375
|
-
};
|
|
23376
|
-
const toolBtnStyle = {
|
|
23377
|
-
display: "flex",
|
|
23378
|
-
alignItems: "center",
|
|
23379
|
-
justifyContent: "center",
|
|
23380
|
-
width: 24,
|
|
23381
|
-
height: 24,
|
|
23382
|
-
padding: 0,
|
|
23383
|
-
background: "transparent",
|
|
23384
|
-
border: "1px solid transparent",
|
|
23385
|
-
borderRadius: "50%",
|
|
23386
|
-
fontSize: 13,
|
|
23387
|
-
lineHeight: 1,
|
|
23388
|
-
color: "#444",
|
|
23389
|
-
cursor: "pointer"
|
|
23390
|
-
};
|
|
23391
|
-
const inputStyle$1 = {
|
|
23392
|
-
width: "100%",
|
|
23393
|
-
boxSizing: "border-box",
|
|
23394
|
-
padding: "6px 8px",
|
|
23395
|
-
border: "1px solid #d8d8d8",
|
|
23396
|
-
borderRadius: 6,
|
|
23397
|
-
fontSize: 13,
|
|
23398
|
-
color: "#222",
|
|
23399
|
-
background: "#fff",
|
|
23400
|
-
fontFamily: "inherit"
|
|
23401
|
-
};
|
|
23402
23180
|
const HONEYPOT_NAME = "website";
|
|
23403
23181
|
const warnedEndpointKeys = /* @__PURE__ */ new Set();
|
|
23404
23182
|
function cx(...parts) {
|
|
@@ -23414,6 +23192,7 @@ function SettoForm({
|
|
|
23414
23192
|
}) {
|
|
23415
23193
|
const { t, i18n } = useTranslation();
|
|
23416
23194
|
const { config, editMode, store } = useSetto();
|
|
23195
|
+
const FieldTools = useSettoChromeSlot("form-field-tools");
|
|
23417
23196
|
const guestEdit = useGuestEdit();
|
|
23418
23197
|
const editable = editMode || guestEdit.active;
|
|
23419
23198
|
const lng = i18n.language;
|
|
@@ -23661,8 +23440,8 @@ function SettoForm({
|
|
|
23661
23440
|
}
|
|
23662
23441
|
),
|
|
23663
23442
|
error ? /* @__PURE__ */ jsx("p", { id: errorId, className: cx("setto-form__field-error", classNames.fieldError), children: t(`form.validation.${error}`, { defaultValue: FIELD_ERROR_DEFAULTS[error] }) }) : null,
|
|
23664
|
-
editable && store ? /* @__PURE__ */ jsx(
|
|
23665
|
-
|
|
23443
|
+
editable && store && FieldTools ? /* @__PURE__ */ jsx(
|
|
23444
|
+
FieldTools,
|
|
23666
23445
|
{
|
|
23667
23446
|
field,
|
|
23668
23447
|
index,
|
|
@@ -25063,222 +24842,21 @@ function collectRelativeKeys(nodes, prefix, out) {
|
|
|
25063
24842
|
}
|
|
25064
24843
|
}
|
|
25065
24844
|
}
|
|
25066
|
-
const IN_PROGRESS_STATUSES = ["pending", "building"];
|
|
25067
|
-
function isDeploymentInProgress(status) {
|
|
25068
|
-
return IN_PROGRESS_STATUSES.includes(status);
|
|
25069
|
-
}
|
|
25070
|
-
const STALE_PENDING_MS = 20 * 60 * 1e3;
|
|
25071
|
-
const STALE_BUILDING_MS = 45 * 60 * 1e3;
|
|
25072
|
-
function isDeploymentStale(row) {
|
|
25073
|
-
if (row.status !== "pending" && row.status !== "building") return false;
|
|
25074
|
-
const now = Date.now();
|
|
25075
|
-
if (row.status === "pending") {
|
|
25076
|
-
return now - new Date(row.started_at).getTime() > STALE_PENDING_MS;
|
|
25077
|
-
}
|
|
25078
|
-
return now - new Date(row.updated_at).getTime() > STALE_BUILDING_MS;
|
|
25079
|
-
}
|
|
25080
|
-
async function fetchInProgressDeploymentId(supabase, siteId) {
|
|
25081
|
-
const { data } = await supabase.from("deployments").select("id, status, started_at, updated_at").eq("site_id", siteId).in("status", [...IN_PROGRESS_STATUSES]).order("started_at", { ascending: false }).limit(5);
|
|
25082
|
-
const rows = data ?? [];
|
|
25083
|
-
const fresh = rows.find((r) => !isDeploymentStale(r));
|
|
25084
|
-
return fresh?.id ?? null;
|
|
25085
|
-
}
|
|
25086
|
-
async function waitForDeploymentByCommit(supabase, siteId, commitSha, timeoutMs = 6e4) {
|
|
25087
|
-
const deadline = Date.now() + timeoutMs;
|
|
25088
|
-
while (Date.now() < deadline) {
|
|
25089
|
-
const { data } = await supabase.from("deployments").select("id").eq("site_id", siteId).eq("commit_sha", commitSha).order("started_at", { ascending: false }).limit(1).maybeSingle();
|
|
25090
|
-
if (data?.id) return data.id;
|
|
25091
|
-
await new Promise((r) => setTimeout(r, 2e3));
|
|
25092
|
-
}
|
|
25093
|
-
return null;
|
|
25094
|
-
}
|
|
25095
|
-
function useDeploymentStatus(supabase, deploymentId) {
|
|
25096
|
-
const [row, setRow] = useState(null);
|
|
25097
|
-
useEffect(() => {
|
|
25098
|
-
if (!deploymentId) {
|
|
25099
|
-
setRow(null);
|
|
25100
|
-
return;
|
|
25101
|
-
}
|
|
25102
|
-
let channel = null;
|
|
25103
|
-
let cancelled = false;
|
|
25104
|
-
const load = async () => {
|
|
25105
|
-
const { data } = await supabase.from("deployments").select("*").eq("id", deploymentId).maybeSingle();
|
|
25106
|
-
if (!cancelled && data) setRow(data);
|
|
25107
|
-
};
|
|
25108
|
-
void load();
|
|
25109
|
-
channel = supabase.channel(`setto-dep-${deploymentId}`).on(
|
|
25110
|
-
"postgres_changes",
|
|
25111
|
-
{
|
|
25112
|
-
event: "UPDATE",
|
|
25113
|
-
schema: "public",
|
|
25114
|
-
table: "deployments",
|
|
25115
|
-
filter: `id=eq.${deploymentId}`
|
|
25116
|
-
},
|
|
25117
|
-
(payload) => {
|
|
25118
|
-
setRow(payload.new);
|
|
25119
|
-
}
|
|
25120
|
-
).subscribe();
|
|
25121
|
-
return () => {
|
|
25122
|
-
cancelled = true;
|
|
25123
|
-
if (channel) supabase.removeChannel(channel);
|
|
25124
|
-
};
|
|
25125
|
-
}, [supabase, deploymentId]);
|
|
25126
|
-
return { row, status: row?.status ?? null };
|
|
25127
|
-
}
|
|
25128
|
-
function deploymentStepIndex(status) {
|
|
25129
|
-
switch (status) {
|
|
25130
|
-
case "pending":
|
|
25131
|
-
return 0;
|
|
25132
|
-
case "building":
|
|
25133
|
-
return 1;
|
|
25134
|
-
case "ready":
|
|
25135
|
-
return 2;
|
|
25136
|
-
default:
|
|
25137
|
-
return -1;
|
|
25138
|
-
}
|
|
25139
|
-
}
|
|
25140
|
-
function isDeploymentStepReached(status, i) {
|
|
25141
|
-
return deploymentStepIndex(status) >= i;
|
|
25142
|
-
}
|
|
25143
|
-
const STEPS = ["Committed", "Building", "Deployed"];
|
|
25144
|
-
const PROGRESS_STYLE_ID = "setto-publish-progress-styles";
|
|
25145
|
-
function usePublishProgressStyles() {
|
|
25146
|
-
useEffect(() => {
|
|
25147
|
-
if (document.getElementById(PROGRESS_STYLE_ID)) return;
|
|
25148
|
-
const el = document.createElement("style");
|
|
25149
|
-
el.id = PROGRESS_STYLE_ID;
|
|
25150
|
-
el.textContent = `
|
|
25151
|
-
@keyframes setto-step-pulse {
|
|
25152
|
-
0%, 100% { opacity: 1; transform: scaleY(1); }
|
|
25153
|
-
50% { opacity: 0.72; transform: scaleY(0.82); }
|
|
25154
|
-
}
|
|
25155
|
-
@keyframes setto-step-shimmer {
|
|
25156
|
-
0% { background-position: 120% 0; }
|
|
25157
|
-
100% { background-position: -120% 0; }
|
|
25158
|
-
}
|
|
25159
|
-
.setto-step-bar-active {
|
|
25160
|
-
animation: setto-step-pulse 1.5s ease-in-out infinite;
|
|
25161
|
-
transform-origin: center bottom;
|
|
25162
|
-
}
|
|
25163
|
-
.setto-step-bar-shimmer {
|
|
25164
|
-
background: linear-gradient(
|
|
25165
|
-
90deg,
|
|
25166
|
-
#640aff 0%,
|
|
25167
|
-
#8b5cf6 45%,
|
|
25168
|
-
#640aff 90%
|
|
25169
|
-
) !important;
|
|
25170
|
-
background-size: 220% 100% !important;
|
|
25171
|
-
animation: setto-step-shimmer 2.2s ease-in-out infinite;
|
|
25172
|
-
}
|
|
25173
|
-
.setto-step-label-active {
|
|
25174
|
-
animation: setto-step-pulse 1.5s ease-in-out infinite;
|
|
25175
|
-
}
|
|
25176
|
-
`;
|
|
25177
|
-
document.head.appendChild(el);
|
|
25178
|
-
}, []);
|
|
25179
|
-
}
|
|
25180
|
-
function PublishProgressSteps({
|
|
25181
|
-
status,
|
|
25182
|
-
compact,
|
|
25183
|
-
buildingElapsed
|
|
25184
|
-
}) {
|
|
25185
|
-
usePublishProgressStyles();
|
|
25186
|
-
const scopeId = useId().replace(/:/g, "");
|
|
25187
|
-
const errored = status === "error" || status === "canceled";
|
|
25188
|
-
const activeIndex = deploymentStepIndex(status);
|
|
25189
|
-
return /* @__PURE__ */ jsx(
|
|
25190
|
-
"div",
|
|
25191
|
-
{
|
|
25192
|
-
className: `setto-progress-${scopeId}`,
|
|
25193
|
-
style: {
|
|
25194
|
-
display: "flex",
|
|
25195
|
-
gap: compact ? 3 : 8,
|
|
25196
|
-
fontSize: compact ? 11 : 12,
|
|
25197
|
-
minWidth: compact ? 100 : 200
|
|
25198
|
-
},
|
|
25199
|
-
children: STEPS.map((label, i) => {
|
|
25200
|
-
const reached = isDeploymentStepReached(status, i);
|
|
25201
|
-
const active = activeIndex === i && !errored;
|
|
25202
|
-
const barClass = active ? "setto-step-bar-active setto-step-bar-shimmer" : void 0;
|
|
25203
|
-
return /* @__PURE__ */ jsxs("div", { style: { flex: 1, textAlign: "center", minWidth: 0 }, children: [
|
|
25204
|
-
/* @__PURE__ */ jsx(
|
|
25205
|
-
"div",
|
|
25206
|
-
{
|
|
25207
|
-
className: barClass,
|
|
25208
|
-
style: {
|
|
25209
|
-
height: compact ? 3 : 4,
|
|
25210
|
-
borderRadius: 2,
|
|
25211
|
-
background: errored ? "#fca5a5" : reached ? "#640AFF" : "#e5e5e5",
|
|
25212
|
-
marginBottom: compact ? 0 : 6,
|
|
25213
|
-
transition: "background 0.35s ease"
|
|
25214
|
-
}
|
|
25215
|
-
}
|
|
25216
|
-
),
|
|
25217
|
-
!compact ? /* @__PURE__ */ jsxs(
|
|
25218
|
-
"span",
|
|
25219
|
-
{
|
|
25220
|
-
className: active ? "setto-step-label-active" : void 0,
|
|
25221
|
-
style: {
|
|
25222
|
-
color: active ? "#1a1a1a" : "#888",
|
|
25223
|
-
fontWeight: active ? 500 : 400,
|
|
25224
|
-
display: "inline-block"
|
|
25225
|
-
},
|
|
25226
|
-
children: [
|
|
25227
|
-
label,
|
|
25228
|
-
active && status === "building" && buildingElapsed != null ? ` (${buildingElapsed}s)` : active ? " …" : ""
|
|
25229
|
-
]
|
|
25230
|
-
}
|
|
25231
|
-
) : null
|
|
25232
|
-
] }, label);
|
|
25233
|
-
})
|
|
25234
|
-
}
|
|
25235
|
-
);
|
|
25236
|
-
}
|
|
25237
|
-
function PublishProgressBar({ status, compact, href }) {
|
|
25238
|
-
const inner = /* @__PURE__ */ jsx(PublishProgressSteps, { status, compact });
|
|
25239
|
-
const wrapStyle = {
|
|
25240
|
-
fontSize: compact ? 11 : 12,
|
|
25241
|
-
minWidth: compact ? 100 : 200
|
|
25242
|
-
};
|
|
25243
|
-
if (href) {
|
|
25244
|
-
return /* @__PURE__ */ jsx(
|
|
25245
|
-
"a",
|
|
25246
|
-
{
|
|
25247
|
-
href,
|
|
25248
|
-
title: "Se publiseringsstatus",
|
|
25249
|
-
style: {
|
|
25250
|
-
...wrapStyle,
|
|
25251
|
-
display: "block",
|
|
25252
|
-
textDecoration: "none",
|
|
25253
|
-
color: "inherit",
|
|
25254
|
-
cursor: "pointer",
|
|
25255
|
-
borderRadius: 4,
|
|
25256
|
-
padding: compact ? "4px 2px" : void 0
|
|
25257
|
-
},
|
|
25258
|
-
children: inner
|
|
25259
|
-
}
|
|
25260
|
-
);
|
|
25261
|
-
}
|
|
25262
|
-
return /* @__PURE__ */ jsx("div", { style: wrapStyle, children: inner });
|
|
25263
|
-
}
|
|
25264
24845
|
export {
|
|
25265
24846
|
AuthGate,
|
|
25266
24847
|
BLOCK_REGISTRY,
|
|
25267
24848
|
BLOCK_TYPES,
|
|
25268
24849
|
CHECKOUT_INTENT,
|
|
24850
|
+
DEFAULT_LANGUAGE,
|
|
25269
24851
|
EMPTY_I18N_STORE_SNAPSHOT,
|
|
25270
24852
|
FAB_INSET,
|
|
25271
24853
|
FAB_SIZE,
|
|
25272
24854
|
FORM_FIELD_TYPES,
|
|
25273
24855
|
FORM_FIELD_TYPE_LABELS,
|
|
24856
|
+
FloatingPopover,
|
|
25274
24857
|
GuestEditProvider,
|
|
25275
|
-
IN_PROGRESS_STATUSES,
|
|
25276
|
-
PublishProgressBar,
|
|
25277
|
-
PublishProgressSteps,
|
|
25278
24858
|
SETTO_BASE,
|
|
25279
24859
|
SETTO_MODULES,
|
|
25280
|
-
STALE_BUILDING_MS,
|
|
25281
|
-
STALE_PENDING_MS,
|
|
25282
24860
|
SettoAdminApp,
|
|
25283
24861
|
SettoAnimation,
|
|
25284
24862
|
SettoBlock,
|
|
@@ -25288,8 +24866,8 @@ export {
|
|
|
25288
24866
|
SettoForm,
|
|
25289
24867
|
SettoIcon,
|
|
25290
24868
|
SettoImage,
|
|
24869
|
+
SettoLanguages,
|
|
25291
24870
|
SettoMark,
|
|
25292
|
-
SettoPreviewBar,
|
|
25293
24871
|
SettoProvider,
|
|
25294
24872
|
SettoRepeater,
|
|
25295
24873
|
SettoSection,
|
|
@@ -25300,30 +24878,28 @@ export {
|
|
|
25300
24878
|
checkoutIntentUrl,
|
|
25301
24879
|
currentAdminView,
|
|
25302
24880
|
extractContentKeys,
|
|
25303
|
-
|
|
24881
|
+
fieldTypeHasOptions,
|
|
24882
|
+
fieldTypeHasPlaceholder,
|
|
25304
24883
|
formFieldsPrefix,
|
|
25305
24884
|
isAdminDeploymentView,
|
|
25306
24885
|
isAdminPanelView,
|
|
25307
24886
|
isAdminRoute,
|
|
25308
|
-
isDeploymentInProgress,
|
|
25309
|
-
isDeploymentStale,
|
|
25310
24887
|
isFormFieldType,
|
|
25311
24888
|
isLinkContext,
|
|
25312
|
-
|
|
24889
|
+
languageName,
|
|
25313
24890
|
nodeChildren,
|
|
25314
24891
|
parseBlockTree,
|
|
25315
|
-
|
|
24892
|
+
pickInitialLanguage,
|
|
25316
24893
|
readLocaleFields,
|
|
25317
24894
|
settoSubmissionUrl,
|
|
25318
24895
|
siteHomeUrl,
|
|
24896
|
+
siteLanguages,
|
|
25319
24897
|
useCoarsePointer,
|
|
25320
|
-
useDeploymentStatus,
|
|
25321
24898
|
useGuestEdit,
|
|
25322
24899
|
useSectionTheme,
|
|
25323
24900
|
useSetto,
|
|
25324
24901
|
useSettoChromeSlot,
|
|
25325
24902
|
validateBlockTree,
|
|
25326
|
-
waitForDeploymentByCommit,
|
|
25327
24903
|
wantsCheckout,
|
|
25328
24904
|
withoutCheckoutIntent
|
|
25329
24905
|
};
|