@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/index.js CHANGED
@@ -585,6 +585,7 @@ function BlocksRenderer({
585
585
  }
586
586
 
587
587
  // ../richtext/src/types.ts
588
+ var PT_TABLE_COLUMN_TYPES = ["text", "number", "money", "boolean", "rating", "link"];
588
589
  function pullQuoteHref(link) {
589
590
  if (typeof link === "string") return link.trim();
590
591
  if (link && typeof link === "object" && typeof link.href === "string") {
@@ -592,6 +593,8 @@ function pullQuoteHref(link) {
592
593
  }
593
594
  return "";
594
595
  }
596
+ var isPortableTextBlock = (n) => !!n && typeof n === "object" && n._type === "block";
597
+ var isPortableTextSpan = (n) => !!n && typeof n === "object" && n._type === "span";
595
598
  var PT_TYPES = {
596
599
  /** A generic embedded block: `{schemaKey, schemaLabel, fields}`, the successor to Lexical's BlockNode. */
597
600
  block: "bcmsBlock",
@@ -813,7 +816,14 @@ function normalizeCell(raw, at, mint, read) {
813
816
  ...raw.header === true ? { header: true } : {},
814
817
  ...span === void 0 ? {} : { span },
815
818
  ...rowSpan === void 0 ? {} : { rowSpan },
816
- ...typeof raw.group === "string" ? { group: raw.group } : {}
819
+ ...typeof raw.group === "string" ? { group: raw.group } : {},
820
+ ...raw.highlight === true ? { highlight: true } : {},
821
+ ...raw.sortable === true ? { sortable: true } : {},
822
+ // The V2 table's typed cells: the column (first-row cell), the per-cell type, the typed value.
823
+ ...columnOf(raw.column) ? { column: columnOf(raw.column) } : {},
824
+ ...isColumnType(raw.type) ? { type: raw.type } : {},
825
+ ...typeof raw.value === "boolean" || typeof raw.value === "number" && Number.isFinite(raw.value) ? { value: raw.value } : {},
826
+ ...typeof raw.note === "string" && raw.note ? { note: raw.note } : {}
817
827
  };
818
828
  }
819
829
  function normalizeRow(raw, at, mint, read) {
@@ -822,7 +832,11 @@ function normalizeRow(raw, at, mint, read) {
822
832
  _key: isRecord(raw) && isValidKey(raw._key) ? raw._key : mint("r", at),
823
833
  cells: cells.map((c, col) => normalizeCell(c, [at, col], mint, read)),
824
834
  ...isRecord(raw) && raw.head === true ? { head: true } : {},
825
- ...isRecord(raw) && typeof raw.group === "string" ? { group: raw.group } : {}
835
+ ...isRecord(raw) && typeof raw.group === "string" ? { group: raw.group } : {},
836
+ // The V2 table's two row flags ("Totals row", "Highlight this row"); see `PortableTextTableRow`.
837
+ ...isRecord(raw) && raw.footer === true ? { footer: true } : {},
838
+ ...isRecord(raw) && raw.highlight === true ? { highlight: true } : {},
839
+ ...isRecord(raw) && isColumnType(raw.type) ? { type: raw.type } : {}
826
840
  };
827
841
  }
828
842
  function builtInTableToTable(node, key = randomKey) {
@@ -879,9 +893,62 @@ function tableExtras(node) {
879
893
  ...typeof node.caption === "string" ? { caption: node.caption } : {},
880
894
  ...typeof node.notes === "string" ? { notes: node.notes } : {},
881
895
  ...rowGroups ? { rowGroups } : {},
882
- ...columnGroups ? { columnGroups } : {}
896
+ ...columnGroups ? { columnGroups } : {},
897
+ ...node.filterable === true ? { filterable: true } : {},
898
+ ...jsonLdOf(node.jsonLd) ? { jsonLd: jsonLdOf(node.jsonLd) } : {},
899
+ ...designOf(node.design) ? { design: designOf(node.design) } : {},
900
+ ...footnotesOf(node.footnotes) ? { footnotes: footnotesOf(node.footnotes) } : {}
883
901
  };
884
902
  }
903
+ function footnotesOf(v) {
904
+ if (!Array.isArray(v)) return void 0;
905
+ 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 : "" }));
906
+ return out.length ? out : void 0;
907
+ }
908
+ function footnoteNumbers(table) {
909
+ return new Map((footnotesOf(table.footnotes) ?? []).map((f, i) => [f._key, i + 1]));
910
+ }
911
+ function designOf(v) {
912
+ if (!isRecord(v)) return void 0;
913
+ const d = {
914
+ ...v.responsive === "stack" || v.responsive === "cards" ? { responsive: v.responsive } : {},
915
+ ...v.sticky === true ? { sticky: true } : {}
916
+ };
917
+ return Object.keys(d).length ? d : void 0;
918
+ }
919
+ var JSONLD_TYPES = ["ItemList", "Dataset", "Offers"];
920
+ function jsonLdOf(v) {
921
+ if (!isRecord(v) || !JSONLD_TYPES.includes(v.type)) return void 0;
922
+ return {
923
+ type: v.type,
924
+ ...v.axis === "rows" || v.axis === "columns" ? { axis: v.axis } : {},
925
+ ...typeof v.priceRow === "string" && v.priceRow ? { priceRow: v.priceRow } : {}
926
+ };
927
+ }
928
+ function tableSections(table) {
929
+ const rows = Array.isArray(table.rows) ? table.rows : [];
930
+ const headCount = rows.findIndex((r) => !isRecord(r) || r.head !== true);
931
+ const head = rows.slice(0, headCount === -1 ? rows.length : headCount);
932
+ let footFrom = rows.length;
933
+ while (footFrom > head.length && isRecord(rows[footFrom - 1]) && rows[footFrom - 1].footer === true) footFrom -= 1;
934
+ return { head, body: rows.slice(head.length, footFrom), foot: rows.slice(footFrom) };
935
+ }
936
+ function highlightedColumns(table) {
937
+ return markedColumns(table, "highlight");
938
+ }
939
+ function sortableColumns(table) {
940
+ return markedColumns(table, "sortable");
941
+ }
942
+ function markedColumns(table, flag) {
943
+ const out = /* @__PURE__ */ new Set();
944
+ const first = Array.isArray(table.rows) ? table.rows[0] : void 0;
945
+ let at = 0;
946
+ for (const cell of isRecord(first) && Array.isArray(first.cells) ? first.cells : []) {
947
+ if (isRecord(cell) && cell[flag] === true) out.add(at);
948
+ at += isRecord(cell) && typeof cell.span === "number" && Number.isInteger(cell.span) && cell.span > 1 ? cell.span : 1;
949
+ }
950
+ return out;
951
+ }
885
952
  function tableGroupLayout(table) {
886
953
  const rows = Array.isArray(table.rows) ? table.rows : [];
887
954
  const labels = (list) => new Map(
@@ -899,8 +966,7 @@ function tableGroupLayout(table) {
899
966
  else columns.push({ width: spanOf2(cell), ...key === void 0 ? {} : { key, label: colLabels.get(key) } });
900
967
  }
901
968
  const rowLabels = labels(table.rowGroups);
902
- const headCount = rows.findIndex((r) => !isRecord(r) || r.head !== true);
903
- const body = headCount === -1 ? [] : rows.slice(headCount);
969
+ const { body } = tableSections(table);
904
970
  const runs = [];
905
971
  for (const row of body) {
906
972
  const key = isRecord(row) && typeof row.group === "string" && rowLabels.has(row.group) ? row.group : void 0;
@@ -914,6 +980,133 @@ function tableGroupLayout(table) {
914
980
  width
915
981
  };
916
982
  }
983
+ var NUMERIC_TYPES = /* @__PURE__ */ new Set(["number", "money", "rating"]);
984
+ var isColumnType = (v) => PT_TABLE_COLUMN_TYPES.includes(v);
985
+ function columnOf(v) {
986
+ if (!isRecord(v)) return void 0;
987
+ const str = (x) => typeof x === "string" && x !== "" ? x : void 0;
988
+ const int = (x, lo, hi) => Number.isInteger(x) && x >= lo && x <= hi ? x : void 0;
989
+ const c = {
990
+ ...isColumnType(v.type) ? { type: v.type } : {},
991
+ ...str(v.description) ? { description: str(v.description) } : {},
992
+ ...str(v.unit) ? { unit: str(v.unit) } : {},
993
+ ...typeof v.currency === "string" && /^[A-Z]{3}$/.test(v.currency) ? { currency: v.currency } : {},
994
+ ...int(v.decimals, 0, 10) !== void 0 ? { decimals: int(v.decimals, 0, 10) } : {},
995
+ ...v.align === "start" || v.align === "center" || v.align === "end" ? { align: v.align } : {},
996
+ ...str(v.trueLabel) ? { trueLabel: str(v.trueLabel) } : {},
997
+ ...str(v.falseLabel) ? { falseLabel: str(v.falseLabel) } : {},
998
+ ...int(v.max, 1, 10) !== void 0 ? { max: int(v.max, 1, 10) } : {}
999
+ };
1000
+ return Object.keys(c).length ? c : void 0;
1001
+ }
1002
+ function tableColumns(table) {
1003
+ const out = [];
1004
+ const first = Array.isArray(table.rows) ? table.rows[0] : void 0;
1005
+ for (const cell of isRecord(first) && Array.isArray(first.cells) ? first.cells : []) {
1006
+ const w = isRecord(cell) && typeof cell.span === "number" && Number.isInteger(cell.span) && cell.span > 1 ? cell.span : 1;
1007
+ const col = isRecord(cell) ? columnOf(cell.column) : void 0;
1008
+ for (let i = 0; i < w; i += 1) out.push(col);
1009
+ }
1010
+ return out;
1011
+ }
1012
+ function isTypedTable(table) {
1013
+ if (tableColumns(table).some(Boolean)) return true;
1014
+ return (Array.isArray(table.rows) ? table.rows : []).some(
1015
+ (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)))
1016
+ );
1017
+ }
1018
+ 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";
1019
+ var columnAlign = (col, type) => col?.align ?? (NUMERIC_TYPES.has(type) ? "end" : "start");
1020
+ var TRUE_WORDS = /* @__PURE__ */ new Set(["\u2713", "\u2714", "\u2714\uFE0F", "\u2611", "\u2705", "yes", "y", "true", "included", "available", "supported", "x \u2713", "check", "\u25CF"]);
1021
+ var FALSE_WORDS = /* @__PURE__ */ new Set(["\u2717", "\u2715", "\u2716", "\u274C", "\xD7", "x", "no", "n", "false", "not included", "unavailable", "not available", "unsupported", "\u2014", "\u2013", "-", "\u25CB"]);
1022
+ function toBool(v) {
1023
+ if (typeof v === "boolean") return v;
1024
+ if (typeof v !== "string") return null;
1025
+ const s = v.trim().toLowerCase();
1026
+ return TRUE_WORDS.has(s) ? true : FALSE_WORDS.has(s) ? false : null;
1027
+ }
1028
+ function toNumber(v) {
1029
+ if (typeof v === "number") return Number.isFinite(v) ? v : null;
1030
+ if (typeof v !== "string") return null;
1031
+ const s = v.replace(/[,\s]/g, "").replace(/[^\d.+-]/g, "");
1032
+ if (!s || !/^[+-]?\d*\.?\d+$/.test(s)) return null;
1033
+ return Number(s);
1034
+ }
1035
+ var formatNumber = (n, decimals) => new Intl.NumberFormat("en-US", decimals != null ? { minimumFractionDigits: decimals, maximumFractionDigits: decimals } : { maximumFractionDigits: 6 }).format(n);
1036
+ function formatMoney(n, currency = "USD", decimals) {
1037
+ try {
1038
+ const whole = Number.isInteger(n) && decimals == null;
1039
+ return new Intl.NumberFormat("en-US", { style: "currency", currency, minimumFractionDigits: whole ? 0 : decimals ?? 2, maximumFractionDigits: whole ? 0 : decimals ?? 2 }).format(n);
1040
+ } catch {
1041
+ return `${formatNumber(n, decimals)} ${currency}`;
1042
+ }
1043
+ }
1044
+ var boolLabels = (col) => ({ yes: col?.trueLabel || "Included", no: col?.falseLabel || "Not included" });
1045
+ function typedCellText(col, type, v) {
1046
+ if (v == null || v === "") return "";
1047
+ const words = String(v).replace(/\s+/g, " ").trim();
1048
+ switch (type) {
1049
+ case "boolean": {
1050
+ const b = toBool(v);
1051
+ return b == null ? words : b ? boolLabels(col).yes : boolLabels(col).no;
1052
+ }
1053
+ case "number": {
1054
+ const n = toNumber(v);
1055
+ return n == null ? words : `${formatNumber(n, col?.decimals)}${col?.unit ? /^[%°]/.test(col.unit) ? col.unit : ` ${col.unit}` : ""}`;
1056
+ }
1057
+ case "money": {
1058
+ const n = toNumber(v);
1059
+ return n == null ? words : formatMoney(n, col?.currency || "USD", col?.decimals);
1060
+ }
1061
+ case "rating": {
1062
+ const n = toNumber(v);
1063
+ return n == null ? words : `${formatNumber(n, 1).replace(/\.0$/, "")} out of ${col?.max ?? 5}`;
1064
+ }
1065
+ default:
1066
+ return words;
1067
+ }
1068
+ }
1069
+ var escHtml = (s) => s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
1070
+ function typedCellView(column, row, cell, inHead) {
1071
+ if (inHead) {
1072
+ const align2 = columnAlign(column, column?.type ?? "text");
1073
+ return { lead: align2 !== "start" ? [["data-align", align2]] : [], tail: column?.description ? [["title", column.description]] : [] };
1074
+ }
1075
+ const type = cellTypeOf(column, row, cell);
1076
+ const words = portableTextWords(cell.content);
1077
+ const v = typeof cell.value === "number" || typeof cell.value === "boolean" ? cell.value : words;
1078
+ const tail = isColumnType(cell.type) ? [["data-type", cell.type]] : [];
1079
+ let inner;
1080
+ if (v === "") {
1081
+ inner = cell.header === true ? "" : '<span aria-hidden="true" class="bt-dash">\u2014</span><span class="bt-sr">No value</span>';
1082
+ tail.push(["data-empty", ""]);
1083
+ } else if (type === "boolean") {
1084
+ const b = toBool(v);
1085
+ if (b == null) inner = escHtml(words);
1086
+ else {
1087
+ const { yes, no } = boolLabels(column);
1088
+ inner = `<span aria-hidden="true" class="bt-bool" data-bool="${b}">${b ? "\u2713" : "\u2013"}</span><span class="bt-sr">${escHtml(b ? yes : no)}</span>`;
1089
+ tail.push(["data-value", String(b)]);
1090
+ }
1091
+ } else if (type === "rating") {
1092
+ const n = toNumber(v);
1093
+ if (n == null) inner = escHtml(words);
1094
+ else {
1095
+ inner = `<span aria-hidden="true" class="bt-star">\u2605</span> ${escHtml(formatNumber(n, 1).replace(/\.0$/, ""))}<span class="bt-dim"> / ${column?.max ?? 5}</span>`;
1096
+ tail.push(["data-value", String(n)]);
1097
+ }
1098
+ } else if (type === "number" || type === "money") {
1099
+ const n = toNumber(v);
1100
+ inner = escHtml(typedCellText(column, type, v));
1101
+ if (n != null) tail.push(["data-value", String(n)]);
1102
+ }
1103
+ const align = columnAlign(column, type);
1104
+ if (align !== "start") tail.push(["data-align", align]);
1105
+ return { lead: [], tail, ...inner === void 0 ? {} : { inner } };
1106
+ }
1107
+ function portableTextWords(content) {
1108
+ 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();
1109
+ }
917
1110
 
918
1111
  // ../richtext/src/key-takeaways.ts
919
1112
  var isRecord2 = (v) => !!v && typeof v === "object" && !Array.isArray(v);
@@ -939,6 +1132,77 @@ function detachedComponentCopy(value) {
939
1132
  ...typeof f.variant === "string" ? { variant: f.variant } : {}
940
1133
  };
941
1134
  }
1135
+ function componentCopyLines(copy) {
1136
+ const line = (v) => {
1137
+ if (typeof v === "number" && Number.isFinite(v)) return String(v);
1138
+ if (typeof v === "string") {
1139
+ const t = v.replace(/<[^>]+>/g, " ").replace(/&nbsp;/g, " ").replace(/\s+/g, " ").trim();
1140
+ return t || null;
1141
+ }
1142
+ if (isRecord3(v)) return line(v.label ?? v.text ?? v.title);
1143
+ return null;
1144
+ };
1145
+ return Object.values(copy.props).map(line).filter((s) => !!s);
1146
+ }
1147
+
1148
+ // ../richtext/src/plain.ts
1149
+ function portableTextToPlainText(value) {
1150
+ if (!Array.isArray(value)) return "";
1151
+ const lines = [];
1152
+ for (const node of value) {
1153
+ if (isPortableTextBlock(node)) {
1154
+ lines.push(node.children.map((c) => isPortableTextSpan(c) ? c.text : "").join(""));
1155
+ continue;
1156
+ }
1157
+ if (node._type === PT_TYPES.code && typeof node.code === "string") {
1158
+ lines.push(node.code);
1159
+ }
1160
+ if (node._type === PT_TYPES.pullQuote) {
1161
+ lines.push(...pullQuoteLines(node));
1162
+ continue;
1163
+ }
1164
+ if (node._type === PT_TYPES.keyTakeaways) {
1165
+ const { title, points } = keyTakeawaysView(node);
1166
+ lines.push(...title ? [title] : [], ...points);
1167
+ continue;
1168
+ }
1169
+ const table = migrateTable(node);
1170
+ if (table) {
1171
+ lines.push(...tableLines(table));
1172
+ continue;
1173
+ }
1174
+ const copy = detachedComponentCopy(node);
1175
+ if (copy) {
1176
+ lines.push(...componentCopyLines(copy));
1177
+ continue;
1178
+ }
1179
+ }
1180
+ return lines.join("\n");
1181
+ }
1182
+ var tableLines = (table) => {
1183
+ const rows = Array.isArray(table.rows) ? table.rows : [];
1184
+ const rowLine = (row) => (Array.isArray(row?.cells) ? row.cells : []).map((cell) => portableTextToPlainText(cell?.content).replace(/\s+/g, " ").trim()).join(" ").trim();
1185
+ const layout = tableGroupLayout(table);
1186
+ const one = (s) => typeof s === "string" ? s.replace(/\s+/g, " ").trim() : "";
1187
+ const head = layout.rows.length ? rows.slice(0, rows.length - layout.rows.reduce((n, r) => n + r.rows.length, 0)) : rows;
1188
+ const lines = [
1189
+ one(table.caption),
1190
+ layout.columns.map((r) => one(r.label)).filter(Boolean).join(" "),
1191
+ ...head.map(rowLine),
1192
+ ...layout.rows.flatMap((run) => [...run.label === void 0 ? [] : [one(run.label)], ...run.rows.map(rowLine)]),
1193
+ one(table.notes),
1194
+ // footnotes, numbered as the html numbers them (V2 tableSearchText reads them too)
1195
+ ...(Array.isArray(table.footnotes) ? table.footnotes : []).map((f, i) => one(`[${i + 1}] ${typeof f?.text === "string" ? f.text : ""}`))
1196
+ ];
1197
+ const fnCount = Array.isArray(table.footnotes) ? table.footnotes.length : 0;
1198
+ const extras = /* @__PURE__ */ new Set([0, 1, lines.length - 1 - fnCount]);
1199
+ return lines.filter((l, i) => l !== "" || !extras.has(i));
1200
+ };
1201
+ var pullQuoteLines = (v) => {
1202
+ const text = (x) => typeof x === "string" ? x.trim() : "";
1203
+ const by = [text(v.name), text(v.role)].filter(Boolean).join(", ");
1204
+ return [text(v.quote), ...by ? [`\u2014 ${by}`] : []].filter(Boolean);
1205
+ };
942
1206
 
943
1207
  // ../richtext/src/read.ts
944
1208
  var isRecord4 = (v) => !!v && typeof v === "object" && !Array.isArray(v);
@@ -1000,41 +1264,104 @@ function Table({ value }) {
1000
1264
  );
1001
1265
  const rows = Array.isArray(value?.rows) ? value.rows : [];
1002
1266
  if (rows.length === 0) return null;
1003
- const cells = (row) => (Array.isArray(row?.cells) ? row.cells : []).map((cell) => {
1004
- const Tag = cell?.header === true ? "th" : "td";
1005
- const span2 = typeof cell?.span === "number" && cell.span > 1 ? cell.span : void 0;
1006
- const rowSpan = typeof cell?.rowSpan === "number" && cell.rowSpan > 1 ? cell.rowSpan : void 0;
1007
- const content = Array.isArray(cell?.content) ? cell.content : [];
1008
- const lone = content.length === 1 && content[0]._type === "block" && content[0].style === "normal" && !content[0].listItem;
1009
- return /* @__PURE__ */ jsx14(Tag, { ...span2 ? { colSpan: span2 } : {}, ...rowSpan ? { rowSpan } : {}, children: /* @__PURE__ */ jsx14(PT, { value: content, components: lone ? inline : components }) }, cell?._key);
1010
- });
1011
- const rowEl = (row) => /* @__PURE__ */ jsx14("tr", { children: cells(row) }, row?._key);
1012
- const headCount = rows.findIndex((r) => r?.head !== true);
1013
- const head = headCount === -1 ? rows.length : headCount;
1267
+ const marked = highlightedColumns(value);
1268
+ const sortable = sortableColumns(value);
1269
+ const design = designOf(value.design);
1270
+ const stacked = design?.responsive === "stack" || design?.responsive === "cards";
1271
+ const role = (r) => stacked ? { role: r } : {};
1272
+ const labels = [];
1273
+ if (stacked) {
1274
+ const headRows = tableSections(value).head;
1275
+ let at = 0;
1276
+ for (const c of Array.isArray(headRows[headRows.length - 1]?.cells) ? headRows[headRows.length - 1].cells : []) {
1277
+ const text = portableTextToPlainText(Array.isArray(c?.content) ? c.content : []).replace(/\s+/g, " ").trim();
1278
+ const w = typeof c?.span === "number" && c.span > 1 ? c.span : 1;
1279
+ for (let i = 0; i < w; i += 1) labels[at + i] = text;
1280
+ at += w;
1281
+ }
1282
+ }
1283
+ const columns = tableColumns(value);
1284
+ const typed = isTypedTable(value);
1285
+ const headSet = new Set(tableSections(value).head);
1286
+ const props = (list) => Object.fromEntries(list);
1287
+ const fnNumber = footnoteNumbers(value);
1288
+ const footnotes = footnotesOf(value.footnotes) ?? [];
1289
+ const tableId = typeof value._key === "string" && value._key ? value._key : "tbl";
1290
+ const cells = (row) => {
1291
+ let col = 0;
1292
+ return (Array.isArray(row?.cells) ? row.cells : []).map((cell) => {
1293
+ const Tag = cell?.header === true ? "th" : "td";
1294
+ const span2 = typeof cell?.span === "number" && cell.span > 1 ? cell.span : void 0;
1295
+ const at = col;
1296
+ col += span2 ?? 1;
1297
+ const rowSpan = typeof cell?.rowSpan === "number" && cell.rowSpan > 1 ? cell.rowSpan : void 0;
1298
+ const n = typeof cell?.note === "string" ? fnNumber.get(cell.note) : void 0;
1299
+ const content = Array.isArray(cell?.content) ? cell.content : [];
1300
+ const lone = content.length === 1 && content[0]._type === "block" && content[0].style === "normal" && !content[0].listItem;
1301
+ const view = typed ? typedCellView(columns[at], row, cell ?? {}, headSet.has(row)) : void 0;
1302
+ const noteAttr = n ? [["data-note", cell.note]] : [];
1303
+ const tail = [...(view?.tail ?? []).filter(([k]) => k !== "data-align"), ...noteAttr, ...(view?.tail ?? []).filter(([k]) => k === "data-align")];
1304
+ const supHtml = n ? `<sup><a href="#${tableId}-fn-${n}" aria-label="Note ${n}">${n}</a></sup>` : "";
1305
+ return /* @__PURE__ */ jsx14(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__ */ jsxs11(Fragment2, { children: [
1306
+ /* @__PURE__ */ jsx14(PT, { value: content, components: lone ? inline : components }),
1307
+ n ? /* @__PURE__ */ jsx14("sup", { children: /* @__PURE__ */ jsx14("a", { href: `#${tableId}-fn-${n}`, "aria-label": `Note ${n}`, children: n }) }) : null
1308
+ ] }) }, cell?._key);
1309
+ });
1310
+ };
1311
+ const rowEl = (row) => /* @__PURE__ */ jsx14("tr", { ...role("row"), ...typeof row?.type === "string" ? { "data-type": row.type } : {}, ...row?.highlight === true ? { "data-highlight": "true" } : {}, children: cells(row) }, row?._key);
1312
+ const sections = tableSections(value);
1313
+ const head = sections.head.length;
1314
+ const foot = sections.foot;
1014
1315
  const layout = tableGroupLayout(value);
1015
1316
  const caption = typeof value.caption === "string" ? value.caption.trim() : "";
1016
1317
  const notes = typeof value.notes === "string" ? value.notes.trim() : "";
1017
- const notesId = notes && typeof value._key === "string" && value._key ? `${value._key}-notes` : void 0;
1318
+ const notesId = (notes || footnotes.length) && typeof value._key === "string" && value._key ? `${value._key}-notes` : void 0;
1018
1319
  const span = (n) => n > 1 ? n : void 0;
1019
- const groupRow = layout.columns.length ? /* @__PURE__ */ jsx14("tr", { children: layout.columns.flatMap(
1020
- (run, i) => run.label === void 0 ? Array.from({ length: run.width }, (_, k) => /* @__PURE__ */ jsx14("td", {}, `${i}-${k}`)) : [/* @__PURE__ */ jsx14("th", { scope: "colgroup", colSpan: span(run.width), "data-group": run.key, children: run.label }, i)]
1320
+ const groupRow = layout.columns.length ? /* @__PURE__ */ jsx14("tr", { ...role("row"), children: layout.columns.flatMap(
1321
+ (run, i) => run.label === void 0 ? Array.from({ length: run.width }, (_, k) => /* @__PURE__ */ jsx14("td", { ...role("cell") }, `${i}-${k}`)) : [/* @__PURE__ */ jsx14("th", { ...role("columnheader"), scope: "colgroup", colSpan: span(run.width), "data-group": run.key, children: run.label }, i)]
1021
1322
  ) }) : null;
1022
- const bodies = layout.rows.length ? layout.rows.map((run, i) => /* @__PURE__ */ jsxs11("tbody", { "data-key": run.key, children: [
1023
- run.label !== void 0 && /* @__PURE__ */ jsx14("tr", { children: /* @__PURE__ */ jsx14("th", { scope: "rowgroup", colSpan: span(layout.width), children: run.label }) }),
1323
+ const bodies = layout.rows.length ? layout.rows.map((run, i) => /* @__PURE__ */ jsxs11("tbody", { ...role("rowgroup"), "data-key": run.key, children: [
1324
+ run.label !== void 0 && /* @__PURE__ */ jsx14("tr", { ...role("row"), children: /* @__PURE__ */ jsx14("th", { ...role("rowheader"), scope: "rowgroup", colSpan: span(layout.width), children: run.label }) }),
1024
1325
  run.rows.map(rowEl)
1025
- ] }, i)) : head < rows.length && /* @__PURE__ */ jsx14("tbody", { children: rows.slice(head).map(rowEl) });
1026
- const table = /* @__PURE__ */ jsxs11("table", { ...value.legacy === true ? { className: "bcms-table" } : {}, ...notesId ? { "aria-describedby": notesId } : {}, children: [
1326
+ ] }, i)) : sections.body.length > 0 && /* @__PURE__ */ jsx14("tbody", { ...role("rowgroup"), children: sections.body.map(rowEl) });
1327
+ const colProps = (i) => {
1328
+ const c = columns[i];
1329
+ return {
1330
+ ...c ? { "data-type": c.type ?? "text" } : {},
1331
+ ...c?.unit ? { "data-unit": c.unit } : {},
1332
+ ...c?.currency ? { "data-currency": c.currency } : {},
1333
+ ...c?.decimals != null ? { "data-decimals": String(c.decimals) } : {},
1334
+ ...c?.align ? { "data-align": c.align } : {},
1335
+ ...sortable.has(i) ? { "data-sortable": "true" } : {},
1336
+ ...c?.trueLabel ? { "data-true": c.trueLabel } : {},
1337
+ ...c?.falseLabel ? { "data-false": c.falseLabel } : {},
1338
+ ...c?.max != null ? { "data-max": String(c.max) } : {},
1339
+ ...c?.description ? { "data-description": c.description } : {}
1340
+ };
1341
+ };
1342
+ const table = /* @__PURE__ */ jsxs11("table", { ...role("table"), ...value.legacy === true ? { className: "bcms-table" } : {}, ...notesId ? { "aria-describedby": notesId } : {}, children: [
1027
1343
  caption && /* @__PURE__ */ jsx14("caption", { children: caption }),
1028
- layout.columns.map((run, i) => /* @__PURE__ */ jsx14("colgroup", { span: span(run.width) }, i)),
1029
- (groupRow || head > 0) && /* @__PURE__ */ jsxs11("thead", { children: [
1344
+ sortable.size || columns.some(Boolean) ? (layout.columns.length ? layout.columns.map((run) => run.width) : [layout.width]).map((w, i, all) => {
1345
+ const from = all.slice(0, i).reduce((n, x) => n + x, 0);
1346
+ return /* @__PURE__ */ jsx14("colgroup", { children: Array.from({ length: w }, (_, k) => /* @__PURE__ */ jsx14("col", { ...colProps(from + k) }, k)) }, i);
1347
+ }) : layout.columns.map((run, i) => /* @__PURE__ */ jsx14("colgroup", { span: span(run.width) }, i)),
1348
+ (groupRow || head > 0) && /* @__PURE__ */ jsxs11("thead", { ...role("rowgroup"), children: [
1030
1349
  groupRow,
1031
1350
  rows.slice(0, head).map(rowEl)
1032
1351
  ] }),
1033
- bodies
1352
+ bodies,
1353
+ foot.length > 0 && /* @__PURE__ */ jsx14("tfoot", { ...role("rowgroup"), children: foot.map(rowEl) })
1034
1354
  ] });
1035
- return notes ? /* @__PURE__ */ jsxs11("figure", { "data-rt": "table", children: [
1355
+ const filter = value.filterable === true;
1356
+ const ld = jsonLdOf(value.jsonLd);
1357
+ const ldAttrs = ld ? { "data-jsonld": ld.type, ...ld.axis ? { "data-jsonld-axis": ld.axis } : {}, ...ld.priceRow ? { "data-jsonld-price": ld.priceRow } : {} } : {};
1358
+ const designAttrs = design ? { ...design.responsive ? { "data-responsive": design.responsive } : {}, ...design.sticky ? { "data-sticky": "true" } : {} } : {};
1359
+ return notes || filter || ld || design || footnotes.length ? /* @__PURE__ */ jsxs11("figure", { "data-rt": "table", ...designAttrs, ...filter ? { "data-filter": "true" } : {}, ...ldAttrs, children: [
1036
1360
  table,
1037
- /* @__PURE__ */ jsx14("p", { "data-notes": "", id: notesId, children: notes })
1361
+ footnotes.length ? /* @__PURE__ */ jsxs11("div", { className: "bt-notes", id: `${tableId}-notes`, children: [
1362
+ notes ? /* @__PURE__ */ jsx14("p", { "data-notes": "", children: notes }) : null,
1363
+ /* @__PURE__ */ jsx14("ol", { "data-footnotes": "", children: footnotes.map((f, i) => /* @__PURE__ */ jsx14("li", { id: `${tableId}-fn-${i + 1}`, "data-key": f._key, children: f.text }, f._key)) })
1364
+ ] }) : notes ? /* @__PURE__ */ jsx14("p", { "data-notes": "", id: notesId, children: notes }) : null
1038
1365
  ] }) : table;
1039
1366
  }
1040
1367
  function buildComponents(renderBlock) {