ngx-xtroedge-cms 1.3.0 → 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 +135 -58
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +135 -58
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -277,7 +277,9 @@ var DEFAULT_EDITABLE_TAGS = [
|
|
|
277
277
|
"blockquote",
|
|
278
278
|
"td",
|
|
279
279
|
"th",
|
|
280
|
-
"div"
|
|
280
|
+
"div",
|
|
281
|
+
"header",
|
|
282
|
+
"footer"
|
|
281
283
|
];
|
|
282
284
|
var XtroedgeCMS = class _XtroedgeCMS {
|
|
283
285
|
constructor(config) {
|
|
@@ -751,7 +753,7 @@ var XtroedgeCMS = class _XtroedgeCMS {
|
|
|
751
753
|
if (this.containerSelector) {
|
|
752
754
|
return document.querySelector(this.containerSelector) || document.body;
|
|
753
755
|
}
|
|
754
|
-
return document.
|
|
756
|
+
return document.body;
|
|
755
757
|
}
|
|
756
758
|
autoDetectElements() {
|
|
757
759
|
const container = this.resolveContainer();
|
|
@@ -759,7 +761,29 @@ var XtroedgeCMS = class _XtroedgeCMS {
|
|
|
759
761
|
for (const el of this.autoDetectedElements) {
|
|
760
762
|
if (!document.contains(el)) this.autoDetectedElements.delete(el);
|
|
761
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
|
+
};
|
|
762
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
|
+
};
|
|
763
787
|
const selector = this.editableTags.join(",");
|
|
764
788
|
const elements = container.querySelectorAll(selector);
|
|
765
789
|
elements.forEach((el) => {
|
|
@@ -768,12 +792,13 @@ var XtroedgeCMS = class _XtroedgeCMS {
|
|
|
768
792
|
if (el.children.length > 3) return;
|
|
769
793
|
const text = this.getDirectTextContent(el).trim();
|
|
770
794
|
if (text.length < 2) return;
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
el.
|
|
776
|
-
|
|
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));
|
|
777
802
|
});
|
|
778
803
|
}
|
|
779
804
|
scanDOM() {
|
|
@@ -844,8 +869,13 @@ var XtroedgeCMS = class _XtroedgeCMS {
|
|
|
844
869
|
const currentVal = this.getPageText(key);
|
|
845
870
|
if (text !== currentVal) this.onTextChanged(key, text);
|
|
846
871
|
};
|
|
847
|
-
|
|
848
|
-
|
|
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);
|
|
849
879
|
}
|
|
850
880
|
detachElement(el) {
|
|
851
881
|
const info = this.managedElements.get(el);
|
|
@@ -853,11 +883,12 @@ var XtroedgeCMS = class _XtroedgeCMS {
|
|
|
853
883
|
el.removeEventListener("blur", info.blurHandler);
|
|
854
884
|
el.removeEventListener("keydown", info.keydownHandler);
|
|
855
885
|
el.removeEventListener("input", info.inputHandler);
|
|
886
|
+
el.removeEventListener("click", info.clickHandler, true);
|
|
856
887
|
el.removeAttribute("contenteditable");
|
|
857
888
|
this.managedElements.delete(el);
|
|
858
889
|
}
|
|
859
890
|
}
|
|
860
|
-
enableElementEdit(el, _key, blurH, keyH, inputH) {
|
|
891
|
+
enableElementEdit(el, _key, blurH, keyH, inputH, clickH) {
|
|
861
892
|
const val = this.getPageText(_key);
|
|
862
893
|
if (val) el.textContent = val;
|
|
863
894
|
el.setAttribute("contenteditable", "true");
|
|
@@ -871,8 +902,9 @@ var XtroedgeCMS = class _XtroedgeCMS {
|
|
|
871
902
|
el.addEventListener("blur", blurH);
|
|
872
903
|
el.addEventListener("keydown", keyH);
|
|
873
904
|
el.addEventListener("input", inputH);
|
|
905
|
+
el.addEventListener("click", clickH, true);
|
|
874
906
|
}
|
|
875
|
-
disableElementEdit(el, _key, blurH, keyH, inputH) {
|
|
907
|
+
disableElementEdit(el, _key, blurH, keyH, inputH, clickH) {
|
|
876
908
|
el.removeAttribute("contenteditable");
|
|
877
909
|
el.style.borderBottom = "";
|
|
878
910
|
el.style.padding = "";
|
|
@@ -886,11 +918,12 @@ var XtroedgeCMS = class _XtroedgeCMS {
|
|
|
886
918
|
el.removeEventListener("blur", blurH);
|
|
887
919
|
el.removeEventListener("keydown", keyH);
|
|
888
920
|
el.removeEventListener("input", inputH);
|
|
921
|
+
el.removeEventListener("click", clickH, true);
|
|
889
922
|
}
|
|
890
923
|
applyEditMode(editMode) {
|
|
891
924
|
for (const [el, info] of this.managedElements) {
|
|
892
|
-
if (editMode) this.enableElementEdit(el, info.key, info.blurHandler, info.keydownHandler, info.inputHandler);
|
|
893
|
-
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);
|
|
894
927
|
}
|
|
895
928
|
this.applyImageEditMode(editMode);
|
|
896
929
|
}
|
|
@@ -1212,23 +1245,43 @@ var XtroedgeCMS = class _XtroedgeCMS {
|
|
|
1212
1245
|
getPageSection() {
|
|
1213
1246
|
return this.currentSlug.replace(/^\//, "").replace(/-/g, "_").toUpperCase();
|
|
1214
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
|
+
}
|
|
1215
1259
|
async loadPageContent(status) {
|
|
1216
1260
|
this.setLoading(true);
|
|
1217
1261
|
this.cleanOldHistory();
|
|
1218
1262
|
try {
|
|
1219
|
-
const
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
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
|
+
};
|
|
1225
1274
|
} else {
|
|
1226
1275
|
this.buildDefaultTexts();
|
|
1227
1276
|
}
|
|
1228
|
-
const
|
|
1229
|
-
if (
|
|
1230
|
-
|
|
1231
|
-
|
|
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
|
+
}
|
|
1232
1285
|
}
|
|
1233
1286
|
this.originalTexts = JSON.parse(JSON.stringify(this.pageTexts));
|
|
1234
1287
|
this.originalImages = JSON.parse(JSON.stringify(this.pageImages));
|
|
@@ -1246,18 +1299,24 @@ var XtroedgeCMS = class _XtroedgeCMS {
|
|
|
1246
1299
|
async loadPublishedContent() {
|
|
1247
1300
|
this.setLoading(true);
|
|
1248
1301
|
try {
|
|
1249
|
-
const
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
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));
|
|
1255
1310
|
this.updateElementTexts();
|
|
1256
1311
|
}
|
|
1257
|
-
const
|
|
1258
|
-
if (
|
|
1259
|
-
|
|
1260
|
-
|
|
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
|
+
}
|
|
1261
1320
|
}
|
|
1262
1321
|
} catch {
|
|
1263
1322
|
} finally {
|
|
@@ -1295,6 +1354,18 @@ var XtroedgeCMS = class _XtroedgeCMS {
|
|
|
1295
1354
|
}
|
|
1296
1355
|
}
|
|
1297
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
|
+
}
|
|
1298
1369
|
async saveChanges() {
|
|
1299
1370
|
if (!this.config.apiBase) {
|
|
1300
1371
|
this.showToast("No API configured. Set apiBase to enable save.", "error");
|
|
@@ -1302,19 +1373,22 @@ var XtroedgeCMS = class _XtroedgeCMS {
|
|
|
1302
1373
|
}
|
|
1303
1374
|
this.isSaving = true;
|
|
1304
1375
|
this.updateUI();
|
|
1305
|
-
const payload = {
|
|
1306
|
-
slug: this.currentSlug,
|
|
1307
|
-
title: this.currentTitle,
|
|
1308
|
-
site_identifier: this.siteIdentifier,
|
|
1309
|
-
content: { texts: this.pageTexts, images: this.pageImages }
|
|
1310
|
-
};
|
|
1311
1376
|
try {
|
|
1312
|
-
const
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
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");
|
|
1318
1392
|
this.isSaving = false;
|
|
1319
1393
|
this.resetAfterSave();
|
|
1320
1394
|
this.updateUI();
|
|
@@ -1334,19 +1408,22 @@ var XtroedgeCMS = class _XtroedgeCMS {
|
|
|
1334
1408
|
}
|
|
1335
1409
|
this.isPublishing = true;
|
|
1336
1410
|
this.updateUI();
|
|
1337
|
-
const payload = {
|
|
1338
|
-
slug: this.currentSlug,
|
|
1339
|
-
title: this.currentTitle,
|
|
1340
|
-
site_identifier: this.siteIdentifier,
|
|
1341
|
-
published_content: { texts: this.pageTexts, images: this.pageImages }
|
|
1342
|
-
};
|
|
1343
1411
|
try {
|
|
1344
|
-
const
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
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");
|
|
1350
1427
|
this.isPublishing = false;
|
|
1351
1428
|
this.resetAfterSave();
|
|
1352
1429
|
this.updateUI();
|