ngx-xtroedge-cms 1.3.1 → 1.3.2
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/index.d.mts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.global.js +3 -3
- package/dist/index.js +132 -57
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +132 -57
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -753,7 +753,7 @@ var XtroedgeCMS = class _XtroedgeCMS {
|
|
|
753
753
|
if (this.containerSelector) {
|
|
754
754
|
return document.querySelector(this.containerSelector) || document.body;
|
|
755
755
|
}
|
|
756
|
-
return document.
|
|
756
|
+
return document.body;
|
|
757
757
|
}
|
|
758
758
|
autoDetectElements() {
|
|
759
759
|
const container = this.resolveContainer();
|
|
@@ -761,7 +761,29 @@ var XtroedgeCMS = class _XtroedgeCMS {
|
|
|
761
761
|
for (const el of this.autoDetectedElements) {
|
|
762
762
|
if (!document.contains(el)) this.autoDetectedElements.delete(el);
|
|
763
763
|
}
|
|
764
|
+
const getSectionSlug = (el) => {
|
|
765
|
+
let parent = el.parentElement;
|
|
766
|
+
while (parent && parent !== document.body) {
|
|
767
|
+
const tag = parent.tagName.toLowerCase();
|
|
768
|
+
if (tag === "header" || tag.endsWith("-header")) return "/header";
|
|
769
|
+
if (tag === "footer" || tag.endsWith("-footer")) return "/footer";
|
|
770
|
+
parent = parent.parentElement;
|
|
771
|
+
}
|
|
772
|
+
return this.currentSlug;
|
|
773
|
+
};
|
|
764
774
|
const tagCounters = {};
|
|
775
|
+
const assignKey = (el, sectionSlug) => {
|
|
776
|
+
const sectionKey = sectionSlug === "/header" ? "header" : sectionSlug === "/footer" ? "footer" : "page";
|
|
777
|
+
if (!tagCounters[sectionKey]) tagCounters[sectionKey] = {};
|
|
778
|
+
const tag = el.tagName.toLowerCase();
|
|
779
|
+
if (!tagCounters[sectionKey][tag]) tagCounters[sectionKey][tag] = 0;
|
|
780
|
+
const prefix = sectionKey !== "page" ? `${sectionKey}_` : "";
|
|
781
|
+
const key = `${prefix}xcms_${tag}_${tagCounters[sectionKey][tag]}`;
|
|
782
|
+
tagCounters[sectionKey][tag]++;
|
|
783
|
+
el.setAttribute("data-cms", key);
|
|
784
|
+
el.setAttribute("data-cms-section", sectionSlug);
|
|
785
|
+
this.autoDetectedElements.add(el);
|
|
786
|
+
};
|
|
765
787
|
const selector = this.editableTags.join(",");
|
|
766
788
|
const elements = container.querySelectorAll(selector);
|
|
767
789
|
elements.forEach((el) => {
|
|
@@ -770,12 +792,13 @@ var XtroedgeCMS = class _XtroedgeCMS {
|
|
|
770
792
|
if (el.children.length > 3) return;
|
|
771
793
|
const text = this.getDirectTextContent(el).trim();
|
|
772
794
|
if (text.length < 2) return;
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
el.
|
|
778
|
-
|
|
795
|
+
assignKey(el, getSectionSlug(el));
|
|
796
|
+
});
|
|
797
|
+
const dataEditables = container.querySelectorAll("[data-editable]");
|
|
798
|
+
dataEditables.forEach((el) => {
|
|
799
|
+
if (el.hasAttribute("data-cms")) return;
|
|
800
|
+
if (el.closest("#xtroedge-cms-root, script, style, noscript")) return;
|
|
801
|
+
assignKey(el, getSectionSlug(el));
|
|
779
802
|
});
|
|
780
803
|
}
|
|
781
804
|
scanDOM() {
|
|
@@ -846,8 +869,13 @@ var XtroedgeCMS = class _XtroedgeCMS {
|
|
|
846
869
|
const currentVal = this.getPageText(key);
|
|
847
870
|
if (text !== currentVal) this.onTextChanged(key, text);
|
|
848
871
|
};
|
|
849
|
-
|
|
850
|
-
|
|
872
|
+
const clickHandler = (e) => {
|
|
873
|
+
e.preventDefault();
|
|
874
|
+
e.stopPropagation();
|
|
875
|
+
};
|
|
876
|
+
const sectionSlug = el.getAttribute("data-cms-section") || this.currentSlug;
|
|
877
|
+
this.managedElements.set(el, { key, sectionSlug, blurHandler, keydownHandler, inputHandler, clickHandler });
|
|
878
|
+
if (this.editMode) this.enableElementEdit(el, key, blurHandler, keydownHandler, inputHandler, clickHandler);
|
|
851
879
|
}
|
|
852
880
|
detachElement(el) {
|
|
853
881
|
const info = this.managedElements.get(el);
|
|
@@ -855,11 +883,12 @@ var XtroedgeCMS = class _XtroedgeCMS {
|
|
|
855
883
|
el.removeEventListener("blur", info.blurHandler);
|
|
856
884
|
el.removeEventListener("keydown", info.keydownHandler);
|
|
857
885
|
el.removeEventListener("input", info.inputHandler);
|
|
886
|
+
el.removeEventListener("click", info.clickHandler, true);
|
|
858
887
|
el.removeAttribute("contenteditable");
|
|
859
888
|
this.managedElements.delete(el);
|
|
860
889
|
}
|
|
861
890
|
}
|
|
862
|
-
enableElementEdit(el, _key, blurH, keyH, inputH) {
|
|
891
|
+
enableElementEdit(el, _key, blurH, keyH, inputH, clickH) {
|
|
863
892
|
const val = this.getPageText(_key);
|
|
864
893
|
if (val) el.textContent = val;
|
|
865
894
|
el.setAttribute("contenteditable", "true");
|
|
@@ -873,8 +902,9 @@ var XtroedgeCMS = class _XtroedgeCMS {
|
|
|
873
902
|
el.addEventListener("blur", blurH);
|
|
874
903
|
el.addEventListener("keydown", keyH);
|
|
875
904
|
el.addEventListener("input", inputH);
|
|
905
|
+
el.addEventListener("click", clickH, true);
|
|
876
906
|
}
|
|
877
|
-
disableElementEdit(el, _key, blurH, keyH, inputH) {
|
|
907
|
+
disableElementEdit(el, _key, blurH, keyH, inputH, clickH) {
|
|
878
908
|
el.removeAttribute("contenteditable");
|
|
879
909
|
el.style.borderBottom = "";
|
|
880
910
|
el.style.padding = "";
|
|
@@ -888,11 +918,12 @@ var XtroedgeCMS = class _XtroedgeCMS {
|
|
|
888
918
|
el.removeEventListener("blur", blurH);
|
|
889
919
|
el.removeEventListener("keydown", keyH);
|
|
890
920
|
el.removeEventListener("input", inputH);
|
|
921
|
+
el.removeEventListener("click", clickH, true);
|
|
891
922
|
}
|
|
892
923
|
applyEditMode(editMode) {
|
|
893
924
|
for (const [el, info] of this.managedElements) {
|
|
894
|
-
if (editMode) this.enableElementEdit(el, info.key, info.blurHandler, info.keydownHandler, info.inputHandler);
|
|
895
|
-
else this.disableElementEdit(el, info.key, info.blurHandler, info.keydownHandler, info.inputHandler);
|
|
925
|
+
if (editMode) this.enableElementEdit(el, info.key, info.blurHandler, info.keydownHandler, info.inputHandler, info.clickHandler);
|
|
926
|
+
else this.disableElementEdit(el, info.key, info.blurHandler, info.keydownHandler, info.inputHandler, info.clickHandler);
|
|
896
927
|
}
|
|
897
928
|
this.applyImageEditMode(editMode);
|
|
898
929
|
}
|
|
@@ -1214,23 +1245,43 @@ var XtroedgeCMS = class _XtroedgeCMS {
|
|
|
1214
1245
|
getPageSection() {
|
|
1215
1246
|
return this.currentSlug.replace(/^\//, "").replace(/-/g, "_").toUpperCase();
|
|
1216
1247
|
}
|
|
1248
|
+
// Fetch texts for a single slug and merge into pageTexts
|
|
1249
|
+
async fetchSectionTexts(slug, status) {
|
|
1250
|
+
try {
|
|
1251
|
+
const res = await fetch(`${this.config.apiBase}/web-page/get?slug=${encodeURIComponent(slug)}&status=${status}&site_identifier=${encodeURIComponent(this.siteIdentifier)}`);
|
|
1252
|
+
if (!res.ok) return null;
|
|
1253
|
+
const page = await res.json();
|
|
1254
|
+
return page?.published_content?.texts || page?.content?.texts || null;
|
|
1255
|
+
} catch {
|
|
1256
|
+
return null;
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1217
1259
|
async loadPageContent(status) {
|
|
1218
1260
|
this.setLoading(true);
|
|
1219
1261
|
this.cleanOldHistory();
|
|
1220
1262
|
try {
|
|
1221
|
-
const
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1263
|
+
const [pageTexts, headerTexts, footerTexts] = await Promise.all([
|
|
1264
|
+
this.fetchSectionTexts(this.currentSlug, status),
|
|
1265
|
+
this.fetchSectionTexts("/header", status),
|
|
1266
|
+
this.fetchSectionTexts("/footer", status)
|
|
1267
|
+
]);
|
|
1268
|
+
if (pageTexts || headerTexts || footerTexts) {
|
|
1269
|
+
this.pageTexts = {
|
|
1270
|
+
...pageTexts || {},
|
|
1271
|
+
...headerTexts || {},
|
|
1272
|
+
...footerTexts || {}
|
|
1273
|
+
};
|
|
1227
1274
|
} else {
|
|
1228
1275
|
this.buildDefaultTexts();
|
|
1229
1276
|
}
|
|
1230
|
-
const
|
|
1231
|
-
if (
|
|
1232
|
-
|
|
1233
|
-
|
|
1277
|
+
const resImg = await fetch(`${this.config.apiBase}/web-page/get?slug=${encodeURIComponent(this.currentSlug)}&status=${status}&site_identifier=${encodeURIComponent(this.siteIdentifier)}`).catch(() => null);
|
|
1278
|
+
if (resImg?.ok) {
|
|
1279
|
+
const page = await resImg.json();
|
|
1280
|
+
const images = page?.published_content?.images || page?.content?.images;
|
|
1281
|
+
if (images) {
|
|
1282
|
+
this.pageImages = JSON.parse(JSON.stringify(images));
|
|
1283
|
+
this.applyPageImages();
|
|
1284
|
+
}
|
|
1234
1285
|
}
|
|
1235
1286
|
this.originalTexts = JSON.parse(JSON.stringify(this.pageTexts));
|
|
1236
1287
|
this.originalImages = JSON.parse(JSON.stringify(this.pageImages));
|
|
@@ -1248,18 +1299,24 @@ var XtroedgeCMS = class _XtroedgeCMS {
|
|
|
1248
1299
|
async loadPublishedContent() {
|
|
1249
1300
|
this.setLoading(true);
|
|
1250
1301
|
try {
|
|
1251
|
-
const
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1302
|
+
const [pageTexts, headerTexts, footerTexts] = await Promise.all([
|
|
1303
|
+
this.fetchSectionTexts(this.currentSlug, "published"),
|
|
1304
|
+
this.fetchSectionTexts("/header", "published"),
|
|
1305
|
+
this.fetchSectionTexts("/footer", "published")
|
|
1306
|
+
]);
|
|
1307
|
+
const merged = { ...pageTexts || {}, ...headerTexts || {}, ...footerTexts || {} };
|
|
1308
|
+
if (Object.keys(merged).length > 0) {
|
|
1309
|
+
this.pageTexts = JSON.parse(JSON.stringify(merged));
|
|
1257
1310
|
this.updateElementTexts();
|
|
1258
1311
|
}
|
|
1259
|
-
const
|
|
1260
|
-
if (
|
|
1261
|
-
|
|
1262
|
-
|
|
1312
|
+
const res = await fetch(`${this.config.apiBase}/web-page/get?slug=${encodeURIComponent(this.currentSlug)}&status=published&site_identifier=${encodeURIComponent(this.siteIdentifier)}`);
|
|
1313
|
+
if (res.ok) {
|
|
1314
|
+
const page = await res.json();
|
|
1315
|
+
const images = page?.published_content?.images;
|
|
1316
|
+
if (images && Object.keys(images).length > 0) {
|
|
1317
|
+
this.pageImages = JSON.parse(JSON.stringify(images));
|
|
1318
|
+
this.applyPageImages();
|
|
1319
|
+
}
|
|
1263
1320
|
}
|
|
1264
1321
|
} catch {
|
|
1265
1322
|
} finally {
|
|
@@ -1297,6 +1354,18 @@ var XtroedgeCMS = class _XtroedgeCMS {
|
|
|
1297
1354
|
}
|
|
1298
1355
|
}
|
|
1299
1356
|
}
|
|
1357
|
+
// Group pageTexts by section slug based on managedElements info
|
|
1358
|
+
groupTextsBySection() {
|
|
1359
|
+
const sections = {};
|
|
1360
|
+
for (const [, info] of this.managedElements) {
|
|
1361
|
+
const slug = info.sectionSlug;
|
|
1362
|
+
if (!sections[slug]) sections[slug] = {};
|
|
1363
|
+
if (this.pageTexts[info.key] !== void 0) {
|
|
1364
|
+
sections[slug][info.key] = this.pageTexts[info.key];
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
return sections;
|
|
1368
|
+
}
|
|
1300
1369
|
async saveChanges() {
|
|
1301
1370
|
if (!this.config.apiBase) {
|
|
1302
1371
|
this.showToast("No API configured. Set apiBase to enable save.", "error");
|
|
@@ -1304,19 +1373,22 @@ var XtroedgeCMS = class _XtroedgeCMS {
|
|
|
1304
1373
|
}
|
|
1305
1374
|
this.isSaving = true;
|
|
1306
1375
|
this.updateUI();
|
|
1307
|
-
const payload = {
|
|
1308
|
-
slug: this.currentSlug,
|
|
1309
|
-
title: this.currentTitle,
|
|
1310
|
-
site_identifier: this.siteIdentifier,
|
|
1311
|
-
content: { texts: this.pageTexts, images: this.pageImages }
|
|
1312
|
-
};
|
|
1313
1376
|
try {
|
|
1314
|
-
const
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1377
|
+
const sections = this.groupTextsBySection();
|
|
1378
|
+
const requests = Object.entries(sections).map(
|
|
1379
|
+
([slug, texts]) => fetch(`${this.config.apiBase}/web_page/save`, {
|
|
1380
|
+
method: "POST",
|
|
1381
|
+
headers: { "Content-Type": "application/json" },
|
|
1382
|
+
body: JSON.stringify({
|
|
1383
|
+
slug,
|
|
1384
|
+
title: slug === this.currentSlug ? this.currentTitle : slug.replace("/", ""),
|
|
1385
|
+
site_identifier: this.siteIdentifier,
|
|
1386
|
+
content: { texts, ...slug === this.currentSlug ? { images: this.pageImages } : {} }
|
|
1387
|
+
})
|
|
1388
|
+
})
|
|
1389
|
+
);
|
|
1390
|
+
const results = await Promise.all(requests);
|
|
1391
|
+
if (results.some((r) => !r.ok)) throw new Error("One or more sections failed to save");
|
|
1320
1392
|
this.isSaving = false;
|
|
1321
1393
|
this.resetAfterSave();
|
|
1322
1394
|
this.updateUI();
|
|
@@ -1336,19 +1408,22 @@ var XtroedgeCMS = class _XtroedgeCMS {
|
|
|
1336
1408
|
}
|
|
1337
1409
|
this.isPublishing = true;
|
|
1338
1410
|
this.updateUI();
|
|
1339
|
-
const payload = {
|
|
1340
|
-
slug: this.currentSlug,
|
|
1341
|
-
title: this.currentTitle,
|
|
1342
|
-
site_identifier: this.siteIdentifier,
|
|
1343
|
-
published_content: { texts: this.pageTexts, images: this.pageImages }
|
|
1344
|
-
};
|
|
1345
1411
|
try {
|
|
1346
|
-
const
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1412
|
+
const sections = this.groupTextsBySection();
|
|
1413
|
+
const requests = Object.entries(sections).map(
|
|
1414
|
+
([slug, texts]) => fetch(`${this.config.apiBase}/web_page/publish`, {
|
|
1415
|
+
method: "POST",
|
|
1416
|
+
headers: { "Content-Type": "application/json" },
|
|
1417
|
+
body: JSON.stringify({
|
|
1418
|
+
slug,
|
|
1419
|
+
title: slug === this.currentSlug ? this.currentTitle : slug.replace("/", ""),
|
|
1420
|
+
site_identifier: this.siteIdentifier,
|
|
1421
|
+
published_content: { texts, ...slug === this.currentSlug ? { images: this.pageImages } : {} }
|
|
1422
|
+
})
|
|
1423
|
+
})
|
|
1424
|
+
);
|
|
1425
|
+
const results = await Promise.all(requests);
|
|
1426
|
+
if (results.some((r) => !r.ok)) throw new Error("One or more sections failed to publish");
|
|
1352
1427
|
this.isPublishing = false;
|
|
1353
1428
|
this.resetAfterSave();
|
|
1354
1429
|
this.updateUI();
|