@websline/cms-view-utils 1.5.3 → 1.7.0

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.
@@ -1 +1 @@
1
- (()=>{var r={BLOCK_ADD:"BLOCK_ADD",BLOCK_DELETE:"BLOCK_DELETE",BLOCK_EDIT:"BLOCK_EDIT",BLOCK_POSITION:"BLOCK_POSITION",BLOCK_SHOW:"BLOCK_SHOW",BLOCK_HIDE:"BLOCK_HIDE",PATH_CHANGED:"PATH_CHANGED",PATH_ON_LOAD:"PATH_ON_LOAD",IFRAME_DOM_LOADED:"IFRAME_DOM_LOADED",CURRENT_BLOCK_EDIT:"CURRENT_BLOCK_EDIT",SCROLL_TO_BLOCK:"SCROLL_TO_BLOCK"},_=(i,a={})=>{i&&window.parent!==window&&window.parent.postMessage({type:i,payload:a},"*")};(function(){if(window.parent===window||window.__PREVIEW_BRIDGE_INITIALIZED__)return;window.__PREVIEW_BRIDGE_INITIALIZED__=!0;let i=t=>t.metaKey||t.ctrlKey||t.shiftKey,a=t=>{let n=t.target.closest("a[href]");if(!n||n.target==="_blank"||i(t))return;let o=new URL(n.href,window.location.origin);o.origin===window.location.origin&&(t.preventDefault(),t.stopPropagation(),_(r.PATH_CHANGED,{path:o.pathname}))};document.addEventListener("click",a,!0),_(r.PATH_ON_LOAD,{path:window.location.pathname}),window.addEventListener("load",()=>{_(r.IFRAME_DOM_LOADED)});let L=t=>{let n=`[data-cms-block-id="${t}"]`,o=0,e=10,O=setInterval(()=>{let E=document.querySelector(n);E&&(E.scrollIntoView({behavior:"smooth",block:"start"}),clearInterval(O)),++o>=e&&clearInterval(O)},100)};window.addEventListener("message",t=>{let{type:n,payload:o}=t.data||{};if(n&&n===r.SCROLL_TO_BLOCK){let{draftBlockUuid:e}=o||{};if(!e)return;L(e)}})})();})();
1
+ (()=>{var r={BLOCK_ADD:"BLOCK_ADD",BLOCK_COMMENT:"BLOCK_COMMENT",BLOCK_DELETE:"BLOCK_DELETE",BLOCK_EDIT:"BLOCK_EDIT",BLOCK_POSITION:"BLOCK_POSITION",BLOCK_SHOW:"BLOCK_SHOW",BLOCK_HIDE:"BLOCK_HIDE",PATH_CHANGED:"PATH_CHANGED",PATH_ON_LOAD:"PATH_ON_LOAD",IFRAME_DOM_LOADED:"IFRAME_DOM_LOADED",COMMENT_COUNTS:"COMMENT_COUNTS",CURRENT_BLOCK_EDIT:"CURRENT_BLOCK_EDIT",SCROLL_TO_BLOCK:"SCROLL_TO_BLOCK"},_=(i,O={})=>{i&&window.parent!==window&&window.parent.postMessage({type:i,payload:O},"*")};(function(){if(window.parent===window||window.__PREVIEW_BRIDGE_INITIALIZED__)return;window.__PREVIEW_BRIDGE_INITIALIZED__=!0;let i=t=>t.metaKey||t.ctrlKey||t.shiftKey,O=t=>{let n=t.target.closest("a[href]");if(!n||n.target==="_blank"||i(t))return;let o=new URL(n.href,window.location.origin);o.origin===window.location.origin&&(t.preventDefault(),t.stopPropagation(),_(r.PATH_CHANGED,{path:o.pathname}))};document.addEventListener("click",O,!0),_(r.PATH_ON_LOAD,{path:window.location.pathname}),window.addEventListener("load",()=>{_(r.IFRAME_DOM_LOADED)});let L=t=>{let n=`[data-cms-block-id="${t}"]`,o=0,e=10,a=setInterval(()=>{let E=document.querySelector(n);E&&(E.scrollIntoView({behavior:"smooth",block:"start"}),clearInterval(a)),++o>=e&&clearInterval(a)},100)};window.addEventListener("message",t=>{let{type:n,payload:o}=t.data||{};if(n&&n===r.SCROLL_TO_BLOCK){let{draftBlockUuid:e}=o||{};if(!e)return;L(e)}})})();})();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@websline/cms-view-utils",
3
- "version": "1.5.3",
3
+ "version": "1.7.0",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "src",
@@ -21,10 +21,10 @@
21
21
  "devDependencies": {
22
22
  "@eslint/compat": "^2.1.0",
23
23
  "@eslint/js": "^10.0.1",
24
- "eslint": "^10.7.0",
24
+ "eslint": "^10.8.1",
25
25
  "eslint-config-prettier": "^10.1.8",
26
26
  "eslint-plugin-svelte": "^3.22.0",
27
- "globals": "^17.7.0",
27
+ "globals": "^17.10.0",
28
28
  "prettier": "^3.9.6",
29
29
  "prettier-plugin-svelte": "^4.1.1",
30
30
  "prettier-plugin-tailwindcss": "^0.8.1",
@@ -1,4 +1,4 @@
1
- const fetchFaqs = async ({ excludeId, limit, locale, tags }) => {
1
+ const fetchFaqs = async ({ excludeId, limit, locale, tags, groups }) => {
2
2
  const params = new URLSearchParams();
3
3
 
4
4
  if (excludeId != null && excludeId !== "") {
@@ -17,6 +17,14 @@ const fetchFaqs = async ({ excludeId, limit, locale, tags }) => {
17
17
  }
18
18
  }
19
19
 
20
+ if (Array.isArray(groups)) {
21
+ for (const group of groups) {
22
+ if (group != null && group !== "") {
23
+ params.append("groups", group);
24
+ }
25
+ }
26
+ }
27
+
20
28
  const query = params.toString();
21
29
  const url = `/api/cms/api/public/abm-faqs/${locale}${query ? `?${query}` : ""}`;
22
30
 
@@ -49,6 +49,12 @@
49
49
  if (autoTranslate) return;
50
50
  notifyCMS(EVENTS.BLOCK_ADD, { position });
51
51
  break;
52
+ case CMS_ACTIONS.comment:
53
+ notifyCMS(EVENTS.BLOCK_COMMENT, {
54
+ blockIdentifier: block.identifier,
55
+ draftBlockUuid: block.uuid,
56
+ });
57
+ break;
52
58
  case CMS_ACTIONS.delete:
53
59
  if (isSynced) return;
54
60
  notifyCMS(EVENTS.BLOCK_DELETE, {
@@ -126,20 +132,36 @@
126
132
  isHovered = true;
127
133
  }, 50);
128
134
 
135
+ let commentCount = $state(0);
136
+ // Off for users without the comment read permission; the CMS says so in the same
137
+ // message as the counts.
138
+ let commentsEnabled = $state(true);
139
+
129
140
  const handler = (event) => {
130
141
  const { type, payload } = event.data || {};
131
- if (type !== EVENTS.CURRENT_BLOCK_EDIT) return;
132
142
 
133
- const { draftBlockUuid } = payload || {};
143
+ if (type === EVENTS.CURRENT_BLOCK_EDIT) {
144
+ isActive = payload?.draftBlockUuid === block.uuid;
145
+ return;
146
+ }
134
147
 
135
- isActive = draftBlockUuid === block.uuid;
148
+ // The counts arrive from the CMS: this iframe has no session for its API.
149
+ if (type === EVENTS.COMMENT_COUNTS) {
150
+ commentCount = payload?.counts?.[block.uuid] ?? 0;
151
+ commentsEnabled = payload?.enabled !== false;
152
+ }
136
153
  };
137
154
 
138
155
  let isOutside = $derived(calcBarPosition === "outside");
139
156
 
140
157
  let containerShape = $derived.by(() => {
141
- if (isOutside) return "absolute rounded-t-lg -top-8";
142
- return `rounded-b-lg top-1.5 ${isSticky === "full" ? "fixed" : "absolute"}`;
158
+ // Both orientations have to read as the same bar, and the block's 6px frame
159
+ // counts: pointing up the bar overlaps it by 4px, pointing down it starts
160
+ // below it. 40px up (offset by the same 4px) and 36px down therefore end up
161
+ // equally tall — 42px including the line — and the 4px bottom padding mirrors
162
+ // the icon inset: 4px towards the line, 8px towards the rounded edge.
163
+ if (isOutside) return "absolute h-10 rounded-t-lg -top-9";
164
+ return `h-9 rounded-b-lg pb-1 top-1.5 ${isSticky === "full" ? "fixed" : "absolute"}`;
143
165
  });
144
166
 
145
167
  let cornerEdges = $derived(
@@ -150,13 +172,16 @@
150
172
  "after:top-0 after:-right-4 after:shadow-[-6px_-6px_0_0_var(--block-color)]",
151
173
  );
152
174
 
175
+ // 34px from the icon in both directions, which lands 2px off the bar either way.
153
176
  let tooltipClasses = $derived(
154
- `tooltip absolute ${isOutside ? "-top-8" : "top-8"} bg-[var(--block-color)] leading-none text-xs p-1.5 rounded left-1/2 -translate-x-1/2 whitespace-nowrap transition-opacity duration-150`,
177
+ `tooltip absolute ${isOutside ? "-top-8.5" : "top-8.5"} bg-[var(--block-color)] leading-none text-xs p-1.5 rounded left-1/2 -translate-x-1/2 whitespace-nowrap transition-opacity duration-150`,
155
178
  );
156
179
 
157
180
  const labels = {
158
181
  de: {
159
182
  add: "Hinzufügen",
183
+ comment: "Kommentare",
184
+ commentAria: "Kommentare des Blocks",
160
185
  position: "Anordnen",
161
186
  delete: "Löschen",
162
187
  edit: "Bearbeiten",
@@ -167,6 +192,8 @@
167
192
  },
168
193
  en: {
169
194
  add: "Add",
195
+ comment: "Comments",
196
+ commentAria: "Comments of the block",
170
197
  position: "Arrange",
171
198
  delete: "Delete",
172
199
  edit: "Edit",
@@ -210,14 +237,14 @@
210
237
  {/if}
211
238
 
212
239
  <div
213
- class="left-1/2 z-9999 flex h-9 -translate-x-1/2 items-center justify-center gap-1.5 px-3 transition-opacity duration-100 before:absolute before:h-4 before:w-4 before:rounded-full before:bg-transparent before:content-[''] after:absolute after:h-4 after:w-4 after:rounded-full after:bg-transparent after:content-[''] {containerShape} {cornerEdges}"
240
+ class="left-1/2 z-9999 flex -translate-x-1/2 items-center justify-center gap-1.5 px-3 transition-opacity duration-100 before:absolute before:h-4 before:w-4 before:rounded-full before:bg-transparent before:content-[''] after:absolute after:h-4 after:w-4 after:rounded-full after:bg-transparent after:content-[''] {containerShape} {cornerEdges}"
214
241
  style="background: var(--block-color); color: var(--block-text);"
215
242
  class:opacity-100={toolbarVisible}
216
243
  class:opacity-0={!toolbarVisible}>
217
244
  <button
218
245
  aria-label={t.addAria}
219
246
  class={[
220
- "cursor-pointer",
247
+ "relative cursor-pointer",
221
248
  (autoTranslate || isSynced) && "pointer-events-none opacity-40",
222
249
  ]}
223
250
  onmouseenter={() => showTooltip("add")}
@@ -322,7 +349,7 @@
322
349
  <button
323
350
  aria-label={t.positionAria}
324
351
  class={[
325
- "cursor-pointer",
352
+ "relative cursor-pointer",
326
353
  (autoTranslate || isSynced) && "pointer-events-none opacity-40",
327
354
  ]}
328
355
  onmouseenter={() => showTooltip("position")}
@@ -352,7 +379,10 @@
352
379
 
353
380
  <button
354
381
  aria-label={t.deleteAria}
355
- class={["cursor-pointer", isSynced && "pointer-events-none opacity-40"]}
382
+ class={[
383
+ "relative cursor-pointer",
384
+ isSynced && "pointer-events-none opacity-40",
385
+ ]}
356
386
  onmouseenter={() => showTooltip("delete")}
357
387
  onmouseleave={hideTooltip}
358
388
  onclick={(event) => handleClick(event, CMS_ACTIONS.delete)}>
@@ -378,6 +408,39 @@
378
408
  </div>
379
409
  </button>
380
410
 
411
+ {#if commentsEnabled}
412
+ <button
413
+ aria-label={t.commentAria}
414
+ class="relative flex cursor-pointer items-center gap-1"
415
+ onmouseenter={() => showTooltip("comment")}
416
+ onmouseleave={hideTooltip}
417
+ onclick={(event) => handleClick(event, CMS_ACTIONS.comment)}>
418
+ <svg
419
+ width="24"
420
+ height="24"
421
+ viewBox="0 0 24 24"
422
+ fill="none"
423
+ xmlns="http://www.w3.org/2000/svg">
424
+ <path
425
+ d="M6.99997 9H16M6.99997 13H16M6.99997 17H12M22 12C22 17.5229 17.5229 22 12 22C9.01327 22 2 22 2 22C2 22 2 14.5361 2 12C2 6.47715 6.47717 2 12 2C17.5229 2 22 6.47715 22 12Z"
426
+ stroke="var(--block-text)"
427
+ stroke-width="1.5"
428
+ stroke-linecap="round"
429
+ stroke-linejoin="round" />
430
+ </svg>
431
+ {#if commentCount > 0}
432
+ <span class="text-xs leading-none tabular-nums">{commentCount}</span>
433
+ {/if}
434
+ <div
435
+ class={tooltipClasses}
436
+ class:opacity-0={activeTooltip !== "comment"}
437
+ class:opacity-100={activeTooltip === "comment"}
438
+ class:pointer-events-none={activeTooltip !== "comment"}>
439
+ {t.comment}
440
+ </div>
441
+ </button>
442
+ {/if}
443
+
381
444
  <button
382
445
  aria-label={t.editAria}
383
446
  class="relative cursor-pointer"
@@ -1,5 +1,6 @@
1
1
  const CMS_ACTIONS = {
2
2
  add: "add-block",
3
+ comment: "comment-block",
3
4
  copy: "copy-block",
4
5
  delete: "delete-block",
5
6
  edit: "edit-block",
@@ -1,5 +1,6 @@
1
1
  const EVENTS = {
2
2
  BLOCK_ADD: "BLOCK_ADD",
3
+ BLOCK_COMMENT: "BLOCK_COMMENT",
3
4
  BLOCK_DELETE: "BLOCK_DELETE",
4
5
  BLOCK_EDIT: "BLOCK_EDIT",
5
6
  BLOCK_POSITION: "BLOCK_POSITION",
@@ -8,6 +9,7 @@ const EVENTS = {
8
9
  PATH_CHANGED: "PATH_CHANGED",
9
10
  PATH_ON_LOAD: "PATH_ON_LOAD",
10
11
  IFRAME_DOM_LOADED: "IFRAME_DOM_LOADED",
12
+ COMMENT_COUNTS: "COMMENT_COUNTS",
11
13
  CURRENT_BLOCK_EDIT: "CURRENT_BLOCK_EDIT",
12
14
  SCROLL_TO_BLOCK: "SCROLL_TO_BLOCK",
13
15
  };