@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.js CHANGED
@@ -843,6 +843,10 @@ var en_default = {
843
843
  borderWidth: "Border Width",
844
844
  borderColor: "Border Color",
845
845
  clearBorder: "Clear Border",
846
+ formula: "Formula",
847
+ apply: "Apply",
848
+ clear: "Clear",
849
+ recalculate: "Recalculate",
846
850
  done: "Done"
847
851
  },
848
852
  callout: {
@@ -1219,6 +1223,10 @@ var vi_default = {
1219
1223
  borderWidth: "\u0110\u1ED9 d\xE0y vi\u1EC1n",
1220
1224
  borderColor: "M\xE0u vi\u1EC1n",
1221
1225
  clearBorder: "X\xF3a vi\u1EC1n",
1226
+ formula: "C\xF4ng th\u1EE9c",
1227
+ apply: "\xC1p d\u1EE5ng",
1228
+ clear: "X\xF3a",
1229
+ recalculate: "T\xEDnh l\u1EA1i",
1222
1230
  done: "Xong"
1223
1231
  },
1224
1232
  callout: {
@@ -27657,6 +27665,46 @@ var CustomTableCell = TableCell2.extend({
27657
27665
  "data-border-width": attributes.borderWidth
27658
27666
  };
27659
27667
  }
27668
+ },
27669
+ cellId: {
27670
+ default: null,
27671
+ parseHTML: (element) => element.getAttribute("data-cell-id") || null,
27672
+ renderHTML: (attributes) => {
27673
+ if (!attributes.cellId) return {};
27674
+ return {
27675
+ "data-cell-id": attributes.cellId
27676
+ };
27677
+ }
27678
+ },
27679
+ numberFormat: {
27680
+ default: null,
27681
+ parseHTML: (element) => element.getAttribute("data-number-format") || null,
27682
+ renderHTML: (attributes) => {
27683
+ if (!attributes.numberFormat) return {};
27684
+ return {
27685
+ "data-number-format": attributes.numberFormat
27686
+ };
27687
+ }
27688
+ },
27689
+ formula: {
27690
+ default: null,
27691
+ parseHTML: (element) => element.getAttribute("data-formula") || null,
27692
+ renderHTML: (attributes) => {
27693
+ if (!attributes.formula) return {};
27694
+ return {
27695
+ "data-formula": attributes.formula
27696
+ };
27697
+ }
27698
+ },
27699
+ computedValue: {
27700
+ default: null,
27701
+ parseHTML: (element) => element.getAttribute("data-computed-value") || null,
27702
+ renderHTML: (attributes) => {
27703
+ if (!attributes.computedValue) return {};
27704
+ return {
27705
+ "data-computed-value": attributes.computedValue
27706
+ };
27707
+ }
27660
27708
  }
27661
27709
  };
27662
27710
  },
@@ -27724,6 +27772,46 @@ var CustomTableHeader = TableHeader2.extend({
27724
27772
  "data-border-width": attributes.borderWidth
27725
27773
  };
27726
27774
  }
27775
+ },
27776
+ cellId: {
27777
+ default: null,
27778
+ parseHTML: (element) => element.getAttribute("data-cell-id") || null,
27779
+ renderHTML: (attributes) => {
27780
+ if (!attributes.cellId) return {};
27781
+ return {
27782
+ "data-cell-id": attributes.cellId
27783
+ };
27784
+ }
27785
+ },
27786
+ numberFormat: {
27787
+ default: null,
27788
+ parseHTML: (element) => element.getAttribute("data-number-format") || null,
27789
+ renderHTML: (attributes) => {
27790
+ if (!attributes.numberFormat) return {};
27791
+ return {
27792
+ "data-number-format": attributes.numberFormat
27793
+ };
27794
+ }
27795
+ },
27796
+ formula: {
27797
+ default: null,
27798
+ parseHTML: (element) => element.getAttribute("data-formula") || null,
27799
+ renderHTML: (attributes) => {
27800
+ if (!attributes.formula) return {};
27801
+ return {
27802
+ "data-formula": attributes.formula
27803
+ };
27804
+ }
27805
+ },
27806
+ computedValue: {
27807
+ default: null,
27808
+ parseHTML: (element) => element.getAttribute("data-computed-value") || null,
27809
+ renderHTML: (attributes) => {
27810
+ if (!attributes.computedValue) return {};
27811
+ return {
27812
+ "data-computed-value": attributes.computedValue
27813
+ };
27814
+ }
27727
27815
  }
27728
27816
  };
27729
27817
  },
@@ -28474,6 +28562,34 @@ function collectChildren(node) {
28474
28562
  function createEmptyCellNode(cellNode) {
28475
28563
  return cellNode.type.createAndFill(cellNode.attrs) ?? cellNode;
28476
28564
  }
28565
+ function createCellCopyForColumnDuplicate(cellNode) {
28566
+ return cellNode.type.create(cellNode.attrs, cellNode.content);
28567
+ }
28568
+ function getTableRows(tableNode) {
28569
+ const rows = [];
28570
+ tableNode.forEach((rowNode, rowOffset) => {
28571
+ const cells = [];
28572
+ rowNode.forEach((cellNode, cellOffset, index) => {
28573
+ cells.push({
28574
+ index,
28575
+ node: cellNode,
28576
+ relativePos: rowOffset + 1 + cellOffset
28577
+ });
28578
+ });
28579
+ rows.push({
28580
+ node: rowNode,
28581
+ cells
28582
+ });
28583
+ });
28584
+ return rows;
28585
+ }
28586
+ function safeFindCell(map, relativePos) {
28587
+ try {
28588
+ return map.findCell(relativePos);
28589
+ } catch {
28590
+ return null;
28591
+ }
28592
+ }
28477
28593
  function getSelectedTableRect(editor) {
28478
28594
  const cellSelection = getCellSelectionPositions(editor.state.selection);
28479
28595
  if (cellSelection) {
@@ -28599,34 +28715,49 @@ function duplicateTableRowAt(editor, rowIndex, cellPos) {
28599
28715
  }
28600
28716
  function clearTableRowAt(editor, rowIndex, cellPos) {
28601
28717
  return replaceTableAtCellPos(editor, cellPos, (tableNode) => {
28602
- const rows = collectChildren(tableNode);
28603
- const rowNode = rows[rowIndex];
28604
- if (!rowNode) return null;
28605
- const cells = collectChildren(rowNode).map((cellNode) => createEmptyCellNode(cellNode));
28606
- rows[rowIndex] = rowNode.type.create(rowNode.attrs, cells);
28718
+ const map = TableMap.get(tableNode);
28719
+ if (rowIndex < 0 || rowIndex >= map.height) return null;
28720
+ const rows = getTableRows(tableNode).map((rowInfo) => {
28721
+ const cells = collectChildren(rowInfo.node);
28722
+ for (const entry of rowInfo.cells) {
28723
+ const rect = safeFindCell(map, entry.relativePos);
28724
+ if (!rect || rect.top > rowIndex || rowIndex >= rect.bottom) continue;
28725
+ cells[entry.index] = createEmptyCellNode(entry.node);
28726
+ }
28727
+ return rowInfo.node.type.create(rowInfo.node.attrs, cells);
28728
+ });
28607
28729
  return tableNode.type.create(tableNode.attrs, rows);
28608
28730
  });
28609
28731
  }
28610
28732
  function duplicateTableColumnAt(editor, columnIndex, cellPos) {
28611
28733
  return replaceTableAtCellPos(editor, cellPos, (tableNode) => {
28612
- const rows = collectChildren(tableNode).map((rowNode) => {
28613
- const cells = collectChildren(rowNode);
28614
- const cellNode = cells[columnIndex];
28615
- if (!cellNode) return rowNode;
28616
- cells.splice(columnIndex + 1, 0, cellNode.copy(cellNode.content));
28617
- return rowNode.type.create(rowNode.attrs, cells);
28734
+ const map = TableMap.get(tableNode);
28735
+ if (columnIndex < 0 || columnIndex >= map.width) return null;
28736
+ const rows = getTableRows(tableNode).map((rowInfo, rowIndex) => {
28737
+ const cells = collectChildren(rowInfo.node);
28738
+ const sourceCell = rowInfo.cells.find((entry) => {
28739
+ const rect = safeFindCell(map, entry.relativePos);
28740
+ return rect && rect.top === rowIndex && rect.left <= columnIndex && columnIndex < rect.right;
28741
+ });
28742
+ if (!sourceCell) return rowInfo.node;
28743
+ cells.splice(sourceCell.index + 1, 0, createCellCopyForColumnDuplicate(sourceCell.node));
28744
+ return rowInfo.node.type.create(rowInfo.node.attrs, cells);
28618
28745
  });
28619
28746
  return tableNode.type.create(tableNode.attrs, rows);
28620
28747
  });
28621
28748
  }
28622
28749
  function clearTableColumnAt(editor, columnIndex, cellPos) {
28623
28750
  return replaceTableAtCellPos(editor, cellPos, (tableNode) => {
28624
- const rows = collectChildren(tableNode).map((rowNode) => {
28625
- const cells = collectChildren(rowNode);
28626
- const cellNode = cells[columnIndex];
28627
- if (!cellNode) return rowNode;
28628
- cells[columnIndex] = createEmptyCellNode(cellNode);
28629
- return rowNode.type.create(rowNode.attrs, cells);
28751
+ const map = TableMap.get(tableNode);
28752
+ if (columnIndex < 0 || columnIndex >= map.width) return null;
28753
+ const rows = getTableRows(tableNode).map((rowInfo) => {
28754
+ const cells = collectChildren(rowInfo.node);
28755
+ for (const entry of rowInfo.cells) {
28756
+ const rect = safeFindCell(map, entry.relativePos);
28757
+ if (!rect || rect.left > columnIndex || columnIndex >= rect.right) continue;
28758
+ cells[entry.index] = createEmptyCellNode(entry.node);
28759
+ }
28760
+ return rowInfo.node.type.create(rowInfo.node.attrs, cells);
28630
28761
  });
28631
28762
  return tableNode.type.create(tableNode.attrs, rows);
28632
28763
  });
@@ -29678,7 +29809,7 @@ var EditorToolbar = ({
29678
29809
  // src/components/UEditor/menus.tsx
29679
29810
  import { useCallback as useCallback22, useEffect as useEffect36, useMemo as useMemo23, useRef as useRef33, useState as useState48 } from "react";
29680
29811
  import { useEditorState as useEditorState2 } from "@tiptap/react";
29681
- import { isInTable as isSelectionInTable, setCellAttr } from "@tiptap/pm/tables";
29812
+ import { isInTable as isSelectionInTable, setCellAttr as setCellAttr2 } from "@tiptap/pm/tables";
29682
29813
  import { createPortal as createPortal8 } from "react-dom";
29683
29814
  import {
29684
29815
  AlignCenter as AlignCenter2,
@@ -29700,13 +29831,410 @@ import {
29700
29831
  Strikethrough as StrikethroughIcon2,
29701
29832
  Edit2,
29702
29833
  Unlink,
29703
- ExternalLink as ExternalLink3
29834
+ ExternalLink as ExternalLink3,
29835
+ Sigma
29704
29836
  } from "lucide-react";
29837
+
29838
+ // src/components/UEditor/table-formula-commands.ts
29839
+ import { selectedRect as selectedRect2, setCellAttr, TableMap as TableMap2 } from "@tiptap/pm/tables";
29840
+
29841
+ // src/components/UEditor/table-formula.ts
29842
+ var CELL_ADDRESS_RE = /^([A-Z]+)([1-9]\d*)$/i;
29843
+ var CELL_RANGE_RE = /^([A-Z]+[1-9]\d*):([A-Z]+[1-9]\d*)$/i;
29844
+ var SUPPORTED_FUNCTIONS = /* @__PURE__ */ new Set(["SUM", "AVG", "MIN", "MAX", "COUNT"]);
29845
+ function columnNameToIndex(columnName) {
29846
+ const normalized = columnName.trim().toUpperCase();
29847
+ if (!/^[A-Z]+$/.test(normalized)) {
29848
+ return -1;
29849
+ }
29850
+ let index = 0;
29851
+ for (const char of normalized) {
29852
+ index = index * 26 + char.charCodeAt(0) - 64;
29853
+ }
29854
+ return index - 1;
29855
+ }
29856
+ function indexToColumnName(index) {
29857
+ if (!Number.isInteger(index) || index < 0) {
29858
+ return "";
29859
+ }
29860
+ let value = index + 1;
29861
+ let name = "";
29862
+ while (value > 0) {
29863
+ const remainder = (value - 1) % 26;
29864
+ name = String.fromCharCode(65 + remainder) + name;
29865
+ value = Math.floor((value - 1) / 26);
29866
+ }
29867
+ return name;
29868
+ }
29869
+ function parseTableCellAddress(input) {
29870
+ const match = input.trim().match(CELL_ADDRESS_RE);
29871
+ if (!match) {
29872
+ return null;
29873
+ }
29874
+ const column = columnNameToIndex(match[1] ?? "");
29875
+ const row = Number.parseInt(match[2] ?? "", 10) - 1;
29876
+ if (column < 0 || row < 0) {
29877
+ return null;
29878
+ }
29879
+ return {
29880
+ column,
29881
+ row,
29882
+ label: `${indexToColumnName(column)}${row + 1}`
29883
+ };
29884
+ }
29885
+ function parseTableCellRange(input) {
29886
+ const match = input.trim().match(CELL_RANGE_RE);
29887
+ if (!match) {
29888
+ return null;
29889
+ }
29890
+ const from = parseTableCellAddress(match[1] ?? "");
29891
+ const to = parseTableCellAddress(match[2] ?? "");
29892
+ if (!from || !to) {
29893
+ return null;
29894
+ }
29895
+ return { from, to };
29896
+ }
29897
+ function getTableCellRangeLabels(range) {
29898
+ const startColumn = Math.min(range.from.column, range.to.column);
29899
+ const endColumn = Math.max(range.from.column, range.to.column);
29900
+ const startRow = Math.min(range.from.row, range.to.row);
29901
+ const endRow = Math.max(range.from.row, range.to.row);
29902
+ const labels = [];
29903
+ for (let row = startRow; row <= endRow; row += 1) {
29904
+ for (let column = startColumn; column <= endColumn; column += 1) {
29905
+ labels.push(`${indexToColumnName(column)}${row + 1}`);
29906
+ }
29907
+ }
29908
+ return labels;
29909
+ }
29910
+ function normalizeTableFormula(formula) {
29911
+ return formula.trim().replace(/^=/, "").trim();
29912
+ }
29913
+ function evaluateBasicTableFormula(formula, getCellValue) {
29914
+ const normalized = normalizeTableFormula(formula);
29915
+ if (!normalized) {
29916
+ return { value: null, error: "empty" };
29917
+ }
29918
+ const tokens = tokenizeFormula(normalized);
29919
+ if (!tokens) {
29920
+ return { value: null, error: "invalid-formula" };
29921
+ }
29922
+ const parser = new FormulaParser(tokens, getCellValue);
29923
+ const result = parser.parseExpression();
29924
+ if (result.error) {
29925
+ return result;
29926
+ }
29927
+ if (!parser.isComplete()) {
29928
+ return { value: null, error: "invalid-formula" };
29929
+ }
29930
+ return result;
29931
+ }
29932
+ function tokenizeFormula(formula) {
29933
+ const tokens = [];
29934
+ let index = 0;
29935
+ while (index < formula.length) {
29936
+ const char = formula[index];
29937
+ if (!char) break;
29938
+ if (/\s/.test(char)) {
29939
+ index += 1;
29940
+ continue;
29941
+ }
29942
+ if (char === "," || char === "+" || char === "-" || char === "*" || char === "/" || char === "(" || char === ")") {
29943
+ if (char === ",") tokens.push({ type: "comma", value: char });
29944
+ else if (char === "(" || char === ")") tokens.push({ type: "paren", value: char });
29945
+ else tokens.push({ type: "operator", value: char });
29946
+ index += 1;
29947
+ continue;
29948
+ }
29949
+ const numberMatch = formula.slice(index).match(/^\d+(?:\.\d+)?/);
29950
+ if (numberMatch?.[0]) {
29951
+ tokens.push({ type: "number", value: Number.parseFloat(numberMatch[0]) });
29952
+ index += numberMatch[0].length;
29953
+ continue;
29954
+ }
29955
+ const identifierMatch = formula.slice(index).match(/^[A-Z]+[1-9]\d*(?::[A-Z]+[1-9]\d*)?|^[A-Z]+/i);
29956
+ if (identifierMatch?.[0]) {
29957
+ const value = identifierMatch[0].toUpperCase();
29958
+ if (CELL_RANGE_RE.test(value)) tokens.push({ type: "range", value });
29959
+ else if (parseTableCellAddress(value)) tokens.push({ type: "cell", value });
29960
+ else if (SUPPORTED_FUNCTIONS.has(value)) tokens.push({ type: "function", value });
29961
+ else return null;
29962
+ index += identifierMatch[0].length;
29963
+ continue;
29964
+ }
29965
+ return null;
29966
+ }
29967
+ return tokens;
29968
+ }
29969
+ var FormulaParser = class {
29970
+ constructor(tokens, getCellValue) {
29971
+ this.tokens = tokens;
29972
+ this.getCellValue = getCellValue;
29973
+ this.index = 0;
29974
+ }
29975
+ isComplete() {
29976
+ return this.index >= this.tokens.length;
29977
+ }
29978
+ parseExpression() {
29979
+ let left = this.parseTerm();
29980
+ while (!left.error) {
29981
+ const operator = this.peekOperator(["+", "-"]);
29982
+ if (!operator) break;
29983
+ this.index += 1;
29984
+ const right = this.parseTerm();
29985
+ if (right.error) return right;
29986
+ left = {
29987
+ value: operator.value === "+" ? left.value + right.value : left.value - right.value,
29988
+ error: null
29989
+ };
29990
+ }
29991
+ return left;
29992
+ }
29993
+ parseTerm() {
29994
+ let left = this.parseFactor();
29995
+ while (!left.error) {
29996
+ const operator = this.peekOperator(["*", "/"]);
29997
+ if (!operator) break;
29998
+ this.index += 1;
29999
+ const right = this.parseFactor();
30000
+ if (right.error) return right;
30001
+ if (operator.value === "/" && right.value === 0) {
30002
+ return { value: null, error: "division-by-zero" };
30003
+ }
30004
+ left = {
30005
+ value: operator.value === "*" ? left.value * right.value : left.value / right.value,
30006
+ error: null
30007
+ };
30008
+ }
30009
+ return left;
30010
+ }
30011
+ parseFactor() {
30012
+ const token = this.tokens[this.index];
30013
+ if (!token) {
30014
+ return { value: null, error: "invalid-formula" };
30015
+ }
30016
+ if (token.type === "operator" && token.value === "-") {
30017
+ this.index += 1;
30018
+ const value = this.parseFactor();
30019
+ if (value.error) return value;
30020
+ return { value: -value.value, error: null };
30021
+ }
30022
+ if (token.type === "number") {
30023
+ this.index += 1;
30024
+ return { value: token.value, error: null };
30025
+ }
30026
+ if (token.type === "cell") {
30027
+ this.index += 1;
30028
+ return this.readCellNumber(token.value);
30029
+ }
30030
+ if (token.type === "function") {
30031
+ return this.parseFunction(token.value);
30032
+ }
30033
+ if (token.type === "paren" && token.value === "(") {
30034
+ this.index += 1;
30035
+ const value = this.parseExpression();
30036
+ if (value.error) return value;
30037
+ if (!this.consumeParen(")")) {
30038
+ return { value: null, error: "invalid-formula" };
30039
+ }
30040
+ return value;
30041
+ }
30042
+ return { value: null, error: "invalid-formula" };
30043
+ }
30044
+ parseFunction(name) {
30045
+ this.index += 1;
30046
+ if (!this.consumeParen("(")) {
30047
+ return { value: null, error: "invalid-formula" };
30048
+ }
30049
+ const values = [];
30050
+ while (true) {
30051
+ const token = this.tokens[this.index];
30052
+ if (!token) {
30053
+ return { value: null, error: "invalid-formula" };
30054
+ }
30055
+ if (token.type === "range") {
30056
+ this.index += 1;
30057
+ const range = parseTableCellRange(token.value);
30058
+ if (!range) return { value: null, error: "invalid-reference" };
30059
+ for (const label of getTableCellRangeLabels(range)) {
30060
+ const cellValue = this.readCellNumber(label);
30061
+ if (cellValue.error) return cellValue;
30062
+ values.push(cellValue.value);
30063
+ }
30064
+ } else {
30065
+ const value = this.parseExpression();
30066
+ if (value.error) return value;
30067
+ values.push(value.value);
30068
+ }
30069
+ if (this.consumeComma()) {
30070
+ continue;
30071
+ }
30072
+ if (this.consumeParen(")")) {
30073
+ break;
30074
+ }
30075
+ return { value: null, error: "invalid-formula" };
30076
+ }
30077
+ if (values.length === 0) {
30078
+ return { value: null, error: "invalid-formula" };
30079
+ }
30080
+ if (name === "SUM") return { value: values.reduce((sum, value) => sum + value, 0), error: null };
30081
+ if (name === "AVG") return { value: values.reduce((sum, value) => sum + value, 0) / values.length, error: null };
30082
+ if (name === "MIN") return { value: Math.min(...values), error: null };
30083
+ if (name === "MAX") return { value: Math.max(...values), error: null };
30084
+ if (name === "COUNT") return { value: values.length, error: null };
30085
+ return { value: null, error: "invalid-formula" };
30086
+ }
30087
+ readCellNumber(label) {
30088
+ const value = this.getCellValue(label);
30089
+ const parsed = typeof value === "number" ? value : Number.parseFloat(String(value ?? "").trim());
30090
+ if (!Number.isFinite(parsed)) {
30091
+ return { value: null, error: "invalid-reference" };
30092
+ }
30093
+ return { value: parsed, error: null };
30094
+ }
30095
+ peekOperator(operators) {
30096
+ const token = this.tokens[this.index];
30097
+ return token?.type === "operator" && operators.includes(token.value) ? token : null;
30098
+ }
30099
+ consumeComma() {
30100
+ if (this.tokens[this.index]?.type !== "comma") {
30101
+ return false;
30102
+ }
30103
+ this.index += 1;
30104
+ return true;
30105
+ }
30106
+ consumeParen(value) {
30107
+ const token = this.tokens[this.index];
30108
+ if (token?.type !== "paren" || token.value !== value) {
30109
+ return false;
30110
+ }
30111
+ this.index += 1;
30112
+ return true;
30113
+ }
30114
+ };
30115
+
30116
+ // src/components/UEditor/table-formula-commands.ts
30117
+ function collectChildren2(node) {
30118
+ const children = [];
30119
+ node.forEach((child) => children.push(child));
30120
+ return children;
30121
+ }
30122
+ function getTableRows2(tableNode) {
30123
+ const rows = [];
30124
+ tableNode.forEach((rowNode, rowOffset) => {
30125
+ const cells = [];
30126
+ rowNode.forEach((cellNode, cellOffset, index) => {
30127
+ cells.push({
30128
+ index,
30129
+ node: cellNode,
30130
+ relativePos: rowOffset + 1 + cellOffset
30131
+ });
30132
+ });
30133
+ rows.push({ node: rowNode, cells });
30134
+ });
30135
+ return rows;
30136
+ }
30137
+ function safeFindCell2(map, relativePos) {
30138
+ try {
30139
+ return map.findCell(relativePos);
30140
+ } catch {
30141
+ return null;
30142
+ }
30143
+ }
30144
+ function getCellText(cellNode) {
30145
+ return cellNode.textBetween(0, cellNode.content.size, "\n").trim();
30146
+ }
30147
+ function buildTableValueGetter(tableNode) {
30148
+ const map = TableMap2.get(tableNode);
30149
+ const values = /* @__PURE__ */ new Map();
30150
+ for (const rowInfo of getTableRows2(tableNode)) {
30151
+ for (const entry of rowInfo.cells) {
30152
+ const rect = safeFindCell2(map, entry.relativePos);
30153
+ if (!rect) continue;
30154
+ const label = `${indexToColumnName(rect.left)}${rect.top + 1}`;
30155
+ const computedValue = entry.node.attrs.computedValue;
30156
+ values.set(label, typeof computedValue === "string" && computedValue.trim() ? computedValue : getCellText(entry.node));
30157
+ }
30158
+ }
30159
+ return (label) => values.get(label.toUpperCase());
30160
+ }
30161
+ function getFormulaComputedValue(formula, tableNode) {
30162
+ const result = evaluateBasicTableFormula(formula, buildTableValueGetter(tableNode));
30163
+ return result.error ? `#${result.error.toUpperCase()}` : String(result.value);
30164
+ }
30165
+ function normalizeFormulaInput(formula) {
30166
+ const trimmed = formula.trim();
30167
+ if (!trimmed) return "";
30168
+ return trimmed.startsWith("=") ? trimmed : `=${trimmed}`;
30169
+ }
30170
+ function setSelectedTableCellFormula(editor, formula) {
30171
+ const normalized = normalizeFormulaInput(formula);
30172
+ const { state, view } = editor;
30173
+ if (!normalized) {
30174
+ const clearedFormula = setCellAttr("formula", null)(state, view.dispatch.bind(view));
30175
+ const clearedValue = setCellAttr("computedValue", null)(editor.state, view.dispatch.bind(view));
30176
+ if (clearedFormula || clearedValue) {
30177
+ view.focus();
30178
+ dispatchTableLayoutChange(editor);
30179
+ return true;
30180
+ }
30181
+ return false;
30182
+ }
30183
+ const rect = selectedRect2(state);
30184
+ const computedValue = getFormulaComputedValue(normalized, rect.table);
30185
+ const appliedFormula = setCellAttr("formula", normalized)(state, view.dispatch.bind(view));
30186
+ const appliedValue = setCellAttr("computedValue", computedValue)(editor.state, view.dispatch.bind(view));
30187
+ if (appliedFormula || appliedValue) {
30188
+ view.focus();
30189
+ dispatchTableLayoutChange(editor);
30190
+ return true;
30191
+ }
30192
+ return false;
30193
+ }
30194
+ function clearSelectedTableCellFormula(editor) {
30195
+ return setSelectedTableCellFormula(editor, "");
30196
+ }
30197
+ function recalculateSelectedTable(editor) {
30198
+ const rect = selectedRect2(editor.state);
30199
+ const tableNode = rect.table;
30200
+ const map = TableMap2.get(tableNode);
30201
+ const getCellValue = buildTableValueGetter(tableNode);
30202
+ let changed = false;
30203
+ const rows = getTableRows2(tableNode).map((rowInfo) => {
30204
+ const cells = collectChildren2(rowInfo.node);
30205
+ for (const entry of rowInfo.cells) {
30206
+ const formula = typeof entry.node.attrs.formula === "string" ? entry.node.attrs.formula.trim() : "";
30207
+ if (!formula) continue;
30208
+ const rectForCell = safeFindCell2(map, entry.relativePos);
30209
+ if (!rectForCell) continue;
30210
+ const result = evaluateBasicTableFormula(formula, getCellValue);
30211
+ const computedValue = result.error ? `#${result.error.toUpperCase()}` : String(result.value);
30212
+ if (entry.node.attrs.computedValue === computedValue) continue;
30213
+ cells[entry.index] = entry.node.type.create(
30214
+ {
30215
+ ...entry.node.attrs,
30216
+ computedValue
30217
+ },
30218
+ entry.node.content,
30219
+ entry.node.marks
30220
+ );
30221
+ changed = true;
30222
+ }
30223
+ return rowInfo.node.type.create(rowInfo.node.attrs, cells);
30224
+ });
30225
+ if (!changed) return false;
30226
+ const nextTable = tableNode.type.create(tableNode.attrs, rows);
30227
+ editor.view.dispatch(editor.state.tr.replaceWith(rect.tableStart - 1, rect.tableStart - 1 + tableNode.nodeSize, nextTable));
30228
+ dispatchTableLayoutChange(editor);
30229
+ return true;
30230
+ }
30231
+
30232
+ // src/components/UEditor/menus.tsx
29705
30233
  import { Fragment as Fragment29, jsx as jsx86, jsxs as jsxs72 } from "react/jsx-runtime";
29706
30234
  function applyTableCellBackground(editor, color) {
29707
30235
  const value = color || null;
29708
30236
  const { state, view } = editor;
29709
- const applied = setCellAttr("backgroundColor", value)(state, view.dispatch.bind(view));
30237
+ const applied = setCellAttr2("backgroundColor", value)(state, view.dispatch.bind(view));
29710
30238
  if (applied) {
29711
30239
  view.focus();
29712
30240
  return;
@@ -29732,6 +30260,8 @@ var BubbleMenuContent = ({
29732
30260
  setShowLinkInput(initialShowLinkInput);
29733
30261
  }, [initialShowLinkInput]);
29734
30262
  const [showTypographyPanel, setShowTypographyPanel] = useState48(false);
30263
+ const [showFormulaPanel, setShowFormulaPanel] = useState48(false);
30264
+ const [formulaDraft, setFormulaDraft] = useState48("");
29735
30265
  const [showFontSizeOptions, setShowFontSizeOptions] = useState48(false);
29736
30266
  const [fontSizeDraft, setFontSizeDraft] = useState48("");
29737
30267
  const isImageSelected = editor.isActive("image");
@@ -29742,6 +30272,7 @@ var BubbleMenuContent = ({
29742
30272
  const currentTextColor = normalizeStyleValue(textStyleAttrs.color) || "inherit";
29743
30273
  const currentHighlightColor = normalizeStyleValue(editor.getAttributes("highlight").color) || "";
29744
30274
  const currentCellBgColor = normalizeStyleValue(editor.getAttributes("tableCell").backgroundColor || editor.getAttributes("tableHeader").backgroundColor) || "";
30275
+ const currentCellFormula = normalizeStyleValue(editor.getAttributes("tableCell").formula || editor.getAttributes("tableHeader").formula) || "";
29745
30276
  const isInTable2 = isSelectionInTable(editor.state);
29746
30277
  const canMergeCells = isInTable2 && editor.can().mergeCells();
29747
30278
  const canSplitCell = isInTable2 && editor.can().splitCell();
@@ -29758,6 +30289,9 @@ var BubbleMenuContent = ({
29758
30289
  useEffect36(() => {
29759
30290
  setFontSizeDraft(currentFontSize.replace(/px$/i, ""));
29760
30291
  }, [currentFontSize]);
30292
+ useEffect36(() => {
30293
+ setFormulaDraft(currentCellFormula);
30294
+ }, [currentCellFormula]);
29761
30295
  const applyFontSizeDraft = () => {
29762
30296
  const normalized = fontSizeDraft.trim();
29763
30297
  if (!normalized) {
@@ -29960,6 +30494,82 @@ var BubbleMenuContent = ({
29960
30494
  )
29961
30495
  ] });
29962
30496
  }
30497
+ if (showFormulaPanel && isInTable2) {
30498
+ return /* @__PURE__ */ jsxs72("div", { className: "w-72 p-2", children: [
30499
+ /* @__PURE__ */ jsxs72("div", { className: "mb-2 flex items-center justify-between gap-2", children: [
30500
+ /* @__PURE__ */ jsx86("span", { className: "px-1 text-[11px] font-semibold uppercase tracking-wide text-muted-foreground", children: t("tableMenu.formula") || "Formula" }),
30501
+ /* @__PURE__ */ jsx86(
30502
+ "button",
30503
+ {
30504
+ type: "button",
30505
+ onClick: () => {
30506
+ setShowFormulaPanel(false);
30507
+ onKeepOpenChange?.(false);
30508
+ },
30509
+ className: "rounded-md px-2 py-1 text-xs text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
30510
+ children: t("colors.done")
30511
+ }
30512
+ )
30513
+ ] }),
30514
+ /* @__PURE__ */ jsx86("div", { className: "flex h-9 items-center overflow-hidden rounded-md border border-border/60 bg-muted/40", children: /* @__PURE__ */ jsx86(
30515
+ "input",
30516
+ {
30517
+ value: formulaDraft,
30518
+ onChange: (event) => setFormulaDraft(event.target.value),
30519
+ onMouseDown: (event) => event.stopPropagation(),
30520
+ onClick: (event) => event.stopPropagation(),
30521
+ onKeyDown: (event) => {
30522
+ event.stopPropagation();
30523
+ if (event.key === "Enter") {
30524
+ event.preventDefault();
30525
+ setSelectedTableCellFormula(editor, formulaDraft);
30526
+ setShowFormulaPanel(false);
30527
+ onKeepOpenChange?.(false);
30528
+ }
30529
+ },
30530
+ "aria-label": t("tableMenu.formula") || "Formula",
30531
+ placeholder: "=SUM(A1:A3)",
30532
+ className: "h-full min-w-0 flex-1 bg-transparent px-2 text-sm font-medium text-foreground outline-none"
30533
+ }
30534
+ ) }),
30535
+ /* @__PURE__ */ jsxs72("div", { className: "mt-2 grid grid-cols-3 gap-1", children: [
30536
+ /* @__PURE__ */ jsx86(
30537
+ "button",
30538
+ {
30539
+ type: "button",
30540
+ onClick: () => {
30541
+ setSelectedTableCellFormula(editor, formulaDraft);
30542
+ setShowFormulaPanel(false);
30543
+ onKeepOpenChange?.(false);
30544
+ },
30545
+ className: "h-8 rounded-md bg-primary/10 text-xs font-semibold text-primary transition-colors hover:bg-primary/15",
30546
+ children: t("tableMenu.apply") || "Apply"
30547
+ }
30548
+ ),
30549
+ /* @__PURE__ */ jsx86(
30550
+ "button",
30551
+ {
30552
+ type: "button",
30553
+ onClick: () => {
30554
+ clearSelectedTableCellFormula(editor);
30555
+ setFormulaDraft("");
30556
+ },
30557
+ className: "h-8 rounded-md bg-muted/40 text-xs font-semibold text-foreground transition-colors hover:bg-muted",
30558
+ children: t("tableMenu.clear") || "Clear"
30559
+ }
30560
+ ),
30561
+ /* @__PURE__ */ jsx86(
30562
+ "button",
30563
+ {
30564
+ type: "button",
30565
+ onClick: () => recalculateSelectedTable(editor),
30566
+ className: "h-8 rounded-md bg-muted/40 text-xs font-semibold text-foreground transition-colors hover:bg-muted",
30567
+ children: t("tableMenu.recalculate") || "Recalc"
30568
+ }
30569
+ )
30570
+ ] })
30571
+ ] });
30572
+ }
29963
30573
  if (showTypographyPanel) {
29964
30574
  return /* @__PURE__ */ jsxs72("div", { className: "w-72 p-2", children: [
29965
30575
  /* @__PURE__ */ jsxs72("div", { className: "mb-2 flex items-center justify-between gap-2", children: [
@@ -30173,6 +30783,21 @@ var BubbleMenuContent = ({
30173
30783
  )
30174
30784
  }
30175
30785
  ),
30786
+ /* @__PURE__ */ jsx86(
30787
+ ToolbarButton,
30788
+ {
30789
+ onMouseDown: () => {
30790
+ onKeepOpenChange?.(true);
30791
+ },
30792
+ onClick: () => {
30793
+ setFormulaDraft(currentCellFormula);
30794
+ setShowFormulaPanel(true);
30795
+ },
30796
+ active: Boolean(currentCellFormula),
30797
+ title: t("tableMenu.formula") || "Formula",
30798
+ children: /* @__PURE__ */ jsx86(Sigma, { className: "w-4 h-4" })
30799
+ }
30800
+ ),
30176
30801
  /* @__PURE__ */ jsx86(
30177
30802
  ToolbarButton,
30178
30803
  {
@@ -34014,7 +34639,7 @@ if (typeof WeakMap != "undefined") {
34014
34639
  return cache[cachePos++] = value;
34015
34640
  };
34016
34641
  }
34017
- var TableMap2 = class {
34642
+ var TableMap3 = class {
34018
34643
  constructor(width, height, map, problems) {
34019
34644
  this.width = width;
34020
34645
  this.height = height;
@@ -34151,7 +34776,7 @@ function computeMap(table) {
34151
34776
  pos++;
34152
34777
  }
34153
34778
  if (width === 0 || height === 0) (problems || (problems = [])).push({ type: "zero_sized" });
34154
- const tableMap = new TableMap2(width, height, map, problems);
34779
+ const tableMap = new TableMap3(width, height, map, problems);
34155
34780
  let badWidths = false;
34156
34781
  for (let i = 0; !badWidths && i < colWidths.length; i += 2) if (colWidths[i] != null && colWidths[i + 1] < height) badWidths = true;
34157
34782
  if (badWidths) findBadColWidths(tableMap, colWidths, table);
@@ -34255,7 +34880,7 @@ function inSameTable($cellA, $cellB) {
34255
34880
  }
34256
34881
  function nextCell($pos, axis, dir) {
34257
34882
  const table = $pos.node(-1);
34258
- const map = TableMap2.get(table);
34883
+ const map = TableMap3.get(table);
34259
34884
  const tableStart = $pos.start(-1);
34260
34885
  const moved = map.nextCell($pos.pos - tableStart, axis, dir);
34261
34886
  return moved == null ? null : $pos.node(0).resolve(tableStart + moved);
@@ -34275,7 +34900,7 @@ function removeColSpan(attrs, pos, n = 1) {
34275
34900
  var CellSelection = class CellSelection2 extends Selection {
34276
34901
  constructor($anchorCell, $headCell = $anchorCell) {
34277
34902
  const table = $anchorCell.node(-1);
34278
- const map = TableMap2.get(table);
34903
+ const map = TableMap3.get(table);
34279
34904
  const tableStart = $anchorCell.start(-1);
34280
34905
  const rect = map.rectBetween($anchorCell.pos - tableStart, $headCell.pos - tableStart);
34281
34906
  const doc = $anchorCell.node(0);
@@ -34304,7 +34929,7 @@ var CellSelection = class CellSelection2 extends Selection {
34304
34929
  }
34305
34930
  content() {
34306
34931
  const table = this.$anchorCell.node(-1);
34307
- const map = TableMap2.get(table);
34932
+ const map = TableMap3.get(table);
34308
34933
  const tableStart = this.$anchorCell.start(-1);
34309
34934
  const rect = map.rectBetween(this.$anchorCell.pos - tableStart, this.$headCell.pos - tableStart);
34310
34935
  const seen = {};
@@ -34358,7 +34983,7 @@ var CellSelection = class CellSelection2 extends Selection {
34358
34983
  }
34359
34984
  forEachCell(f) {
34360
34985
  const table = this.$anchorCell.node(-1);
34361
- const map = TableMap2.get(table);
34986
+ const map = TableMap3.get(table);
34362
34987
  const tableStart = this.$anchorCell.start(-1);
34363
34988
  const cells = map.cellsInRect(map.rectBetween(this.$anchorCell.pos - tableStart, this.$headCell.pos - tableStart));
34364
34989
  for (let i = 0; i < cells.length; i++) f(table.nodeAt(cells[i]), tableStart + cells[i]);
@@ -34373,7 +34998,7 @@ var CellSelection = class CellSelection2 extends Selection {
34373
34998
  }
34374
34999
  static colSelection($anchorCell, $headCell = $anchorCell) {
34375
35000
  const table = $anchorCell.node(-1);
34376
- const map = TableMap2.get(table);
35001
+ const map = TableMap3.get(table);
34377
35002
  const tableStart = $anchorCell.start(-1);
34378
35003
  const anchorRect = map.findCell($anchorCell.pos - tableStart);
34379
35004
  const headRect = map.findCell($headCell.pos - tableStart);
@@ -34389,7 +35014,7 @@ var CellSelection = class CellSelection2 extends Selection {
34389
35014
  }
34390
35015
  isRowSelection() {
34391
35016
  const table = this.$anchorCell.node(-1);
34392
- const map = TableMap2.get(table);
35017
+ const map = TableMap3.get(table);
34393
35018
  const tableStart = this.$anchorCell.start(-1);
34394
35019
  const anchorLeft = map.colCount(this.$anchorCell.pos - tableStart);
34395
35020
  const headLeft = map.colCount(this.$headCell.pos - tableStart);
@@ -34403,7 +35028,7 @@ var CellSelection = class CellSelection2 extends Selection {
34403
35028
  }
34404
35029
  static rowSelection($anchorCell, $headCell = $anchorCell) {
34405
35030
  const table = $anchorCell.node(-1);
34406
- const map = TableMap2.get(table);
35031
+ const map = TableMap3.get(table);
34407
35032
  const tableStart = $anchorCell.start(-1);
34408
35033
  const anchorRect = map.findCell($anchorCell.pos - tableStart);
34409
35034
  const headRect = map.findCell($headCell.pos - tableStart);
@@ -34452,7 +35077,7 @@ var CellBookmark = class CellBookmark2 {
34452
35077
  };
34453
35078
  var fixTablesKey = new PluginKey4("fix-tables");
34454
35079
  function convertTableNodeToArrayOfRows(tableNode) {
34455
- const map = TableMap2.get(tableNode);
35080
+ const map = TableMap3.get(tableNode);
34456
35081
  const rows = [];
34457
35082
  const rowCount = map.height;
34458
35083
  const colCount$1 = map.width;
@@ -34483,7 +35108,7 @@ function convertTableNodeToArrayOfRows(tableNode) {
34483
35108
  }
34484
35109
  function convertArrayOfRowsToTableNode(tableNode, arrayOfNodes) {
34485
35110
  const newRows = [];
34486
- const map = TableMap2.get(tableNode);
35111
+ const map = TableMap3.get(tableNode);
34487
35112
  const rowCount = map.height;
34488
35113
  const colCount$1 = map.width;
34489
35114
  for (let rowIndex = 0; rowIndex < rowCount; rowIndex++) {
@@ -34532,7 +35157,7 @@ function findParentNode(predicate, $pos) {
34532
35157
  function getCellsInColumn(columnIndex, selection) {
34533
35158
  const table = findTable(selection.$from);
34534
35159
  if (!table) return;
34535
- const map = TableMap2.get(table.node);
35160
+ const map = TableMap3.get(table.node);
34536
35161
  if (columnIndex < 0 || columnIndex > map.width - 1) return;
34537
35162
  return map.cellsInRect({
34538
35163
  left: columnIndex,
@@ -34553,7 +35178,7 @@ function getCellsInColumn(columnIndex, selection) {
34553
35178
  function getCellsInRow(rowIndex, selection) {
34554
35179
  const table = findTable(selection.$from);
34555
35180
  if (!table) return;
34556
- const map = TableMap2.get(table.node);
35181
+ const map = TableMap3.get(table.node);
34557
35182
  if (rowIndex < 0 || rowIndex > map.height - 1) return;
34558
35183
  return map.cellsInRect({
34559
35184
  left: 0,
@@ -34682,7 +35307,7 @@ function moveColumn(moveColParams) {
34682
35307
  const newTable = moveTableColumn$1(table.node, indexesOriginColumn, indexesTargetColumn, 0);
34683
35308
  tr.replaceWith(table.pos, table.pos + table.node.nodeSize, newTable);
34684
35309
  if (!select) return true;
34685
- const map = TableMap2.get(newTable);
35310
+ const map = TableMap3.get(newTable);
34686
35311
  const start = table.start;
34687
35312
  const index = targetIndex;
34688
35313
  const lastCell = map.positionAt(map.height - 1, index, newTable);
@@ -34710,7 +35335,7 @@ function moveRow(moveRowParams) {
34710
35335
  const newTable = moveTableRow$1(table.node, indexesOriginRow, indexesTargetRow, 0);
34711
35336
  tr.replaceWith(table.pos, table.pos + table.node.nodeSize, newTable);
34712
35337
  if (!select) return true;
34713
- const map = TableMap2.get(newTable);
35338
+ const map = TableMap3.get(newTable);
34714
35339
  const start = table.start;
34715
35340
  const index = targetIndex;
34716
35341
  const lastCell = map.positionAt(index, map.width - 1, newTable);
@@ -34725,12 +35350,12 @@ function moveTableRow$1(table, indexesOrigin, indexesTarget, direction) {
34725
35350
  rows = moveRowInArrayOfRows(rows, indexesOrigin, indexesTarget, direction);
34726
35351
  return convertArrayOfRowsToTableNode(table, rows);
34727
35352
  }
34728
- function selectedRect2(state) {
35353
+ function selectedRect3(state) {
34729
35354
  const sel = state.selection;
34730
35355
  const $pos = selectionCell(state);
34731
35356
  const table = $pos.node(-1);
34732
35357
  const tableStart = $pos.start(-1);
34733
- const map = TableMap2.get(table);
35358
+ const map = TableMap3.get(table);
34734
35359
  return {
34735
35360
  ...sel instanceof CellSelection ? map.rectBetween(sel.$anchorCell.pos - tableStart, sel.$headCell.pos - tableStart) : map.findCell($pos.pos - tableStart),
34736
35361
  tableStart,
@@ -34743,7 +35368,7 @@ function deprecated_toggleHeader(type) {
34743
35368
  if (!isInTable(state)) return false;
34744
35369
  if (dispatch) {
34745
35370
  const types = tableNodeTypes(state.schema);
34746
- const rect = selectedRect2(state), tr = state.tr;
35371
+ const rect = selectedRect3(state), tr = state.tr;
34747
35372
  const cells = rect.map.cellsInRect(type == "column" ? {
34748
35373
  left: rect.left,
34749
35374
  top: 0,
@@ -34783,7 +35408,7 @@ function toggleHeader(type, options) {
34783
35408
  if (!isInTable(state)) return false;
34784
35409
  if (dispatch) {
34785
35410
  const types = tableNodeTypes(state.schema);
34786
- const rect = selectedRect2(state), tr = state.tr;
35411
+ const rect = selectedRect3(state), tr = state.tr;
34787
35412
  const isHeaderRowEnabled = isHeaderEnabledByType("row", rect, types);
34788
35413
  const isHeaderColumnEnabled = isHeaderEnabledByType("column", rect, types);
34789
35414
  const selectionStartsAt = (type === "column" ? isHeaderRowEnabled : type === "row" ? isHeaderColumnEnabled : false) ? 1 : 0;
@@ -34944,6 +35569,262 @@ import {
34944
35569
  Trash2 as Trash24
34945
35570
  } from "lucide-react";
34946
35571
 
35572
+ // src/components/UEditor/table-layout-model.ts
35573
+ var FALLBACK_TABLE_ROW_HEIGHT = 44;
35574
+ var FALLBACK_TABLE_COLUMN_WIDTH = 160;
35575
+ function getVisibleTableBounds(layout) {
35576
+ const left = Math.max(layout.tableLeft, layout.wrapperLeft);
35577
+ const top = Math.max(layout.tableTop, layout.wrapperTop);
35578
+ const right = Math.min(layout.tableLeft + layout.tableWidth, layout.wrapperLeft + layout.viewportWidth);
35579
+ const bottom = Math.min(layout.tableTop + layout.tableHeight, layout.wrapperTop + layout.viewportHeight);
35580
+ return {
35581
+ left,
35582
+ top,
35583
+ right,
35584
+ bottom,
35585
+ width: Math.max(0, right - left),
35586
+ height: Math.max(0, bottom - top)
35587
+ };
35588
+ }
35589
+ function metricOrFallback(value, fallback) {
35590
+ return Number.isFinite(value) && value > 0 ? value : fallback;
35591
+ }
35592
+ function parsePixelMetric(value) {
35593
+ if (!value) return null;
35594
+ const parsed = Number.parseFloat(value);
35595
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
35596
+ }
35597
+ function getPrimaryCell(table) {
35598
+ const cell = table.querySelector("th,td");
35599
+ return cell instanceof HTMLTableCellElement ? cell : null;
35600
+ }
35601
+ function getLastCell(table) {
35602
+ const lastRow = table.rows.item(table.rows.length - 1);
35603
+ if (!(lastRow instanceof HTMLTableRowElement)) return null;
35604
+ const cell = lastRow.cells.item(lastRow.cells.length - 1);
35605
+ return cell instanceof HTMLTableCellElement ? cell : null;
35606
+ }
35607
+ function getCellFromTarget(target) {
35608
+ const element = resolveEventElement(target);
35609
+ if (!element) return null;
35610
+ const directCell = element.closest("th,td");
35611
+ if (directCell instanceof HTMLTableCellElement) {
35612
+ return directCell;
35613
+ }
35614
+ const table = element.closest("table");
35615
+ if (table instanceof HTMLTableElement) {
35616
+ return getPrimaryCell(table);
35617
+ }
35618
+ return null;
35619
+ }
35620
+ function findTableInfo(editor, pos) {
35621
+ const $pos = editor.state.doc.resolve(pos);
35622
+ for (let depth = $pos.depth; depth > 0; depth -= 1) {
35623
+ const node = $pos.node(depth);
35624
+ if (node.type.name === "table") {
35625
+ return {
35626
+ node,
35627
+ pos: $pos.before(depth),
35628
+ start: $pos.start(depth)
35629
+ };
35630
+ }
35631
+ }
35632
+ return null;
35633
+ }
35634
+ function getCellRelativePosFromDomPos(map, tableStart, domPos) {
35635
+ const relativeDomPos = domPos - tableStart;
35636
+ const seen = /* @__PURE__ */ new Set();
35637
+ for (const relativeCellPos of map.map) {
35638
+ if (seen.has(relativeCellPos)) continue;
35639
+ seen.add(relativeCellPos);
35640
+ if (relativeDomPos === relativeCellPos || relativeDomPos === relativeCellPos + 1) {
35641
+ return relativeCellPos;
35642
+ }
35643
+ }
35644
+ return null;
35645
+ }
35646
+ function buildLogicalColumnMetrics({
35647
+ editor,
35648
+ surface,
35649
+ surfaceRect,
35650
+ tableElement,
35651
+ tableInfo,
35652
+ tableLeft,
35653
+ tableWidth
35654
+ }) {
35655
+ const map = TableMap3.get(tableInfo.node);
35656
+ const fallbackWidth = metricOrFallback(tableWidth / map.width, FALLBACK_TABLE_COLUMN_WIDTH);
35657
+ const firstRow = tableElement.rows.item(0);
35658
+ const visualColumns = [];
35659
+ if (firstRow) {
35660
+ for (const tableCell of Array.from(firstRow.cells)) {
35661
+ if (!(tableCell instanceof HTMLTableCellElement)) continue;
35662
+ const cellPos = editor.view.posAtDOM(tableCell, 0);
35663
+ const relativeCellPos = getCellRelativePosFromDomPos(map, tableInfo.start, cellPos);
35664
+ if (relativeCellPos == null) continue;
35665
+ const cellMapRect = map.findCell(relativeCellPos);
35666
+ const cellRect = tableCell.getBoundingClientRect();
35667
+ const cellStart = cellRect.width > 0 ? cellRect.left - surfaceRect.left + surface.scrollLeft : tableLeft + cellMapRect.left * fallbackWidth;
35668
+ const size = metricOrFallback(cellRect.width, fallbackWidth * Math.max(1, cellMapRect.right - cellMapRect.left));
35669
+ visualColumns.push({
35670
+ index: cellMapRect.left,
35671
+ cellPos: tableInfo.start + relativeCellPos,
35672
+ start: cellStart,
35673
+ size,
35674
+ center: cellStart + size / 2
35675
+ });
35676
+ }
35677
+ }
35678
+ if (visualColumns.length > 0) {
35679
+ return visualColumns.sort((a, b) => a.index - b.index);
35680
+ }
35681
+ const cols = Array.from(tableElement.querySelectorAll("colgroup > col"));
35682
+ const parsedWidths = cols.slice(0, map.width).map((col) => parsePixelMetric(col.style.width) ?? parsePixelMetric(col.getAttribute("width")));
35683
+ const hasCompleteColWidths = parsedWidths.length >= map.width && parsedWidths.every((width) => typeof width === "number");
35684
+ let cursor = tableLeft;
35685
+ return Array.from({ length: map.width }, (_, index) => {
35686
+ const size = hasCompleteColWidths ? parsedWidths[index] : fallbackWidth;
35687
+ const start = hasCompleteColWidths ? cursor : tableLeft + index * fallbackWidth;
35688
+ cursor += size;
35689
+ return {
35690
+ index,
35691
+ cellPos: tableInfo.start + map.positionAt(0, index, tableInfo.node),
35692
+ start,
35693
+ size,
35694
+ center: start + size / 2
35695
+ };
35696
+ });
35697
+ }
35698
+ function buildLogicalRowMetrics({
35699
+ editor,
35700
+ surface,
35701
+ surfaceRect,
35702
+ tableInfo,
35703
+ rows,
35704
+ tableTop,
35705
+ tableHeight,
35706
+ cornerCell
35707
+ }) {
35708
+ const map = TableMap3.get(tableInfo.node);
35709
+ const fallbackHeight = metricOrFallback(tableHeight / map.height, FALLBACK_TABLE_ROW_HEIGHT);
35710
+ const visualRows = [];
35711
+ const seenCellPositions = /* @__PURE__ */ new Set();
35712
+ for (let rowIndex = 0; rowIndex < map.height; rowIndex += 1) {
35713
+ const relativeCellPos = map.map[rowIndex * map.width];
35714
+ if (seenCellPositions.has(relativeCellPos)) continue;
35715
+ seenCellPositions.add(relativeCellPos);
35716
+ const cellMapRect = map.findCell(relativeCellPos);
35717
+ const cellDom = editor.view.nodeDOM(tableInfo.start + relativeCellPos);
35718
+ const tableCell = cellDom instanceof HTMLTableCellElement ? cellDom : null;
35719
+ if (tableCell) {
35720
+ const cellRect = tableCell.getBoundingClientRect();
35721
+ const start = cellRect.height > 0 ? cellRect.top - surfaceRect.top + surface.scrollTop : tableTop + cellMapRect.top * fallbackHeight;
35722
+ const size = metricOrFallback(cellRect.height, fallbackHeight * Math.max(1, cellMapRect.bottom - cellMapRect.top));
35723
+ visualRows.push({
35724
+ index: cellMapRect.top,
35725
+ cellPos: tableInfo.start + relativeCellPos,
35726
+ start,
35727
+ size,
35728
+ center: start + size / 2
35729
+ });
35730
+ }
35731
+ }
35732
+ if (visualRows.length > 0) {
35733
+ return visualRows.sort((a, b) => a.index - b.index);
35734
+ }
35735
+ return rows.map((tableRow, index) => {
35736
+ const rowRect = tableRow.getBoundingClientRect();
35737
+ const anchorCell = tableRow.cells.item(0) ?? cornerCell;
35738
+ const start = rowRect.height > 0 ? rowRect.top - surfaceRect.top + surface.scrollTop : tableTop + index * fallbackHeight;
35739
+ const size = metricOrFallback(rowRect.height, fallbackHeight);
35740
+ return {
35741
+ index,
35742
+ cellPos: editor.view.posAtDOM(anchorCell, 0),
35743
+ start,
35744
+ size,
35745
+ center: start + size / 2
35746
+ };
35747
+ });
35748
+ }
35749
+ function buildTableControlLayout(editor, surface, cell) {
35750
+ const row = cell.closest("tr");
35751
+ const table = cell.closest("table");
35752
+ if (!(row instanceof HTMLTableRowElement) || !(table instanceof HTMLTableElement)) {
35753
+ return null;
35754
+ }
35755
+ const rows = Array.from(table.rows).filter((item) => item instanceof HTMLTableRowElement);
35756
+ const cornerCell = getLastCell(table);
35757
+ const cellPos = editor.view.posAtDOM(cell, 0);
35758
+ const tableInfo = findTableInfo(editor, cellPos);
35759
+ if (rows.length === 0 || !tableInfo || !(cornerCell instanceof HTMLTableCellElement)) {
35760
+ return null;
35761
+ }
35762
+ const map = TableMap3.get(tableInfo.node);
35763
+ const surfaceRect = surface.getBoundingClientRect();
35764
+ const tableRect = table.getBoundingClientRect();
35765
+ const wrapperElement = table.closest(".tableWrapper");
35766
+ const wrapper = wrapperElement instanceof HTMLElement ? wrapperElement : null;
35767
+ const wrapperRect = wrapper?.getBoundingClientRect() ?? tableRect;
35768
+ const tableLeft = tableRect.left - surfaceRect.left + surface.scrollLeft;
35769
+ const tableTop = tableRect.top - surfaceRect.top + surface.scrollTop;
35770
+ const avgRowHeight = metricOrFallback(tableRect.height / rows.length, FALLBACK_TABLE_ROW_HEIGHT);
35771
+ const avgColumnWidth = metricOrFallback(tableRect.width / map.width, FALLBACK_TABLE_COLUMN_WIDTH);
35772
+ const tableWidth = metricOrFallback(tableRect.width, avgColumnWidth * map.width);
35773
+ const tableHeight = metricOrFallback(tableRect.height, avgRowHeight * rows.length);
35774
+ const wrapperLeft = wrapperRect.left - surfaceRect.left + surface.scrollLeft;
35775
+ const wrapperTop = wrapperRect.top - surfaceRect.top + surface.scrollTop;
35776
+ const wrapperWidth = metricOrFallback(wrapperRect.width, tableWidth);
35777
+ const wrapperHeight = metricOrFallback(wrapperRect.height, tableHeight);
35778
+ const viewportWidth = metricOrFallback(wrapper?.clientWidth ?? wrapperRect.width, tableWidth);
35779
+ const viewportHeight = metricOrFallback(wrapper?.clientHeight ?? wrapperRect.height, tableHeight);
35780
+ const verticalScrollbarWidth = Math.max(0, Math.round(wrapperWidth - viewportWidth));
35781
+ const horizontalScrollbarHeight = Math.max(0, Math.round(wrapperHeight - viewportHeight));
35782
+ const rowHandles = buildLogicalRowMetrics({
35783
+ editor,
35784
+ surface,
35785
+ surfaceRect,
35786
+ tableInfo,
35787
+ rows,
35788
+ tableTop,
35789
+ tableHeight,
35790
+ cornerCell
35791
+ });
35792
+ const columnHandles = buildLogicalColumnMetrics({
35793
+ editor,
35794
+ surface,
35795
+ surfaceRect,
35796
+ tableElement: table,
35797
+ tableInfo,
35798
+ tableLeft,
35799
+ tableWidth
35800
+ });
35801
+ const activeCellRelativePos = getCellRelativePosFromDomPos(map, tableInfo.start, cellPos);
35802
+ const activeCellRect = activeCellRelativePos != null ? map.findCell(activeCellRelativePos) : { left: cell.cellIndex, top: row.rowIndex };
35803
+ const normalizedCellPos = activeCellRelativePos != null ? tableInfo.start + activeCellRelativePos : cellPos;
35804
+ return {
35805
+ cellPos: normalizedCellPos,
35806
+ cornerCellPos: tableInfo.start + map.positionAt(map.height - 1, map.width - 1, tableInfo.node),
35807
+ activeRowIndex: activeCellRect.top,
35808
+ activeColumnIndex: activeCellRect.left,
35809
+ tableLeft,
35810
+ tableTop,
35811
+ tableWidth,
35812
+ tableHeight,
35813
+ wrapperLeft,
35814
+ wrapperTop,
35815
+ wrapperWidth,
35816
+ wrapperHeight,
35817
+ viewportWidth,
35818
+ viewportHeight,
35819
+ horizontalScrollbarHeight,
35820
+ verticalScrollbarWidth,
35821
+ avgRowHeight,
35822
+ avgColumnWidth,
35823
+ rowHandles,
35824
+ columnHandles
35825
+ };
35826
+ }
35827
+
34947
35828
  // src/components/UEditor/table-hover-state.ts
34948
35829
  var MENU_HOVER_PADDING = 18;
34949
35830
  var ROW_HANDLE_HOVER_WIDTH = 28;
@@ -34977,18 +35858,18 @@ function buildTableHoverState({
34977
35858
  const directAddRow = targetElement?.closest?.("[data-table-control='add-row']");
34978
35859
  const directRowHandleIndex = directRowHandle instanceof HTMLElement ? Number.parseInt(directRowHandle.dataset.rowHandleIndex ?? "", 10) : Number.NaN;
34979
35860
  const directColumnHandleIndex = directColumnHandle instanceof HTMLElement ? Number.parseInt(directColumnHandle.dataset.columnHandleIndex ?? "", 10) : Number.NaN;
34980
- const visibleTableWidth = Math.min(layout.tableWidth, layout.viewportWidth);
34981
- const visibleTableHeight = Math.min(layout.tableHeight, layout.viewportHeight);
35861
+ const visibleBounds = getVisibleTableBounds(layout);
35862
+ const rowRailTop = layout.wrapperTop + layout.wrapperHeight;
34982
35863
  const isMouseInTable = relativeX >= layout.tableLeft && relativeX <= layout.tableLeft + layout.tableWidth && relativeY >= layout.tableTop && relativeY <= layout.tableTop + layout.tableHeight;
34983
35864
  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;
34984
35865
  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;
34985
35866
  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;
34986
35867
  const lastRow = layout.rowHandles[layout.rowHandles.length - 1];
34987
35868
  const lastCol = layout.columnHandles[layout.columnHandles.length - 1];
34988
- const isMouseInLastColumn = lastCol ? relativeX >= lastCol.start && relativeX <= lastCol.start + lastCol.size && relativeY >= layout.tableTop && relativeY <= layout.tableTop + visibleTableHeight : false;
34989
- const addColumnVisible = Boolean(directAddColumn) || relativeX >= layout.tableLeft + visibleTableWidth && relativeX <= layout.tableLeft + visibleTableWidth + ADD_COLUMN_HOVER_WIDTH && relativeY >= layout.tableTop && relativeY <= layout.tableTop + visibleTableHeight || isMouseInLastColumn;
34990
- const isMouseInLastRow = lastRow ? relativeY >= lastRow.start && relativeY <= lastRow.start + lastRow.size && relativeX >= layout.tableLeft && relativeX <= layout.tableLeft + visibleTableWidth : false;
34991
- const addRowVisible = Boolean(directAddRow) || relativeY >= layout.tableTop + visibleTableHeight && relativeY <= layout.tableTop + visibleTableHeight + ADD_ROW_HOVER_HEIGHT && relativeX >= layout.tableLeft && relativeX <= layout.tableLeft + visibleTableWidth || isMouseInLastRow;
35869
+ const isMouseInLastColumn = lastCol ? relativeX >= lastCol.start && relativeX <= lastCol.start + lastCol.size && relativeY >= visibleBounds.top && relativeY <= visibleBounds.bottom : false;
35870
+ const addColumnVisible = Boolean(directAddColumn) || relativeX >= visibleBounds.right && relativeX <= visibleBounds.right + ADD_COLUMN_HOVER_WIDTH && relativeY >= visibleBounds.top && relativeY <= visibleBounds.bottom || isMouseInLastColumn;
35871
+ const isMouseInLastRow = lastRow ? relativeY >= lastRow.start && relativeY <= lastRow.start + lastRow.size && relativeX >= visibleBounds.left && relativeX <= visibleBounds.right : false;
35872
+ const addRowVisible = Boolean(directAddRow) || relativeY >= rowRailTop && relativeY <= rowRailTop + ADD_ROW_HOVER_HEIGHT && relativeX >= visibleBounds.left && relativeX <= visibleBounds.right || isMouseInLastRow;
34992
35873
  return {
34993
35874
  menuVisible,
34994
35875
  addColumnVisible,
@@ -35110,12 +35991,11 @@ function TableAddRails({
35110
35991
  quickAddColumnLabel,
35111
35992
  quickAddRowLabel
35112
35993
  }) {
35113
- const visibleTableWidth = Math.min(layout.tableWidth, layout.viewportWidth);
35114
- const visibleTableHeight = Math.min(layout.tableHeight, layout.viewportHeight);
35115
- const columnRailTop = layout.tableTop;
35116
- const columnRailLeft = layout.tableLeft + visibleTableWidth + ADD_COLUMN_RAIL_GAP;
35994
+ const visibleBounds = getVisibleTableBounds(layout);
35995
+ const columnRailTop = visibleBounds.top;
35996
+ const columnRailLeft = visibleBounds.right + ADD_COLUMN_RAIL_GAP;
35117
35997
  const rowRailTop = layout.wrapperTop + layout.wrapperHeight + ADD_ROW_RAIL_GAP;
35118
- const rowRailLeft = Math.max(layout.tableLeft, layout.wrapperLeft);
35998
+ const rowRailLeft = visibleBounds.left;
35119
35999
  const showColumnRail = controlsVisible || addColumnVisible;
35120
36000
  const showRowRail = controlsVisible || addRowVisible;
35121
36001
  return /* @__PURE__ */ jsxs75(Fragment32, { children: [
@@ -35143,10 +36023,10 @@ function TableAddRails({
35143
36023
  "transition-[opacity,transform,colors] duration-150 hover:bg-accent hover:text-foreground disabled:opacity-50 disabled:cursor-not-allowed"
35144
36024
  ),
35145
36025
  style: {
35146
- top: showColumnRail ? columnRailTop : columnRailTop + Math.max(0, visibleTableHeight / 2 - 24),
36026
+ top: showColumnRail ? columnRailTop : columnRailTop + Math.max(0, visibleBounds.height / 2 - 24),
35147
36027
  left: columnRailLeft,
35148
36028
  width: showColumnRail ? 18 : 12,
35149
- height: showColumnRail ? visibleTableHeight : 48,
36029
+ height: showColumnRail ? visibleBounds.height : 48,
35150
36030
  opacity: showColumnRail ? 1 : 0,
35151
36031
  transform: showColumnRail ? "scale(1)" : "scale(0.92)",
35152
36032
  pointerEvents: showColumnRail ? "auto" : "none"
@@ -35181,8 +36061,8 @@ function TableAddRails({
35181
36061
  ),
35182
36062
  style: {
35183
36063
  top: rowRailTop,
35184
- left: showRowRail ? rowRailLeft : rowRailLeft + Math.max(0, visibleTableWidth / 2 - 24),
35185
- width: showRowRail ? visibleTableWidth : 48,
36064
+ left: showRowRail ? rowRailLeft : rowRailLeft + Math.max(0, visibleBounds.width / 2 - 24),
36065
+ width: showRowRail ? visibleBounds.width : 48,
35186
36066
  height: showRowRail ? 16 : 12,
35187
36067
  opacity: showRowRail ? 1 : 0,
35188
36068
  transform: showRowRail ? "scale(1)" : "scale(0.92)",
@@ -35407,248 +36287,6 @@ function TableColumnHandles({
35407
36287
  }) });
35408
36288
  }
35409
36289
 
35410
- // src/components/UEditor/table-layout-model.ts
35411
- var FALLBACK_TABLE_ROW_HEIGHT = 44;
35412
- var FALLBACK_TABLE_COLUMN_WIDTH = 160;
35413
- function metricOrFallback(value, fallback) {
35414
- return Number.isFinite(value) && value > 0 ? value : fallback;
35415
- }
35416
- function parsePixelMetric(value) {
35417
- if (!value) return null;
35418
- const parsed = Number.parseFloat(value);
35419
- return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
35420
- }
35421
- function getPrimaryCell(table) {
35422
- const cell = table.querySelector("th,td");
35423
- return cell instanceof HTMLTableCellElement ? cell : null;
35424
- }
35425
- function getLastCell(table) {
35426
- const lastRow = table.rows.item(table.rows.length - 1);
35427
- if (!(lastRow instanceof HTMLTableRowElement)) return null;
35428
- const cell = lastRow.cells.item(lastRow.cells.length - 1);
35429
- return cell instanceof HTMLTableCellElement ? cell : null;
35430
- }
35431
- function getCellFromTarget(target) {
35432
- const element = resolveEventElement(target);
35433
- if (!element) return null;
35434
- const directCell = element.closest("th,td");
35435
- if (directCell instanceof HTMLTableCellElement) {
35436
- return directCell;
35437
- }
35438
- const table = element.closest("table");
35439
- if (table instanceof HTMLTableElement) {
35440
- return getPrimaryCell(table);
35441
- }
35442
- return null;
35443
- }
35444
- function findTableInfo(editor, pos) {
35445
- const $pos = editor.state.doc.resolve(pos);
35446
- for (let depth = $pos.depth; depth > 0; depth -= 1) {
35447
- const node = $pos.node(depth);
35448
- if (node.type.name === "table") {
35449
- return {
35450
- node,
35451
- pos: $pos.before(depth),
35452
- start: $pos.start(depth)
35453
- };
35454
- }
35455
- }
35456
- return null;
35457
- }
35458
- function getCellRelativePosFromDomPos(map, tableStart, domPos) {
35459
- const relativeDomPos = domPos - tableStart;
35460
- const seen = /* @__PURE__ */ new Set();
35461
- for (const relativeCellPos of map.map) {
35462
- if (seen.has(relativeCellPos)) continue;
35463
- seen.add(relativeCellPos);
35464
- if (relativeDomPos === relativeCellPos || relativeDomPos === relativeCellPos + 1) {
35465
- return relativeCellPos;
35466
- }
35467
- }
35468
- return null;
35469
- }
35470
- function buildLogicalColumnMetrics({
35471
- editor,
35472
- surface,
35473
- surfaceRect,
35474
- tableElement,
35475
- tableInfo,
35476
- tableLeft,
35477
- tableWidth
35478
- }) {
35479
- const map = TableMap2.get(tableInfo.node);
35480
- const fallbackWidth = metricOrFallback(tableWidth / map.width, FALLBACK_TABLE_COLUMN_WIDTH);
35481
- const firstRow = tableElement.rows.item(0);
35482
- const visualColumns = [];
35483
- if (firstRow) {
35484
- for (const tableCell of Array.from(firstRow.cells)) {
35485
- if (!(tableCell instanceof HTMLTableCellElement)) continue;
35486
- const cellPos = editor.view.posAtDOM(tableCell, 0);
35487
- const relativeCellPos = getCellRelativePosFromDomPos(map, tableInfo.start, cellPos);
35488
- if (relativeCellPos == null) continue;
35489
- const cellMapRect = map.findCell(relativeCellPos);
35490
- const cellRect = tableCell.getBoundingClientRect();
35491
- const cellStart = cellRect.width > 0 ? cellRect.left - surfaceRect.left + surface.scrollLeft : tableLeft + cellMapRect.left * fallbackWidth;
35492
- const size = metricOrFallback(cellRect.width, fallbackWidth * Math.max(1, cellMapRect.right - cellMapRect.left));
35493
- visualColumns.push({
35494
- index: cellMapRect.left,
35495
- cellPos: tableInfo.start + relativeCellPos,
35496
- start: cellStart,
35497
- size,
35498
- center: cellStart + size / 2
35499
- });
35500
- }
35501
- }
35502
- if (visualColumns.length > 0) {
35503
- return visualColumns.sort((a, b) => a.index - b.index);
35504
- }
35505
- const cols = Array.from(tableElement.querySelectorAll("colgroup > col"));
35506
- const parsedWidths = cols.slice(0, map.width).map((col) => parsePixelMetric(col.style.width) ?? parsePixelMetric(col.getAttribute("width")));
35507
- const hasCompleteColWidths = parsedWidths.length >= map.width && parsedWidths.every((width) => typeof width === "number");
35508
- let cursor = tableLeft;
35509
- return Array.from({ length: map.width }, (_, index) => {
35510
- const size = hasCompleteColWidths ? parsedWidths[index] : fallbackWidth;
35511
- const start = hasCompleteColWidths ? cursor : tableLeft + index * fallbackWidth;
35512
- cursor += size;
35513
- return {
35514
- index,
35515
- cellPos: tableInfo.start + map.positionAt(0, index, tableInfo.node),
35516
- start,
35517
- size,
35518
- center: start + size / 2
35519
- };
35520
- });
35521
- }
35522
- function buildLogicalRowMetrics({
35523
- editor,
35524
- surface,
35525
- surfaceRect,
35526
- tableInfo,
35527
- rows,
35528
- tableTop,
35529
- tableHeight,
35530
- cornerCell
35531
- }) {
35532
- const map = TableMap2.get(tableInfo.node);
35533
- const fallbackHeight = metricOrFallback(tableHeight / map.height, FALLBACK_TABLE_ROW_HEIGHT);
35534
- const visualRows = [];
35535
- const seenCellPositions = /* @__PURE__ */ new Set();
35536
- for (let rowIndex = 0; rowIndex < map.height; rowIndex += 1) {
35537
- const relativeCellPos = map.map[rowIndex * map.width];
35538
- if (seenCellPositions.has(relativeCellPos)) continue;
35539
- seenCellPositions.add(relativeCellPos);
35540
- const cellMapRect = map.findCell(relativeCellPos);
35541
- const cellDom = editor.view.nodeDOM(tableInfo.start + relativeCellPos);
35542
- const tableCell = cellDom instanceof HTMLTableCellElement ? cellDom : null;
35543
- if (tableCell) {
35544
- const cellRect = tableCell.getBoundingClientRect();
35545
- const start = cellRect.height > 0 ? cellRect.top - surfaceRect.top + surface.scrollTop : tableTop + cellMapRect.top * fallbackHeight;
35546
- const size = metricOrFallback(cellRect.height, fallbackHeight * Math.max(1, cellMapRect.bottom - cellMapRect.top));
35547
- visualRows.push({
35548
- index: cellMapRect.top,
35549
- cellPos: tableInfo.start + relativeCellPos,
35550
- start,
35551
- size,
35552
- center: start + size / 2
35553
- });
35554
- }
35555
- }
35556
- if (visualRows.length > 0) {
35557
- return visualRows.sort((a, b) => a.index - b.index);
35558
- }
35559
- return rows.map((tableRow, index) => {
35560
- const rowRect = tableRow.getBoundingClientRect();
35561
- const anchorCell = tableRow.cells.item(0) ?? cornerCell;
35562
- const start = rowRect.height > 0 ? rowRect.top - surfaceRect.top + surface.scrollTop : tableTop + index * fallbackHeight;
35563
- const size = metricOrFallback(rowRect.height, fallbackHeight);
35564
- return {
35565
- index,
35566
- cellPos: editor.view.posAtDOM(anchorCell, 0),
35567
- start,
35568
- size,
35569
- center: start + size / 2
35570
- };
35571
- });
35572
- }
35573
- function buildTableControlLayout(editor, surface, cell) {
35574
- const row = cell.closest("tr");
35575
- const table = cell.closest("table");
35576
- if (!(row instanceof HTMLTableRowElement) || !(table instanceof HTMLTableElement)) {
35577
- return null;
35578
- }
35579
- const rows = Array.from(table.rows).filter((item) => item instanceof HTMLTableRowElement);
35580
- const cornerCell = getLastCell(table);
35581
- const cellPos = editor.view.posAtDOM(cell, 0);
35582
- const tableInfo = findTableInfo(editor, cellPos);
35583
- if (rows.length === 0 || !tableInfo || !(cornerCell instanceof HTMLTableCellElement)) {
35584
- return null;
35585
- }
35586
- const map = TableMap2.get(tableInfo.node);
35587
- const surfaceRect = surface.getBoundingClientRect();
35588
- const tableRect = table.getBoundingClientRect();
35589
- const wrapperElement = table.closest(".tableWrapper");
35590
- const wrapper = wrapperElement instanceof HTMLElement ? wrapperElement : null;
35591
- const wrapperRect = wrapper?.getBoundingClientRect() ?? tableRect;
35592
- const tableLeft = tableRect.left - surfaceRect.left + surface.scrollLeft;
35593
- const tableTop = tableRect.top - surfaceRect.top + surface.scrollTop;
35594
- const avgRowHeight = metricOrFallback(tableRect.height / rows.length, FALLBACK_TABLE_ROW_HEIGHT);
35595
- const avgColumnWidth = metricOrFallback(tableRect.width / map.width, FALLBACK_TABLE_COLUMN_WIDTH);
35596
- const tableWidth = metricOrFallback(tableRect.width, avgColumnWidth * map.width);
35597
- const tableHeight = metricOrFallback(tableRect.height, avgRowHeight * rows.length);
35598
- const wrapperLeft = wrapperRect.left - surfaceRect.left + surface.scrollLeft;
35599
- const wrapperTop = wrapperRect.top - surfaceRect.top + surface.scrollTop;
35600
- const wrapperWidth = metricOrFallback(wrapperRect.width, tableWidth);
35601
- const wrapperHeight = metricOrFallback(wrapperRect.height, tableHeight);
35602
- const viewportWidth = metricOrFallback(wrapper?.clientWidth ?? wrapperRect.width, tableWidth);
35603
- const viewportHeight = metricOrFallback(wrapper?.clientHeight ?? wrapperRect.height, tableHeight);
35604
- const verticalScrollbarWidth = Math.max(0, Math.round(wrapperWidth - viewportWidth));
35605
- const horizontalScrollbarHeight = Math.max(0, Math.round(wrapperHeight - viewportHeight));
35606
- const rowHandles = buildLogicalRowMetrics({
35607
- editor,
35608
- surface,
35609
- surfaceRect,
35610
- tableInfo,
35611
- rows,
35612
- tableTop,
35613
- tableHeight,
35614
- cornerCell
35615
- });
35616
- const columnHandles = buildLogicalColumnMetrics({
35617
- editor,
35618
- surface,
35619
- surfaceRect,
35620
- tableElement: table,
35621
- tableInfo,
35622
- tableLeft,
35623
- tableWidth
35624
- });
35625
- const activeCellRelativePos = getCellRelativePosFromDomPos(map, tableInfo.start, cellPos);
35626
- const activeCellRect = activeCellRelativePos != null ? map.findCell(activeCellRelativePos) : { left: cell.cellIndex, top: row.rowIndex };
35627
- const normalizedCellPos = activeCellRelativePos != null ? tableInfo.start + activeCellRelativePos : cellPos;
35628
- return {
35629
- cellPos: normalizedCellPos,
35630
- cornerCellPos: tableInfo.start + map.positionAt(map.height - 1, map.width - 1, tableInfo.node),
35631
- activeRowIndex: activeCellRect.top,
35632
- activeColumnIndex: activeCellRect.left,
35633
- tableLeft,
35634
- tableTop,
35635
- tableWidth,
35636
- tableHeight,
35637
- wrapperLeft,
35638
- wrapperTop,
35639
- wrapperWidth,
35640
- wrapperHeight,
35641
- viewportWidth,
35642
- viewportHeight,
35643
- horizontalScrollbarHeight,
35644
- verticalScrollbarWidth,
35645
- avgRowHeight,
35646
- avgColumnWidth,
35647
- rowHandles,
35648
- columnHandles
35649
- };
35650
- }
35651
-
35652
36290
  // src/components/UEditor/table-controls.tsx
35653
36291
  import { Fragment as Fragment34, jsx as jsx91, jsxs as jsxs76 } from "react/jsx-runtime";
35654
36292
  var TABLE_MENU_TOP_OFFSET = 10;