@duffcloudservices/cms 0.3.13 → 0.3.15
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/README.md +23 -0
- package/dist/editor/editorBridge.d.ts +4 -0
- package/dist/editor/editorBridge.js +48 -15
- package/dist/editor/editorBridge.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +68 -15
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/DcsReviewShowcase.vue +17 -22
- package/src/composables/useReviewContent.ts +88 -21
package/README.md
CHANGED
|
@@ -304,6 +304,29 @@ import { useTextContent } from '@duffcloudservices/cms'
|
|
|
304
304
|
|
|
305
305
|
The API is the same, so no other code changes are needed.
|
|
306
306
|
|
|
307
|
+
## DCS Visual Editor Bridge
|
|
308
|
+
|
|
309
|
+
For DCS-managed sites, `src/editor/editorBridge.ts` is the shared
|
|
310
|
+
discovery/runtime layer that turns site DOM markers into portal editing
|
|
311
|
+
entry points.
|
|
312
|
+
|
|
313
|
+
Current first-party surfaces:
|
|
314
|
+
|
|
315
|
+
- **Managed forms** — discovers `[data-form-key]`, reports
|
|
316
|
+
`hasManagedForms`/`managedFormIds`, and emits
|
|
317
|
+
`dcs:managed-form-click`
|
|
318
|
+
- **Reviews** — discovers `[data-dcs-reviews]`, reports
|
|
319
|
+
`hasReviews`/`reviewKeys`, emits `dcs:reviews-click`, and accepts
|
|
320
|
+
structured `dcs:update-reviews` data for live preview refresh
|
|
321
|
+
|
|
322
|
+
The bridge owns discovery and affordances; the portal owns the actual
|
|
323
|
+
editing workflow. Keep every entry point converged on one sheet per
|
|
324
|
+
component family instead of creating parallel editors.
|
|
325
|
+
|
|
326
|
+
See [`../FIRST-PARTY-COMPONENTS.md`](../FIRST-PARTY-COMPONENTS.md) for
|
|
327
|
+
the shared contract across runtime markers, bridge events, portal
|
|
328
|
+
workflows, publishing, rollout, and validation.
|
|
329
|
+
|
|
307
330
|
## License
|
|
308
331
|
|
|
309
332
|
MIT
|
|
@@ -39,6 +39,10 @@ interface EditorReadyPayload {
|
|
|
39
39
|
hasBlogContent: boolean;
|
|
40
40
|
/** Current innerHTML of the blog content element, if present */
|
|
41
41
|
blogContentHtml: string | null;
|
|
42
|
+
/** Whether the page has a [data-event-content] or [data-rich-content="event"] element */
|
|
43
|
+
hasEventContent: boolean;
|
|
44
|
+
/** Current innerHTML of the event content element, if present */
|
|
45
|
+
eventContentHtml: string | null;
|
|
42
46
|
/** Current blog post title text from [data-blog-title], if present */
|
|
43
47
|
blogTitle: string | null;
|
|
44
48
|
/** Current blog post summary/description text from [data-blog-summary], if present */
|
|
@@ -12,15 +12,14 @@ function postToParent(type, data) {
|
|
|
12
12
|
}
|
|
13
13
|
var TEXT_KEY_SELECTOR = "[data-text-key], [data-dcs-text]";
|
|
14
14
|
var REVIEW_SELECTOR = "[data-dcs-reviews]";
|
|
15
|
+
var BLOG_CONTENT_SELECTOR = "[data-blog-content]";
|
|
16
|
+
var EVENT_CONTENT_SELECTOR = '[data-event-content], [data-rich-content="event"]';
|
|
15
17
|
function getTextKey(el) {
|
|
16
18
|
return el.dataset.dcsText ?? el.dataset.textKey ?? "";
|
|
17
19
|
}
|
|
18
20
|
function textKeySelector(key) {
|
|
19
21
|
return `[data-text-key="${key}"], [data-dcs-text="${key}"]`;
|
|
20
22
|
}
|
|
21
|
-
function reviewSelector(key) {
|
|
22
|
-
return `[data-dcs-reviews="${key}"]`;
|
|
23
|
-
}
|
|
24
23
|
var editorActive = false;
|
|
25
24
|
var bridgeInitialized = false;
|
|
26
25
|
var activeEditElement = null;
|
|
@@ -44,6 +43,7 @@ var initializedSections = /* @__PURE__ */ new WeakSet();
|
|
|
44
43
|
var initializedReviewElements = /* @__PURE__ */ new WeakSet();
|
|
45
44
|
var initializedManagedFormElements = /* @__PURE__ */ new WeakSet();
|
|
46
45
|
var blogContentInitialized = false;
|
|
46
|
+
var eventContentInitialized = false;
|
|
47
47
|
var blogMetadataInitialized = false;
|
|
48
48
|
function scheduleRediscovery() {
|
|
49
49
|
if (rediscoveryTimer) clearTimeout(rediscoveryTimer);
|
|
@@ -148,6 +148,7 @@ function checkForNavigation() {
|
|
|
148
148
|
removeArrayEditIcon();
|
|
149
149
|
removeReviewEditIcon();
|
|
150
150
|
blogContentInitialized = false;
|
|
151
|
+
eventContentInitialized = false;
|
|
151
152
|
blogMetadataInitialized = false;
|
|
152
153
|
setTimeout(() => {
|
|
153
154
|
rediscoverAndNotify();
|
|
@@ -167,9 +168,12 @@ function rediscoverAndNotify() {
|
|
|
167
168
|
const reviewKeys = discoverReviewKeys();
|
|
168
169
|
const managedFormIds = discoverManagedFormIds();
|
|
169
170
|
const contentHeight = measureContentHeight(sections);
|
|
170
|
-
const blogEl = document.querySelector(
|
|
171
|
+
const blogEl = document.querySelector(BLOG_CONTENT_SELECTOR);
|
|
171
172
|
const hasBlogContent = !!blogEl;
|
|
172
173
|
const blogContentHtml = blogEl ? blogEl.innerHTML : null;
|
|
174
|
+
const eventEl = document.querySelector(EVENT_CONTENT_SELECTOR);
|
|
175
|
+
const hasEventContent = !!eventEl;
|
|
176
|
+
const eventContentHtml = eventEl ? eventEl.innerHTML : null;
|
|
173
177
|
const blogTitleEl = document.querySelector("[data-blog-title]");
|
|
174
178
|
const blogSummaryEl = document.querySelector("[data-blog-summary]");
|
|
175
179
|
const blogTitle = blogTitleEl ? blogTitleEl.textContent?.trim() ?? null : null;
|
|
@@ -180,6 +184,8 @@ function rediscoverAndNotify() {
|
|
|
180
184
|
contentHeight,
|
|
181
185
|
hasBlogContent,
|
|
182
186
|
blogContentHtml,
|
|
187
|
+
hasEventContent,
|
|
188
|
+
eventContentHtml,
|
|
183
189
|
blogTitle,
|
|
184
190
|
blogSummary,
|
|
185
191
|
hasManagedForms: managedFormIds.length > 0,
|
|
@@ -304,7 +310,7 @@ function applyEditorToElements() {
|
|
|
304
310
|
});
|
|
305
311
|
});
|
|
306
312
|
if (!blogContentInitialized) {
|
|
307
|
-
const blogContentEl = document.querySelector(
|
|
313
|
+
const blogContentEl = document.querySelector(BLOG_CONTENT_SELECTOR);
|
|
308
314
|
if (blogContentEl) {
|
|
309
315
|
blogContentInitialized = true;
|
|
310
316
|
blogContentEl.classList.add("dcs-blog-content");
|
|
@@ -325,6 +331,28 @@ function applyEditorToElements() {
|
|
|
325
331
|
postToParent("dcs:blog-content-ready", { content: blogContentEl.innerHTML });
|
|
326
332
|
}
|
|
327
333
|
}
|
|
334
|
+
if (!eventContentInitialized) {
|
|
335
|
+
const eventContentEl = document.querySelector(EVENT_CONTENT_SELECTOR);
|
|
336
|
+
if (eventContentEl) {
|
|
337
|
+
eventContentInitialized = true;
|
|
338
|
+
eventContentEl.classList.add("dcs-blog-content");
|
|
339
|
+
eventContentEl.addEventListener("click", (e) => {
|
|
340
|
+
const target = e.target;
|
|
341
|
+
if (target.closest(TEXT_KEY_SELECTOR)) return;
|
|
342
|
+
e.preventDefault();
|
|
343
|
+
e.stopPropagation();
|
|
344
|
+
postToParent("dcs:blog-content-click", { textKey: "event-content" });
|
|
345
|
+
});
|
|
346
|
+
eventContentEl.addEventListener("dblclick", (e) => {
|
|
347
|
+
const target = e.target;
|
|
348
|
+
if (target.closest(TEXT_KEY_SELECTOR)) return;
|
|
349
|
+
e.preventDefault();
|
|
350
|
+
e.stopPropagation();
|
|
351
|
+
postToParent("dcs:blog-content-click", { textKey: "event-content" });
|
|
352
|
+
});
|
|
353
|
+
postToParent("dcs:blog-content-ready", { content: eventContentEl.innerHTML });
|
|
354
|
+
}
|
|
355
|
+
}
|
|
328
356
|
if (!blogMetadataInitialized) {
|
|
329
357
|
const titleEl = document.querySelector("[data-blog-title]");
|
|
330
358
|
const summaryEl = document.querySelector("[data-blog-summary]");
|
|
@@ -1027,21 +1055,23 @@ function handleMessage(event) {
|
|
|
1027
1055
|
break;
|
|
1028
1056
|
case "dcs:update-blog-content":
|
|
1029
1057
|
if (data && typeof data === "object" && "content" in data) {
|
|
1030
|
-
const
|
|
1031
|
-
if (
|
|
1032
|
-
|
|
1058
|
+
const contentEl = document.querySelector(BLOG_CONTENT_SELECTOR) ?? document.querySelector(EVENT_CONTENT_SELECTOR);
|
|
1059
|
+
if (contentEl) {
|
|
1060
|
+
contentEl.innerHTML = data.content;
|
|
1033
1061
|
scheduleRediscovery();
|
|
1034
1062
|
}
|
|
1035
1063
|
}
|
|
1036
1064
|
break;
|
|
1037
1065
|
case "dcs:update-reviews":
|
|
1038
|
-
if (data && typeof data === "object" && "key" in data
|
|
1066
|
+
if (data && typeof data === "object" && "key" in data) {
|
|
1039
1067
|
const d = data;
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1068
|
+
globalThis.dispatchEvent(new CustomEvent("dcs:reviews-updated", {
|
|
1069
|
+
detail: {
|
|
1070
|
+
key: d.key,
|
|
1071
|
+
reviews: Array.isArray(d.reviews) ? d.reviews : null
|
|
1072
|
+
}
|
|
1073
|
+
}));
|
|
1074
|
+
scheduleRediscovery();
|
|
1045
1075
|
}
|
|
1046
1076
|
break;
|
|
1047
1077
|
}
|
|
@@ -1124,7 +1154,8 @@ function initEditorBridge() {
|
|
|
1124
1154
|
const reviewKeys = discoverReviewKeys();
|
|
1125
1155
|
const managedFormIds = discoverManagedFormIds();
|
|
1126
1156
|
const contentHeight = measureContentHeight(sections);
|
|
1127
|
-
const blogEl = document.querySelector(
|
|
1157
|
+
const blogEl = document.querySelector(BLOG_CONTENT_SELECTOR);
|
|
1158
|
+
const eventEl = document.querySelector(EVENT_CONTENT_SELECTOR);
|
|
1128
1159
|
const blogTitleEl = document.querySelector("[data-blog-title]");
|
|
1129
1160
|
const blogSummaryEl = document.querySelector("[data-blog-summary]");
|
|
1130
1161
|
postToParent("dcs:ready", {
|
|
@@ -1133,6 +1164,8 @@ function initEditorBridge() {
|
|
|
1133
1164
|
contentHeight,
|
|
1134
1165
|
hasBlogContent: !!blogEl,
|
|
1135
1166
|
blogContentHtml: blogEl ? blogEl.innerHTML : null,
|
|
1167
|
+
hasEventContent: !!eventEl,
|
|
1168
|
+
eventContentHtml: eventEl ? eventEl.innerHTML : null,
|
|
1136
1169
|
blogTitle: blogTitleEl ? blogTitleEl.textContent?.trim() ?? null : null,
|
|
1137
1170
|
blogSummary: blogSummaryEl ? blogSummaryEl.textContent?.trim() ?? null : null,
|
|
1138
1171
|
hasManagedForms: managedFormIds.length > 0,
|