@sevenfold/setto-client 0.15.0 → 0.16.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.
@@ -21431,11 +21431,11 @@ function useSettoChromeSlot(name) {
21431
21431
  slotListeners.delete(listener);
21432
21432
  };
21433
21433
  }, []);
21434
- const Offered = slots.get(name) ?? null;
21434
+ const Offered = slots.get(name);
21435
21435
  return useMemo(() => {
21436
21436
  if (!Offered) return null;
21437
- return function SettoChromeSlot() {
21438
- return /* @__PURE__ */ jsx(ChromeBoundary, { onError: () => retireSlot(name), children: /* @__PURE__ */ jsx(Offered, {}) });
21437
+ return function SettoChromeSlot(props) {
21438
+ return /* @__PURE__ */ jsx(ChromeBoundary, { onError: () => retireSlot(name), children: React.createElement(Offered, props) });
21439
21439
  };
21440
21440
  }, [Offered, name]);
21441
21441
  }
@@ -21552,7 +21552,7 @@ function withoutCheckoutIntent() {
21552
21552
  }
21553
21553
  const FAB_SIZE = 52;
21554
21554
  const FAB_INSET = 16;
21555
- const STYLE_ID$3 = "setto-edit-layout";
21555
+ const STYLE_ID$2 = "setto-edit-layout";
21556
21556
  const HTML_CLASS = "setto-edit-mode";
21557
21557
  function useSettoDocumentLayout(active) {
21558
21558
  useEffect(() => {
@@ -21561,10 +21561,10 @@ function useSettoDocumentLayout(active) {
21561
21561
  html.classList.add(HTML_CLASS);
21562
21562
  html.style.setProperty("--setto-fab-size", `${FAB_SIZE}px`);
21563
21563
  html.style.setProperty("--setto-fab-inset", `${FAB_INSET}px`);
21564
- let styleEl = document.getElementById(STYLE_ID$3);
21564
+ let styleEl = document.getElementById(STYLE_ID$2);
21565
21565
  if (!styleEl) {
21566
21566
  styleEl = document.createElement("style");
21567
- styleEl.id = STYLE_ID$3;
21567
+ styleEl.id = STYLE_ID$2;
21568
21568
  styleEl.textContent = `
21569
21569
  html.setto-edit-mode {
21570
21570
  width: 100%;
@@ -21838,202 +21838,6 @@ function useGuestEdit() {
21838
21838
  }
21839
21839
  };
21840
21840
  }
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
21841
  function isLinkContext(el) {
22038
21842
  const key = el.getAttribute("data-setto-key");
22039
21843
  if (key?.startsWith("lang.")) return false;
@@ -22839,12 +22643,12 @@ function SettoImage({ srcKey, alt, className, style, ...rest }) {
22839
22643
  )
22840
22644
  ] });
22841
22645
  }
22842
- const STYLE_ID$2 = "setto-repeater-styles";
22646
+ const STYLE_ID$1 = "setto-repeater-styles";
22843
22647
  function useRepeaterStyles() {
22844
22648
  useEffect(() => {
22845
- if (document.getElementById(STYLE_ID$2)) return;
22649
+ if (document.getElementById(STYLE_ID$1)) return;
22846
22650
  const el = document.createElement("style");
22847
- el.id = STYLE_ID$2;
22651
+ el.id = STYLE_ID$1;
22848
22652
  el.textContent = `
22849
22653
  .setto-repeater-remove {
22850
22654
  opacity: 0;
@@ -23129,276 +22933,6 @@ function buildEnvelope(fields, values, extra) {
23129
22933
  function settoSubmissionUrl(apiUrl, siteId, formId) {
23130
22934
  return `${apiUrl.replace(/\/+$/, "")}/sites/${encodeURIComponent(siteId)}/forms/${encodeURIComponent(formId)}/submissions`;
23131
22935
  }
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
22936
  const HONEYPOT_NAME = "website";
23403
22937
  const warnedEndpointKeys = /* @__PURE__ */ new Set();
23404
22938
  function cx(...parts) {
@@ -23414,6 +22948,7 @@ function SettoForm({
23414
22948
  }) {
23415
22949
  const { t, i18n } = useTranslation();
23416
22950
  const { config, editMode, store } = useSetto();
22951
+ const FieldTools = useSettoChromeSlot("form-field-tools");
23417
22952
  const guestEdit = useGuestEdit();
23418
22953
  const editable = editMode || guestEdit.active;
23419
22954
  const lng = i18n.language;
@@ -23661,8 +23196,8 @@ function SettoForm({
23661
23196
  }
23662
23197
  ),
23663
23198
  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
- FormFieldTools,
23199
+ editable && store && FieldTools ? /* @__PURE__ */ jsx(
23200
+ FieldTools,
23666
23201
  {
23667
23202
  field,
23668
23203
  index,
@@ -25063,204 +24598,6 @@ function collectRelativeKeys(nodes, prefix, out) {
25063
24598
  }
25064
24599
  }
25065
24600
  }
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
24601
  export {
25265
24602
  AuthGate,
25266
24603
  BLOCK_REGISTRY,
@@ -25271,14 +24608,10 @@ export {
25271
24608
  FAB_SIZE,
25272
24609
  FORM_FIELD_TYPES,
25273
24610
  FORM_FIELD_TYPE_LABELS,
24611
+ FloatingPopover,
25274
24612
  GuestEditProvider,
25275
- IN_PROGRESS_STATUSES,
25276
- PublishProgressBar,
25277
- PublishProgressSteps,
25278
24613
  SETTO_BASE,
25279
24614
  SETTO_MODULES,
25280
- STALE_BUILDING_MS,
25281
- STALE_PENDING_MS,
25282
24615
  SettoAdminApp,
25283
24616
  SettoAnimation,
25284
24617
  SettoBlock,
@@ -25289,7 +24622,6 @@ export {
25289
24622
  SettoIcon,
25290
24623
  SettoImage,
25291
24624
  SettoMark,
25292
- SettoPreviewBar,
25293
24625
  SettoProvider,
25294
24626
  SettoRepeater,
25295
24627
  SettoSection,
@@ -25300,30 +24632,25 @@ export {
25300
24632
  checkoutIntentUrl,
25301
24633
  currentAdminView,
25302
24634
  extractContentKeys,
25303
- fetchInProgressDeploymentId,
24635
+ fieldTypeHasOptions,
24636
+ fieldTypeHasPlaceholder,
25304
24637
  formFieldsPrefix,
25305
24638
  isAdminDeploymentView,
25306
24639
  isAdminPanelView,
25307
24640
  isAdminRoute,
25308
- isDeploymentInProgress,
25309
- isDeploymentStale,
25310
24641
  isFormFieldType,
25311
24642
  isLinkContext,
25312
- isPreviewHost,
25313
24643
  nodeChildren,
25314
24644
  parseBlockTree,
25315
- previewBarState,
25316
24645
  readLocaleFields,
25317
24646
  settoSubmissionUrl,
25318
24647
  siteHomeUrl,
25319
24648
  useCoarsePointer,
25320
- useDeploymentStatus,
25321
24649
  useGuestEdit,
25322
24650
  useSectionTheme,
25323
24651
  useSetto,
25324
24652
  useSettoChromeSlot,
25325
24653
  validateBlockTree,
25326
- waitForDeploymentByCommit,
25327
24654
  wantsCheckout,
25328
24655
  withoutCheckoutIntent
25329
24656
  };