@underverse-ui/underverse 1.0.150 → 1.0.151

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
@@ -1029,6 +1029,10 @@ var en_default = {
1029
1029
  borderWidth: "Border Width",
1030
1030
  borderColor: "Border Color",
1031
1031
  clearBorder: "Clear Border",
1032
+ formula: "Formula",
1033
+ apply: "Apply",
1034
+ clear: "Clear",
1035
+ recalculate: "Recalculate",
1032
1036
  done: "Done"
1033
1037
  },
1034
1038
  callout: {
@@ -1405,6 +1409,10 @@ var vi_default = {
1405
1409
  borderWidth: "\u0110\u1ED9 d\xE0y vi\u1EC1n",
1406
1410
  borderColor: "M\xE0u vi\u1EC1n",
1407
1411
  clearBorder: "X\xF3a vi\u1EC1n",
1412
+ formula: "C\xF4ng th\u1EE9c",
1413
+ apply: "\xC1p d\u1EE5ng",
1414
+ clear: "X\xF3a",
1415
+ recalculate: "T\xEDnh l\u1EA1i",
1408
1416
  done: "Xong"
1409
1417
  },
1410
1418
  callout: {
@@ -27812,6 +27820,46 @@ var CustomTableCell = import_extension_table_cell.default.extend({
27812
27820
  "data-border-width": attributes.borderWidth
27813
27821
  };
27814
27822
  }
27823
+ },
27824
+ cellId: {
27825
+ default: null,
27826
+ parseHTML: (element) => element.getAttribute("data-cell-id") || null,
27827
+ renderHTML: (attributes) => {
27828
+ if (!attributes.cellId) return {};
27829
+ return {
27830
+ "data-cell-id": attributes.cellId
27831
+ };
27832
+ }
27833
+ },
27834
+ numberFormat: {
27835
+ default: null,
27836
+ parseHTML: (element) => element.getAttribute("data-number-format") || null,
27837
+ renderHTML: (attributes) => {
27838
+ if (!attributes.numberFormat) return {};
27839
+ return {
27840
+ "data-number-format": attributes.numberFormat
27841
+ };
27842
+ }
27843
+ },
27844
+ formula: {
27845
+ default: null,
27846
+ parseHTML: (element) => element.getAttribute("data-formula") || null,
27847
+ renderHTML: (attributes) => {
27848
+ if (!attributes.formula) return {};
27849
+ return {
27850
+ "data-formula": attributes.formula
27851
+ };
27852
+ }
27853
+ },
27854
+ computedValue: {
27855
+ default: null,
27856
+ parseHTML: (element) => element.getAttribute("data-computed-value") || null,
27857
+ renderHTML: (attributes) => {
27858
+ if (!attributes.computedValue) return {};
27859
+ return {
27860
+ "data-computed-value": attributes.computedValue
27861
+ };
27862
+ }
27815
27863
  }
27816
27864
  };
27817
27865
  },
@@ -27879,6 +27927,46 @@ var CustomTableHeader = import_extension_table_header.default.extend({
27879
27927
  "data-border-width": attributes.borderWidth
27880
27928
  };
27881
27929
  }
27930
+ },
27931
+ cellId: {
27932
+ default: null,
27933
+ parseHTML: (element) => element.getAttribute("data-cell-id") || null,
27934
+ renderHTML: (attributes) => {
27935
+ if (!attributes.cellId) return {};
27936
+ return {
27937
+ "data-cell-id": attributes.cellId
27938
+ };
27939
+ }
27940
+ },
27941
+ numberFormat: {
27942
+ default: null,
27943
+ parseHTML: (element) => element.getAttribute("data-number-format") || null,
27944
+ renderHTML: (attributes) => {
27945
+ if (!attributes.numberFormat) return {};
27946
+ return {
27947
+ "data-number-format": attributes.numberFormat
27948
+ };
27949
+ }
27950
+ },
27951
+ formula: {
27952
+ default: null,
27953
+ parseHTML: (element) => element.getAttribute("data-formula") || null,
27954
+ renderHTML: (attributes) => {
27955
+ if (!attributes.formula) return {};
27956
+ return {
27957
+ "data-formula": attributes.formula
27958
+ };
27959
+ }
27960
+ },
27961
+ computedValue: {
27962
+ default: null,
27963
+ parseHTML: (element) => element.getAttribute("data-computed-value") || null,
27964
+ renderHTML: (attributes) => {
27965
+ if (!attributes.computedValue) return {};
27966
+ return {
27967
+ "data-computed-value": attributes.computedValue
27968
+ };
27969
+ }
27882
27970
  }
27883
27971
  };
27884
27972
  },
@@ -28591,6 +28679,34 @@ function collectChildren(node) {
28591
28679
  function createEmptyCellNode(cellNode) {
28592
28680
  return cellNode.type.createAndFill(cellNode.attrs) ?? cellNode;
28593
28681
  }
28682
+ function createCellCopyForColumnDuplicate(cellNode) {
28683
+ return cellNode.type.create(cellNode.attrs, cellNode.content);
28684
+ }
28685
+ function getTableRows(tableNode) {
28686
+ const rows = [];
28687
+ tableNode.forEach((rowNode, rowOffset) => {
28688
+ const cells = [];
28689
+ rowNode.forEach((cellNode, cellOffset, index) => {
28690
+ cells.push({
28691
+ index,
28692
+ node: cellNode,
28693
+ relativePos: rowOffset + 1 + cellOffset
28694
+ });
28695
+ });
28696
+ rows.push({
28697
+ node: rowNode,
28698
+ cells
28699
+ });
28700
+ });
28701
+ return rows;
28702
+ }
28703
+ function safeFindCell(map, relativePos) {
28704
+ try {
28705
+ return map.findCell(relativePos);
28706
+ } catch {
28707
+ return null;
28708
+ }
28709
+ }
28594
28710
  function getSelectedTableRect(editor) {
28595
28711
  const cellSelection = getCellSelectionPositions(editor.state.selection);
28596
28712
  if (cellSelection) {
@@ -28716,34 +28832,49 @@ function duplicateTableRowAt(editor, rowIndex, cellPos) {
28716
28832
  }
28717
28833
  function clearTableRowAt(editor, rowIndex, cellPos) {
28718
28834
  return replaceTableAtCellPos(editor, cellPos, (tableNode) => {
28719
- const rows = collectChildren(tableNode);
28720
- const rowNode = rows[rowIndex];
28721
- if (!rowNode) return null;
28722
- const cells = collectChildren(rowNode).map((cellNode) => createEmptyCellNode(cellNode));
28723
- rows[rowIndex] = rowNode.type.create(rowNode.attrs, cells);
28835
+ const map = import_tables.TableMap.get(tableNode);
28836
+ if (rowIndex < 0 || rowIndex >= map.height) return null;
28837
+ const rows = getTableRows(tableNode).map((rowInfo) => {
28838
+ const cells = collectChildren(rowInfo.node);
28839
+ for (const entry of rowInfo.cells) {
28840
+ const rect = safeFindCell(map, entry.relativePos);
28841
+ if (!rect || rect.top > rowIndex || rowIndex >= rect.bottom) continue;
28842
+ cells[entry.index] = createEmptyCellNode(entry.node);
28843
+ }
28844
+ return rowInfo.node.type.create(rowInfo.node.attrs, cells);
28845
+ });
28724
28846
  return tableNode.type.create(tableNode.attrs, rows);
28725
28847
  });
28726
28848
  }
28727
28849
  function duplicateTableColumnAt(editor, columnIndex, cellPos) {
28728
28850
  return replaceTableAtCellPos(editor, cellPos, (tableNode) => {
28729
- const rows = collectChildren(tableNode).map((rowNode) => {
28730
- const cells = collectChildren(rowNode);
28731
- const cellNode = cells[columnIndex];
28732
- if (!cellNode) return rowNode;
28733
- cells.splice(columnIndex + 1, 0, cellNode.copy(cellNode.content));
28734
- return rowNode.type.create(rowNode.attrs, cells);
28851
+ const map = import_tables.TableMap.get(tableNode);
28852
+ if (columnIndex < 0 || columnIndex >= map.width) return null;
28853
+ const rows = getTableRows(tableNode).map((rowInfo, rowIndex) => {
28854
+ const cells = collectChildren(rowInfo.node);
28855
+ const sourceCell = rowInfo.cells.find((entry) => {
28856
+ const rect = safeFindCell(map, entry.relativePos);
28857
+ return rect && rect.top === rowIndex && rect.left <= columnIndex && columnIndex < rect.right;
28858
+ });
28859
+ if (!sourceCell) return rowInfo.node;
28860
+ cells.splice(sourceCell.index + 1, 0, createCellCopyForColumnDuplicate(sourceCell.node));
28861
+ return rowInfo.node.type.create(rowInfo.node.attrs, cells);
28735
28862
  });
28736
28863
  return tableNode.type.create(tableNode.attrs, rows);
28737
28864
  });
28738
28865
  }
28739
28866
  function clearTableColumnAt(editor, columnIndex, cellPos) {
28740
28867
  return replaceTableAtCellPos(editor, cellPos, (tableNode) => {
28741
- const rows = collectChildren(tableNode).map((rowNode) => {
28742
- const cells = collectChildren(rowNode);
28743
- const cellNode = cells[columnIndex];
28744
- if (!cellNode) return rowNode;
28745
- cells[columnIndex] = createEmptyCellNode(cellNode);
28746
- return rowNode.type.create(rowNode.attrs, cells);
28868
+ const map = import_tables.TableMap.get(tableNode);
28869
+ if (columnIndex < 0 || columnIndex >= map.width) return null;
28870
+ const rows = getTableRows(tableNode).map((rowInfo) => {
28871
+ const cells = collectChildren(rowInfo.node);
28872
+ for (const entry of rowInfo.cells) {
28873
+ const rect = safeFindCell(map, entry.relativePos);
28874
+ if (!rect || rect.left > columnIndex || columnIndex >= rect.right) continue;
28875
+ cells[entry.index] = createEmptyCellNode(entry.node);
28876
+ }
28877
+ return rowInfo.node.type.create(rowInfo.node.attrs, cells);
28747
28878
  });
28748
28879
  return tableNode.type.create(tableNode.attrs, rows);
28749
28880
  });
@@ -29795,14 +29926,410 @@ var EditorToolbar = ({
29795
29926
  // src/components/UEditor/menus.tsx
29796
29927
  var import_react62 = require("react");
29797
29928
  var import_react63 = require("@tiptap/react");
29798
- var import_tables2 = require("@tiptap/pm/tables");
29929
+ var import_tables3 = require("@tiptap/pm/tables");
29799
29930
  var import_react_dom8 = require("react-dom");
29800
29931
  var import_lucide_react49 = require("lucide-react");
29932
+
29933
+ // src/components/UEditor/table-formula-commands.ts
29934
+ var import_tables2 = require("@tiptap/pm/tables");
29935
+
29936
+ // src/components/UEditor/table-formula.ts
29937
+ var CELL_ADDRESS_RE = /^([A-Z]+)([1-9]\d*)$/i;
29938
+ var CELL_RANGE_RE = /^([A-Z]+[1-9]\d*):([A-Z]+[1-9]\d*)$/i;
29939
+ var SUPPORTED_FUNCTIONS = /* @__PURE__ */ new Set(["SUM", "AVG", "MIN", "MAX", "COUNT"]);
29940
+ function columnNameToIndex(columnName) {
29941
+ const normalized = columnName.trim().toUpperCase();
29942
+ if (!/^[A-Z]+$/.test(normalized)) {
29943
+ return -1;
29944
+ }
29945
+ let index = 0;
29946
+ for (const char of normalized) {
29947
+ index = index * 26 + char.charCodeAt(0) - 64;
29948
+ }
29949
+ return index - 1;
29950
+ }
29951
+ function indexToColumnName(index) {
29952
+ if (!Number.isInteger(index) || index < 0) {
29953
+ return "";
29954
+ }
29955
+ let value = index + 1;
29956
+ let name = "";
29957
+ while (value > 0) {
29958
+ const remainder = (value - 1) % 26;
29959
+ name = String.fromCharCode(65 + remainder) + name;
29960
+ value = Math.floor((value - 1) / 26);
29961
+ }
29962
+ return name;
29963
+ }
29964
+ function parseTableCellAddress(input) {
29965
+ const match = input.trim().match(CELL_ADDRESS_RE);
29966
+ if (!match) {
29967
+ return null;
29968
+ }
29969
+ const column = columnNameToIndex(match[1] ?? "");
29970
+ const row = Number.parseInt(match[2] ?? "", 10) - 1;
29971
+ if (column < 0 || row < 0) {
29972
+ return null;
29973
+ }
29974
+ return {
29975
+ column,
29976
+ row,
29977
+ label: `${indexToColumnName(column)}${row + 1}`
29978
+ };
29979
+ }
29980
+ function parseTableCellRange(input) {
29981
+ const match = input.trim().match(CELL_RANGE_RE);
29982
+ if (!match) {
29983
+ return null;
29984
+ }
29985
+ const from = parseTableCellAddress(match[1] ?? "");
29986
+ const to = parseTableCellAddress(match[2] ?? "");
29987
+ if (!from || !to) {
29988
+ return null;
29989
+ }
29990
+ return { from, to };
29991
+ }
29992
+ function getTableCellRangeLabels(range) {
29993
+ const startColumn = Math.min(range.from.column, range.to.column);
29994
+ const endColumn = Math.max(range.from.column, range.to.column);
29995
+ const startRow = Math.min(range.from.row, range.to.row);
29996
+ const endRow = Math.max(range.from.row, range.to.row);
29997
+ const labels = [];
29998
+ for (let row = startRow; row <= endRow; row += 1) {
29999
+ for (let column = startColumn; column <= endColumn; column += 1) {
30000
+ labels.push(`${indexToColumnName(column)}${row + 1}`);
30001
+ }
30002
+ }
30003
+ return labels;
30004
+ }
30005
+ function normalizeTableFormula(formula) {
30006
+ return formula.trim().replace(/^=/, "").trim();
30007
+ }
30008
+ function evaluateBasicTableFormula(formula, getCellValue) {
30009
+ const normalized = normalizeTableFormula(formula);
30010
+ if (!normalized) {
30011
+ return { value: null, error: "empty" };
30012
+ }
30013
+ const tokens = tokenizeFormula(normalized);
30014
+ if (!tokens) {
30015
+ return { value: null, error: "invalid-formula" };
30016
+ }
30017
+ const parser = new FormulaParser(tokens, getCellValue);
30018
+ const result = parser.parseExpression();
30019
+ if (result.error) {
30020
+ return result;
30021
+ }
30022
+ if (!parser.isComplete()) {
30023
+ return { value: null, error: "invalid-formula" };
30024
+ }
30025
+ return result;
30026
+ }
30027
+ function tokenizeFormula(formula) {
30028
+ const tokens = [];
30029
+ let index = 0;
30030
+ while (index < formula.length) {
30031
+ const char = formula[index];
30032
+ if (!char) break;
30033
+ if (/\s/.test(char)) {
30034
+ index += 1;
30035
+ continue;
30036
+ }
30037
+ if (char === "," || char === "+" || char === "-" || char === "*" || char === "/" || char === "(" || char === ")") {
30038
+ if (char === ",") tokens.push({ type: "comma", value: char });
30039
+ else if (char === "(" || char === ")") tokens.push({ type: "paren", value: char });
30040
+ else tokens.push({ type: "operator", value: char });
30041
+ index += 1;
30042
+ continue;
30043
+ }
30044
+ const numberMatch = formula.slice(index).match(/^\d+(?:\.\d+)?/);
30045
+ if (numberMatch?.[0]) {
30046
+ tokens.push({ type: "number", value: Number.parseFloat(numberMatch[0]) });
30047
+ index += numberMatch[0].length;
30048
+ continue;
30049
+ }
30050
+ const identifierMatch = formula.slice(index).match(/^[A-Z]+[1-9]\d*(?::[A-Z]+[1-9]\d*)?|^[A-Z]+/i);
30051
+ if (identifierMatch?.[0]) {
30052
+ const value = identifierMatch[0].toUpperCase();
30053
+ if (CELL_RANGE_RE.test(value)) tokens.push({ type: "range", value });
30054
+ else if (parseTableCellAddress(value)) tokens.push({ type: "cell", value });
30055
+ else if (SUPPORTED_FUNCTIONS.has(value)) tokens.push({ type: "function", value });
30056
+ else return null;
30057
+ index += identifierMatch[0].length;
30058
+ continue;
30059
+ }
30060
+ return null;
30061
+ }
30062
+ return tokens;
30063
+ }
30064
+ var FormulaParser = class {
30065
+ constructor(tokens, getCellValue) {
30066
+ this.tokens = tokens;
30067
+ this.getCellValue = getCellValue;
30068
+ this.index = 0;
30069
+ }
30070
+ isComplete() {
30071
+ return this.index >= this.tokens.length;
30072
+ }
30073
+ parseExpression() {
30074
+ let left = this.parseTerm();
30075
+ while (!left.error) {
30076
+ const operator = this.peekOperator(["+", "-"]);
30077
+ if (!operator) break;
30078
+ this.index += 1;
30079
+ const right = this.parseTerm();
30080
+ if (right.error) return right;
30081
+ left = {
30082
+ value: operator.value === "+" ? left.value + right.value : left.value - right.value,
30083
+ error: null
30084
+ };
30085
+ }
30086
+ return left;
30087
+ }
30088
+ parseTerm() {
30089
+ let left = this.parseFactor();
30090
+ while (!left.error) {
30091
+ const operator = this.peekOperator(["*", "/"]);
30092
+ if (!operator) break;
30093
+ this.index += 1;
30094
+ const right = this.parseFactor();
30095
+ if (right.error) return right;
30096
+ if (operator.value === "/" && right.value === 0) {
30097
+ return { value: null, error: "division-by-zero" };
30098
+ }
30099
+ left = {
30100
+ value: operator.value === "*" ? left.value * right.value : left.value / right.value,
30101
+ error: null
30102
+ };
30103
+ }
30104
+ return left;
30105
+ }
30106
+ parseFactor() {
30107
+ const token = this.tokens[this.index];
30108
+ if (!token) {
30109
+ return { value: null, error: "invalid-formula" };
30110
+ }
30111
+ if (token.type === "operator" && token.value === "-") {
30112
+ this.index += 1;
30113
+ const value = this.parseFactor();
30114
+ if (value.error) return value;
30115
+ return { value: -value.value, error: null };
30116
+ }
30117
+ if (token.type === "number") {
30118
+ this.index += 1;
30119
+ return { value: token.value, error: null };
30120
+ }
30121
+ if (token.type === "cell") {
30122
+ this.index += 1;
30123
+ return this.readCellNumber(token.value);
30124
+ }
30125
+ if (token.type === "function") {
30126
+ return this.parseFunction(token.value);
30127
+ }
30128
+ if (token.type === "paren" && token.value === "(") {
30129
+ this.index += 1;
30130
+ const value = this.parseExpression();
30131
+ if (value.error) return value;
30132
+ if (!this.consumeParen(")")) {
30133
+ return { value: null, error: "invalid-formula" };
30134
+ }
30135
+ return value;
30136
+ }
30137
+ return { value: null, error: "invalid-formula" };
30138
+ }
30139
+ parseFunction(name) {
30140
+ this.index += 1;
30141
+ if (!this.consumeParen("(")) {
30142
+ return { value: null, error: "invalid-formula" };
30143
+ }
30144
+ const values = [];
30145
+ while (true) {
30146
+ const token = this.tokens[this.index];
30147
+ if (!token) {
30148
+ return { value: null, error: "invalid-formula" };
30149
+ }
30150
+ if (token.type === "range") {
30151
+ this.index += 1;
30152
+ const range = parseTableCellRange(token.value);
30153
+ if (!range) return { value: null, error: "invalid-reference" };
30154
+ for (const label of getTableCellRangeLabels(range)) {
30155
+ const cellValue = this.readCellNumber(label);
30156
+ if (cellValue.error) return cellValue;
30157
+ values.push(cellValue.value);
30158
+ }
30159
+ } else {
30160
+ const value = this.parseExpression();
30161
+ if (value.error) return value;
30162
+ values.push(value.value);
30163
+ }
30164
+ if (this.consumeComma()) {
30165
+ continue;
30166
+ }
30167
+ if (this.consumeParen(")")) {
30168
+ break;
30169
+ }
30170
+ return { value: null, error: "invalid-formula" };
30171
+ }
30172
+ if (values.length === 0) {
30173
+ return { value: null, error: "invalid-formula" };
30174
+ }
30175
+ if (name === "SUM") return { value: values.reduce((sum, value) => sum + value, 0), error: null };
30176
+ if (name === "AVG") return { value: values.reduce((sum, value) => sum + value, 0) / values.length, error: null };
30177
+ if (name === "MIN") return { value: Math.min(...values), error: null };
30178
+ if (name === "MAX") return { value: Math.max(...values), error: null };
30179
+ if (name === "COUNT") return { value: values.length, error: null };
30180
+ return { value: null, error: "invalid-formula" };
30181
+ }
30182
+ readCellNumber(label) {
30183
+ const value = this.getCellValue(label);
30184
+ const parsed = typeof value === "number" ? value : Number.parseFloat(String(value ?? "").trim());
30185
+ if (!Number.isFinite(parsed)) {
30186
+ return { value: null, error: "invalid-reference" };
30187
+ }
30188
+ return { value: parsed, error: null };
30189
+ }
30190
+ peekOperator(operators) {
30191
+ const token = this.tokens[this.index];
30192
+ return token?.type === "operator" && operators.includes(token.value) ? token : null;
30193
+ }
30194
+ consumeComma() {
30195
+ if (this.tokens[this.index]?.type !== "comma") {
30196
+ return false;
30197
+ }
30198
+ this.index += 1;
30199
+ return true;
30200
+ }
30201
+ consumeParen(value) {
30202
+ const token = this.tokens[this.index];
30203
+ if (token?.type !== "paren" || token.value !== value) {
30204
+ return false;
30205
+ }
30206
+ this.index += 1;
30207
+ return true;
30208
+ }
30209
+ };
30210
+
30211
+ // src/components/UEditor/table-formula-commands.ts
30212
+ function collectChildren2(node) {
30213
+ const children = [];
30214
+ node.forEach((child) => children.push(child));
30215
+ return children;
30216
+ }
30217
+ function getTableRows2(tableNode) {
30218
+ const rows = [];
30219
+ tableNode.forEach((rowNode, rowOffset) => {
30220
+ const cells = [];
30221
+ rowNode.forEach((cellNode, cellOffset, index) => {
30222
+ cells.push({
30223
+ index,
30224
+ node: cellNode,
30225
+ relativePos: rowOffset + 1 + cellOffset
30226
+ });
30227
+ });
30228
+ rows.push({ node: rowNode, cells });
30229
+ });
30230
+ return rows;
30231
+ }
30232
+ function safeFindCell2(map, relativePos) {
30233
+ try {
30234
+ return map.findCell(relativePos);
30235
+ } catch {
30236
+ return null;
30237
+ }
30238
+ }
30239
+ function getCellText(cellNode) {
30240
+ return cellNode.textBetween(0, cellNode.content.size, "\n").trim();
30241
+ }
30242
+ function buildTableValueGetter(tableNode) {
30243
+ const map = import_tables2.TableMap.get(tableNode);
30244
+ const values = /* @__PURE__ */ new Map();
30245
+ for (const rowInfo of getTableRows2(tableNode)) {
30246
+ for (const entry of rowInfo.cells) {
30247
+ const rect = safeFindCell2(map, entry.relativePos);
30248
+ if (!rect) continue;
30249
+ const label = `${indexToColumnName(rect.left)}${rect.top + 1}`;
30250
+ const computedValue = entry.node.attrs.computedValue;
30251
+ values.set(label, typeof computedValue === "string" && computedValue.trim() ? computedValue : getCellText(entry.node));
30252
+ }
30253
+ }
30254
+ return (label) => values.get(label.toUpperCase());
30255
+ }
30256
+ function getFormulaComputedValue(formula, tableNode) {
30257
+ const result = evaluateBasicTableFormula(formula, buildTableValueGetter(tableNode));
30258
+ return result.error ? `#${result.error.toUpperCase()}` : String(result.value);
30259
+ }
30260
+ function normalizeFormulaInput(formula) {
30261
+ const trimmed = formula.trim();
30262
+ if (!trimmed) return "";
30263
+ return trimmed.startsWith("=") ? trimmed : `=${trimmed}`;
30264
+ }
30265
+ function setSelectedTableCellFormula(editor, formula) {
30266
+ const normalized = normalizeFormulaInput(formula);
30267
+ const { state, view } = editor;
30268
+ if (!normalized) {
30269
+ const clearedFormula = (0, import_tables2.setCellAttr)("formula", null)(state, view.dispatch.bind(view));
30270
+ const clearedValue = (0, import_tables2.setCellAttr)("computedValue", null)(editor.state, view.dispatch.bind(view));
30271
+ if (clearedFormula || clearedValue) {
30272
+ view.focus();
30273
+ dispatchTableLayoutChange(editor);
30274
+ return true;
30275
+ }
30276
+ return false;
30277
+ }
30278
+ const rect = (0, import_tables2.selectedRect)(state);
30279
+ const computedValue = getFormulaComputedValue(normalized, rect.table);
30280
+ const appliedFormula = (0, import_tables2.setCellAttr)("formula", normalized)(state, view.dispatch.bind(view));
30281
+ const appliedValue = (0, import_tables2.setCellAttr)("computedValue", computedValue)(editor.state, view.dispatch.bind(view));
30282
+ if (appliedFormula || appliedValue) {
30283
+ view.focus();
30284
+ dispatchTableLayoutChange(editor);
30285
+ return true;
30286
+ }
30287
+ return false;
30288
+ }
30289
+ function clearSelectedTableCellFormula(editor) {
30290
+ return setSelectedTableCellFormula(editor, "");
30291
+ }
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);
30297
+ let changed = false;
30298
+ const rows = getTableRows2(tableNode).map((rowInfo) => {
30299
+ const cells = collectChildren2(rowInfo.node);
30300
+ for (const entry of rowInfo.cells) {
30301
+ const formula = typeof entry.node.attrs.formula === "string" ? entry.node.attrs.formula.trim() : "";
30302
+ if (!formula) continue;
30303
+ const rectForCell = safeFindCell2(map, entry.relativePos);
30304
+ 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;
30308
+ cells[entry.index] = entry.node.type.create(
30309
+ {
30310
+ ...entry.node.attrs,
30311
+ computedValue
30312
+ },
30313
+ entry.node.content,
30314
+ entry.node.marks
30315
+ );
30316
+ changed = true;
30317
+ }
30318
+ return rowInfo.node.type.create(rowInfo.node.attrs, cells);
30319
+ });
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));
30323
+ dispatchTableLayoutChange(editor);
30324
+ return true;
30325
+ }
30326
+
30327
+ // src/components/UEditor/menus.tsx
29801
30328
  var import_jsx_runtime86 = require("react/jsx-runtime");
29802
30329
  function applyTableCellBackground(editor, color) {
29803
30330
  const value = color || null;
29804
30331
  const { state, view } = editor;
29805
- const applied = (0, import_tables2.setCellAttr)("backgroundColor", value)(state, view.dispatch.bind(view));
30332
+ const applied = (0, import_tables3.setCellAttr)("backgroundColor", value)(state, view.dispatch.bind(view));
29806
30333
  if (applied) {
29807
30334
  view.focus();
29808
30335
  return;
@@ -29828,6 +30355,8 @@ var BubbleMenuContent = ({
29828
30355
  setShowLinkInput(initialShowLinkInput);
29829
30356
  }, [initialShowLinkInput]);
29830
30357
  const [showTypographyPanel, setShowTypographyPanel] = (0, import_react62.useState)(false);
30358
+ const [showFormulaPanel, setShowFormulaPanel] = (0, import_react62.useState)(false);
30359
+ const [formulaDraft, setFormulaDraft] = (0, import_react62.useState)("");
29831
30360
  const [showFontSizeOptions, setShowFontSizeOptions] = (0, import_react62.useState)(false);
29832
30361
  const [fontSizeDraft, setFontSizeDraft] = (0, import_react62.useState)("");
29833
30362
  const isImageSelected = editor.isActive("image");
@@ -29838,7 +30367,8 @@ var BubbleMenuContent = ({
29838
30367
  const currentTextColor = normalizeStyleValue(textStyleAttrs.color) || "inherit";
29839
30368
  const currentHighlightColor = normalizeStyleValue(editor.getAttributes("highlight").color) || "";
29840
30369
  const currentCellBgColor = normalizeStyleValue(editor.getAttributes("tableCell").backgroundColor || editor.getAttributes("tableHeader").backgroundColor) || "";
29841
- const isInTable2 = (0, import_tables2.isInTable)(editor.state);
30370
+ const currentCellFormula = normalizeStyleValue(editor.getAttributes("tableCell").formula || editor.getAttributes("tableHeader").formula) || "";
30371
+ const isInTable2 = (0, import_tables3.isInTable)(editor.state);
29842
30372
  const canMergeCells = isInTable2 && editor.can().mergeCells();
29843
30373
  const canSplitCell = isInTable2 && editor.can().splitCell();
29844
30374
  const currentFontSize = normalizeStyleValue(textStyleAttrs.fontSize);
@@ -29854,6 +30384,9 @@ var BubbleMenuContent = ({
29854
30384
  (0, import_react62.useEffect)(() => {
29855
30385
  setFontSizeDraft(currentFontSize.replace(/px$/i, ""));
29856
30386
  }, [currentFontSize]);
30387
+ (0, import_react62.useEffect)(() => {
30388
+ setFormulaDraft(currentCellFormula);
30389
+ }, [currentCellFormula]);
29857
30390
  const applyFontSizeDraft = () => {
29858
30391
  const normalized = fontSizeDraft.trim();
29859
30392
  if (!normalized) {
@@ -30056,6 +30589,82 @@ var BubbleMenuContent = ({
30056
30589
  )
30057
30590
  ] });
30058
30591
  }
30592
+ if (showFormulaPanel && isInTable2) {
30593
+ return /* @__PURE__ */ (0, import_jsx_runtime86.jsxs)("div", { className: "w-72 p-2", children: [
30594
+ /* @__PURE__ */ (0, import_jsx_runtime86.jsxs)("div", { className: "mb-2 flex items-center justify-between gap-2", children: [
30595
+ /* @__PURE__ */ (0, import_jsx_runtime86.jsx)("span", { className: "px-1 text-[11px] font-semibold uppercase tracking-wide text-muted-foreground", children: t("tableMenu.formula") || "Formula" }),
30596
+ /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(
30597
+ "button",
30598
+ {
30599
+ type: "button",
30600
+ onClick: () => {
30601
+ setShowFormulaPanel(false);
30602
+ onKeepOpenChange?.(false);
30603
+ },
30604
+ className: "rounded-md px-2 py-1 text-xs text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
30605
+ children: t("colors.done")
30606
+ }
30607
+ )
30608
+ ] }),
30609
+ /* @__PURE__ */ (0, import_jsx_runtime86.jsx)("div", { className: "flex h-9 items-center overflow-hidden rounded-md border border-border/60 bg-muted/40", children: /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(
30610
+ "input",
30611
+ {
30612
+ value: formulaDraft,
30613
+ onChange: (event) => setFormulaDraft(event.target.value),
30614
+ onMouseDown: (event) => event.stopPropagation(),
30615
+ onClick: (event) => event.stopPropagation(),
30616
+ onKeyDown: (event) => {
30617
+ event.stopPropagation();
30618
+ if (event.key === "Enter") {
30619
+ event.preventDefault();
30620
+ setSelectedTableCellFormula(editor, formulaDraft);
30621
+ setShowFormulaPanel(false);
30622
+ onKeepOpenChange?.(false);
30623
+ }
30624
+ },
30625
+ "aria-label": t("tableMenu.formula") || "Formula",
30626
+ placeholder: "=SUM(A1:A3)",
30627
+ className: "h-full min-w-0 flex-1 bg-transparent px-2 text-sm font-medium text-foreground outline-none"
30628
+ }
30629
+ ) }),
30630
+ /* @__PURE__ */ (0, import_jsx_runtime86.jsxs)("div", { className: "mt-2 grid grid-cols-3 gap-1", children: [
30631
+ /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(
30632
+ "button",
30633
+ {
30634
+ type: "button",
30635
+ onClick: () => {
30636
+ setSelectedTableCellFormula(editor, formulaDraft);
30637
+ setShowFormulaPanel(false);
30638
+ onKeepOpenChange?.(false);
30639
+ },
30640
+ className: "h-8 rounded-md bg-primary/10 text-xs font-semibold text-primary transition-colors hover:bg-primary/15",
30641
+ children: t("tableMenu.apply") || "Apply"
30642
+ }
30643
+ ),
30644
+ /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(
30645
+ "button",
30646
+ {
30647
+ type: "button",
30648
+ onClick: () => {
30649
+ clearSelectedTableCellFormula(editor);
30650
+ setFormulaDraft("");
30651
+ },
30652
+ className: "h-8 rounded-md bg-muted/40 text-xs font-semibold text-foreground transition-colors hover:bg-muted",
30653
+ children: t("tableMenu.clear") || "Clear"
30654
+ }
30655
+ ),
30656
+ /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(
30657
+ "button",
30658
+ {
30659
+ type: "button",
30660
+ onClick: () => recalculateSelectedTable(editor),
30661
+ className: "h-8 rounded-md bg-muted/40 text-xs font-semibold text-foreground transition-colors hover:bg-muted",
30662
+ children: t("tableMenu.recalculate") || "Recalc"
30663
+ }
30664
+ )
30665
+ ] })
30666
+ ] });
30667
+ }
30059
30668
  if (showTypographyPanel) {
30060
30669
  return /* @__PURE__ */ (0, import_jsx_runtime86.jsxs)("div", { className: "w-72 p-2", children: [
30061
30670
  /* @__PURE__ */ (0, import_jsx_runtime86.jsxs)("div", { className: "mb-2 flex items-center justify-between gap-2", children: [
@@ -30269,6 +30878,21 @@ var BubbleMenuContent = ({
30269
30878
  )
30270
30879
  }
30271
30880
  ),
30881
+ /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(
30882
+ ToolbarButton,
30883
+ {
30884
+ onMouseDown: () => {
30885
+ onKeepOpenChange?.(true);
30886
+ },
30887
+ onClick: () => {
30888
+ setFormulaDraft(currentCellFormula);
30889
+ setShowFormulaPanel(true);
30890
+ },
30891
+ active: Boolean(currentCellFormula),
30892
+ title: t("tableMenu.formula") || "Formula",
30893
+ children: /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(import_lucide_react49.Sigma, { className: "w-4 h-4" })
30894
+ }
30895
+ ),
30272
30896
  /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(
30273
30897
  ToolbarButton,
30274
30898
  {
@@ -34110,7 +34734,7 @@ if (typeof WeakMap != "undefined") {
34110
34734
  return cache[cachePos++] = value;
34111
34735
  };
34112
34736
  }
34113
- var TableMap2 = class {
34737
+ var TableMap3 = class {
34114
34738
  constructor(width, height, map, problems) {
34115
34739
  this.width = width;
34116
34740
  this.height = height;
@@ -34247,7 +34871,7 @@ function computeMap(table) {
34247
34871
  pos++;
34248
34872
  }
34249
34873
  if (width === 0 || height === 0) (problems || (problems = [])).push({ type: "zero_sized" });
34250
- const tableMap = new TableMap2(width, height, map, problems);
34874
+ const tableMap = new TableMap3(width, height, map, problems);
34251
34875
  let badWidths = false;
34252
34876
  for (let i = 0; !badWidths && i < colWidths.length; i += 2) if (colWidths[i] != null && colWidths[i + 1] < height) badWidths = true;
34253
34877
  if (badWidths) findBadColWidths(tableMap, colWidths, table);
@@ -34351,7 +34975,7 @@ function inSameTable($cellA, $cellB) {
34351
34975
  }
34352
34976
  function nextCell($pos, axis, dir) {
34353
34977
  const table = $pos.node(-1);
34354
- const map = TableMap2.get(table);
34978
+ const map = TableMap3.get(table);
34355
34979
  const tableStart = $pos.start(-1);
34356
34980
  const moved = map.nextCell($pos.pos - tableStart, axis, dir);
34357
34981
  return moved == null ? null : $pos.node(0).resolve(tableStart + moved);
@@ -34371,7 +34995,7 @@ function removeColSpan(attrs, pos, n = 1) {
34371
34995
  var CellSelection = class CellSelection2 extends Selection {
34372
34996
  constructor($anchorCell, $headCell = $anchorCell) {
34373
34997
  const table = $anchorCell.node(-1);
34374
- const map = TableMap2.get(table);
34998
+ const map = TableMap3.get(table);
34375
34999
  const tableStart = $anchorCell.start(-1);
34376
35000
  const rect = map.rectBetween($anchorCell.pos - tableStart, $headCell.pos - tableStart);
34377
35001
  const doc = $anchorCell.node(0);
@@ -34400,7 +35024,7 @@ var CellSelection = class CellSelection2 extends Selection {
34400
35024
  }
34401
35025
  content() {
34402
35026
  const table = this.$anchorCell.node(-1);
34403
- const map = TableMap2.get(table);
35027
+ const map = TableMap3.get(table);
34404
35028
  const tableStart = this.$anchorCell.start(-1);
34405
35029
  const rect = map.rectBetween(this.$anchorCell.pos - tableStart, this.$headCell.pos - tableStart);
34406
35030
  const seen = {};
@@ -34454,7 +35078,7 @@ var CellSelection = class CellSelection2 extends Selection {
34454
35078
  }
34455
35079
  forEachCell(f) {
34456
35080
  const table = this.$anchorCell.node(-1);
34457
- const map = TableMap2.get(table);
35081
+ const map = TableMap3.get(table);
34458
35082
  const tableStart = this.$anchorCell.start(-1);
34459
35083
  const cells = map.cellsInRect(map.rectBetween(this.$anchorCell.pos - tableStart, this.$headCell.pos - tableStart));
34460
35084
  for (let i = 0; i < cells.length; i++) f(table.nodeAt(cells[i]), tableStart + cells[i]);
@@ -34469,7 +35093,7 @@ var CellSelection = class CellSelection2 extends Selection {
34469
35093
  }
34470
35094
  static colSelection($anchorCell, $headCell = $anchorCell) {
34471
35095
  const table = $anchorCell.node(-1);
34472
- const map = TableMap2.get(table);
35096
+ const map = TableMap3.get(table);
34473
35097
  const tableStart = $anchorCell.start(-1);
34474
35098
  const anchorRect = map.findCell($anchorCell.pos - tableStart);
34475
35099
  const headRect = map.findCell($headCell.pos - tableStart);
@@ -34485,7 +35109,7 @@ var CellSelection = class CellSelection2 extends Selection {
34485
35109
  }
34486
35110
  isRowSelection() {
34487
35111
  const table = this.$anchorCell.node(-1);
34488
- const map = TableMap2.get(table);
35112
+ const map = TableMap3.get(table);
34489
35113
  const tableStart = this.$anchorCell.start(-1);
34490
35114
  const anchorLeft = map.colCount(this.$anchorCell.pos - tableStart);
34491
35115
  const headLeft = map.colCount(this.$headCell.pos - tableStart);
@@ -34499,7 +35123,7 @@ var CellSelection = class CellSelection2 extends Selection {
34499
35123
  }
34500
35124
  static rowSelection($anchorCell, $headCell = $anchorCell) {
34501
35125
  const table = $anchorCell.node(-1);
34502
- const map = TableMap2.get(table);
35126
+ const map = TableMap3.get(table);
34503
35127
  const tableStart = $anchorCell.start(-1);
34504
35128
  const anchorRect = map.findCell($anchorCell.pos - tableStart);
34505
35129
  const headRect = map.findCell($headCell.pos - tableStart);
@@ -34548,7 +35172,7 @@ var CellBookmark = class CellBookmark2 {
34548
35172
  };
34549
35173
  var fixTablesKey = new PluginKey4("fix-tables");
34550
35174
  function convertTableNodeToArrayOfRows(tableNode) {
34551
- const map = TableMap2.get(tableNode);
35175
+ const map = TableMap3.get(tableNode);
34552
35176
  const rows = [];
34553
35177
  const rowCount = map.height;
34554
35178
  const colCount$1 = map.width;
@@ -34579,7 +35203,7 @@ function convertTableNodeToArrayOfRows(tableNode) {
34579
35203
  }
34580
35204
  function convertArrayOfRowsToTableNode(tableNode, arrayOfNodes) {
34581
35205
  const newRows = [];
34582
- const map = TableMap2.get(tableNode);
35206
+ const map = TableMap3.get(tableNode);
34583
35207
  const rowCount = map.height;
34584
35208
  const colCount$1 = map.width;
34585
35209
  for (let rowIndex = 0; rowIndex < rowCount; rowIndex++) {
@@ -34628,7 +35252,7 @@ function findParentNode(predicate, $pos) {
34628
35252
  function getCellsInColumn(columnIndex, selection) {
34629
35253
  const table = findTable(selection.$from);
34630
35254
  if (!table) return;
34631
- const map = TableMap2.get(table.node);
35255
+ const map = TableMap3.get(table.node);
34632
35256
  if (columnIndex < 0 || columnIndex > map.width - 1) return;
34633
35257
  return map.cellsInRect({
34634
35258
  left: columnIndex,
@@ -34649,7 +35273,7 @@ function getCellsInColumn(columnIndex, selection) {
34649
35273
  function getCellsInRow(rowIndex, selection) {
34650
35274
  const table = findTable(selection.$from);
34651
35275
  if (!table) return;
34652
- const map = TableMap2.get(table.node);
35276
+ const map = TableMap3.get(table.node);
34653
35277
  if (rowIndex < 0 || rowIndex > map.height - 1) return;
34654
35278
  return map.cellsInRect({
34655
35279
  left: 0,
@@ -34778,7 +35402,7 @@ function moveColumn(moveColParams) {
34778
35402
  const newTable = moveTableColumn$1(table.node, indexesOriginColumn, indexesTargetColumn, 0);
34779
35403
  tr.replaceWith(table.pos, table.pos + table.node.nodeSize, newTable);
34780
35404
  if (!select) return true;
34781
- const map = TableMap2.get(newTable);
35405
+ const map = TableMap3.get(newTable);
34782
35406
  const start = table.start;
34783
35407
  const index = targetIndex;
34784
35408
  const lastCell = map.positionAt(map.height - 1, index, newTable);
@@ -34806,7 +35430,7 @@ function moveRow(moveRowParams) {
34806
35430
  const newTable = moveTableRow$1(table.node, indexesOriginRow, indexesTargetRow, 0);
34807
35431
  tr.replaceWith(table.pos, table.pos + table.node.nodeSize, newTable);
34808
35432
  if (!select) return true;
34809
- const map = TableMap2.get(newTable);
35433
+ const map = TableMap3.get(newTable);
34810
35434
  const start = table.start;
34811
35435
  const index = targetIndex;
34812
35436
  const lastCell = map.positionAt(index, map.width - 1, newTable);
@@ -34821,12 +35445,12 @@ function moveTableRow$1(table, indexesOrigin, indexesTarget, direction) {
34821
35445
  rows = moveRowInArrayOfRows(rows, indexesOrigin, indexesTarget, direction);
34822
35446
  return convertArrayOfRowsToTableNode(table, rows);
34823
35447
  }
34824
- function selectedRect2(state) {
35448
+ function selectedRect3(state) {
34825
35449
  const sel = state.selection;
34826
35450
  const $pos = selectionCell(state);
34827
35451
  const table = $pos.node(-1);
34828
35452
  const tableStart = $pos.start(-1);
34829
- const map = TableMap2.get(table);
35453
+ const map = TableMap3.get(table);
34830
35454
  return {
34831
35455
  ...sel instanceof CellSelection ? map.rectBetween(sel.$anchorCell.pos - tableStart, sel.$headCell.pos - tableStart) : map.findCell($pos.pos - tableStart),
34832
35456
  tableStart,
@@ -34839,7 +35463,7 @@ function deprecated_toggleHeader(type) {
34839
35463
  if (!isInTable(state)) return false;
34840
35464
  if (dispatch) {
34841
35465
  const types = tableNodeTypes(state.schema);
34842
- const rect = selectedRect2(state), tr = state.tr;
35466
+ const rect = selectedRect3(state), tr = state.tr;
34843
35467
  const cells = rect.map.cellsInRect(type == "column" ? {
34844
35468
  left: rect.left,
34845
35469
  top: 0,
@@ -34879,7 +35503,7 @@ function toggleHeader(type, options) {
34879
35503
  if (!isInTable(state)) return false;
34880
35504
  if (dispatch) {
34881
35505
  const types = tableNodeTypes(state.schema);
34882
- const rect = selectedRect2(state), tr = state.tr;
35506
+ const rect = selectedRect3(state), tr = state.tr;
34883
35507
  const isHeaderRowEnabled = isHeaderEnabledByType("row", rect, types);
34884
35508
  const isHeaderColumnEnabled = isHeaderEnabledByType("column", rect, types);
34885
35509
  const selectionStartsAt = (type === "column" ? isHeaderRowEnabled : type === "row" ? isHeaderColumnEnabled : false) ? 1 : 0;
@@ -35029,6 +35653,262 @@ var columnResizingPluginKey = new PluginKey4("tableColumnResizing");
35029
35653
  // src/components/UEditor/table-controls.tsx
35030
35654
  var import_lucide_react52 = require("lucide-react");
35031
35655
 
35656
+ // src/components/UEditor/table-layout-model.ts
35657
+ var FALLBACK_TABLE_ROW_HEIGHT = 44;
35658
+ var FALLBACK_TABLE_COLUMN_WIDTH = 160;
35659
+ function getVisibleTableBounds(layout) {
35660
+ const left = Math.max(layout.tableLeft, layout.wrapperLeft);
35661
+ const top = Math.max(layout.tableTop, layout.wrapperTop);
35662
+ const right = Math.min(layout.tableLeft + layout.tableWidth, layout.wrapperLeft + layout.viewportWidth);
35663
+ const bottom = Math.min(layout.tableTop + layout.tableHeight, layout.wrapperTop + layout.viewportHeight);
35664
+ return {
35665
+ left,
35666
+ top,
35667
+ right,
35668
+ bottom,
35669
+ width: Math.max(0, right - left),
35670
+ height: Math.max(0, bottom - top)
35671
+ };
35672
+ }
35673
+ function metricOrFallback(value, fallback) {
35674
+ return Number.isFinite(value) && value > 0 ? value : fallback;
35675
+ }
35676
+ function parsePixelMetric(value) {
35677
+ if (!value) return null;
35678
+ const parsed = Number.parseFloat(value);
35679
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
35680
+ }
35681
+ function getPrimaryCell(table) {
35682
+ const cell = table.querySelector("th,td");
35683
+ return cell instanceof HTMLTableCellElement ? cell : null;
35684
+ }
35685
+ function getLastCell(table) {
35686
+ const lastRow = table.rows.item(table.rows.length - 1);
35687
+ if (!(lastRow instanceof HTMLTableRowElement)) return null;
35688
+ const cell = lastRow.cells.item(lastRow.cells.length - 1);
35689
+ return cell instanceof HTMLTableCellElement ? cell : null;
35690
+ }
35691
+ function getCellFromTarget(target) {
35692
+ const element = resolveEventElement(target);
35693
+ if (!element) return null;
35694
+ const directCell = element.closest("th,td");
35695
+ if (directCell instanceof HTMLTableCellElement) {
35696
+ return directCell;
35697
+ }
35698
+ const table = element.closest("table");
35699
+ if (table instanceof HTMLTableElement) {
35700
+ return getPrimaryCell(table);
35701
+ }
35702
+ return null;
35703
+ }
35704
+ function findTableInfo(editor, pos) {
35705
+ const $pos = editor.state.doc.resolve(pos);
35706
+ for (let depth = $pos.depth; depth > 0; depth -= 1) {
35707
+ const node = $pos.node(depth);
35708
+ if (node.type.name === "table") {
35709
+ return {
35710
+ node,
35711
+ pos: $pos.before(depth),
35712
+ start: $pos.start(depth)
35713
+ };
35714
+ }
35715
+ }
35716
+ return null;
35717
+ }
35718
+ function getCellRelativePosFromDomPos(map, tableStart, domPos) {
35719
+ const relativeDomPos = domPos - tableStart;
35720
+ const seen = /* @__PURE__ */ new Set();
35721
+ for (const relativeCellPos of map.map) {
35722
+ if (seen.has(relativeCellPos)) continue;
35723
+ seen.add(relativeCellPos);
35724
+ if (relativeDomPos === relativeCellPos || relativeDomPos === relativeCellPos + 1) {
35725
+ return relativeCellPos;
35726
+ }
35727
+ }
35728
+ return null;
35729
+ }
35730
+ function buildLogicalColumnMetrics({
35731
+ editor,
35732
+ surface,
35733
+ surfaceRect,
35734
+ tableElement,
35735
+ tableInfo,
35736
+ tableLeft,
35737
+ tableWidth
35738
+ }) {
35739
+ const map = TableMap3.get(tableInfo.node);
35740
+ const fallbackWidth = metricOrFallback(tableWidth / map.width, FALLBACK_TABLE_COLUMN_WIDTH);
35741
+ const firstRow = tableElement.rows.item(0);
35742
+ const visualColumns = [];
35743
+ if (firstRow) {
35744
+ for (const tableCell of Array.from(firstRow.cells)) {
35745
+ if (!(tableCell instanceof HTMLTableCellElement)) continue;
35746
+ const cellPos = editor.view.posAtDOM(tableCell, 0);
35747
+ const relativeCellPos = getCellRelativePosFromDomPos(map, tableInfo.start, cellPos);
35748
+ if (relativeCellPos == null) continue;
35749
+ const cellMapRect = map.findCell(relativeCellPos);
35750
+ const cellRect = tableCell.getBoundingClientRect();
35751
+ const cellStart = cellRect.width > 0 ? cellRect.left - surfaceRect.left + surface.scrollLeft : tableLeft + cellMapRect.left * fallbackWidth;
35752
+ const size = metricOrFallback(cellRect.width, fallbackWidth * Math.max(1, cellMapRect.right - cellMapRect.left));
35753
+ visualColumns.push({
35754
+ index: cellMapRect.left,
35755
+ cellPos: tableInfo.start + relativeCellPos,
35756
+ start: cellStart,
35757
+ size,
35758
+ center: cellStart + size / 2
35759
+ });
35760
+ }
35761
+ }
35762
+ if (visualColumns.length > 0) {
35763
+ return visualColumns.sort((a, b) => a.index - b.index);
35764
+ }
35765
+ const cols = Array.from(tableElement.querySelectorAll("colgroup > col"));
35766
+ const parsedWidths = cols.slice(0, map.width).map((col) => parsePixelMetric(col.style.width) ?? parsePixelMetric(col.getAttribute("width")));
35767
+ const hasCompleteColWidths = parsedWidths.length >= map.width && parsedWidths.every((width) => typeof width === "number");
35768
+ let cursor = tableLeft;
35769
+ return Array.from({ length: map.width }, (_, index) => {
35770
+ const size = hasCompleteColWidths ? parsedWidths[index] : fallbackWidth;
35771
+ const start = hasCompleteColWidths ? cursor : tableLeft + index * fallbackWidth;
35772
+ cursor += size;
35773
+ return {
35774
+ index,
35775
+ cellPos: tableInfo.start + map.positionAt(0, index, tableInfo.node),
35776
+ start,
35777
+ size,
35778
+ center: start + size / 2
35779
+ };
35780
+ });
35781
+ }
35782
+ function buildLogicalRowMetrics({
35783
+ editor,
35784
+ surface,
35785
+ surfaceRect,
35786
+ tableInfo,
35787
+ rows,
35788
+ tableTop,
35789
+ tableHeight,
35790
+ cornerCell
35791
+ }) {
35792
+ const map = TableMap3.get(tableInfo.node);
35793
+ const fallbackHeight = metricOrFallback(tableHeight / map.height, FALLBACK_TABLE_ROW_HEIGHT);
35794
+ const visualRows = [];
35795
+ const seenCellPositions = /* @__PURE__ */ new Set();
35796
+ for (let rowIndex = 0; rowIndex < map.height; rowIndex += 1) {
35797
+ const relativeCellPos = map.map[rowIndex * map.width];
35798
+ if (seenCellPositions.has(relativeCellPos)) continue;
35799
+ seenCellPositions.add(relativeCellPos);
35800
+ const cellMapRect = map.findCell(relativeCellPos);
35801
+ const cellDom = editor.view.nodeDOM(tableInfo.start + relativeCellPos);
35802
+ const tableCell = cellDom instanceof HTMLTableCellElement ? cellDom : null;
35803
+ if (tableCell) {
35804
+ const cellRect = tableCell.getBoundingClientRect();
35805
+ const start = cellRect.height > 0 ? cellRect.top - surfaceRect.top + surface.scrollTop : tableTop + cellMapRect.top * fallbackHeight;
35806
+ const size = metricOrFallback(cellRect.height, fallbackHeight * Math.max(1, cellMapRect.bottom - cellMapRect.top));
35807
+ visualRows.push({
35808
+ index: cellMapRect.top,
35809
+ cellPos: tableInfo.start + relativeCellPos,
35810
+ start,
35811
+ size,
35812
+ center: start + size / 2
35813
+ });
35814
+ }
35815
+ }
35816
+ if (visualRows.length > 0) {
35817
+ return visualRows.sort((a, b) => a.index - b.index);
35818
+ }
35819
+ return rows.map((tableRow, index) => {
35820
+ const rowRect = tableRow.getBoundingClientRect();
35821
+ const anchorCell = tableRow.cells.item(0) ?? cornerCell;
35822
+ const start = rowRect.height > 0 ? rowRect.top - surfaceRect.top + surface.scrollTop : tableTop + index * fallbackHeight;
35823
+ const size = metricOrFallback(rowRect.height, fallbackHeight);
35824
+ return {
35825
+ index,
35826
+ cellPos: editor.view.posAtDOM(anchorCell, 0),
35827
+ start,
35828
+ size,
35829
+ center: start + size / 2
35830
+ };
35831
+ });
35832
+ }
35833
+ function buildTableControlLayout(editor, surface, cell) {
35834
+ const row = cell.closest("tr");
35835
+ const table = cell.closest("table");
35836
+ if (!(row instanceof HTMLTableRowElement) || !(table instanceof HTMLTableElement)) {
35837
+ return null;
35838
+ }
35839
+ const rows = Array.from(table.rows).filter((item) => item instanceof HTMLTableRowElement);
35840
+ const cornerCell = getLastCell(table);
35841
+ const cellPos = editor.view.posAtDOM(cell, 0);
35842
+ const tableInfo = findTableInfo(editor, cellPos);
35843
+ if (rows.length === 0 || !tableInfo || !(cornerCell instanceof HTMLTableCellElement)) {
35844
+ return null;
35845
+ }
35846
+ const map = TableMap3.get(tableInfo.node);
35847
+ const surfaceRect = surface.getBoundingClientRect();
35848
+ const tableRect = table.getBoundingClientRect();
35849
+ const wrapperElement = table.closest(".tableWrapper");
35850
+ const wrapper = wrapperElement instanceof HTMLElement ? wrapperElement : null;
35851
+ const wrapperRect = wrapper?.getBoundingClientRect() ?? tableRect;
35852
+ const tableLeft = tableRect.left - surfaceRect.left + surface.scrollLeft;
35853
+ const tableTop = tableRect.top - surfaceRect.top + surface.scrollTop;
35854
+ const avgRowHeight = metricOrFallback(tableRect.height / rows.length, FALLBACK_TABLE_ROW_HEIGHT);
35855
+ const avgColumnWidth = metricOrFallback(tableRect.width / map.width, FALLBACK_TABLE_COLUMN_WIDTH);
35856
+ const tableWidth = metricOrFallback(tableRect.width, avgColumnWidth * map.width);
35857
+ const tableHeight = metricOrFallback(tableRect.height, avgRowHeight * rows.length);
35858
+ const wrapperLeft = wrapperRect.left - surfaceRect.left + surface.scrollLeft;
35859
+ const wrapperTop = wrapperRect.top - surfaceRect.top + surface.scrollTop;
35860
+ const wrapperWidth = metricOrFallback(wrapperRect.width, tableWidth);
35861
+ const wrapperHeight = metricOrFallback(wrapperRect.height, tableHeight);
35862
+ const viewportWidth = metricOrFallback(wrapper?.clientWidth ?? wrapperRect.width, tableWidth);
35863
+ const viewportHeight = metricOrFallback(wrapper?.clientHeight ?? wrapperRect.height, tableHeight);
35864
+ const verticalScrollbarWidth = Math.max(0, Math.round(wrapperWidth - viewportWidth));
35865
+ const horizontalScrollbarHeight = Math.max(0, Math.round(wrapperHeight - viewportHeight));
35866
+ const rowHandles = buildLogicalRowMetrics({
35867
+ editor,
35868
+ surface,
35869
+ surfaceRect,
35870
+ tableInfo,
35871
+ rows,
35872
+ tableTop,
35873
+ tableHeight,
35874
+ cornerCell
35875
+ });
35876
+ const columnHandles = buildLogicalColumnMetrics({
35877
+ editor,
35878
+ surface,
35879
+ surfaceRect,
35880
+ tableElement: table,
35881
+ tableInfo,
35882
+ tableLeft,
35883
+ tableWidth
35884
+ });
35885
+ const activeCellRelativePos = getCellRelativePosFromDomPos(map, tableInfo.start, cellPos);
35886
+ const activeCellRect = activeCellRelativePos != null ? map.findCell(activeCellRelativePos) : { left: cell.cellIndex, top: row.rowIndex };
35887
+ const normalizedCellPos = activeCellRelativePos != null ? tableInfo.start + activeCellRelativePos : cellPos;
35888
+ return {
35889
+ cellPos: normalizedCellPos,
35890
+ cornerCellPos: tableInfo.start + map.positionAt(map.height - 1, map.width - 1, tableInfo.node),
35891
+ activeRowIndex: activeCellRect.top,
35892
+ activeColumnIndex: activeCellRect.left,
35893
+ tableLeft,
35894
+ tableTop,
35895
+ tableWidth,
35896
+ tableHeight,
35897
+ wrapperLeft,
35898
+ wrapperTop,
35899
+ wrapperWidth,
35900
+ wrapperHeight,
35901
+ viewportWidth,
35902
+ viewportHeight,
35903
+ horizontalScrollbarHeight,
35904
+ verticalScrollbarWidth,
35905
+ avgRowHeight,
35906
+ avgColumnWidth,
35907
+ rowHandles,
35908
+ columnHandles
35909
+ };
35910
+ }
35911
+
35032
35912
  // src/components/UEditor/table-hover-state.ts
35033
35913
  var MENU_HOVER_PADDING = 18;
35034
35914
  var ROW_HANDLE_HOVER_WIDTH = 28;
@@ -35062,18 +35942,18 @@ function buildTableHoverState({
35062
35942
  const directAddRow = targetElement?.closest?.("[data-table-control='add-row']");
35063
35943
  const directRowHandleIndex = directRowHandle instanceof HTMLElement ? Number.parseInt(directRowHandle.dataset.rowHandleIndex ?? "", 10) : Number.NaN;
35064
35944
  const directColumnHandleIndex = directColumnHandle instanceof HTMLElement ? Number.parseInt(directColumnHandle.dataset.columnHandleIndex ?? "", 10) : Number.NaN;
35065
- const visibleTableWidth = Math.min(layout.tableWidth, layout.viewportWidth);
35066
- const visibleTableHeight = Math.min(layout.tableHeight, layout.viewportHeight);
35945
+ const visibleBounds = getVisibleTableBounds(layout);
35946
+ const rowRailTop = layout.wrapperTop + layout.wrapperHeight;
35067
35947
  const isMouseInTable = relativeX >= layout.tableLeft && relativeX <= layout.tableLeft + layout.tableWidth && relativeY >= layout.tableTop && relativeY <= layout.tableTop + layout.tableHeight;
35068
35948
  const rowHandleIndex = Number.isFinite(directRowHandleIndex) ? directRowHandleIndex : layout.rowHandles.find((rowHandle) => relativeX >= layout.tableLeft - ROW_HANDLE_HOVER_WIDTH && relativeX <= layout.tableLeft && Math.abs(relativeY - rowHandle.center) <= HANDLE_HOVER_RADIUS)?.index ?? null;
35069
35949
  const columnHandleIndex = Number.isFinite(directColumnHandleIndex) ? directColumnHandleIndex : layout.columnHandles.find((columnHandle) => relativeY >= layout.tableTop - COLUMN_HANDLE_HOVER_HEIGHT && relativeY <= layout.tableTop && Math.abs(relativeX - columnHandle.center) <= HANDLE_HOVER_RADIUS)?.index ?? null;
35070
35950
  const menuVisible = Boolean(directTableMenu) || isMouseInTable || relativeX >= layout.tableLeft - MENU_HOVER_PADDING && relativeX <= layout.tableLeft + 42 && relativeY >= layout.tableTop - COLUMN_HANDLE_HOVER_HEIGHT && relativeY <= layout.tableTop + MENU_HOVER_PADDING;
35071
35951
  const lastRow = layout.rowHandles[layout.rowHandles.length - 1];
35072
35952
  const lastCol = layout.columnHandles[layout.columnHandles.length - 1];
35073
- const isMouseInLastColumn = lastCol ? relativeX >= lastCol.start && relativeX <= lastCol.start + lastCol.size && relativeY >= layout.tableTop && relativeY <= layout.tableTop + visibleTableHeight : false;
35074
- const addColumnVisible = Boolean(directAddColumn) || relativeX >= layout.tableLeft + visibleTableWidth && relativeX <= layout.tableLeft + visibleTableWidth + ADD_COLUMN_HOVER_WIDTH && relativeY >= layout.tableTop && relativeY <= layout.tableTop + visibleTableHeight || isMouseInLastColumn;
35075
- const isMouseInLastRow = lastRow ? relativeY >= lastRow.start && relativeY <= lastRow.start + lastRow.size && relativeX >= layout.tableLeft && relativeX <= layout.tableLeft + visibleTableWidth : false;
35076
- const addRowVisible = Boolean(directAddRow) || relativeY >= layout.tableTop + visibleTableHeight && relativeY <= layout.tableTop + visibleTableHeight + ADD_ROW_HOVER_HEIGHT && relativeX >= layout.tableLeft && relativeX <= layout.tableLeft + visibleTableWidth || isMouseInLastRow;
35953
+ const isMouseInLastColumn = lastCol ? relativeX >= lastCol.start && relativeX <= lastCol.start + lastCol.size && relativeY >= visibleBounds.top && relativeY <= visibleBounds.bottom : false;
35954
+ const addColumnVisible = Boolean(directAddColumn) || relativeX >= visibleBounds.right && relativeX <= visibleBounds.right + ADD_COLUMN_HOVER_WIDTH && relativeY >= visibleBounds.top && relativeY <= visibleBounds.bottom || isMouseInLastColumn;
35955
+ const isMouseInLastRow = lastRow ? relativeY >= lastRow.start && relativeY <= lastRow.start + lastRow.size && relativeX >= visibleBounds.left && relativeX <= visibleBounds.right : false;
35956
+ const addRowVisible = Boolean(directAddRow) || relativeY >= rowRailTop && relativeY <= rowRailTop + ADD_ROW_HOVER_HEIGHT && relativeX >= visibleBounds.left && relativeX <= visibleBounds.right || isMouseInLastRow;
35077
35957
  return {
35078
35958
  menuVisible,
35079
35959
  addColumnVisible,
@@ -35195,12 +36075,11 @@ function TableAddRails({
35195
36075
  quickAddColumnLabel,
35196
36076
  quickAddRowLabel
35197
36077
  }) {
35198
- const visibleTableWidth = Math.min(layout.tableWidth, layout.viewportWidth);
35199
- const visibleTableHeight = Math.min(layout.tableHeight, layout.viewportHeight);
35200
- const columnRailTop = layout.tableTop;
35201
- const columnRailLeft = layout.tableLeft + visibleTableWidth + ADD_COLUMN_RAIL_GAP;
36078
+ const visibleBounds = getVisibleTableBounds(layout);
36079
+ const columnRailTop = visibleBounds.top;
36080
+ const columnRailLeft = visibleBounds.right + ADD_COLUMN_RAIL_GAP;
35202
36081
  const rowRailTop = layout.wrapperTop + layout.wrapperHeight + ADD_ROW_RAIL_GAP;
35203
- const rowRailLeft = Math.max(layout.tableLeft, layout.wrapperLeft);
36082
+ const rowRailLeft = visibleBounds.left;
35204
36083
  const showColumnRail = controlsVisible || addColumnVisible;
35205
36084
  const showRowRail = controlsVisible || addRowVisible;
35206
36085
  return /* @__PURE__ */ (0, import_jsx_runtime89.jsxs)(import_jsx_runtime89.Fragment, { children: [
@@ -35228,10 +36107,10 @@ function TableAddRails({
35228
36107
  "transition-[opacity,transform,colors] duration-150 hover:bg-accent hover:text-foreground disabled:opacity-50 disabled:cursor-not-allowed"
35229
36108
  ),
35230
36109
  style: {
35231
- top: showColumnRail ? columnRailTop : columnRailTop + Math.max(0, visibleTableHeight / 2 - 24),
36110
+ top: showColumnRail ? columnRailTop : columnRailTop + Math.max(0, visibleBounds.height / 2 - 24),
35232
36111
  left: columnRailLeft,
35233
36112
  width: showColumnRail ? 18 : 12,
35234
- height: showColumnRail ? visibleTableHeight : 48,
36113
+ height: showColumnRail ? visibleBounds.height : 48,
35235
36114
  opacity: showColumnRail ? 1 : 0,
35236
36115
  transform: showColumnRail ? "scale(1)" : "scale(0.92)",
35237
36116
  pointerEvents: showColumnRail ? "auto" : "none"
@@ -35266,8 +36145,8 @@ function TableAddRails({
35266
36145
  ),
35267
36146
  style: {
35268
36147
  top: rowRailTop,
35269
- left: showRowRail ? rowRailLeft : rowRailLeft + Math.max(0, visibleTableWidth / 2 - 24),
35270
- width: showRowRail ? visibleTableWidth : 48,
36148
+ left: showRowRail ? rowRailLeft : rowRailLeft + Math.max(0, visibleBounds.width / 2 - 24),
36149
+ width: showRowRail ? visibleBounds.width : 48,
35271
36150
  height: showRowRail ? 16 : 12,
35272
36151
  opacity: showRowRail ? 1 : 0,
35273
36152
  transform: showRowRail ? "scale(1)" : "scale(0.92)",
@@ -35492,248 +36371,6 @@ function TableColumnHandles({
35492
36371
  }) });
35493
36372
  }
35494
36373
 
35495
- // src/components/UEditor/table-layout-model.ts
35496
- var FALLBACK_TABLE_ROW_HEIGHT = 44;
35497
- var FALLBACK_TABLE_COLUMN_WIDTH = 160;
35498
- function metricOrFallback(value, fallback) {
35499
- return Number.isFinite(value) && value > 0 ? value : fallback;
35500
- }
35501
- function parsePixelMetric(value) {
35502
- if (!value) return null;
35503
- const parsed = Number.parseFloat(value);
35504
- return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
35505
- }
35506
- function getPrimaryCell(table) {
35507
- const cell = table.querySelector("th,td");
35508
- return cell instanceof HTMLTableCellElement ? cell : null;
35509
- }
35510
- function getLastCell(table) {
35511
- const lastRow = table.rows.item(table.rows.length - 1);
35512
- if (!(lastRow instanceof HTMLTableRowElement)) return null;
35513
- const cell = lastRow.cells.item(lastRow.cells.length - 1);
35514
- return cell instanceof HTMLTableCellElement ? cell : null;
35515
- }
35516
- function getCellFromTarget(target) {
35517
- const element = resolveEventElement(target);
35518
- if (!element) return null;
35519
- const directCell = element.closest("th,td");
35520
- if (directCell instanceof HTMLTableCellElement) {
35521
- return directCell;
35522
- }
35523
- const table = element.closest("table");
35524
- if (table instanceof HTMLTableElement) {
35525
- return getPrimaryCell(table);
35526
- }
35527
- return null;
35528
- }
35529
- function findTableInfo(editor, pos) {
35530
- const $pos = editor.state.doc.resolve(pos);
35531
- for (let depth = $pos.depth; depth > 0; depth -= 1) {
35532
- const node = $pos.node(depth);
35533
- if (node.type.name === "table") {
35534
- return {
35535
- node,
35536
- pos: $pos.before(depth),
35537
- start: $pos.start(depth)
35538
- };
35539
- }
35540
- }
35541
- return null;
35542
- }
35543
- function getCellRelativePosFromDomPos(map, tableStart, domPos) {
35544
- const relativeDomPos = domPos - tableStart;
35545
- const seen = /* @__PURE__ */ new Set();
35546
- for (const relativeCellPos of map.map) {
35547
- if (seen.has(relativeCellPos)) continue;
35548
- seen.add(relativeCellPos);
35549
- if (relativeDomPos === relativeCellPos || relativeDomPos === relativeCellPos + 1) {
35550
- return relativeCellPos;
35551
- }
35552
- }
35553
- return null;
35554
- }
35555
- function buildLogicalColumnMetrics({
35556
- editor,
35557
- surface,
35558
- surfaceRect,
35559
- tableElement,
35560
- tableInfo,
35561
- tableLeft,
35562
- tableWidth
35563
- }) {
35564
- const map = TableMap2.get(tableInfo.node);
35565
- const fallbackWidth = metricOrFallback(tableWidth / map.width, FALLBACK_TABLE_COLUMN_WIDTH);
35566
- const firstRow = tableElement.rows.item(0);
35567
- const visualColumns = [];
35568
- if (firstRow) {
35569
- for (const tableCell of Array.from(firstRow.cells)) {
35570
- if (!(tableCell instanceof HTMLTableCellElement)) continue;
35571
- const cellPos = editor.view.posAtDOM(tableCell, 0);
35572
- const relativeCellPos = getCellRelativePosFromDomPos(map, tableInfo.start, cellPos);
35573
- if (relativeCellPos == null) continue;
35574
- const cellMapRect = map.findCell(relativeCellPos);
35575
- const cellRect = tableCell.getBoundingClientRect();
35576
- const cellStart = cellRect.width > 0 ? cellRect.left - surfaceRect.left + surface.scrollLeft : tableLeft + cellMapRect.left * fallbackWidth;
35577
- const size = metricOrFallback(cellRect.width, fallbackWidth * Math.max(1, cellMapRect.right - cellMapRect.left));
35578
- visualColumns.push({
35579
- index: cellMapRect.left,
35580
- cellPos: tableInfo.start + relativeCellPos,
35581
- start: cellStart,
35582
- size,
35583
- center: cellStart + size / 2
35584
- });
35585
- }
35586
- }
35587
- if (visualColumns.length > 0) {
35588
- return visualColumns.sort((a, b) => a.index - b.index);
35589
- }
35590
- const cols = Array.from(tableElement.querySelectorAll("colgroup > col"));
35591
- const parsedWidths = cols.slice(0, map.width).map((col) => parsePixelMetric(col.style.width) ?? parsePixelMetric(col.getAttribute("width")));
35592
- const hasCompleteColWidths = parsedWidths.length >= map.width && parsedWidths.every((width) => typeof width === "number");
35593
- let cursor = tableLeft;
35594
- return Array.from({ length: map.width }, (_, index) => {
35595
- const size = hasCompleteColWidths ? parsedWidths[index] : fallbackWidth;
35596
- const start = hasCompleteColWidths ? cursor : tableLeft + index * fallbackWidth;
35597
- cursor += size;
35598
- return {
35599
- index,
35600
- cellPos: tableInfo.start + map.positionAt(0, index, tableInfo.node),
35601
- start,
35602
- size,
35603
- center: start + size / 2
35604
- };
35605
- });
35606
- }
35607
- function buildLogicalRowMetrics({
35608
- editor,
35609
- surface,
35610
- surfaceRect,
35611
- tableInfo,
35612
- rows,
35613
- tableTop,
35614
- tableHeight,
35615
- cornerCell
35616
- }) {
35617
- const map = TableMap2.get(tableInfo.node);
35618
- const fallbackHeight = metricOrFallback(tableHeight / map.height, FALLBACK_TABLE_ROW_HEIGHT);
35619
- const visualRows = [];
35620
- const seenCellPositions = /* @__PURE__ */ new Set();
35621
- for (let rowIndex = 0; rowIndex < map.height; rowIndex += 1) {
35622
- const relativeCellPos = map.map[rowIndex * map.width];
35623
- if (seenCellPositions.has(relativeCellPos)) continue;
35624
- seenCellPositions.add(relativeCellPos);
35625
- const cellMapRect = map.findCell(relativeCellPos);
35626
- const cellDom = editor.view.nodeDOM(tableInfo.start + relativeCellPos);
35627
- const tableCell = cellDom instanceof HTMLTableCellElement ? cellDom : null;
35628
- if (tableCell) {
35629
- const cellRect = tableCell.getBoundingClientRect();
35630
- const start = cellRect.height > 0 ? cellRect.top - surfaceRect.top + surface.scrollTop : tableTop + cellMapRect.top * fallbackHeight;
35631
- const size = metricOrFallback(cellRect.height, fallbackHeight * Math.max(1, cellMapRect.bottom - cellMapRect.top));
35632
- visualRows.push({
35633
- index: cellMapRect.top,
35634
- cellPos: tableInfo.start + relativeCellPos,
35635
- start,
35636
- size,
35637
- center: start + size / 2
35638
- });
35639
- }
35640
- }
35641
- if (visualRows.length > 0) {
35642
- return visualRows.sort((a, b) => a.index - b.index);
35643
- }
35644
- return rows.map((tableRow, index) => {
35645
- const rowRect = tableRow.getBoundingClientRect();
35646
- const anchorCell = tableRow.cells.item(0) ?? cornerCell;
35647
- const start = rowRect.height > 0 ? rowRect.top - surfaceRect.top + surface.scrollTop : tableTop + index * fallbackHeight;
35648
- const size = metricOrFallback(rowRect.height, fallbackHeight);
35649
- return {
35650
- index,
35651
- cellPos: editor.view.posAtDOM(anchorCell, 0),
35652
- start,
35653
- size,
35654
- center: start + size / 2
35655
- };
35656
- });
35657
- }
35658
- function buildTableControlLayout(editor, surface, cell) {
35659
- const row = cell.closest("tr");
35660
- const table = cell.closest("table");
35661
- if (!(row instanceof HTMLTableRowElement) || !(table instanceof HTMLTableElement)) {
35662
- return null;
35663
- }
35664
- const rows = Array.from(table.rows).filter((item) => item instanceof HTMLTableRowElement);
35665
- const cornerCell = getLastCell(table);
35666
- const cellPos = editor.view.posAtDOM(cell, 0);
35667
- const tableInfo = findTableInfo(editor, cellPos);
35668
- if (rows.length === 0 || !tableInfo || !(cornerCell instanceof HTMLTableCellElement)) {
35669
- return null;
35670
- }
35671
- const map = TableMap2.get(tableInfo.node);
35672
- const surfaceRect = surface.getBoundingClientRect();
35673
- const tableRect = table.getBoundingClientRect();
35674
- const wrapperElement = table.closest(".tableWrapper");
35675
- const wrapper = wrapperElement instanceof HTMLElement ? wrapperElement : null;
35676
- const wrapperRect = wrapper?.getBoundingClientRect() ?? tableRect;
35677
- const tableLeft = tableRect.left - surfaceRect.left + surface.scrollLeft;
35678
- const tableTop = tableRect.top - surfaceRect.top + surface.scrollTop;
35679
- const avgRowHeight = metricOrFallback(tableRect.height / rows.length, FALLBACK_TABLE_ROW_HEIGHT);
35680
- const avgColumnWidth = metricOrFallback(tableRect.width / map.width, FALLBACK_TABLE_COLUMN_WIDTH);
35681
- const tableWidth = metricOrFallback(tableRect.width, avgColumnWidth * map.width);
35682
- const tableHeight = metricOrFallback(tableRect.height, avgRowHeight * rows.length);
35683
- const wrapperLeft = wrapperRect.left - surfaceRect.left + surface.scrollLeft;
35684
- const wrapperTop = wrapperRect.top - surfaceRect.top + surface.scrollTop;
35685
- const wrapperWidth = metricOrFallback(wrapperRect.width, tableWidth);
35686
- const wrapperHeight = metricOrFallback(wrapperRect.height, tableHeight);
35687
- const viewportWidth = metricOrFallback(wrapper?.clientWidth ?? wrapperRect.width, tableWidth);
35688
- const viewportHeight = metricOrFallback(wrapper?.clientHeight ?? wrapperRect.height, tableHeight);
35689
- const verticalScrollbarWidth = Math.max(0, Math.round(wrapperWidth - viewportWidth));
35690
- const horizontalScrollbarHeight = Math.max(0, Math.round(wrapperHeight - viewportHeight));
35691
- const rowHandles = buildLogicalRowMetrics({
35692
- editor,
35693
- surface,
35694
- surfaceRect,
35695
- tableInfo,
35696
- rows,
35697
- tableTop,
35698
- tableHeight,
35699
- cornerCell
35700
- });
35701
- const columnHandles = buildLogicalColumnMetrics({
35702
- editor,
35703
- surface,
35704
- surfaceRect,
35705
- tableElement: table,
35706
- tableInfo,
35707
- tableLeft,
35708
- tableWidth
35709
- });
35710
- const activeCellRelativePos = getCellRelativePosFromDomPos(map, tableInfo.start, cellPos);
35711
- const activeCellRect = activeCellRelativePos != null ? map.findCell(activeCellRelativePos) : { left: cell.cellIndex, top: row.rowIndex };
35712
- const normalizedCellPos = activeCellRelativePos != null ? tableInfo.start + activeCellRelativePos : cellPos;
35713
- return {
35714
- cellPos: normalizedCellPos,
35715
- cornerCellPos: tableInfo.start + map.positionAt(map.height - 1, map.width - 1, tableInfo.node),
35716
- activeRowIndex: activeCellRect.top,
35717
- activeColumnIndex: activeCellRect.left,
35718
- tableLeft,
35719
- tableTop,
35720
- tableWidth,
35721
- tableHeight,
35722
- wrapperLeft,
35723
- wrapperTop,
35724
- wrapperWidth,
35725
- wrapperHeight,
35726
- viewportWidth,
35727
- viewportHeight,
35728
- horizontalScrollbarHeight,
35729
- verticalScrollbarWidth,
35730
- avgRowHeight,
35731
- avgColumnWidth,
35732
- rowHandles,
35733
- columnHandles
35734
- };
35735
- }
35736
-
35737
36374
  // src/components/UEditor/table-controls.tsx
35738
36375
  var import_jsx_runtime92 = require("react/jsx-runtime");
35739
36376
  var TABLE_MENU_TOP_OFFSET = 10;