@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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "package": "@underverse-ui/underverse",
3
- "version": "1.0.151",
3
+ "version": "1.0.152",
4
4
  "sourceEntry": "src/index.ts",
5
5
  "totalExports": 232,
6
6
  "exports": [
package/dist/index.cjs CHANGED
@@ -26148,6 +26148,10 @@ function getTableCellAttrs(cell, styles, defaultBackgroundColor) {
26148
26148
  if (borderAttrs.borderColor) attrs.borderColor = borderAttrs.borderColor;
26149
26149
  if (borderAttrs.borderStyle) attrs.borderStyle = borderAttrs.borderStyle;
26150
26150
  if (borderAttrs.borderWidth) attrs.borderWidth = borderAttrs.borderWidth;
26151
+ if (cell.getAttribute("data-cell-id")) attrs.cellId = cell.getAttribute("data-cell-id") ?? void 0;
26152
+ if (cell.getAttribute("data-number-format")) attrs.numberFormat = cell.getAttribute("data-number-format") ?? void 0;
26153
+ if (cell.getAttribute("data-formula")) attrs.formula = cell.getAttribute("data-formula") ?? void 0;
26154
+ if (cell.getAttribute("data-computed-value")) attrs.computedValue = cell.getAttribute("data-computed-value") ?? void 0;
26151
26155
  if (colspan > 1) attrs.colspan = colspan;
26152
26156
  if (rowspan > 1) attrs.rowspan = rowspan;
26153
26157
  if (colwidth) attrs.colwidth = colwidth;
@@ -27775,6 +27779,16 @@ var CodeBlockView = ({
27775
27779
  };
27776
27780
 
27777
27781
  // src/components/UEditor/extensions.ts
27782
+ function getFormulaStateAttributes(attributes) {
27783
+ const formula = attributes["data-formula"];
27784
+ if (!formula) {
27785
+ return {};
27786
+ }
27787
+ const computedValue = String(attributes["data-computed-value"] ?? "");
27788
+ return {
27789
+ "data-formula-state": computedValue.startsWith("#") ? "error" : "computed"
27790
+ };
27791
+ }
27778
27792
  var CustomTableCell = import_extension_table_cell.default.extend({
27779
27793
  addAttributes() {
27780
27794
  return {
@@ -27877,7 +27891,7 @@ var CustomTableCell = import_extension_table_cell.default.extend({
27877
27891
  mergedStyle = mergedStyle.replace(/^;/, "").trim();
27878
27892
  return [
27879
27893
  "td",
27880
- (0, import_core13.mergeAttributes)(this.options.HTMLAttributes, HTMLAttributes, mergedStyle ? { style: mergedStyle } : {}),
27894
+ (0, import_core13.mergeAttributes)(this.options.HTMLAttributes, HTMLAttributes, getFormulaStateAttributes(HTMLAttributes), mergedStyle ? { style: mergedStyle } : {}),
27881
27895
  0
27882
27896
  ];
27883
27897
  }
@@ -27984,7 +27998,7 @@ var CustomTableHeader = import_extension_table_header.default.extend({
27984
27998
  mergedStyle = mergedStyle.replace(/^;/, "").trim();
27985
27999
  return [
27986
28000
  "th",
27987
- (0, import_core13.mergeAttributes)(this.options.HTMLAttributes, HTMLAttributes, mergedStyle ? { style: mergedStyle } : {}),
28001
+ (0, import_core13.mergeAttributes)(this.options.HTMLAttributes, HTMLAttributes, getFormulaStateAttributes(HTMLAttributes), mergedStyle ? { style: mergedStyle } : {}),
27988
28002
  0
27989
28003
  ];
27990
28004
  }
@@ -30005,6 +30019,99 @@ function getTableCellRangeLabels(range) {
30005
30019
  function normalizeTableFormula(formula) {
30006
30020
  return formula.trim().replace(/^=/, "").trim();
30007
30021
  }
30022
+ function formatFormulaError(error) {
30023
+ return `#${error.toUpperCase()}`;
30024
+ }
30025
+ function getTableFormulaReferences(formula) {
30026
+ const normalized = normalizeTableFormula(formula);
30027
+ if (!normalized) return [];
30028
+ const tokens = tokenizeFormula(normalized);
30029
+ if (!tokens) return [];
30030
+ const references = /* @__PURE__ */ new Set();
30031
+ for (const token of tokens) {
30032
+ if (token.type === "cell") {
30033
+ references.add(token.value);
30034
+ } else if (token.type === "range") {
30035
+ const range = parseTableCellRange(token.value);
30036
+ if (!range) continue;
30037
+ for (const label of getTableCellRangeLabels(range)) {
30038
+ references.add(label);
30039
+ }
30040
+ }
30041
+ }
30042
+ return Array.from(references);
30043
+ }
30044
+ function buildTableFormulaDependencyGraph(cells) {
30045
+ const dependencies = /* @__PURE__ */ new Map();
30046
+ const dependents = /* @__PURE__ */ new Map();
30047
+ const formulas = /* @__PURE__ */ new Map();
30048
+ for (const cell of cells) {
30049
+ const label = cell.label.toUpperCase();
30050
+ formulas.set(label, cell.formula);
30051
+ dependencies.set(label, new Set(getTableFormulaReferences(cell.formula)));
30052
+ if (!dependents.has(label)) {
30053
+ dependents.set(label, /* @__PURE__ */ new Set());
30054
+ }
30055
+ }
30056
+ for (const [label, refs] of dependencies) {
30057
+ for (const ref of refs) {
30058
+ if (!dependents.has(ref)) {
30059
+ dependents.set(ref, /* @__PURE__ */ new Set());
30060
+ }
30061
+ dependents.get(ref)?.add(label);
30062
+ }
30063
+ }
30064
+ return { dependencies, dependents, formulas };
30065
+ }
30066
+ function getTableFormulaCircularReferences(graph) {
30067
+ const visiting = /* @__PURE__ */ new Set();
30068
+ const visited = /* @__PURE__ */ new Set();
30069
+ const circular = /* @__PURE__ */ new Set();
30070
+ const stack = [];
30071
+ const visit = (label) => {
30072
+ if (visiting.has(label)) {
30073
+ const start = stack.indexOf(label);
30074
+ for (const cycleLabel of start >= 0 ? stack.slice(start) : [label]) {
30075
+ circular.add(cycleLabel);
30076
+ }
30077
+ return;
30078
+ }
30079
+ if (visited.has(label)) return;
30080
+ visiting.add(label);
30081
+ stack.push(label);
30082
+ for (const ref of graph.dependencies.get(label) ?? []) {
30083
+ if (graph.formulas.has(ref)) {
30084
+ visit(ref);
30085
+ }
30086
+ }
30087
+ stack.pop();
30088
+ visiting.delete(label);
30089
+ visited.add(label);
30090
+ };
30091
+ for (const label of graph.formulas.keys()) {
30092
+ visit(label);
30093
+ }
30094
+ return circular;
30095
+ }
30096
+ function getTableFormulaRecalculationOrder(graph) {
30097
+ const circular = getTableFormulaCircularReferences(graph);
30098
+ const visited = /* @__PURE__ */ new Set();
30099
+ const order = [];
30100
+ const visit = (label) => {
30101
+ if (visited.has(label) || circular.has(label)) return;
30102
+ visited.add(label);
30103
+ for (const ref of graph.dependencies.get(label) ?? []) {
30104
+ if (graph.formulas.has(ref)) {
30105
+ visit(ref);
30106
+ }
30107
+ }
30108
+ order.push(label);
30109
+ };
30110
+ for (const label of graph.formulas.keys()) {
30111
+ visit(label);
30112
+ }
30113
+ return { order, circular };
30114
+ }
30008
30115
  function evaluateBasicTableFormula(formula, getCellValue) {
30009
30116
  const normalized = normalizeTableFormula(formula);
30010
30117
  if (!normalized) {
@@ -30209,6 +30316,7 @@ var FormulaParser = class {
30209
30316
  };
30210
30317
 
30211
30318
  // src/components/UEditor/table-formula-commands.ts
30319
+ var UEDITOR_TABLE_FORMULA_RECALCULATE_META = "ueditorTableFormulaRecalculate";
30212
30320
  function collectChildren2(node) {
30213
30321
  const children = [];
30214
30322
  node.forEach((child) => children.push(child));
@@ -30239,6 +30347,13 @@ function safeFindCell2(map, relativePos) {
30239
30347
  function getCellText(cellNode) {
30240
30348
  return cellNode.textBetween(0, cellNode.content.size, "\n").trim();
30241
30349
  }
30350
+ function createCellDisplayContent(cellNode, displayValue) {
30351
+ const paragraphType = cellNode.type.schema.nodes.paragraph;
30352
+ if (!paragraphType || !displayValue) {
30353
+ return paragraphType ? [paragraphType.create()] : cellNode.content;
30354
+ }
30355
+ return [paragraphType.create(null, cellNode.type.schema.text(displayValue))];
30356
+ }
30242
30357
  function buildTableValueGetter(tableNode) {
30243
30358
  const map = import_tables2.TableMap.get(tableNode);
30244
30359
  const values = /* @__PURE__ */ new Map();
@@ -30253,6 +30368,20 @@ function buildTableValueGetter(tableNode) {
30253
30368
  }
30254
30369
  return (label) => values.get(label.toUpperCase());
30255
30370
  }
30371
+ function buildTableValueMap(tableNode) {
30372
+ const map = import_tables2.TableMap.get(tableNode);
30373
+ const values = /* @__PURE__ */ new Map();
30374
+ for (const rowInfo of getTableRows2(tableNode)) {
30375
+ for (const entry of rowInfo.cells) {
30376
+ const rect = safeFindCell2(map, entry.relativePos);
30377
+ if (!rect) continue;
30378
+ const label = `${indexToColumnName(rect.left)}${rect.top + 1}`;
30379
+ const computedValue = entry.node.attrs.computedValue;
30380
+ values.set(label, typeof computedValue === "string" && computedValue.trim() ? computedValue : getCellText(entry.node));
30381
+ }
30382
+ }
30383
+ return values;
30384
+ }
30256
30385
  function getFormulaComputedValue(formula, tableNode) {
30257
30386
  const result = evaluateBasicTableFormula(formula, buildTableValueGetter(tableNode));
30258
30387
  return result.error ? `#${result.error.toUpperCase()}` : String(result.value);
@@ -30289,37 +30418,132 @@ function setSelectedTableCellFormula(editor, formula) {
30289
30418
  function clearSelectedTableCellFormula(editor) {
30290
30419
  return setSelectedTableCellFormula(editor, "");
30291
30420
  }
30292
- function recalculateSelectedTable(editor) {
30293
- const rect = (0, import_tables2.selectedRect)(editor.state);
30294
- const tableNode = rect.table;
30295
- const map = import_tables2.TableMap.get(tableNode);
30296
- const getCellValue = buildTableValueGetter(tableNode);
30421
+ function promoteFormulaTextInTableNode(tableNode) {
30297
30422
  let changed = false;
30298
30423
  const rows = getTableRows2(tableNode).map((rowInfo) => {
30299
30424
  const cells = collectChildren2(rowInfo.node);
30425
+ for (const entry of rowInfo.cells) {
30426
+ const text = getCellText(entry.node);
30427
+ const formula = text.startsWith("=") ? normalizeFormulaInput(text) : "";
30428
+ if (!formula || entry.node.attrs.formula === formula) continue;
30429
+ cells[entry.index] = entry.node.type.create(
30430
+ {
30431
+ ...entry.node.attrs,
30432
+ formula,
30433
+ computedValue: null
30434
+ },
30435
+ entry.node.content,
30436
+ entry.node.marks
30437
+ );
30438
+ changed = true;
30439
+ }
30440
+ return rowInfo.node.type.create(rowInfo.node.attrs, cells);
30441
+ });
30442
+ return {
30443
+ tableNode: changed ? tableNode.type.create(tableNode.attrs, rows) : tableNode,
30444
+ changed
30445
+ };
30446
+ }
30447
+ function recalculateTableNode(tableNode) {
30448
+ const promoted = promoteFormulaTextInTableNode(tableNode);
30449
+ tableNode = promoted.tableNode;
30450
+ const map = import_tables2.TableMap.get(tableNode);
30451
+ const values = buildTableValueMap(tableNode);
30452
+ const formulaEntries = /* @__PURE__ */ new Map();
30453
+ let changed = false;
30454
+ const rowInfos = getTableRows2(tableNode);
30455
+ for (const [rowIndex, rowInfo] of rowInfos.entries()) {
30300
30456
  for (const entry of rowInfo.cells) {
30301
30457
  const formula = typeof entry.node.attrs.formula === "string" ? entry.node.attrs.formula.trim() : "";
30302
30458
  if (!formula) continue;
30303
30459
  const rectForCell = safeFindCell2(map, entry.relativePos);
30304
30460
  if (!rectForCell) continue;
30305
- const result = evaluateBasicTableFormula(formula, getCellValue);
30306
- const computedValue = result.error ? `#${result.error.toUpperCase()}` : String(result.value);
30307
- if (entry.node.attrs.computedValue === computedValue) continue;
30461
+ const label = `${indexToColumnName(rectForCell.left)}${rectForCell.top + 1}`;
30462
+ formulaEntries.set(label, {
30463
+ rowIndex,
30464
+ cellIndex: entry.index,
30465
+ node: entry.node,
30466
+ formula
30467
+ });
30468
+ }
30469
+ }
30470
+ const graph = buildTableFormulaDependencyGraph(
30471
+ Array.from(formulaEntries, ([label, entry]) => ({
30472
+ label,
30473
+ formula: entry.formula
30474
+ }))
30475
+ );
30476
+ const { order, circular } = getTableFormulaRecalculationOrder(graph);
30477
+ const computedValues = /* @__PURE__ */ new Map();
30478
+ const getCellValue = (label) => values.get(label.toUpperCase());
30479
+ for (const label of circular) {
30480
+ computedValues.set(label, formatFormulaError("circular-reference"));
30481
+ values.set(label, formatFormulaError("circular-reference"));
30482
+ }
30483
+ for (const label of order) {
30484
+ const entry = formulaEntries.get(label);
30485
+ if (!entry) continue;
30486
+ const result = evaluateBasicTableFormula(entry.formula, getCellValue);
30487
+ const computedValue = result.error ? formatFormulaError(result.error) : String(result.value);
30488
+ computedValues.set(label, computedValue);
30489
+ values.set(label, computedValue);
30490
+ }
30491
+ const rows = rowInfos.map((rowInfo) => {
30492
+ const cells = collectChildren2(rowInfo.node);
30493
+ for (const entry of rowInfo.cells) {
30494
+ const rectForCell = safeFindCell2(map, entry.relativePos);
30495
+ if (!rectForCell) continue;
30496
+ const label = `${indexToColumnName(rectForCell.left)}${rectForCell.top + 1}`;
30497
+ const computedValue = computedValues.get(label);
30498
+ if (computedValue == null) continue;
30499
+ const contentMatchesComputedValue = getCellText(entry.node) === computedValue;
30500
+ if (entry.node.attrs.computedValue === computedValue && contentMatchesComputedValue) continue;
30308
30501
  cells[entry.index] = entry.node.type.create(
30309
30502
  {
30310
30503
  ...entry.node.attrs,
30311
30504
  computedValue
30312
30505
  },
30313
- entry.node.content,
30506
+ createCellDisplayContent(entry.node, computedValue),
30314
30507
  entry.node.marks
30315
30508
  );
30316
30509
  changed = true;
30317
30510
  }
30318
30511
  return rowInfo.node.type.create(rowInfo.node.attrs, cells);
30319
30512
  });
30320
- if (!changed) return false;
30321
- const nextTable = tableNode.type.create(tableNode.attrs, rows);
30322
- editor.view.dispatch(editor.state.tr.replaceWith(rect.tableStart - 1, rect.tableStart - 1 + tableNode.nodeSize, nextTable));
30513
+ if (!changed) return promoted.changed ? tableNode : null;
30514
+ return tableNode.type.create(tableNode.attrs, rows);
30515
+ }
30516
+ function recalculateSelectedTable(editor) {
30517
+ const rect = (0, import_tables2.selectedRect)(editor.state);
30518
+ const tableNode = rect.table;
30519
+ const nextTable = recalculateTableNode(tableNode);
30520
+ if (!nextTable) return false;
30521
+ editor.view.dispatch(
30522
+ editor.state.tr.replaceWith(rect.tableStart - 1, rect.tableStart - 1 + tableNode.nodeSize, nextTable).setMeta(UEDITOR_TABLE_FORMULA_RECALCULATE_META, true)
30523
+ );
30524
+ dispatchTableLayoutChange(editor);
30525
+ return true;
30526
+ }
30527
+ function recalculateAllTableFormulas(editor) {
30528
+ const replacements = [];
30529
+ editor.state.doc.descendants((node, pos) => {
30530
+ if (node.type.name !== "table") return true;
30531
+ const nextTable = recalculateTableNode(node);
30532
+ if (nextTable) {
30533
+ replacements.push({
30534
+ from: pos,
30535
+ to: pos + node.nodeSize,
30536
+ node: nextTable
30537
+ });
30538
+ }
30539
+ return false;
30540
+ });
30541
+ if (replacements.length === 0) return false;
30542
+ let tr = editor.state.tr;
30543
+ for (const replacement of replacements.reverse()) {
30544
+ tr = tr.replaceWith(replacement.from, replacement.to, replacement.node);
30545
+ }
30546
+ editor.view.dispatch(tr.setMeta(UEDITOR_TABLE_FORMULA_RECALCULATE_META, true));
30323
30547
  dispatchTableLayoutChange(editor);
30324
30548
  return true;
30325
30549
  }
@@ -38317,6 +38541,7 @@ var UEditor = import_react69.default.forwardRef(({
38317
38541
  const effectivePlaceholder = placeholder ?? t("placeholder");
38318
38542
  const inFlightPrepareRef = (0, import_react69.useRef)(null);
38319
38543
  const lastAppliedContentRef = (0, import_react69.useRef)(content ?? "");
38544
+ const scheduledFormulaRecalculateRef = (0, import_react69.useRef)(false);
38320
38545
  const resolvedUploadFile = (0, import_react69.useMemo)(() => {
38321
38546
  if (uploadFile) return uploadFile;
38322
38547
  if (uploadFileForSave) {
@@ -38380,7 +38605,16 @@ var UEditor = import_react69.default.forwardRef(({
38380
38605
  class: UEDITOR_PROSEMIRROR_CLASS_NAME
38381
38606
  }
38382
38607
  },
38383
- onUpdate: ({ editor: editor2 }) => {
38608
+ onUpdate: ({ editor: editor2, transaction }) => {
38609
+ if (!transaction.getMeta(UEDITOR_TABLE_FORMULA_RECALCULATE_META) && !scheduledFormulaRecalculateRef.current) {
38610
+ scheduledFormulaRecalculateRef.current = true;
38611
+ queueMicrotask(() => {
38612
+ scheduledFormulaRecalculateRef.current = false;
38613
+ if (!editor2.isDestroyed) {
38614
+ recalculateAllTableFormulas(editor2);
38615
+ }
38616
+ });
38617
+ }
38384
38618
  const html = editor2.getHTML();
38385
38619
  onChange?.(html);
38386
38620
  onHtmlChange?.(html);
@@ -38418,6 +38652,14 @@ var UEditor = import_react69.default.forwardRef(({
38418
38652
  }),
38419
38653
  [content, editor, uploadImageForSave, uploadFileForSave, uploadImageConcurrency]
38420
38654
  );
38655
+ (0, import_react69.useEffect)(() => {
38656
+ if (!editor) return;
38657
+ queueMicrotask(() => {
38658
+ if (!editor.isDestroyed) {
38659
+ recalculateAllTableFormulas(editor);
38660
+ }
38661
+ });
38662
+ }, [editor]);
38421
38663
  (0, import_react69.useEffect)(() => {
38422
38664
  if (!editor) return;
38423
38665
  const nextContent = content ?? "";
@@ -38427,6 +38669,11 @@ var UEditor = import_react69.default.forwardRef(({
38427
38669
  Promise.resolve().then(() => {
38428
38670
  if (!editor.isDestroyed) {
38429
38671
  editor.commands.setContent(nextContent, { emitUpdate: false });
38672
+ queueMicrotask(() => {
38673
+ if (!editor.isDestroyed) {
38674
+ recalculateAllTableFormulas(editor);
38675
+ }
38676
+ });
38430
38677
  }
38431
38678
  });
38432
38679
  }