@sevenfold/setto-client 0.2.8 → 0.3.3
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/edit-mode/publish-dialog.d.ts +0 -2
- package/dist/index.d.ts +7 -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 +739 -24
- 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
|
};
|
|
@@ -21880,16 +22064,6 @@ function PublishDialog({
|
|
|
21880
22064
|
return /* @__PURE__ */ jsx(EditOverlay, { onClose: void 0, children: isReady ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
21881
22065
|
/* @__PURE__ */ jsx("h2", { style: { margin: "0 0 12px", fontSize: 18, fontWeight: 600 }, children: "Publisert" }),
|
|
21882
22066
|
/* @__PURE__ */ jsx("p", { style: { margin: "0 0 20px", fontSize: 14, lineHeight: 1.5, color: "#444" }, children: "Endringene dine er live." }),
|
|
21883
|
-
row?.url ? /* @__PURE__ */ jsx(
|
|
21884
|
-
"a",
|
|
21885
|
-
{
|
|
21886
|
-
href: row.url,
|
|
21887
|
-
target: "_blank",
|
|
21888
|
-
rel: "noreferrer",
|
|
21889
|
-
style: { ...linkStyle$1, display: "block", marginBottom: 16 },
|
|
21890
|
-
children: "Åpne publisert side →"
|
|
21891
|
-
}
|
|
21892
|
-
) : null,
|
|
21893
22067
|
/* @__PURE__ */ jsx("button", { type: "button", onClick: onComplete, style: primaryBtnStyle, children: "OK" })
|
|
21894
22068
|
] }) : errored ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
21895
22069
|
/* @__PURE__ */ jsx("h2", { style: { margin: "0 0 12px", fontSize: 18, fontWeight: 600, color: "#dc2626" }, children: "Publisering feilet" }),
|
|
@@ -21927,11 +22101,6 @@ const secondaryBtnStyle = {
|
|
|
21927
22101
|
color: "#666",
|
|
21928
22102
|
border: "1px solid #ddd"
|
|
21929
22103
|
};
|
|
21930
|
-
const linkStyle$1 = {
|
|
21931
|
-
color: "#640AFF",
|
|
21932
|
-
fontSize: 14,
|
|
21933
|
-
textDecoration: "none"
|
|
21934
|
-
};
|
|
21935
22104
|
function guessLangFromPath(path, languages) {
|
|
21936
22105
|
for (const lng of languages) {
|
|
21937
22106
|
if (path.includes(`/${lng}.`) || path.includes(`/${lng}/`)) return lng;
|
|
@@ -21982,7 +22151,7 @@ function useEditBaseline() {
|
|
|
21982
22151
|
}, [session, store, config.siteId]);
|
|
21983
22152
|
}
|
|
21984
22153
|
function EditToolbar() {
|
|
21985
|
-
const { store, themeStore, api, config, supabase } = useSetto();
|
|
22154
|
+
const { store, themeStore, assetStore, api, config, supabase } = useSetto();
|
|
21986
22155
|
const { i18n } = useTranslation();
|
|
21987
22156
|
useEditBaseline();
|
|
21988
22157
|
const snap = useSyncExternalStore(
|
|
@@ -21997,6 +22166,12 @@ function EditToolbar() {
|
|
|
21997
22166
|
() => themeStore?.snapshot().version ?? 0,
|
|
21998
22167
|
() => 0
|
|
21999
22168
|
);
|
|
22169
|
+
useSyncExternalStore(
|
|
22170
|
+
(cb) => assetStore?.subscribe(() => cb()) ?? (() => {
|
|
22171
|
+
}),
|
|
22172
|
+
() => assetStore?.snapshot().version ?? 0,
|
|
22173
|
+
() => 0
|
|
22174
|
+
);
|
|
22000
22175
|
const [publishing, setPublishing] = useState(false);
|
|
22001
22176
|
const [activeDeployment, setActiveDeployment] = useState(null);
|
|
22002
22177
|
const [publishDialogOpen, setPublishDialogOpen] = useState(false);
|
|
@@ -22028,7 +22203,8 @@ function EditToolbar() {
|
|
|
22028
22203
|
}, [api, supabase, config.siteId]);
|
|
22029
22204
|
const textDraftCount = snap.drafts.size;
|
|
22030
22205
|
const themeDraftCount = themeStore?.size() ?? 0;
|
|
22031
|
-
const
|
|
22206
|
+
const assetDraftCount = assetStore?.size() ?? 0;
|
|
22207
|
+
const draftCount = textDraftCount + themeDraftCount + assetDraftCount;
|
|
22032
22208
|
const hasDrafts = draftCount > 0;
|
|
22033
22209
|
const showToolbarProgress = activeDeployment !== null && !publishDialogOpen && deploymentRow !== null && isDeploymentInProgress(deploymentRow.status) && !isDeploymentStale(deploymentRow);
|
|
22034
22210
|
const showPublishedNotice = publishedNotice && !publishDialogOpen;
|
|
@@ -22124,9 +22300,33 @@ function EditToolbar() {
|
|
|
22124
22300
|
content: JSON.stringify(themeStore.serialise(), null, 2) + "\n"
|
|
22125
22301
|
});
|
|
22126
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
|
+
}
|
|
22127
22326
|
const result = await api.publish(config.siteId, files);
|
|
22128
22327
|
store.commit();
|
|
22129
22328
|
themeStore?.commit();
|
|
22329
|
+
assetStore?.commit();
|
|
22130
22330
|
let deploymentId = result.deploymentId;
|
|
22131
22331
|
if (!deploymentId && result.trackingDeferred) {
|
|
22132
22332
|
deploymentId = await waitForDeploymentByCommit(
|
|
@@ -22467,11 +22667,13 @@ function SettoProvider({ config, children }) {
|
|
|
22467
22667
|
const { i18n } = useTranslation();
|
|
22468
22668
|
const storeRef = useRef(null);
|
|
22469
22669
|
const themeStoreRef = useRef(null);
|
|
22670
|
+
const assetStoreRef = useRef(null);
|
|
22470
22671
|
const [storeReady, setStoreReady] = useState(false);
|
|
22471
22672
|
useEffect(() => {
|
|
22472
22673
|
if (!i18n) return;
|
|
22473
22674
|
storeRef.current = new I18nStore(i18n);
|
|
22474
22675
|
themeStoreRef.current = new ThemeStore(config.theme ?? {});
|
|
22676
|
+
assetStoreRef.current = new AssetStore();
|
|
22475
22677
|
setStoreReady(true);
|
|
22476
22678
|
}, [i18n, config.theme]);
|
|
22477
22679
|
const editMode = !!session && editParam;
|
|
@@ -22483,7 +22685,8 @@ function SettoProvider({ config, children }) {
|
|
|
22483
22685
|
authLoading,
|
|
22484
22686
|
editMode,
|
|
22485
22687
|
store: storeReady ? storeRef.current : null,
|
|
22486
|
-
themeStore: storeReady ? themeStoreRef.current : null
|
|
22688
|
+
themeStore: storeReady ? themeStoreRef.current : null,
|
|
22689
|
+
assetStore: storeReady ? assetStoreRef.current : null
|
|
22487
22690
|
};
|
|
22488
22691
|
return /* @__PURE__ */ jsx(SettoContext.Provider, { value, children: /* @__PURE__ */ jsx(SectionEditProvider, { children: editMode && value.store ? /* @__PURE__ */ jsx(EditModeShell, { themeStore: value.themeStore, children }) : children }) });
|
|
22489
22692
|
}
|
|
@@ -22674,6 +22877,15 @@ function T({ k }) {
|
|
|
22674
22877
|
const handleInput = (e) => {
|
|
22675
22878
|
store?.set(k, i18n.language, e.currentTarget.textContent ?? "");
|
|
22676
22879
|
};
|
|
22880
|
+
const handlePaste = (e) => {
|
|
22881
|
+
e.preventDefault();
|
|
22882
|
+
const text = e.clipboardData.getData("text/plain");
|
|
22883
|
+
if (!text) return;
|
|
22884
|
+
const el = e.currentTarget;
|
|
22885
|
+
el.focus();
|
|
22886
|
+
document.execCommand("insertText", false, text);
|
|
22887
|
+
store?.set(k, i18n.language, el.textContent ?? "");
|
|
22888
|
+
};
|
|
22677
22889
|
const lang = toBcp47(i18n.language);
|
|
22678
22890
|
const showLinkChip = coarsePointer && focused && linkContext;
|
|
22679
22891
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
@@ -22694,6 +22906,7 @@ function T({ k }) {
|
|
|
22694
22906
|
},
|
|
22695
22907
|
onBlur: handleBlur,
|
|
22696
22908
|
onInput: handleInput,
|
|
22909
|
+
onPaste: handlePaste,
|
|
22697
22910
|
onMouseDown: handleMouseDown,
|
|
22698
22911
|
onMouseUp: handleMouseUp,
|
|
22699
22912
|
onClick: handleClick,
|
|
@@ -22721,6 +22934,9 @@ function toBcp47(code) {
|
|
|
22721
22934
|
function isTextEditClick(target) {
|
|
22722
22935
|
if (target.closest("[data-setto-key]")) return true;
|
|
22723
22936
|
if (target.closest("[data-setto-ui]")) return true;
|
|
22937
|
+
if (target.closest("[data-setto-icon]")) return true;
|
|
22938
|
+
if (target.closest("[data-setto-image]")) return true;
|
|
22939
|
+
if (target.closest("[data-setto-repeater]")) return true;
|
|
22724
22940
|
let node = target;
|
|
22725
22941
|
while (node) {
|
|
22726
22942
|
if (node.matches("h1,h2,h3,h4,h5,h6,p,li,figcaption") && node.querySelector("[data-setto-key]")) {
|
|
@@ -22836,6 +23052,502 @@ const SettoBlock = forwardRef(
|
|
|
22836
23052
|
);
|
|
22837
23053
|
}
|
|
22838
23054
|
);
|
|
23055
|
+
function FloatingPopover({
|
|
23056
|
+
open,
|
|
23057
|
+
anchorRef,
|
|
23058
|
+
onClose,
|
|
23059
|
+
ariaLabel,
|
|
23060
|
+
children,
|
|
23061
|
+
style,
|
|
23062
|
+
minWidth = 280
|
|
23063
|
+
}) {
|
|
23064
|
+
const popoverRef = useRef(null);
|
|
23065
|
+
const [pos, setPos] = useState(null);
|
|
23066
|
+
useLayoutEffect(() => {
|
|
23067
|
+
if (!open || !anchorRef.current) {
|
|
23068
|
+
setPos(null);
|
|
23069
|
+
return;
|
|
23070
|
+
}
|
|
23071
|
+
const update = () => {
|
|
23072
|
+
const anchor = anchorRef.current;
|
|
23073
|
+
const pop = popoverRef.current;
|
|
23074
|
+
if (!anchor) return;
|
|
23075
|
+
const rect = anchor.getBoundingClientRect();
|
|
23076
|
+
const popoverH = pop?.offsetHeight ?? 200;
|
|
23077
|
+
const popoverW = pop?.offsetWidth ?? minWidth;
|
|
23078
|
+
let top = rect.bottom + 6;
|
|
23079
|
+
let left = rect.left;
|
|
23080
|
+
if (left + popoverW > window.innerWidth - 8) {
|
|
23081
|
+
left = window.innerWidth - popoverW - 8;
|
|
23082
|
+
}
|
|
23083
|
+
if (left < 8) left = 8;
|
|
23084
|
+
if (top + popoverH > window.innerHeight - 8) {
|
|
23085
|
+
top = rect.top - popoverH - 6;
|
|
23086
|
+
}
|
|
23087
|
+
if (top < TOOLBAR_HEIGHT + 4) top = rect.bottom + 6;
|
|
23088
|
+
setPos({ top, left });
|
|
23089
|
+
};
|
|
23090
|
+
update();
|
|
23091
|
+
requestAnimationFrame(update);
|
|
23092
|
+
window.addEventListener("scroll", update, true);
|
|
23093
|
+
window.addEventListener("resize", update);
|
|
23094
|
+
return () => {
|
|
23095
|
+
window.removeEventListener("scroll", update, true);
|
|
23096
|
+
window.removeEventListener("resize", update);
|
|
23097
|
+
};
|
|
23098
|
+
}, [open, anchorRef, minWidth]);
|
|
23099
|
+
useEffect(() => {
|
|
23100
|
+
if (!open) return;
|
|
23101
|
+
const onKey = (e) => {
|
|
23102
|
+
if (e.key === "Escape") onClose();
|
|
23103
|
+
};
|
|
23104
|
+
window.addEventListener("keydown", onKey);
|
|
23105
|
+
return () => window.removeEventListener("keydown", onKey);
|
|
23106
|
+
}, [open, onClose]);
|
|
23107
|
+
useEffect(() => {
|
|
23108
|
+
if (!open) return;
|
|
23109
|
+
const close = (e) => {
|
|
23110
|
+
const target = e.target;
|
|
23111
|
+
if (anchorRef.current?.contains(target)) return;
|
|
23112
|
+
if (popoverRef.current?.contains(target)) return;
|
|
23113
|
+
onClose();
|
|
23114
|
+
};
|
|
23115
|
+
document.addEventListener("mousedown", close);
|
|
23116
|
+
return () => document.removeEventListener("mousedown", close);
|
|
23117
|
+
}, [open, onClose, anchorRef]);
|
|
23118
|
+
if (!open) return null;
|
|
23119
|
+
return createPortal(
|
|
23120
|
+
/* @__PURE__ */ jsx(
|
|
23121
|
+
"div",
|
|
23122
|
+
{
|
|
23123
|
+
ref: popoverRef,
|
|
23124
|
+
role: "dialog",
|
|
23125
|
+
"aria-label": ariaLabel,
|
|
23126
|
+
"data-setto-ui": true,
|
|
23127
|
+
style: {
|
|
23128
|
+
position: "fixed",
|
|
23129
|
+
top: pos?.top ?? -9999,
|
|
23130
|
+
left: pos?.left ?? 0,
|
|
23131
|
+
visibility: pos ? "visible" : "hidden",
|
|
23132
|
+
zIndex: 2147483647,
|
|
23133
|
+
background: "#fff",
|
|
23134
|
+
border: "1px solid #e0e0e0",
|
|
23135
|
+
borderRadius: 8,
|
|
23136
|
+
boxShadow: "0 8px 24px rgba(0,0,0,0.15)",
|
|
23137
|
+
fontFamily: 'system-ui, -apple-system, "Segoe UI", sans-serif',
|
|
23138
|
+
...style
|
|
23139
|
+
},
|
|
23140
|
+
onMouseDown: (e) => e.stopPropagation(),
|
|
23141
|
+
onClick: (e) => e.stopPropagation(),
|
|
23142
|
+
children
|
|
23143
|
+
}
|
|
23144
|
+
),
|
|
23145
|
+
document.body
|
|
23146
|
+
);
|
|
23147
|
+
}
|
|
23148
|
+
function SettoIcon({ k, icons, size = 24, className, style }) {
|
|
23149
|
+
const { t, i18n } = useTranslation();
|
|
23150
|
+
const { editMode, store } = useSetto();
|
|
23151
|
+
const anchorRef = useRef(null);
|
|
23152
|
+
const [open, setOpen] = useState(false);
|
|
23153
|
+
const [search, setSearch] = useState("");
|
|
23154
|
+
const [, force] = useState(0);
|
|
23155
|
+
useEffect(() => {
|
|
23156
|
+
if (!store) return;
|
|
23157
|
+
return store.subscribe(() => force((x) => x + 1));
|
|
23158
|
+
}, [store]);
|
|
23159
|
+
const iconName = store ? store.get(k, i18n.language) : t(k);
|
|
23160
|
+
const Icon = icons[iconName] ?? icons[Object.keys(icons)[0] ?? ""] ?? null;
|
|
23161
|
+
if (!Icon) return null;
|
|
23162
|
+
if (!editMode) {
|
|
23163
|
+
return /* @__PURE__ */ jsx(Icon, { size, className, style });
|
|
23164
|
+
}
|
|
23165
|
+
const handleClick = (e) => {
|
|
23166
|
+
e.stopPropagation();
|
|
23167
|
+
e.preventDefault();
|
|
23168
|
+
setOpen((v) => !v);
|
|
23169
|
+
};
|
|
23170
|
+
const filtered = Object.entries(icons).filter(
|
|
23171
|
+
([name]) => name.toLowerCase().includes(search.toLowerCase())
|
|
23172
|
+
);
|
|
23173
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
23174
|
+
/* @__PURE__ */ jsx(
|
|
23175
|
+
"span",
|
|
23176
|
+
{
|
|
23177
|
+
ref: anchorRef,
|
|
23178
|
+
"data-setto-icon": true,
|
|
23179
|
+
"data-setto-key": k,
|
|
23180
|
+
role: "button",
|
|
23181
|
+
tabIndex: 0,
|
|
23182
|
+
"aria-label": "Velg ikon",
|
|
23183
|
+
onClick: handleClick,
|
|
23184
|
+
onKeyDown: (e) => {
|
|
23185
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
23186
|
+
e.preventDefault();
|
|
23187
|
+
setOpen((v) => !v);
|
|
23188
|
+
}
|
|
23189
|
+
},
|
|
23190
|
+
style: {
|
|
23191
|
+
display: "inline-flex",
|
|
23192
|
+
cursor: "pointer",
|
|
23193
|
+
outline: open ? "2px solid #640AFF" : void 0,
|
|
23194
|
+
outlineOffset: 2,
|
|
23195
|
+
borderRadius: 4
|
|
23196
|
+
},
|
|
23197
|
+
children: /* @__PURE__ */ jsx(Icon, { size, className, style })
|
|
23198
|
+
}
|
|
23199
|
+
),
|
|
23200
|
+
/* @__PURE__ */ jsxs(
|
|
23201
|
+
FloatingPopover,
|
|
23202
|
+
{
|
|
23203
|
+
open,
|
|
23204
|
+
anchorRef,
|
|
23205
|
+
onClose: () => {
|
|
23206
|
+
setOpen(false);
|
|
23207
|
+
setSearch("");
|
|
23208
|
+
},
|
|
23209
|
+
ariaLabel: "Ikonvelger",
|
|
23210
|
+
minWidth: 320,
|
|
23211
|
+
style: { padding: 12, maxWidth: 360 },
|
|
23212
|
+
children: [
|
|
23213
|
+
/* @__PURE__ */ jsx(
|
|
23214
|
+
"input",
|
|
23215
|
+
{
|
|
23216
|
+
type: "search",
|
|
23217
|
+
value: search,
|
|
23218
|
+
onChange: (e) => setSearch(e.target.value),
|
|
23219
|
+
placeholder: "Søk ikon…",
|
|
23220
|
+
"data-setto-ui": true,
|
|
23221
|
+
style: {
|
|
23222
|
+
width: "100%",
|
|
23223
|
+
boxSizing: "border-box",
|
|
23224
|
+
padding: "8px 10px",
|
|
23225
|
+
marginBottom: 10,
|
|
23226
|
+
border: "1px solid #e0e0e0",
|
|
23227
|
+
borderRadius: 6,
|
|
23228
|
+
fontSize: 13
|
|
23229
|
+
}
|
|
23230
|
+
}
|
|
23231
|
+
),
|
|
23232
|
+
/* @__PURE__ */ jsx(
|
|
23233
|
+
"div",
|
|
23234
|
+
{
|
|
23235
|
+
style: {
|
|
23236
|
+
display: "grid",
|
|
23237
|
+
gridTemplateColumns: "repeat(6, 1fr)",
|
|
23238
|
+
gap: 4,
|
|
23239
|
+
maxHeight: 240,
|
|
23240
|
+
overflowY: "auto"
|
|
23241
|
+
},
|
|
23242
|
+
children: filtered.map(([name, Item]) => {
|
|
23243
|
+
const selected = name === iconName;
|
|
23244
|
+
return /* @__PURE__ */ jsx(
|
|
23245
|
+
"button",
|
|
23246
|
+
{
|
|
23247
|
+
type: "button",
|
|
23248
|
+
title: name,
|
|
23249
|
+
"data-setto-ui": true,
|
|
23250
|
+
onClick: () => {
|
|
23251
|
+
store?.set(k, i18n.language, name);
|
|
23252
|
+
setOpen(false);
|
|
23253
|
+
setSearch("");
|
|
23254
|
+
},
|
|
23255
|
+
style: {
|
|
23256
|
+
display: "flex",
|
|
23257
|
+
alignItems: "center",
|
|
23258
|
+
justifyContent: "center",
|
|
23259
|
+
padding: 8,
|
|
23260
|
+
background: selected ? "#f5f3ff" : "transparent",
|
|
23261
|
+
border: selected ? "1px solid #640AFF" : "1px solid transparent",
|
|
23262
|
+
borderRadius: 6,
|
|
23263
|
+
cursor: "pointer",
|
|
23264
|
+
color: style?.color ?? "currentColor"
|
|
23265
|
+
},
|
|
23266
|
+
children: /* @__PURE__ */ jsx(Item, { size: 20 })
|
|
23267
|
+
},
|
|
23268
|
+
name
|
|
23269
|
+
);
|
|
23270
|
+
})
|
|
23271
|
+
}
|
|
23272
|
+
)
|
|
23273
|
+
]
|
|
23274
|
+
}
|
|
23275
|
+
)
|
|
23276
|
+
] });
|
|
23277
|
+
}
|
|
23278
|
+
const ACCEPT = "image/png,image/jpeg,image/webp,image/svg+xml";
|
|
23279
|
+
function setSrcAllLanguages(store, i18n, srcKey, value) {
|
|
23280
|
+
const langs = i18n.languages?.length ? i18n.languages : [i18n.language];
|
|
23281
|
+
for (const lng of langs) {
|
|
23282
|
+
store.set(srcKey, lng, value);
|
|
23283
|
+
}
|
|
23284
|
+
}
|
|
23285
|
+
function SettoImage({ srcKey, alt, className, style, ...rest }) {
|
|
23286
|
+
const { t, i18n } = useTranslation();
|
|
23287
|
+
const { editMode, store, assetStore } = useSetto();
|
|
23288
|
+
const fileRef = useRef(null);
|
|
23289
|
+
const [hovered, setHovered] = useState(false);
|
|
23290
|
+
const [uploading, setUploading] = useState(false);
|
|
23291
|
+
const [, force] = useState(0);
|
|
23292
|
+
useEffect(() => {
|
|
23293
|
+
if (!store) return;
|
|
23294
|
+
return store.subscribe(() => force((x) => x + 1));
|
|
23295
|
+
}, [store]);
|
|
23296
|
+
const [, forceAssets] = useState(0);
|
|
23297
|
+
useEffect(() => {
|
|
23298
|
+
if (!assetStore) return;
|
|
23299
|
+
return assetStore.subscribe(() => forceAssets((x) => x + 1));
|
|
23300
|
+
}, [assetStore]);
|
|
23301
|
+
const srcPath = store ? store.get(srcKey, i18n.language) : t(srcKey);
|
|
23302
|
+
const previewUrl = assetStore?.getPreviewUrl(srcKey);
|
|
23303
|
+
const src = previewUrl ?? srcPath;
|
|
23304
|
+
if (!editMode) {
|
|
23305
|
+
return /* @__PURE__ */ jsx("img", { src: srcPath, alt, className, style, ...rest });
|
|
23306
|
+
}
|
|
23307
|
+
const isFullWidth = className?.includes("w-full");
|
|
23308
|
+
const isFullHeight = className?.includes("h-full");
|
|
23309
|
+
const openFilePicker = (e) => {
|
|
23310
|
+
e.stopPropagation();
|
|
23311
|
+
e.preventDefault();
|
|
23312
|
+
if (!uploading) fileRef.current?.click();
|
|
23313
|
+
};
|
|
23314
|
+
const handleFile = async (e) => {
|
|
23315
|
+
const file = e.target.files?.[0];
|
|
23316
|
+
e.target.value = "";
|
|
23317
|
+
if (!file || !assetStore || !store) return;
|
|
23318
|
+
setUploading(true);
|
|
23319
|
+
try {
|
|
23320
|
+
const originalPublicPath = store.original(srcKey, i18n.language);
|
|
23321
|
+
const { repoPath, publicPath } = generateSettoImagePaths(srcKey, file);
|
|
23322
|
+
await assetStore.set({
|
|
23323
|
+
srcKey,
|
|
23324
|
+
originalPublicPath,
|
|
23325
|
+
repoPath,
|
|
23326
|
+
publicPath,
|
|
23327
|
+
file
|
|
23328
|
+
});
|
|
23329
|
+
setSrcAllLanguages(store, i18n, srcKey, publicPath);
|
|
23330
|
+
} finally {
|
|
23331
|
+
setUploading(false);
|
|
23332
|
+
}
|
|
23333
|
+
};
|
|
23334
|
+
const handleRevert = (e) => {
|
|
23335
|
+
e.stopPropagation();
|
|
23336
|
+
e.preventDefault();
|
|
23337
|
+
if (!assetStore || !store) return;
|
|
23338
|
+
const original = assetStore.revert(srcKey);
|
|
23339
|
+
if (original !== null) {
|
|
23340
|
+
setSrcAllLanguages(store, i18n, srcKey, original);
|
|
23341
|
+
}
|
|
23342
|
+
};
|
|
23343
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
23344
|
+
/* @__PURE__ */ jsxs(
|
|
23345
|
+
"span",
|
|
23346
|
+
{
|
|
23347
|
+
"data-setto-image": true,
|
|
23348
|
+
"data-setto-ui": true,
|
|
23349
|
+
onMouseEnter: () => setHovered(true),
|
|
23350
|
+
onMouseLeave: () => setHovered(false),
|
|
23351
|
+
style: {
|
|
23352
|
+
display: isFullWidth || isFullHeight ? "block" : "inline-block",
|
|
23353
|
+
width: isFullWidth ? "100%" : void 0,
|
|
23354
|
+
height: isFullHeight ? "100%" : void 0,
|
|
23355
|
+
position: "relative",
|
|
23356
|
+
outline: hovered ? "2px solid #640AFF" : void 0,
|
|
23357
|
+
outlineOffset: 2,
|
|
23358
|
+
borderRadius: 4
|
|
23359
|
+
},
|
|
23360
|
+
children: [
|
|
23361
|
+
/* @__PURE__ */ jsx("img", { src, alt, className, style, ...rest }),
|
|
23362
|
+
previewUrl && !hovered ? /* @__PURE__ */ jsx(
|
|
23363
|
+
"span",
|
|
23364
|
+
{
|
|
23365
|
+
"aria-hidden": true,
|
|
23366
|
+
"data-setto-ui": true,
|
|
23367
|
+
style: {
|
|
23368
|
+
position: "absolute",
|
|
23369
|
+
top: 4,
|
|
23370
|
+
right: 4,
|
|
23371
|
+
background: "#640AFF",
|
|
23372
|
+
color: "#fff",
|
|
23373
|
+
fontSize: 10,
|
|
23374
|
+
padding: "2px 6px",
|
|
23375
|
+
borderRadius: 4,
|
|
23376
|
+
fontFamily: "system-ui, sans-serif",
|
|
23377
|
+
pointerEvents: "none"
|
|
23378
|
+
},
|
|
23379
|
+
children: "Nytt bilde"
|
|
23380
|
+
}
|
|
23381
|
+
) : null,
|
|
23382
|
+
hovered ? /* @__PURE__ */ jsxs(
|
|
23383
|
+
"span",
|
|
23384
|
+
{
|
|
23385
|
+
"data-setto-ui": true,
|
|
23386
|
+
style: {
|
|
23387
|
+
position: "absolute",
|
|
23388
|
+
inset: 0,
|
|
23389
|
+
display: "flex",
|
|
23390
|
+
flexDirection: "column",
|
|
23391
|
+
alignItems: "center",
|
|
23392
|
+
justifyContent: "center",
|
|
23393
|
+
gap: 8,
|
|
23394
|
+
background: "rgba(0, 0, 0, 0.45)",
|
|
23395
|
+
borderRadius: 4
|
|
23396
|
+
},
|
|
23397
|
+
children: [
|
|
23398
|
+
/* @__PURE__ */ jsx(
|
|
23399
|
+
"button",
|
|
23400
|
+
{
|
|
23401
|
+
type: "button",
|
|
23402
|
+
"data-setto-ui": true,
|
|
23403
|
+
disabled: uploading,
|
|
23404
|
+
onClick: openFilePicker,
|
|
23405
|
+
onMouseDown: (e) => e.stopPropagation(),
|
|
23406
|
+
style: {
|
|
23407
|
+
padding: "10px 18px",
|
|
23408
|
+
background: "#fff",
|
|
23409
|
+
color: "#222",
|
|
23410
|
+
border: "none",
|
|
23411
|
+
borderRadius: 6,
|
|
23412
|
+
fontSize: 13,
|
|
23413
|
+
fontWeight: 500,
|
|
23414
|
+
fontFamily: 'system-ui, -apple-system, "Segoe UI", sans-serif',
|
|
23415
|
+
cursor: uploading ? "wait" : "pointer",
|
|
23416
|
+
boxShadow: "0 2px 8px rgba(0,0,0,0.2)"
|
|
23417
|
+
},
|
|
23418
|
+
children: uploading ? "Laster opp…" : "Velg fil…"
|
|
23419
|
+
}
|
|
23420
|
+
),
|
|
23421
|
+
previewUrl ? /* @__PURE__ */ jsx(
|
|
23422
|
+
"button",
|
|
23423
|
+
{
|
|
23424
|
+
type: "button",
|
|
23425
|
+
"data-setto-ui": true,
|
|
23426
|
+
disabled: uploading,
|
|
23427
|
+
onClick: handleRevert,
|
|
23428
|
+
onMouseDown: (e) => e.stopPropagation(),
|
|
23429
|
+
style: {
|
|
23430
|
+
padding: "6px 12px",
|
|
23431
|
+
background: "transparent",
|
|
23432
|
+
color: "#fff",
|
|
23433
|
+
border: "1px solid rgba(255,255,255,0.6)",
|
|
23434
|
+
borderRadius: 6,
|
|
23435
|
+
fontSize: 12,
|
|
23436
|
+
fontFamily: 'system-ui, -apple-system, "Segoe UI", sans-serif',
|
|
23437
|
+
cursor: uploading ? "wait" : "pointer"
|
|
23438
|
+
},
|
|
23439
|
+
children: "Angre"
|
|
23440
|
+
}
|
|
23441
|
+
) : null
|
|
23442
|
+
]
|
|
23443
|
+
}
|
|
23444
|
+
) : null
|
|
23445
|
+
]
|
|
23446
|
+
}
|
|
23447
|
+
),
|
|
23448
|
+
/* @__PURE__ */ jsx(
|
|
23449
|
+
"input",
|
|
23450
|
+
{
|
|
23451
|
+
ref: fileRef,
|
|
23452
|
+
type: "file",
|
|
23453
|
+
accept: ACCEPT,
|
|
23454
|
+
hidden: true,
|
|
23455
|
+
onChange: handleFile
|
|
23456
|
+
}
|
|
23457
|
+
)
|
|
23458
|
+
] });
|
|
23459
|
+
}
|
|
23460
|
+
function SettoRepeater({
|
|
23461
|
+
itemsKey,
|
|
23462
|
+
defaultItem,
|
|
23463
|
+
itemLabel = "element",
|
|
23464
|
+
children,
|
|
23465
|
+
className
|
|
23466
|
+
}) {
|
|
23467
|
+
const { i18n } = useTranslation();
|
|
23468
|
+
const { editMode, store } = useSetto();
|
|
23469
|
+
const lng = i18n.language;
|
|
23470
|
+
const [, force] = useState(0);
|
|
23471
|
+
useEffect(() => {
|
|
23472
|
+
if (!store) return;
|
|
23473
|
+
return store.subscribe(() => force((x) => x + 1));
|
|
23474
|
+
}, [store]);
|
|
23475
|
+
const keys = store ? store.getListKeys(itemsKey, lng) : [];
|
|
23476
|
+
const handleAdd = () => {
|
|
23477
|
+
store?.addListItem(itemsKey, lng, defaultItem);
|
|
23478
|
+
};
|
|
23479
|
+
const handleRemove = (itemKey) => {
|
|
23480
|
+
if (keys.length <= 1) return;
|
|
23481
|
+
store?.removeListItem(itemsKey, itemKey, lng);
|
|
23482
|
+
};
|
|
23483
|
+
return /* @__PURE__ */ jsxs("div", { className, "data-setto-repeater": true, children: [
|
|
23484
|
+
editMode ? /* @__PURE__ */ jsx(
|
|
23485
|
+
"div",
|
|
23486
|
+
{
|
|
23487
|
+
"data-setto-ui": true,
|
|
23488
|
+
style: {
|
|
23489
|
+
display: "flex",
|
|
23490
|
+
justifyContent: "flex-end",
|
|
23491
|
+
marginBottom: 8,
|
|
23492
|
+
gap: 8
|
|
23493
|
+
},
|
|
23494
|
+
children: /* @__PURE__ */ jsxs(
|
|
23495
|
+
"button",
|
|
23496
|
+
{
|
|
23497
|
+
type: "button",
|
|
23498
|
+
"data-setto-ui": true,
|
|
23499
|
+
onClick: (e) => {
|
|
23500
|
+
e.stopPropagation();
|
|
23501
|
+
handleAdd();
|
|
23502
|
+
},
|
|
23503
|
+
style: controlBtnStyle,
|
|
23504
|
+
children: [
|
|
23505
|
+
"+ Legg til ",
|
|
23506
|
+
itemLabel
|
|
23507
|
+
]
|
|
23508
|
+
}
|
|
23509
|
+
)
|
|
23510
|
+
}
|
|
23511
|
+
) : null,
|
|
23512
|
+
keys.map((itemKey, index) => /* @__PURE__ */ jsxs("div", { className: "setto-repeater-item", style: { position: "relative" }, children: [
|
|
23513
|
+
editMode ? /* @__PURE__ */ jsx(
|
|
23514
|
+
"button",
|
|
23515
|
+
{
|
|
23516
|
+
type: "button",
|
|
23517
|
+
"data-setto-ui": true,
|
|
23518
|
+
"aria-label": `Fjern ${itemLabel}`,
|
|
23519
|
+
disabled: keys.length <= 1,
|
|
23520
|
+
onClick: (e) => {
|
|
23521
|
+
e.stopPropagation();
|
|
23522
|
+
handleRemove(itemKey);
|
|
23523
|
+
},
|
|
23524
|
+
style: {
|
|
23525
|
+
...controlBtnStyle,
|
|
23526
|
+
position: "absolute",
|
|
23527
|
+
top: 0,
|
|
23528
|
+
right: 0,
|
|
23529
|
+
zIndex: 2,
|
|
23530
|
+
padding: "4px 8px",
|
|
23531
|
+
fontSize: 11,
|
|
23532
|
+
opacity: keys.length <= 1 ? 0.4 : 1
|
|
23533
|
+
},
|
|
23534
|
+
children: "× Fjern"
|
|
23535
|
+
}
|
|
23536
|
+
) : null,
|
|
23537
|
+
children(itemKey, index)
|
|
23538
|
+
] }, itemKey))
|
|
23539
|
+
] });
|
|
23540
|
+
}
|
|
23541
|
+
const controlBtnStyle = {
|
|
23542
|
+
padding: "6px 10px",
|
|
23543
|
+
background: "#f7f7f7",
|
|
23544
|
+
border: "1px solid #e0e0e0",
|
|
23545
|
+
borderRadius: 6,
|
|
23546
|
+
fontSize: 12,
|
|
23547
|
+
color: "#444",
|
|
23548
|
+
cursor: "pointer",
|
|
23549
|
+
fontFamily: 'system-ui, -apple-system, "Segoe UI", sans-serif'
|
|
23550
|
+
};
|
|
22839
23551
|
function editModeUrl(pathname = "/") {
|
|
22840
23552
|
const u = new URL(window.location.href);
|
|
22841
23553
|
u.pathname = pathname;
|
|
@@ -23475,7 +24187,10 @@ export {
|
|
|
23475
24187
|
AuthGate,
|
|
23476
24188
|
SettoAdminApp,
|
|
23477
24189
|
SettoBlock,
|
|
24190
|
+
SettoIcon,
|
|
24191
|
+
SettoImage,
|
|
23478
24192
|
SettoProvider,
|
|
24193
|
+
SettoRepeater,
|
|
23479
24194
|
SettoSection,
|
|
23480
24195
|
T,
|
|
23481
24196
|
useSectionTheme,
|