@sevenfold/setto-client 0.14.0 → 0.15.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/chrome.d.ts +2 -0
- package/dist/guest-edit.d.ts +13 -7
- package/dist/index.d.ts +2 -0
- package/dist/provider.d.ts +12 -0
- package/dist/section-context.d.ts +11 -6
- package/dist/setto-client.js +77 -517
- package/dist/setto-client.js.map +1 -1
- package/package.json +1 -1
- package/dist/edit-mode/link-hint.d.ts +0 -5
- package/dist/edit-mode/section-toolbar.d.ts +0 -1
- package/dist/lib/color-utils.d.ts +0 -4
package/dist/setto-client.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx, jsxs, Fragment } from "react/jsx-runtime";
|
|
2
2
|
import * as React from "react";
|
|
3
|
-
import { useState, useRef, useEffect, useMemo,
|
|
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";
|
|
@@ -21452,6 +21452,7 @@ function SettoChrome() {
|
|
|
21452
21452
|
currentSite: null,
|
|
21453
21453
|
sitesLoaded: false,
|
|
21454
21454
|
editMode: false,
|
|
21455
|
+
guestEdit: false,
|
|
21455
21456
|
authLoading: true
|
|
21456
21457
|
}));
|
|
21457
21458
|
stateRef.current = () => ({
|
|
@@ -21464,6 +21465,7 @@ function SettoChrome() {
|
|
|
21464
21465
|
} : null,
|
|
21465
21466
|
sitesLoaded: setto.sites !== null,
|
|
21466
21467
|
editMode: setto.editMode,
|
|
21468
|
+
guestEdit: setto.guestEdit,
|
|
21467
21469
|
authLoading: setto.authLoading
|
|
21468
21470
|
});
|
|
21469
21471
|
useEffect(() => {
|
|
@@ -21569,9 +21571,14 @@ function useSettoDocumentLayout(active) {
|
|
|
21569
21571
|
overflow-x: clip;
|
|
21570
21572
|
}
|
|
21571
21573
|
|
|
21574
|
+
/*
|
|
21575
|
+
* Anchored by its right edge, which is also what makes the menu open
|
|
21576
|
+
* leftwards: the pill grows and this corner does not move, so the
|
|
21577
|
+
* button somebody just tapped stays under their thumb.
|
|
21578
|
+
*/
|
|
21572
21579
|
html.setto-edit-mode #setto-toolbar-root {
|
|
21573
21580
|
position: fixed;
|
|
21574
|
-
|
|
21581
|
+
right: var(--setto-fab-inset);
|
|
21575
21582
|
bottom: var(--setto-fab-inset);
|
|
21576
21583
|
z-index: 2147483647;
|
|
21577
21584
|
}
|
|
@@ -21625,492 +21632,6 @@ function notifyLayoutChange() {
|
|
|
21625
21632
|
window.dispatchEvent(new Event("resize"));
|
|
21626
21633
|
});
|
|
21627
21634
|
}
|
|
21628
|
-
function isLinkContext(el) {
|
|
21629
|
-
const key = el.getAttribute("data-setto-key");
|
|
21630
|
-
if (key?.startsWith("lang.")) return false;
|
|
21631
|
-
if (el.closest("[data-setto-no-hint]")) return false;
|
|
21632
|
-
return !!(el.closest("a[href]") || el.closest("button"));
|
|
21633
|
-
}
|
|
21634
|
-
function activateLinkContext(el) {
|
|
21635
|
-
const anchor = el.closest("a[href]");
|
|
21636
|
-
if (anchor instanceof HTMLAnchorElement) {
|
|
21637
|
-
anchor.click();
|
|
21638
|
-
return;
|
|
21639
|
-
}
|
|
21640
|
-
const button = el.closest("button");
|
|
21641
|
-
if (button instanceof HTMLButtonElement) {
|
|
21642
|
-
button.click();
|
|
21643
|
-
}
|
|
21644
|
-
}
|
|
21645
|
-
const QUERY = "(hover: none) and (pointer: coarse)";
|
|
21646
|
-
function subscribe(onChange) {
|
|
21647
|
-
const mq = window.matchMedia(QUERY);
|
|
21648
|
-
mq.addEventListener("change", onChange);
|
|
21649
|
-
return () => mq.removeEventListener("change", onChange);
|
|
21650
|
-
}
|
|
21651
|
-
function getSnapshot() {
|
|
21652
|
-
return window.matchMedia(QUERY).matches;
|
|
21653
|
-
}
|
|
21654
|
-
function useCoarsePointer() {
|
|
21655
|
-
return useSyncExternalStore(subscribe, getSnapshot, () => false);
|
|
21656
|
-
}
|
|
21657
|
-
const MARGIN$2 = 10;
|
|
21658
|
-
const OFFSET$1 = 14;
|
|
21659
|
-
function hintLabel() {
|
|
21660
|
-
const isMac = navigator.platform.toUpperCase().includes("MAC");
|
|
21661
|
-
return isMac ? "⌘+klikk for å åpne" : "Ctrl+klikk for å åpne";
|
|
21662
|
-
}
|
|
21663
|
-
function placeTooltip(anchor, width, height) {
|
|
21664
|
-
const vw = window.innerWidth;
|
|
21665
|
-
const vh = window.innerHeight;
|
|
21666
|
-
let left = anchor.x + OFFSET$1;
|
|
21667
|
-
let top = anchor.y + OFFSET$1;
|
|
21668
|
-
if (left + width > vw - MARGIN$2) {
|
|
21669
|
-
left = anchor.x - width - OFFSET$1;
|
|
21670
|
-
}
|
|
21671
|
-
if (left < MARGIN$2) {
|
|
21672
|
-
left = MARGIN$2;
|
|
21673
|
-
}
|
|
21674
|
-
if (top + height > vh - MARGIN$2) {
|
|
21675
|
-
top = anchor.y - height - OFFSET$1;
|
|
21676
|
-
}
|
|
21677
|
-
if (top < MARGIN$2) {
|
|
21678
|
-
top = MARGIN$2;
|
|
21679
|
-
}
|
|
21680
|
-
return { left, top };
|
|
21681
|
-
}
|
|
21682
|
-
function EditLinkHint() {
|
|
21683
|
-
const coarsePointer = useCoarsePointer();
|
|
21684
|
-
const [anchor, setAnchor] = useState(null);
|
|
21685
|
-
const [position, setPosition] = useState(null);
|
|
21686
|
-
const tipRef = useRef(null);
|
|
21687
|
-
const text = hintLabel();
|
|
21688
|
-
useEffect(() => {
|
|
21689
|
-
if (coarsePointer) return;
|
|
21690
|
-
const onMove = (e) => {
|
|
21691
|
-
const target = e.target;
|
|
21692
|
-
if (!(target instanceof Element)) {
|
|
21693
|
-
setAnchor(null);
|
|
21694
|
-
return;
|
|
21695
|
-
}
|
|
21696
|
-
const editable = target.closest("[data-setto-key]");
|
|
21697
|
-
if (!(editable instanceof HTMLElement) || !isLinkContext(editable)) {
|
|
21698
|
-
setAnchor(null);
|
|
21699
|
-
return;
|
|
21700
|
-
}
|
|
21701
|
-
setAnchor({ x: e.clientX, y: e.clientY });
|
|
21702
|
-
};
|
|
21703
|
-
document.addEventListener("mousemove", onMove, true);
|
|
21704
|
-
return () => document.removeEventListener("mousemove", onMove, true);
|
|
21705
|
-
}, [coarsePointer]);
|
|
21706
|
-
useLayoutEffect(() => {
|
|
21707
|
-
if (!anchor || !tipRef.current) {
|
|
21708
|
-
setPosition(null);
|
|
21709
|
-
return;
|
|
21710
|
-
}
|
|
21711
|
-
const { width, height } = tipRef.current.getBoundingClientRect();
|
|
21712
|
-
setPosition(placeTooltip(anchor, width, height));
|
|
21713
|
-
}, [anchor, text]);
|
|
21714
|
-
if (coarsePointer || !anchor) return null;
|
|
21715
|
-
const style = {
|
|
21716
|
-
position: "fixed",
|
|
21717
|
-
left: position?.left ?? anchor.x + OFFSET$1,
|
|
21718
|
-
top: position?.top ?? anchor.y + OFFSET$1,
|
|
21719
|
-
visibility: position ? "visible" : "hidden",
|
|
21720
|
-
background: "#1a1a1a",
|
|
21721
|
-
color: "#fff",
|
|
21722
|
-
fontSize: 11,
|
|
21723
|
-
padding: "4px 8px",
|
|
21724
|
-
borderRadius: 4,
|
|
21725
|
-
pointerEvents: "none",
|
|
21726
|
-
whiteSpace: "nowrap",
|
|
21727
|
-
fontFamily: "system-ui, sans-serif",
|
|
21728
|
-
boxShadow: "0 2px 8px rgba(0,0,0,0.15)"
|
|
21729
|
-
};
|
|
21730
|
-
return /* @__PURE__ */ jsx("div", { ref: tipRef, style, children: text });
|
|
21731
|
-
}
|
|
21732
|
-
function colorsMatch(a, b) {
|
|
21733
|
-
const na = normalizeColor(a);
|
|
21734
|
-
const nb = normalizeColor(b);
|
|
21735
|
-
if (!na || !nb) return false;
|
|
21736
|
-
return na === nb;
|
|
21737
|
-
}
|
|
21738
|
-
function normalizeColor(color) {
|
|
21739
|
-
const trimmed = color.trim().toLowerCase();
|
|
21740
|
-
if (/^#[0-9a-f]{3}$/.test(trimmed)) {
|
|
21741
|
-
const [, r, g, b] = trimmed.match(/^#(.)(.)(.)$/) ?? [];
|
|
21742
|
-
return `#${r}${r}${g}${g}${b}${b}`;
|
|
21743
|
-
}
|
|
21744
|
-
if (/^#[0-9a-f]{6}$/.test(trimmed)) return trimmed;
|
|
21745
|
-
const rgb = trimmed.match(/^rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)/);
|
|
21746
|
-
if (rgb) {
|
|
21747
|
-
const [, r, g, b] = rgb;
|
|
21748
|
-
if (r === void 0 || g === void 0 || b === void 0) return null;
|
|
21749
|
-
const hex = (n) => Number(n).toString(16).padStart(2, "0");
|
|
21750
|
-
return `#${hex(r)}${hex(g)}${hex(b)}`;
|
|
21751
|
-
}
|
|
21752
|
-
return null;
|
|
21753
|
-
}
|
|
21754
|
-
function swatchBorder(color) {
|
|
21755
|
-
const n = normalizeColor(color);
|
|
21756
|
-
if (n === "#ffffff" || n === "#e6dccf") return "#ccc";
|
|
21757
|
-
return "transparent";
|
|
21758
|
-
}
|
|
21759
|
-
const SectionEditContext = createContext(null);
|
|
21760
|
-
function SectionEditProvider({ children }) {
|
|
21761
|
-
const [selectedId, setSelectedId] = useState(null);
|
|
21762
|
-
const selectSection = useCallback((id) => {
|
|
21763
|
-
setSelectedId(id);
|
|
21764
|
-
}, []);
|
|
21765
|
-
const value = useMemo(
|
|
21766
|
-
() => ({ selectedId, selectSection }),
|
|
21767
|
-
[selectedId, selectSection]
|
|
21768
|
-
);
|
|
21769
|
-
return /* @__PURE__ */ jsx(SectionEditContext.Provider, { value, children });
|
|
21770
|
-
}
|
|
21771
|
-
function useSectionEdit() {
|
|
21772
|
-
const ctx = useContext(SectionEditContext);
|
|
21773
|
-
if (!ctx) {
|
|
21774
|
-
throw new Error("useSectionEdit() must be used inside SectionEditProvider");
|
|
21775
|
-
}
|
|
21776
|
-
return ctx;
|
|
21777
|
-
}
|
|
21778
|
-
const SECTION_TOOLBAR_HEIGHT = 44;
|
|
21779
|
-
const GAP = 8;
|
|
21780
|
-
const MARGIN$1 = 8;
|
|
21781
|
-
function computePosition(targetEl, toolbarW, toolbarH) {
|
|
21782
|
-
const rect = targetEl.getBoundingClientRect();
|
|
21783
|
-
let left = rect.left + (rect.width - toolbarW) / 2;
|
|
21784
|
-
left = Math.max(MARGIN$1, Math.min(left, window.innerWidth - toolbarW - MARGIN$1));
|
|
21785
|
-
const top = Math.max(MARGIN$1, rect.top - toolbarH - GAP);
|
|
21786
|
-
return { top, left };
|
|
21787
|
-
}
|
|
21788
|
-
function BrandColorPicker({
|
|
21789
|
-
label,
|
|
21790
|
-
value,
|
|
21791
|
-
brandColors,
|
|
21792
|
-
open,
|
|
21793
|
-
onToggle,
|
|
21794
|
-
onSelect,
|
|
21795
|
-
popoverPlacement
|
|
21796
|
-
}) {
|
|
21797
|
-
const buttonRef = useRef(null);
|
|
21798
|
-
const popoverRef = useRef(null);
|
|
21799
|
-
const [popoverPos, setPopoverPos] = useState(null);
|
|
21800
|
-
const matched = brandColors.find((b) => colorsMatch(value, b.value));
|
|
21801
|
-
const displayColor = matched?.value ?? (value || "#cccccc");
|
|
21802
|
-
const displayLabel = matched?.label ?? label;
|
|
21803
|
-
useLayoutEffect(() => {
|
|
21804
|
-
if (!open || !buttonRef.current) {
|
|
21805
|
-
setPopoverPos(null);
|
|
21806
|
-
return;
|
|
21807
|
-
}
|
|
21808
|
-
const update = () => {
|
|
21809
|
-
const btn = buttonRef.current;
|
|
21810
|
-
const pop = popoverRef.current;
|
|
21811
|
-
if (!btn) return;
|
|
21812
|
-
const rect = btn.getBoundingClientRect();
|
|
21813
|
-
const popoverH = pop?.offsetHeight ?? 120;
|
|
21814
|
-
const popoverW = pop?.offsetWidth ?? 180;
|
|
21815
|
-
let top = popoverPlacement === "above" ? rect.top - popoverH - 6 : rect.bottom + 6;
|
|
21816
|
-
let left = rect.left;
|
|
21817
|
-
if (left + popoverW > window.innerWidth - 8) {
|
|
21818
|
-
left = window.innerWidth - popoverW - 8;
|
|
21819
|
-
}
|
|
21820
|
-
if (left < 8) left = 8;
|
|
21821
|
-
if (top < 8) top = rect.bottom + 6;
|
|
21822
|
-
if (top + popoverH > window.innerHeight - 8) {
|
|
21823
|
-
top = rect.top - popoverH - 6;
|
|
21824
|
-
}
|
|
21825
|
-
setPopoverPos({ top, left });
|
|
21826
|
-
};
|
|
21827
|
-
update();
|
|
21828
|
-
requestAnimationFrame(update);
|
|
21829
|
-
window.addEventListener("scroll", update, true);
|
|
21830
|
-
window.addEventListener("resize", update);
|
|
21831
|
-
return () => {
|
|
21832
|
-
window.removeEventListener("scroll", update, true);
|
|
21833
|
-
window.removeEventListener("resize", update);
|
|
21834
|
-
};
|
|
21835
|
-
}, [open, popoverPlacement, brandColors.length]);
|
|
21836
|
-
const popover = open ? /* @__PURE__ */ jsx(
|
|
21837
|
-
"div",
|
|
21838
|
-
{
|
|
21839
|
-
ref: popoverRef,
|
|
21840
|
-
role: "listbox",
|
|
21841
|
-
"aria-label": label,
|
|
21842
|
-
"data-setto-ui": true,
|
|
21843
|
-
style: {
|
|
21844
|
-
position: "fixed",
|
|
21845
|
-
top: popoverPos?.top ?? -9999,
|
|
21846
|
-
left: popoverPos?.left ?? 0,
|
|
21847
|
-
visibility: popoverPos ? "visible" : "hidden",
|
|
21848
|
-
zIndex: 2147483647,
|
|
21849
|
-
display: "grid",
|
|
21850
|
-
gridTemplateColumns: "repeat(4, 1fr)",
|
|
21851
|
-
gap: 4,
|
|
21852
|
-
padding: 8,
|
|
21853
|
-
background: "#fff",
|
|
21854
|
-
border: "1px solid #e0e0e0",
|
|
21855
|
-
borderRadius: 8,
|
|
21856
|
-
boxShadow: "0 8px 24px rgba(0,0,0,0.15)",
|
|
21857
|
-
minWidth: 160
|
|
21858
|
-
},
|
|
21859
|
-
onMouseDown: (e) => e.stopPropagation(),
|
|
21860
|
-
onClick: (e) => e.stopPropagation(),
|
|
21861
|
-
children: brandColors.map((brand) => {
|
|
21862
|
-
const selected = colorsMatch(value, brand.value);
|
|
21863
|
-
return /* @__PURE__ */ jsxs(
|
|
21864
|
-
"button",
|
|
21865
|
-
{
|
|
21866
|
-
type: "button",
|
|
21867
|
-
role: "option",
|
|
21868
|
-
"aria-selected": selected,
|
|
21869
|
-
title: brand.label,
|
|
21870
|
-
onClick: () => onSelect(brand.value),
|
|
21871
|
-
style: {
|
|
21872
|
-
display: "flex",
|
|
21873
|
-
flexDirection: "column",
|
|
21874
|
-
alignItems: "center",
|
|
21875
|
-
gap: 4,
|
|
21876
|
-
padding: 6,
|
|
21877
|
-
background: selected ? "#f5f3ff" : "transparent",
|
|
21878
|
-
border: selected ? "1px solid #640AFF" : "1px solid transparent",
|
|
21879
|
-
borderRadius: 6,
|
|
21880
|
-
cursor: "pointer"
|
|
21881
|
-
},
|
|
21882
|
-
children: [
|
|
21883
|
-
/* @__PURE__ */ jsx(
|
|
21884
|
-
"span",
|
|
21885
|
-
{
|
|
21886
|
-
"aria-hidden": true,
|
|
21887
|
-
style: {
|
|
21888
|
-
width: 24,
|
|
21889
|
-
height: 24,
|
|
21890
|
-
borderRadius: "50%",
|
|
21891
|
-
background: brand.value,
|
|
21892
|
-
border: `1px solid ${swatchBorder(brand.value)}`
|
|
21893
|
-
}
|
|
21894
|
-
}
|
|
21895
|
-
),
|
|
21896
|
-
/* @__PURE__ */ jsx("span", { style: { fontSize: 10, color: "#666", lineHeight: 1.2 }, children: brand.label })
|
|
21897
|
-
]
|
|
21898
|
-
},
|
|
21899
|
-
brand.value
|
|
21900
|
-
);
|
|
21901
|
-
})
|
|
21902
|
-
}
|
|
21903
|
-
) : null;
|
|
21904
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
21905
|
-
/* @__PURE__ */ jsxs(
|
|
21906
|
-
"button",
|
|
21907
|
-
{
|
|
21908
|
-
ref: buttonRef,
|
|
21909
|
-
type: "button",
|
|
21910
|
-
"data-setto-ui": true,
|
|
21911
|
-
onMouseDown: (e) => e.stopPropagation(),
|
|
21912
|
-
onClick: (e) => {
|
|
21913
|
-
e.stopPropagation();
|
|
21914
|
-
onToggle();
|
|
21915
|
-
},
|
|
21916
|
-
"aria-expanded": open,
|
|
21917
|
-
"aria-haspopup": "listbox",
|
|
21918
|
-
"aria-label": `${label}: ${displayLabel}`,
|
|
21919
|
-
title: `${label}: ${displayLabel}`,
|
|
21920
|
-
style: {
|
|
21921
|
-
display: "inline-flex",
|
|
21922
|
-
alignItems: "center",
|
|
21923
|
-
gap: 6,
|
|
21924
|
-
padding: "4px 8px 4px 6px",
|
|
21925
|
-
background: open ? "#f5f3ff" : "#f7f7f7",
|
|
21926
|
-
border: open ? "1px solid #640AFF" : "1px solid #e0e0e0",
|
|
21927
|
-
borderRadius: 6,
|
|
21928
|
-
cursor: "pointer",
|
|
21929
|
-
fontSize: 11,
|
|
21930
|
-
color: "#444",
|
|
21931
|
-
whiteSpace: "nowrap",
|
|
21932
|
-
flexShrink: 0
|
|
21933
|
-
},
|
|
21934
|
-
children: [
|
|
21935
|
-
/* @__PURE__ */ jsx(
|
|
21936
|
-
"span",
|
|
21937
|
-
{
|
|
21938
|
-
"aria-hidden": true,
|
|
21939
|
-
style: {
|
|
21940
|
-
width: 18,
|
|
21941
|
-
height: 18,
|
|
21942
|
-
borderRadius: "50%",
|
|
21943
|
-
background: displayColor,
|
|
21944
|
-
border: `1px solid ${swatchBorder(displayColor)}`,
|
|
21945
|
-
flexShrink: 0
|
|
21946
|
-
}
|
|
21947
|
-
}
|
|
21948
|
-
),
|
|
21949
|
-
/* @__PURE__ */ jsx("span", { children: label }),
|
|
21950
|
-
/* @__PURE__ */ jsx("span", { "aria-hidden": true, style: { color: "#999", fontSize: 10, lineHeight: 1 }, children: "▾" })
|
|
21951
|
-
]
|
|
21952
|
-
}
|
|
21953
|
-
),
|
|
21954
|
-
popover ? createPortal(popover, document.body) : null
|
|
21955
|
-
] });
|
|
21956
|
-
}
|
|
21957
|
-
function SectionToolbar() {
|
|
21958
|
-
const { themeStore, config } = useSetto();
|
|
21959
|
-
const { selectedId, selectSection } = useSectionEdit();
|
|
21960
|
-
const [pos, setPos] = useState(null);
|
|
21961
|
-
const [openField, setOpenField] = useState(null);
|
|
21962
|
-
const barRef = useRef(null);
|
|
21963
|
-
const schema = selectedId ? config.sectionSchemas?.[selectedId] : null;
|
|
21964
|
-
const brandColors = config.brandColors ?? [];
|
|
21965
|
-
useLayoutEffect(() => {
|
|
21966
|
-
if (!selectedId) {
|
|
21967
|
-
setPos(null);
|
|
21968
|
-
return;
|
|
21969
|
-
}
|
|
21970
|
-
const el = document.querySelector(
|
|
21971
|
-
`[data-setto-section="${selectedId}"]`
|
|
21972
|
-
);
|
|
21973
|
-
if (!el) {
|
|
21974
|
-
setPos(null);
|
|
21975
|
-
return;
|
|
21976
|
-
}
|
|
21977
|
-
const update = () => {
|
|
21978
|
-
const bar = barRef.current;
|
|
21979
|
-
if (!bar) return;
|
|
21980
|
-
const w = bar.offsetWidth;
|
|
21981
|
-
const h = bar.offsetHeight || SECTION_TOOLBAR_HEIGHT;
|
|
21982
|
-
setPos(computePosition(el, w, h));
|
|
21983
|
-
};
|
|
21984
|
-
update();
|
|
21985
|
-
requestAnimationFrame(update);
|
|
21986
|
-
window.addEventListener("scroll", update, true);
|
|
21987
|
-
window.addEventListener("resize", update);
|
|
21988
|
-
return () => {
|
|
21989
|
-
window.removeEventListener("scroll", update, true);
|
|
21990
|
-
window.removeEventListener("resize", update);
|
|
21991
|
-
};
|
|
21992
|
-
}, [selectedId, openField, schema?.fields.length]);
|
|
21993
|
-
useEffect(() => {
|
|
21994
|
-
setOpenField(null);
|
|
21995
|
-
}, [selectedId]);
|
|
21996
|
-
useEffect(() => {
|
|
21997
|
-
if (!openField) return;
|
|
21998
|
-
const close = (e) => {
|
|
21999
|
-
const target = e.target;
|
|
22000
|
-
if (barRef.current?.contains(target)) return;
|
|
22001
|
-
if (target instanceof Element && target.closest("[data-setto-ui]")) return;
|
|
22002
|
-
setOpenField(null);
|
|
22003
|
-
};
|
|
22004
|
-
document.addEventListener("mousedown", close);
|
|
22005
|
-
return () => document.removeEventListener("mousedown", close);
|
|
22006
|
-
}, [openField]);
|
|
22007
|
-
useEffect(() => {
|
|
22008
|
-
if (!selectedId) return;
|
|
22009
|
-
const onKey = (e) => {
|
|
22010
|
-
if (e.key !== "Escape") return;
|
|
22011
|
-
if (openField) {
|
|
22012
|
-
setOpenField(null);
|
|
22013
|
-
return;
|
|
22014
|
-
}
|
|
22015
|
-
selectSection(null);
|
|
22016
|
-
};
|
|
22017
|
-
window.addEventListener("keydown", onKey);
|
|
22018
|
-
return () => window.removeEventListener("keydown", onKey);
|
|
22019
|
-
}, [selectedId, selectSection, openField]);
|
|
22020
|
-
if (!selectedId || !schema || !themeStore || brandColors.length === 0) {
|
|
22021
|
-
return null;
|
|
22022
|
-
}
|
|
22023
|
-
const colors = themeStore.getSection(selectedId);
|
|
22024
|
-
return /* @__PURE__ */ jsxs(
|
|
22025
|
-
"div",
|
|
22026
|
-
{
|
|
22027
|
-
ref: barRef,
|
|
22028
|
-
role: "toolbar",
|
|
22029
|
-
"data-setto-ui": true,
|
|
22030
|
-
"data-setto-theme-id": selectedId,
|
|
22031
|
-
"aria-label": `Farger — ${schema.label}`,
|
|
22032
|
-
style: {
|
|
22033
|
-
position: "fixed",
|
|
22034
|
-
top: pos?.top ?? -9999,
|
|
22035
|
-
left: pos?.left ?? 0,
|
|
22036
|
-
width: "max-content",
|
|
22037
|
-
maxWidth: `calc(100vw - ${MARGIN$1 * 2}px)`,
|
|
22038
|
-
minHeight: SECTION_TOOLBAR_HEIGHT,
|
|
22039
|
-
zIndex: 2147483646,
|
|
22040
|
-
display: "flex",
|
|
22041
|
-
alignItems: "center",
|
|
22042
|
-
gap: 8,
|
|
22043
|
-
padding: "0 12px",
|
|
22044
|
-
background: "#ffffff",
|
|
22045
|
-
border: "1px solid #e0e0e0",
|
|
22046
|
-
borderRadius: 8,
|
|
22047
|
-
boxShadow: "0 4px 20px rgba(0,0,0,0.12)",
|
|
22048
|
-
fontFamily: 'system-ui, -apple-system, "Segoe UI", sans-serif',
|
|
22049
|
-
fontSize: 12,
|
|
22050
|
-
pointerEvents: "auto",
|
|
22051
|
-
userSelect: "none",
|
|
22052
|
-
visibility: pos ? "visible" : "hidden"
|
|
22053
|
-
},
|
|
22054
|
-
onMouseDown: (e) => e.stopPropagation(),
|
|
22055
|
-
onClick: (e) => e.stopPropagation(),
|
|
22056
|
-
children: [
|
|
22057
|
-
/* @__PURE__ */ jsx("span", { style: { fontWeight: 600, whiteSpace: "nowrap", flexShrink: 0 }, children: schema.label }),
|
|
22058
|
-
/* @__PURE__ */ jsx("span", { style: { color: "#ccc", flexShrink: 0 }, children: "·" }),
|
|
22059
|
-
/* @__PURE__ */ jsx(
|
|
22060
|
-
"div",
|
|
22061
|
-
{
|
|
22062
|
-
style: {
|
|
22063
|
-
display: "flex",
|
|
22064
|
-
alignItems: "center",
|
|
22065
|
-
gap: 6,
|
|
22066
|
-
flexWrap: "nowrap"
|
|
22067
|
-
},
|
|
22068
|
-
children: schema.fields.map((field) => /* @__PURE__ */ jsx(
|
|
22069
|
-
BrandColorPicker,
|
|
22070
|
-
{
|
|
22071
|
-
label: field.label,
|
|
22072
|
-
value: colors[field.key] ?? "",
|
|
22073
|
-
brandColors,
|
|
22074
|
-
open: openField === field.key,
|
|
22075
|
-
popoverPlacement: "below",
|
|
22076
|
-
onToggle: () => setOpenField((prev) => prev === field.key ? null : field.key),
|
|
22077
|
-
onSelect: (v) => {
|
|
22078
|
-
themeStore.set(selectedId, field.key, v);
|
|
22079
|
-
setOpenField(null);
|
|
22080
|
-
}
|
|
22081
|
-
},
|
|
22082
|
-
field.key
|
|
22083
|
-
))
|
|
22084
|
-
}
|
|
22085
|
-
),
|
|
22086
|
-
/* @__PURE__ */ jsx(
|
|
22087
|
-
"button",
|
|
22088
|
-
{
|
|
22089
|
-
type: "button",
|
|
22090
|
-
"data-setto-ui": true,
|
|
22091
|
-
onMouseDown: (e) => e.stopPropagation(),
|
|
22092
|
-
onClick: (e) => {
|
|
22093
|
-
e.stopPropagation();
|
|
22094
|
-
selectSection(null);
|
|
22095
|
-
},
|
|
22096
|
-
"aria-label": "Lukk",
|
|
22097
|
-
style: {
|
|
22098
|
-
flexShrink: 0,
|
|
22099
|
-
background: "transparent",
|
|
22100
|
-
border: "none",
|
|
22101
|
-
color: "#999",
|
|
22102
|
-
cursor: "pointer",
|
|
22103
|
-
fontSize: 16,
|
|
22104
|
-
lineHeight: 1,
|
|
22105
|
-
padding: "2px 6px"
|
|
22106
|
-
},
|
|
22107
|
-
children: "×"
|
|
22108
|
-
}
|
|
22109
|
-
)
|
|
22110
|
-
]
|
|
22111
|
-
}
|
|
22112
|
-
);
|
|
22113
|
-
}
|
|
22114
21635
|
function SettoMark({
|
|
22115
21636
|
size = 24,
|
|
22116
21637
|
letterColor = "#F2EBDF",
|
|
@@ -22175,6 +21696,8 @@ const pillStyle = {
|
|
|
22175
21696
|
function EditModeShell({ children, themeStore, active }) {
|
|
22176
21697
|
useSettoDocumentLayout(active);
|
|
22177
21698
|
const Toolbar = useSettoChromeSlot("toolbar");
|
|
21699
|
+
const SectionTools = useSettoChromeSlot("section-toolbar");
|
|
21700
|
+
const LinkHint = useSettoChromeSlot("link-hint");
|
|
22178
21701
|
const [toolbarRoot, setToolbarRoot] = useState(null);
|
|
22179
21702
|
const [hintRoot, setHintRoot] = useState(null);
|
|
22180
21703
|
const [sectionToolbarRoot, setSectionToolbarRoot] = useState(null);
|
|
@@ -22208,8 +21731,8 @@ function EditModeShell({ children, themeStore, active }) {
|
|
|
22208
21731
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
22209
21732
|
children,
|
|
22210
21733
|
active && toolbarRoot ? createPortal(Toolbar ? /* @__PURE__ */ jsx(Toolbar, {}) : /* @__PURE__ */ jsx(ToolbarUnavailable, {}), toolbarRoot) : null,
|
|
22211
|
-
active && hintRoot ? createPortal(/* @__PURE__ */ jsx(
|
|
22212
|
-
active && sectionToolbarRoot && themeStore ? createPortal(/* @__PURE__ */ jsx(
|
|
21734
|
+
active && hintRoot && LinkHint ? createPortal(/* @__PURE__ */ jsx(LinkHint, {}), hintRoot) : null,
|
|
21735
|
+
active && sectionToolbarRoot && themeStore && SectionTools ? createPortal(/* @__PURE__ */ jsx(SectionTools, {}), sectionToolbarRoot) : null
|
|
22213
21736
|
] });
|
|
22214
21737
|
}
|
|
22215
21738
|
const SettoContext = createContext(null);
|
|
@@ -22256,6 +21779,16 @@ function SettoProvider({ config, children }) {
|
|
|
22256
21779
|
setStoreReady(true);
|
|
22257
21780
|
}, [i18n, config.theme]);
|
|
22258
21781
|
const editMode = !!session && !!access.currentSite && !onAdminRoute && !authLoading && !access.loading;
|
|
21782
|
+
const [selectedSectionId, setSelectedSectionId] = useState(null);
|
|
21783
|
+
const selectSection = useCallback((id) => setSelectedSectionId(id), []);
|
|
21784
|
+
const [guestEdit, setGuestEdit] = useState(false);
|
|
21785
|
+
useEffect(() => {
|
|
21786
|
+
if (editMode && guestEdit) setGuestEdit(false);
|
|
21787
|
+
}, [editMode, guestEdit]);
|
|
21788
|
+
useEffect(() => {
|
|
21789
|
+
document.documentElement.classList.toggle("setto-guest-editing", guestEdit);
|
|
21790
|
+
return () => document.documentElement.classList.remove("setto-guest-editing");
|
|
21791
|
+
}, [guestEdit]);
|
|
22259
21792
|
const value = {
|
|
22260
21793
|
config,
|
|
22261
21794
|
supabase,
|
|
@@ -22267,10 +21800,14 @@ function SettoProvider({ config, children }) {
|
|
|
22267
21800
|
sites: access.sites,
|
|
22268
21801
|
store: storeReady ? storeRef.current : null,
|
|
22269
21802
|
themeStore: storeReady ? themeStoreRef.current : null,
|
|
22270
|
-
assetStore: storeReady ? assetStoreRef.current : null
|
|
21803
|
+
assetStore: storeReady ? assetStoreRef.current : null,
|
|
21804
|
+
selectedSectionId,
|
|
21805
|
+
selectSection,
|
|
21806
|
+
guestEdit,
|
|
21807
|
+
setGuestEdit
|
|
22271
21808
|
};
|
|
22272
21809
|
const editShellActive = editMode && !!value.store;
|
|
22273
|
-
return /* @__PURE__ */ jsx(SettoContext.Provider, { value, children: /* @__PURE__ */ jsxs(
|
|
21810
|
+
return /* @__PURE__ */ jsx(SettoContext.Provider, { value, children: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
22274
21811
|
/* @__PURE__ */ jsx(EditModeShell, { active: editShellActive, themeStore: value.themeStore, children }),
|
|
22275
21812
|
/* @__PURE__ */ jsx(SettoChrome, {})
|
|
22276
21813
|
] }) });
|
|
@@ -22282,36 +21819,24 @@ function useSetto() {
|
|
|
22282
21819
|
}
|
|
22283
21820
|
return ctx;
|
|
22284
21821
|
}
|
|
22285
|
-
const GuestEditContext = createContext(null);
|
|
22286
21822
|
function GuestEditProvider({ children }) {
|
|
22287
|
-
const {
|
|
22288
|
-
const
|
|
22289
|
-
|
|
22290
|
-
useEffect(() => {
|
|
22291
|
-
if (editMode && active) setActive(false);
|
|
22292
|
-
}, [editMode, active]);
|
|
22293
|
-
useEffect(() => {
|
|
22294
|
-
document.documentElement.classList.toggle("setto-guest-editing", active);
|
|
22295
|
-
return () => document.documentElement.classList.remove("setto-guest-editing");
|
|
22296
|
-
}, [active]);
|
|
22297
|
-
const start = useCallback(() => setActive(true), []);
|
|
22298
|
-
const stop = useCallback(() => {
|
|
22299
|
-
setActive(false);
|
|
22300
|
-
selectSection(null);
|
|
22301
|
-
}, [selectSection]);
|
|
22302
|
-
return /* @__PURE__ */ jsxs(GuestEditContext.Provider, { value: { active, start, stop }, children: [
|
|
21823
|
+
const { guestEdit, themeStore } = useSetto();
|
|
21824
|
+
const SectionTools = useSettoChromeSlot("section-toolbar");
|
|
21825
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
22303
21826
|
children,
|
|
22304
|
-
|
|
21827
|
+
guestEdit && themeStore && SectionTools ? createPortal(/* @__PURE__ */ jsx(SectionTools, {}), document.body) : null
|
|
22305
21828
|
] });
|
|
22306
21829
|
}
|
|
22307
21830
|
function useGuestEdit() {
|
|
22308
|
-
const
|
|
22309
|
-
|
|
22310
|
-
|
|
22311
|
-
|
|
22312
|
-
|
|
22313
|
-
|
|
22314
|
-
|
|
21831
|
+
const { guestEdit, setGuestEdit, selectSection } = useSetto();
|
|
21832
|
+
return {
|
|
21833
|
+
active: guestEdit,
|
|
21834
|
+
start: () => setGuestEdit(true),
|
|
21835
|
+
stop: () => {
|
|
21836
|
+
setGuestEdit(false);
|
|
21837
|
+
selectSection(null);
|
|
21838
|
+
}
|
|
21839
|
+
};
|
|
22315
21840
|
}
|
|
22316
21841
|
const PREVIEW_HOST = /(^|\.)site\.setto\.no$/i;
|
|
22317
21842
|
function isPreviewHost(hostname) {
|
|
@@ -22509,6 +22034,23 @@ const errorStyle = {
|
|
|
22509
22034
|
textOverflow: "ellipsis",
|
|
22510
22035
|
whiteSpace: "nowrap"
|
|
22511
22036
|
};
|
|
22037
|
+
function isLinkContext(el) {
|
|
22038
|
+
const key = el.getAttribute("data-setto-key");
|
|
22039
|
+
if (key?.startsWith("lang.")) return false;
|
|
22040
|
+
if (el.closest("[data-setto-no-hint]")) return false;
|
|
22041
|
+
return !!(el.closest("a[href]") || el.closest("button"));
|
|
22042
|
+
}
|
|
22043
|
+
function activateLinkContext(el) {
|
|
22044
|
+
const anchor = el.closest("a[href]");
|
|
22045
|
+
if (anchor instanceof HTMLAnchorElement) {
|
|
22046
|
+
anchor.click();
|
|
22047
|
+
return;
|
|
22048
|
+
}
|
|
22049
|
+
const button = el.closest("button");
|
|
22050
|
+
if (button instanceof HTMLButtonElement) {
|
|
22051
|
+
button.click();
|
|
22052
|
+
}
|
|
22053
|
+
}
|
|
22512
22054
|
const MARGIN = 10;
|
|
22513
22055
|
const OFFSET = 8;
|
|
22514
22056
|
const MIN_TOUCH = 44;
|
|
@@ -22623,6 +22165,18 @@ function rangeFromPoint(doc, x, y) {
|
|
|
22623
22165
|
range.collapse(true);
|
|
22624
22166
|
return range;
|
|
22625
22167
|
}
|
|
22168
|
+
const QUERY = "(hover: none) and (pointer: coarse)";
|
|
22169
|
+
function subscribe(onChange) {
|
|
22170
|
+
const mq = window.matchMedia(QUERY);
|
|
22171
|
+
mq.addEventListener("change", onChange);
|
|
22172
|
+
return () => mq.removeEventListener("change", onChange);
|
|
22173
|
+
}
|
|
22174
|
+
function getSnapshot() {
|
|
22175
|
+
return window.matchMedia(QUERY).matches;
|
|
22176
|
+
}
|
|
22177
|
+
function useCoarsePointer() {
|
|
22178
|
+
return useSyncExternalStore(subscribe, getSnapshot, () => false);
|
|
22179
|
+
}
|
|
22626
22180
|
function T({ k }) {
|
|
22627
22181
|
const { t, i18n } = useTranslation();
|
|
22628
22182
|
const { editMode, store } = useSetto();
|
|
@@ -22745,6 +22299,10 @@ function toBcp47(code) {
|
|
|
22745
22299
|
if (base === "en") return "en";
|
|
22746
22300
|
return code;
|
|
22747
22301
|
}
|
|
22302
|
+
function useSectionEdit() {
|
|
22303
|
+
const { selectedSectionId, selectSection } = useSetto();
|
|
22304
|
+
return { selectedId: selectedSectionId, selectSection };
|
|
22305
|
+
}
|
|
22748
22306
|
function isTextEditClick(target) {
|
|
22749
22307
|
if (target.closest("[data-setto-key]")) return true;
|
|
22750
22308
|
if (target.closest("[data-setto-ui]")) return true;
|
|
@@ -25750,6 +25308,7 @@ export {
|
|
|
25750
25308
|
isDeploymentInProgress,
|
|
25751
25309
|
isDeploymentStale,
|
|
25752
25310
|
isFormFieldType,
|
|
25311
|
+
isLinkContext,
|
|
25753
25312
|
isPreviewHost,
|
|
25754
25313
|
nodeChildren,
|
|
25755
25314
|
parseBlockTree,
|
|
@@ -25757,6 +25316,7 @@ export {
|
|
|
25757
25316
|
readLocaleFields,
|
|
25758
25317
|
settoSubmissionUrl,
|
|
25759
25318
|
siteHomeUrl,
|
|
25319
|
+
useCoarsePointer,
|
|
25760
25320
|
useDeploymentStatus,
|
|
25761
25321
|
useGuestEdit,
|
|
25762
25322
|
useSectionTheme,
|