@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/dist/renderer.js
CHANGED
|
@@ -77,6 +77,14 @@ var PT_TYPES = {
|
|
|
77
77
|
gallery: "gallery",
|
|
78
78
|
/** A table of contents built from the headings — see `PortableTextTableOfContents`. The V2 kit's `<nav data-rt="toc">`. */
|
|
79
79
|
tableOfContents: "tableOfContents",
|
|
80
|
+
/** A footnote, an INLINE object in body text — see `PortableTextFootnote`. The V2 kit's `<sup data-rt="fn">`. */
|
|
81
|
+
footnote: "footnote",
|
|
82
|
+
/** Columns of text — see `PortableTextColumns`. The V2 kit's `<div data-rt="columns">`; the legacy `builtin:columns` reads the same way. */
|
|
83
|
+
columns: "columns",
|
|
84
|
+
/** An author bio — see `PortableTextAuthorBio`. The V2 kit's `<aside data-rt="author">`. */
|
|
85
|
+
authorBio: "authorBio",
|
|
86
|
+
/** Related posts — see `PortableTextRelatedPosts`. The V2 kit's `<aside data-rt="related">`. */
|
|
87
|
+
relatedPosts: "relatedPosts",
|
|
80
88
|
/**
|
|
81
89
|
* A table. Spelled `table`, unprefixed, because that is what every other Portable Text
|
|
82
90
|
* producer calls one — the Sanity-shaped `{_type:"table", rows}` this platform used to refuse
|
|
@@ -106,7 +114,10 @@ var STORABLE_BLOCK_TYPES = /* @__PURE__ */ new Set([
|
|
|
106
114
|
PT_TYPES.keyTakeaways,
|
|
107
115
|
PT_TYPES.embed,
|
|
108
116
|
PT_TYPES.gallery,
|
|
109
|
-
PT_TYPES.tableOfContents
|
|
117
|
+
PT_TYPES.tableOfContents,
|
|
118
|
+
PT_TYPES.columns,
|
|
119
|
+
PT_TYPES.authorBio,
|
|
120
|
+
PT_TYPES.relatedPosts
|
|
110
121
|
]);
|
|
111
122
|
var EMBEDDED_COMPONENT_KEY = "component";
|
|
112
123
|
|
|
@@ -581,6 +592,28 @@ function keyTakeawaysView(value) {
|
|
|
581
592
|
return { title, points };
|
|
582
593
|
}
|
|
583
594
|
|
|
595
|
+
// ../richtext/src/image-layout.ts
|
|
596
|
+
import {
|
|
597
|
+
focalInCrop,
|
|
598
|
+
formatImageRect,
|
|
599
|
+
imageValueUrl,
|
|
600
|
+
normalizeImageCrop,
|
|
601
|
+
normalizeImageFocal,
|
|
602
|
+
objectPosition,
|
|
603
|
+
parseImageRect
|
|
604
|
+
} from "@bettercms-ai/image-url";
|
|
605
|
+
function richTextImageFrame(src, crop, focal) {
|
|
606
|
+
const c = normalizeImageCrop(crop);
|
|
607
|
+
const f = normalizeImageFocal(focal);
|
|
608
|
+
if (!c && !f) return { src, crop: null, focal: null, position: null, data: [] };
|
|
609
|
+
const drawn = imageValueUrl({ url: src, crop: c, focal: f }) ?? src;
|
|
610
|
+
const data = [];
|
|
611
|
+
if (drawn !== src) data.push(["data-src", src]);
|
|
612
|
+
if (c) data.push(["data-crop", formatImageRect(c)]);
|
|
613
|
+
if (f) data.push(["data-focal", `${f.x},${f.y}`]);
|
|
614
|
+
return { src: drawn, crop: c, focal: f, position: f ? objectPosition(focalInCrop(f, c)) : null, data };
|
|
615
|
+
}
|
|
616
|
+
|
|
584
617
|
// ../richtext/src/gallery.ts
|
|
585
618
|
var isRecord3 = (v) => !!v && typeof v === "object" && !Array.isArray(v);
|
|
586
619
|
var galleryMediaSrc = (raw) => safeHref(raw) ?? (raw !== "" && !raw.startsWith("//") && !/[:\s]/.test(raw) ? raw : null);
|
|
@@ -606,9 +639,12 @@ function galleryView(value) {
|
|
|
606
639
|
if (!isRecord3(item) || !isRecord3(item.image)) return;
|
|
607
640
|
const src = galleryMediaSrc(typeof item.image.src === "string" ? item.image.src.trim() : "");
|
|
608
641
|
if (!src) return;
|
|
642
|
+
const frame = richTextImageFrame(src, item.image.crop, item.image.focal);
|
|
609
643
|
images.push({
|
|
610
644
|
key: typeof item._key === "string" && item._key ? item._key : `i${i}`,
|
|
611
|
-
src,
|
|
645
|
+
src: frame.src,
|
|
646
|
+
position: frame.position,
|
|
647
|
+
data: frame.data,
|
|
612
648
|
alt: typeof item.image.alt === "string" ? item.image.alt : "",
|
|
613
649
|
caption: typeof item.caption === "string" ? item.caption.trim() : ""
|
|
614
650
|
});
|
|
@@ -616,16 +652,49 @@ function galleryView(value) {
|
|
|
616
652
|
return { layout, images };
|
|
617
653
|
}
|
|
618
654
|
|
|
619
|
-
// ../richtext/src/
|
|
655
|
+
// ../richtext/src/columns.ts
|
|
656
|
+
var PT_COLUMNS_LIMITS = { min: 2, max: 4 };
|
|
657
|
+
var BUILTIN_COLUMNS_KEY = "builtin:columns";
|
|
620
658
|
var isRecord4 = (v) => !!v && typeof v === "object" && !Array.isArray(v);
|
|
621
|
-
var
|
|
659
|
+
var COLUMNS_STYLES = {
|
|
660
|
+
s: { row: "display:flex;flex-wrap:wrap;gap:0.75rem", col: "flex:1 1 14rem;min-width:0" },
|
|
661
|
+
m: { row: "display:flex;flex-wrap:wrap;gap:1.25rem", col: "flex:1 1 14rem;min-width:0" },
|
|
662
|
+
l: { row: "display:flex;flex-wrap:wrap;gap:2rem", col: "flex:1 1 14rem;min-width:0" }
|
|
663
|
+
};
|
|
664
|
+
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: [] }] }));
|
|
665
|
+
function columnsView(node) {
|
|
666
|
+
if (!isRecord4(node)) return null;
|
|
667
|
+
if (node._type === PT_TYPES.columns) {
|
|
668
|
+
const gap = node.gap === "s" || node.gap === "l" ? node.gap : "m";
|
|
669
|
+
const columns = (Array.isArray(node.columns) ? node.columns : []).map((c, i) => ({
|
|
670
|
+
key: isRecord4(c) && typeof c._key === "string" && c._key ? c._key : `c${i}`,
|
|
671
|
+
content: isRecord4(c) && Array.isArray(c.content) ? c.content : []
|
|
672
|
+
}));
|
|
673
|
+
return { gap, columns };
|
|
674
|
+
}
|
|
675
|
+
if (node._type === PT_TYPES.block && node.schemaKey === BUILTIN_COLUMNS_KEY) {
|
|
676
|
+
const f = isRecord4(node.fields) ? node.fields : {};
|
|
677
|
+
if (!Array.isArray(f.columns) || !f.columns.every((c) => typeof c === "string")) return null;
|
|
678
|
+
const gap = f.gap === "small" ? "s" : f.gap === "large" ? "l" : "m";
|
|
679
|
+
const columns = (Array.isArray(f.columns) ? f.columns : []).slice(0, PT_COLUMNS_LIMITS.max).map((c, i) => ({
|
|
680
|
+
key: `c${i}`,
|
|
681
|
+
content: legacyContent(typeof c === "string" ? c : "", `c${i}`)
|
|
682
|
+
}));
|
|
683
|
+
return { gap, columns };
|
|
684
|
+
}
|
|
685
|
+
return null;
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
// ../richtext/src/component-copy.ts
|
|
689
|
+
var isRecord5 = (v) => !!v && typeof v === "object" && !Array.isArray(v);
|
|
690
|
+
var fieldsOf = (value) => isRecord5(value) && value.schemaKey === EMBEDDED_COMPONENT_KEY && isRecord5(value.fields) ? value.fields : null;
|
|
622
691
|
function detachedComponentCopy(value) {
|
|
623
692
|
const f = fieldsOf(value);
|
|
624
693
|
if (!f || f.detached !== true || !Array.isArray(f.blocks)) return null;
|
|
625
694
|
return {
|
|
626
|
-
id:
|
|
695
|
+
id: isRecord5(value) && typeof value._key === "string" ? value._key : "",
|
|
627
696
|
componentId: typeof f.componentId === "string" ? f.componentId : "",
|
|
628
|
-
props:
|
|
697
|
+
props: isRecord5(f.props) ? f.props : {},
|
|
629
698
|
blocks: f.blocks,
|
|
630
699
|
...typeof f.sourcePublishedAt === "string" ? { sourcePublishedAt: f.sourcePublishedAt } : {},
|
|
631
700
|
...typeof f.variant === "string" ? { variant: f.variant } : {}
|
|
@@ -638,7 +707,7 @@ function componentCopyLines(copy) {
|
|
|
638
707
|
const t = v.replace(/<[^>]+>/g, " ").replace(/ /g, " ").replace(/\s+/g, " ").trim();
|
|
639
708
|
return t || null;
|
|
640
709
|
}
|
|
641
|
-
if (
|
|
710
|
+
if (isRecord5(v)) return line(v.label ?? v.text ?? v.title);
|
|
642
711
|
return null;
|
|
643
712
|
};
|
|
644
713
|
return Object.values(copy.props).map(line).filter((s) => !!s);
|
|
@@ -669,6 +738,14 @@ function portableTextToPlainText(value) {
|
|
|
669
738
|
lines.push(...galleryView(node).images.map((x) => x.caption).filter(Boolean));
|
|
670
739
|
continue;
|
|
671
740
|
}
|
|
741
|
+
const cols = columnsView(node);
|
|
742
|
+
if (cols) {
|
|
743
|
+
for (const c of cols.columns) {
|
|
744
|
+
const text = portableTextToPlainText(c.content);
|
|
745
|
+
if (text) lines.push(text);
|
|
746
|
+
}
|
|
747
|
+
continue;
|
|
748
|
+
}
|
|
672
749
|
const table = migrateTable(node);
|
|
673
750
|
if (table) {
|
|
674
751
|
lines.push(...tableLines(table));
|
|
@@ -709,20 +786,30 @@ var pullQuoteLines = (v) => {
|
|
|
709
786
|
|
|
710
787
|
// ../richtext/src/toc.ts
|
|
711
788
|
import { slugify } from "@bettercms-ai/types";
|
|
712
|
-
var
|
|
713
|
-
var
|
|
789
|
+
var isRecord6 = (v) => !!v && typeof v === "object" && !Array.isArray(v);
|
|
790
|
+
var isHeadingAnchor = (v) => typeof v === "string" && /^[A-Za-z0-9][A-Za-z0-9_:.-]{0,127}$/.test(v);
|
|
791
|
+
var headingText = (node) => (Array.isArray(node.children) ? node.children : []).map((c) => isRecord6(c) && typeof c.text === "string" ? c.text : "").join("").replace(//g, "").trim();
|
|
714
792
|
function outlineOf(value) {
|
|
715
|
-
const
|
|
716
|
-
const nodes = /* @__PURE__ */ new WeakMap();
|
|
793
|
+
const heads = [];
|
|
717
794
|
const used = /* @__PURE__ */ new Set();
|
|
718
795
|
for (const node of Array.isArray(value) ? value : []) {
|
|
719
|
-
if (!
|
|
796
|
+
if (!isRecord6(node) || node._type !== "block" || node.listItem || node.style !== "h2" && node.style !== "h3") continue;
|
|
720
797
|
const text = headingText(node);
|
|
721
798
|
if (!text) continue;
|
|
722
|
-
const
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
799
|
+
const anchor = isHeadingAnchor(node.anchor) && !used.has(node.anchor) ? node.anchor : null;
|
|
800
|
+
if (anchor) used.add(anchor);
|
|
801
|
+
heads.push({ node, text, anchor });
|
|
802
|
+
}
|
|
803
|
+
const items = [];
|
|
804
|
+
const nodes = /* @__PURE__ */ new WeakMap();
|
|
805
|
+
for (const { node, text, anchor } of heads) {
|
|
806
|
+
let id = anchor;
|
|
807
|
+
if (!id) {
|
|
808
|
+
const base = slugify(text);
|
|
809
|
+
id = base;
|
|
810
|
+
for (let n = 2; used.has(id); n++) id = `${base}-${n}`;
|
|
811
|
+
used.add(id);
|
|
812
|
+
}
|
|
726
813
|
nodes.set(node, id);
|
|
727
814
|
items.push({ id, text, depth: node.style === "h2" ? 2 : 3 });
|
|
728
815
|
}
|
|
@@ -739,25 +826,79 @@ function tocTree(items, depth) {
|
|
|
739
826
|
return out;
|
|
740
827
|
}
|
|
741
828
|
function tocView(value) {
|
|
742
|
-
const v =
|
|
829
|
+
const v = isRecord6(value) ? value : {};
|
|
743
830
|
const title = typeof v.title === "string" ? v.title.trim() : "";
|
|
744
831
|
return { title, label: title || "Table of contents", depth: v.depth === "2" ? "2" : "3" };
|
|
745
832
|
}
|
|
746
833
|
|
|
834
|
+
// ../richtext/src/footnote.ts
|
|
835
|
+
var isRecord7 = (v) => !!v && typeof v === "object" && !Array.isArray(v);
|
|
836
|
+
function footnoteOrder(value) {
|
|
837
|
+
const notes = [];
|
|
838
|
+
const nodes = /* @__PURE__ */ new WeakMap();
|
|
839
|
+
for (const node of Array.isArray(value) ? value : []) {
|
|
840
|
+
if (!isRecord7(node) || node._type !== "block" || !Array.isArray(node.children)) continue;
|
|
841
|
+
for (const child of node.children) {
|
|
842
|
+
if (!isRecord7(child) || child._type !== PT_TYPES.footnote) continue;
|
|
843
|
+
nodes.set(child, notes.push(typeof child.note === "string" ? child.note : ""));
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
return { notes, nodes };
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
// ../richtext/src/references.ts
|
|
850
|
+
var PT_RELATED_LIMITS = { titleChars: 40, minCount: 1, maxCount: 6, posts: 6 };
|
|
851
|
+
var isRecord8 = (v) => !!v && typeof v === "object" && !Array.isArray(v);
|
|
852
|
+
var refOf = (v) => isRecord8(v) && typeof v._ref === "string" ? v._ref : "";
|
|
853
|
+
var imageSrc = (raw) => safeHref(raw) ?? (raw !== "" && !raw.startsWith("//") && !/[:\s]/.test(raw) ? raw : null);
|
|
854
|
+
function relatedPostsView(value, resolved = {}) {
|
|
855
|
+
const v = isRecord8(value) ? value : {};
|
|
856
|
+
const title = typeof v.title === "string" ? v.title.trim() : "";
|
|
857
|
+
const mode = v.mode === "manual" || v.mode === "newest" ? v.mode : "topic";
|
|
858
|
+
const n = typeof v.count === "number" && Number.isInteger(v.count) ? v.count : 3;
|
|
859
|
+
const count = Math.min(PT_RELATED_LIMITS.maxCount, Math.max(PT_RELATED_LIMITS.minCount, n));
|
|
860
|
+
const picked = (Array.isArray(v.posts) ? v.posts : []).map(refOf).filter(Boolean);
|
|
861
|
+
const key = typeof v._key === "string" ? v._key : "";
|
|
862
|
+
const ids = mode === "manual" ? picked : (resolved.related?.[key] ?? []).slice(0, count);
|
|
863
|
+
const items = [];
|
|
864
|
+
for (const id of ids) {
|
|
865
|
+
const post = resolved.posts?.[id];
|
|
866
|
+
const url = post && typeof post.url === "string" ? safeHref(post.url) : null;
|
|
867
|
+
if (post && typeof post.title === "string" && url) items.push({ ...post, id, url });
|
|
868
|
+
}
|
|
869
|
+
return { key, title, mode, count, picked, items };
|
|
870
|
+
}
|
|
871
|
+
function authorBioView(value, resolved = {}) {
|
|
872
|
+
const key = isRecord8(value) && typeof value._key === "string" ? value._key : "";
|
|
873
|
+
const id = refOf(isRecord8(value) ? value.author : void 0);
|
|
874
|
+
const a = id ? resolved.authors?.[id] : void 0;
|
|
875
|
+
if (!a || typeof a.name !== "string") return { key, id, author: null };
|
|
876
|
+
const avatar = typeof a.avatar === "string" ? imageSrc(a.avatar.trim()) : null;
|
|
877
|
+
return {
|
|
878
|
+
key,
|
|
879
|
+
id,
|
|
880
|
+
author: {
|
|
881
|
+
name: a.name,
|
|
882
|
+
...avatar ? { avatar } : {},
|
|
883
|
+
...typeof a.bio === "string" && a.bio.trim() ? { bio: a.bio.trim() } : {}
|
|
884
|
+
}
|
|
885
|
+
};
|
|
886
|
+
}
|
|
887
|
+
|
|
747
888
|
// ../richtext/src/read.ts
|
|
748
|
-
var
|
|
889
|
+
var isRecord9 = (v) => !!v && typeof v === "object" && !Array.isArray(v);
|
|
749
890
|
function normalizePortableText(value, key = randomKey) {
|
|
750
891
|
if (!Array.isArray(value)) return [];
|
|
751
892
|
return value.flatMap((raw) => {
|
|
752
|
-
if (!
|
|
893
|
+
if (!isRecord9(raw) || typeof raw._type !== "string") return [];
|
|
753
894
|
const table = migrateTable(raw, key, (v, cellKey) => normalizePortableText(v, cellKey));
|
|
754
895
|
if (table) return [table];
|
|
755
|
-
const node = { ...raw, _key:
|
|
896
|
+
const node = { ...raw, _key: isRecord9(raw) && typeof raw._key === "string" && raw._key ? raw._key : key() };
|
|
756
897
|
if (node._type === "block") {
|
|
757
898
|
if (!Array.isArray(node.markDefs)) node.markDefs = [];
|
|
758
899
|
if (typeof node.style !== "string") node.style = "normal";
|
|
759
900
|
node.children = (Array.isArray(node.children) ? node.children : []).map((c) => {
|
|
760
|
-
if (!
|
|
901
|
+
if (!isRecord9(c)) return c;
|
|
761
902
|
const child = { ...c, _key: typeof c._key === "string" && c._key ? c._key : key() };
|
|
762
903
|
if (child._type === "span" && !Array.isArray(child.marks)) child.marks = [];
|
|
763
904
|
return child;
|
|
@@ -767,9 +908,9 @@ function normalizePortableText(value, key = randomKey) {
|
|
|
767
908
|
});
|
|
768
909
|
}
|
|
769
910
|
function readStructuredContent(props) {
|
|
770
|
-
if (!
|
|
911
|
+
if (!isRecord9(props)) return null;
|
|
771
912
|
const candidate = props.content ?? props.value;
|
|
772
|
-
const arr = Array.isArray(candidate) ? candidate :
|
|
913
|
+
const arr = Array.isArray(candidate) ? candidate : isRecord9(candidate) ? Array.isArray(candidate.content) ? candidate.content : Array.isArray(candidate.value) ? candidate.value : null : null;
|
|
773
914
|
return arr && arr.length > 0 ? arr : null;
|
|
774
915
|
}
|
|
775
916
|
|
|
@@ -1126,6 +1267,47 @@ function XCard({ url, card }) {
|
|
|
1126
1267
|
/* @__PURE__ */ jsx2("a", { href: url, children: card.date || "View on X" })
|
|
1127
1268
|
] });
|
|
1128
1269
|
}
|
|
1270
|
+
function RelatedPosts({ value, resolved }) {
|
|
1271
|
+
const { key, title, mode, count, picked, items } = relatedPostsView(value, resolved);
|
|
1272
|
+
if (!resolved) {
|
|
1273
|
+
return /* @__PURE__ */ jsx2("aside", { "data-rt": "related", "data-key": key, "data-mode": mode, "data-count": count, "data-posts": picked.join(","), "data-title": title || void 0 });
|
|
1274
|
+
}
|
|
1275
|
+
if (!items.length) return null;
|
|
1276
|
+
return /* @__PURE__ */ jsxs("aside", { "data-rt": "related", "data-mode": mode, "data-count": count, "data-posts": picked.join(","), children: [
|
|
1277
|
+
title ? /* @__PURE__ */ jsx2("p", { "data-title": "", children: title }) : null,
|
|
1278
|
+
items.length ? /* @__PURE__ */ jsx2("ul", { children: items.map((p) => /* @__PURE__ */ jsx2("li", { children: /* @__PURE__ */ jsx2("a", { href: p.url, children: p.title }) }, p.id)) }) : null
|
|
1279
|
+
] });
|
|
1280
|
+
}
|
|
1281
|
+
function AuthorBio({ value, resolved }) {
|
|
1282
|
+
const { key, id, author } = authorBioView(value, resolved);
|
|
1283
|
+
if (!resolved) return /* @__PURE__ */ jsx2("aside", { "data-rt": "author", "data-key": key, "data-author": id });
|
|
1284
|
+
if (!author) return null;
|
|
1285
|
+
return /* @__PURE__ */ jsxs("aside", { "data-rt": "author", "data-author": id, children: [
|
|
1286
|
+
author?.avatar ? /* @__PURE__ */ jsx2("img", { src: author.avatar, alt: "" }) : null,
|
|
1287
|
+
author ? /* @__PURE__ */ jsx2("p", { "data-name": "", children: author.name }) : null,
|
|
1288
|
+
author?.bio ? /* @__PURE__ */ jsx2("p", { "data-bio": "", children: author.bio }) : null
|
|
1289
|
+
] });
|
|
1290
|
+
}
|
|
1291
|
+
function Columns({ value, components }) {
|
|
1292
|
+
const view = columnsView(value);
|
|
1293
|
+
if (!view) return null;
|
|
1294
|
+
const s = COLUMNS_STYLES[view.gap];
|
|
1295
|
+
return /* @__PURE__ */ jsx2("div", { "data-rt": "columns", "data-gap": view.gap, "data-cols": view.columns.length, style: styleObject(s.row), children: view.columns.map((c) => /* @__PURE__ */ jsx2("div", { "data-col": "true", style: styleObject(s.col), children: c.content.length ? /* @__PURE__ */ jsx2(PT, { value: c.content, components }) : null }, c.key)) });
|
|
1296
|
+
}
|
|
1297
|
+
function FootnoteMark({ value, footnotes }) {
|
|
1298
|
+
const note = typeof value.note === "string" ? value.note : "";
|
|
1299
|
+
const n = footnotes.nodes.get(value);
|
|
1300
|
+
if (!n) return /* @__PURE__ */ jsx2("sup", { "data-rt": "fn", "data-note": note });
|
|
1301
|
+
return /* @__PURE__ */ jsx2("sup", { "data-rt": "fn", id: `fnref-${n}`, "data-note": note, children: /* @__PURE__ */ jsx2("a", { href: `#fn-${n}`, "aria-label": `Footnote ${n}`, children: n }) });
|
|
1302
|
+
}
|
|
1303
|
+
function FootnoteList({ notes }) {
|
|
1304
|
+
if (!notes.length) return null;
|
|
1305
|
+
return /* @__PURE__ */ jsx2("ol", { className: "cs-footnotes", "data-rt": "footnotes", "aria-label": "Footnotes", children: notes.map((note, i) => /* @__PURE__ */ jsxs("li", { id: `fn-${i + 1}`, children: [
|
|
1306
|
+
note,
|
|
1307
|
+
" ",
|
|
1308
|
+
/* @__PURE__ */ jsx2("a", { href: `#fnref-${i + 1}`, "aria-label": `Back to reference ${i + 1}`, children: "\u21A9" })
|
|
1309
|
+
] }, i)) });
|
|
1310
|
+
}
|
|
1129
1311
|
function Toc({ value, outline }) {
|
|
1130
1312
|
const { title, label, depth } = tocView(value);
|
|
1131
1313
|
const link = (o) => /* @__PURE__ */ jsx2("a", { href: `#${o.id}`, children: o.text });
|
|
@@ -1141,7 +1323,16 @@ function Gallery({ value }) {
|
|
|
1141
1323
|
const { layout, images } = galleryView(value);
|
|
1142
1324
|
const s = GALLERY_STYLES[layout];
|
|
1143
1325
|
return /* @__PURE__ */ jsx2("figure", { "data-rt": "gallery", "data-layout": layout, children: /* @__PURE__ */ jsx2("ul", { style: styleObject(s.ul), tabIndex: layout === "carousel" ? 0 : void 0, children: images.map((x) => /* @__PURE__ */ jsx2("li", { style: styleObject(s.li), children: /* @__PURE__ */ jsxs("figure", { style: styleObject(s.figure), children: [
|
|
1144
|
-
/* @__PURE__ */ jsx2(
|
|
1326
|
+
/* @__PURE__ */ jsx2(
|
|
1327
|
+
"img",
|
|
1328
|
+
{
|
|
1329
|
+
alt: x.alt,
|
|
1330
|
+
style: styleObject(x.position ? `${s.img};object-position:${x.position}` : s.img),
|
|
1331
|
+
loading: "lazy",
|
|
1332
|
+
...Object.fromEntries(x.data),
|
|
1333
|
+
src: x.src
|
|
1334
|
+
}
|
|
1335
|
+
),
|
|
1145
1336
|
x.caption ? /* @__PURE__ */ jsx2("figcaption", { children: x.caption }) : null
|
|
1146
1337
|
] }) }, x.key)) }) });
|
|
1147
1338
|
}
|
|
@@ -1165,8 +1356,8 @@ function Embed({ value, embeds }) {
|
|
|
1165
1356
|
}
|
|
1166
1357
|
return /* @__PURE__ */ jsx2("figure", { "data-rt": "embed", "data-url": url, "data-provider": r.provider, "data-aspect": aspect, "aria-label": title, children: body });
|
|
1167
1358
|
}
|
|
1168
|
-
function buildComponents(renderBlock, embeds = {}, outline = { items: [], nodes: /* @__PURE__ */ new WeakMap() }) {
|
|
1169
|
-
|
|
1359
|
+
function buildComponents(renderBlock, embeds = {}, outline = { items: [], nodes: /* @__PURE__ */ new WeakMap() }, footnotes = { notes: [], nodes: /* @__PURE__ */ new WeakMap() }, resolved) {
|
|
1360
|
+
const components = {
|
|
1170
1361
|
// RT5: every top-level h2 and h3 carries its outline id, as html.ts writes it.
|
|
1171
1362
|
block: {
|
|
1172
1363
|
h2: ({ value, children }) => /* @__PURE__ */ jsx2("h2", { id: outline.nodes.get(value), children }),
|
|
@@ -1194,6 +1385,7 @@ function buildComponents(renderBlock, embeds = {}, outline = { items: [], nodes:
|
|
|
1194
1385
|
if (rendered !== void 0) return /* @__PURE__ */ jsx2(Fragment, { children: rendered });
|
|
1195
1386
|
const builtIn = calloutOrToggle(value);
|
|
1196
1387
|
if (builtIn) return builtIn;
|
|
1388
|
+
if (columnsView(value)) return /* @__PURE__ */ jsx2(Columns, { value, components });
|
|
1197
1389
|
const copy = detachedComponentCopy(value);
|
|
1198
1390
|
if (copy) {
|
|
1199
1391
|
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 }) });
|
|
@@ -1226,6 +1418,9 @@ function buildComponents(renderBlock, embeds = {}, outline = { items: [], nodes:
|
|
|
1226
1418
|
[PT_TYPES.embed]: ({ value }) => /* @__PURE__ */ jsx2(Embed, { value, embeds }),
|
|
1227
1419
|
[PT_TYPES.gallery]: ({ value }) => /* @__PURE__ */ jsx2(Gallery, { value }),
|
|
1228
1420
|
[PT_TYPES.tableOfContents]: ({ value }) => /* @__PURE__ */ jsx2(Toc, { value, outline }),
|
|
1421
|
+
[PT_TYPES.columns]: ({ value }) => /* @__PURE__ */ jsx2(Columns, { value, components }),
|
|
1422
|
+
[PT_TYPES.relatedPosts]: ({ value }) => /* @__PURE__ */ jsx2(RelatedPosts, { value, resolved }),
|
|
1423
|
+
[PT_TYPES.authorBio]: ({ value }) => /* @__PURE__ */ jsx2(AuthorBio, { value, resolved }),
|
|
1229
1424
|
/**
|
|
1230
1425
|
* Key takeaways — the React half of `portableTextToHtml`'s, and the same DOM: the V2 kit's
|
|
1231
1426
|
* `<aside data-rt="takeaways">`, read through the one `keyTakeawaysView` both renderers share.
|
|
@@ -1238,6 +1433,7 @@ function buildComponents(renderBlock, embeds = {}, outline = { items: [], nodes:
|
|
|
1238
1433
|
/* @__PURE__ */ jsx2("ul", { children: points.map((p, i) => /* @__PURE__ */ jsx2("li", { children: p }, i)) })
|
|
1239
1434
|
] });
|
|
1240
1435
|
},
|
|
1436
|
+
[PT_TYPES.footnote]: ({ value }) => /* @__PURE__ */ jsx2(FootnoteMark, { value, footnotes }),
|
|
1241
1437
|
[PT_TYPES.mention]: ({ value }) => {
|
|
1242
1438
|
const v = value;
|
|
1243
1439
|
return /* @__PURE__ */ jsx2("span", { className: "bcms-mention", children: typeof v.label === "string" ? v.label : "" });
|
|
@@ -1302,6 +1498,7 @@ function buildComponents(renderBlock, embeds = {}, outline = { items: [], nodes:
|
|
|
1302
1498
|
unknownType: ({ children }) => /* @__PURE__ */ jsx2(Fragment, { children: children ?? null }),
|
|
1303
1499
|
unknownBlockStyle: ({ children }) => /* @__PURE__ */ jsx2("p", { children })
|
|
1304
1500
|
};
|
|
1501
|
+
return components;
|
|
1305
1502
|
}
|
|
1306
1503
|
var isListItem = (node) => node._type === "block" && typeof node.listItem === "string" && !!node.listItem;
|
|
1307
1504
|
function runsOf(blocks) {
|
|
@@ -1342,10 +1539,14 @@ function calloutOrToggle(value) {
|
|
|
1342
1539
|
}
|
|
1343
1540
|
return null;
|
|
1344
1541
|
}
|
|
1345
|
-
function PortableText({ value, bindingBase, renderBlock, embeds }) {
|
|
1542
|
+
function PortableText({ value, bindingBase, renderBlock, embeds, resolved }) {
|
|
1346
1543
|
const blocks = React.useMemo(() => normalizePortableText(readValue(value)), [value]);
|
|
1347
1544
|
const outline = React.useMemo(() => outlineOf(blocks), [blocks]);
|
|
1348
|
-
const
|
|
1545
|
+
const footnotes = React.useMemo(() => footnoteOrder(blocks), [blocks]);
|
|
1546
|
+
const components = React.useMemo(
|
|
1547
|
+
() => buildComponents(renderBlock, embeds, outline, footnotes, resolved),
|
|
1548
|
+
[renderBlock, embeds, outline, footnotes, resolved]
|
|
1549
|
+
);
|
|
1349
1550
|
const listComponents = React.useMemo(() => {
|
|
1350
1551
|
if (!bindingBase) return null;
|
|
1351
1552
|
const keys = new Set(blocks.map((b) => b._key));
|
|
@@ -1361,10 +1562,19 @@ function PortableText({ value, bindingBase, renderBlock, embeds }) {
|
|
|
1361
1562
|
};
|
|
1362
1563
|
}, [bindingBase, blocks, components]);
|
|
1363
1564
|
if (blocks.length === 0) return null;
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1565
|
+
const notes = /* @__PURE__ */ jsx2(FootnoteList, { notes: footnotes.notes });
|
|
1566
|
+
if (!bindingBase || !listComponents) {
|
|
1567
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1568
|
+
/* @__PURE__ */ jsx2(PT, { value: blocks, components }),
|
|
1569
|
+
notes
|
|
1570
|
+
] });
|
|
1571
|
+
}
|
|
1572
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1573
|
+
runsOf(blocks).map(
|
|
1574
|
+
(run) => isListItem(run[0]) ? /* @__PURE__ */ jsx2(PT, { value: run, components: listComponents }, run[0]._key) : /* @__PURE__ */ jsx2(Bound, { base: bindingBase, nodeKey: run[0]._key, children: /* @__PURE__ */ jsx2(PT, { value: run, components }) }, run[0]._key)
|
|
1575
|
+
),
|
|
1576
|
+
notes
|
|
1577
|
+
] });
|
|
1368
1578
|
}
|
|
1369
1579
|
function readValue(value) {
|
|
1370
1580
|
if (Array.isArray(value)) return value;
|
|
@@ -1538,7 +1748,7 @@ function Spacer({ height = 32, className = "" }) {
|
|
|
1538
1748
|
// src/blocks/Columns.tsx
|
|
1539
1749
|
import "react";
|
|
1540
1750
|
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
1541
|
-
function
|
|
1751
|
+
function Columns2({
|
|
1542
1752
|
columns,
|
|
1543
1753
|
gap = 16,
|
|
1544
1754
|
className = "",
|
|
@@ -1792,7 +2002,7 @@ var BLOCK_REGISTRY = {
|
|
|
1792
2002
|
icon: "minus"
|
|
1793
2003
|
},
|
|
1794
2004
|
columns: {
|
|
1795
|
-
component:
|
|
2005
|
+
component: Columns2,
|
|
1796
2006
|
label: "Columns",
|
|
1797
2007
|
icon: "columns"
|
|
1798
2008
|
},
|