@bettercms-ai/ui 0.17.1 → 0.19.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/registry.js CHANGED
@@ -152,6 +152,7 @@ function BlocksRenderer({
152
152
  }
153
153
 
154
154
  // ../richtext/src/types.ts
155
+ var PT_TABLE_COLUMN_TYPES = ["text", "number", "money", "boolean", "rating", "link"];
155
156
  function pullQuoteHref(link) {
156
157
  if (typeof link === "string") return link.trim();
157
158
  if (link && typeof link === "object" && typeof link.href === "string") {
@@ -159,6 +160,8 @@ function pullQuoteHref(link) {
159
160
  }
160
161
  return "";
161
162
  }
163
+ var isPortableTextBlock = (n) => !!n && typeof n === "object" && n._type === "block";
164
+ var isPortableTextSpan = (n) => !!n && typeof n === "object" && n._type === "span";
162
165
  var PT_TYPES = {
163
166
  /** A generic embedded block: `{schemaKey, schemaLabel, fields}`, the successor to Lexical's BlockNode. */
164
167
  block: "bcmsBlock",
@@ -380,7 +383,14 @@ function normalizeCell(raw, at, mint, read) {
380
383
  ...raw.header === true ? { header: true } : {},
381
384
  ...span === void 0 ? {} : { span },
382
385
  ...rowSpan === void 0 ? {} : { rowSpan },
383
- ...typeof raw.group === "string" ? { group: raw.group } : {}
386
+ ...typeof raw.group === "string" ? { group: raw.group } : {},
387
+ ...raw.highlight === true ? { highlight: true } : {},
388
+ ...raw.sortable === true ? { sortable: true } : {},
389
+ // The V2 table's typed cells: the column (first-row cell), the per-cell type, the typed value.
390
+ ...columnOf(raw.column) ? { column: columnOf(raw.column) } : {},
391
+ ...isColumnType(raw.type) ? { type: raw.type } : {},
392
+ ...typeof raw.value === "boolean" || typeof raw.value === "number" && Number.isFinite(raw.value) ? { value: raw.value } : {},
393
+ ...typeof raw.note === "string" && raw.note ? { note: raw.note } : {}
384
394
  };
385
395
  }
386
396
  function normalizeRow(raw, at, mint, read) {
@@ -389,7 +399,11 @@ function normalizeRow(raw, at, mint, read) {
389
399
  _key: isRecord(raw) && isValidKey(raw._key) ? raw._key : mint("r", at),
390
400
  cells: cells.map((c, col) => normalizeCell(c, [at, col], mint, read)),
391
401
  ...isRecord(raw) && raw.head === true ? { head: true } : {},
392
- ...isRecord(raw) && typeof raw.group === "string" ? { group: raw.group } : {}
402
+ ...isRecord(raw) && typeof raw.group === "string" ? { group: raw.group } : {},
403
+ // The V2 table's two row flags ("Totals row", "Highlight this row"); see `PortableTextTableRow`.
404
+ ...isRecord(raw) && raw.footer === true ? { footer: true } : {},
405
+ ...isRecord(raw) && raw.highlight === true ? { highlight: true } : {},
406
+ ...isRecord(raw) && isColumnType(raw.type) ? { type: raw.type } : {}
393
407
  };
394
408
  }
395
409
  function builtInTableToTable(node, key = randomKey) {
@@ -446,9 +460,62 @@ function tableExtras(node) {
446
460
  ...typeof node.caption === "string" ? { caption: node.caption } : {},
447
461
  ...typeof node.notes === "string" ? { notes: node.notes } : {},
448
462
  ...rowGroups ? { rowGroups } : {},
449
- ...columnGroups ? { columnGroups } : {}
463
+ ...columnGroups ? { columnGroups } : {},
464
+ ...node.filterable === true ? { filterable: true } : {},
465
+ ...jsonLdOf(node.jsonLd) ? { jsonLd: jsonLdOf(node.jsonLd) } : {},
466
+ ...designOf(node.design) ? { design: designOf(node.design) } : {},
467
+ ...footnotesOf(node.footnotes) ? { footnotes: footnotesOf(node.footnotes) } : {}
450
468
  };
451
469
  }
470
+ function footnotesOf(v) {
471
+ if (!Array.isArray(v)) return void 0;
472
+ const out = v.filter((f) => isRecord(f) && typeof f._key === "string" && f._key !== "").map((f) => ({ _key: f._key, text: typeof f.text === "string" ? f.text : "" }));
473
+ return out.length ? out : void 0;
474
+ }
475
+ function footnoteNumbers(table) {
476
+ return new Map((footnotesOf(table.footnotes) ?? []).map((f, i) => [f._key, i + 1]));
477
+ }
478
+ function designOf(v) {
479
+ if (!isRecord(v)) return void 0;
480
+ const d = {
481
+ ...v.responsive === "stack" || v.responsive === "cards" ? { responsive: v.responsive } : {},
482
+ ...v.sticky === true ? { sticky: true } : {}
483
+ };
484
+ return Object.keys(d).length ? d : void 0;
485
+ }
486
+ var JSONLD_TYPES = ["ItemList", "Dataset", "Offers"];
487
+ function jsonLdOf(v) {
488
+ if (!isRecord(v) || !JSONLD_TYPES.includes(v.type)) return void 0;
489
+ return {
490
+ type: v.type,
491
+ ...v.axis === "rows" || v.axis === "columns" ? { axis: v.axis } : {},
492
+ ...typeof v.priceRow === "string" && v.priceRow ? { priceRow: v.priceRow } : {}
493
+ };
494
+ }
495
+ function tableSections(table) {
496
+ const rows = Array.isArray(table.rows) ? table.rows : [];
497
+ const headCount = rows.findIndex((r) => !isRecord(r) || r.head !== true);
498
+ const head = rows.slice(0, headCount === -1 ? rows.length : headCount);
499
+ let footFrom = rows.length;
500
+ while (footFrom > head.length && isRecord(rows[footFrom - 1]) && rows[footFrom - 1].footer === true) footFrom -= 1;
501
+ return { head, body: rows.slice(head.length, footFrom), foot: rows.slice(footFrom) };
502
+ }
503
+ function highlightedColumns(table) {
504
+ return markedColumns(table, "highlight");
505
+ }
506
+ function sortableColumns(table) {
507
+ return markedColumns(table, "sortable");
508
+ }
509
+ function markedColumns(table, flag) {
510
+ const out = /* @__PURE__ */ new Set();
511
+ const first = Array.isArray(table.rows) ? table.rows[0] : void 0;
512
+ let at = 0;
513
+ for (const cell of isRecord(first) && Array.isArray(first.cells) ? first.cells : []) {
514
+ if (isRecord(cell) && cell[flag] === true) out.add(at);
515
+ at += isRecord(cell) && typeof cell.span === "number" && Number.isInteger(cell.span) && cell.span > 1 ? cell.span : 1;
516
+ }
517
+ return out;
518
+ }
452
519
  function tableGroupLayout(table) {
453
520
  const rows = Array.isArray(table.rows) ? table.rows : [];
454
521
  const labels = (list) => new Map(
@@ -466,8 +533,7 @@ function tableGroupLayout(table) {
466
533
  else columns.push({ width: spanOf2(cell), ...key === void 0 ? {} : { key, label: colLabels.get(key) } });
467
534
  }
468
535
  const rowLabels = labels(table.rowGroups);
469
- const headCount = rows.findIndex((r) => !isRecord(r) || r.head !== true);
470
- const body = headCount === -1 ? [] : rows.slice(headCount);
536
+ const { body } = tableSections(table);
471
537
  const runs = [];
472
538
  for (const row of body) {
473
539
  const key = isRecord(row) && typeof row.group === "string" && rowLabels.has(row.group) ? row.group : void 0;
@@ -481,6 +547,133 @@ function tableGroupLayout(table) {
481
547
  width
482
548
  };
483
549
  }
550
+ var NUMERIC_TYPES = /* @__PURE__ */ new Set(["number", "money", "rating"]);
551
+ var isColumnType = (v) => PT_TABLE_COLUMN_TYPES.includes(v);
552
+ function columnOf(v) {
553
+ if (!isRecord(v)) return void 0;
554
+ const str = (x) => typeof x === "string" && x !== "" ? x : void 0;
555
+ const int = (x, lo, hi) => Number.isInteger(x) && x >= lo && x <= hi ? x : void 0;
556
+ const c = {
557
+ ...isColumnType(v.type) ? { type: v.type } : {},
558
+ ...str(v.description) ? { description: str(v.description) } : {},
559
+ ...str(v.unit) ? { unit: str(v.unit) } : {},
560
+ ...typeof v.currency === "string" && /^[A-Z]{3}$/.test(v.currency) ? { currency: v.currency } : {},
561
+ ...int(v.decimals, 0, 10) !== void 0 ? { decimals: int(v.decimals, 0, 10) } : {},
562
+ ...v.align === "start" || v.align === "center" || v.align === "end" ? { align: v.align } : {},
563
+ ...str(v.trueLabel) ? { trueLabel: str(v.trueLabel) } : {},
564
+ ...str(v.falseLabel) ? { falseLabel: str(v.falseLabel) } : {},
565
+ ...int(v.max, 1, 10) !== void 0 ? { max: int(v.max, 1, 10) } : {}
566
+ };
567
+ return Object.keys(c).length ? c : void 0;
568
+ }
569
+ function tableColumns(table) {
570
+ const out = [];
571
+ const first = Array.isArray(table.rows) ? table.rows[0] : void 0;
572
+ for (const cell of isRecord(first) && Array.isArray(first.cells) ? first.cells : []) {
573
+ const w = isRecord(cell) && typeof cell.span === "number" && Number.isInteger(cell.span) && cell.span > 1 ? cell.span : 1;
574
+ const col = isRecord(cell) ? columnOf(cell.column) : void 0;
575
+ for (let i = 0; i < w; i += 1) out.push(col);
576
+ }
577
+ return out;
578
+ }
579
+ function isTypedTable(table) {
580
+ if (tableColumns(table).some(Boolean)) return true;
581
+ return (Array.isArray(table.rows) ? table.rows : []).some(
582
+ (r) => isRecord(r) && (r.type !== void 0 || Array.isArray(r.cells) && r.cells.some((c) => isRecord(c) && (c.type !== void 0 || c.value !== void 0)))
583
+ );
584
+ }
585
+ var cellTypeOf = (col, row, cell) => (isRecord(cell) && isColumnType(cell.type) ? cell.type : void 0) ?? (isRecord(row) && isColumnType(row.type) ? row.type : void 0) ?? col?.type ?? "text";
586
+ var columnAlign = (col, type) => col?.align ?? (NUMERIC_TYPES.has(type) ? "end" : "start");
587
+ var TRUE_WORDS = /* @__PURE__ */ new Set(["\u2713", "\u2714", "\u2714\uFE0F", "\u2611", "\u2705", "yes", "y", "true", "included", "available", "supported", "x \u2713", "check", "\u25CF"]);
588
+ var FALSE_WORDS = /* @__PURE__ */ new Set(["\u2717", "\u2715", "\u2716", "\u274C", "\xD7", "x", "no", "n", "false", "not included", "unavailable", "not available", "unsupported", "\u2014", "\u2013", "-", "\u25CB"]);
589
+ function toBool(v) {
590
+ if (typeof v === "boolean") return v;
591
+ if (typeof v !== "string") return null;
592
+ const s = v.trim().toLowerCase();
593
+ return TRUE_WORDS.has(s) ? true : FALSE_WORDS.has(s) ? false : null;
594
+ }
595
+ function toNumber(v) {
596
+ if (typeof v === "number") return Number.isFinite(v) ? v : null;
597
+ if (typeof v !== "string") return null;
598
+ const s = v.replace(/[,\s]/g, "").replace(/[^\d.+-]/g, "");
599
+ if (!s || !/^[+-]?\d*\.?\d+$/.test(s)) return null;
600
+ return Number(s);
601
+ }
602
+ var formatNumber = (n, decimals) => new Intl.NumberFormat("en-US", decimals != null ? { minimumFractionDigits: decimals, maximumFractionDigits: decimals } : { maximumFractionDigits: 6 }).format(n);
603
+ function formatMoney(n, currency = "USD", decimals) {
604
+ try {
605
+ const whole = Number.isInteger(n) && decimals == null;
606
+ return new Intl.NumberFormat("en-US", { style: "currency", currency, minimumFractionDigits: whole ? 0 : decimals ?? 2, maximumFractionDigits: whole ? 0 : decimals ?? 2 }).format(n);
607
+ } catch {
608
+ return `${formatNumber(n, decimals)} ${currency}`;
609
+ }
610
+ }
611
+ var boolLabels = (col) => ({ yes: col?.trueLabel || "Included", no: col?.falseLabel || "Not included" });
612
+ function typedCellText(col, type, v) {
613
+ if (v == null || v === "") return "";
614
+ const words = String(v).replace(/\s+/g, " ").trim();
615
+ switch (type) {
616
+ case "boolean": {
617
+ const b = toBool(v);
618
+ return b == null ? words : b ? boolLabels(col).yes : boolLabels(col).no;
619
+ }
620
+ case "number": {
621
+ const n = toNumber(v);
622
+ return n == null ? words : `${formatNumber(n, col?.decimals)}${col?.unit ? /^[%°]/.test(col.unit) ? col.unit : ` ${col.unit}` : ""}`;
623
+ }
624
+ case "money": {
625
+ const n = toNumber(v);
626
+ return n == null ? words : formatMoney(n, col?.currency || "USD", col?.decimals);
627
+ }
628
+ case "rating": {
629
+ const n = toNumber(v);
630
+ return n == null ? words : `${formatNumber(n, 1).replace(/\.0$/, "")} out of ${col?.max ?? 5}`;
631
+ }
632
+ default:
633
+ return words;
634
+ }
635
+ }
636
+ var escHtml = (s) => s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
637
+ function typedCellView(column, row, cell, inHead) {
638
+ if (inHead) {
639
+ const align2 = columnAlign(column, column?.type ?? "text");
640
+ return { lead: align2 !== "start" ? [["data-align", align2]] : [], tail: column?.description ? [["title", column.description]] : [] };
641
+ }
642
+ const type = cellTypeOf(column, row, cell);
643
+ const words = portableTextWords(cell.content);
644
+ const v = typeof cell.value === "number" || typeof cell.value === "boolean" ? cell.value : words;
645
+ const tail = isColumnType(cell.type) ? [["data-type", cell.type]] : [];
646
+ let inner;
647
+ if (v === "") {
648
+ inner = cell.header === true ? "" : '<span aria-hidden="true" class="bt-dash">\u2014</span><span class="bt-sr">No value</span>';
649
+ tail.push(["data-empty", ""]);
650
+ } else if (type === "boolean") {
651
+ const b = toBool(v);
652
+ if (b == null) inner = escHtml(words);
653
+ else {
654
+ const { yes, no } = boolLabels(column);
655
+ inner = `<span aria-hidden="true" class="bt-bool" data-bool="${b}">${b ? "\u2713" : "\u2013"}</span><span class="bt-sr">${escHtml(b ? yes : no)}</span>`;
656
+ tail.push(["data-value", String(b)]);
657
+ }
658
+ } else if (type === "rating") {
659
+ const n = toNumber(v);
660
+ if (n == null) inner = escHtml(words);
661
+ else {
662
+ inner = `<span aria-hidden="true" class="bt-star">\u2605</span> ${escHtml(formatNumber(n, 1).replace(/\.0$/, ""))}<span class="bt-dim"> / ${column?.max ?? 5}</span>`;
663
+ tail.push(["data-value", String(n)]);
664
+ }
665
+ } else if (type === "number" || type === "money") {
666
+ const n = toNumber(v);
667
+ inner = escHtml(typedCellText(column, type, v));
668
+ if (n != null) tail.push(["data-value", String(n)]);
669
+ }
670
+ const align = columnAlign(column, type);
671
+ if (align !== "start") tail.push(["data-align", align]);
672
+ return { lead: [], tail, ...inner === void 0 ? {} : { inner } };
673
+ }
674
+ function portableTextWords(content) {
675
+ return (Array.isArray(content) ? content : []).map((b) => isRecord(b) && Array.isArray(b.children) ? b.children.map((c) => isRecord(c) && typeof c.text === "string" ? c.text : "").join("") : "").join(" ").replace(/\s+/g, " ").trim();
676
+ }
484
677
 
485
678
  // ../richtext/src/key-takeaways.ts
486
679
  var isRecord2 = (v) => !!v && typeof v === "object" && !Array.isArray(v);
@@ -506,6 +699,77 @@ function detachedComponentCopy(value) {
506
699
  ...typeof f.variant === "string" ? { variant: f.variant } : {}
507
700
  };
508
701
  }
702
+ function componentCopyLines(copy) {
703
+ const line = (v) => {
704
+ if (typeof v === "number" && Number.isFinite(v)) return String(v);
705
+ if (typeof v === "string") {
706
+ const t = v.replace(/<[^>]+>/g, " ").replace(/&nbsp;/g, " ").replace(/\s+/g, " ").trim();
707
+ return t || null;
708
+ }
709
+ if (isRecord3(v)) return line(v.label ?? v.text ?? v.title);
710
+ return null;
711
+ };
712
+ return Object.values(copy.props).map(line).filter((s) => !!s);
713
+ }
714
+
715
+ // ../richtext/src/plain.ts
716
+ function portableTextToPlainText(value) {
717
+ if (!Array.isArray(value)) return "";
718
+ const lines = [];
719
+ for (const node of value) {
720
+ if (isPortableTextBlock(node)) {
721
+ lines.push(node.children.map((c) => isPortableTextSpan(c) ? c.text : "").join(""));
722
+ continue;
723
+ }
724
+ if (node._type === PT_TYPES.code && typeof node.code === "string") {
725
+ lines.push(node.code);
726
+ }
727
+ if (node._type === PT_TYPES.pullQuote) {
728
+ lines.push(...pullQuoteLines(node));
729
+ continue;
730
+ }
731
+ if (node._type === PT_TYPES.keyTakeaways) {
732
+ const { title, points } = keyTakeawaysView(node);
733
+ lines.push(...title ? [title] : [], ...points);
734
+ continue;
735
+ }
736
+ const table = migrateTable(node);
737
+ if (table) {
738
+ lines.push(...tableLines(table));
739
+ continue;
740
+ }
741
+ const copy = detachedComponentCopy(node);
742
+ if (copy) {
743
+ lines.push(...componentCopyLines(copy));
744
+ continue;
745
+ }
746
+ }
747
+ return lines.join("\n");
748
+ }
749
+ var tableLines = (table) => {
750
+ const rows = Array.isArray(table.rows) ? table.rows : [];
751
+ const rowLine = (row) => (Array.isArray(row?.cells) ? row.cells : []).map((cell) => portableTextToPlainText(cell?.content).replace(/\s+/g, " ").trim()).join(" ").trim();
752
+ const layout = tableGroupLayout(table);
753
+ const one = (s) => typeof s === "string" ? s.replace(/\s+/g, " ").trim() : "";
754
+ const head = layout.rows.length ? rows.slice(0, rows.length - layout.rows.reduce((n, r) => n + r.rows.length, 0)) : rows;
755
+ const lines = [
756
+ one(table.caption),
757
+ layout.columns.map((r) => one(r.label)).filter(Boolean).join(" "),
758
+ ...head.map(rowLine),
759
+ ...layout.rows.flatMap((run) => [...run.label === void 0 ? [] : [one(run.label)], ...run.rows.map(rowLine)]),
760
+ one(table.notes),
761
+ // footnotes, numbered as the html numbers them (V2 tableSearchText reads them too)
762
+ ...(Array.isArray(table.footnotes) ? table.footnotes : []).map((f, i) => one(`[${i + 1}] ${typeof f?.text === "string" ? f.text : ""}`))
763
+ ];
764
+ const fnCount = Array.isArray(table.footnotes) ? table.footnotes.length : 0;
765
+ const extras = /* @__PURE__ */ new Set([0, 1, lines.length - 1 - fnCount]);
766
+ return lines.filter((l, i) => l !== "" || !extras.has(i));
767
+ };
768
+ var pullQuoteLines = (v) => {
769
+ const text = (x) => typeof x === "string" ? x.trim() : "";
770
+ const by = [text(v.name), text(v.role)].filter(Boolean).join(", ");
771
+ return [text(v.quote), ...by ? [`\u2014 ${by}`] : []].filter(Boolean);
772
+ };
509
773
 
510
774
  // ../richtext/src/read.ts
511
775
  var isRecord4 = (v) => !!v && typeof v === "object" && !Array.isArray(v);
@@ -567,41 +831,104 @@ function Table({ value }) {
567
831
  );
568
832
  const rows = Array.isArray(value?.rows) ? value.rows : [];
569
833
  if (rows.length === 0) return null;
570
- const cells = (row) => (Array.isArray(row?.cells) ? row.cells : []).map((cell) => {
571
- const Tag = cell?.header === true ? "th" : "td";
572
- const span2 = typeof cell?.span === "number" && cell.span > 1 ? cell.span : void 0;
573
- const rowSpan = typeof cell?.rowSpan === "number" && cell.rowSpan > 1 ? cell.rowSpan : void 0;
574
- const content = Array.isArray(cell?.content) ? cell.content : [];
575
- const lone = content.length === 1 && content[0]._type === "block" && content[0].style === "normal" && !content[0].listItem;
576
- return /* @__PURE__ */ jsx3(Tag, { ...span2 ? { colSpan: span2 } : {}, ...rowSpan ? { rowSpan } : {}, children: /* @__PURE__ */ jsx3(PT, { value: content, components: lone ? inline : components }) }, cell?._key);
577
- });
578
- const rowEl = (row) => /* @__PURE__ */ jsx3("tr", { children: cells(row) }, row?._key);
579
- const headCount = rows.findIndex((r) => r?.head !== true);
580
- const head = headCount === -1 ? rows.length : headCount;
834
+ const marked = highlightedColumns(value);
835
+ const sortable = sortableColumns(value);
836
+ const design = designOf(value.design);
837
+ const stacked = design?.responsive === "stack" || design?.responsive === "cards";
838
+ const role = (r) => stacked ? { role: r } : {};
839
+ const labels = [];
840
+ if (stacked) {
841
+ const headRows = tableSections(value).head;
842
+ let at = 0;
843
+ for (const c of Array.isArray(headRows[headRows.length - 1]?.cells) ? headRows[headRows.length - 1].cells : []) {
844
+ const text = portableTextToPlainText(Array.isArray(c?.content) ? c.content : []).replace(/\s+/g, " ").trim();
845
+ const w = typeof c?.span === "number" && c.span > 1 ? c.span : 1;
846
+ for (let i = 0; i < w; i += 1) labels[at + i] = text;
847
+ at += w;
848
+ }
849
+ }
850
+ const columns = tableColumns(value);
851
+ const typed = isTypedTable(value);
852
+ const headSet = new Set(tableSections(value).head);
853
+ const props = (list) => Object.fromEntries(list);
854
+ const fnNumber = footnoteNumbers(value);
855
+ const footnotes = footnotesOf(value.footnotes) ?? [];
856
+ const tableId = typeof value._key === "string" && value._key ? value._key : "tbl";
857
+ const cells = (row) => {
858
+ let col = 0;
859
+ return (Array.isArray(row?.cells) ? row.cells : []).map((cell) => {
860
+ const Tag = cell?.header === true ? "th" : "td";
861
+ const span2 = typeof cell?.span === "number" && cell.span > 1 ? cell.span : void 0;
862
+ const at = col;
863
+ col += span2 ?? 1;
864
+ const rowSpan = typeof cell?.rowSpan === "number" && cell.rowSpan > 1 ? cell.rowSpan : void 0;
865
+ const n = typeof cell?.note === "string" ? fnNumber.get(cell.note) : void 0;
866
+ const content = Array.isArray(cell?.content) ? cell.content : [];
867
+ const lone = content.length === 1 && content[0]._type === "block" && content[0].style === "normal" && !content[0].listItem;
868
+ const view = typed ? typedCellView(columns[at], row, cell ?? {}, headSet.has(row)) : void 0;
869
+ const noteAttr = n ? [["data-note", cell.note]] : [];
870
+ const tail = [...(view?.tail ?? []).filter(([k]) => k !== "data-align"), ...noteAttr, ...(view?.tail ?? []).filter(([k]) => k === "data-align")];
871
+ const supHtml = n ? `<sup><a href="#${tableId}-fn-${n}" aria-label="Note ${n}">${n}</a></sup>` : "";
872
+ return /* @__PURE__ */ jsx3(Tag, { ...Tag === "td" ? role("cell") : {}, ...span2 ? { colSpan: span2 } : {}, ...rowSpan ? { rowSpan } : {}, ...props(view?.lead ?? []), ...stacked && Tag === "td" ? { "data-label": labels[at] || `Column ${at + 1}` } : {}, ...marked.has(at) ? { "data-highlight": "true" } : {}, ...props(tail), ...view?.inner !== void 0 ? { dangerouslySetInnerHTML: { __html: view.inner + supHtml } } : {}, children: view?.inner !== void 0 ? void 0 : /* @__PURE__ */ jsxs2(Fragment, { children: [
873
+ /* @__PURE__ */ jsx3(PT, { value: content, components: lone ? inline : components }),
874
+ n ? /* @__PURE__ */ jsx3("sup", { children: /* @__PURE__ */ jsx3("a", { href: `#${tableId}-fn-${n}`, "aria-label": `Note ${n}`, children: n }) }) : null
875
+ ] }) }, cell?._key);
876
+ });
877
+ };
878
+ const rowEl = (row) => /* @__PURE__ */ jsx3("tr", { ...role("row"), ...typeof row?.type === "string" ? { "data-type": row.type } : {}, ...row?.highlight === true ? { "data-highlight": "true" } : {}, children: cells(row) }, row?._key);
879
+ const sections = tableSections(value);
880
+ const head = sections.head.length;
881
+ const foot = sections.foot;
581
882
  const layout = tableGroupLayout(value);
582
883
  const caption = typeof value.caption === "string" ? value.caption.trim() : "";
583
884
  const notes = typeof value.notes === "string" ? value.notes.trim() : "";
584
- const notesId = notes && typeof value._key === "string" && value._key ? `${value._key}-notes` : void 0;
885
+ const notesId = (notes || footnotes.length) && typeof value._key === "string" && value._key ? `${value._key}-notes` : void 0;
585
886
  const span = (n) => n > 1 ? n : void 0;
586
- const groupRow = layout.columns.length ? /* @__PURE__ */ jsx3("tr", { children: layout.columns.flatMap(
587
- (run, i) => run.label === void 0 ? Array.from({ length: run.width }, (_, k) => /* @__PURE__ */ jsx3("td", {}, `${i}-${k}`)) : [/* @__PURE__ */ jsx3("th", { scope: "colgroup", colSpan: span(run.width), "data-group": run.key, children: run.label }, i)]
887
+ const groupRow = layout.columns.length ? /* @__PURE__ */ jsx3("tr", { ...role("row"), children: layout.columns.flatMap(
888
+ (run, i) => run.label === void 0 ? Array.from({ length: run.width }, (_, k) => /* @__PURE__ */ jsx3("td", { ...role("cell") }, `${i}-${k}`)) : [/* @__PURE__ */ jsx3("th", { ...role("columnheader"), scope: "colgroup", colSpan: span(run.width), "data-group": run.key, children: run.label }, i)]
588
889
  ) }) : null;
589
- const bodies = layout.rows.length ? layout.rows.map((run, i) => /* @__PURE__ */ jsxs2("tbody", { "data-key": run.key, children: [
590
- run.label !== void 0 && /* @__PURE__ */ jsx3("tr", { children: /* @__PURE__ */ jsx3("th", { scope: "rowgroup", colSpan: span(layout.width), children: run.label }) }),
890
+ const bodies = layout.rows.length ? layout.rows.map((run, i) => /* @__PURE__ */ jsxs2("tbody", { ...role("rowgroup"), "data-key": run.key, children: [
891
+ run.label !== void 0 && /* @__PURE__ */ jsx3("tr", { ...role("row"), children: /* @__PURE__ */ jsx3("th", { ...role("rowheader"), scope: "rowgroup", colSpan: span(layout.width), children: run.label }) }),
591
892
  run.rows.map(rowEl)
592
- ] }, i)) : head < rows.length && /* @__PURE__ */ jsx3("tbody", { children: rows.slice(head).map(rowEl) });
593
- const table = /* @__PURE__ */ jsxs2("table", { ...value.legacy === true ? { className: "bcms-table" } : {}, ...notesId ? { "aria-describedby": notesId } : {}, children: [
893
+ ] }, i)) : sections.body.length > 0 && /* @__PURE__ */ jsx3("tbody", { ...role("rowgroup"), children: sections.body.map(rowEl) });
894
+ const colProps = (i) => {
895
+ const c = columns[i];
896
+ return {
897
+ ...c ? { "data-type": c.type ?? "text" } : {},
898
+ ...c?.unit ? { "data-unit": c.unit } : {},
899
+ ...c?.currency ? { "data-currency": c.currency } : {},
900
+ ...c?.decimals != null ? { "data-decimals": String(c.decimals) } : {},
901
+ ...c?.align ? { "data-align": c.align } : {},
902
+ ...sortable.has(i) ? { "data-sortable": "true" } : {},
903
+ ...c?.trueLabel ? { "data-true": c.trueLabel } : {},
904
+ ...c?.falseLabel ? { "data-false": c.falseLabel } : {},
905
+ ...c?.max != null ? { "data-max": String(c.max) } : {},
906
+ ...c?.description ? { "data-description": c.description } : {}
907
+ };
908
+ };
909
+ const table = /* @__PURE__ */ jsxs2("table", { ...role("table"), ...value.legacy === true ? { className: "bcms-table" } : {}, ...notesId ? { "aria-describedby": notesId } : {}, children: [
594
910
  caption && /* @__PURE__ */ jsx3("caption", { children: caption }),
595
- layout.columns.map((run, i) => /* @__PURE__ */ jsx3("colgroup", { span: span(run.width) }, i)),
596
- (groupRow || head > 0) && /* @__PURE__ */ jsxs2("thead", { children: [
911
+ sortable.size || columns.some(Boolean) ? (layout.columns.length ? layout.columns.map((run) => run.width) : [layout.width]).map((w, i, all) => {
912
+ const from = all.slice(0, i).reduce((n, x) => n + x, 0);
913
+ return /* @__PURE__ */ jsx3("colgroup", { children: Array.from({ length: w }, (_, k) => /* @__PURE__ */ jsx3("col", { ...colProps(from + k) }, k)) }, i);
914
+ }) : layout.columns.map((run, i) => /* @__PURE__ */ jsx3("colgroup", { span: span(run.width) }, i)),
915
+ (groupRow || head > 0) && /* @__PURE__ */ jsxs2("thead", { ...role("rowgroup"), children: [
597
916
  groupRow,
598
917
  rows.slice(0, head).map(rowEl)
599
918
  ] }),
600
- bodies
919
+ bodies,
920
+ foot.length > 0 && /* @__PURE__ */ jsx3("tfoot", { ...role("rowgroup"), children: foot.map(rowEl) })
601
921
  ] });
602
- return notes ? /* @__PURE__ */ jsxs2("figure", { "data-rt": "table", children: [
922
+ const filter = value.filterable === true;
923
+ const ld = jsonLdOf(value.jsonLd);
924
+ const ldAttrs = ld ? { "data-jsonld": ld.type, ...ld.axis ? { "data-jsonld-axis": ld.axis } : {}, ...ld.priceRow ? { "data-jsonld-price": ld.priceRow } : {} } : {};
925
+ const designAttrs = design ? { ...design.responsive ? { "data-responsive": design.responsive } : {}, ...design.sticky ? { "data-sticky": "true" } : {} } : {};
926
+ return notes || filter || ld || design || footnotes.length ? /* @__PURE__ */ jsxs2("figure", { "data-rt": "table", ...designAttrs, ...filter ? { "data-filter": "true" } : {}, ...ldAttrs, children: [
603
927
  table,
604
- /* @__PURE__ */ jsx3("p", { "data-notes": "", id: notesId, children: notes })
928
+ footnotes.length ? /* @__PURE__ */ jsxs2("div", { className: "bt-notes", id: `${tableId}-notes`, children: [
929
+ notes ? /* @__PURE__ */ jsx3("p", { "data-notes": "", children: notes }) : null,
930
+ /* @__PURE__ */ jsx3("ol", { "data-footnotes": "", children: footnotes.map((f, i) => /* @__PURE__ */ jsx3("li", { id: `${tableId}-fn-${i + 1}`, "data-key": f._key, children: f.text }, f._key)) })
931
+ ] }) : notes ? /* @__PURE__ */ jsx3("p", { "data-notes": "", id: notesId, children: notes }) : null
605
932
  ] }) : table;
606
933
  }
607
934
  function buildComponents(renderBlock) {