@websline/cms-view-utils 1.6.0 → 1.7.1
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/previewBridge.global.js +1 -1
- package/package.json +4 -4
- package/src/client/fetchBoard.js +8 -0
- package/src/client/fetchBoards.js +7 -0
- package/src/client/fetchCollections.js +10 -0
- package/src/client/fetchFaqs.js +11 -0
- package/src/client/fetchPageTeasers.js +63 -0
- package/src/client/fetchSimilarPackages.js +9 -0
- package/src/client/fetchSimilarRooms.js +9 -0
- package/src/components/EditableBlock.svelte +73 -10
- package/src/editor/cmsActions.js +1 -0
- package/src/editor/notify.js +2 -0
- package/src/index.js +1 -0
|
@@ -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,
|
|
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.
|
|
3
|
+
"version": "1.7.1",
|
|
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.8.
|
|
24
|
+
"eslint": "^10.8.1",
|
|
25
25
|
"eslint-config-prettier": "^10.1.8",
|
|
26
|
-
"eslint-plugin-svelte": "^3.
|
|
27
|
-
"globals": "^17.
|
|
26
|
+
"eslint-plugin-svelte": "^3.23.0",
|
|
27
|
+
"globals": "^17.11.0",
|
|
28
28
|
"prettier": "^3.9.6",
|
|
29
29
|
"prettier-plugin-svelte": "^4.1.1",
|
|
30
30
|
"prettier-plugin-tailwindcss": "^0.8.1",
|
package/src/client/fetchBoard.js
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fetch a single ABM board.
|
|
3
|
+
*
|
|
4
|
+
* @param {object} options
|
|
5
|
+
* @param {string} options.locale - Locale code, e.g. `"de"`.
|
|
6
|
+
* @param {string|number} options.boardId - ABM id of the board.
|
|
7
|
+
* @returns {Promise<object>} The board object.
|
|
8
|
+
*/
|
|
1
9
|
const fetchBoard = async ({ locale, boardId }) => {
|
|
2
10
|
const url = `/api/cms/api/public/abm-boards/${locale}/${boardId}`;
|
|
3
11
|
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fetch all ABM boards.
|
|
3
|
+
*
|
|
4
|
+
* @param {object} options
|
|
5
|
+
* @param {string} options.locale - Locale code, e.g. `"de"`.
|
|
6
|
+
* @returns {Promise<object[]>} The boards.
|
|
7
|
+
*/
|
|
1
8
|
const fetchBoards = async ({ locale }) => {
|
|
2
9
|
const url = `/api/cms/api/public/abm-boards/${locale}`;
|
|
3
10
|
|
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fetch the items of a collection, or a single item of it.
|
|
3
|
+
*
|
|
4
|
+
* @param {object} options
|
|
5
|
+
* @param {string} options.locale - Locale code, e.g. `"de"`.
|
|
6
|
+
* @param {string} options.identifier - Collection identifier as configured in the CMS, e.g. `"blog"`.
|
|
7
|
+
* @param {string} [options.itemUuid] - Return only this item, as a plain object instead of an array.
|
|
8
|
+
* @param {number} [options.limit] - Maximum number of items, counted from the start of the CMS order. Ignored together with `itemUuid`.
|
|
9
|
+
* @returns {Promise<object[]|object>} An array of items, or a single item when `itemUuid` is set.
|
|
10
|
+
*/
|
|
1
11
|
const fetchCollections = async ({ identifier, itemUuid, limit, locale }) => {
|
|
2
12
|
const params = new URLSearchParams();
|
|
3
13
|
|
package/src/client/fetchFaqs.js
CHANGED
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fetch ABM FAQs.
|
|
3
|
+
*
|
|
4
|
+
* @param {object} options
|
|
5
|
+
* @param {string} options.locale - Locale code, e.g. `"de"`.
|
|
6
|
+
* @param {string|number} [options.excludeId] - ABM FAQ id to leave out, e.g. the FAQ already shown on the page.
|
|
7
|
+
* @param {string[]} [options.tags] - Keep only FAQs carrying at least one of these tags (OR, not AND).
|
|
8
|
+
* @param {string[]} [options.groups] - Keep only FAQs belonging to one of these group ids.
|
|
9
|
+
* @param {number} [options.limit] - Maximum number of FAQs, applied after the filters.
|
|
10
|
+
* @returns {Promise<object[]>} The matching FAQs.
|
|
11
|
+
*/
|
|
1
12
|
const fetchFaqs = async ({ excludeId, limit, locale, tags, groups }) => {
|
|
2
13
|
const params = new URLSearchParams();
|
|
3
14
|
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fetch the teasers of the tenant's published pages, e.g. for a blog index or related-posts list.
|
|
3
|
+
*
|
|
4
|
+
* @param {object} options
|
|
5
|
+
* @param {string} options.locale - Locale code, e.g. `"de"`.
|
|
6
|
+
* @param {string} [options.excludePageUuid] - Page UUID to leave out, e.g. the page currently shown.
|
|
7
|
+
* @param {boolean} [options.isBlog] - `true` for blog pages only, `false` for non-blog pages only, omit for all.
|
|
8
|
+
* @param {string[]} [options.tags] - Keep only pages carrying at least one of these tag UUIDs (OR, not AND).
|
|
9
|
+
* @param {"newest"|"oldest"} [options.order] - Sort by publication date; defaults to `"newest"`.
|
|
10
|
+
* @param {number} [options.offset] - Number of teasers to skip, applied after all filters.
|
|
11
|
+
* @param {number} [options.limit] - Maximum number of teasers; omit for all — there is no default.
|
|
12
|
+
* @returns {Promise<object[]>} The teasers.
|
|
13
|
+
*/
|
|
14
|
+
const fetchPageTeasers = async ({
|
|
15
|
+
locale,
|
|
16
|
+
excludePageUuid,
|
|
17
|
+
isBlog,
|
|
18
|
+
tags,
|
|
19
|
+
order,
|
|
20
|
+
offset,
|
|
21
|
+
limit,
|
|
22
|
+
}) => {
|
|
23
|
+
const params = new URLSearchParams();
|
|
24
|
+
|
|
25
|
+
if (excludePageUuid != null && excludePageUuid !== "") {
|
|
26
|
+
params.set("excludePageUuid", excludePageUuid);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// The API accepts exactly `true` or `false` — anything else answers 422,
|
|
30
|
+
// so only real booleans are forwarded.
|
|
31
|
+
if (isBlog === true || isBlog === false) {
|
|
32
|
+
params.set("isBlog", isBlog);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (Array.isArray(tags)) {
|
|
36
|
+
for (const tag of tags) {
|
|
37
|
+
if (tag != null && tag !== "") {
|
|
38
|
+
params.append("tags", tag);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (order != null && order !== "") {
|
|
44
|
+
params.set("order", order);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (offset != null) {
|
|
48
|
+
params.set("offset", offset);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (limit != null) {
|
|
52
|
+
params.set("limit", limit);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const query = params.toString();
|
|
56
|
+
const url = `/api/cms/api/public/page-teasers/${locale}${query ? `?${query}` : ""}`;
|
|
57
|
+
|
|
58
|
+
const response = await fetch(url, { method: "GET" });
|
|
59
|
+
|
|
60
|
+
return response.json();
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export { fetchPageTeasers };
|
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fetch ABM packages, typically for a "similar packages" section.
|
|
3
|
+
*
|
|
4
|
+
* @param {object} options
|
|
5
|
+
* @param {string} options.locale - Locale code, e.g. `"de"`.
|
|
6
|
+
* @param {string|number} [options.excludeId] - ABM package id to leave out, e.g. the package currently shown.
|
|
7
|
+
* @param {number} [options.limit] - Maximum number of packages to return.
|
|
8
|
+
* @returns {Promise<object[]>} The packages.
|
|
9
|
+
*/
|
|
1
10
|
const fetchSimilarPackages = async ({ excludeId, limit, locale }) => {
|
|
2
11
|
const params = new URLSearchParams();
|
|
3
12
|
|
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fetch ABM rooms, typically for a "similar rooms" section.
|
|
3
|
+
*
|
|
4
|
+
* @param {object} options
|
|
5
|
+
* @param {string} options.locale - Locale code, e.g. `"de"`.
|
|
6
|
+
* @param {string|number} [options.excludeId] - ABM room id to leave out, e.g. the room currently shown.
|
|
7
|
+
* @param {number} [options.limit] - Maximum number of rooms to return.
|
|
8
|
+
* @returns {Promise<object[]>} The rooms.
|
|
9
|
+
*/
|
|
1
10
|
const fetchSimilarRooms = async ({ excludeId, limit, locale }) => {
|
|
2
11
|
const params = new URLSearchParams();
|
|
3
12
|
|
|
@@ -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
|
-
|
|
143
|
+
if (type === EVENTS.CURRENT_BLOCK_EDIT) {
|
|
144
|
+
isActive = payload?.draftBlockUuid === block.uuid;
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
134
147
|
|
|
135
|
-
|
|
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
|
-
|
|
142
|
-
|
|
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
|
|
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={[
|
|
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"
|
package/src/editor/cmsActions.js
CHANGED
package/src/editor/notify.js
CHANGED
|
@@ -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
|
};
|
package/src/index.js
CHANGED
|
@@ -5,6 +5,7 @@ export { fetchFaqs } from "./client/fetchFaqs.js";
|
|
|
5
5
|
export { fetchCollections } from "./client/fetchCollections.js";
|
|
6
6
|
export { fetchBoards } from "./client/fetchBoards.js";
|
|
7
7
|
export { fetchBoard } from "./client/fetchBoard.js";
|
|
8
|
+
export { fetchPageTeasers } from "./client/fetchPageTeasers.js";
|
|
8
9
|
|
|
9
10
|
export { default as AddBlockPlaceholder } from "./components/AddBlockPlaceholder.svelte";
|
|
10
11
|
export { default as BlockWrapper } from "./components/BlockWrapper.svelte";
|