@bettercms-ai/ui 0.22.0 → 0.23.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/README.md +2 -2
- package/dist/blocks.d.ts +10 -4
- package/dist/blocks.js +242 -32
- package/dist/blocks.js.map +1 -1
- package/dist/index.js +242 -32
- package/dist/index.js.map +1 -1
- package/dist/registry.js +244 -34
- package/dist/registry.js.map +1 -1
- package/dist/renderer.js +244 -34
- package/dist/renderer.js.map +1 -1
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @bettercms-ai/ui
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> Published on npm as `@bettercms-ai/ui`.
|
|
4
4
|
|
|
5
5
|
Shared page-builder block components for BetterCMS. Contains presentation-only React components for the 7 core block types: **Heading**, **Text**, **Image**, **Button**, **Spacer**, **Columns**, and **Video**.
|
|
6
6
|
|
|
@@ -93,4 +93,4 @@ cd packages/ui && npx shadcn@latest add -p ../../ button
|
|
|
93
93
|
|
|
94
94
|
## Note on npm publishing
|
|
95
95
|
|
|
96
|
-
|
|
96
|
+
Published to npm. Admin and web repos use their own local shadcn/ui copies for non-page-builder UI (Dialog, Sheet, Table, etc.); this package carries the page-builder blocks and renderer.
|
package/dist/blocks.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { a as Button, C as Columns, H as Heading, I as Image, S as Spacer, T as Text, V as Video } from './Video-vB2IlrdH.js';
|
|
2
2
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
3
|
-
import { SectionProps, ContentBlock, NavbarProps, FooterProps, SliderProps, TabsProps, ComponentProps, FormProps } from '@bettercms-ai/types';
|
|
3
|
+
import { SectionProps, ContentBlock, NavbarProps, FooterProps, SliderProps, TabsProps, ComponentProps, FormProps, RichTextResolved } from '@bettercms-ai/types';
|
|
4
4
|
import React from 'react';
|
|
5
5
|
import { EmbedRender } from '@bettercms-ai/richtext';
|
|
6
6
|
|
|
@@ -56,11 +56,17 @@ interface PortableTextProps {
|
|
|
56
56
|
*/
|
|
57
57
|
renderBlock?: (value: Record<string, unknown>) => React.ReactNode;
|
|
58
58
|
/**
|
|
59
|
-
* RT3: the X cards fetched for this render (by url)
|
|
60
|
-
* `
|
|
59
|
+
* RT3: the X cards fetched for this render (by canonical url), as `portableTextToHtml`'s
|
|
60
|
+
* `embeds`. Absent: every X post is a link card.
|
|
61
61
|
*/
|
|
62
62
|
embeds?: EmbedRender;
|
|
63
|
+
/**
|
|
64
|
+
* RT8: the entries this value's author bios and related posts point at, resolved by the caller
|
|
65
|
+
* when the page is produced (never stored), as `portableTextToHtml`'s `resolved`. Absent: each
|
|
66
|
+
* block is the placeholder aside the stored html holds.
|
|
67
|
+
*/
|
|
68
|
+
resolved?: RichTextResolved;
|
|
63
69
|
}
|
|
64
|
-
declare function PortableText({ value, bindingBase, renderBlock, embeds }: PortableTextProps): react_jsx_runtime.JSX.Element | null;
|
|
70
|
+
declare function PortableText({ value, bindingBase, renderBlock, embeds, resolved }: PortableTextProps): react_jsx_runtime.JSX.Element | null;
|
|
65
71
|
|
|
66
72
|
export { ComponentInstance, Footer, FormPlaceholder, Navbar, PortableText, type PortableTextProps, Section, Slider, Tabs };
|
package/dist/blocks.js
CHANGED
|
@@ -626,6 +626,14 @@ var PT_TYPES = {
|
|
|
626
626
|
gallery: "gallery",
|
|
627
627
|
/** A table of contents built from the headings — see `PortableTextTableOfContents`. The V2 kit's `<nav data-rt="toc">`. */
|
|
628
628
|
tableOfContents: "tableOfContents",
|
|
629
|
+
/** A footnote, an INLINE object in body text — see `PortableTextFootnote`. The V2 kit's `<sup data-rt="fn">`. */
|
|
630
|
+
footnote: "footnote",
|
|
631
|
+
/** Columns of text — see `PortableTextColumns`. The V2 kit's `<div data-rt="columns">`; the legacy `builtin:columns` reads the same way. */
|
|
632
|
+
columns: "columns",
|
|
633
|
+
/** An author bio — see `PortableTextAuthorBio`. The V2 kit's `<aside data-rt="author">`. */
|
|
634
|
+
authorBio: "authorBio",
|
|
635
|
+
/** Related posts — see `PortableTextRelatedPosts`. The V2 kit's `<aside data-rt="related">`. */
|
|
636
|
+
relatedPosts: "relatedPosts",
|
|
629
637
|
/**
|
|
630
638
|
* A table. Spelled `table`, unprefixed, because that is what every other Portable Text
|
|
631
639
|
* producer calls one — the Sanity-shaped `{_type:"table", rows}` this platform used to refuse
|
|
@@ -655,7 +663,10 @@ var STORABLE_BLOCK_TYPES = /* @__PURE__ */ new Set([
|
|
|
655
663
|
PT_TYPES.keyTakeaways,
|
|
656
664
|
PT_TYPES.embed,
|
|
657
665
|
PT_TYPES.gallery,
|
|
658
|
-
PT_TYPES.tableOfContents
|
|
666
|
+
PT_TYPES.tableOfContents,
|
|
667
|
+
PT_TYPES.columns,
|
|
668
|
+
PT_TYPES.authorBio,
|
|
669
|
+
PT_TYPES.relatedPosts
|
|
659
670
|
]);
|
|
660
671
|
var EMBEDDED_COMPONENT_KEY = "component";
|
|
661
672
|
|
|
@@ -1130,6 +1141,28 @@ function keyTakeawaysView(value) {
|
|
|
1130
1141
|
return { title, points };
|
|
1131
1142
|
}
|
|
1132
1143
|
|
|
1144
|
+
// ../richtext/src/image-layout.ts
|
|
1145
|
+
import {
|
|
1146
|
+
focalInCrop,
|
|
1147
|
+
formatImageRect,
|
|
1148
|
+
imageValueUrl,
|
|
1149
|
+
normalizeImageCrop,
|
|
1150
|
+
normalizeImageFocal,
|
|
1151
|
+
objectPosition,
|
|
1152
|
+
parseImageRect
|
|
1153
|
+
} from "@bettercms-ai/image-url";
|
|
1154
|
+
function richTextImageFrame(src, crop, focal) {
|
|
1155
|
+
const c = normalizeImageCrop(crop);
|
|
1156
|
+
const f = normalizeImageFocal(focal);
|
|
1157
|
+
if (!c && !f) return { src, crop: null, focal: null, position: null, data: [] };
|
|
1158
|
+
const drawn = imageValueUrl({ url: src, crop: c, focal: f }) ?? src;
|
|
1159
|
+
const data = [];
|
|
1160
|
+
if (drawn !== src) data.push(["data-src", src]);
|
|
1161
|
+
if (c) data.push(["data-crop", formatImageRect(c)]);
|
|
1162
|
+
if (f) data.push(["data-focal", `${f.x},${f.y}`]);
|
|
1163
|
+
return { src: drawn, crop: c, focal: f, position: f ? objectPosition(focalInCrop(f, c)) : null, data };
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1133
1166
|
// ../richtext/src/gallery.ts
|
|
1134
1167
|
var isRecord3 = (v) => !!v && typeof v === "object" && !Array.isArray(v);
|
|
1135
1168
|
var galleryMediaSrc = (raw) => safeHref(raw) ?? (raw !== "" && !raw.startsWith("//") && !/[:\s]/.test(raw) ? raw : null);
|
|
@@ -1155,9 +1188,12 @@ function galleryView(value) {
|
|
|
1155
1188
|
if (!isRecord3(item) || !isRecord3(item.image)) return;
|
|
1156
1189
|
const src = galleryMediaSrc(typeof item.image.src === "string" ? item.image.src.trim() : "");
|
|
1157
1190
|
if (!src) return;
|
|
1191
|
+
const frame = richTextImageFrame(src, item.image.crop, item.image.focal);
|
|
1158
1192
|
images.push({
|
|
1159
1193
|
key: typeof item._key === "string" && item._key ? item._key : `i${i}`,
|
|
1160
|
-
src,
|
|
1194
|
+
src: frame.src,
|
|
1195
|
+
position: frame.position,
|
|
1196
|
+
data: frame.data,
|
|
1161
1197
|
alt: typeof item.image.alt === "string" ? item.image.alt : "",
|
|
1162
1198
|
caption: typeof item.caption === "string" ? item.caption.trim() : ""
|
|
1163
1199
|
});
|
|
@@ -1165,16 +1201,49 @@ function galleryView(value) {
|
|
|
1165
1201
|
return { layout, images };
|
|
1166
1202
|
}
|
|
1167
1203
|
|
|
1168
|
-
// ../richtext/src/
|
|
1204
|
+
// ../richtext/src/columns.ts
|
|
1205
|
+
var PT_COLUMNS_LIMITS = { min: 2, max: 4 };
|
|
1206
|
+
var BUILTIN_COLUMNS_KEY = "builtin:columns";
|
|
1169
1207
|
var isRecord4 = (v) => !!v && typeof v === "object" && !Array.isArray(v);
|
|
1170
|
-
var
|
|
1208
|
+
var COLUMNS_STYLES = {
|
|
1209
|
+
s: { row: "display:flex;flex-wrap:wrap;gap:0.75rem", col: "flex:1 1 14rem;min-width:0" },
|
|
1210
|
+
m: { row: "display:flex;flex-wrap:wrap;gap:1.25rem", col: "flex:1 1 14rem;min-width:0" },
|
|
1211
|
+
l: { row: "display:flex;flex-wrap:wrap;gap:2rem", col: "flex:1 1 14rem;min-width:0" }
|
|
1212
|
+
};
|
|
1213
|
+
var legacyContent = (text, key) => text.split(/\n{2,}/).filter((p) => p.trim() !== "").map((p, i) => ({ _type: "block", _key: `${key}p${i}`, style: "normal", markDefs: [], children: [{ _type: "span", _key: `${key}s${i}`, text: p, marks: [] }] }));
|
|
1214
|
+
function columnsView(node) {
|
|
1215
|
+
if (!isRecord4(node)) return null;
|
|
1216
|
+
if (node._type === PT_TYPES.columns) {
|
|
1217
|
+
const gap = node.gap === "s" || node.gap === "l" ? node.gap : "m";
|
|
1218
|
+
const columns = (Array.isArray(node.columns) ? node.columns : []).map((c, i) => ({
|
|
1219
|
+
key: isRecord4(c) && typeof c._key === "string" && c._key ? c._key : `c${i}`,
|
|
1220
|
+
content: isRecord4(c) && Array.isArray(c.content) ? c.content : []
|
|
1221
|
+
}));
|
|
1222
|
+
return { gap, columns };
|
|
1223
|
+
}
|
|
1224
|
+
if (node._type === PT_TYPES.block && node.schemaKey === BUILTIN_COLUMNS_KEY) {
|
|
1225
|
+
const f = isRecord4(node.fields) ? node.fields : {};
|
|
1226
|
+
if (!Array.isArray(f.columns) || !f.columns.every((c) => typeof c === "string")) return null;
|
|
1227
|
+
const gap = f.gap === "small" ? "s" : f.gap === "large" ? "l" : "m";
|
|
1228
|
+
const columns = (Array.isArray(f.columns) ? f.columns : []).slice(0, PT_COLUMNS_LIMITS.max).map((c, i) => ({
|
|
1229
|
+
key: `c${i}`,
|
|
1230
|
+
content: legacyContent(typeof c === "string" ? c : "", `c${i}`)
|
|
1231
|
+
}));
|
|
1232
|
+
return { gap, columns };
|
|
1233
|
+
}
|
|
1234
|
+
return null;
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
// ../richtext/src/component-copy.ts
|
|
1238
|
+
var isRecord5 = (v) => !!v && typeof v === "object" && !Array.isArray(v);
|
|
1239
|
+
var fieldsOf = (value) => isRecord5(value) && value.schemaKey === EMBEDDED_COMPONENT_KEY && isRecord5(value.fields) ? value.fields : null;
|
|
1171
1240
|
function detachedComponentCopy(value) {
|
|
1172
1241
|
const f = fieldsOf(value);
|
|
1173
1242
|
if (!f || f.detached !== true || !Array.isArray(f.blocks)) return null;
|
|
1174
1243
|
return {
|
|
1175
|
-
id:
|
|
1244
|
+
id: isRecord5(value) && typeof value._key === "string" ? value._key : "",
|
|
1176
1245
|
componentId: typeof f.componentId === "string" ? f.componentId : "",
|
|
1177
|
-
props:
|
|
1246
|
+
props: isRecord5(f.props) ? f.props : {},
|
|
1178
1247
|
blocks: f.blocks,
|
|
1179
1248
|
...typeof f.sourcePublishedAt === "string" ? { sourcePublishedAt: f.sourcePublishedAt } : {},
|
|
1180
1249
|
...typeof f.variant === "string" ? { variant: f.variant } : {}
|
|
@@ -1187,7 +1256,7 @@ function componentCopyLines(copy) {
|
|
|
1187
1256
|
const t = v.replace(/<[^>]+>/g, " ").replace(/ /g, " ").replace(/\s+/g, " ").trim();
|
|
1188
1257
|
return t || null;
|
|
1189
1258
|
}
|
|
1190
|
-
if (
|
|
1259
|
+
if (isRecord5(v)) return line(v.label ?? v.text ?? v.title);
|
|
1191
1260
|
return null;
|
|
1192
1261
|
};
|
|
1193
1262
|
return Object.values(copy.props).map(line).filter((s) => !!s);
|
|
@@ -1218,6 +1287,14 @@ function portableTextToPlainText(value) {
|
|
|
1218
1287
|
lines.push(...galleryView(node).images.map((x) => x.caption).filter(Boolean));
|
|
1219
1288
|
continue;
|
|
1220
1289
|
}
|
|
1290
|
+
const cols = columnsView(node);
|
|
1291
|
+
if (cols) {
|
|
1292
|
+
for (const c of cols.columns) {
|
|
1293
|
+
const text = portableTextToPlainText(c.content);
|
|
1294
|
+
if (text) lines.push(text);
|
|
1295
|
+
}
|
|
1296
|
+
continue;
|
|
1297
|
+
}
|
|
1221
1298
|
const table = migrateTable(node);
|
|
1222
1299
|
if (table) {
|
|
1223
1300
|
lines.push(...tableLines(table));
|
|
@@ -1258,20 +1335,30 @@ var pullQuoteLines = (v) => {
|
|
|
1258
1335
|
|
|
1259
1336
|
// ../richtext/src/toc.ts
|
|
1260
1337
|
import { slugify } from "@bettercms-ai/types";
|
|
1261
|
-
var
|
|
1262
|
-
var
|
|
1338
|
+
var isRecord6 = (v) => !!v && typeof v === "object" && !Array.isArray(v);
|
|
1339
|
+
var isHeadingAnchor = (v) => typeof v === "string" && /^[A-Za-z0-9][A-Za-z0-9_:.-]{0,127}$/.test(v);
|
|
1340
|
+
var headingText = (node) => (Array.isArray(node.children) ? node.children : []).map((c) => isRecord6(c) && typeof c.text === "string" ? c.text : "").join("").replace(//g, "").trim();
|
|
1263
1341
|
function outlineOf(value) {
|
|
1264
|
-
const
|
|
1265
|
-
const nodes = /* @__PURE__ */ new WeakMap();
|
|
1342
|
+
const heads = [];
|
|
1266
1343
|
const used = /* @__PURE__ */ new Set();
|
|
1267
1344
|
for (const node of Array.isArray(value) ? value : []) {
|
|
1268
|
-
if (!
|
|
1345
|
+
if (!isRecord6(node) || node._type !== "block" || node.listItem || node.style !== "h2" && node.style !== "h3") continue;
|
|
1269
1346
|
const text = headingText(node);
|
|
1270
1347
|
if (!text) continue;
|
|
1271
|
-
const
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1348
|
+
const anchor = isHeadingAnchor(node.anchor) && !used.has(node.anchor) ? node.anchor : null;
|
|
1349
|
+
if (anchor) used.add(anchor);
|
|
1350
|
+
heads.push({ node, text, anchor });
|
|
1351
|
+
}
|
|
1352
|
+
const items = [];
|
|
1353
|
+
const nodes = /* @__PURE__ */ new WeakMap();
|
|
1354
|
+
for (const { node, text, anchor } of heads) {
|
|
1355
|
+
let id = anchor;
|
|
1356
|
+
if (!id) {
|
|
1357
|
+
const base = slugify(text);
|
|
1358
|
+
id = base;
|
|
1359
|
+
for (let n = 2; used.has(id); n++) id = `${base}-${n}`;
|
|
1360
|
+
used.add(id);
|
|
1361
|
+
}
|
|
1275
1362
|
nodes.set(node, id);
|
|
1276
1363
|
items.push({ id, text, depth: node.style === "h2" ? 2 : 3 });
|
|
1277
1364
|
}
|
|
@@ -1288,25 +1375,79 @@ function tocTree(items, depth) {
|
|
|
1288
1375
|
return out;
|
|
1289
1376
|
}
|
|
1290
1377
|
function tocView(value) {
|
|
1291
|
-
const v =
|
|
1378
|
+
const v = isRecord6(value) ? value : {};
|
|
1292
1379
|
const title = typeof v.title === "string" ? v.title.trim() : "";
|
|
1293
1380
|
return { title, label: title || "Table of contents", depth: v.depth === "2" ? "2" : "3" };
|
|
1294
1381
|
}
|
|
1295
1382
|
|
|
1383
|
+
// ../richtext/src/footnote.ts
|
|
1384
|
+
var isRecord7 = (v) => !!v && typeof v === "object" && !Array.isArray(v);
|
|
1385
|
+
function footnoteOrder(value) {
|
|
1386
|
+
const notes = [];
|
|
1387
|
+
const nodes = /* @__PURE__ */ new WeakMap();
|
|
1388
|
+
for (const node of Array.isArray(value) ? value : []) {
|
|
1389
|
+
if (!isRecord7(node) || node._type !== "block" || !Array.isArray(node.children)) continue;
|
|
1390
|
+
for (const child of node.children) {
|
|
1391
|
+
if (!isRecord7(child) || child._type !== PT_TYPES.footnote) continue;
|
|
1392
|
+
nodes.set(child, notes.push(typeof child.note === "string" ? child.note : ""));
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
return { notes, nodes };
|
|
1396
|
+
}
|
|
1397
|
+
|
|
1398
|
+
// ../richtext/src/references.ts
|
|
1399
|
+
var PT_RELATED_LIMITS = { titleChars: 40, minCount: 1, maxCount: 6, posts: 6 };
|
|
1400
|
+
var isRecord8 = (v) => !!v && typeof v === "object" && !Array.isArray(v);
|
|
1401
|
+
var refOf = (v) => isRecord8(v) && typeof v._ref === "string" ? v._ref : "";
|
|
1402
|
+
var imageSrc = (raw) => safeHref(raw) ?? (raw !== "" && !raw.startsWith("//") && !/[:\s]/.test(raw) ? raw : null);
|
|
1403
|
+
function relatedPostsView(value, resolved = {}) {
|
|
1404
|
+
const v = isRecord8(value) ? value : {};
|
|
1405
|
+
const title = typeof v.title === "string" ? v.title.trim() : "";
|
|
1406
|
+
const mode = v.mode === "manual" || v.mode === "newest" ? v.mode : "topic";
|
|
1407
|
+
const n = typeof v.count === "number" && Number.isInteger(v.count) ? v.count : 3;
|
|
1408
|
+
const count = Math.min(PT_RELATED_LIMITS.maxCount, Math.max(PT_RELATED_LIMITS.minCount, n));
|
|
1409
|
+
const picked = (Array.isArray(v.posts) ? v.posts : []).map(refOf).filter(Boolean);
|
|
1410
|
+
const key = typeof v._key === "string" ? v._key : "";
|
|
1411
|
+
const ids = mode === "manual" ? picked : (resolved.related?.[key] ?? []).slice(0, count);
|
|
1412
|
+
const items = [];
|
|
1413
|
+
for (const id of ids) {
|
|
1414
|
+
const post = resolved.posts?.[id];
|
|
1415
|
+
const url = post && typeof post.url === "string" ? safeHref(post.url) : null;
|
|
1416
|
+
if (post && typeof post.title === "string" && url) items.push({ ...post, id, url });
|
|
1417
|
+
}
|
|
1418
|
+
return { key, title, mode, count, picked, items };
|
|
1419
|
+
}
|
|
1420
|
+
function authorBioView(value, resolved = {}) {
|
|
1421
|
+
const key = isRecord8(value) && typeof value._key === "string" ? value._key : "";
|
|
1422
|
+
const id = refOf(isRecord8(value) ? value.author : void 0);
|
|
1423
|
+
const a = id ? resolved.authors?.[id] : void 0;
|
|
1424
|
+
if (!a || typeof a.name !== "string") return { key, id, author: null };
|
|
1425
|
+
const avatar = typeof a.avatar === "string" ? imageSrc(a.avatar.trim()) : null;
|
|
1426
|
+
return {
|
|
1427
|
+
key,
|
|
1428
|
+
id,
|
|
1429
|
+
author: {
|
|
1430
|
+
name: a.name,
|
|
1431
|
+
...avatar ? { avatar } : {},
|
|
1432
|
+
...typeof a.bio === "string" && a.bio.trim() ? { bio: a.bio.trim() } : {}
|
|
1433
|
+
}
|
|
1434
|
+
};
|
|
1435
|
+
}
|
|
1436
|
+
|
|
1296
1437
|
// ../richtext/src/read.ts
|
|
1297
|
-
var
|
|
1438
|
+
var isRecord9 = (v) => !!v && typeof v === "object" && !Array.isArray(v);
|
|
1298
1439
|
function normalizePortableText(value, key = randomKey) {
|
|
1299
1440
|
if (!Array.isArray(value)) return [];
|
|
1300
1441
|
return value.flatMap((raw) => {
|
|
1301
|
-
if (!
|
|
1442
|
+
if (!isRecord9(raw) || typeof raw._type !== "string") return [];
|
|
1302
1443
|
const table = migrateTable(raw, key, (v, cellKey) => normalizePortableText(v, cellKey));
|
|
1303
1444
|
if (table) return [table];
|
|
1304
|
-
const node = { ...raw, _key:
|
|
1445
|
+
const node = { ...raw, _key: isRecord9(raw) && typeof raw._key === "string" && raw._key ? raw._key : key() };
|
|
1305
1446
|
if (node._type === "block") {
|
|
1306
1447
|
if (!Array.isArray(node.markDefs)) node.markDefs = [];
|
|
1307
1448
|
if (typeof node.style !== "string") node.style = "normal";
|
|
1308
1449
|
node.children = (Array.isArray(node.children) ? node.children : []).map((c) => {
|
|
1309
|
-
if (!
|
|
1450
|
+
if (!isRecord9(c)) return c;
|
|
1310
1451
|
const child = { ...c, _key: typeof c._key === "string" && c._key ? c._key : key() };
|
|
1311
1452
|
if (child._type === "span" && !Array.isArray(child.marks)) child.marks = [];
|
|
1312
1453
|
return child;
|
|
@@ -1316,9 +1457,9 @@ function normalizePortableText(value, key = randomKey) {
|
|
|
1316
1457
|
});
|
|
1317
1458
|
}
|
|
1318
1459
|
function readStructuredContent(props) {
|
|
1319
|
-
if (!
|
|
1460
|
+
if (!isRecord9(props)) return null;
|
|
1320
1461
|
const candidate = props.content ?? props.value;
|
|
1321
|
-
const arr = Array.isArray(candidate) ? candidate :
|
|
1462
|
+
const arr = Array.isArray(candidate) ? candidate : isRecord9(candidate) ? Array.isArray(candidate.content) ? candidate.content : Array.isArray(candidate.value) ? candidate.value : null : null;
|
|
1322
1463
|
return arr && arr.length > 0 ? arr : null;
|
|
1323
1464
|
}
|
|
1324
1465
|
|
|
@@ -1675,6 +1816,47 @@ function XCard({ url, card }) {
|
|
|
1675
1816
|
/* @__PURE__ */ jsx14("a", { href: url, children: card.date || "View on X" })
|
|
1676
1817
|
] });
|
|
1677
1818
|
}
|
|
1819
|
+
function RelatedPosts({ value, resolved }) {
|
|
1820
|
+
const { key, title, mode, count, picked, items } = relatedPostsView(value, resolved);
|
|
1821
|
+
if (!resolved) {
|
|
1822
|
+
return /* @__PURE__ */ jsx14("aside", { "data-rt": "related", "data-key": key, "data-mode": mode, "data-count": count, "data-posts": picked.join(","), "data-title": title || void 0 });
|
|
1823
|
+
}
|
|
1824
|
+
if (!items.length) return null;
|
|
1825
|
+
return /* @__PURE__ */ jsxs11("aside", { "data-rt": "related", "data-mode": mode, "data-count": count, "data-posts": picked.join(","), children: [
|
|
1826
|
+
title ? /* @__PURE__ */ jsx14("p", { "data-title": "", children: title }) : null,
|
|
1827
|
+
items.length ? /* @__PURE__ */ jsx14("ul", { children: items.map((p) => /* @__PURE__ */ jsx14("li", { children: /* @__PURE__ */ jsx14("a", { href: p.url, children: p.title }) }, p.id)) }) : null
|
|
1828
|
+
] });
|
|
1829
|
+
}
|
|
1830
|
+
function AuthorBio({ value, resolved }) {
|
|
1831
|
+
const { key, id, author } = authorBioView(value, resolved);
|
|
1832
|
+
if (!resolved) return /* @__PURE__ */ jsx14("aside", { "data-rt": "author", "data-key": key, "data-author": id });
|
|
1833
|
+
if (!author) return null;
|
|
1834
|
+
return /* @__PURE__ */ jsxs11("aside", { "data-rt": "author", "data-author": id, children: [
|
|
1835
|
+
author?.avatar ? /* @__PURE__ */ jsx14("img", { src: author.avatar, alt: "" }) : null,
|
|
1836
|
+
author ? /* @__PURE__ */ jsx14("p", { "data-name": "", children: author.name }) : null,
|
|
1837
|
+
author?.bio ? /* @__PURE__ */ jsx14("p", { "data-bio": "", children: author.bio }) : null
|
|
1838
|
+
] });
|
|
1839
|
+
}
|
|
1840
|
+
function Columns2({ value, components }) {
|
|
1841
|
+
const view = columnsView(value);
|
|
1842
|
+
if (!view) return null;
|
|
1843
|
+
const s = COLUMNS_STYLES[view.gap];
|
|
1844
|
+
return /* @__PURE__ */ jsx14("div", { "data-rt": "columns", "data-gap": view.gap, "data-cols": view.columns.length, style: styleObject(s.row), children: view.columns.map((c) => /* @__PURE__ */ jsx14("div", { "data-col": "true", style: styleObject(s.col), children: c.content.length ? /* @__PURE__ */ jsx14(PT, { value: c.content, components }) : null }, c.key)) });
|
|
1845
|
+
}
|
|
1846
|
+
function FootnoteMark({ value, footnotes }) {
|
|
1847
|
+
const note = typeof value.note === "string" ? value.note : "";
|
|
1848
|
+
const n = footnotes.nodes.get(value);
|
|
1849
|
+
if (!n) return /* @__PURE__ */ jsx14("sup", { "data-rt": "fn", "data-note": note });
|
|
1850
|
+
return /* @__PURE__ */ jsx14("sup", { "data-rt": "fn", id: `fnref-${n}`, "data-note": note, children: /* @__PURE__ */ jsx14("a", { href: `#fn-${n}`, "aria-label": `Footnote ${n}`, children: n }) });
|
|
1851
|
+
}
|
|
1852
|
+
function FootnoteList({ notes }) {
|
|
1853
|
+
if (!notes.length) return null;
|
|
1854
|
+
return /* @__PURE__ */ jsx14("ol", { className: "cs-footnotes", "data-rt": "footnotes", "aria-label": "Footnotes", children: notes.map((note, i) => /* @__PURE__ */ jsxs11("li", { id: `fn-${i + 1}`, children: [
|
|
1855
|
+
note,
|
|
1856
|
+
" ",
|
|
1857
|
+
/* @__PURE__ */ jsx14("a", { href: `#fnref-${i + 1}`, "aria-label": `Back to reference ${i + 1}`, children: "\u21A9" })
|
|
1858
|
+
] }, i)) });
|
|
1859
|
+
}
|
|
1678
1860
|
function Toc({ value, outline }) {
|
|
1679
1861
|
const { title, label, depth } = tocView(value);
|
|
1680
1862
|
const link = (o) => /* @__PURE__ */ jsx14("a", { href: `#${o.id}`, children: o.text });
|
|
@@ -1690,7 +1872,16 @@ function Gallery({ value }) {
|
|
|
1690
1872
|
const { layout, images } = galleryView(value);
|
|
1691
1873
|
const s = GALLERY_STYLES[layout];
|
|
1692
1874
|
return /* @__PURE__ */ jsx14("figure", { "data-rt": "gallery", "data-layout": layout, children: /* @__PURE__ */ jsx14("ul", { style: styleObject(s.ul), tabIndex: layout === "carousel" ? 0 : void 0, children: images.map((x) => /* @__PURE__ */ jsx14("li", { style: styleObject(s.li), children: /* @__PURE__ */ jsxs11("figure", { style: styleObject(s.figure), children: [
|
|
1693
|
-
/* @__PURE__ */ jsx14(
|
|
1875
|
+
/* @__PURE__ */ jsx14(
|
|
1876
|
+
"img",
|
|
1877
|
+
{
|
|
1878
|
+
alt: x.alt,
|
|
1879
|
+
style: styleObject(x.position ? `${s.img};object-position:${x.position}` : s.img),
|
|
1880
|
+
loading: "lazy",
|
|
1881
|
+
...Object.fromEntries(x.data),
|
|
1882
|
+
src: x.src
|
|
1883
|
+
}
|
|
1884
|
+
),
|
|
1694
1885
|
x.caption ? /* @__PURE__ */ jsx14("figcaption", { children: x.caption }) : null
|
|
1695
1886
|
] }) }, x.key)) }) });
|
|
1696
1887
|
}
|
|
@@ -1714,8 +1905,8 @@ function Embed({ value, embeds }) {
|
|
|
1714
1905
|
}
|
|
1715
1906
|
return /* @__PURE__ */ jsx14("figure", { "data-rt": "embed", "data-url": url, "data-provider": r.provider, "data-aspect": aspect, "aria-label": title, children: body });
|
|
1716
1907
|
}
|
|
1717
|
-
function buildComponents(renderBlock, embeds = {}, outline = { items: [], nodes: /* @__PURE__ */ new WeakMap() }) {
|
|
1718
|
-
|
|
1908
|
+
function buildComponents(renderBlock, embeds = {}, outline = { items: [], nodes: /* @__PURE__ */ new WeakMap() }, footnotes = { notes: [], nodes: /* @__PURE__ */ new WeakMap() }, resolved) {
|
|
1909
|
+
const components = {
|
|
1719
1910
|
// RT5: every top-level h2 and h3 carries its outline id, as html.ts writes it.
|
|
1720
1911
|
block: {
|
|
1721
1912
|
h2: ({ value, children }) => /* @__PURE__ */ jsx14("h2", { id: outline.nodes.get(value), children }),
|
|
@@ -1743,6 +1934,7 @@ function buildComponents(renderBlock, embeds = {}, outline = { items: [], nodes:
|
|
|
1743
1934
|
if (rendered !== void 0) return /* @__PURE__ */ jsx14(Fragment2, { children: rendered });
|
|
1744
1935
|
const builtIn = calloutOrToggle(value);
|
|
1745
1936
|
if (builtIn) return builtIn;
|
|
1937
|
+
if (columnsView(value)) return /* @__PURE__ */ jsx14(Columns2, { value, components });
|
|
1746
1938
|
const copy = detachedComponentCopy(value);
|
|
1747
1939
|
if (copy) {
|
|
1748
1940
|
return /* @__PURE__ */ jsx14("div", { className: "bcms-block bcms-block--component", "data-bcms-component-copy": copy.componentId, "data-bcms-instance": copy.id, children: /* @__PURE__ */ jsx14(BlocksRenderer, { blocks: copy.blocks }) });
|
|
@@ -1775,6 +1967,9 @@ function buildComponents(renderBlock, embeds = {}, outline = { items: [], nodes:
|
|
|
1775
1967
|
[PT_TYPES.embed]: ({ value }) => /* @__PURE__ */ jsx14(Embed, { value, embeds }),
|
|
1776
1968
|
[PT_TYPES.gallery]: ({ value }) => /* @__PURE__ */ jsx14(Gallery, { value }),
|
|
1777
1969
|
[PT_TYPES.tableOfContents]: ({ value }) => /* @__PURE__ */ jsx14(Toc, { value, outline }),
|
|
1970
|
+
[PT_TYPES.columns]: ({ value }) => /* @__PURE__ */ jsx14(Columns2, { value, components }),
|
|
1971
|
+
[PT_TYPES.relatedPosts]: ({ value }) => /* @__PURE__ */ jsx14(RelatedPosts, { value, resolved }),
|
|
1972
|
+
[PT_TYPES.authorBio]: ({ value }) => /* @__PURE__ */ jsx14(AuthorBio, { value, resolved }),
|
|
1778
1973
|
/**
|
|
1779
1974
|
* Key takeaways — the React half of `portableTextToHtml`'s, and the same DOM: the V2 kit's
|
|
1780
1975
|
* `<aside data-rt="takeaways">`, read through the one `keyTakeawaysView` both renderers share.
|
|
@@ -1787,6 +1982,7 @@ function buildComponents(renderBlock, embeds = {}, outline = { items: [], nodes:
|
|
|
1787
1982
|
/* @__PURE__ */ jsx14("ul", { children: points.map((p, i) => /* @__PURE__ */ jsx14("li", { children: p }, i)) })
|
|
1788
1983
|
] });
|
|
1789
1984
|
},
|
|
1985
|
+
[PT_TYPES.footnote]: ({ value }) => /* @__PURE__ */ jsx14(FootnoteMark, { value, footnotes }),
|
|
1790
1986
|
[PT_TYPES.mention]: ({ value }) => {
|
|
1791
1987
|
const v = value;
|
|
1792
1988
|
return /* @__PURE__ */ jsx14("span", { className: "bcms-mention", children: typeof v.label === "string" ? v.label : "" });
|
|
@@ -1851,6 +2047,7 @@ function buildComponents(renderBlock, embeds = {}, outline = { items: [], nodes:
|
|
|
1851
2047
|
unknownType: ({ children }) => /* @__PURE__ */ jsx14(Fragment2, { children: children ?? null }),
|
|
1852
2048
|
unknownBlockStyle: ({ children }) => /* @__PURE__ */ jsx14("p", { children })
|
|
1853
2049
|
};
|
|
2050
|
+
return components;
|
|
1854
2051
|
}
|
|
1855
2052
|
var isListItem = (node) => node._type === "block" && typeof node.listItem === "string" && !!node.listItem;
|
|
1856
2053
|
function runsOf(blocks) {
|
|
@@ -1891,10 +2088,14 @@ function calloutOrToggle(value) {
|
|
|
1891
2088
|
}
|
|
1892
2089
|
return null;
|
|
1893
2090
|
}
|
|
1894
|
-
function PortableText({ value, bindingBase, renderBlock, embeds }) {
|
|
2091
|
+
function PortableText({ value, bindingBase, renderBlock, embeds, resolved }) {
|
|
1895
2092
|
const blocks = React6.useMemo(() => normalizePortableText(readValue(value)), [value]);
|
|
1896
2093
|
const outline = React6.useMemo(() => outlineOf(blocks), [blocks]);
|
|
1897
|
-
const
|
|
2094
|
+
const footnotes = React6.useMemo(() => footnoteOrder(blocks), [blocks]);
|
|
2095
|
+
const components = React6.useMemo(
|
|
2096
|
+
() => buildComponents(renderBlock, embeds, outline, footnotes, resolved),
|
|
2097
|
+
[renderBlock, embeds, outline, footnotes, resolved]
|
|
2098
|
+
);
|
|
1898
2099
|
const listComponents = React6.useMemo(() => {
|
|
1899
2100
|
if (!bindingBase) return null;
|
|
1900
2101
|
const keys = new Set(blocks.map((b) => b._key));
|
|
@@ -1910,10 +2111,19 @@ function PortableText({ value, bindingBase, renderBlock, embeds }) {
|
|
|
1910
2111
|
};
|
|
1911
2112
|
}, [bindingBase, blocks, components]);
|
|
1912
2113
|
if (blocks.length === 0) return null;
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
2114
|
+
const notes = /* @__PURE__ */ jsx14(FootnoteList, { notes: footnotes.notes });
|
|
2115
|
+
if (!bindingBase || !listComponents) {
|
|
2116
|
+
return /* @__PURE__ */ jsxs11(Fragment2, { children: [
|
|
2117
|
+
/* @__PURE__ */ jsx14(PT, { value: blocks, components }),
|
|
2118
|
+
notes
|
|
2119
|
+
] });
|
|
2120
|
+
}
|
|
2121
|
+
return /* @__PURE__ */ jsxs11(Fragment2, { children: [
|
|
2122
|
+
runsOf(blocks).map(
|
|
2123
|
+
(run) => isListItem(run[0]) ? /* @__PURE__ */ jsx14(PT, { value: run, components: listComponents }, run[0]._key) : /* @__PURE__ */ jsx14(Bound, { base: bindingBase, nodeKey: run[0]._key, children: /* @__PURE__ */ jsx14(PT, { value: run, components }) }, run[0]._key)
|
|
2124
|
+
),
|
|
2125
|
+
notes
|
|
2126
|
+
] });
|
|
1917
2127
|
}
|
|
1918
2128
|
function readValue(value) {
|
|
1919
2129
|
if (Array.isArray(value)) return value;
|