@sevenfold/setto-client 0.2.9 → 0.3.4
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/SettoIcon.d.ts +20 -0
- package/dist/SettoImage.d.ts +10 -0
- package/dist/SettoRepeater.d.ts +15 -0
- package/dist/edit-mode/floating-popover.d.ts +17 -0
- package/dist/guest-edit.d.ts +16 -0
- package/dist/index.d.ts +8 -1
- package/dist/lib/api.d.ts +6 -5
- package/dist/lib/asset-store.d.ts +45 -0
- package/dist/lib/i18n-store.d.ts +11 -0
- package/dist/lib/image-paths.d.ts +12 -0
- package/dist/lib/nested-resource.d.ts +9 -0
- package/dist/provider.d.ts +2 -0
- package/dist/setto-client.js +770 -11
- package/dist/setto-client.js.map +1 -1
- package/dist/types.d.ts +5 -0
- package/package.json +1 -1
package/dist/setto-client.js
CHANGED
|
@@ -20748,15 +20748,37 @@ function createApi(args) {
|
|
|
20748
20748
|
if (!res.ok) throw await asError(res, "getContent failed");
|
|
20749
20749
|
return parseJson(res);
|
|
20750
20750
|
},
|
|
20751
|
-
async publish(siteId, files,
|
|
20751
|
+
async publish(siteId, files, options) {
|
|
20752
20752
|
const token = await bearer(supabase);
|
|
20753
|
+
const assets = options?.assets ?? [];
|
|
20754
|
+
if (assets.length === 0) {
|
|
20755
|
+
const res2 = await fetch(`${apiUrl}/sites/${encodeURIComponent(siteId)}/publish`, {
|
|
20756
|
+
method: "POST",
|
|
20757
|
+
headers: {
|
|
20758
|
+
"Content-Type": "application/json",
|
|
20759
|
+
Authorization: `Bearer ${token}`
|
|
20760
|
+
},
|
|
20761
|
+
body: JSON.stringify({ files, message: options?.message })
|
|
20762
|
+
});
|
|
20763
|
+
if (!res2.ok) throw await asError(res2, "publish failed");
|
|
20764
|
+
return parseJson(res2);
|
|
20765
|
+
}
|
|
20766
|
+
const form = new FormData();
|
|
20767
|
+
form.append(
|
|
20768
|
+
"manifest",
|
|
20769
|
+
JSON.stringify({
|
|
20770
|
+
files,
|
|
20771
|
+
assets: assets.map((a) => ({ path: a.repoPath })),
|
|
20772
|
+
message: options?.message
|
|
20773
|
+
})
|
|
20774
|
+
);
|
|
20775
|
+
assets.forEach((asset, index) => {
|
|
20776
|
+
form.append(`asset_${index}`, asset.file, asset.file.name);
|
|
20777
|
+
});
|
|
20753
20778
|
const res = await fetch(`${apiUrl}/sites/${encodeURIComponent(siteId)}/publish`, {
|
|
20754
20779
|
method: "POST",
|
|
20755
|
-
headers: {
|
|
20756
|
-
|
|
20757
|
-
Authorization: `Bearer ${token}`
|
|
20758
|
-
},
|
|
20759
|
-
body: JSON.stringify({ files, message })
|
|
20780
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
20781
|
+
body: form
|
|
20760
20782
|
});
|
|
20761
20783
|
if (!res.ok) throw await asError(res, "publish failed");
|
|
20762
20784
|
return parseJson(res);
|
|
@@ -20786,6 +20808,38 @@ function createApi(args) {
|
|
|
20786
20808
|
}
|
|
20787
20809
|
};
|
|
20788
20810
|
}
|
|
20811
|
+
function getNested(obj, key) {
|
|
20812
|
+
if (!obj || typeof obj !== "object") return void 0;
|
|
20813
|
+
const parts = key.split(".");
|
|
20814
|
+
let cur = obj;
|
|
20815
|
+
for (const part of parts) {
|
|
20816
|
+
if (!cur || typeof cur !== "object") return void 0;
|
|
20817
|
+
cur = cur[part];
|
|
20818
|
+
}
|
|
20819
|
+
return cur;
|
|
20820
|
+
}
|
|
20821
|
+
function deleteNested(obj, key) {
|
|
20822
|
+
const parts = key.split(".");
|
|
20823
|
+
let cur = obj;
|
|
20824
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
20825
|
+
const part = parts[i];
|
|
20826
|
+
const next = cur[part];
|
|
20827
|
+
if (!next || typeof next !== "object") return;
|
|
20828
|
+
cur = next;
|
|
20829
|
+
}
|
|
20830
|
+
delete cur[parts[parts.length - 1]];
|
|
20831
|
+
}
|
|
20832
|
+
function collectKeysUnder(obj, prefix) {
|
|
20833
|
+
const base = getNested(obj, prefix);
|
|
20834
|
+
if (!base || typeof base !== "object" || Array.isArray(base)) return [];
|
|
20835
|
+
return Object.keys(base).sort(compareListKeys);
|
|
20836
|
+
}
|
|
20837
|
+
function compareListKeys(a, b) {
|
|
20838
|
+
const na = Number(a);
|
|
20839
|
+
const nb = Number(b);
|
|
20840
|
+
if (!Number.isNaN(na) && !Number.isNaN(nb)) return na - nb;
|
|
20841
|
+
return a.localeCompare(b);
|
|
20842
|
+
}
|
|
20789
20843
|
const EMPTY_I18N_STORE_SNAPSHOT = {
|
|
20790
20844
|
drafts: /* @__PURE__ */ new Map(),
|
|
20791
20845
|
version: 0
|
|
@@ -20875,6 +20929,49 @@ class I18nStore {
|
|
|
20875
20929
|
size() {
|
|
20876
20930
|
return this.drafts.size;
|
|
20877
20931
|
}
|
|
20932
|
+
/** Returns sorted item keys under a list prefix, e.g. `faq.items` → ['1','2',…]. */
|
|
20933
|
+
getListKeys(prefix, lng) {
|
|
20934
|
+
const bundle = this.i18n.getResourceBundle(lng, this.ns);
|
|
20935
|
+
return collectKeysUnder(bundle, prefix);
|
|
20936
|
+
}
|
|
20937
|
+
/**
|
|
20938
|
+
* Adds a new list item under `prefix` with the given field defaults.
|
|
20939
|
+
* Adds the same key across all loaded languages.
|
|
20940
|
+
*/
|
|
20941
|
+
addListItem(prefix, _lng, template) {
|
|
20942
|
+
const langs = this.i18n.languages?.length ? this.i18n.languages : [_lng];
|
|
20943
|
+
const allKeys = langs.flatMap((l) => this.getListKeys(prefix, l));
|
|
20944
|
+
const numeric = allKeys.map(Number).filter((n) => !Number.isNaN(n));
|
|
20945
|
+
const nextKey = numeric.length > 0 ? String(Math.max(...numeric) + 1) : "1";
|
|
20946
|
+
for (const lang of langs) {
|
|
20947
|
+
for (const [field, value] of Object.entries(template)) {
|
|
20948
|
+
this.set(`${prefix}.${nextKey}.${field}`, lang, value);
|
|
20949
|
+
}
|
|
20950
|
+
}
|
|
20951
|
+
return nextKey;
|
|
20952
|
+
}
|
|
20953
|
+
/** Removes a list item and all nested keys under `prefix.itemKey` in every language. */
|
|
20954
|
+
removeListItem(prefix, itemKey, _lng) {
|
|
20955
|
+
const langs = this.i18n.languages?.length ? this.i18n.languages : [_lng];
|
|
20956
|
+
for (const lang of langs) {
|
|
20957
|
+
const bundle = this.i18n.getResourceBundle(lang, this.ns) ?? {};
|
|
20958
|
+
const item = getNested(bundle, `${prefix}.${itemKey}`);
|
|
20959
|
+
if (!item || typeof item !== "object") continue;
|
|
20960
|
+
for (const field of Object.keys(item)) {
|
|
20961
|
+
const fullKey = `${prefix}.${itemKey}.${field}`;
|
|
20962
|
+
const id = `${lang}::${fullKey}`;
|
|
20963
|
+
this.drafts.delete(id);
|
|
20964
|
+
}
|
|
20965
|
+
deleteNested(bundle, `${prefix}.${itemKey}`);
|
|
20966
|
+
this.i18n.addResourceBundle(lang, this.ns, bundle, true, true);
|
|
20967
|
+
}
|
|
20968
|
+
this.i18n.emit("languageChanged", this.i18n.language);
|
|
20969
|
+
this.bump();
|
|
20970
|
+
}
|
|
20971
|
+
/** Re-sorts list keys after removal (optional compaction — not used by default). */
|
|
20972
|
+
sortListKeys(prefix, lng) {
|
|
20973
|
+
return this.getListKeys(prefix, lng).sort(compareListKeys);
|
|
20974
|
+
}
|
|
20878
20975
|
/**
|
|
20879
20976
|
* Serialises the full resource bundle per language as it currently looks in
|
|
20880
20977
|
* i18next (with drafts applied). Used as the payload for /publish.
|
|
@@ -20896,6 +20993,93 @@ class I18nStore {
|
|
|
20896
20993
|
for (const fn of this.listeners) fn(this.cachedSnapshot);
|
|
20897
20994
|
}
|
|
20898
20995
|
}
|
|
20996
|
+
const SETTO_IMAGE_REPO_DIR = "public/images/setto";
|
|
20997
|
+
const SETTO_IMAGE_URL_PREFIX = "/images/setto";
|
|
20998
|
+
const MIME_EXT = {
|
|
20999
|
+
"image/png": "png",
|
|
21000
|
+
"image/jpeg": "jpg",
|
|
21001
|
+
"image/webp": "webp",
|
|
21002
|
+
"image/svg+xml": "svg"
|
|
21003
|
+
};
|
|
21004
|
+
function extensionFromMime(mime) {
|
|
21005
|
+
return MIME_EXT[mime] ?? "bin";
|
|
21006
|
+
}
|
|
21007
|
+
function generateSettoImagePaths(srcKey, file) {
|
|
21008
|
+
const ext = extensionFromMime(file.type);
|
|
21009
|
+
const slug = srcKey.replace(/[^a-zA-Z0-9]+/g, "-").replace(/^-|-$/g, "") || "image";
|
|
21010
|
+
const name = `${slug}-${Date.now()}.${ext}`;
|
|
21011
|
+
return {
|
|
21012
|
+
repoPath: `${SETTO_IMAGE_REPO_DIR}/${name}`,
|
|
21013
|
+
publicPath: `${SETTO_IMAGE_URL_PREFIX}/${name}`
|
|
21014
|
+
};
|
|
21015
|
+
}
|
|
21016
|
+
class AssetStore {
|
|
21017
|
+
drafts = /* @__PURE__ */ new Map();
|
|
21018
|
+
version = 0;
|
|
21019
|
+
listeners = /* @__PURE__ */ new Set();
|
|
21020
|
+
cachedSnapshot = {
|
|
21021
|
+
drafts: this.drafts,
|
|
21022
|
+
version: 0
|
|
21023
|
+
};
|
|
21024
|
+
snapshot() {
|
|
21025
|
+
return this.cachedSnapshot;
|
|
21026
|
+
}
|
|
21027
|
+
subscribe(listener) {
|
|
21028
|
+
this.listeners.add(listener);
|
|
21029
|
+
return () => this.listeners.delete(listener);
|
|
21030
|
+
}
|
|
21031
|
+
/** Returns a preview URL if a draft exists for this src key. */
|
|
21032
|
+
getPreviewUrl(srcKey) {
|
|
21033
|
+
return this.drafts.get(srcKey)?.previewUrl ?? null;
|
|
21034
|
+
}
|
|
21035
|
+
async set(args) {
|
|
21036
|
+
const existing = this.drafts.get(args.srcKey);
|
|
21037
|
+
if (existing) URL.revokeObjectURL(existing.previewUrl);
|
|
21038
|
+
const previewUrl = URL.createObjectURL(args.file);
|
|
21039
|
+
this.drafts.set(args.srcKey, {
|
|
21040
|
+
srcKey: args.srcKey,
|
|
21041
|
+
repoPath: args.repoPath,
|
|
21042
|
+
publicPath: args.publicPath,
|
|
21043
|
+
originalPublicPath: args.originalPublicPath,
|
|
21044
|
+
file: args.file,
|
|
21045
|
+
previewUrl
|
|
21046
|
+
});
|
|
21047
|
+
this.bump();
|
|
21048
|
+
}
|
|
21049
|
+
revert(srcKey) {
|
|
21050
|
+
const draft = this.drafts.get(srcKey);
|
|
21051
|
+
if (!draft) return null;
|
|
21052
|
+
URL.revokeObjectURL(draft.previewUrl);
|
|
21053
|
+
const original = draft.originalPublicPath;
|
|
21054
|
+
this.drafts.delete(srcKey);
|
|
21055
|
+
this.bump();
|
|
21056
|
+
return original;
|
|
21057
|
+
}
|
|
21058
|
+
revertAll() {
|
|
21059
|
+
for (const draft of this.drafts.values()) {
|
|
21060
|
+
URL.revokeObjectURL(draft.previewUrl);
|
|
21061
|
+
}
|
|
21062
|
+
this.drafts.clear();
|
|
21063
|
+
this.bump();
|
|
21064
|
+
}
|
|
21065
|
+
commit() {
|
|
21066
|
+
this.revertAll();
|
|
21067
|
+
}
|
|
21068
|
+
size() {
|
|
21069
|
+
return this.drafts.size;
|
|
21070
|
+
}
|
|
21071
|
+
pendingAssets() {
|
|
21072
|
+
return Array.from(this.drafts.values());
|
|
21073
|
+
}
|
|
21074
|
+
static isValidRepoPath(path) {
|
|
21075
|
+
return path.startsWith(`${SETTO_IMAGE_REPO_DIR}/`);
|
|
21076
|
+
}
|
|
21077
|
+
bump() {
|
|
21078
|
+
this.version += 1;
|
|
21079
|
+
this.cachedSnapshot = { drafts: this.drafts, version: this.version };
|
|
21080
|
+
for (const fn of this.listeners) fn(this.cachedSnapshot);
|
|
21081
|
+
}
|
|
21082
|
+
}
|
|
20899
21083
|
const EMPTY_THEME_STORE_SNAPSHOT = {
|
|
20900
21084
|
version: 0
|
|
20901
21085
|
};
|
|
@@ -21967,7 +22151,7 @@ function useEditBaseline() {
|
|
|
21967
22151
|
}, [session, store, config.siteId]);
|
|
21968
22152
|
}
|
|
21969
22153
|
function EditToolbar() {
|
|
21970
|
-
const { store, themeStore, api, config, supabase } = useSetto();
|
|
22154
|
+
const { store, themeStore, assetStore, api, config, supabase } = useSetto();
|
|
21971
22155
|
const { i18n } = useTranslation();
|
|
21972
22156
|
useEditBaseline();
|
|
21973
22157
|
const snap = useSyncExternalStore(
|
|
@@ -21982,6 +22166,12 @@ function EditToolbar() {
|
|
|
21982
22166
|
() => themeStore?.snapshot().version ?? 0,
|
|
21983
22167
|
() => 0
|
|
21984
22168
|
);
|
|
22169
|
+
useSyncExternalStore(
|
|
22170
|
+
(cb) => assetStore?.subscribe(() => cb()) ?? (() => {
|
|
22171
|
+
}),
|
|
22172
|
+
() => assetStore?.snapshot().version ?? 0,
|
|
22173
|
+
() => 0
|
|
22174
|
+
);
|
|
21985
22175
|
const [publishing, setPublishing] = useState(false);
|
|
21986
22176
|
const [activeDeployment, setActiveDeployment] = useState(null);
|
|
21987
22177
|
const [publishDialogOpen, setPublishDialogOpen] = useState(false);
|
|
@@ -22013,7 +22203,8 @@ function EditToolbar() {
|
|
|
22013
22203
|
}, [api, supabase, config.siteId]);
|
|
22014
22204
|
const textDraftCount = snap.drafts.size;
|
|
22015
22205
|
const themeDraftCount = themeStore?.size() ?? 0;
|
|
22016
|
-
const
|
|
22206
|
+
const assetDraftCount = assetStore?.size() ?? 0;
|
|
22207
|
+
const draftCount = textDraftCount + themeDraftCount + assetDraftCount;
|
|
22017
22208
|
const hasDrafts = draftCount > 0;
|
|
22018
22209
|
const showToolbarProgress = activeDeployment !== null && !publishDialogOpen && deploymentRow !== null && isDeploymentInProgress(deploymentRow.status) && !isDeploymentStale(deploymentRow);
|
|
22019
22210
|
const showPublishedNotice = publishedNotice && !publishDialogOpen;
|
|
@@ -22109,9 +22300,33 @@ function EditToolbar() {
|
|
|
22109
22300
|
content: JSON.stringify(themeStore.serialise(), null, 2) + "\n"
|
|
22110
22301
|
});
|
|
22111
22302
|
}
|
|
22303
|
+
if (assetStore && assetDraftCount > 0) {
|
|
22304
|
+
const result2 = await api.publish(config.siteId, files, {
|
|
22305
|
+
assets: assetStore.pendingAssets()
|
|
22306
|
+
});
|
|
22307
|
+
store.commit();
|
|
22308
|
+
themeStore?.commit();
|
|
22309
|
+
assetStore.commit();
|
|
22310
|
+
let deploymentId2 = result2.deploymentId;
|
|
22311
|
+
if (!deploymentId2 && result2.trackingDeferred) {
|
|
22312
|
+
deploymentId2 = await waitForDeploymentByCommit(
|
|
22313
|
+
supabase,
|
|
22314
|
+
config.siteId,
|
|
22315
|
+
result2.commitSha
|
|
22316
|
+
);
|
|
22317
|
+
}
|
|
22318
|
+
if (deploymentId2) {
|
|
22319
|
+
setActiveDeployment(deploymentId2);
|
|
22320
|
+
} else {
|
|
22321
|
+
setPublishDialogOpen(false);
|
|
22322
|
+
setPublishedNotice(true);
|
|
22323
|
+
}
|
|
22324
|
+
return;
|
|
22325
|
+
}
|
|
22112
22326
|
const result = await api.publish(config.siteId, files);
|
|
22113
22327
|
store.commit();
|
|
22114
22328
|
themeStore?.commit();
|
|
22329
|
+
assetStore?.commit();
|
|
22115
22330
|
let deploymentId = result.deploymentId;
|
|
22116
22331
|
if (!deploymentId && result.trackingDeferred) {
|
|
22117
22332
|
deploymentId = await waitForDeploymentByCommit(
|
|
@@ -22452,11 +22667,13 @@ function SettoProvider({ config, children }) {
|
|
|
22452
22667
|
const { i18n } = useTranslation();
|
|
22453
22668
|
const storeRef = useRef(null);
|
|
22454
22669
|
const themeStoreRef = useRef(null);
|
|
22670
|
+
const assetStoreRef = useRef(null);
|
|
22455
22671
|
const [storeReady, setStoreReady] = useState(false);
|
|
22456
22672
|
useEffect(() => {
|
|
22457
22673
|
if (!i18n) return;
|
|
22458
22674
|
storeRef.current = new I18nStore(i18n);
|
|
22459
22675
|
themeStoreRef.current = new ThemeStore(config.theme ?? {});
|
|
22676
|
+
assetStoreRef.current = new AssetStore();
|
|
22460
22677
|
setStoreReady(true);
|
|
22461
22678
|
}, [i18n, config.theme]);
|
|
22462
22679
|
const editMode = !!session && editParam;
|
|
@@ -22468,7 +22685,8 @@ function SettoProvider({ config, children }) {
|
|
|
22468
22685
|
authLoading,
|
|
22469
22686
|
editMode,
|
|
22470
22687
|
store: storeReady ? storeRef.current : null,
|
|
22471
|
-
themeStore: storeReady ? themeStoreRef.current : null
|
|
22688
|
+
themeStore: storeReady ? themeStoreRef.current : null,
|
|
22689
|
+
assetStore: storeReady ? assetStoreRef.current : null
|
|
22472
22690
|
};
|
|
22473
22691
|
return /* @__PURE__ */ jsx(SettoContext.Provider, { value, children: /* @__PURE__ */ jsx(SectionEditProvider, { children: editMode && value.store ? /* @__PURE__ */ jsx(EditModeShell, { themeStore: value.themeStore, children }) : children }) });
|
|
22474
22692
|
}
|
|
@@ -22479,6 +22697,30 @@ function useSetto() {
|
|
|
22479
22697
|
}
|
|
22480
22698
|
return ctx;
|
|
22481
22699
|
}
|
|
22700
|
+
const GuestEditContext = createContext(null);
|
|
22701
|
+
function GuestEditProvider({ children }) {
|
|
22702
|
+
const { editMode } = useSetto();
|
|
22703
|
+
const [active, setActive] = useState(false);
|
|
22704
|
+
useEffect(() => {
|
|
22705
|
+
if (editMode && active) setActive(false);
|
|
22706
|
+
}, [editMode, active]);
|
|
22707
|
+
useEffect(() => {
|
|
22708
|
+
document.documentElement.classList.toggle("setto-guest-editing", active);
|
|
22709
|
+
return () => document.documentElement.classList.remove("setto-guest-editing");
|
|
22710
|
+
}, [active]);
|
|
22711
|
+
const start = useCallback(() => setActive(true), []);
|
|
22712
|
+
const stop = useCallback(() => setActive(false), []);
|
|
22713
|
+
return /* @__PURE__ */ jsx(GuestEditContext.Provider, { value: { active, start, stop }, children });
|
|
22714
|
+
}
|
|
22715
|
+
function useGuestEdit() {
|
|
22716
|
+
const ctx = useContext(GuestEditContext);
|
|
22717
|
+
if (!ctx) {
|
|
22718
|
+
return { active: false, start: () => {
|
|
22719
|
+
}, stop: () => {
|
|
22720
|
+
} };
|
|
22721
|
+
}
|
|
22722
|
+
return ctx;
|
|
22723
|
+
}
|
|
22482
22724
|
const MARGIN = 10;
|
|
22483
22725
|
const OFFSET = 8;
|
|
22484
22726
|
const MIN_TOUCH = 44;
|
|
@@ -22596,6 +22838,8 @@ function rangeFromPoint(doc, x, y) {
|
|
|
22596
22838
|
function T({ k }) {
|
|
22597
22839
|
const { t, i18n } = useTranslation();
|
|
22598
22840
|
const { editMode, store } = useSetto();
|
|
22841
|
+
const guestEdit = useGuestEdit();
|
|
22842
|
+
const editable = editMode || guestEdit.active;
|
|
22599
22843
|
const ref = useRef(null);
|
|
22600
22844
|
const [focused, setFocused] = useState(false);
|
|
22601
22845
|
const [linkContext, setLinkContext] = useState(false);
|
|
@@ -22611,7 +22855,7 @@ function T({ k }) {
|
|
|
22611
22855
|
if (!el || focused || !editMode) return;
|
|
22612
22856
|
if (el.textContent !== value) el.textContent = value;
|
|
22613
22857
|
}, [value, focused, editMode]);
|
|
22614
|
-
if (!
|
|
22858
|
+
if (!editable) return /* @__PURE__ */ jsx(Fragment, { children: value });
|
|
22615
22859
|
const handleMouseDown = (e) => {
|
|
22616
22860
|
e.stopPropagation();
|
|
22617
22861
|
const el = e.currentTarget;
|
|
@@ -22657,8 +22901,18 @@ function T({ k }) {
|
|
|
22657
22901
|
commit(e.currentTarget);
|
|
22658
22902
|
};
|
|
22659
22903
|
const handleInput = (e) => {
|
|
22904
|
+
if (!editMode) return;
|
|
22660
22905
|
store?.set(k, i18n.language, e.currentTarget.textContent ?? "");
|
|
22661
22906
|
};
|
|
22907
|
+
const handlePaste = (e) => {
|
|
22908
|
+
e.preventDefault();
|
|
22909
|
+
const text = e.clipboardData.getData("text/plain");
|
|
22910
|
+
if (!text) return;
|
|
22911
|
+
const el = e.currentTarget;
|
|
22912
|
+
el.focus();
|
|
22913
|
+
document.execCommand("insertText", false, text);
|
|
22914
|
+
store?.set(k, i18n.language, el.textContent ?? "");
|
|
22915
|
+
};
|
|
22662
22916
|
const lang = toBcp47(i18n.language);
|
|
22663
22917
|
const showLinkChip = coarsePointer && focused && linkContext;
|
|
22664
22918
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
@@ -22679,6 +22933,7 @@ function T({ k }) {
|
|
|
22679
22933
|
},
|
|
22680
22934
|
onBlur: handleBlur,
|
|
22681
22935
|
onInput: handleInput,
|
|
22936
|
+
onPaste: handlePaste,
|
|
22682
22937
|
onMouseDown: handleMouseDown,
|
|
22683
22938
|
onMouseUp: handleMouseUp,
|
|
22684
22939
|
onClick: handleClick,
|
|
@@ -22687,7 +22942,7 @@ function T({ k }) {
|
|
|
22687
22942
|
style: {
|
|
22688
22943
|
cursor: "text",
|
|
22689
22944
|
color: "inherit",
|
|
22690
|
-
outline: focused ? "2px solid #640AFF" : void 0,
|
|
22945
|
+
outline: focused ? editMode ? "2px solid #640AFF" : "2px solid #C4502A" : void 0,
|
|
22691
22946
|
outlineOffset: 2,
|
|
22692
22947
|
borderRadius: 2,
|
|
22693
22948
|
transition: "outline-color 120ms"
|
|
@@ -22706,6 +22961,9 @@ function toBcp47(code) {
|
|
|
22706
22961
|
function isTextEditClick(target) {
|
|
22707
22962
|
if (target.closest("[data-setto-key]")) return true;
|
|
22708
22963
|
if (target.closest("[data-setto-ui]")) return true;
|
|
22964
|
+
if (target.closest("[data-setto-icon]")) return true;
|
|
22965
|
+
if (target.closest("[data-setto-image]")) return true;
|
|
22966
|
+
if (target.closest("[data-setto-repeater]")) return true;
|
|
22709
22967
|
let node = target;
|
|
22710
22968
|
while (node) {
|
|
22711
22969
|
if (node.matches("h1,h2,h3,h4,h5,h6,p,li,figcaption") && node.querySelector("[data-setto-key]")) {
|
|
@@ -22821,6 +23079,502 @@ const SettoBlock = forwardRef(
|
|
|
22821
23079
|
);
|
|
22822
23080
|
}
|
|
22823
23081
|
);
|
|
23082
|
+
function FloatingPopover({
|
|
23083
|
+
open,
|
|
23084
|
+
anchorRef,
|
|
23085
|
+
onClose,
|
|
23086
|
+
ariaLabel,
|
|
23087
|
+
children,
|
|
23088
|
+
style,
|
|
23089
|
+
minWidth = 280
|
|
23090
|
+
}) {
|
|
23091
|
+
const popoverRef = useRef(null);
|
|
23092
|
+
const [pos, setPos] = useState(null);
|
|
23093
|
+
useLayoutEffect(() => {
|
|
23094
|
+
if (!open || !anchorRef.current) {
|
|
23095
|
+
setPos(null);
|
|
23096
|
+
return;
|
|
23097
|
+
}
|
|
23098
|
+
const update = () => {
|
|
23099
|
+
const anchor = anchorRef.current;
|
|
23100
|
+
const pop = popoverRef.current;
|
|
23101
|
+
if (!anchor) return;
|
|
23102
|
+
const rect = anchor.getBoundingClientRect();
|
|
23103
|
+
const popoverH = pop?.offsetHeight ?? 200;
|
|
23104
|
+
const popoverW = pop?.offsetWidth ?? minWidth;
|
|
23105
|
+
let top = rect.bottom + 6;
|
|
23106
|
+
let left = rect.left;
|
|
23107
|
+
if (left + popoverW > window.innerWidth - 8) {
|
|
23108
|
+
left = window.innerWidth - popoverW - 8;
|
|
23109
|
+
}
|
|
23110
|
+
if (left < 8) left = 8;
|
|
23111
|
+
if (top + popoverH > window.innerHeight - 8) {
|
|
23112
|
+
top = rect.top - popoverH - 6;
|
|
23113
|
+
}
|
|
23114
|
+
if (top < TOOLBAR_HEIGHT + 4) top = rect.bottom + 6;
|
|
23115
|
+
setPos({ top, left });
|
|
23116
|
+
};
|
|
23117
|
+
update();
|
|
23118
|
+
requestAnimationFrame(update);
|
|
23119
|
+
window.addEventListener("scroll", update, true);
|
|
23120
|
+
window.addEventListener("resize", update);
|
|
23121
|
+
return () => {
|
|
23122
|
+
window.removeEventListener("scroll", update, true);
|
|
23123
|
+
window.removeEventListener("resize", update);
|
|
23124
|
+
};
|
|
23125
|
+
}, [open, anchorRef, minWidth]);
|
|
23126
|
+
useEffect(() => {
|
|
23127
|
+
if (!open) return;
|
|
23128
|
+
const onKey = (e) => {
|
|
23129
|
+
if (e.key === "Escape") onClose();
|
|
23130
|
+
};
|
|
23131
|
+
window.addEventListener("keydown", onKey);
|
|
23132
|
+
return () => window.removeEventListener("keydown", onKey);
|
|
23133
|
+
}, [open, onClose]);
|
|
23134
|
+
useEffect(() => {
|
|
23135
|
+
if (!open) return;
|
|
23136
|
+
const close = (e) => {
|
|
23137
|
+
const target = e.target;
|
|
23138
|
+
if (anchorRef.current?.contains(target)) return;
|
|
23139
|
+
if (popoverRef.current?.contains(target)) return;
|
|
23140
|
+
onClose();
|
|
23141
|
+
};
|
|
23142
|
+
document.addEventListener("mousedown", close);
|
|
23143
|
+
return () => document.removeEventListener("mousedown", close);
|
|
23144
|
+
}, [open, onClose, anchorRef]);
|
|
23145
|
+
if (!open) return null;
|
|
23146
|
+
return createPortal(
|
|
23147
|
+
/* @__PURE__ */ jsx(
|
|
23148
|
+
"div",
|
|
23149
|
+
{
|
|
23150
|
+
ref: popoverRef,
|
|
23151
|
+
role: "dialog",
|
|
23152
|
+
"aria-label": ariaLabel,
|
|
23153
|
+
"data-setto-ui": true,
|
|
23154
|
+
style: {
|
|
23155
|
+
position: "fixed",
|
|
23156
|
+
top: pos?.top ?? -9999,
|
|
23157
|
+
left: pos?.left ?? 0,
|
|
23158
|
+
visibility: pos ? "visible" : "hidden",
|
|
23159
|
+
zIndex: 2147483647,
|
|
23160
|
+
background: "#fff",
|
|
23161
|
+
border: "1px solid #e0e0e0",
|
|
23162
|
+
borderRadius: 8,
|
|
23163
|
+
boxShadow: "0 8px 24px rgba(0,0,0,0.15)",
|
|
23164
|
+
fontFamily: 'system-ui, -apple-system, "Segoe UI", sans-serif',
|
|
23165
|
+
...style
|
|
23166
|
+
},
|
|
23167
|
+
onMouseDown: (e) => e.stopPropagation(),
|
|
23168
|
+
onClick: (e) => e.stopPropagation(),
|
|
23169
|
+
children
|
|
23170
|
+
}
|
|
23171
|
+
),
|
|
23172
|
+
document.body
|
|
23173
|
+
);
|
|
23174
|
+
}
|
|
23175
|
+
function SettoIcon({ k, icons, size = 24, className, style }) {
|
|
23176
|
+
const { t, i18n } = useTranslation();
|
|
23177
|
+
const { editMode, store } = useSetto();
|
|
23178
|
+
const anchorRef = useRef(null);
|
|
23179
|
+
const [open, setOpen] = useState(false);
|
|
23180
|
+
const [search, setSearch] = useState("");
|
|
23181
|
+
const [, force] = useState(0);
|
|
23182
|
+
useEffect(() => {
|
|
23183
|
+
if (!store) return;
|
|
23184
|
+
return store.subscribe(() => force((x) => x + 1));
|
|
23185
|
+
}, [store]);
|
|
23186
|
+
const iconName = store ? store.get(k, i18n.language) : t(k);
|
|
23187
|
+
const Icon = icons[iconName] ?? icons[Object.keys(icons)[0] ?? ""] ?? null;
|
|
23188
|
+
if (!Icon) return null;
|
|
23189
|
+
if (!editMode) {
|
|
23190
|
+
return /* @__PURE__ */ jsx(Icon, { size, className, style });
|
|
23191
|
+
}
|
|
23192
|
+
const handleClick = (e) => {
|
|
23193
|
+
e.stopPropagation();
|
|
23194
|
+
e.preventDefault();
|
|
23195
|
+
setOpen((v) => !v);
|
|
23196
|
+
};
|
|
23197
|
+
const filtered = Object.entries(icons).filter(
|
|
23198
|
+
([name]) => name.toLowerCase().includes(search.toLowerCase())
|
|
23199
|
+
);
|
|
23200
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
23201
|
+
/* @__PURE__ */ jsx(
|
|
23202
|
+
"span",
|
|
23203
|
+
{
|
|
23204
|
+
ref: anchorRef,
|
|
23205
|
+
"data-setto-icon": true,
|
|
23206
|
+
"data-setto-key": k,
|
|
23207
|
+
role: "button",
|
|
23208
|
+
tabIndex: 0,
|
|
23209
|
+
"aria-label": "Velg ikon",
|
|
23210
|
+
onClick: handleClick,
|
|
23211
|
+
onKeyDown: (e) => {
|
|
23212
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
23213
|
+
e.preventDefault();
|
|
23214
|
+
setOpen((v) => !v);
|
|
23215
|
+
}
|
|
23216
|
+
},
|
|
23217
|
+
style: {
|
|
23218
|
+
display: "inline-flex",
|
|
23219
|
+
cursor: "pointer",
|
|
23220
|
+
outline: open ? "2px solid #640AFF" : void 0,
|
|
23221
|
+
outlineOffset: 2,
|
|
23222
|
+
borderRadius: 4
|
|
23223
|
+
},
|
|
23224
|
+
children: /* @__PURE__ */ jsx(Icon, { size, className, style })
|
|
23225
|
+
}
|
|
23226
|
+
),
|
|
23227
|
+
/* @__PURE__ */ jsxs(
|
|
23228
|
+
FloatingPopover,
|
|
23229
|
+
{
|
|
23230
|
+
open,
|
|
23231
|
+
anchorRef,
|
|
23232
|
+
onClose: () => {
|
|
23233
|
+
setOpen(false);
|
|
23234
|
+
setSearch("");
|
|
23235
|
+
},
|
|
23236
|
+
ariaLabel: "Ikonvelger",
|
|
23237
|
+
minWidth: 320,
|
|
23238
|
+
style: { padding: 12, maxWidth: 360 },
|
|
23239
|
+
children: [
|
|
23240
|
+
/* @__PURE__ */ jsx(
|
|
23241
|
+
"input",
|
|
23242
|
+
{
|
|
23243
|
+
type: "search",
|
|
23244
|
+
value: search,
|
|
23245
|
+
onChange: (e) => setSearch(e.target.value),
|
|
23246
|
+
placeholder: "Søk ikon…",
|
|
23247
|
+
"data-setto-ui": true,
|
|
23248
|
+
style: {
|
|
23249
|
+
width: "100%",
|
|
23250
|
+
boxSizing: "border-box",
|
|
23251
|
+
padding: "8px 10px",
|
|
23252
|
+
marginBottom: 10,
|
|
23253
|
+
border: "1px solid #e0e0e0",
|
|
23254
|
+
borderRadius: 6,
|
|
23255
|
+
fontSize: 13
|
|
23256
|
+
}
|
|
23257
|
+
}
|
|
23258
|
+
),
|
|
23259
|
+
/* @__PURE__ */ jsx(
|
|
23260
|
+
"div",
|
|
23261
|
+
{
|
|
23262
|
+
style: {
|
|
23263
|
+
display: "grid",
|
|
23264
|
+
gridTemplateColumns: "repeat(6, 1fr)",
|
|
23265
|
+
gap: 4,
|
|
23266
|
+
maxHeight: 240,
|
|
23267
|
+
overflowY: "auto"
|
|
23268
|
+
},
|
|
23269
|
+
children: filtered.map(([name, Item]) => {
|
|
23270
|
+
const selected = name === iconName;
|
|
23271
|
+
return /* @__PURE__ */ jsx(
|
|
23272
|
+
"button",
|
|
23273
|
+
{
|
|
23274
|
+
type: "button",
|
|
23275
|
+
title: name,
|
|
23276
|
+
"data-setto-ui": true,
|
|
23277
|
+
onClick: () => {
|
|
23278
|
+
store?.set(k, i18n.language, name);
|
|
23279
|
+
setOpen(false);
|
|
23280
|
+
setSearch("");
|
|
23281
|
+
},
|
|
23282
|
+
style: {
|
|
23283
|
+
display: "flex",
|
|
23284
|
+
alignItems: "center",
|
|
23285
|
+
justifyContent: "center",
|
|
23286
|
+
padding: 8,
|
|
23287
|
+
background: selected ? "#f5f3ff" : "transparent",
|
|
23288
|
+
border: selected ? "1px solid #640AFF" : "1px solid transparent",
|
|
23289
|
+
borderRadius: 6,
|
|
23290
|
+
cursor: "pointer",
|
|
23291
|
+
color: style?.color ?? "currentColor"
|
|
23292
|
+
},
|
|
23293
|
+
children: /* @__PURE__ */ jsx(Item, { size: 20 })
|
|
23294
|
+
},
|
|
23295
|
+
name
|
|
23296
|
+
);
|
|
23297
|
+
})
|
|
23298
|
+
}
|
|
23299
|
+
)
|
|
23300
|
+
]
|
|
23301
|
+
}
|
|
23302
|
+
)
|
|
23303
|
+
] });
|
|
23304
|
+
}
|
|
23305
|
+
const ACCEPT = "image/png,image/jpeg,image/webp,image/svg+xml";
|
|
23306
|
+
function setSrcAllLanguages(store, i18n, srcKey, value) {
|
|
23307
|
+
const langs = i18n.languages?.length ? i18n.languages : [i18n.language];
|
|
23308
|
+
for (const lng of langs) {
|
|
23309
|
+
store.set(srcKey, lng, value);
|
|
23310
|
+
}
|
|
23311
|
+
}
|
|
23312
|
+
function SettoImage({ srcKey, alt, className, style, ...rest }) {
|
|
23313
|
+
const { t, i18n } = useTranslation();
|
|
23314
|
+
const { editMode, store, assetStore } = useSetto();
|
|
23315
|
+
const fileRef = useRef(null);
|
|
23316
|
+
const [hovered, setHovered] = useState(false);
|
|
23317
|
+
const [uploading, setUploading] = useState(false);
|
|
23318
|
+
const [, force] = useState(0);
|
|
23319
|
+
useEffect(() => {
|
|
23320
|
+
if (!store) return;
|
|
23321
|
+
return store.subscribe(() => force((x) => x + 1));
|
|
23322
|
+
}, [store]);
|
|
23323
|
+
const [, forceAssets] = useState(0);
|
|
23324
|
+
useEffect(() => {
|
|
23325
|
+
if (!assetStore) return;
|
|
23326
|
+
return assetStore.subscribe(() => forceAssets((x) => x + 1));
|
|
23327
|
+
}, [assetStore]);
|
|
23328
|
+
const srcPath = store ? store.get(srcKey, i18n.language) : t(srcKey);
|
|
23329
|
+
const previewUrl = assetStore?.getPreviewUrl(srcKey);
|
|
23330
|
+
const src = previewUrl ?? srcPath;
|
|
23331
|
+
if (!editMode) {
|
|
23332
|
+
return /* @__PURE__ */ jsx("img", { src: srcPath, alt, className, style, ...rest });
|
|
23333
|
+
}
|
|
23334
|
+
const isFullWidth = className?.includes("w-full");
|
|
23335
|
+
const isFullHeight = className?.includes("h-full");
|
|
23336
|
+
const openFilePicker = (e) => {
|
|
23337
|
+
e.stopPropagation();
|
|
23338
|
+
e.preventDefault();
|
|
23339
|
+
if (!uploading) fileRef.current?.click();
|
|
23340
|
+
};
|
|
23341
|
+
const handleFile = async (e) => {
|
|
23342
|
+
const file = e.target.files?.[0];
|
|
23343
|
+
e.target.value = "";
|
|
23344
|
+
if (!file || !assetStore || !store) return;
|
|
23345
|
+
setUploading(true);
|
|
23346
|
+
try {
|
|
23347
|
+
const originalPublicPath = store.original(srcKey, i18n.language);
|
|
23348
|
+
const { repoPath, publicPath } = generateSettoImagePaths(srcKey, file);
|
|
23349
|
+
await assetStore.set({
|
|
23350
|
+
srcKey,
|
|
23351
|
+
originalPublicPath,
|
|
23352
|
+
repoPath,
|
|
23353
|
+
publicPath,
|
|
23354
|
+
file
|
|
23355
|
+
});
|
|
23356
|
+
setSrcAllLanguages(store, i18n, srcKey, publicPath);
|
|
23357
|
+
} finally {
|
|
23358
|
+
setUploading(false);
|
|
23359
|
+
}
|
|
23360
|
+
};
|
|
23361
|
+
const handleRevert = (e) => {
|
|
23362
|
+
e.stopPropagation();
|
|
23363
|
+
e.preventDefault();
|
|
23364
|
+
if (!assetStore || !store) return;
|
|
23365
|
+
const original = assetStore.revert(srcKey);
|
|
23366
|
+
if (original !== null) {
|
|
23367
|
+
setSrcAllLanguages(store, i18n, srcKey, original);
|
|
23368
|
+
}
|
|
23369
|
+
};
|
|
23370
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
23371
|
+
/* @__PURE__ */ jsxs(
|
|
23372
|
+
"span",
|
|
23373
|
+
{
|
|
23374
|
+
"data-setto-image": true,
|
|
23375
|
+
"data-setto-ui": true,
|
|
23376
|
+
onMouseEnter: () => setHovered(true),
|
|
23377
|
+
onMouseLeave: () => setHovered(false),
|
|
23378
|
+
style: {
|
|
23379
|
+
display: isFullWidth || isFullHeight ? "block" : "inline-block",
|
|
23380
|
+
width: isFullWidth ? "100%" : void 0,
|
|
23381
|
+
height: isFullHeight ? "100%" : void 0,
|
|
23382
|
+
position: "relative",
|
|
23383
|
+
outline: hovered ? "2px solid #640AFF" : void 0,
|
|
23384
|
+
outlineOffset: 2,
|
|
23385
|
+
borderRadius: 4
|
|
23386
|
+
},
|
|
23387
|
+
children: [
|
|
23388
|
+
/* @__PURE__ */ jsx("img", { src, alt, className, style, ...rest }),
|
|
23389
|
+
previewUrl && !hovered ? /* @__PURE__ */ jsx(
|
|
23390
|
+
"span",
|
|
23391
|
+
{
|
|
23392
|
+
"aria-hidden": true,
|
|
23393
|
+
"data-setto-ui": true,
|
|
23394
|
+
style: {
|
|
23395
|
+
position: "absolute",
|
|
23396
|
+
top: 4,
|
|
23397
|
+
right: 4,
|
|
23398
|
+
background: "#640AFF",
|
|
23399
|
+
color: "#fff",
|
|
23400
|
+
fontSize: 10,
|
|
23401
|
+
padding: "2px 6px",
|
|
23402
|
+
borderRadius: 4,
|
|
23403
|
+
fontFamily: "system-ui, sans-serif",
|
|
23404
|
+
pointerEvents: "none"
|
|
23405
|
+
},
|
|
23406
|
+
children: "Nytt bilde"
|
|
23407
|
+
}
|
|
23408
|
+
) : null,
|
|
23409
|
+
hovered ? /* @__PURE__ */ jsxs(
|
|
23410
|
+
"span",
|
|
23411
|
+
{
|
|
23412
|
+
"data-setto-ui": true,
|
|
23413
|
+
style: {
|
|
23414
|
+
position: "absolute",
|
|
23415
|
+
inset: 0,
|
|
23416
|
+
display: "flex",
|
|
23417
|
+
flexDirection: "column",
|
|
23418
|
+
alignItems: "center",
|
|
23419
|
+
justifyContent: "center",
|
|
23420
|
+
gap: 8,
|
|
23421
|
+
background: "rgba(0, 0, 0, 0.45)",
|
|
23422
|
+
borderRadius: 4
|
|
23423
|
+
},
|
|
23424
|
+
children: [
|
|
23425
|
+
/* @__PURE__ */ jsx(
|
|
23426
|
+
"button",
|
|
23427
|
+
{
|
|
23428
|
+
type: "button",
|
|
23429
|
+
"data-setto-ui": true,
|
|
23430
|
+
disabled: uploading,
|
|
23431
|
+
onClick: openFilePicker,
|
|
23432
|
+
onMouseDown: (e) => e.stopPropagation(),
|
|
23433
|
+
style: {
|
|
23434
|
+
padding: "10px 18px",
|
|
23435
|
+
background: "#fff",
|
|
23436
|
+
color: "#222",
|
|
23437
|
+
border: "none",
|
|
23438
|
+
borderRadius: 6,
|
|
23439
|
+
fontSize: 13,
|
|
23440
|
+
fontWeight: 500,
|
|
23441
|
+
fontFamily: 'system-ui, -apple-system, "Segoe UI", sans-serif',
|
|
23442
|
+
cursor: uploading ? "wait" : "pointer",
|
|
23443
|
+
boxShadow: "0 2px 8px rgba(0,0,0,0.2)"
|
|
23444
|
+
},
|
|
23445
|
+
children: uploading ? "Laster opp…" : "Velg fil…"
|
|
23446
|
+
}
|
|
23447
|
+
),
|
|
23448
|
+
previewUrl ? /* @__PURE__ */ jsx(
|
|
23449
|
+
"button",
|
|
23450
|
+
{
|
|
23451
|
+
type: "button",
|
|
23452
|
+
"data-setto-ui": true,
|
|
23453
|
+
disabled: uploading,
|
|
23454
|
+
onClick: handleRevert,
|
|
23455
|
+
onMouseDown: (e) => e.stopPropagation(),
|
|
23456
|
+
style: {
|
|
23457
|
+
padding: "6px 12px",
|
|
23458
|
+
background: "transparent",
|
|
23459
|
+
color: "#fff",
|
|
23460
|
+
border: "1px solid rgba(255,255,255,0.6)",
|
|
23461
|
+
borderRadius: 6,
|
|
23462
|
+
fontSize: 12,
|
|
23463
|
+
fontFamily: 'system-ui, -apple-system, "Segoe UI", sans-serif',
|
|
23464
|
+
cursor: uploading ? "wait" : "pointer"
|
|
23465
|
+
},
|
|
23466
|
+
children: "Angre"
|
|
23467
|
+
}
|
|
23468
|
+
) : null
|
|
23469
|
+
]
|
|
23470
|
+
}
|
|
23471
|
+
) : null
|
|
23472
|
+
]
|
|
23473
|
+
}
|
|
23474
|
+
),
|
|
23475
|
+
/* @__PURE__ */ jsx(
|
|
23476
|
+
"input",
|
|
23477
|
+
{
|
|
23478
|
+
ref: fileRef,
|
|
23479
|
+
type: "file",
|
|
23480
|
+
accept: ACCEPT,
|
|
23481
|
+
hidden: true,
|
|
23482
|
+
onChange: handleFile
|
|
23483
|
+
}
|
|
23484
|
+
)
|
|
23485
|
+
] });
|
|
23486
|
+
}
|
|
23487
|
+
function SettoRepeater({
|
|
23488
|
+
itemsKey,
|
|
23489
|
+
defaultItem,
|
|
23490
|
+
itemLabel = "element",
|
|
23491
|
+
children,
|
|
23492
|
+
className
|
|
23493
|
+
}) {
|
|
23494
|
+
const { i18n } = useTranslation();
|
|
23495
|
+
const { editMode, store } = useSetto();
|
|
23496
|
+
const lng = i18n.language;
|
|
23497
|
+
const [, force] = useState(0);
|
|
23498
|
+
useEffect(() => {
|
|
23499
|
+
if (!store) return;
|
|
23500
|
+
return store.subscribe(() => force((x) => x + 1));
|
|
23501
|
+
}, [store]);
|
|
23502
|
+
const keys = store ? store.getListKeys(itemsKey, lng) : [];
|
|
23503
|
+
const handleAdd = () => {
|
|
23504
|
+
store?.addListItem(itemsKey, lng, defaultItem);
|
|
23505
|
+
};
|
|
23506
|
+
const handleRemove = (itemKey) => {
|
|
23507
|
+
if (keys.length <= 1) return;
|
|
23508
|
+
store?.removeListItem(itemsKey, itemKey, lng);
|
|
23509
|
+
};
|
|
23510
|
+
return /* @__PURE__ */ jsxs("div", { className, "data-setto-repeater": true, children: [
|
|
23511
|
+
editMode ? /* @__PURE__ */ jsx(
|
|
23512
|
+
"div",
|
|
23513
|
+
{
|
|
23514
|
+
"data-setto-ui": true,
|
|
23515
|
+
style: {
|
|
23516
|
+
display: "flex",
|
|
23517
|
+
justifyContent: "flex-end",
|
|
23518
|
+
marginBottom: 8,
|
|
23519
|
+
gap: 8
|
|
23520
|
+
},
|
|
23521
|
+
children: /* @__PURE__ */ jsxs(
|
|
23522
|
+
"button",
|
|
23523
|
+
{
|
|
23524
|
+
type: "button",
|
|
23525
|
+
"data-setto-ui": true,
|
|
23526
|
+
onClick: (e) => {
|
|
23527
|
+
e.stopPropagation();
|
|
23528
|
+
handleAdd();
|
|
23529
|
+
},
|
|
23530
|
+
style: controlBtnStyle,
|
|
23531
|
+
children: [
|
|
23532
|
+
"+ Legg til ",
|
|
23533
|
+
itemLabel
|
|
23534
|
+
]
|
|
23535
|
+
}
|
|
23536
|
+
)
|
|
23537
|
+
}
|
|
23538
|
+
) : null,
|
|
23539
|
+
keys.map((itemKey, index) => /* @__PURE__ */ jsxs("div", { className: "setto-repeater-item", style: { position: "relative" }, children: [
|
|
23540
|
+
editMode ? /* @__PURE__ */ jsx(
|
|
23541
|
+
"button",
|
|
23542
|
+
{
|
|
23543
|
+
type: "button",
|
|
23544
|
+
"data-setto-ui": true,
|
|
23545
|
+
"aria-label": `Fjern ${itemLabel}`,
|
|
23546
|
+
disabled: keys.length <= 1,
|
|
23547
|
+
onClick: (e) => {
|
|
23548
|
+
e.stopPropagation();
|
|
23549
|
+
handleRemove(itemKey);
|
|
23550
|
+
},
|
|
23551
|
+
style: {
|
|
23552
|
+
...controlBtnStyle,
|
|
23553
|
+
position: "absolute",
|
|
23554
|
+
top: 0,
|
|
23555
|
+
right: 0,
|
|
23556
|
+
zIndex: 2,
|
|
23557
|
+
padding: "4px 8px",
|
|
23558
|
+
fontSize: 11,
|
|
23559
|
+
opacity: keys.length <= 1 ? 0.4 : 1
|
|
23560
|
+
},
|
|
23561
|
+
children: "× Fjern"
|
|
23562
|
+
}
|
|
23563
|
+
) : null,
|
|
23564
|
+
children(itemKey, index)
|
|
23565
|
+
] }, itemKey))
|
|
23566
|
+
] });
|
|
23567
|
+
}
|
|
23568
|
+
const controlBtnStyle = {
|
|
23569
|
+
padding: "6px 10px",
|
|
23570
|
+
background: "#f7f7f7",
|
|
23571
|
+
border: "1px solid #e0e0e0",
|
|
23572
|
+
borderRadius: 6,
|
|
23573
|
+
fontSize: 12,
|
|
23574
|
+
color: "#444",
|
|
23575
|
+
cursor: "pointer",
|
|
23576
|
+
fontFamily: 'system-ui, -apple-system, "Segoe UI", sans-serif'
|
|
23577
|
+
};
|
|
22824
23578
|
function editModeUrl(pathname = "/") {
|
|
22825
23579
|
const u = new URL(window.location.href);
|
|
22826
23580
|
u.pathname = pathname;
|
|
@@ -23458,11 +24212,16 @@ function depDotStyle(status) {
|
|
|
23458
24212
|
}
|
|
23459
24213
|
export {
|
|
23460
24214
|
AuthGate,
|
|
24215
|
+
GuestEditProvider,
|
|
23461
24216
|
SettoAdminApp,
|
|
23462
24217
|
SettoBlock,
|
|
24218
|
+
SettoIcon,
|
|
24219
|
+
SettoImage,
|
|
23463
24220
|
SettoProvider,
|
|
24221
|
+
SettoRepeater,
|
|
23464
24222
|
SettoSection,
|
|
23465
24223
|
T,
|
|
24224
|
+
useGuestEdit,
|
|
23466
24225
|
useSectionTheme,
|
|
23467
24226
|
useSetto
|
|
23468
24227
|
};
|