@bettercms-ai/astro 0.27.0 → 0.27.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/components/BcmsBlocks.astro +40 -19
- package/package.json +6 -6
|
@@ -26,13 +26,13 @@
|
|
|
26
26
|
* actually contains that block (mirrors render-page.ts's `ctx.flags` gating).
|
|
27
27
|
*/
|
|
28
28
|
import type { ContentBlock } from "@bettercms-ai/types";
|
|
29
|
-
import { RESPONSIVE_CSS, blockStyleToCss, hideOnAttr, usesHideOn, visibleBlocks, windowState, blockStyleToInnerCss, splitButtonStyle, buttonIconOf, buttonIconSvg, buttonVariantOf, componentInstanceAddresses, headingPaintCss, withButtonLookProps } from "@bettercms-ai/types";
|
|
29
|
+
import { RESPONSIVE_CSS, imageFrameCss, blockStyleToCss, hiddenEverywhere, hideOnAttr, usesHideOn, visibleBlocks, windowState, blockStyleToInnerCss, splitButtonStyle, buttonIconOf, buttonIconSvg, buttonVariantOf, componentInstanceAddresses, headingPaintCss, withButtonLookProps } from "@bettercms-ai/types";
|
|
30
30
|
import { bind, bindProps } from "../dist/bindings.js";
|
|
31
31
|
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 {
|
|
35
|
+
import { embeddedComponentRender, 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";
|
|
@@ -69,8 +69,9 @@ interface Props {
|
|
|
69
69
|
/** The instant block windows (`block.window`) are read against. Defaults to the render time:
|
|
70
70
|
* the build for a static page, the request for a server-rendered one. */
|
|
71
71
|
now?: Date;
|
|
72
|
-
/** Draw the blocks outside their window too, marked `data-bcms-window="pending|expired"`,
|
|
73
|
-
*
|
|
72
|
+
/** Draw the blocks outside their window too, marked `data-bcms-window="pending|expired"`, and the
|
|
73
|
+
* blocks hidden on every breakpoint, marked `data-bcms-hidden="all"`, for a draft preview. Off,
|
|
74
|
+
* neither is rendered, at any depth. */
|
|
74
75
|
showScheduled?: boolean;
|
|
75
76
|
}
|
|
76
77
|
|
|
@@ -174,13 +175,18 @@ const EMBED_SLOT = "<!--bcms-embed-->";
|
|
|
174
175
|
* `chunks` has one more entry than `embeds`.
|
|
175
176
|
*/
|
|
176
177
|
function richTextParts(structured: NonNullable<ReturnType<typeof readStructuredContent>>, base: string | null) {
|
|
177
|
-
|
|
178
|
+
/** A linked embed is its `component` block; a separate copy, its own frozen blocks (it never looks the component up). */
|
|
179
|
+
const embeds: ({ kind: "linked"; block: ContentBlock } | { kind: "copy"; componentId: string; id: string; blocks: ContentBlock[] })[] = [];
|
|
178
180
|
const html = portableTextToHtml(structured, {
|
|
179
181
|
...(base ? { bindingBase: base } : {}),
|
|
180
182
|
renderBlock: (v) => {
|
|
181
|
-
const
|
|
182
|
-
if (!
|
|
183
|
-
embeds.push(
|
|
183
|
+
const embed = embeddedComponentRender(v);
|
|
184
|
+
if (!embed) return undefined;
|
|
185
|
+
embeds.push(
|
|
186
|
+
embed.kind === "linked"
|
|
187
|
+
? { kind: "linked", block: embed.block as ContentBlock }
|
|
188
|
+
: { kind: "copy", componentId: embed.copy.componentId, id: embed.copy.id, blocks: embed.copy.blocks as ContentBlock[] },
|
|
189
|
+
);
|
|
184
190
|
return EMBED_SLOT;
|
|
185
191
|
},
|
|
186
192
|
});
|
|
@@ -270,7 +276,9 @@ const pass = { forms, components, turnstileSiteKey, nested: true, fieldAddresses
|
|
|
270
276
|
*/
|
|
271
277
|
/** `showScheduled` only: which side of its window a drawn block is on. Rides the block's root, like the id. */
|
|
272
278
|
const scheduled = showScheduled && windowState(block, at) !== "live" ? windowState(block, at) : undefined;
|
|
273
|
-
|
|
279
|
+
/** `showScheduled` only: hidden on every breakpoint ("Hide on this page"), drawn so its author can find it. */
|
|
280
|
+
const hiddenAll = showScheduled && hiddenEverywhere(block) ? "all" : undefined;
|
|
281
|
+
const blockAttrs = style || inner || hide ? {} : nested ? { "data-bcms-window": scheduled, "data-bcms-hidden": hiddenAll } : { "data-bcms-block": block.id, "data-bcms-window": scheduled, "data-bcms-hidden": hiddenAll };
|
|
274
282
|
const node = (() => {
|
|
275
283
|
switch (block.type) {
|
|
276
284
|
case "heading": {
|
|
@@ -321,24 +329,37 @@ const pass = { forms, components, turnstileSiteKey, nested: true, fieldAddresses
|
|
|
321
329
|
const { chunks, embeds } = richTextParts(structured, base);
|
|
322
330
|
return (
|
|
323
331
|
<div class="bcms-text" {...blockAttrs}>
|
|
324
|
-
{chunks.map((html, i) =>
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
332
|
+
{chunks.map((html, i) => {
|
|
333
|
+
const e = embeds[i];
|
|
334
|
+
return (
|
|
335
|
+
<Fragment>
|
|
336
|
+
<Fragment set:html={html} />
|
|
337
|
+
{e?.kind === "linked" && <Astro.self blocks={[e.block]} depth={depth} seen={seen} {...pass} owner={base} />}
|
|
338
|
+
{e?.kind === "copy" && (
|
|
339
|
+
<div class="bcms-block bcms-block--component" data-bcms-component-copy={e.componentId} data-bcms-instance={e.id}>
|
|
340
|
+
<Astro.self blocks={e.blocks} depth={depth + 1} seen={seen} {...pass} fieldAddresses={new Map()} />
|
|
341
|
+
</div>
|
|
342
|
+
)}
|
|
343
|
+
</Fragment>
|
|
344
|
+
);
|
|
345
|
+
})}
|
|
330
346
|
</div>
|
|
331
347
|
);
|
|
332
348
|
}
|
|
333
349
|
return <div class="bcms-text" {...bind(block.id, "html", "richtext", bctx)} {...blockAttrs} set:html={p.html ?? ""} />;
|
|
334
350
|
}
|
|
335
351
|
case "image": {
|
|
352
|
+
// An unset src renders nothing, wrapper included, as on the hosted lane (render-page D3).
|
|
353
|
+
if (!String(p.src ?? "").trim()) return null;
|
|
336
354
|
// Matches the Next adapter: the image block is click-to-select on the Visual Editor
|
|
337
355
|
// canvas like heading/text/button, rather than dock-only. `kind="image"` means select
|
|
338
356
|
// + live src reflection, never contentEditable.
|
|
339
357
|
// `alt` rides the props lane: this element already carries the src binding and one
|
|
340
358
|
// element cannot hold two addresses.
|
|
359
|
+
// V2's media frame (`style.aspect`), drawn as the hosted lane draws it.
|
|
360
|
+
const frame = imageFrameCss(block.style);
|
|
341
361
|
const imgBind = {
|
|
362
|
+
...(frame ? { style: [`width:${frame.width}`, `aspect-ratio:${frame.aspectRatio}`, `object-fit:${frame.objectFit}`, frame.borderRadius ? `border-radius:${frame.borderRadius}` : ""].filter(Boolean).join(";") } : {}),
|
|
342
363
|
...bind(block.id, "src", "image", bctx),
|
|
343
364
|
...bindProps(block.id, [{ prop: "alt", kind: "text", attr: "alt", present: p.alt != null }], bctx),
|
|
344
365
|
};
|
|
@@ -599,10 +620,10 @@ const pass = { forms, components, turnstileSiteKey, nested: true, fieldAddresses
|
|
|
599
620
|
* box is the root and carries the marker instead.
|
|
600
621
|
*/
|
|
601
622
|
const stamp = nested ? undefined : block.id;
|
|
602
|
-
if (style && inner) return <div style={style} data-bcms-hide={hide} data-bcms-block={stamp} data-bcms-window={scheduled}><div class="bcms-block-inner" style={inner}>{node}</div></div>;
|
|
603
|
-
if (hide) return <div style={style} data-bcms-hide={hide} data-bcms-block={stamp} data-bcms-window={scheduled}>{inner ? <div class="bcms-block-inner" style={inner}>{node}</div> : node}</div>;
|
|
604
|
-
if (inner) return <div class="bcms-block-inner" style={inner} data-bcms-block={stamp} data-bcms-window={scheduled}>{node}</div>;
|
|
605
|
-
if (style) return <div style={style} data-bcms-block={stamp} data-bcms-window={scheduled}>{node}</div>;
|
|
623
|
+
if (style && inner) return <div style={style} data-bcms-hide={hide} data-bcms-block={stamp} data-bcms-window={scheduled} data-bcms-hidden={hiddenAll}><div class="bcms-block-inner" style={inner}>{node}</div></div>;
|
|
624
|
+
if (hide) return <div style={style} data-bcms-hide={hide} data-bcms-block={stamp} data-bcms-window={scheduled} data-bcms-hidden={hiddenAll}>{inner ? <div class="bcms-block-inner" style={inner}>{node}</div> : node}</div>;
|
|
625
|
+
if (inner) return <div class="bcms-block-inner" style={inner} data-bcms-block={stamp} data-bcms-window={scheduled} data-bcms-hidden={hiddenAll}>{node}</div>;
|
|
626
|
+
if (style) return <div style={style} data-bcms-block={stamp} data-bcms-window={scheduled} data-bcms-hidden={hiddenAll}>{node}</div>;
|
|
606
627
|
return node;
|
|
607
628
|
})}
|
|
608
629
|
</div>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bettercms-ai/astro",
|
|
3
|
-
"version": "0.27.
|
|
3
|
+
"version": "0.27.2",
|
|
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",
|
|
@@ -61,11 +61,11 @@
|
|
|
61
61
|
},
|
|
62
62
|
"dependencies": {
|
|
63
63
|
"@bettercms-ai/image-url": "^0.4.0",
|
|
64
|
-
"@bettercms-ai/next": "^0.26.
|
|
65
|
-
"@bettercms-ai/richtext": "^0.
|
|
66
|
-
"@bettercms-ai/sdk": "^1.
|
|
67
|
-
"@bettercms-ai/types": "^1.
|
|
68
|
-
"@bettercms-ai/ui": "^0.
|
|
64
|
+
"@bettercms-ai/next": "^0.26.2",
|
|
65
|
+
"@bettercms-ai/richtext": "^0.10.0",
|
|
66
|
+
"@bettercms-ai/sdk": "^1.31.0",
|
|
67
|
+
"@bettercms-ai/types": "^1.47.0",
|
|
68
|
+
"@bettercms-ai/ui": "^0.18.0",
|
|
69
69
|
"libphonenumber-js": "^1.13.13"
|
|
70
70
|
},
|
|
71
71
|
"peerDependencies": {
|