@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/renderer.js CHANGED
@@ -36,6 +36,7 @@ import React from "react";
36
36
  import { PortableText as PT } from "@portabletext/react";
37
37
 
38
38
  // ../richtext/src/types.ts
39
+ var PT_TABLE_COLUMN_TYPES = ["text", "number", "money", "boolean", "rating", "link"];
39
40
  function pullQuoteHref(link) {
40
41
  if (typeof link === "string") return link.trim();
41
42
  if (link && typeof link === "object" && typeof link.href === "string") {
@@ -43,6 +44,8 @@ function pullQuoteHref(link) {
43
44
  }
44
45
  return "";
45
46
  }
47
+ var isPortableTextBlock = (n) => !!n && typeof n === "object" && n._type === "block";
48
+ var isPortableTextSpan = (n) => !!n && typeof n === "object" && n._type === "span";
46
49
  var PT_TYPES = {
47
50
  /** A generic embedded block: `{schemaKey, schemaLabel, fields}`, the successor to Lexical's BlockNode. */
48
51
  block: "bcmsBlock",
@@ -264,7 +267,14 @@ function normalizeCell(raw, at, mint, read) {
264
267
  ...raw.header === true ? { header: true } : {},
265
268
  ...span === void 0 ? {} : { span },
266
269
  ...rowSpan === void 0 ? {} : { rowSpan },
267
- ...typeof raw.group === "string" ? { group: raw.group } : {}
270
+ ...typeof raw.group === "string" ? { group: raw.group } : {},
271
+ ...raw.highlight === true ? { highlight: true } : {},
272
+ ...raw.sortable === true ? { sortable: true } : {},
273
+ // The V2 table's typed cells: the column (first-row cell), the per-cell type, the typed value.
274
+ ...columnOf(raw.column) ? { column: columnOf(raw.column) } : {},
275
+ ...isColumnType(raw.type) ? { type: raw.type } : {},
276
+ ...typeof raw.value === "boolean" || typeof raw.value === "number" && Number.isFinite(raw.value) ? { value: raw.value } : {},
277
+ ...typeof raw.note === "string" && raw.note ? { note: raw.note } : {}
268
278
  };
269
279
  }
270
280
  function normalizeRow(raw, at, mint, read) {
@@ -273,7 +283,11 @@ function normalizeRow(raw, at, mint, read) {
273
283
  _key: isRecord(raw) && isValidKey(raw._key) ? raw._key : mint("r", at),
274
284
  cells: cells.map((c, col) => normalizeCell(c, [at, col], mint, read)),
275
285
  ...isRecord(raw) && raw.head === true ? { head: true } : {},
276
- ...isRecord(raw) && typeof raw.group === "string" ? { group: raw.group } : {}
286
+ ...isRecord(raw) && typeof raw.group === "string" ? { group: raw.group } : {},
287
+ // The V2 table's two row flags ("Totals row", "Highlight this row"); see `PortableTextTableRow`.
288
+ ...isRecord(raw) && raw.footer === true ? { footer: true } : {},
289
+ ...isRecord(raw) && raw.highlight === true ? { highlight: true } : {},
290
+ ...isRecord(raw) && isColumnType(raw.type) ? { type: raw.type } : {}
277
291
  };
278
292
  }
279
293
  function builtInTableToTable(node, key = randomKey) {
@@ -330,9 +344,62 @@ function tableExtras(node) {
330
344
  ...typeof node.caption === "string" ? { caption: node.caption } : {},
331
345
  ...typeof node.notes === "string" ? { notes: node.notes } : {},
332
346
  ...rowGroups ? { rowGroups } : {},
333
- ...columnGroups ? { columnGroups } : {}
347
+ ...columnGroups ? { columnGroups } : {},
348
+ ...node.filterable === true ? { filterable: true } : {},
349
+ ...jsonLdOf(node.jsonLd) ? { jsonLd: jsonLdOf(node.jsonLd) } : {},
350
+ ...designOf(node.design) ? { design: designOf(node.design) } : {},
351
+ ...footnotesOf(node.footnotes) ? { footnotes: footnotesOf(node.footnotes) } : {}
334
352
  };
335
353
  }
354
+ function footnotesOf(v) {
355
+ if (!Array.isArray(v)) return void 0;
356
+ 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 : "" }));
357
+ return out.length ? out : void 0;
358
+ }
359
+ function footnoteNumbers(table) {
360
+ return new Map((footnotesOf(table.footnotes) ?? []).map((f, i) => [f._key, i + 1]));
361
+ }
362
+ function designOf(v) {
363
+ if (!isRecord(v)) return void 0;
364
+ const d = {
365
+ ...v.responsive === "stack" || v.responsive === "cards" ? { responsive: v.responsive } : {},
366
+ ...v.sticky === true ? { sticky: true } : {}
367
+ };
368
+ return Object.keys(d).length ? d : void 0;
369
+ }
370
+ var JSONLD_TYPES = ["ItemList", "Dataset", "Offers"];
371
+ function jsonLdOf(v) {
372
+ if (!isRecord(v) || !JSONLD_TYPES.includes(v.type)) return void 0;
373
+ return {
374
+ type: v.type,
375
+ ...v.axis === "rows" || v.axis === "columns" ? { axis: v.axis } : {},
376
+ ...typeof v.priceRow === "string" && v.priceRow ? { priceRow: v.priceRow } : {}
377
+ };
378
+ }
379
+ function tableSections(table) {
380
+ const rows = Array.isArray(table.rows) ? table.rows : [];
381
+ const headCount = rows.findIndex((r) => !isRecord(r) || r.head !== true);
382
+ const head = rows.slice(0, headCount === -1 ? rows.length : headCount);
383
+ let footFrom = rows.length;
384
+ while (footFrom > head.length && isRecord(rows[footFrom - 1]) && rows[footFrom - 1].footer === true) footFrom -= 1;
385
+ return { head, body: rows.slice(head.length, footFrom), foot: rows.slice(footFrom) };
386
+ }
387
+ function highlightedColumns(table) {
388
+ return markedColumns(table, "highlight");
389
+ }
390
+ function sortableColumns(table) {
391
+ return markedColumns(table, "sortable");
392
+ }
393
+ function markedColumns(table, flag) {
394
+ const out = /* @__PURE__ */ new Set();
395
+ const first = Array.isArray(table.rows) ? table.rows[0] : void 0;
396
+ let at = 0;
397
+ for (const cell of isRecord(first) && Array.isArray(first.cells) ? first.cells : []) {
398
+ if (isRecord(cell) && cell[flag] === true) out.add(at);
399
+ at += isRecord(cell) && typeof cell.span === "number" && Number.isInteger(cell.span) && cell.span > 1 ? cell.span : 1;
400
+ }
401
+ return out;
402
+ }
336
403
  function tableGroupLayout(table) {
337
404
  const rows = Array.isArray(table.rows) ? table.rows : [];
338
405
  const labels = (list) => new Map(
@@ -350,8 +417,7 @@ function tableGroupLayout(table) {
350
417
  else columns.push({ width: spanOf2(cell), ...key === void 0 ? {} : { key, label: colLabels.get(key) } });
351
418
  }
352
419
  const rowLabels = labels(table.rowGroups);
353
- const headCount = rows.findIndex((r) => !isRecord(r) || r.head !== true);
354
- const body = headCount === -1 ? [] : rows.slice(headCount);
420
+ const { body } = tableSections(table);
355
421
  const runs = [];
356
422
  for (const row of body) {
357
423
  const key = isRecord(row) && typeof row.group === "string" && rowLabels.has(row.group) ? row.group : void 0;
@@ -365,6 +431,133 @@ function tableGroupLayout(table) {
365
431
  width
366
432
  };
367
433
  }
434
+ var NUMERIC_TYPES = /* @__PURE__ */ new Set(["number", "money", "rating"]);
435
+ var isColumnType = (v) => PT_TABLE_COLUMN_TYPES.includes(v);
436
+ function columnOf(v) {
437
+ if (!isRecord(v)) return void 0;
438
+ const str = (x) => typeof x === "string" && x !== "" ? x : void 0;
439
+ const int = (x, lo, hi) => Number.isInteger(x) && x >= lo && x <= hi ? x : void 0;
440
+ const c = {
441
+ ...isColumnType(v.type) ? { type: v.type } : {},
442
+ ...str(v.description) ? { description: str(v.description) } : {},
443
+ ...str(v.unit) ? { unit: str(v.unit) } : {},
444
+ ...typeof v.currency === "string" && /^[A-Z]{3}$/.test(v.currency) ? { currency: v.currency } : {},
445
+ ...int(v.decimals, 0, 10) !== void 0 ? { decimals: int(v.decimals, 0, 10) } : {},
446
+ ...v.align === "start" || v.align === "center" || v.align === "end" ? { align: v.align } : {},
447
+ ...str(v.trueLabel) ? { trueLabel: str(v.trueLabel) } : {},
448
+ ...str(v.falseLabel) ? { falseLabel: str(v.falseLabel) } : {},
449
+ ...int(v.max, 1, 10) !== void 0 ? { max: int(v.max, 1, 10) } : {}
450
+ };
451
+ return Object.keys(c).length ? c : void 0;
452
+ }
453
+ function tableColumns(table) {
454
+ const out = [];
455
+ const first = Array.isArray(table.rows) ? table.rows[0] : void 0;
456
+ for (const cell of isRecord(first) && Array.isArray(first.cells) ? first.cells : []) {
457
+ const w = isRecord(cell) && typeof cell.span === "number" && Number.isInteger(cell.span) && cell.span > 1 ? cell.span : 1;
458
+ const col = isRecord(cell) ? columnOf(cell.column) : void 0;
459
+ for (let i = 0; i < w; i += 1) out.push(col);
460
+ }
461
+ return out;
462
+ }
463
+ function isTypedTable(table) {
464
+ if (tableColumns(table).some(Boolean)) return true;
465
+ return (Array.isArray(table.rows) ? table.rows : []).some(
466
+ (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)))
467
+ );
468
+ }
469
+ 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";
470
+ var columnAlign = (col, type) => col?.align ?? (NUMERIC_TYPES.has(type) ? "end" : "start");
471
+ var TRUE_WORDS = /* @__PURE__ */ new Set(["\u2713", "\u2714", "\u2714\uFE0F", "\u2611", "\u2705", "yes", "y", "true", "included", "available", "supported", "x \u2713", "check", "\u25CF"]);
472
+ var FALSE_WORDS = /* @__PURE__ */ new Set(["\u2717", "\u2715", "\u2716", "\u274C", "\xD7", "x", "no", "n", "false", "not included", "unavailable", "not available", "unsupported", "\u2014", "\u2013", "-", "\u25CB"]);
473
+ function toBool(v) {
474
+ if (typeof v === "boolean") return v;
475
+ if (typeof v !== "string") return null;
476
+ const s = v.trim().toLowerCase();
477
+ return TRUE_WORDS.has(s) ? true : FALSE_WORDS.has(s) ? false : null;
478
+ }
479
+ function toNumber(v) {
480
+ if (typeof v === "number") return Number.isFinite(v) ? v : null;
481
+ if (typeof v !== "string") return null;
482
+ const s = v.replace(/[,\s]/g, "").replace(/[^\d.+-]/g, "");
483
+ if (!s || !/^[+-]?\d*\.?\d+$/.test(s)) return null;
484
+ return Number(s);
485
+ }
486
+ var formatNumber = (n, decimals) => new Intl.NumberFormat("en-US", decimals != null ? { minimumFractionDigits: decimals, maximumFractionDigits: decimals } : { maximumFractionDigits: 6 }).format(n);
487
+ function formatMoney(n, currency = "USD", decimals) {
488
+ try {
489
+ const whole = Number.isInteger(n) && decimals == null;
490
+ return new Intl.NumberFormat("en-US", { style: "currency", currency, minimumFractionDigits: whole ? 0 : decimals ?? 2, maximumFractionDigits: whole ? 0 : decimals ?? 2 }).format(n);
491
+ } catch {
492
+ return `${formatNumber(n, decimals)} ${currency}`;
493
+ }
494
+ }
495
+ var boolLabels = (col) => ({ yes: col?.trueLabel || "Included", no: col?.falseLabel || "Not included" });
496
+ function typedCellText(col, type, v) {
497
+ if (v == null || v === "") return "";
498
+ const words = String(v).replace(/\s+/g, " ").trim();
499
+ switch (type) {
500
+ case "boolean": {
501
+ const b = toBool(v);
502
+ return b == null ? words : b ? boolLabels(col).yes : boolLabels(col).no;
503
+ }
504
+ case "number": {
505
+ const n = toNumber(v);
506
+ return n == null ? words : `${formatNumber(n, col?.decimals)}${col?.unit ? /^[%°]/.test(col.unit) ? col.unit : ` ${col.unit}` : ""}`;
507
+ }
508
+ case "money": {
509
+ const n = toNumber(v);
510
+ return n == null ? words : formatMoney(n, col?.currency || "USD", col?.decimals);
511
+ }
512
+ case "rating": {
513
+ const n = toNumber(v);
514
+ return n == null ? words : `${formatNumber(n, 1).replace(/\.0$/, "")} out of ${col?.max ?? 5}`;
515
+ }
516
+ default:
517
+ return words;
518
+ }
519
+ }
520
+ var escHtml = (s) => s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
521
+ function typedCellView(column, row, cell, inHead) {
522
+ if (inHead) {
523
+ const align2 = columnAlign(column, column?.type ?? "text");
524
+ return { lead: align2 !== "start" ? [["data-align", align2]] : [], tail: column?.description ? [["title", column.description]] : [] };
525
+ }
526
+ const type = cellTypeOf(column, row, cell);
527
+ const words = portableTextWords(cell.content);
528
+ const v = typeof cell.value === "number" || typeof cell.value === "boolean" ? cell.value : words;
529
+ const tail = isColumnType(cell.type) ? [["data-type", cell.type]] : [];
530
+ let inner;
531
+ if (v === "") {
532
+ inner = cell.header === true ? "" : '<span aria-hidden="true" class="bt-dash">\u2014</span><span class="bt-sr">No value</span>';
533
+ tail.push(["data-empty", ""]);
534
+ } else if (type === "boolean") {
535
+ const b = toBool(v);
536
+ if (b == null) inner = escHtml(words);
537
+ else {
538
+ const { yes, no } = boolLabels(column);
539
+ inner = `<span aria-hidden="true" class="bt-bool" data-bool="${b}">${b ? "\u2713" : "\u2013"}</span><span class="bt-sr">${escHtml(b ? yes : no)}</span>`;
540
+ tail.push(["data-value", String(b)]);
541
+ }
542
+ } else if (type === "rating") {
543
+ const n = toNumber(v);
544
+ if (n == null) inner = escHtml(words);
545
+ else {
546
+ inner = `<span aria-hidden="true" class="bt-star">\u2605</span> ${escHtml(formatNumber(n, 1).replace(/\.0$/, ""))}<span class="bt-dim"> / ${column?.max ?? 5}</span>`;
547
+ tail.push(["data-value", String(n)]);
548
+ }
549
+ } else if (type === "number" || type === "money") {
550
+ const n = toNumber(v);
551
+ inner = escHtml(typedCellText(column, type, v));
552
+ if (n != null) tail.push(["data-value", String(n)]);
553
+ }
554
+ const align = columnAlign(column, type);
555
+ if (align !== "start") tail.push(["data-align", align]);
556
+ return { lead: [], tail, ...inner === void 0 ? {} : { inner } };
557
+ }
558
+ function portableTextWords(content) {
559
+ 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();
560
+ }
368
561
 
369
562
  // ../richtext/src/key-takeaways.ts
370
563
  var isRecord2 = (v) => !!v && typeof v === "object" && !Array.isArray(v);
@@ -390,6 +583,77 @@ function detachedComponentCopy(value) {
390
583
  ...typeof f.variant === "string" ? { variant: f.variant } : {}
391
584
  };
392
585
  }
586
+ function componentCopyLines(copy) {
587
+ const line = (v) => {
588
+ if (typeof v === "number" && Number.isFinite(v)) return String(v);
589
+ if (typeof v === "string") {
590
+ const t = v.replace(/<[^>]+>/g, " ").replace(/&nbsp;/g, " ").replace(/\s+/g, " ").trim();
591
+ return t || null;
592
+ }
593
+ if (isRecord3(v)) return line(v.label ?? v.text ?? v.title);
594
+ return null;
595
+ };
596
+ return Object.values(copy.props).map(line).filter((s) => !!s);
597
+ }
598
+
599
+ // ../richtext/src/plain.ts
600
+ function portableTextToPlainText(value) {
601
+ if (!Array.isArray(value)) return "";
602
+ const lines = [];
603
+ for (const node of value) {
604
+ if (isPortableTextBlock(node)) {
605
+ lines.push(node.children.map((c) => isPortableTextSpan(c) ? c.text : "").join(""));
606
+ continue;
607
+ }
608
+ if (node._type === PT_TYPES.code && typeof node.code === "string") {
609
+ lines.push(node.code);
610
+ }
611
+ if (node._type === PT_TYPES.pullQuote) {
612
+ lines.push(...pullQuoteLines(node));
613
+ continue;
614
+ }
615
+ if (node._type === PT_TYPES.keyTakeaways) {
616
+ const { title, points } = keyTakeawaysView(node);
617
+ lines.push(...title ? [title] : [], ...points);
618
+ continue;
619
+ }
620
+ const table = migrateTable(node);
621
+ if (table) {
622
+ lines.push(...tableLines(table));
623
+ continue;
624
+ }
625
+ const copy = detachedComponentCopy(node);
626
+ if (copy) {
627
+ lines.push(...componentCopyLines(copy));
628
+ continue;
629
+ }
630
+ }
631
+ return lines.join("\n");
632
+ }
633
+ var tableLines = (table) => {
634
+ const rows = Array.isArray(table.rows) ? table.rows : [];
635
+ const rowLine = (row) => (Array.isArray(row?.cells) ? row.cells : []).map((cell) => portableTextToPlainText(cell?.content).replace(/\s+/g, " ").trim()).join(" ").trim();
636
+ const layout = tableGroupLayout(table);
637
+ const one = (s) => typeof s === "string" ? s.replace(/\s+/g, " ").trim() : "";
638
+ const head = layout.rows.length ? rows.slice(0, rows.length - layout.rows.reduce((n, r) => n + r.rows.length, 0)) : rows;
639
+ const lines = [
640
+ one(table.caption),
641
+ layout.columns.map((r) => one(r.label)).filter(Boolean).join(" "),
642
+ ...head.map(rowLine),
643
+ ...layout.rows.flatMap((run) => [...run.label === void 0 ? [] : [one(run.label)], ...run.rows.map(rowLine)]),
644
+ one(table.notes),
645
+ // footnotes, numbered as the html numbers them (V2 tableSearchText reads them too)
646
+ ...(Array.isArray(table.footnotes) ? table.footnotes : []).map((f, i) => one(`[${i + 1}] ${typeof f?.text === "string" ? f.text : ""}`))
647
+ ];
648
+ const fnCount = Array.isArray(table.footnotes) ? table.footnotes.length : 0;
649
+ const extras = /* @__PURE__ */ new Set([0, 1, lines.length - 1 - fnCount]);
650
+ return lines.filter((l, i) => l !== "" || !extras.has(i));
651
+ };
652
+ var pullQuoteLines = (v) => {
653
+ const text = (x) => typeof x === "string" ? x.trim() : "";
654
+ const by = [text(v.name), text(v.role)].filter(Boolean).join(", ");
655
+ return [text(v.quote), ...by ? [`\u2014 ${by}`] : []].filter(Boolean);
656
+ };
393
657
 
394
658
  // ../richtext/src/read.ts
395
659
  var isRecord4 = (v) => !!v && typeof v === "object" && !Array.isArray(v);
@@ -451,41 +715,104 @@ function Table({ value }) {
451
715
  );
452
716
  const rows = Array.isArray(value?.rows) ? value.rows : [];
453
717
  if (rows.length === 0) return null;
454
- const cells = (row) => (Array.isArray(row?.cells) ? row.cells : []).map((cell) => {
455
- const Tag = cell?.header === true ? "th" : "td";
456
- const span2 = typeof cell?.span === "number" && cell.span > 1 ? cell.span : void 0;
457
- const rowSpan = typeof cell?.rowSpan === "number" && cell.rowSpan > 1 ? cell.rowSpan : void 0;
458
- const content = Array.isArray(cell?.content) ? cell.content : [];
459
- const lone = content.length === 1 && content[0]._type === "block" && content[0].style === "normal" && !content[0].listItem;
460
- return /* @__PURE__ */ jsx2(Tag, { ...span2 ? { colSpan: span2 } : {}, ...rowSpan ? { rowSpan } : {}, children: /* @__PURE__ */ jsx2(PT, { value: content, components: lone ? inline : components }) }, cell?._key);
461
- });
462
- const rowEl = (row) => /* @__PURE__ */ jsx2("tr", { children: cells(row) }, row?._key);
463
- const headCount = rows.findIndex((r) => r?.head !== true);
464
- const head = headCount === -1 ? rows.length : headCount;
718
+ const marked = highlightedColumns(value);
719
+ const sortable = sortableColumns(value);
720
+ const design = designOf(value.design);
721
+ const stacked = design?.responsive === "stack" || design?.responsive === "cards";
722
+ const role = (r) => stacked ? { role: r } : {};
723
+ const labels = [];
724
+ if (stacked) {
725
+ const headRows = tableSections(value).head;
726
+ let at = 0;
727
+ for (const c of Array.isArray(headRows[headRows.length - 1]?.cells) ? headRows[headRows.length - 1].cells : []) {
728
+ const text = portableTextToPlainText(Array.isArray(c?.content) ? c.content : []).replace(/\s+/g, " ").trim();
729
+ const w = typeof c?.span === "number" && c.span > 1 ? c.span : 1;
730
+ for (let i = 0; i < w; i += 1) labels[at + i] = text;
731
+ at += w;
732
+ }
733
+ }
734
+ const columns = tableColumns(value);
735
+ const typed = isTypedTable(value);
736
+ const headSet = new Set(tableSections(value).head);
737
+ const props = (list) => Object.fromEntries(list);
738
+ const fnNumber = footnoteNumbers(value);
739
+ const footnotes = footnotesOf(value.footnotes) ?? [];
740
+ const tableId = typeof value._key === "string" && value._key ? value._key : "tbl";
741
+ const cells = (row) => {
742
+ let col = 0;
743
+ return (Array.isArray(row?.cells) ? row.cells : []).map((cell) => {
744
+ const Tag = cell?.header === true ? "th" : "td";
745
+ const span2 = typeof cell?.span === "number" && cell.span > 1 ? cell.span : void 0;
746
+ const at = col;
747
+ col += span2 ?? 1;
748
+ const rowSpan = typeof cell?.rowSpan === "number" && cell.rowSpan > 1 ? cell.rowSpan : void 0;
749
+ const n = typeof cell?.note === "string" ? fnNumber.get(cell.note) : void 0;
750
+ const content = Array.isArray(cell?.content) ? cell.content : [];
751
+ const lone = content.length === 1 && content[0]._type === "block" && content[0].style === "normal" && !content[0].listItem;
752
+ const view = typed ? typedCellView(columns[at], row, cell ?? {}, headSet.has(row)) : void 0;
753
+ const noteAttr = n ? [["data-note", cell.note]] : [];
754
+ const tail = [...(view?.tail ?? []).filter(([k]) => k !== "data-align"), ...noteAttr, ...(view?.tail ?? []).filter(([k]) => k === "data-align")];
755
+ const supHtml = n ? `<sup><a href="#${tableId}-fn-${n}" aria-label="Note ${n}">${n}</a></sup>` : "";
756
+ return /* @__PURE__ */ jsx2(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__ */ jsxs(Fragment, { children: [
757
+ /* @__PURE__ */ jsx2(PT, { value: content, components: lone ? inline : components }),
758
+ n ? /* @__PURE__ */ jsx2("sup", { children: /* @__PURE__ */ jsx2("a", { href: `#${tableId}-fn-${n}`, "aria-label": `Note ${n}`, children: n }) }) : null
759
+ ] }) }, cell?._key);
760
+ });
761
+ };
762
+ const rowEl = (row) => /* @__PURE__ */ jsx2("tr", { ...role("row"), ...typeof row?.type === "string" ? { "data-type": row.type } : {}, ...row?.highlight === true ? { "data-highlight": "true" } : {}, children: cells(row) }, row?._key);
763
+ const sections = tableSections(value);
764
+ const head = sections.head.length;
765
+ const foot = sections.foot;
465
766
  const layout = tableGroupLayout(value);
466
767
  const caption = typeof value.caption === "string" ? value.caption.trim() : "";
467
768
  const notes = typeof value.notes === "string" ? value.notes.trim() : "";
468
- const notesId = notes && typeof value._key === "string" && value._key ? `${value._key}-notes` : void 0;
769
+ const notesId = (notes || footnotes.length) && typeof value._key === "string" && value._key ? `${value._key}-notes` : void 0;
469
770
  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)]
771
+ const groupRow = layout.columns.length ? /* @__PURE__ */ jsx2("tr", { ...role("row"), children: layout.columns.flatMap(
772
+ (run, i) => run.label === void 0 ? Array.from({ length: run.width }, (_, k) => /* @__PURE__ */ jsx2("td", { ...role("cell") }, `${i}-${k}`)) : [/* @__PURE__ */ jsx2("th", { ...role("columnheader"), scope: "colgroup", colSpan: span(run.width), "data-group": run.key, children: run.label }, i)]
472
773
  ) }) : 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 }) }),
774
+ const bodies = layout.rows.length ? layout.rows.map((run, i) => /* @__PURE__ */ jsxs("tbody", { ...role("rowgroup"), "data-key": run.key, children: [
775
+ run.label !== void 0 && /* @__PURE__ */ jsx2("tr", { ...role("row"), children: /* @__PURE__ */ jsx2("th", { ...role("rowheader"), scope: "rowgroup", colSpan: span(layout.width), children: run.label }) }),
475
776
  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: [
777
+ ] }, i)) : sections.body.length > 0 && /* @__PURE__ */ jsx2("tbody", { ...role("rowgroup"), children: sections.body.map(rowEl) });
778
+ const colProps = (i) => {
779
+ const c = columns[i];
780
+ return {
781
+ ...c ? { "data-type": c.type ?? "text" } : {},
782
+ ...c?.unit ? { "data-unit": c.unit } : {},
783
+ ...c?.currency ? { "data-currency": c.currency } : {},
784
+ ...c?.decimals != null ? { "data-decimals": String(c.decimals) } : {},
785
+ ...c?.align ? { "data-align": c.align } : {},
786
+ ...sortable.has(i) ? { "data-sortable": "true" } : {},
787
+ ...c?.trueLabel ? { "data-true": c.trueLabel } : {},
788
+ ...c?.falseLabel ? { "data-false": c.falseLabel } : {},
789
+ ...c?.max != null ? { "data-max": String(c.max) } : {},
790
+ ...c?.description ? { "data-description": c.description } : {}
791
+ };
792
+ };
793
+ const table = /* @__PURE__ */ jsxs("table", { ...role("table"), ...value.legacy === true ? { className: "bcms-table" } : {}, ...notesId ? { "aria-describedby": notesId } : {}, children: [
478
794
  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: [
795
+ sortable.size || columns.some(Boolean) ? (layout.columns.length ? layout.columns.map((run) => run.width) : [layout.width]).map((w, i, all) => {
796
+ const from = all.slice(0, i).reduce((n, x) => n + x, 0);
797
+ return /* @__PURE__ */ jsx2("colgroup", { children: Array.from({ length: w }, (_, k) => /* @__PURE__ */ jsx2("col", { ...colProps(from + k) }, k)) }, i);
798
+ }) : layout.columns.map((run, i) => /* @__PURE__ */ jsx2("colgroup", { span: span(run.width) }, i)),
799
+ (groupRow || head > 0) && /* @__PURE__ */ jsxs("thead", { ...role("rowgroup"), children: [
481
800
  groupRow,
482
801
  rows.slice(0, head).map(rowEl)
483
802
  ] }),
484
- bodies
803
+ bodies,
804
+ foot.length > 0 && /* @__PURE__ */ jsx2("tfoot", { ...role("rowgroup"), children: foot.map(rowEl) })
485
805
  ] });
486
- return notes ? /* @__PURE__ */ jsxs("figure", { "data-rt": "table", children: [
806
+ const filter = value.filterable === true;
807
+ const ld = jsonLdOf(value.jsonLd);
808
+ const ldAttrs = ld ? { "data-jsonld": ld.type, ...ld.axis ? { "data-jsonld-axis": ld.axis } : {}, ...ld.priceRow ? { "data-jsonld-price": ld.priceRow } : {} } : {};
809
+ const designAttrs = design ? { ...design.responsive ? { "data-responsive": design.responsive } : {}, ...design.sticky ? { "data-sticky": "true" } : {} } : {};
810
+ return notes || filter || ld || design || footnotes.length ? /* @__PURE__ */ jsxs("figure", { "data-rt": "table", ...designAttrs, ...filter ? { "data-filter": "true" } : {}, ...ldAttrs, children: [
487
811
  table,
488
- /* @__PURE__ */ jsx2("p", { "data-notes": "", id: notesId, children: notes })
812
+ footnotes.length ? /* @__PURE__ */ jsxs("div", { className: "bt-notes", id: `${tableId}-notes`, children: [
813
+ notes ? /* @__PURE__ */ jsx2("p", { "data-notes": "", children: notes }) : null,
814
+ /* @__PURE__ */ jsx2("ol", { "data-footnotes": "", children: footnotes.map((f, i) => /* @__PURE__ */ jsx2("li", { id: `${tableId}-fn-${i + 1}`, "data-key": f._key, children: f.text }, f._key)) })
815
+ ] }) : notes ? /* @__PURE__ */ jsx2("p", { "data-notes": "", id: notesId, children: notes }) : null
489
816
  ] }) : table;
490
817
  }
491
818
  function buildComponents(renderBlock) {