@bettercms-ai/astro 0.14.5 → 0.15.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.
- package/components/BcmsBlocks.astro +43 -3
- package/package.json +5 -5
|
@@ -32,7 +32,7 @@ import type { DeliveryComponent, DeliveryForm } from "@bettercms-ai/sdk";
|
|
|
32
32
|
import { applyOverrides } from "@bettercms-ai/ui/resolve";
|
|
33
33
|
import { addressFor } from "../dist/bindings.js";
|
|
34
34
|
import { portableTextToHtml } from "@bettercms-ai/richtext/html";
|
|
35
|
-
import { readStructuredContent } from "@bettercms-ai/richtext";
|
|
35
|
+
import { embeddedComponentBlock, readStructuredContent } from "@bettercms-ai/richtext";
|
|
36
36
|
import { headingParts, richtextHeadingLevel } from "@bettercms-ai/richtext/heading";
|
|
37
37
|
import BcmsImage from "./BcmsImage.astro";
|
|
38
38
|
import BcmsForm from "./BcmsForm.astro";
|
|
@@ -60,6 +60,11 @@ interface Props {
|
|
|
60
60
|
* does not declare gets no address, rather than one that binds to no field.
|
|
61
61
|
* Internal; do not pass. */
|
|
62
62
|
fieldAddresses?: Map<string, string>;
|
|
63
|
+
/** Set when `blocks` is a component EMBEDDED in rich text: the address of that rich-text field,
|
|
64
|
+
* which names the instance (`<owner>#<_key>`), or null when the field has none — so the
|
|
65
|
+
* embed gets no addresses rather than a bare `_key` that resolves to nothing.
|
|
66
|
+
* Internal; do not pass. */
|
|
67
|
+
owner?: string | null;
|
|
63
68
|
class?: string;
|
|
64
69
|
}
|
|
65
70
|
|
|
@@ -76,6 +81,7 @@ const {
|
|
|
76
81
|
nested = false,
|
|
77
82
|
containerAttrs = {},
|
|
78
83
|
fieldAddresses,
|
|
84
|
+
owner,
|
|
79
85
|
class: className,
|
|
80
86
|
} = Astro.props;
|
|
81
87
|
/** What `bind`/`bindProps` read. One object, so every call site cannot disagree. */
|
|
@@ -125,6 +131,30 @@ function resolveComponent(
|
|
|
125
131
|
return { id: def.id, blocks: resolved, props: def.props ?? [] };
|
|
126
132
|
}
|
|
127
133
|
|
|
134
|
+
/** What an embedded component's slot looks like in the rendered html — content cannot spell a comment. */
|
|
135
|
+
const EMBED_SLOT = "<!--bcms-embed-->";
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Portable Text → the html around each embedded component, plus those components as ordinary
|
|
139
|
+
* `component` blocks. An Astro component cannot render to a string inside `portableTextToHtml`,
|
|
140
|
+
* so the hook leaves a slot and the template mounts each embed there through `<Astro.self>` —
|
|
141
|
+
* the same `component` case (resolver, cycle guard, identity attributes) a placed instance uses.
|
|
142
|
+
* `chunks` has one more entry than `embeds`.
|
|
143
|
+
*/
|
|
144
|
+
function richTextParts(structured: NonNullable<ReturnType<typeof readStructuredContent>>, base: string | null) {
|
|
145
|
+
const embeds: ContentBlock[] = [];
|
|
146
|
+
const html = portableTextToHtml(structured, {
|
|
147
|
+
...(base ? { bindingBase: base } : {}),
|
|
148
|
+
renderBlock: (v) => {
|
|
149
|
+
const block = embeddedComponentBlock(v);
|
|
150
|
+
if (!block) return undefined;
|
|
151
|
+
embeds.push(block as ContentBlock);
|
|
152
|
+
return EMBED_SLOT;
|
|
153
|
+
},
|
|
154
|
+
});
|
|
155
|
+
return { chunks: html.split(EMBED_SLOT), embeds };
|
|
156
|
+
}
|
|
157
|
+
|
|
128
158
|
/**
|
|
129
159
|
* Depth-limited scan for a block type anywhere in the tree, components included, so the
|
|
130
160
|
* slider/tabs runtimes only ship on pages that use them — the same gating render-page.ts
|
|
@@ -247,7 +277,17 @@ const pass = { forms, components, turnstileSiteKey, nested: true, fieldAddresses
|
|
|
247
277
|
const structured = readStructuredContent(p);
|
|
248
278
|
if (structured) {
|
|
249
279
|
const base = addressFor(block.id, "html", bctx);
|
|
250
|
-
|
|
280
|
+
const { chunks, embeds } = richTextParts(structured, base);
|
|
281
|
+
return (
|
|
282
|
+
<div class="bcms-text" {...blockAttrs}>
|
|
283
|
+
{chunks.map((html, i) => (
|
|
284
|
+
<Fragment>
|
|
285
|
+
<Fragment set:html={html} />
|
|
286
|
+
{embeds[i] && <Astro.self blocks={[embeds[i]]} depth={depth} seen={seen} {...pass} owner={base} />}
|
|
287
|
+
</Fragment>
|
|
288
|
+
))}
|
|
289
|
+
</div>
|
|
290
|
+
);
|
|
251
291
|
}
|
|
252
292
|
return <div class="bcms-text" {...bind(block.id, "html", "richtext", bctx)} {...blockAttrs} set:html={p.html ?? ""} />;
|
|
253
293
|
}
|
|
@@ -474,7 +514,7 @@ const pass = { forms, components, turnstileSiteKey, nested: true, fieldAddresses
|
|
|
474
514
|
"data-bcms-instance": block.id,
|
|
475
515
|
}}
|
|
476
516
|
{...pass}
|
|
477
|
-
fieldAddresses={componentInstanceAddresses(block.id, resolved.props)}
|
|
517
|
+
fieldAddresses={owner === null ? new Map() : componentInstanceAddresses(block.id, resolved.props, owner)}
|
|
478
518
|
/>
|
|
479
519
|
);
|
|
480
520
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bettercms-ai/astro",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "The BetterCMS adapter for Astro — a `bettercms()` integration, a `bettercms:client` virtual module, typed content loaders, draft preview, and native .astro rendering components.",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -54,11 +54,11 @@
|
|
|
54
54
|
},
|
|
55
55
|
"dependencies": {
|
|
56
56
|
"@bettercms-ai/image-url": "^0.3.0",
|
|
57
|
-
"@bettercms-ai/next": "^0.
|
|
58
|
-
"@bettercms-ai/richtext": "^0.
|
|
57
|
+
"@bettercms-ai/next": "^0.14.0",
|
|
58
|
+
"@bettercms-ai/richtext": "^0.7.0",
|
|
59
59
|
"@bettercms-ai/sdk": "^1.22.0",
|
|
60
|
-
"@bettercms-ai/types": "^1.
|
|
61
|
-
"@bettercms-ai/ui": "^0.
|
|
60
|
+
"@bettercms-ai/types": "^1.20.0",
|
|
61
|
+
"@bettercms-ai/ui": "^0.9.0",
|
|
62
62
|
"libphonenumber-js": "^1.13.13"
|
|
63
63
|
},
|
|
64
64
|
"peerDependencies": {
|