@bettercms-ai/ui 0.16.0 → 0.17.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/dist/blocks.js +1127 -763
- package/dist/blocks.js.map +1 -1
- package/dist/index.js +1124 -980
- package/dist/index.js.map +1 -1
- package/dist/registry.js +368 -106
- package/dist/registry.js.map +1 -1
- package/dist/renderer.d.ts +2 -2
- package/dist/renderer.js +163 -19
- package/dist/renderer.js.map +1 -1
- package/dist/resolve.js +20 -3
- package/dist/resolve.js.map +1 -1
- package/dist/tokens.d.ts +1 -1
- package/dist/tokens.js +3 -0
- package/dist/tokens.js.map +1 -1
- package/package.json +3 -3
package/dist/renderer.d.ts
CHANGED
|
@@ -19,8 +19,8 @@ interface BlocksRendererProps {
|
|
|
19
19
|
annotate?: boolean;
|
|
20
20
|
/**
|
|
21
21
|
* The instant block windows (`block.window`) are read against. Defaults to the render time.
|
|
22
|
-
* A block outside its window is not rendered, at any depth; with
|
|
23
|
-
* marked `data-bcms-window="pending|expired"`
|
|
22
|
+
* A block outside its window, or hidden on every breakpoint, is not rendered, at any depth; with
|
|
23
|
+
* `annotate` it is rendered and marked `data-bcms-window="pending|expired"` / `data-bcms-hidden="all"`.
|
|
24
24
|
*/
|
|
25
25
|
now?: Date;
|
|
26
26
|
}
|
package/dist/renderer.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
// src/renderer.tsx
|
|
4
|
-
import { RESPONSIVE_CSS, hideOnAttr, splitButtonStyle, usesHideOn, visibleBlocks, windowState } from "@bettercms-ai/types";
|
|
4
|
+
import { RESPONSIVE_CSS, hiddenEverywhere, hideOnAttr, splitButtonStyle, usesHideOn, visibleBlocks, windowState } from "@bettercms-ai/types";
|
|
5
5
|
|
|
6
6
|
// src/blocks/Heading.tsx
|
|
7
7
|
import { jsx } from "react/jsx-runtime";
|
|
@@ -36,6 +36,13 @@ import React from "react";
|
|
|
36
36
|
import { PortableText as PT } from "@portabletext/react";
|
|
37
37
|
|
|
38
38
|
// ../richtext/src/types.ts
|
|
39
|
+
function pullQuoteHref(link) {
|
|
40
|
+
if (typeof link === "string") return link.trim();
|
|
41
|
+
if (link && typeof link === "object" && typeof link.href === "string") {
|
|
42
|
+
return link.href.trim();
|
|
43
|
+
}
|
|
44
|
+
return "";
|
|
45
|
+
}
|
|
39
46
|
var PT_TYPES = {
|
|
40
47
|
/** A generic embedded block: `{schemaKey, schemaLabel, fields}`, the successor to Lexical's BlockNode. */
|
|
41
48
|
block: "bcmsBlock",
|
|
@@ -53,6 +60,10 @@ var PT_TYPES = {
|
|
|
53
60
|
video: "video",
|
|
54
61
|
/** A downloadable document — a PDF, a spec sheet. `<a href download>`, nothing more. */
|
|
55
62
|
file: "file",
|
|
63
|
+
/** A pull quote — see `PortableTextPullQuote`. The V2 kit's `<figure data-rt="pullquote">`. */
|
|
64
|
+
pullQuote: "pullQuote",
|
|
65
|
+
/** Key takeaways — see `PortableTextKeyTakeaways`. The V2 kit's `<aside data-rt="takeaways">`. */
|
|
66
|
+
keyTakeaways: "keyTakeaways",
|
|
56
67
|
/**
|
|
57
68
|
* A table. Spelled `table`, unprefixed, because that is what every other Portable Text
|
|
58
69
|
* producer calls one — the Sanity-shaped `{_type:"table", rows}` this platform used to refuse
|
|
@@ -77,8 +88,11 @@ var STORABLE_BLOCK_TYPES = /* @__PURE__ */ new Set([
|
|
|
77
88
|
PT_TYPES.divider,
|
|
78
89
|
PT_TYPES.mention,
|
|
79
90
|
PT_TYPES.video,
|
|
80
|
-
PT_TYPES.file
|
|
91
|
+
PT_TYPES.file,
|
|
92
|
+
PT_TYPES.pullQuote,
|
|
93
|
+
PT_TYPES.keyTakeaways
|
|
81
94
|
]);
|
|
95
|
+
var EMBEDDED_COMPONENT_KEY = "component";
|
|
82
96
|
|
|
83
97
|
// ../richtext/src/styled.ts
|
|
84
98
|
var STYLED_TAGS = [
|
|
@@ -249,7 +263,8 @@ function normalizeCell(raw, at, mint, read) {
|
|
|
249
263
|
content: Array.isArray(raw.content) ? read(raw.content, inner) : textContent(typeof raw.text === "string" ? raw.text : "", inner),
|
|
250
264
|
...raw.header === true ? { header: true } : {},
|
|
251
265
|
...span === void 0 ? {} : { span },
|
|
252
|
-
...rowSpan === void 0 ? {} : { rowSpan }
|
|
266
|
+
...rowSpan === void 0 ? {} : { rowSpan },
|
|
267
|
+
...typeof raw.group === "string" ? { group: raw.group } : {}
|
|
253
268
|
};
|
|
254
269
|
}
|
|
255
270
|
function normalizeRow(raw, at, mint, read) {
|
|
@@ -257,7 +272,8 @@ function normalizeRow(raw, at, mint, read) {
|
|
|
257
272
|
return {
|
|
258
273
|
_key: isRecord(raw) && isValidKey(raw._key) ? raw._key : mint("r", at),
|
|
259
274
|
cells: cells.map((c, col) => normalizeCell(c, [at, col], mint, read)),
|
|
260
|
-
...isRecord(raw) && raw.head === true ? { head: true } : {}
|
|
275
|
+
...isRecord(raw) && raw.head === true ? { head: true } : {},
|
|
276
|
+
...isRecord(raw) && typeof raw.group === "string" ? { group: raw.group } : {}
|
|
261
277
|
};
|
|
262
278
|
}
|
|
263
279
|
function builtInTableToTable(node, key = randomKey) {
|
|
@@ -302,24 +318,93 @@ function migrateTable(node, key = randomKey, read = asIs) {
|
|
|
302
318
|
rows: rows.map((r, i) => normalizeRow(r, i, mint, read)),
|
|
303
319
|
// Carried through: a migrated table that an author saves back is still the same table, and
|
|
304
320
|
// dropping the flag on that save would change the page the next time it renders.
|
|
305
|
-
...node.legacy === true ? { legacy: true } : {}
|
|
321
|
+
...node.legacy === true ? { legacy: true } : {},
|
|
322
|
+
...tableExtras(node)
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
function tableExtras(node) {
|
|
326
|
+
const groups = (list) => Array.isArray(list) ? list.filter((g) => isRecord(g) && typeof g._key === "string" && g._key !== "").map((g) => ({ _key: g._key, label: typeof g.label === "string" ? g.label : "" })) : void 0;
|
|
327
|
+
const rowGroups = groups(node.rowGroups);
|
|
328
|
+
const columnGroups = groups(node.columnGroups);
|
|
329
|
+
return {
|
|
330
|
+
...typeof node.caption === "string" ? { caption: node.caption } : {},
|
|
331
|
+
...typeof node.notes === "string" ? { notes: node.notes } : {},
|
|
332
|
+
...rowGroups ? { rowGroups } : {},
|
|
333
|
+
...columnGroups ? { columnGroups } : {}
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
function tableGroupLayout(table) {
|
|
337
|
+
const rows = Array.isArray(table.rows) ? table.rows : [];
|
|
338
|
+
const labels = (list) => new Map(
|
|
339
|
+
(Array.isArray(list) ? list : []).filter((g) => isRecord(g) && typeof g._key === "string").map((g) => [g._key, typeof g.label === "string" ? g.label : ""])
|
|
340
|
+
);
|
|
341
|
+
const cellsOf = (row) => isRecord(row) && Array.isArray(row.cells) ? row.cells : [];
|
|
342
|
+
const spanOf2 = (cell) => isRecord(cell) && typeof cell.span === "number" && Number.isInteger(cell.span) && cell.span > 1 ? cell.span : 1;
|
|
343
|
+
const width = rows.reduce((max, row) => Math.max(max, cellsOf(row).reduce((n, c) => n + spanOf2(c), 0)), 0);
|
|
344
|
+
const colLabels = labels(table.columnGroups);
|
|
345
|
+
const columns = [];
|
|
346
|
+
for (const cell of cellsOf(rows[0])) {
|
|
347
|
+
const key = isRecord(cell) && typeof cell.group === "string" && colLabels.has(cell.group) ? cell.group : void 0;
|
|
348
|
+
const last = columns[columns.length - 1];
|
|
349
|
+
if (last && last.key === key) last.width += spanOf2(cell);
|
|
350
|
+
else columns.push({ width: spanOf2(cell), ...key === void 0 ? {} : { key, label: colLabels.get(key) } });
|
|
351
|
+
}
|
|
352
|
+
const rowLabels = labels(table.rowGroups);
|
|
353
|
+
const headCount = rows.findIndex((r) => !isRecord(r) || r.head !== true);
|
|
354
|
+
const body = headCount === -1 ? [] : rows.slice(headCount);
|
|
355
|
+
const runs = [];
|
|
356
|
+
for (const row of body) {
|
|
357
|
+
const key = isRecord(row) && typeof row.group === "string" && rowLabels.has(row.group) ? row.group : void 0;
|
|
358
|
+
const last = runs[runs.length - 1];
|
|
359
|
+
if (last && last.key === key) last.rows.push(row);
|
|
360
|
+
else runs.push({ rows: [row], ...key === void 0 ? {} : { key, label: rowLabels.get(key) } });
|
|
361
|
+
}
|
|
362
|
+
return {
|
|
363
|
+
columns: columns.some((r) => r.key !== void 0) ? columns : [],
|
|
364
|
+
rows: runs.some((r) => r.key !== void 0) ? runs : [],
|
|
365
|
+
width
|
|
306
366
|
};
|
|
307
367
|
}
|
|
308
368
|
|
|
309
|
-
// ../richtext/src/
|
|
369
|
+
// ../richtext/src/key-takeaways.ts
|
|
310
370
|
var isRecord2 = (v) => !!v && typeof v === "object" && !Array.isArray(v);
|
|
371
|
+
function keyTakeawaysView(value) {
|
|
372
|
+
const v = isRecord2(value) ? value : {};
|
|
373
|
+
const title = typeof v.title === "string" ? v.title.trim() : "";
|
|
374
|
+
const points = (Array.isArray(v.points) ? v.points : []).map((p) => typeof p === "string" ? p.trim() : "").filter(Boolean);
|
|
375
|
+
return { title, points };
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// ../richtext/src/component-copy.ts
|
|
379
|
+
var isRecord3 = (v) => !!v && typeof v === "object" && !Array.isArray(v);
|
|
380
|
+
var fieldsOf = (value) => isRecord3(value) && value.schemaKey === EMBEDDED_COMPONENT_KEY && isRecord3(value.fields) ? value.fields : null;
|
|
381
|
+
function detachedComponentCopy(value) {
|
|
382
|
+
const f = fieldsOf(value);
|
|
383
|
+
if (!f || f.detached !== true || !Array.isArray(f.blocks)) return null;
|
|
384
|
+
return {
|
|
385
|
+
id: isRecord3(value) && typeof value._key === "string" ? value._key : "",
|
|
386
|
+
componentId: typeof f.componentId === "string" ? f.componentId : "",
|
|
387
|
+
props: isRecord3(f.props) ? f.props : {},
|
|
388
|
+
blocks: f.blocks,
|
|
389
|
+
...typeof f.sourcePublishedAt === "string" ? { sourcePublishedAt: f.sourcePublishedAt } : {},
|
|
390
|
+
...typeof f.variant === "string" ? { variant: f.variant } : {}
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// ../richtext/src/read.ts
|
|
395
|
+
var isRecord4 = (v) => !!v && typeof v === "object" && !Array.isArray(v);
|
|
311
396
|
function normalizePortableText(value, key = randomKey) {
|
|
312
397
|
if (!Array.isArray(value)) return [];
|
|
313
398
|
return value.flatMap((raw) => {
|
|
314
|
-
if (!
|
|
399
|
+
if (!isRecord4(raw) || typeof raw._type !== "string") return [];
|
|
315
400
|
const table = migrateTable(raw, key, (v, cellKey) => normalizePortableText(v, cellKey));
|
|
316
401
|
if (table) return [table];
|
|
317
|
-
const node = { ...raw, _key:
|
|
402
|
+
const node = { ...raw, _key: isRecord4(raw) && typeof raw._key === "string" && raw._key ? raw._key : key() };
|
|
318
403
|
if (node._type === "block") {
|
|
319
404
|
if (!Array.isArray(node.markDefs)) node.markDefs = [];
|
|
320
405
|
if (typeof node.style !== "string") node.style = "normal";
|
|
321
406
|
node.children = (Array.isArray(node.children) ? node.children : []).map((c) => {
|
|
322
|
-
if (!
|
|
407
|
+
if (!isRecord4(c)) return c;
|
|
323
408
|
const child = { ...c, _key: typeof c._key === "string" && c._key ? c._key : key() };
|
|
324
409
|
if (child._type === "span" && !Array.isArray(child.marks)) child.marks = [];
|
|
325
410
|
return child;
|
|
@@ -329,9 +414,9 @@ function normalizePortableText(value, key = randomKey) {
|
|
|
329
414
|
});
|
|
330
415
|
}
|
|
331
416
|
function readStructuredContent(props) {
|
|
332
|
-
if (!
|
|
417
|
+
if (!isRecord4(props)) return null;
|
|
333
418
|
const candidate = props.content ?? props.value;
|
|
334
|
-
const arr = Array.isArray(candidate) ? candidate :
|
|
419
|
+
const arr = Array.isArray(candidate) ? candidate : isRecord4(candidate) ? Array.isArray(candidate.content) ? candidate.content : Array.isArray(candidate.value) ? candidate.value : null : null;
|
|
335
420
|
return arr && arr.length > 0 ? arr : null;
|
|
336
421
|
}
|
|
337
422
|
|
|
@@ -368,19 +453,40 @@ function Table({ value }) {
|
|
|
368
453
|
if (rows.length === 0) return null;
|
|
369
454
|
const cells = (row) => (Array.isArray(row?.cells) ? row.cells : []).map((cell) => {
|
|
370
455
|
const Tag = cell?.header === true ? "th" : "td";
|
|
371
|
-
const
|
|
456
|
+
const span2 = typeof cell?.span === "number" && cell.span > 1 ? cell.span : void 0;
|
|
372
457
|
const rowSpan = typeof cell?.rowSpan === "number" && cell.rowSpan > 1 ? cell.rowSpan : void 0;
|
|
373
458
|
const content = Array.isArray(cell?.content) ? cell.content : [];
|
|
374
459
|
const lone = content.length === 1 && content[0]._type === "block" && content[0].style === "normal" && !content[0].listItem;
|
|
375
|
-
return /* @__PURE__ */ jsx2(Tag, { ...
|
|
460
|
+
return /* @__PURE__ */ jsx2(Tag, { ...span2 ? { colSpan: span2 } : {}, ...rowSpan ? { rowSpan } : {}, children: /* @__PURE__ */ jsx2(PT, { value: content, components: lone ? inline : components }) }, cell?._key);
|
|
376
461
|
});
|
|
377
|
-
const
|
|
462
|
+
const rowEl = (row) => /* @__PURE__ */ jsx2("tr", { children: cells(row) }, row?._key);
|
|
378
463
|
const headCount = rows.findIndex((r) => r?.head !== true);
|
|
379
464
|
const head = headCount === -1 ? rows.length : headCount;
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
465
|
+
const layout = tableGroupLayout(value);
|
|
466
|
+
const caption = typeof value.caption === "string" ? value.caption.trim() : "";
|
|
467
|
+
const notes = typeof value.notes === "string" ? value.notes.trim() : "";
|
|
468
|
+
const notesId = notes && typeof value._key === "string" && value._key ? `${value._key}-notes` : void 0;
|
|
469
|
+
const span = (n) => n > 1 ? n : void 0;
|
|
470
|
+
const groupRow = layout.columns.length ? /* @__PURE__ */ jsx2("tr", { children: layout.columns.flatMap(
|
|
471
|
+
(run, i) => run.label === void 0 ? Array.from({ length: run.width }, (_, k) => /* @__PURE__ */ jsx2("td", {}, `${i}-${k}`)) : [/* @__PURE__ */ jsx2("th", { scope: "colgroup", colSpan: span(run.width), "data-group": run.key, children: run.label }, i)]
|
|
472
|
+
) }) : null;
|
|
473
|
+
const bodies = layout.rows.length ? layout.rows.map((run, i) => /* @__PURE__ */ jsxs("tbody", { "data-key": run.key, children: [
|
|
474
|
+
run.label !== void 0 && /* @__PURE__ */ jsx2("tr", { children: /* @__PURE__ */ jsx2("th", { scope: "rowgroup", colSpan: span(layout.width), children: run.label }) }),
|
|
475
|
+
run.rows.map(rowEl)
|
|
476
|
+
] }, i)) : head < rows.length && /* @__PURE__ */ jsx2("tbody", { children: rows.slice(head).map(rowEl) });
|
|
477
|
+
const table = /* @__PURE__ */ jsxs("table", { ...value.legacy === true ? { className: "bcms-table" } : {}, ...notesId ? { "aria-describedby": notesId } : {}, children: [
|
|
478
|
+
caption && /* @__PURE__ */ jsx2("caption", { children: caption }),
|
|
479
|
+
layout.columns.map((run, i) => /* @__PURE__ */ jsx2("colgroup", { span: span(run.width) }, i)),
|
|
480
|
+
(groupRow || head > 0) && /* @__PURE__ */ jsxs("thead", { children: [
|
|
481
|
+
groupRow,
|
|
482
|
+
rows.slice(0, head).map(rowEl)
|
|
483
|
+
] }),
|
|
484
|
+
bodies
|
|
383
485
|
] });
|
|
486
|
+
return notes ? /* @__PURE__ */ jsxs("figure", { "data-rt": "table", children: [
|
|
487
|
+
table,
|
|
488
|
+
/* @__PURE__ */ jsx2("p", { "data-notes": "", id: notesId, children: notes })
|
|
489
|
+
] }) : table;
|
|
384
490
|
}
|
|
385
491
|
function buildComponents(renderBlock) {
|
|
386
492
|
return {
|
|
@@ -402,10 +508,47 @@ function buildComponents(renderBlock) {
|
|
|
402
508
|
[PT_TYPES.block]: ({ value }) => {
|
|
403
509
|
const rendered = renderBlock?.(value);
|
|
404
510
|
if (rendered !== void 0) return /* @__PURE__ */ jsx2(Fragment, { children: rendered });
|
|
511
|
+
const copy = detachedComponentCopy(value);
|
|
512
|
+
if (copy) {
|
|
513
|
+
return /* @__PURE__ */ jsx2("div", { className: "bcms-block bcms-block--component", "data-bcms-component-copy": copy.componentId, "data-bcms-instance": copy.id, children: /* @__PURE__ */ jsx2(BlocksRenderer, { blocks: copy.blocks }) });
|
|
514
|
+
}
|
|
405
515
|
const v = value;
|
|
406
516
|
const key = typeof v.schemaKey === "string" ? v.schemaKey : "block";
|
|
407
517
|
return /* @__PURE__ */ jsx2("div", { className: "bcms-block", "data-bcms-schema-key": key });
|
|
408
518
|
},
|
|
519
|
+
/**
|
|
520
|
+
* A pull quote — the React half of `portableTextToHtml`'s, and the same DOM: the V2 kit's
|
|
521
|
+
* `<figure data-rt="pullquote">`, the name linked (and the quote cited) only through
|
|
522
|
+
* `safeHref`. pull-quote.test.tsx compares the two.
|
|
523
|
+
*/
|
|
524
|
+
[PT_TYPES.pullQuote]: ({ value }) => {
|
|
525
|
+
const v = value;
|
|
526
|
+
const text = (x) => typeof x === "string" ? x.trim() : "";
|
|
527
|
+
const quote = typeof v.quote === "string" ? v.quote : "";
|
|
528
|
+
const name = text(v.name);
|
|
529
|
+
const role = text(v.role);
|
|
530
|
+
const href = safeHref(pullQuoteHref(v.link)) ?? void 0;
|
|
531
|
+
return /* @__PURE__ */ jsxs("figure", { "data-rt": "pullquote", children: [
|
|
532
|
+
/* @__PURE__ */ jsx2("blockquote", { cite: href, children: /* @__PURE__ */ jsx2("p", { children: quote }) }),
|
|
533
|
+
name || role ? /* @__PURE__ */ jsxs("figcaption", { children: [
|
|
534
|
+
name ? href ? /* @__PURE__ */ jsx2("a", { href, "data-name": "", children: name }) : /* @__PURE__ */ jsx2("span", { "data-name": "", children: name }) : null,
|
|
535
|
+
name && role ? ", " : null,
|
|
536
|
+
role ? /* @__PURE__ */ jsx2("span", { "data-role": "", children: role }) : null
|
|
537
|
+
] }) : null
|
|
538
|
+
] });
|
|
539
|
+
},
|
|
540
|
+
/**
|
|
541
|
+
* Key takeaways — the React half of `portableTextToHtml`'s, and the same DOM: the V2 kit's
|
|
542
|
+
* `<aside data-rt="takeaways">`, read through the one `keyTakeawaysView` both renderers share.
|
|
543
|
+
* portable-text-key-takeaways.test.tsx compares the two.
|
|
544
|
+
*/
|
|
545
|
+
[PT_TYPES.keyTakeaways]: ({ value }) => {
|
|
546
|
+
const { title, points } = keyTakeawaysView(value);
|
|
547
|
+
return /* @__PURE__ */ jsxs("aside", { "data-rt": "takeaways", children: [
|
|
548
|
+
title ? /* @__PURE__ */ jsx2("p", { "data-title": "", children: title }) : null,
|
|
549
|
+
/* @__PURE__ */ jsx2("ul", { children: points.map((p, i) => /* @__PURE__ */ jsx2("li", { children: p }, i)) })
|
|
550
|
+
] });
|
|
551
|
+
},
|
|
409
552
|
[PT_TYPES.mention]: ({ value }) => {
|
|
410
553
|
const v = value;
|
|
411
554
|
return /* @__PURE__ */ jsx2("span", { className: "bcms-mention", children: typeof v.label === "string" ? v.label : "" });
|
|
@@ -989,8 +1132,9 @@ function styleOf(block) {
|
|
|
989
1132
|
function withHide(block, wrapperStyle, el, stampAt) {
|
|
990
1133
|
const hide = hideOnAttr(block.style);
|
|
991
1134
|
const state = stampAt ? windowState(block, stampAt) : "live";
|
|
992
|
-
|
|
993
|
-
|
|
1135
|
+
const hidden = !!stampAt && hiddenEverywhere(block);
|
|
1136
|
+
if (!wrapperStyle && !hide && state === "live" && !hidden) return el;
|
|
1137
|
+
return /* @__PURE__ */ jsx15("div", { style: wrapperStyle, ...hide ? { "data-bcms-hide": hide } : {}, ...state === "live" ? {} : { "data-bcms-window": state }, ...hidden ? { "data-bcms-hidden": "all" } : {}, children: el });
|
|
994
1138
|
}
|
|
995
1139
|
function bindingProps(block, annotate) {
|
|
996
1140
|
if (!annotate || !block.id) return {};
|