@underverse-ui/underverse 1.0.151 → 1.0.152

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
@@ -25993,6 +25993,10 @@ function getTableCellAttrs(cell, styles, defaultBackgroundColor) {
25993
25993
  if (borderAttrs.borderColor) attrs.borderColor = borderAttrs.borderColor;
25994
25994
  if (borderAttrs.borderStyle) attrs.borderStyle = borderAttrs.borderStyle;
25995
25995
  if (borderAttrs.borderWidth) attrs.borderWidth = borderAttrs.borderWidth;
25996
+ if (cell.getAttribute("data-cell-id")) attrs.cellId = cell.getAttribute("data-cell-id") ?? void 0;
25997
+ if (cell.getAttribute("data-number-format")) attrs.numberFormat = cell.getAttribute("data-number-format") ?? void 0;
25998
+ if (cell.getAttribute("data-formula")) attrs.formula = cell.getAttribute("data-formula") ?? void 0;
25999
+ if (cell.getAttribute("data-computed-value")) attrs.computedValue = cell.getAttribute("data-computed-value") ?? void 0;
25996
26000
  if (colspan > 1) attrs.colspan = colspan;
25997
26001
  if (rowspan > 1) attrs.rowspan = rowspan;
25998
26002
  if (colwidth) attrs.colwidth = colwidth;
@@ -27620,6 +27624,16 @@ var CodeBlockView = ({
27620
27624
  };
27621
27625
 
27622
27626
  // src/components/UEditor/extensions.ts
27627
+ function getFormulaStateAttributes(attributes) {
27628
+ const formula = attributes["data-formula"];
27629
+ if (!formula) {
27630
+ return {};
27631
+ }
27632
+ const computedValue = String(attributes["data-computed-value"] ?? "");
27633
+ return {
27634
+ "data-formula-state": computedValue.startsWith("#") ? "error" : "computed"
27635
+ };
27636
+ }
27623
27637
  var CustomTableCell = TableCell2.extend({
27624
27638
  addAttributes() {
27625
27639
  return {
@@ -27722,7 +27736,7 @@ var CustomTableCell = TableCell2.extend({
27722
27736
  mergedStyle = mergedStyle.replace(/^;/, "").trim();
27723
27737
  return [
27724
27738
  "td",
27725
- mergeAttributes5(this.options.HTMLAttributes, HTMLAttributes, mergedStyle ? { style: mergedStyle } : {}),
27739
+ mergeAttributes5(this.options.HTMLAttributes, HTMLAttributes, getFormulaStateAttributes(HTMLAttributes), mergedStyle ? { style: mergedStyle } : {}),
27726
27740
  0
27727
27741
  ];
27728
27742
  }
@@ -27829,7 +27843,7 @@ var CustomTableHeader = TableHeader2.extend({
27829
27843
  mergedStyle = mergedStyle.replace(/^;/, "").trim();
27830
27844
  return [
27831
27845
  "th",
27832
- mergeAttributes5(this.options.HTMLAttributes, HTMLAttributes, mergedStyle ? { style: mergedStyle } : {}),
27846
+ mergeAttributes5(this.options.HTMLAttributes, HTMLAttributes, getFormulaStateAttributes(HTMLAttributes), mergedStyle ? { style: mergedStyle } : {}),
27833
27847
  0
27834
27848
  ];
27835
27849
  }
@@ -29910,6 +29924,99 @@ function getTableCellRangeLabels(range) {
29910
29924
  function normalizeTableFormula(formula) {
29911
29925
  return formula.trim().replace(/^=/, "").trim();
29912
29926
  }
29927
+ function formatFormulaError(error) {
29928
+ return `#${error.toUpperCase()}`;
29929
+ }
29930
+ function getTableFormulaReferences(formula) {
29931
+ const normalized = normalizeTableFormula(formula);
29932
+ if (!normalized) return [];
29933
+ const tokens = tokenizeFormula(normalized);
29934
+ if (!tokens) return [];
29935
+ const references = /* @__PURE__ */ new Set();
29936
+ for (const token of tokens) {
29937
+ if (token.type === "cell") {
29938
+ references.add(token.value);
29939
+ } else if (token.type === "range") {
29940
+ const range = parseTableCellRange(token.value);
29941
+ if (!range) continue;
29942
+ for (const label of getTableCellRangeLabels(range)) {
29943
+ references.add(label);
29944
+ }
29945
+ }
29946
+ }
29947
+ return Array.from(references);
29948
+ }
29949
+ function buildTableFormulaDependencyGraph(cells) {
29950
+ const dependencies = /* @__PURE__ */ new Map();
29951
+ const dependents = /* @__PURE__ */ new Map();
29952
+ const formulas = /* @__PURE__ */ new Map();
29953
+ for (const cell of cells) {
29954
+ const label = cell.label.toUpperCase();
29955
+ formulas.set(label, cell.formula);
29956
+ dependencies.set(label, new Set(getTableFormulaReferences(cell.formula)));
29957
+ if (!dependents.has(label)) {
29958
+ dependents.set(label, /* @__PURE__ */ new Set());
29959
+ }
29960
+ }
29961
+ for (const [label, refs] of dependencies) {
29962
+ for (const ref of refs) {
29963
+ if (!dependents.has(ref)) {
29964
+ dependents.set(ref, /* @__PURE__ */ new Set());
29965
+ }
29966
+ dependents.get(ref)?.add(label);
29967
+ }
29968
+ }
29969
+ return { dependencies, dependents, formulas };
29970
+ }
29971
+ function getTableFormulaCircularReferences(graph) {
29972
+ const visiting = /* @__PURE__ */ new Set();
29973
+ const visited = /* @__PURE__ */ new Set();
29974
+ const circular = /* @__PURE__ */ new Set();
29975
+ const stack = [];
29976
+ const visit = (label) => {
29977
+ if (visiting.has(label)) {
29978
+ const start = stack.indexOf(label);
29979
+ for (const cycleLabel of start >= 0 ? stack.slice(start) : [label]) {
29980
+ circular.add(cycleLabel);
29981
+ }
29982
+ return;
29983
+ }
29984
+ if (visited.has(label)) return;
29985
+ visiting.add(label);
29986
+ stack.push(label);
29987
+ for (const ref of graph.dependencies.get(label) ?? []) {
29988
+ if (graph.formulas.has(ref)) {
29989
+ visit(ref);
29990
+ }
29991
+ }
29992
+ stack.pop();
29993
+ visiting.delete(label);
29994
+ visited.add(label);
29995
+ };
29996
+ for (const label of graph.formulas.keys()) {
29997
+ visit(label);
29998
+ }
29999
+ return circular;
30000
+ }
30001
+ function getTableFormulaRecalculationOrder(graph) {
30002
+ const circular = getTableFormulaCircularReferences(graph);
30003
+ const visited = /* @__PURE__ */ new Set();
30004
+ const order = [];
30005
+ const visit = (label) => {
30006
+ if (visited.has(label) || circular.has(label)) return;
30007
+ visited.add(label);
30008
+ for (const ref of graph.dependencies.get(label) ?? []) {
30009
+ if (graph.formulas.has(ref)) {
30010
+ visit(ref);
30011
+ }
30012
+ }
30013
+ order.push(label);
30014
+ };
30015
+ for (const label of graph.formulas.keys()) {
30016
+ visit(label);
30017
+ }
30018
+ return { order, circular };
30019
+ }
29913
30020
  function evaluateBasicTableFormula(formula, getCellValue) {
29914
30021
  const normalized = normalizeTableFormula(formula);
29915
30022
  if (!normalized) {
@@ -30114,6 +30221,7 @@ var FormulaParser = class {
30114
30221
  };
30115
30222
 
30116
30223
  // src/components/UEditor/table-formula-commands.ts
30224
+ var UEDITOR_TABLE_FORMULA_RECALCULATE_META = "ueditorTableFormulaRecalculate";
30117
30225
  function collectChildren2(node) {
30118
30226
  const children = [];
30119
30227
  node.forEach((child) => children.push(child));
@@ -30144,6 +30252,13 @@ function safeFindCell2(map, relativePos) {
30144
30252
  function getCellText(cellNode) {
30145
30253
  return cellNode.textBetween(0, cellNode.content.size, "\n").trim();
30146
30254
  }
30255
+ function createCellDisplayContent(cellNode, displayValue) {
30256
+ const paragraphType = cellNode.type.schema.nodes.paragraph;
30257
+ if (!paragraphType || !displayValue) {
30258
+ return paragraphType ? [paragraphType.create()] : cellNode.content;
30259
+ }
30260
+ return [paragraphType.create(null, cellNode.type.schema.text(displayValue))];
30261
+ }
30147
30262
  function buildTableValueGetter(tableNode) {
30148
30263
  const map = TableMap2.get(tableNode);
30149
30264
  const values = /* @__PURE__ */ new Map();
@@ -30158,6 +30273,20 @@ function buildTableValueGetter(tableNode) {
30158
30273
  }
30159
30274
  return (label) => values.get(label.toUpperCase());
30160
30275
  }
30276
+ function buildTableValueMap(tableNode) {
30277
+ const map = TableMap2.get(tableNode);
30278
+ const values = /* @__PURE__ */ new Map();
30279
+ for (const rowInfo of getTableRows2(tableNode)) {
30280
+ for (const entry of rowInfo.cells) {
30281
+ const rect = safeFindCell2(map, entry.relativePos);
30282
+ if (!rect) continue;
30283
+ const label = `${indexToColumnName(rect.left)}${rect.top + 1}`;
30284
+ const computedValue = entry.node.attrs.computedValue;
30285
+ values.set(label, typeof computedValue === "string" && computedValue.trim() ? computedValue : getCellText(entry.node));
30286
+ }
30287
+ }
30288
+ return values;
30289
+ }
30161
30290
  function getFormulaComputedValue(formula, tableNode) {
30162
30291
  const result = evaluateBasicTableFormula(formula, buildTableValueGetter(tableNode));
30163
30292
  return result.error ? `#${result.error.toUpperCase()}` : String(result.value);
@@ -30194,37 +30323,132 @@ function setSelectedTableCellFormula(editor, formula) {
30194
30323
  function clearSelectedTableCellFormula(editor) {
30195
30324
  return setSelectedTableCellFormula(editor, "");
30196
30325
  }
30197
- function recalculateSelectedTable(editor) {
30198
- const rect = selectedRect2(editor.state);
30199
- const tableNode = rect.table;
30200
- const map = TableMap2.get(tableNode);
30201
- const getCellValue = buildTableValueGetter(tableNode);
30326
+ function promoteFormulaTextInTableNode(tableNode) {
30202
30327
  let changed = false;
30203
30328
  const rows = getTableRows2(tableNode).map((rowInfo) => {
30204
30329
  const cells = collectChildren2(rowInfo.node);
30330
+ for (const entry of rowInfo.cells) {
30331
+ const text = getCellText(entry.node);
30332
+ const formula = text.startsWith("=") ? normalizeFormulaInput(text) : "";
30333
+ if (!formula || entry.node.attrs.formula === formula) continue;
30334
+ cells[entry.index] = entry.node.type.create(
30335
+ {
30336
+ ...entry.node.attrs,
30337
+ formula,
30338
+ computedValue: null
30339
+ },
30340
+ entry.node.content,
30341
+ entry.node.marks
30342
+ );
30343
+ changed = true;
30344
+ }
30345
+ return rowInfo.node.type.create(rowInfo.node.attrs, cells);
30346
+ });
30347
+ return {
30348
+ tableNode: changed ? tableNode.type.create(tableNode.attrs, rows) : tableNode,
30349
+ changed
30350
+ };
30351
+ }
30352
+ function recalculateTableNode(tableNode) {
30353
+ const promoted = promoteFormulaTextInTableNode(tableNode);
30354
+ tableNode = promoted.tableNode;
30355
+ const map = TableMap2.get(tableNode);
30356
+ const values = buildTableValueMap(tableNode);
30357
+ const formulaEntries = /* @__PURE__ */ new Map();
30358
+ let changed = false;
30359
+ const rowInfos = getTableRows2(tableNode);
30360
+ for (const [rowIndex, rowInfo] of rowInfos.entries()) {
30205
30361
  for (const entry of rowInfo.cells) {
30206
30362
  const formula = typeof entry.node.attrs.formula === "string" ? entry.node.attrs.formula.trim() : "";
30207
30363
  if (!formula) continue;
30208
30364
  const rectForCell = safeFindCell2(map, entry.relativePos);
30209
30365
  if (!rectForCell) continue;
30210
- const result = evaluateBasicTableFormula(formula, getCellValue);
30211
- const computedValue = result.error ? `#${result.error.toUpperCase()}` : String(result.value);
30212
- if (entry.node.attrs.computedValue === computedValue) continue;
30366
+ const label = `${indexToColumnName(rectForCell.left)}${rectForCell.top + 1}`;
30367
+ formulaEntries.set(label, {
30368
+ rowIndex,
30369
+ cellIndex: entry.index,
30370
+ node: entry.node,
30371
+ formula
30372
+ });
30373
+ }
30374
+ }
30375
+ const graph = buildTableFormulaDependencyGraph(
30376
+ Array.from(formulaEntries, ([label, entry]) => ({
30377
+ label,
30378
+ formula: entry.formula
30379
+ }))
30380
+ );
30381
+ const { order, circular } = getTableFormulaRecalculationOrder(graph);
30382
+ const computedValues = /* @__PURE__ */ new Map();
30383
+ const getCellValue = (label) => values.get(label.toUpperCase());
30384
+ for (const label of circular) {
30385
+ computedValues.set(label, formatFormulaError("circular-reference"));
30386
+ values.set(label, formatFormulaError("circular-reference"));
30387
+ }
30388
+ for (const label of order) {
30389
+ const entry = formulaEntries.get(label);
30390
+ if (!entry) continue;
30391
+ const result = evaluateBasicTableFormula(entry.formula, getCellValue);
30392
+ const computedValue = result.error ? formatFormulaError(result.error) : String(result.value);
30393
+ computedValues.set(label, computedValue);
30394
+ values.set(label, computedValue);
30395
+ }
30396
+ const rows = rowInfos.map((rowInfo) => {
30397
+ const cells = collectChildren2(rowInfo.node);
30398
+ for (const entry of rowInfo.cells) {
30399
+ const rectForCell = safeFindCell2(map, entry.relativePos);
30400
+ if (!rectForCell) continue;
30401
+ const label = `${indexToColumnName(rectForCell.left)}${rectForCell.top + 1}`;
30402
+ const computedValue = computedValues.get(label);
30403
+ if (computedValue == null) continue;
30404
+ const contentMatchesComputedValue = getCellText(entry.node) === computedValue;
30405
+ if (entry.node.attrs.computedValue === computedValue && contentMatchesComputedValue) continue;
30213
30406
  cells[entry.index] = entry.node.type.create(
30214
30407
  {
30215
30408
  ...entry.node.attrs,
30216
30409
  computedValue
30217
30410
  },
30218
- entry.node.content,
30411
+ createCellDisplayContent(entry.node, computedValue),
30219
30412
  entry.node.marks
30220
30413
  );
30221
30414
  changed = true;
30222
30415
  }
30223
30416
  return rowInfo.node.type.create(rowInfo.node.attrs, cells);
30224
30417
  });
30225
- if (!changed) return false;
30226
- const nextTable = tableNode.type.create(tableNode.attrs, rows);
30227
- editor.view.dispatch(editor.state.tr.replaceWith(rect.tableStart - 1, rect.tableStart - 1 + tableNode.nodeSize, nextTable));
30418
+ if (!changed) return promoted.changed ? tableNode : null;
30419
+ return tableNode.type.create(tableNode.attrs, rows);
30420
+ }
30421
+ function recalculateSelectedTable(editor) {
30422
+ const rect = selectedRect2(editor.state);
30423
+ const tableNode = rect.table;
30424
+ const nextTable = recalculateTableNode(tableNode);
30425
+ if (!nextTable) return false;
30426
+ editor.view.dispatch(
30427
+ editor.state.tr.replaceWith(rect.tableStart - 1, rect.tableStart - 1 + tableNode.nodeSize, nextTable).setMeta(UEDITOR_TABLE_FORMULA_RECALCULATE_META, true)
30428
+ );
30429
+ dispatchTableLayoutChange(editor);
30430
+ return true;
30431
+ }
30432
+ function recalculateAllTableFormulas(editor) {
30433
+ const replacements = [];
30434
+ editor.state.doc.descendants((node, pos) => {
30435
+ if (node.type.name !== "table") return true;
30436
+ const nextTable = recalculateTableNode(node);
30437
+ if (nextTable) {
30438
+ replacements.push({
30439
+ from: pos,
30440
+ to: pos + node.nodeSize,
30441
+ node: nextTable
30442
+ });
30443
+ }
30444
+ return false;
30445
+ });
30446
+ if (replacements.length === 0) return false;
30447
+ let tr = editor.state.tr;
30448
+ for (const replacement of replacements.reverse()) {
30449
+ tr = tr.replaceWith(replacement.from, replacement.to, replacement.node);
30450
+ }
30451
+ editor.view.dispatch(tr.setMeta(UEDITOR_TABLE_FORMULA_RECALCULATE_META, true));
30228
30452
  dispatchTableLayoutChange(editor);
30229
30453
  return true;
30230
30454
  }
@@ -38261,6 +38485,7 @@ var UEditor = React82.forwardRef(({
38261
38485
  const effectivePlaceholder = placeholder ?? t("placeholder");
38262
38486
  const inFlightPrepareRef = useRef37(null);
38263
38487
  const lastAppliedContentRef = useRef37(content ?? "");
38488
+ const scheduledFormulaRecalculateRef = useRef37(false);
38264
38489
  const resolvedUploadFile = useMemo24(() => {
38265
38490
  if (uploadFile) return uploadFile;
38266
38491
  if (uploadFileForSave) {
@@ -38324,7 +38549,16 @@ var UEditor = React82.forwardRef(({
38324
38549
  class: UEDITOR_PROSEMIRROR_CLASS_NAME
38325
38550
  }
38326
38551
  },
38327
- onUpdate: ({ editor: editor2 }) => {
38552
+ onUpdate: ({ editor: editor2, transaction }) => {
38553
+ if (!transaction.getMeta(UEDITOR_TABLE_FORMULA_RECALCULATE_META) && !scheduledFormulaRecalculateRef.current) {
38554
+ scheduledFormulaRecalculateRef.current = true;
38555
+ queueMicrotask(() => {
38556
+ scheduledFormulaRecalculateRef.current = false;
38557
+ if (!editor2.isDestroyed) {
38558
+ recalculateAllTableFormulas(editor2);
38559
+ }
38560
+ });
38561
+ }
38328
38562
  const html = editor2.getHTML();
38329
38563
  onChange?.(html);
38330
38564
  onHtmlChange?.(html);
@@ -38362,6 +38596,14 @@ var UEditor = React82.forwardRef(({
38362
38596
  }),
38363
38597
  [content, editor, uploadImageForSave, uploadFileForSave, uploadImageConcurrency]
38364
38598
  );
38599
+ useEffect38(() => {
38600
+ if (!editor) return;
38601
+ queueMicrotask(() => {
38602
+ if (!editor.isDestroyed) {
38603
+ recalculateAllTableFormulas(editor);
38604
+ }
38605
+ });
38606
+ }, [editor]);
38365
38607
  useEffect38(() => {
38366
38608
  if (!editor) return;
38367
38609
  const nextContent = content ?? "";
@@ -38371,6 +38613,11 @@ var UEditor = React82.forwardRef(({
38371
38613
  Promise.resolve().then(() => {
38372
38614
  if (!editor.isDestroyed) {
38373
38615
  editor.commands.setContent(nextContent, { emitUpdate: false });
38616
+ queueMicrotask(() => {
38617
+ if (!editor.isDestroyed) {
38618
+ recalculateAllTableFormulas(editor);
38619
+ }
38620
+ });
38374
38621
  }
38375
38622
  });
38376
38623
  }