@underverse-ui/underverse 1.0.183 → 1.0.184

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.cjs CHANGED
@@ -27548,8 +27548,8 @@ function useLocale2() {
27548
27548
  }
27549
27549
 
27550
27550
  // src/components/UEditor/UEditor.tsx
27551
- var import_react79 = __toESM(require("react"), 1);
27552
- var import_react80 = require("@tiptap/react");
27551
+ var import_react80 = __toESM(require("react"), 1);
27552
+ var import_react81 = require("@tiptap/react");
27553
27553
 
27554
27554
  // src/components/UEditor/extensions.ts
27555
27555
  var import_core16 = require("@tiptap/core");
@@ -30067,11 +30067,17 @@ function isInTableCell(editor) {
30067
30067
  }
30068
30068
  function buildFormulaSuggestionItems({ query }) {
30069
30069
  const normalizedQuery = query.trim().toUpperCase();
30070
+ if (!isFormulaFunctionSuggestionQuery(normalizedQuery)) {
30071
+ return [];
30072
+ }
30070
30073
  if (!normalizedQuery) {
30071
30074
  return FORMULA_FUNCTIONS;
30072
30075
  }
30073
30076
  return FORMULA_FUNCTIONS.filter((item) => item.name.startsWith(normalizedQuery));
30074
30077
  }
30078
+ function isFormulaFunctionSuggestionQuery(query) {
30079
+ return /^[A-Z]*$/i.test(query.trim());
30080
+ }
30075
30081
  var FormulaSuggestionList = (0, import_react62.forwardRef)((props, ref) => {
30076
30082
  const t = useSmartTranslations("UEditor");
30077
30083
  const [selectedIndex, setSelectedIndex] = import_react62.default.useState(0);
@@ -30142,7 +30148,11 @@ var FormulaSuggestion = import_core9.Extension.create({
30142
30148
  editor: this.editor,
30143
30149
  char: "=",
30144
30150
  pluginKey: new import_state4.PluginKey("formulaSuggestion"),
30145
- allow: ({ editor }) => isInTableCell(editor),
30151
+ allow: ({ editor, range }) => {
30152
+ if (!isInTableCell(editor)) return false;
30153
+ const suggestionText = editor.state.doc.textBetween(range.from, range.to, "", "");
30154
+ return suggestionText.startsWith("=") && isFormulaFunctionSuggestionQuery(suggestionText.slice(1));
30155
+ },
30146
30156
  command: ({ editor, range, props }) => {
30147
30157
  insertFormulaFunction(editor, range, props);
30148
30158
  },
@@ -34071,51 +34081,98 @@ function buildTableFormulaDependencyGraph(cells) {
34071
34081
  return { dependencies, dependents, formulas };
34072
34082
  }
34073
34083
  function getTableFormulaCircularReferences(graph) {
34074
- const visiting = /* @__PURE__ */ new Set();
34075
- const visited = /* @__PURE__ */ new Set();
34076
34084
  const circular = /* @__PURE__ */ new Set();
34077
- const stack = [];
34078
- const visit = (label) => {
34079
- if (visiting.has(label)) {
34080
- const start = stack.indexOf(label);
34081
- for (const cycleLabel of start >= 0 ? stack.slice(start) : [label]) {
34082
- circular.add(cycleLabel);
34085
+ const visited = /* @__PURE__ */ new Set();
34086
+ const finishOrder = [];
34087
+ for (const startLabel of graph.formulas.keys()) {
34088
+ if (visited.has(startLabel)) continue;
34089
+ visited.add(startLabel);
34090
+ const stack = [
34091
+ {
34092
+ label: startLabel,
34093
+ references: Array.from(graph.dependencies.get(startLabel) ?? []).filter((ref) => graph.formulas.has(ref)),
34094
+ nextIndex: 0
34083
34095
  }
34084
- return;
34085
- }
34086
- if (visited.has(label)) return;
34087
- visiting.add(label);
34088
- stack.push(label);
34089
- for (const ref of graph.dependencies.get(label) ?? []) {
34090
- if (graph.formulas.has(ref)) {
34091
- visit(ref);
34096
+ ];
34097
+ while (stack.length > 0) {
34098
+ const frame = stack[stack.length - 1];
34099
+ if (!frame) break;
34100
+ const reference = frame.references[frame.nextIndex];
34101
+ if (reference) {
34102
+ frame.nextIndex += 1;
34103
+ if (!visited.has(reference)) {
34104
+ visited.add(reference);
34105
+ stack.push({
34106
+ label: reference,
34107
+ references: Array.from(graph.dependencies.get(reference) ?? []).filter((ref) => graph.formulas.has(ref)),
34108
+ nextIndex: 0
34109
+ });
34110
+ }
34111
+ continue;
34092
34112
  }
34113
+ stack.pop();
34114
+ finishOrder.push(frame.label);
34115
+ }
34116
+ }
34117
+ const assigned = /* @__PURE__ */ new Set();
34118
+ for (let index = finishOrder.length - 1; index >= 0; index -= 1) {
34119
+ const startLabel = finishOrder[index];
34120
+ if (!startLabel || assigned.has(startLabel)) continue;
34121
+ const component = [];
34122
+ const stack = [startLabel];
34123
+ assigned.add(startLabel);
34124
+ while (stack.length > 0) {
34125
+ const label = stack.pop();
34126
+ if (!label) continue;
34127
+ component.push(label);
34128
+ for (const dependent of graph.dependents.get(label) ?? []) {
34129
+ if (!graph.formulas.has(dependent) || assigned.has(dependent)) continue;
34130
+ assigned.add(dependent);
34131
+ stack.push(dependent);
34132
+ }
34133
+ }
34134
+ if (component.length > 1) {
34135
+ for (const label of component) circular.add(label);
34136
+ } else {
34137
+ const label = component[0];
34138
+ if (label && graph.dependencies.get(label)?.has(label)) circular.add(label);
34093
34139
  }
34094
- stack.pop();
34095
- visiting.delete(label);
34096
- visited.add(label);
34097
- };
34098
- for (const label of graph.formulas.keys()) {
34099
- visit(label);
34100
34140
  }
34101
34141
  return circular;
34102
34142
  }
34103
34143
  function getTableFormulaRecalculationOrder(graph) {
34104
34144
  const circular = getTableFormulaCircularReferences(graph);
34145
+ const visiting = /* @__PURE__ */ new Set();
34105
34146
  const visited = /* @__PURE__ */ new Set();
34106
34147
  const order = [];
34107
- const visit = (label) => {
34108
- if (visited.has(label) || circular.has(label)) return;
34109
- visited.add(label);
34110
- for (const ref of graph.dependencies.get(label) ?? []) {
34111
- if (graph.formulas.has(ref)) {
34112
- visit(ref);
34148
+ for (const startLabel of graph.formulas.keys()) {
34149
+ if (visited.has(startLabel) || circular.has(startLabel)) continue;
34150
+ const stack = [];
34151
+ const push = (label) => {
34152
+ visiting.add(label);
34153
+ stack.push({
34154
+ label,
34155
+ references: Array.from(graph.dependencies.get(label) ?? []).filter((ref) => graph.formulas.has(ref) && !circular.has(ref)),
34156
+ nextIndex: 0
34157
+ });
34158
+ };
34159
+ push(startLabel);
34160
+ while (stack.length > 0) {
34161
+ const frame = stack[stack.length - 1];
34162
+ if (!frame) break;
34163
+ const reference = frame.references[frame.nextIndex];
34164
+ if (reference) {
34165
+ frame.nextIndex += 1;
34166
+ if (!visited.has(reference) && !visiting.has(reference)) {
34167
+ push(reference);
34168
+ }
34169
+ continue;
34113
34170
  }
34171
+ stack.pop();
34172
+ visiting.delete(frame.label);
34173
+ visited.add(frame.label);
34174
+ order.push(frame.label);
34114
34175
  }
34115
- order.push(label);
34116
- };
34117
- for (const label of graph.formulas.keys()) {
34118
- visit(label);
34119
34176
  }
34120
34177
  return { order, circular };
34121
34178
  }
@@ -34174,9 +34231,11 @@ function tokenizeFormula(formula) {
34174
34231
  index += 1;
34175
34232
  continue;
34176
34233
  }
34177
- const numberMatch = formula.slice(index).match(/^\d+(?:\.\d+)?/);
34234
+ const numberMatch = formula.slice(index).match(/^(?:\d+(?:\.\d*)?|\.\d+)/);
34178
34235
  if (numberMatch?.[0]) {
34179
- tokens.push({ type: "number", value: Number.parseFloat(numberMatch[0]) });
34236
+ const value = Number.parseFloat(numberMatch[0]);
34237
+ if (!Number.isFinite(value)) return null;
34238
+ tokens.push({ type: "number", value });
34180
34239
  index += numberMatch[0].length;
34181
34240
  continue;
34182
34241
  }
@@ -34194,6 +34253,28 @@ function tokenizeFormula(formula) {
34194
34253
  }
34195
34254
  return tokens;
34196
34255
  }
34256
+ function toFiniteFormulaResult(value) {
34257
+ return Number.isFinite(value) ? { value, error: null } : { value: null, error: "invalid-formula" };
34258
+ }
34259
+ function parseTableCellNumericValue(value) {
34260
+ if (typeof value === "number") {
34261
+ return Number.isFinite(value) ? value : null;
34262
+ }
34263
+ let normalized = String(value ?? "").trim();
34264
+ if (!normalized || normalized.startsWith("#")) return null;
34265
+ const isPercent = normalized.endsWith("%");
34266
+ if (isPercent) normalized = normalized.slice(0, -1).trim();
34267
+ const currencyMatch = normalized.match(/^([+-]?)\$(.+)$/);
34268
+ if (currencyMatch) {
34269
+ normalized = `${currencyMatch[1] ?? ""}${currencyMatch[2] ?? ""}`.trim();
34270
+ }
34271
+ const groupedNumber = /^[+-]?\d{1,3}(?:,\d{3})+(?:\.\d*)?(?:[eE][+-]?\d+)?$/;
34272
+ const plainNumber = /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?$/;
34273
+ if (!groupedNumber.test(normalized) && !plainNumber.test(normalized)) return null;
34274
+ const parsed = Number(normalized.replace(/,/g, ""));
34275
+ if (!Number.isFinite(parsed)) return null;
34276
+ return isPercent ? parsed / 100 : parsed;
34277
+ }
34197
34278
  var FormulaParser = class {
34198
34279
  constructor(tokens, getCellValue) {
34199
34280
  this.tokens = tokens;
@@ -34211,10 +34292,7 @@ var FormulaParser = class {
34211
34292
  this.index += 1;
34212
34293
  const right = this.parseTerm();
34213
34294
  if (right.error) return right;
34214
- left = {
34215
- value: operator.value === "+" ? left.value + right.value : left.value - right.value,
34216
- error: null
34217
- };
34295
+ left = toFiniteFormulaResult(operator.value === "+" ? left.value + right.value : left.value - right.value);
34218
34296
  }
34219
34297
  return left;
34220
34298
  }
@@ -34229,10 +34307,7 @@ var FormulaParser = class {
34229
34307
  if (operator.value === "/" && right.value === 0) {
34230
34308
  return { value: null, error: "division-by-zero" };
34231
34309
  }
34232
- left = {
34233
- value: operator.value === "*" ? left.value * right.value : left.value / right.value,
34234
- error: null
34235
- };
34310
+ left = toFiniteFormulaResult(operator.value === "*" ? left.value * right.value : left.value / right.value);
34236
34311
  }
34237
34312
  return left;
34238
34313
  }
@@ -34241,11 +34316,11 @@ var FormulaParser = class {
34241
34316
  if (!token) {
34242
34317
  return { value: null, error: "invalid-formula" };
34243
34318
  }
34244
- if (token.type === "operator" && token.value === "-") {
34319
+ if (token.type === "operator" && (token.value === "-" || token.value === "+")) {
34245
34320
  this.index += 1;
34246
34321
  const value = this.parseFactor();
34247
34322
  if (value.error) return value;
34248
- return { value: -value.value, error: null };
34323
+ return toFiniteFormulaResult(token.value === "-" ? -value.value : value.value);
34249
34324
  }
34250
34325
  if (token.type === "number") {
34251
34326
  this.index += 1;
@@ -34290,11 +34365,10 @@ var FormulaParser = class {
34290
34365
  if (cellValue2 != null) values.push(cellValue2);
34291
34366
  continue;
34292
34367
  }
34293
- const cellValue = this.readCellNumber(label);
34294
- if (cellValue.error) return cellValue;
34295
- values.push(cellValue.value);
34368
+ const cellValue = this.readOptionalCellNumber(label);
34369
+ if (cellValue != null) values.push(cellValue);
34296
34370
  }
34297
- } else if (name === "COUNT" && token.type === "cell") {
34371
+ } else if (token.type === "cell") {
34298
34372
  this.index += 1;
34299
34373
  const cellValue = this.readOptionalCellNumber(token.value);
34300
34374
  if (cellValue != null) values.push(cellValue);
@@ -34311,28 +34385,24 @@ var FormulaParser = class {
34311
34385
  }
34312
34386
  return { value: null, error: "invalid-formula" };
34313
34387
  }
34314
- if (values.length === 0 && name !== "COUNT") {
34315
- return { value: null, error: "invalid-formula" };
34388
+ if (name === "SUM") return toFiniteFormulaResult(values.reduce((sum, value) => sum + value, 0));
34389
+ if (name === "AVG") {
34390
+ return values.length > 0 ? toFiniteFormulaResult(values.reduce((sum, value) => sum + value, 0) / values.length) : { value: null, error: "division-by-zero" };
34316
34391
  }
34317
- if (name === "SUM") return { value: values.reduce((sum, value) => sum + value, 0), error: null };
34318
- if (name === "AVG") return { value: values.reduce((sum, value) => sum + value, 0) / values.length, error: null };
34319
- if (name === "MIN") return { value: Math.min(...values), error: null };
34320
- if (name === "MAX") return { value: Math.max(...values), error: null };
34392
+ if (name === "MIN") return toFiniteFormulaResult(values.length > 0 ? Math.min(...values) : 0);
34393
+ if (name === "MAX") return toFiniteFormulaResult(values.length > 0 ? Math.max(...values) : 0);
34321
34394
  if (name === "COUNT") return { value: values.length, error: null };
34322
34395
  return { value: null, error: "invalid-formula" };
34323
34396
  }
34324
34397
  readCellNumber(label) {
34325
- const value = this.getCellValue(label);
34326
- const parsed = typeof value === "number" ? value : Number.parseFloat(String(value ?? "").trim());
34327
- if (!Number.isFinite(parsed)) {
34398
+ const parsed = parseTableCellNumericValue(this.getCellValue(label));
34399
+ if (parsed == null) {
34328
34400
  return { value: null, error: "invalid-reference" };
34329
34401
  }
34330
34402
  return { value: parsed, error: null };
34331
34403
  }
34332
34404
  readOptionalCellNumber(label) {
34333
- const value = this.getCellValue(label);
34334
- const parsed = typeof value === "number" ? value : Number.parseFloat(String(value ?? "").trim());
34335
- return Number.isFinite(parsed) ? parsed : null;
34405
+ return parseTableCellNumericValue(this.getCellValue(label));
34336
34406
  }
34337
34407
  peekOperator(operators) {
34338
34408
  const token = this.tokens[this.index];
@@ -35448,7 +35518,7 @@ var CustomBubbleMenu = ({
35448
35518
  const BUBBLE_MENU_ESTIMATED_HEIGHT = 44;
35449
35519
  const [isVisible, setIsVisible] = (0, import_react72.useState)(false);
35450
35520
  const [linkInputOpen, setLinkInputOpen] = (0, import_react72.useState)(false);
35451
- const [position, setPosition] = (0, import_react72.useState)({
35521
+ const [position, setPosition2] = (0, import_react72.useState)({
35452
35522
  top: 0,
35453
35523
  left: 0,
35454
35524
  placement: "top"
@@ -35520,7 +35590,7 @@ var CustomBubbleMenu = ({
35520
35590
  Math.max(viewportPadding, (start.left + end.left) / 2)
35521
35591
  );
35522
35592
  const top = placement === "top" ? Math.max(viewportPadding, selectionTop - BUBBLE_MENU_OFFSET) : Math.min(window.innerHeight - viewportPadding, selectionBottom + BUBBLE_MENU_OFFSET);
35523
- setPosition({ top, left, placement });
35593
+ setPosition2({ top, left, placement });
35524
35594
  if (keepOpenRef.current) {
35525
35595
  clearShowTimeout();
35526
35596
  setIsVisible(true);
@@ -42038,9 +42108,402 @@ function useUEditorTableInteractions(editor, editable = true) {
42038
42108
  };
42039
42109
  }
42040
42110
 
42111
+ // src/components/UEditor/use-formula-coordinate-overlay.ts
42112
+ var import_react77 = require("react");
42113
+ var import_tables8 = require("@tiptap/pm/tables");
42114
+
42115
+ // src/components/UEditor/table-formula-range-picker.ts
42116
+ var import_state10 = require("@tiptap/pm/state");
42117
+ var import_tables7 = require("@tiptap/pm/tables");
42118
+ function getCellText2(cellNode) {
42119
+ return cellNode.textBetween(0, cellNode.content.size, "\n").trim();
42120
+ }
42121
+ function getFormulaEditingTableContext(view) {
42122
+ const { $from } = view.state.selection;
42123
+ for (let depth = $from.depth; depth > 0; depth -= 1) {
42124
+ const node = $from.node(depth);
42125
+ if (node.type.name !== "tableCell" && node.type.name !== "tableHeader") continue;
42126
+ if (!getCellText2(node).startsWith("=")) return null;
42127
+ const cellPos = $from.before(depth);
42128
+ const cellDom = view.nodeDOM(cellPos);
42129
+ if (!(cellDom instanceof HTMLTableCellElement)) return null;
42130
+ const tableDepth = depth - 2;
42131
+ const tableNode = tableDepth > 0 ? $from.node(tableDepth) : null;
42132
+ if (!tableNode || tableNode.type.name !== "table") return null;
42133
+ const tableDom = cellDom.closest("table");
42134
+ if (!(tableDom instanceof HTMLTableElement)) return null;
42135
+ return {
42136
+ cellContentEnd: $from.end(depth),
42137
+ cellContentStart: $from.start(depth),
42138
+ cellDom,
42139
+ formula: getCellText2(node),
42140
+ tableDom,
42141
+ tableNode,
42142
+ tablePos: $from.before(tableDepth)
42143
+ };
42144
+ }
42145
+ return null;
42146
+ }
42147
+ function cancelFormulaEditing(view) {
42148
+ const context = getFormulaEditingTableContext(view);
42149
+ if (!context) return false;
42150
+ let tr = view.state.tr.delete(context.cellContentStart, context.cellContentEnd);
42151
+ const selectionPos = Math.min(context.cellContentStart, tr.doc.content.size);
42152
+ tr = tr.setSelection(import_state10.TextSelection.near(tr.doc.resolve(selectionPos)));
42153
+ view.dispatch(tr);
42154
+ view.focus();
42155
+ return true;
42156
+ }
42157
+ function getReferenceInsertionPrefix(view, cellContentStart, insertionPos) {
42158
+ const textBeforeCursor = view.state.doc.textBetween(cellContentStart, insertionPos, "", "");
42159
+ const isInsideFunctionArguments = /[A-Z]+\([^)]*$/i.test(textBeforeCursor);
42160
+ const followsReference = /[A-Z]+[1-9]\d*(?::[A-Z]+[1-9]\d*)?\s*$/i.test(textBeforeCursor);
42161
+ return isInsideFunctionArguments && followsReference ? "," : "";
42162
+ }
42163
+ function findTableForPos(view, pos) {
42164
+ const $pos = view.state.doc.resolve(pos);
42165
+ for (let depth = $pos.depth; depth > 0; depth -= 1) {
42166
+ const node = $pos.node(depth);
42167
+ if (node.type.name === "table") {
42168
+ return {
42169
+ node,
42170
+ pos: $pos.before(depth),
42171
+ start: $pos.start(depth)
42172
+ };
42173
+ }
42174
+ }
42175
+ return null;
42176
+ }
42177
+ function getCellRelativePosFromDomPos2(map, tableStart, domPos) {
42178
+ const relativeDomPos = domPos - tableStart;
42179
+ const seen = /* @__PURE__ */ new Set();
42180
+ for (const relativeCellPos of map.map) {
42181
+ if (seen.has(relativeCellPos)) continue;
42182
+ seen.add(relativeCellPos);
42183
+ if (relativeDomPos === relativeCellPos || relativeDomPos === relativeCellPos + 1) {
42184
+ return relativeCellPos;
42185
+ }
42186
+ }
42187
+ return null;
42188
+ }
42189
+ function getFormulaTableCellInfo(view, target, tablePos) {
42190
+ const element = resolveEventElement(target);
42191
+ const cell = element?.closest?.("th,td");
42192
+ if (!(cell instanceof HTMLTableCellElement)) return null;
42193
+ const domPos = view.posAtDOM(cell, 0);
42194
+ const tableInfo = findTableForPos(view, domPos);
42195
+ if (!tableInfo || tableInfo.pos !== tablePos) return null;
42196
+ const map = import_tables7.TableMap.get(tableInfo.node);
42197
+ const relativeCellPos = getCellRelativePosFromDomPos2(map, tableInfo.start, domPos);
42198
+ if (relativeCellPos == null) return null;
42199
+ const rect = map.findCell(relativeCellPos);
42200
+ return {
42201
+ cell,
42202
+ label: `${indexToColumnName(rect.left)}${rect.top + 1}`,
42203
+ rect
42204
+ };
42205
+ }
42206
+ function normalizeFormulaRangeLabel(fromLabel, toLabel) {
42207
+ return fromLabel === toLabel ? fromLabel : `${fromLabel}:${toLabel}`;
42208
+ }
42209
+ function replacePickedLabel(view, pickState, nextLabel, currentCell) {
42210
+ if (nextLabel === pickState.currentLabel && currentCell === pickState.currentCell) return pickState;
42211
+ if (nextLabel === pickState.currentLabel) {
42212
+ return {
42213
+ ...pickState,
42214
+ currentCell
42215
+ };
42216
+ }
42217
+ let tr = view.state.tr.insertText(nextLabel, pickState.insertedFrom, pickState.insertedTo);
42218
+ const nextTo = pickState.insertedFrom + nextLabel.length;
42219
+ tr = tr.setSelection(import_state10.TextSelection.create(tr.doc, nextTo));
42220
+ view.dispatch(tr);
42221
+ return {
42222
+ ...pickState,
42223
+ insertedTo: nextTo,
42224
+ currentLabel: nextLabel,
42225
+ currentCell
42226
+ };
42227
+ }
42228
+ function beginFormulaRangePick(view, event) {
42229
+ if (event.button !== 0) return null;
42230
+ const formulaCell = getFormulaEditingTableContext(view);
42231
+ if (!formulaCell) return null;
42232
+ const picked = getFormulaTableCellInfo(view, event.target, formulaCell.tablePos);
42233
+ if (!picked || picked.cell === formulaCell.cellDom) return null;
42234
+ const { from, to } = view.state.selection;
42235
+ const prefix = from === to ? getReferenceInsertionPrefix(view, formulaCell.cellContentStart, from) : "";
42236
+ const insertedText = `${prefix}${picked.label}`;
42237
+ const insertedFrom = from + prefix.length;
42238
+ let tr = view.state.tr.insertText(insertedText, from, to);
42239
+ tr = tr.setSelection(import_state10.TextSelection.create(tr.doc, from + insertedText.length));
42240
+ view.dispatch(tr);
42241
+ view.focus();
42242
+ event.preventDefault();
42243
+ event.stopPropagation();
42244
+ return {
42245
+ anchorLabel: picked.label,
42246
+ anchorCell: picked.cell,
42247
+ tablePos: formulaCell.tablePos,
42248
+ insertedFrom,
42249
+ insertedTo: insertedFrom + picked.label.length,
42250
+ currentLabel: picked.label,
42251
+ currentCell: picked.cell
42252
+ };
42253
+ }
42254
+ function updateFormulaRangePick(view, pickState, event) {
42255
+ const picked = getFormulaTableCellInfo(view, event.target, pickState.tablePos);
42256
+ if (!picked) return pickState;
42257
+ event.preventDefault();
42258
+ event.stopPropagation();
42259
+ return replacePickedLabel(
42260
+ view,
42261
+ pickState,
42262
+ normalizeFormulaRangeLabel(pickState.anchorLabel, picked.label),
42263
+ picked.cell
42264
+ );
42265
+ }
42266
+ function getFormulaRangePickHighlight(container, pickState) {
42267
+ if (!container.contains(pickState.anchorCell) || !container.contains(pickState.currentCell)) return null;
42268
+ const containerRect = container.getBoundingClientRect();
42269
+ const anchorRect = pickState.anchorCell.getBoundingClientRect();
42270
+ const currentRect = pickState.currentCell.getBoundingClientRect();
42271
+ const left = Math.min(anchorRect.left, currentRect.left) - containerRect.left + container.scrollLeft;
42272
+ const top = Math.min(anchorRect.top, currentRect.top) - containerRect.top + container.scrollTop;
42273
+ const right = Math.max(anchorRect.right, currentRect.right) - containerRect.left + container.scrollLeft;
42274
+ const bottom = Math.max(anchorRect.bottom, currentRect.bottom) - containerRect.top + container.scrollTop;
42275
+ return {
42276
+ left,
42277
+ top,
42278
+ width: Math.max(0, right - left),
42279
+ height: Math.max(0, bottom - top)
42280
+ };
42281
+ }
42282
+
42283
+ // src/components/UEditor/use-formula-coordinate-overlay.ts
42284
+ function setPosition(element, left, top, width, height) {
42285
+ element.style.left = `${left}px`;
42286
+ element.style.top = `${top}px`;
42287
+ if (width != null) element.style.width = `${Math.max(0, width)}px`;
42288
+ if (height != null) element.style.height = `${Math.max(0, height)}px`;
42289
+ }
42290
+ function createCoordinateLabel(kind, label) {
42291
+ const element = document.createElement("span");
42292
+ element.dataset.ueditorFormulaCoordinate = kind;
42293
+ element.textContent = label;
42294
+ element.className = "pointer-events-none absolute z-30 flex items-center justify-center rounded-[3px] border border-primary/35 bg-primary/90 px-1 font-mono text-[10px] font-semibold leading-none text-primary-foreground shadow-sm";
42295
+ return element;
42296
+ }
42297
+ function createReferenceHighlight(label) {
42298
+ const element = document.createElement("span");
42299
+ element.dataset.ueditorFormulaReference = label;
42300
+ element.className = "pointer-events-none absolute z-[21] rounded-[2px] border-2 border-emerald-500 bg-emerald-500/15";
42301
+ return element;
42302
+ }
42303
+ function useFormulaCoordinateOverlay(editor, containerRef, labels) {
42304
+ const overlayRef = (0, import_react77.useRef)(null);
42305
+ (0, import_react77.useEffect)(() => {
42306
+ if (!editor) return void 0;
42307
+ const overlay = overlayRef.current;
42308
+ const container = containerRef.current;
42309
+ if (!overlay || !container) return void 0;
42310
+ let animationFrame = null;
42311
+ let activeTable = null;
42312
+ let activeTablePos = null;
42313
+ let hoverLabel = null;
42314
+ const clearOverlay = () => {
42315
+ activeTable = null;
42316
+ activeTablePos = null;
42317
+ hoverLabel = null;
42318
+ overlay.replaceChildren();
42319
+ overlay.style.display = "none";
42320
+ };
42321
+ const syncOverlay = () => {
42322
+ animationFrame = null;
42323
+ if (editor.isDestroyed) return;
42324
+ const context = getFormulaEditingTableContext(editor.view);
42325
+ if (!context) {
42326
+ clearOverlay();
42327
+ return;
42328
+ }
42329
+ const containerRect = container.getBoundingClientRect();
42330
+ const tableRect = context.tableDom.getBoundingClientRect();
42331
+ const tableLeft = tableRect.left - containerRect.left + container.scrollLeft;
42332
+ const tableTop = tableRect.top - containerRect.top + container.scrollTop;
42333
+ const map = import_tables8.TableMap.get(context.tableNode);
42334
+ const columnSegments = Array(map.width);
42335
+ const rowSegments = Array(map.height);
42336
+ const cellInfos = [];
42337
+ for (const cell of context.tableDom.querySelectorAll("th,td")) {
42338
+ if (!(cell instanceof HTMLTableCellElement)) continue;
42339
+ const info = getFormulaTableCellInfo(editor.view, cell, context.tablePos);
42340
+ if (!info) continue;
42341
+ cellInfos.push(info);
42342
+ const cellRect = cell.getBoundingClientRect();
42343
+ const columnSpan = Math.max(1, info.rect.right - info.rect.left);
42344
+ const rowSpan = Math.max(1, info.rect.bottom - info.rect.top);
42345
+ const columnWidth = cellRect.width / columnSpan;
42346
+ const rowHeight = cellRect.height / rowSpan;
42347
+ for (let column = info.rect.left; column < info.rect.right; column += 1) {
42348
+ if (!columnSegments[column] || columnSpan < columnSegments[column].span) {
42349
+ columnSegments[column] = {
42350
+ start: cellRect.left - containerRect.left + container.scrollLeft + (column - info.rect.left) * columnWidth,
42351
+ size: columnWidth,
42352
+ span: columnSpan
42353
+ };
42354
+ }
42355
+ }
42356
+ for (let row = info.rect.top; row < info.rect.bottom; row += 1) {
42357
+ if (!rowSegments[row] || rowSpan < rowSegments[row].span) {
42358
+ rowSegments[row] = {
42359
+ start: cellRect.top - containerRect.top + container.scrollTop + (row - info.rect.top) * rowHeight,
42360
+ size: rowHeight,
42361
+ span: rowSpan
42362
+ };
42363
+ }
42364
+ }
42365
+ }
42366
+ const fallbackColumnWidth = map.width > 0 ? tableRect.width / map.width : 0;
42367
+ const fallbackRowHeight = map.height > 0 ? tableRect.height / map.height : 0;
42368
+ const children = [];
42369
+ const actions = document.createElement("div");
42370
+ actions.dataset.ueditorFormulaActions = "";
42371
+ actions.className = "pointer-events-auto absolute z-50 flex items-center gap-1 rounded-md border border-border bg-background p-1 shadow-md";
42372
+ const formulaCellRect = context.cellDom.getBoundingClientRect();
42373
+ setPosition(
42374
+ actions,
42375
+ formulaCellRect.left - containerRect.left + container.scrollLeft,
42376
+ formulaCellRect.bottom - containerRect.top + container.scrollTop + 4
42377
+ );
42378
+ const applyButton = document.createElement("button");
42379
+ applyButton.type = "button";
42380
+ applyButton.dataset.ueditorFormulaApply = "";
42381
+ applyButton.disabled = isDraftTableFormula(context.formula);
42382
+ applyButton.textContent = `${applyButton.disabled ? "" : "\u2713 "}${labels.apply} (Enter)`;
42383
+ applyButton.className = "rounded bg-primary px-2.5 py-1 text-xs font-medium text-primary-foreground hover:bg-primary/90 disabled:cursor-not-allowed disabled:opacity-45";
42384
+ const cancelButton = document.createElement("button");
42385
+ cancelButton.type = "button";
42386
+ cancelButton.dataset.ueditorFormulaCancel = "";
42387
+ cancelButton.textContent = `${labels.cancel} (Esc)`;
42388
+ cancelButton.className = "rounded px-2.5 py-1 text-xs font-medium text-foreground hover:bg-muted";
42389
+ const preserveSelection = (event) => {
42390
+ event.preventDefault();
42391
+ event.stopPropagation();
42392
+ };
42393
+ applyButton.addEventListener("mousedown", preserveSelection);
42394
+ cancelButton.addEventListener("mousedown", preserveSelection);
42395
+ applyButton.addEventListener("click", (event) => {
42396
+ event.preventDefault();
42397
+ event.stopPropagation();
42398
+ if (!applyButton.disabled) recalculateActiveTableFormulas(editor);
42399
+ });
42400
+ cancelButton.addEventListener("click", (event) => {
42401
+ event.preventDefault();
42402
+ event.stopPropagation();
42403
+ cancelFormulaEditing(editor.view);
42404
+ });
42405
+ actions.append(applyButton, cancelButton);
42406
+ children.push(actions);
42407
+ for (let column = 0; column < map.width; column += 1) {
42408
+ const segment = columnSegments[column] ?? {
42409
+ start: tableLeft + column * fallbackColumnWidth,
42410
+ size: fallbackColumnWidth,
42411
+ span: map.width
42412
+ };
42413
+ const label = createCoordinateLabel("column", indexToColumnName(column));
42414
+ setPosition(label, segment.start, Math.max(0, tableTop - 22), segment.size, 20);
42415
+ children.push(label);
42416
+ }
42417
+ for (let row = 0; row < map.height; row += 1) {
42418
+ const segment = rowSegments[row] ?? {
42419
+ start: tableTop + row * fallbackRowHeight,
42420
+ size: fallbackRowHeight,
42421
+ span: map.height
42422
+ };
42423
+ const label = createCoordinateLabel("row", String(row + 1));
42424
+ setPosition(label, Math.max(0, tableLeft - 28), segment.start, 26, segment.size);
42425
+ children.push(label);
42426
+ }
42427
+ const references = new Set(getTableFormulaReferences(context.formula));
42428
+ for (const info of cellInfos) {
42429
+ if (!info || !references.has(info.label)) continue;
42430
+ const cellRect = info.cell.getBoundingClientRect();
42431
+ const highlight = createReferenceHighlight(info.label);
42432
+ setPosition(
42433
+ highlight,
42434
+ cellRect.left - containerRect.left + container.scrollLeft + 2,
42435
+ cellRect.top - containerRect.top + container.scrollTop + 2,
42436
+ cellRect.width - 4,
42437
+ cellRect.height - 4
42438
+ );
42439
+ children.push(highlight);
42440
+ }
42441
+ hoverLabel = document.createElement("span");
42442
+ hoverLabel.dataset.ueditorFormulaHoverLabel = "";
42443
+ hoverLabel.className = "pointer-events-none absolute z-40 hidden rounded bg-foreground px-1.5 py-1 font-mono text-[10px] font-semibold leading-none text-background shadow-md";
42444
+ children.push(hoverLabel);
42445
+ overlay.replaceChildren(...children);
42446
+ overlay.style.display = "block";
42447
+ activeTable = context.tableDom;
42448
+ activeTablePos = context.tablePos;
42449
+ };
42450
+ const scheduleSync = () => {
42451
+ if (animationFrame != null) return;
42452
+ animationFrame = window.requestAnimationFrame(syncOverlay);
42453
+ };
42454
+ const handleMouseMove2 = (event) => {
42455
+ if (!activeTable || activeTablePos == null || !hoverLabel) return;
42456
+ const target = event.target instanceof Element ? event.target.closest("th,td") : null;
42457
+ if (!(target instanceof HTMLTableCellElement) || target.closest("table") !== activeTable) {
42458
+ hoverLabel.style.display = "none";
42459
+ return;
42460
+ }
42461
+ const info = getFormulaTableCellInfo(editor.view, target, activeTablePos);
42462
+ if (!info) {
42463
+ hoverLabel.style.display = "none";
42464
+ return;
42465
+ }
42466
+ const containerRect = container.getBoundingClientRect();
42467
+ const cellRect = target.getBoundingClientRect();
42468
+ hoverLabel.textContent = info.label;
42469
+ hoverLabel.style.display = "block";
42470
+ setPosition(
42471
+ hoverLabel,
42472
+ cellRect.left - containerRect.left + container.scrollLeft + 4,
42473
+ cellRect.top - containerRect.top + container.scrollTop + 4
42474
+ );
42475
+ };
42476
+ const handleMouseLeave2 = () => {
42477
+ if (hoverLabel) hoverLabel.style.display = "none";
42478
+ };
42479
+ editor.on("selectionUpdate", scheduleSync);
42480
+ editor.on("update", scheduleSync);
42481
+ editor.on("focus", scheduleSync);
42482
+ editor.on("blur", scheduleSync);
42483
+ editor.view.dom.addEventListener("mousemove", handleMouseMove2);
42484
+ editor.view.dom.addEventListener("mouseleave", handleMouseLeave2);
42485
+ container.addEventListener(UEDITOR_TABLE_LAYOUT_CHANGE_EVENT, scheduleSync);
42486
+ window.addEventListener("resize", scheduleSync);
42487
+ scheduleSync();
42488
+ return () => {
42489
+ editor.off("selectionUpdate", scheduleSync);
42490
+ editor.off("update", scheduleSync);
42491
+ editor.off("focus", scheduleSync);
42492
+ editor.off("blur", scheduleSync);
42493
+ editor.view.dom.removeEventListener("mousemove", handleMouseMove2);
42494
+ editor.view.dom.removeEventListener("mouseleave", handleMouseLeave2);
42495
+ container.removeEventListener(UEDITOR_TABLE_LAYOUT_CHANGE_EVENT, scheduleSync);
42496
+ window.removeEventListener("resize", scheduleSync);
42497
+ if (animationFrame != null) window.cancelAnimationFrame(animationFrame);
42498
+ clearOverlay();
42499
+ };
42500
+ }, [containerRef, editor, labels.apply, labels.cancel]);
42501
+ return overlayRef;
42502
+ }
42503
+
42041
42504
  // src/components/UEditor/menu-bar.tsx
42042
- var import_react77 = __toESM(require("react"), 1);
42043
- var import_react78 = require("@tiptap/react");
42505
+ var import_react78 = __toESM(require("react"), 1);
42506
+ var import_react79 = require("@tiptap/react");
42044
42507
  var import_lucide_react58 = require("lucide-react");
42045
42508
 
42046
42509
  // src/components/UEditor/preview-html.ts
@@ -42221,7 +42684,7 @@ function renderMenuItems(items) {
42221
42684
  case "sub":
42222
42685
  return /* @__PURE__ */ (0, import_jsx_runtime99.jsx)(DropdownMenuSub, { label: item.label, icon: item.icon, disabled: item.disabled, children: renderMenuItems(item.items) }, i);
42223
42686
  case "custom":
42224
- return /* @__PURE__ */ (0, import_jsx_runtime99.jsx)(import_react77.default.Fragment, { children: item.render() }, item.key);
42687
+ return /* @__PURE__ */ (0, import_jsx_runtime99.jsx)(import_react78.default.Fragment, { children: item.render() }, item.key);
42225
42688
  }
42226
42689
  });
42227
42690
  }
@@ -42597,7 +43060,7 @@ function buildTableMenuItems(t, editor, onInsertTable) {
42597
43060
  }
42598
43061
  ];
42599
43062
  }
42600
- var MenuBarTrigger = import_react77.default.forwardRef(
43063
+ var MenuBarTrigger = import_react78.default.forwardRef(
42601
43064
  ({ children, className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime99.jsx)(
42602
43065
  "button",
42603
43066
  {
@@ -42629,18 +43092,18 @@ var MenuBar = ({
42629
43092
  onPreview
42630
43093
  }) => {
42631
43094
  const t = useSmartTranslations("UEditor");
42632
- (0, import_react78.useEditorState)({
43095
+ (0, import_react79.useEditorState)({
42633
43096
  editor,
42634
43097
  selector: ({ transactionNumber }) => transactionNumber
42635
43098
  });
42636
- const fileInputRef = (0, import_react77.useRef)(null);
42637
- const [showImageInput, setShowImageInput] = (0, import_react77.useState)(false);
42638
- const [showLinkInput, setShowLinkInput] = (0, import_react77.useState)(false);
42639
- const [isInsertMenuOpen, setIsInsertMenuOpen] = (0, import_react77.useState)(false);
42640
- const [showSourceDialog, setShowSourceDialog] = (0, import_react77.useState)(false);
42641
- const [sourceHtml, setSourceHtml] = (0, import_react77.useState)("");
42642
- const [showPreviewDialog, setShowPreviewDialog] = (0, import_react77.useState)(false);
42643
- const previewHtml = (0, import_react77.useMemo)(
43099
+ const fileInputRef = (0, import_react78.useRef)(null);
43100
+ const [showImageInput, setShowImageInput] = (0, import_react78.useState)(false);
43101
+ const [showLinkInput, setShowLinkInput] = (0, import_react78.useState)(false);
43102
+ const [isInsertMenuOpen, setIsInsertMenuOpen] = (0, import_react78.useState)(false);
43103
+ const [showSourceDialog, setShowSourceDialog] = (0, import_react78.useState)(false);
43104
+ const [sourceHtml, setSourceHtml] = (0, import_react78.useState)("");
43105
+ const [showPreviewDialog, setShowPreviewDialog] = (0, import_react78.useState)(false);
43106
+ const previewHtml = (0, import_react78.useMemo)(
42644
43107
  () => showPreviewDialog ? prepareUEditorPreviewHtml(editor.getHTML()) : "",
42645
43108
  [editor, showPreviewDialog]
42646
43109
  );
@@ -42922,149 +43385,9 @@ var MenuBar = ({
42922
43385
  ] });
42923
43386
  };
42924
43387
 
42925
- // src/components/UEditor/table-formula-range-picker.ts
42926
- var import_state10 = require("@tiptap/pm/state");
42927
- var import_tables7 = require("@tiptap/pm/tables");
42928
- function getCellText2(cellNode) {
42929
- return cellNode.textBetween(0, cellNode.content.size, "\n").trim();
42930
- }
42931
- function findSelectionFormulaCell(view) {
42932
- const { $from } = view.state.selection;
42933
- for (let depth = $from.depth; depth > 0; depth -= 1) {
42934
- const node = $from.node(depth);
42935
- if (node.type.name !== "tableCell" && node.type.name !== "tableHeader") continue;
42936
- if (!getCellText2(node).startsWith("=")) return null;
42937
- const cellPos = $from.before(depth);
42938
- const cellDom = view.nodeDOM(cellPos);
42939
- const tableDepth = depth - 2;
42940
- const tableNode = tableDepth > 0 ? $from.node(tableDepth) : null;
42941
- if (!tableNode || tableNode.type.name !== "table") return null;
42942
- return {
42943
- cellDom: cellDom instanceof HTMLTableCellElement ? cellDom : null,
42944
- tablePos: $from.before(tableDepth)
42945
- };
42946
- }
42947
- return null;
42948
- }
42949
- function findTableForPos(view, pos) {
42950
- const $pos = view.state.doc.resolve(pos);
42951
- for (let depth = $pos.depth; depth > 0; depth -= 1) {
42952
- const node = $pos.node(depth);
42953
- if (node.type.name === "table") {
42954
- return {
42955
- node,
42956
- pos: $pos.before(depth),
42957
- start: $pos.start(depth)
42958
- };
42959
- }
42960
- }
42961
- return null;
42962
- }
42963
- function getCellRelativePosFromDomPos2(map, tableStart, domPos) {
42964
- const relativeDomPos = domPos - tableStart;
42965
- const seen = /* @__PURE__ */ new Set();
42966
- for (const relativeCellPos of map.map) {
42967
- if (seen.has(relativeCellPos)) continue;
42968
- seen.add(relativeCellPos);
42969
- if (relativeDomPos === relativeCellPos || relativeDomPos === relativeCellPos + 1) {
42970
- return relativeCellPos;
42971
- }
42972
- }
42973
- return null;
42974
- }
42975
- function getPickedCellLabel(view, target, tablePos) {
42976
- const element = resolveEventElement(target);
42977
- const cell = element?.closest?.("th,td");
42978
- if (!(cell instanceof HTMLTableCellElement)) return null;
42979
- const domPos = view.posAtDOM(cell, 0);
42980
- const tableInfo = findTableForPos(view, domPos);
42981
- if (!tableInfo || tableInfo.pos !== tablePos) return null;
42982
- const map = import_tables7.TableMap.get(tableInfo.node);
42983
- const relativeCellPos = getCellRelativePosFromDomPos2(map, tableInfo.start, domPos);
42984
- if (relativeCellPos == null) return null;
42985
- const rect = map.findCell(relativeCellPos);
42986
- return {
42987
- cell,
42988
- label: `${indexToColumnName(rect.left)}${rect.top + 1}`
42989
- };
42990
- }
42991
- function normalizeFormulaRangeLabel(fromLabel, toLabel) {
42992
- return fromLabel === toLabel ? fromLabel : `${fromLabel}:${toLabel}`;
42993
- }
42994
- function replacePickedLabel(view, pickState, nextLabel, currentCell) {
42995
- if (nextLabel === pickState.currentLabel && currentCell === pickState.currentCell) return pickState;
42996
- if (nextLabel === pickState.currentLabel) {
42997
- return {
42998
- ...pickState,
42999
- currentCell
43000
- };
43001
- }
43002
- let tr = view.state.tr.insertText(nextLabel, pickState.insertedFrom, pickState.insertedTo);
43003
- const nextTo = pickState.insertedFrom + nextLabel.length;
43004
- tr = tr.setSelection(import_state10.TextSelection.create(tr.doc, nextTo));
43005
- view.dispatch(tr);
43006
- return {
43007
- ...pickState,
43008
- insertedTo: nextTo,
43009
- currentLabel: nextLabel,
43010
- currentCell
43011
- };
43012
- }
43013
- function beginFormulaRangePick(view, event) {
43014
- if (event.button !== 0) return null;
43015
- const formulaCell = findSelectionFormulaCell(view);
43016
- if (!formulaCell) return null;
43017
- const picked = getPickedCellLabel(view, event.target, formulaCell.tablePos);
43018
- if (!picked || picked.cell === formulaCell.cellDom) return null;
43019
- const { from, to } = view.state.selection;
43020
- let tr = view.state.tr.insertText(picked.label, from, to);
43021
- tr = tr.setSelection(import_state10.TextSelection.create(tr.doc, from + picked.label.length));
43022
- view.dispatch(tr);
43023
- view.focus();
43024
- event.preventDefault();
43025
- event.stopPropagation();
43026
- return {
43027
- anchorLabel: picked.label,
43028
- anchorCell: picked.cell,
43029
- tablePos: formulaCell.tablePos,
43030
- insertedFrom: from,
43031
- insertedTo: from + picked.label.length,
43032
- currentLabel: picked.label,
43033
- currentCell: picked.cell
43034
- };
43035
- }
43036
- function updateFormulaRangePick(view, pickState, event) {
43037
- const picked = getPickedCellLabel(view, event.target, pickState.tablePos);
43038
- if (!picked) return pickState;
43039
- event.preventDefault();
43040
- event.stopPropagation();
43041
- return replacePickedLabel(
43042
- view,
43043
- pickState,
43044
- normalizeFormulaRangeLabel(pickState.anchorLabel, picked.label),
43045
- picked.cell
43046
- );
43047
- }
43048
- function getFormulaRangePickHighlight(container, pickState) {
43049
- if (!container.contains(pickState.anchorCell) || !container.contains(pickState.currentCell)) return null;
43050
- const containerRect = container.getBoundingClientRect();
43051
- const anchorRect = pickState.anchorCell.getBoundingClientRect();
43052
- const currentRect = pickState.currentCell.getBoundingClientRect();
43053
- const left = Math.min(anchorRect.left, currentRect.left) - containerRect.left + container.scrollLeft;
43054
- const top = Math.min(anchorRect.top, currentRect.top) - containerRect.top + container.scrollTop;
43055
- const right = Math.max(anchorRect.right, currentRect.right) - containerRect.left + container.scrollLeft;
43056
- const bottom = Math.max(anchorRect.bottom, currentRect.bottom) - containerRect.top + container.scrollTop;
43057
- return {
43058
- left,
43059
- top,
43060
- width: Math.max(0, right - left),
43061
- height: Math.max(0, bottom - top)
43062
- };
43063
- }
43064
-
43065
43388
  // src/components/UEditor/UEditor.tsx
43066
43389
  var import_jsx_runtime100 = require("react/jsx-runtime");
43067
- var UEditor = import_react79.default.forwardRef(({
43390
+ var UEditor = import_react80.default.forwardRef(({
43068
43391
  content = "",
43069
43392
  onChange,
43070
43393
  onHtmlChange,
@@ -43105,13 +43428,14 @@ var UEditor = import_react79.default.forwardRef(({
43105
43428
  }, ref) => {
43106
43429
  const t = useSmartTranslations("UEditor");
43107
43430
  const effectivePlaceholder = placeholder ?? t("placeholder");
43108
- const inFlightPrepareRef = (0, import_react79.useRef)(null);
43109
- const lastAppliedContentRef = (0, import_react79.useRef)(content ?? "");
43110
- const scheduledFormulaRecalculateRef = (0, import_react79.useRef)(false);
43111
- const formulaRangePickRef = (0, import_react79.useRef)(null);
43112
- const formulaRangeSurfaceRef = (0, import_react79.useRef)(null);
43113
- const [formulaRangeHighlight, setFormulaRangeHighlight] = import_react79.default.useState(null);
43114
- const scheduleFormulaRecalculate = import_react79.default.useCallback((editor2, options) => {
43431
+ const inFlightPrepareRef = (0, import_react80.useRef)(null);
43432
+ const lastAppliedContentRef = (0, import_react80.useRef)(content ?? "");
43433
+ const scheduledFormulaRecalculateRef = (0, import_react80.useRef)(false);
43434
+ const editorInstanceRef = (0, import_react80.useRef)(null);
43435
+ const formulaRangePickRef = (0, import_react80.useRef)(null);
43436
+ const formulaRangeSurfaceRef = (0, import_react80.useRef)(null);
43437
+ const [formulaRangeHighlight, setFormulaRangeHighlight] = import_react80.default.useState(null);
43438
+ const scheduleFormulaRecalculate = import_react80.default.useCallback((editor2, options) => {
43115
43439
  if (editor2.isDestroyed || scheduledFormulaRecalculateRef.current) return;
43116
43440
  if (!options?.force && isEditingTableFormulaText(editor2)) return;
43117
43441
  scheduledFormulaRecalculateRef.current = true;
@@ -43122,7 +43446,7 @@ var UEditor = import_react79.default.forwardRef(({
43122
43446
  }
43123
43447
  });
43124
43448
  }, []);
43125
- const resolvedUploadFile = (0, import_react79.useMemo)(() => {
43449
+ const resolvedUploadFile = (0, import_react80.useMemo)(() => {
43126
43450
  if (uploadFile) return uploadFile;
43127
43451
  if (uploadFileForSave) {
43128
43452
  return async (file) => {
@@ -43132,7 +43456,7 @@ var UEditor = import_react79.default.forwardRef(({
43132
43456
  }
43133
43457
  return uploadImage;
43134
43458
  }, [uploadFile, uploadFileForSave, uploadImage]);
43135
- const extensions = (0, import_react79.useMemo)(
43459
+ const extensions = (0, import_react80.useMemo)(
43136
43460
  () => [
43137
43461
  ...buildUEditorExtensions({
43138
43462
  placeholder: effectivePlaceholder,
@@ -43151,11 +43475,11 @@ var UEditor = import_react79.default.forwardRef(({
43151
43475
  ],
43152
43476
  [effectivePlaceholder, t, maxCharacters, uploadImage, resolvedUploadFile, imageInsertMode, maxImageFileSize, allowedImageMimeTypes, fallbackToDataUrl, editable, fetchMetadata, extraExtensions]
43153
43477
  );
43154
- const syncFormulaRangeHighlight = import_react79.default.useCallback((pickState) => {
43478
+ const syncFormulaRangeHighlight = import_react80.default.useCallback((pickState) => {
43155
43479
  const container = formulaRangeSurfaceRef.current;
43156
43480
  setFormulaRangeHighlight(container && pickState ? getFormulaRangePickHighlight(container, pickState) : null);
43157
43481
  }, []);
43158
- const editor = (0, import_react80.useEditor)({
43482
+ const editor = (0, import_react81.useEditor)({
43159
43483
  immediatelyRender: false,
43160
43484
  extensions,
43161
43485
  content,
@@ -43196,6 +43520,22 @@ var UEditor = import_react79.default.forwardRef(({
43196
43520
  },
43197
43521
  keydown: (_view, event) => {
43198
43522
  if (!(event instanceof KeyboardEvent)) return false;
43523
+ const formulaContext = getFormulaEditingTableContext(_view);
43524
+ if (formulaContext && event.key === "Enter") {
43525
+ event.preventDefault();
43526
+ event.stopPropagation();
43527
+ const activeEditor = editorInstanceRef.current;
43528
+ if (activeEditor && !isDraftTableFormula(formulaContext.formula)) {
43529
+ recalculateActiveTableFormulas(activeEditor);
43530
+ }
43531
+ return true;
43532
+ }
43533
+ if (formulaContext && event.key === "Escape") {
43534
+ event.preventDefault();
43535
+ event.stopPropagation();
43536
+ cancelFormulaEditing(_view);
43537
+ return true;
43538
+ }
43199
43539
  if (event.key === "ArrowLeft" || event.key === "ArrowRight" || event.key === "ArrowUp" || event.key === "ArrowDown") {
43200
43540
  event.stopPropagation();
43201
43541
  }
@@ -43236,13 +43576,23 @@ var UEditor = import_react79.default.forwardRef(({
43236
43576
  scheduleFormulaRecalculate(editor2, { force: true });
43237
43577
  }
43238
43578
  });
43579
+ (0, import_react80.useEffect)(() => {
43580
+ editorInstanceRef.current = editor;
43581
+ return () => {
43582
+ if (editorInstanceRef.current === editor) editorInstanceRef.current = null;
43583
+ };
43584
+ }, [editor]);
43239
43585
  const {
43240
43586
  editorContentRef,
43241
43587
  tableColumnGuideRef,
43242
43588
  tableRowGuideRef,
43243
43589
  activeTableCellHighlightRef
43244
43590
  } = useUEditorTableInteractions(editor, editable);
43245
- (0, import_react79.useImperativeHandle)(
43591
+ const formulaCoordinateOverlayRef = useFormulaCoordinateOverlay(editor, editorContentRef, {
43592
+ apply: t("tableMenu.apply"),
43593
+ cancel: t("imageInput.cancelBtn")
43594
+ });
43595
+ (0, import_react80.useImperativeHandle)(
43246
43596
  ref,
43247
43597
  () => ({
43248
43598
  editor,
@@ -43267,7 +43617,7 @@ var UEditor = import_react79.default.forwardRef(({
43267
43617
  }),
43268
43618
  [content, editor, uploadImageForSave, uploadFileForSave, uploadImageConcurrency]
43269
43619
  );
43270
- (0, import_react79.useEffect)(() => {
43620
+ (0, import_react80.useEffect)(() => {
43271
43621
  if (!editor) return;
43272
43622
  queueMicrotask(() => {
43273
43623
  if (!editor.isDestroyed) {
@@ -43275,7 +43625,7 @@ var UEditor = import_react79.default.forwardRef(({
43275
43625
  }
43276
43626
  });
43277
43627
  }, [editor]);
43278
- (0, import_react79.useEffect)(() => {
43628
+ (0, import_react80.useEffect)(() => {
43279
43629
  if (!editor) return;
43280
43630
  const nextContent = content ?? "";
43281
43631
  if (lastAppliedContentRef.current === nextContent) return;
@@ -43395,6 +43745,14 @@ var UEditor = import_react79.default.forwardRef(({
43395
43745
  className: "pointer-events-none hidden absolute z-20 rounded-[2px] border-2 border-primary bg-primary/10"
43396
43746
  }
43397
43747
  ),
43748
+ /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(
43749
+ "div",
43750
+ {
43751
+ ref: formulaCoordinateOverlayRef,
43752
+ "data-ueditor-formula-coordinate-overlay": "",
43753
+ className: "pointer-events-none absolute inset-0 z-[21] hidden overflow-visible"
43754
+ }
43755
+ ),
43398
43756
  formulaRangeHighlight && /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(
43399
43757
  "span",
43400
43758
  {
@@ -43411,7 +43769,7 @@ var UEditor = import_react79.default.forwardRef(({
43411
43769
  ),
43412
43770
  editable && /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(TableControls, { editor, containerRef: editorContentRef }),
43413
43771
  /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(
43414
- import_react80.EditorContent,
43772
+ import_react81.EditorContent,
43415
43773
  {
43416
43774
  editor,
43417
43775
  className: "min-h-full"