@meta-1/editor 1.0.7 → 1.0.9
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/package.json +29 -29
- package/src/components/tiptap-node/paragraph-node/paragraph-node.css +12 -4
- package/src/components/tiptap-node/table-node/lib/tiptap-table-utils.ts +412 -499
- package/src/components/tiptap-node/table-node/ui/table-duplicate-row-column-button/use-table-duplicate-row-column.ts +2 -0
- package/src/lib/tiptap-advanced-utils.ts +1 -1
- package/src/lib/tiptap-utils.ts +1 -1
|
@@ -1,85 +1,80 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {
|
|
3
|
-
import type
|
|
4
|
-
import {
|
|
5
|
-
import type { FindNodeResult, Rect } from "@tiptap/pm/tables"
|
|
1
|
+
import type { Node } from "@tiptap/pm/model";
|
|
2
|
+
import type { Command } from "@tiptap/pm/state";
|
|
3
|
+
import { type EditorState, Selection, type Transaction } from "@tiptap/pm/state";
|
|
4
|
+
import type { FindNodeResult, Rect } from "@tiptap/pm/tables";
|
|
6
5
|
import {
|
|
7
|
-
TableMap,
|
|
8
6
|
CellSelection,
|
|
7
|
+
cellAround,
|
|
9
8
|
findTable,
|
|
9
|
+
isInTable,
|
|
10
10
|
selectedRect,
|
|
11
|
-
cellAround,
|
|
12
11
|
selectionCell,
|
|
13
|
-
|
|
14
|
-
} from "@tiptap/pm/tables"
|
|
15
|
-
import { Mapping } from "@tiptap/pm/transform"
|
|
12
|
+
TableMap,
|
|
13
|
+
} from "@tiptap/pm/tables";
|
|
14
|
+
import { Mapping } from "@tiptap/pm/transform";
|
|
15
|
+
import type { Editor } from "@tiptap/react";
|
|
16
16
|
|
|
17
|
-
export const RESIZE_MIN_WIDTH = 35
|
|
18
|
-
export const EMPTY_CELL_WIDTH = 120
|
|
19
|
-
export const EMPTY_CELL_HEIGHT = 40
|
|
17
|
+
export const RESIZE_MIN_WIDTH = 35;
|
|
18
|
+
export const EMPTY_CELL_WIDTH = 120;
|
|
19
|
+
export const EMPTY_CELL_HEIGHT = 40;
|
|
20
20
|
|
|
21
|
-
export type Orientation = "row" | "column"
|
|
21
|
+
export type Orientation = "row" | "column";
|
|
22
22
|
export interface CellInfo extends FindNodeResult {
|
|
23
|
-
row: number
|
|
24
|
-
column: number
|
|
23
|
+
row: number;
|
|
24
|
+
column: number;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
export type CellCoordinates = {
|
|
28
|
-
row: number
|
|
29
|
-
col: number
|
|
30
|
-
}
|
|
28
|
+
row: number;
|
|
29
|
+
col: number;
|
|
30
|
+
};
|
|
31
31
|
|
|
32
|
-
export type SelectionReturnMode = "state" | "transaction" | "dispatch"
|
|
32
|
+
export type SelectionReturnMode = "state" | "transaction" | "dispatch";
|
|
33
33
|
|
|
34
|
-
export type BaseSelectionOptions = { mode?: SelectionReturnMode }
|
|
34
|
+
export type BaseSelectionOptions = { mode?: SelectionReturnMode };
|
|
35
35
|
export type DispatchSelectionOptions = {
|
|
36
|
-
mode: "dispatch"
|
|
37
|
-
dispatch: (tr: Transaction) => void
|
|
38
|
-
}
|
|
39
|
-
export type TransactionSelectionOptions = { mode: "transaction" }
|
|
40
|
-
export type StateSelectionOptions = { mode?: "state" }
|
|
36
|
+
mode: "dispatch";
|
|
37
|
+
dispatch: (tr: Transaction) => void;
|
|
38
|
+
};
|
|
39
|
+
export type TransactionSelectionOptions = { mode: "transaction" };
|
|
40
|
+
export type StateSelectionOptions = { mode?: "state" };
|
|
41
41
|
|
|
42
42
|
export type TableInfo = {
|
|
43
|
-
map: TableMap
|
|
44
|
-
} & FindNodeResult
|
|
43
|
+
map: TableMap;
|
|
44
|
+
} & FindNodeResult;
|
|
45
45
|
|
|
46
46
|
// ============================================================================
|
|
47
47
|
// HELPER CONSTANTS & UTILITIES
|
|
48
48
|
// ============================================================================
|
|
49
49
|
|
|
50
|
-
const EMPTY_CELLS_RESULT = { cells: [], mergedCells: [] }
|
|
50
|
+
const EMPTY_CELLS_RESULT = { cells: [], mergedCells: [] };
|
|
51
51
|
|
|
52
52
|
export function isHTMLElement(n: unknown): n is HTMLElement {
|
|
53
|
-
return n instanceof HTMLElement
|
|
53
|
+
return n instanceof HTMLElement;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
export type DomCellAroundResult =
|
|
57
57
|
| {
|
|
58
|
-
type: "cell"
|
|
59
|
-
domNode: HTMLElement
|
|
60
|
-
tbodyNode: HTMLTableSectionElement | null
|
|
58
|
+
type: "cell";
|
|
59
|
+
domNode: HTMLElement;
|
|
60
|
+
tbodyNode: HTMLTableSectionElement | null;
|
|
61
61
|
}
|
|
62
62
|
| {
|
|
63
|
-
type: "wrapper"
|
|
64
|
-
domNode: HTMLElement
|
|
65
|
-
tbodyNode: HTMLTableSectionElement | null
|
|
66
|
-
}
|
|
63
|
+
type: "wrapper";
|
|
64
|
+
domNode: HTMLElement;
|
|
65
|
+
tbodyNode: HTMLTableSectionElement | null;
|
|
66
|
+
};
|
|
67
67
|
|
|
68
|
-
export function safeClosest<T extends Element>(
|
|
69
|
-
start
|
|
70
|
-
selector: string
|
|
71
|
-
): T | null {
|
|
72
|
-
return (start?.closest?.(selector) as T | null) ?? null
|
|
68
|
+
export function safeClosest<T extends Element>(start: Element | null, selector: string): T | null {
|
|
69
|
+
return (start?.closest?.(selector) as T | null) ?? null;
|
|
73
70
|
}
|
|
74
71
|
|
|
75
72
|
/**
|
|
76
73
|
* Walk up from an element until we find a TD/TH or the table wrapper.
|
|
77
74
|
* Returns the found element plus its tbody (if present).
|
|
78
75
|
*/
|
|
79
|
-
export function domCellAround(
|
|
80
|
-
|
|
81
|
-
): DomCellAroundResult | undefined {
|
|
82
|
-
let current: Element | null = target
|
|
76
|
+
export function domCellAround(target: Element): DomCellAroundResult | undefined {
|
|
77
|
+
let current: Element | null = target;
|
|
83
78
|
|
|
84
79
|
while (
|
|
85
80
|
current &&
|
|
@@ -87,41 +82,39 @@ export function domCellAround(
|
|
|
87
82
|
current.tagName !== "TH" &&
|
|
88
83
|
!current.classList.contains("tableWrapper")
|
|
89
84
|
) {
|
|
90
|
-
if (current.classList.contains("ProseMirror")) return undefined
|
|
91
|
-
current = isHTMLElement(current.parentNode)
|
|
92
|
-
? (current.parentNode as Element)
|
|
93
|
-
: null
|
|
85
|
+
if (current.classList.contains("ProseMirror")) return undefined;
|
|
86
|
+
current = isHTMLElement(current.parentNode) ? (current.parentNode as Element) : null;
|
|
94
87
|
}
|
|
95
88
|
|
|
96
|
-
if (!current) return undefined
|
|
89
|
+
if (!current) return undefined;
|
|
97
90
|
|
|
98
91
|
if (current.tagName === "TD" || current.tagName === "TH") {
|
|
99
92
|
return {
|
|
100
93
|
type: "cell",
|
|
101
94
|
domNode: current as HTMLElement,
|
|
102
95
|
tbodyNode: safeClosest<HTMLTableSectionElement>(current, "tbody"),
|
|
103
|
-
}
|
|
96
|
+
};
|
|
104
97
|
}
|
|
105
98
|
|
|
106
99
|
return {
|
|
107
100
|
type: "wrapper",
|
|
108
101
|
domNode: current as HTMLElement,
|
|
109
102
|
tbodyNode: (current as HTMLElement).querySelector("tbody"),
|
|
110
|
-
}
|
|
103
|
+
};
|
|
111
104
|
}
|
|
112
105
|
|
|
113
106
|
/**
|
|
114
107
|
* Clamps a value between min and max bounds
|
|
115
108
|
*/
|
|
116
109
|
export function clamp(value: number, min: number, max: number): number {
|
|
117
|
-
return Math.max(min, Math.min(value, max))
|
|
110
|
+
return Math.max(min, Math.min(value, max));
|
|
118
111
|
}
|
|
119
112
|
|
|
120
113
|
/**
|
|
121
114
|
* Validates if row/col indices are within table bounds
|
|
122
115
|
*/
|
|
123
116
|
function isWithinBounds(row: number, col: number, map: TableMap): boolean {
|
|
124
|
-
return row >= 0 && row < map.height && col >= 0 && col < map.width
|
|
117
|
+
return row >= 0 && row < map.height && col >= 0 && col < map.width;
|
|
125
118
|
}
|
|
126
119
|
|
|
127
120
|
/**
|
|
@@ -131,34 +124,29 @@ function resolveOrientationIndex(
|
|
|
131
124
|
state: EditorState,
|
|
132
125
|
table: TableInfo,
|
|
133
126
|
orientation: Orientation,
|
|
134
|
-
providedIndex?: number
|
|
127
|
+
providedIndex?: number,
|
|
135
128
|
): number | null {
|
|
136
129
|
if (typeof providedIndex === "number") {
|
|
137
|
-
return providedIndex
|
|
130
|
+
return providedIndex;
|
|
138
131
|
}
|
|
139
132
|
|
|
140
133
|
if (state.selection instanceof CellSelection) {
|
|
141
|
-
const rect = selectedRect(state)
|
|
142
|
-
return orientation === "row" ? rect.top : rect.left
|
|
134
|
+
const rect = selectedRect(state);
|
|
135
|
+
return orientation === "row" ? rect.top : rect.left;
|
|
143
136
|
}
|
|
144
137
|
|
|
145
|
-
const $cell = cellAround(state.selection.$anchor) ?? selectionCell(state)
|
|
146
|
-
if (!$cell) return null
|
|
138
|
+
const $cell = cellAround(state.selection.$anchor) ?? selectionCell(state);
|
|
139
|
+
if (!$cell) return null;
|
|
147
140
|
|
|
148
|
-
const rel = $cell.pos - table.start
|
|
149
|
-
const rect = table.map.findCell(rel)
|
|
150
|
-
return orientation === "row" ? rect.top : rect.left
|
|
141
|
+
const rel = $cell.pos - table.start;
|
|
142
|
+
const rect = table.map.findCell(rel);
|
|
143
|
+
return orientation === "row" ? rect.top : rect.left;
|
|
151
144
|
}
|
|
152
145
|
|
|
153
146
|
/**
|
|
154
147
|
* Creates a CellInfo object from position data
|
|
155
148
|
*/
|
|
156
|
-
function createCellInfo(
|
|
157
|
-
row: number,
|
|
158
|
-
column: number,
|
|
159
|
-
cellPos: number,
|
|
160
|
-
cellNode: Node
|
|
161
|
-
): CellInfo {
|
|
149
|
+
function createCellInfo(row: number, column: number, cellPos: number, cellNode: Node): CellInfo {
|
|
162
150
|
return {
|
|
163
151
|
row,
|
|
164
152
|
column,
|
|
@@ -166,17 +154,17 @@ function createCellInfo(
|
|
|
166
154
|
node: cellNode,
|
|
167
155
|
start: cellPos + 1,
|
|
168
156
|
depth: cellNode ? cellNode.content.size : 0,
|
|
169
|
-
}
|
|
157
|
+
};
|
|
170
158
|
}
|
|
171
159
|
|
|
172
160
|
/**
|
|
173
161
|
* Checks if a cell is merged (has colspan or rowspan > 1)
|
|
174
162
|
*/
|
|
175
163
|
export function isCellMerged(node: Node | null): boolean {
|
|
176
|
-
if (!node) return false
|
|
177
|
-
const colspan = node.attrs.colspan ?? 1
|
|
178
|
-
const rowspan = node.attrs.rowspan ?? 1
|
|
179
|
-
return colspan > 1 || rowspan > 1
|
|
164
|
+
if (!node) return false;
|
|
165
|
+
const colspan = node.attrs.colspan ?? 1;
|
|
166
|
+
const rowspan = node.attrs.rowspan ?? 1;
|
|
167
|
+
return colspan > 1 || rowspan > 1;
|
|
180
168
|
}
|
|
181
169
|
|
|
182
170
|
/**
|
|
@@ -186,110 +174,100 @@ function collectCells(
|
|
|
186
174
|
editor: Editor | null,
|
|
187
175
|
orientation: Orientation,
|
|
188
176
|
index?: number,
|
|
189
|
-
tablePos?: number
|
|
177
|
+
tablePos?: number,
|
|
190
178
|
): { cells: CellInfo[]; mergedCells: CellInfo[] } {
|
|
191
|
-
if (!editor) return EMPTY_CELLS_RESULT
|
|
179
|
+
if (!editor) return EMPTY_CELLS_RESULT;
|
|
192
180
|
|
|
193
|
-
const { state } = editor
|
|
194
|
-
const table = getTable(editor, tablePos)
|
|
195
|
-
if (!table) return EMPTY_CELLS_RESULT
|
|
181
|
+
const { state } = editor;
|
|
182
|
+
const table = getTable(editor, tablePos);
|
|
183
|
+
if (!table) return EMPTY_CELLS_RESULT;
|
|
196
184
|
|
|
197
|
-
const tableStart = table.start
|
|
198
|
-
const tableNode = table.node
|
|
199
|
-
const map = table.map
|
|
185
|
+
const tableStart = table.start;
|
|
186
|
+
const tableNode = table.node;
|
|
187
|
+
const map = table.map;
|
|
200
188
|
|
|
201
|
-
const resolvedIndex = resolveOrientationIndex(
|
|
202
|
-
|
|
203
|
-
table,
|
|
204
|
-
orientation,
|
|
205
|
-
index
|
|
206
|
-
)
|
|
207
|
-
if (resolvedIndex === null) return EMPTY_CELLS_RESULT
|
|
189
|
+
const resolvedIndex = resolveOrientationIndex(state, table, orientation, index);
|
|
190
|
+
if (resolvedIndex === null) return EMPTY_CELLS_RESULT;
|
|
208
191
|
|
|
209
192
|
// Bounds check
|
|
210
|
-
const maxIndex = orientation === "row" ? map.height : map.width
|
|
193
|
+
const maxIndex = orientation === "row" ? map.height : map.width;
|
|
211
194
|
if (resolvedIndex < 0 || resolvedIndex >= maxIndex) {
|
|
212
|
-
return EMPTY_CELLS_RESULT
|
|
195
|
+
return EMPTY_CELLS_RESULT;
|
|
213
196
|
}
|
|
214
197
|
|
|
215
|
-
const cells: CellInfo[] = []
|
|
216
|
-
const mergedCells: CellInfo[] = []
|
|
217
|
-
const seenMerged = new Set<number>()
|
|
198
|
+
const cells: CellInfo[] = [];
|
|
199
|
+
const mergedCells: CellInfo[] = [];
|
|
200
|
+
const seenMerged = new Set<number>();
|
|
218
201
|
|
|
219
|
-
const iterationCount = orientation === "row" ? map.width : map.height
|
|
202
|
+
const iterationCount = orientation === "row" ? map.width : map.height;
|
|
220
203
|
|
|
221
204
|
for (let i = 0; i < iterationCount; i++) {
|
|
222
|
-
const row = orientation === "row" ? resolvedIndex : i
|
|
223
|
-
const col = orientation === "row" ? i : resolvedIndex
|
|
224
|
-
const cellIndex = row * map.width + col
|
|
225
|
-
const mapCell = map.map[cellIndex]
|
|
205
|
+
const row = orientation === "row" ? resolvedIndex : i;
|
|
206
|
+
const col = orientation === "row" ? i : resolvedIndex;
|
|
207
|
+
const cellIndex = row * map.width + col;
|
|
208
|
+
const mapCell = map.map[cellIndex];
|
|
226
209
|
|
|
227
|
-
if (mapCell === undefined) continue
|
|
210
|
+
if (mapCell === undefined) continue;
|
|
228
211
|
|
|
229
|
-
const cellPos = tableStart + mapCell
|
|
230
|
-
const cellNode = tableNode.nodeAt(mapCell)
|
|
231
|
-
if (!cellNode) continue
|
|
212
|
+
const cellPos = tableStart + mapCell;
|
|
213
|
+
const cellNode = tableNode.nodeAt(mapCell);
|
|
214
|
+
if (!cellNode) continue;
|
|
232
215
|
|
|
233
|
-
const cell = createCellInfo(row, col, cellPos, cellNode)
|
|
216
|
+
const cell = createCellInfo(row, col, cellPos, cellNode);
|
|
234
217
|
|
|
235
218
|
if (isCellMerged(cellNode)) {
|
|
236
219
|
if (!seenMerged.has(cellPos)) {
|
|
237
|
-
mergedCells.push(cell)
|
|
238
|
-
seenMerged.add(cellPos)
|
|
220
|
+
mergedCells.push(cell);
|
|
221
|
+
seenMerged.add(cellPos);
|
|
239
222
|
}
|
|
240
223
|
}
|
|
241
224
|
|
|
242
|
-
cells.push(cell)
|
|
225
|
+
cells.push(cell);
|
|
243
226
|
}
|
|
244
227
|
|
|
245
|
-
return { cells, mergedCells }
|
|
228
|
+
return { cells, mergedCells };
|
|
246
229
|
}
|
|
247
230
|
|
|
248
231
|
/**
|
|
249
232
|
* Generic function to count empty cells from the end of a row or column
|
|
250
233
|
*/
|
|
251
|
-
function countEmptyCellsFromEnd(
|
|
252
|
-
editor
|
|
253
|
-
|
|
254
|
-
orientation: Orientation
|
|
255
|
-
): number {
|
|
256
|
-
const table = getTable(editor, tablePos)
|
|
257
|
-
if (!table) return 0
|
|
234
|
+
function countEmptyCellsFromEnd(editor: Editor, tablePos: number, orientation: Orientation): number {
|
|
235
|
+
const table = getTable(editor, tablePos);
|
|
236
|
+
if (!table) return 0;
|
|
258
237
|
|
|
259
|
-
const { doc } = editor.state
|
|
260
|
-
const maxIndex = orientation === "row" ? table.map.height : table.map.width
|
|
238
|
+
const { doc } = editor.state;
|
|
239
|
+
const maxIndex = orientation === "row" ? table.map.height : table.map.width;
|
|
261
240
|
|
|
262
|
-
let emptyCount = 0
|
|
241
|
+
let emptyCount = 0;
|
|
263
242
|
for (let idx = maxIndex - 1; idx >= 0; idx--) {
|
|
264
|
-
const seen = new Set<number>()
|
|
265
|
-
let isLineEmpty = true
|
|
243
|
+
const seen = new Set<number>();
|
|
244
|
+
let isLineEmpty = true;
|
|
266
245
|
|
|
267
|
-
const iterationCount =
|
|
268
|
-
orientation === "row" ? table.map.width : table.map.height
|
|
246
|
+
const iterationCount = orientation === "row" ? table.map.width : table.map.height;
|
|
269
247
|
|
|
270
248
|
for (let i = 0; i < iterationCount; i++) {
|
|
271
|
-
const row = orientation === "row" ? idx : i
|
|
272
|
-
const col = orientation === "row" ? i : idx
|
|
273
|
-
const rel = table.map.positionAt(row, col, table.node)
|
|
249
|
+
const row = orientation === "row" ? idx : i;
|
|
250
|
+
const col = orientation === "row" ? i : idx;
|
|
251
|
+
const rel = table.map.positionAt(row, col, table.node);
|
|
274
252
|
|
|
275
|
-
if (seen.has(rel)) continue
|
|
276
|
-
seen.add(rel)
|
|
253
|
+
if (seen.has(rel)) continue;
|
|
254
|
+
seen.add(rel);
|
|
277
255
|
|
|
278
|
-
const abs = tablePos + 1 + rel
|
|
279
|
-
const cell = doc.nodeAt(abs)
|
|
280
|
-
if (!cell) continue
|
|
256
|
+
const abs = tablePos + 1 + rel;
|
|
257
|
+
const cell = doc.nodeAt(abs);
|
|
258
|
+
if (!cell) continue;
|
|
281
259
|
|
|
282
260
|
if (!isCellEmpty(cell)) {
|
|
283
|
-
isLineEmpty = false
|
|
284
|
-
break
|
|
261
|
+
isLineEmpty = false;
|
|
262
|
+
break;
|
|
285
263
|
}
|
|
286
264
|
}
|
|
287
265
|
|
|
288
|
-
if (isLineEmpty) emptyCount
|
|
289
|
-
else break
|
|
266
|
+
if (isLineEmpty) emptyCount++;
|
|
267
|
+
else break;
|
|
290
268
|
}
|
|
291
269
|
|
|
292
|
-
return emptyCount
|
|
270
|
+
return emptyCount;
|
|
293
271
|
}
|
|
294
272
|
|
|
295
273
|
/**
|
|
@@ -307,34 +285,34 @@ function countEmptyCellsFromEnd(
|
|
|
307
285
|
* If no table is found, returns null.
|
|
308
286
|
*/
|
|
309
287
|
export function getTable(editor: Editor | null, tablePos?: number) {
|
|
310
|
-
if (!editor) return null
|
|
288
|
+
if (!editor) return null;
|
|
311
289
|
|
|
312
|
-
let table = null
|
|
290
|
+
let table: FindNodeResult | null | undefined = null;
|
|
313
291
|
|
|
314
292
|
if (typeof tablePos === "number") {
|
|
315
|
-
const tableNode = editor.state.doc.nodeAt(tablePos)
|
|
293
|
+
const tableNode = editor.state.doc.nodeAt(tablePos);
|
|
316
294
|
if (tableNode?.type.name === "table") {
|
|
317
295
|
table = {
|
|
318
296
|
node: tableNode,
|
|
319
297
|
pos: tablePos,
|
|
320
298
|
start: tablePos + 1,
|
|
321
299
|
depth: editor.state.doc.resolve(tablePos).depth,
|
|
322
|
-
}
|
|
300
|
+
};
|
|
323
301
|
}
|
|
324
302
|
}
|
|
325
303
|
|
|
326
304
|
if (!table) {
|
|
327
|
-
const { state } = editor
|
|
328
|
-
const $from = state.doc.resolve(state.selection.from)
|
|
329
|
-
table = findTable($from)
|
|
305
|
+
const { state } = editor;
|
|
306
|
+
const $from = state.doc.resolve(state.selection.from);
|
|
307
|
+
table = findTable($from);
|
|
330
308
|
}
|
|
331
309
|
|
|
332
|
-
if (!table) return null
|
|
310
|
+
if (!table) return null;
|
|
333
311
|
|
|
334
|
-
const tableMap = TableMap.get(table.node)
|
|
335
|
-
if (!tableMap) return null
|
|
312
|
+
const tableMap = TableMap.get(table.node);
|
|
313
|
+
if (!tableMap) return null;
|
|
336
314
|
|
|
337
|
-
return { ...table, map: tableMap }
|
|
315
|
+
return { ...table, map: tableMap };
|
|
338
316
|
}
|
|
339
317
|
|
|
340
318
|
/**
|
|
@@ -343,50 +321,47 @@ export function getTable(editor: Editor | null, tablePos?: number) {
|
|
|
343
321
|
* @returns true if the selection is inside a table cell; false otherwise
|
|
344
322
|
*/
|
|
345
323
|
export function isSelectionInCell(state: EditorState): boolean {
|
|
346
|
-
const { selection } = state
|
|
347
|
-
const $from = selection.$from
|
|
324
|
+
const { selection } = state;
|
|
325
|
+
const $from = selection.$from;
|
|
348
326
|
|
|
349
327
|
for (let depth = $from.depth; depth > 0; depth--) {
|
|
350
|
-
const node = $from.node(depth)
|
|
328
|
+
const node = $from.node(depth);
|
|
351
329
|
if (node.type.name === "tableCell" || node.type.name === "tableHeader") {
|
|
352
|
-
return true
|
|
330
|
+
return true;
|
|
353
331
|
}
|
|
354
332
|
}
|
|
355
333
|
|
|
356
|
-
return false
|
|
334
|
+
return false;
|
|
357
335
|
}
|
|
358
336
|
|
|
359
337
|
/**
|
|
360
338
|
* Cells overlap a rectangle if any of the cells in the rectangle are merged
|
|
361
339
|
* with cells outside the rectangle.
|
|
362
340
|
*/
|
|
363
|
-
export function cellsOverlapRectangle(
|
|
364
|
-
{ width, height, map }: TableMap,
|
|
365
|
-
rect: Rect
|
|
366
|
-
) {
|
|
341
|
+
export function cellsOverlapRectangle({ width, height, map }: TableMap, rect: Rect) {
|
|
367
342
|
let indexTop = rect.top * width + rect.left,
|
|
368
|
-
indexLeft = indexTop
|
|
343
|
+
indexLeft = indexTop;
|
|
369
344
|
let indexBottom = (rect.bottom - 1) * width + rect.left,
|
|
370
|
-
indexRight = indexTop + (rect.right - rect.left - 1)
|
|
345
|
+
indexRight = indexTop + (rect.right - rect.left - 1);
|
|
371
346
|
for (let i = rect.top; i < rect.bottom; i++) {
|
|
372
347
|
if (
|
|
373
|
-
(rect.left > 0 && map[indexLeft]
|
|
374
|
-
(rect.right < width && map[indexRight]
|
|
348
|
+
(rect.left > 0 && map[indexLeft] === map[indexLeft - 1]) ||
|
|
349
|
+
(rect.right < width && map[indexRight] === map[indexRight + 1])
|
|
375
350
|
)
|
|
376
|
-
return true
|
|
377
|
-
indexLeft += width
|
|
378
|
-
indexRight += width
|
|
351
|
+
return true;
|
|
352
|
+
indexLeft += width;
|
|
353
|
+
indexRight += width;
|
|
379
354
|
}
|
|
380
355
|
for (let i = rect.left; i < rect.right; i++) {
|
|
381
356
|
if (
|
|
382
|
-
(rect.top > 0 && map[indexTop]
|
|
383
|
-
(rect.bottom < height && map[indexBottom]
|
|
357
|
+
(rect.top > 0 && map[indexTop] === map[indexTop - width]) ||
|
|
358
|
+
(rect.bottom < height && map[indexBottom] === map[indexBottom + width])
|
|
384
359
|
)
|
|
385
|
-
return true
|
|
386
|
-
indexTop
|
|
387
|
-
indexBottom
|
|
360
|
+
return true;
|
|
361
|
+
indexTop++;
|
|
362
|
+
indexBottom++;
|
|
388
363
|
}
|
|
389
|
-
return false
|
|
364
|
+
return false;
|
|
390
365
|
}
|
|
391
366
|
|
|
392
367
|
/**
|
|
@@ -396,36 +371,36 @@ export function cellsOverlapRectangle(
|
|
|
396
371
|
* @returns True if the selection was successfully restored, false otherwise
|
|
397
372
|
*/
|
|
398
373
|
export function runPreservingCursor(editor: Editor, fn: () => void): boolean {
|
|
399
|
-
const view = editor.view
|
|
400
|
-
const startSel = view.state.selection
|
|
401
|
-
const bookmark = startSel.getBookmark()
|
|
374
|
+
const view = editor.view;
|
|
375
|
+
const startSel = view.state.selection;
|
|
376
|
+
const bookmark = startSel.getBookmark();
|
|
402
377
|
|
|
403
|
-
const mapping = new Mapping()
|
|
404
|
-
const originalDispatch = view.dispatch
|
|
378
|
+
const mapping = new Mapping();
|
|
379
|
+
const originalDispatch = view.dispatch;
|
|
405
380
|
|
|
406
381
|
view.dispatch = (tr) => {
|
|
407
|
-
mapping.appendMapping(tr.mapping)
|
|
408
|
-
originalDispatch(tr)
|
|
409
|
-
}
|
|
382
|
+
mapping.appendMapping(tr.mapping);
|
|
383
|
+
originalDispatch(tr);
|
|
384
|
+
};
|
|
410
385
|
|
|
411
386
|
try {
|
|
412
|
-
fn()
|
|
387
|
+
fn();
|
|
413
388
|
} finally {
|
|
414
|
-
view.dispatch = originalDispatch
|
|
389
|
+
view.dispatch = originalDispatch;
|
|
415
390
|
}
|
|
416
391
|
|
|
417
392
|
try {
|
|
418
|
-
const sel = bookmark.map(mapping).resolve(view.state.doc)
|
|
419
|
-
view.dispatch(view.state.tr.setSelection(sel))
|
|
420
|
-
return true
|
|
393
|
+
const sel = bookmark.map(mapping).resolve(view.state.doc);
|
|
394
|
+
view.dispatch(view.state.tr.setSelection(sel));
|
|
395
|
+
return true;
|
|
421
396
|
} catch {
|
|
422
397
|
// Fallback: if the exact spot vanished (e.g., cell deleted),
|
|
423
398
|
// go to the nearest valid position.
|
|
424
|
-
const mappedPos = mapping.map(startSel.from, -1)
|
|
425
|
-
const clamped = clamp(mappedPos, 0, view.state.doc.content.size)
|
|
426
|
-
const near = Selection.near(view.state.doc.resolve(clamped), -1)
|
|
427
|
-
view.dispatch(view.state.tr.setSelection(near))
|
|
428
|
-
return false
|
|
399
|
+
const mappedPos = mapping.map(startSel.from, -1);
|
|
400
|
+
const clamped = clamp(mappedPos, 0, view.state.doc.content.size);
|
|
401
|
+
const near = Selection.near(view.state.doc.resolve(clamped), -1);
|
|
402
|
+
view.dispatch(view.state.tr.setSelection(near));
|
|
403
|
+
return false;
|
|
429
404
|
}
|
|
430
405
|
}
|
|
431
406
|
|
|
@@ -443,22 +418,22 @@ export function runPreservingCursor(editor: Editor, fn: () => void): boolean {
|
|
|
443
418
|
* @returns true if the cell is empty; false otherwise
|
|
444
419
|
*/
|
|
445
420
|
export function isCellEmpty(cellNode: Node): boolean {
|
|
446
|
-
if (cellNode.childCount === 0) return true
|
|
421
|
+
if (cellNode.childCount === 0) return true;
|
|
447
422
|
|
|
448
|
-
let isEmpty = true
|
|
423
|
+
let isEmpty = true;
|
|
449
424
|
cellNode.descendants((n) => {
|
|
450
425
|
if (n.isText && n.text?.trim()) {
|
|
451
|
-
isEmpty = false
|
|
452
|
-
return false
|
|
426
|
+
isEmpty = false;
|
|
427
|
+
return false;
|
|
453
428
|
}
|
|
454
429
|
if (n.isLeaf && !n.isText) {
|
|
455
|
-
isEmpty = false
|
|
456
|
-
return false
|
|
430
|
+
isEmpty = false;
|
|
431
|
+
return false;
|
|
457
432
|
}
|
|
458
|
-
return true
|
|
459
|
-
})
|
|
433
|
+
return true;
|
|
434
|
+
});
|
|
460
435
|
|
|
461
|
-
return isEmpty
|
|
436
|
+
return isEmpty;
|
|
462
437
|
}
|
|
463
438
|
|
|
464
439
|
/**
|
|
@@ -475,36 +450,36 @@ export function getTableSelectionType(
|
|
|
475
450
|
editor: Editor | null,
|
|
476
451
|
index?: number,
|
|
477
452
|
orientation?: Orientation,
|
|
478
|
-
tablePos?: number
|
|
453
|
+
tablePos?: number,
|
|
479
454
|
): { orientation: Orientation; index: number } | null {
|
|
480
455
|
if (typeof index === "number" && orientation) {
|
|
481
|
-
return { orientation, index }
|
|
456
|
+
return { orientation, index };
|
|
482
457
|
}
|
|
483
458
|
|
|
484
|
-
if (!editor) return null
|
|
459
|
+
if (!editor) return null;
|
|
485
460
|
|
|
486
|
-
const { state } = editor
|
|
461
|
+
const { state } = editor;
|
|
487
462
|
|
|
488
|
-
const table = getTable(editor, tablePos)
|
|
489
|
-
if (!table) return null
|
|
463
|
+
const table = getTable(editor, tablePos);
|
|
464
|
+
if (!table) return null;
|
|
490
465
|
|
|
491
466
|
if (state.selection instanceof CellSelection) {
|
|
492
|
-
const rect = selectedRect(state)
|
|
493
|
-
const width = rect.right - rect.left
|
|
494
|
-
const height = rect.bottom - rect.top
|
|
467
|
+
const rect = selectedRect(state);
|
|
468
|
+
const width = rect.right - rect.left;
|
|
469
|
+
const height = rect.bottom - rect.top;
|
|
495
470
|
|
|
496
471
|
if (height === 1 && width >= 1) {
|
|
497
|
-
return { orientation: "row", index: rect.top }
|
|
472
|
+
return { orientation: "row", index: rect.top };
|
|
498
473
|
}
|
|
499
474
|
|
|
500
475
|
if (width === 1 && height >= 1) {
|
|
501
|
-
return { orientation: "column", index: rect.left }
|
|
476
|
+
return { orientation: "column", index: rect.left };
|
|
502
477
|
}
|
|
503
478
|
|
|
504
|
-
return null
|
|
479
|
+
return null;
|
|
505
480
|
}
|
|
506
481
|
|
|
507
|
-
return null
|
|
482
|
+
return null;
|
|
508
483
|
}
|
|
509
484
|
|
|
510
485
|
/**
|
|
@@ -523,13 +498,13 @@ export function getRowOrColumnCells(
|
|
|
523
498
|
editor: Editor | null,
|
|
524
499
|
index?: number,
|
|
525
500
|
orientation?: Orientation,
|
|
526
|
-
tablePos?: number
|
|
501
|
+
tablePos?: number,
|
|
527
502
|
): {
|
|
528
|
-
cells: CellInfo[]
|
|
529
|
-
mergedCells: CellInfo[]
|
|
530
|
-
index?: number
|
|
531
|
-
orientation?: Orientation
|
|
532
|
-
tablePos?: number
|
|
503
|
+
cells: CellInfo[];
|
|
504
|
+
mergedCells: CellInfo[];
|
|
505
|
+
index?: number;
|
|
506
|
+
orientation?: Orientation;
|
|
507
|
+
tablePos?: number;
|
|
533
508
|
} {
|
|
534
509
|
const emptyResult = {
|
|
535
510
|
cells: [],
|
|
@@ -537,36 +512,29 @@ export function getRowOrColumnCells(
|
|
|
537
512
|
index: undefined,
|
|
538
513
|
orientation: undefined,
|
|
539
514
|
tablePos: undefined,
|
|
540
|
-
}
|
|
515
|
+
};
|
|
541
516
|
|
|
542
517
|
if (!editor) {
|
|
543
|
-
return emptyResult
|
|
518
|
+
return emptyResult;
|
|
544
519
|
}
|
|
545
520
|
|
|
546
|
-
if (
|
|
547
|
-
|
|
548
|
-
!(editor.state.selection instanceof CellSelection)
|
|
549
|
-
) {
|
|
550
|
-
return emptyResult
|
|
521
|
+
if (typeof index !== "number" && !(editor.state.selection instanceof CellSelection)) {
|
|
522
|
+
return emptyResult;
|
|
551
523
|
}
|
|
552
524
|
|
|
553
|
-
let finalIndex = index
|
|
554
|
-
let finalOrientation = orientation
|
|
525
|
+
let finalIndex = index;
|
|
526
|
+
let finalOrientation = orientation;
|
|
555
527
|
|
|
556
|
-
if (
|
|
557
|
-
|
|
558
|
-
!
|
|
559
|
-
!["row", "column"].includes(finalOrientation)
|
|
560
|
-
) {
|
|
561
|
-
const selectionType = getTableSelectionType(editor)
|
|
562
|
-
if (!selectionType) return emptyResult
|
|
528
|
+
if (typeof finalIndex !== "number" || !finalOrientation || !["row", "column"].includes(finalOrientation)) {
|
|
529
|
+
const selectionType = getTableSelectionType(editor);
|
|
530
|
+
if (!selectionType) return emptyResult;
|
|
563
531
|
|
|
564
|
-
finalIndex = selectionType.index
|
|
565
|
-
finalOrientation = selectionType.orientation
|
|
532
|
+
finalIndex = selectionType.index;
|
|
533
|
+
finalOrientation = selectionType.orientation;
|
|
566
534
|
}
|
|
567
535
|
|
|
568
|
-
const result = collectCells(editor, finalOrientation, finalIndex, tablePos)
|
|
569
|
-
return { ...result, index: finalIndex, orientation: finalOrientation }
|
|
536
|
+
const result = collectCells(editor, finalOrientation, finalIndex, tablePos);
|
|
537
|
+
return { ...result, index: finalIndex, orientation: finalOrientation };
|
|
570
538
|
}
|
|
571
539
|
|
|
572
540
|
/**
|
|
@@ -577,9 +545,9 @@ export function getRowOrColumnCells(
|
|
|
577
545
|
export function getRowCells(
|
|
578
546
|
editor: Editor | null,
|
|
579
547
|
rowIndex?: number,
|
|
580
|
-
tablePos?: number
|
|
548
|
+
tablePos?: number,
|
|
581
549
|
): { cells: CellInfo[]; mergedCells: CellInfo[] } {
|
|
582
|
-
return collectCells(editor, "row", rowIndex, tablePos)
|
|
550
|
+
return collectCells(editor, "row", rowIndex, tablePos);
|
|
583
551
|
}
|
|
584
552
|
|
|
585
553
|
/**
|
|
@@ -590,9 +558,9 @@ export function getRowCells(
|
|
|
590
558
|
export function getColumnCells(
|
|
591
559
|
editor: Editor | null,
|
|
592
560
|
columnIndex?: number,
|
|
593
|
-
tablePos?: number
|
|
561
|
+
tablePos?: number,
|
|
594
562
|
): { cells: CellInfo[]; mergedCells: CellInfo[] } {
|
|
595
|
-
return collectCells(editor, "column", columnIndex, tablePos)
|
|
563
|
+
return collectCells(editor, "column", columnIndex, tablePos);
|
|
596
564
|
}
|
|
597
565
|
|
|
598
566
|
/**
|
|
@@ -610,60 +578,48 @@ export function updateSelectionAfterAction(
|
|
|
610
578
|
editor: Editor,
|
|
611
579
|
orientation: Orientation,
|
|
612
580
|
newIndex: number,
|
|
613
|
-
tablePos?: number
|
|
581
|
+
tablePos?: number,
|
|
614
582
|
): void {
|
|
615
583
|
try {
|
|
616
|
-
const table = getTable(editor, tablePos)
|
|
617
|
-
if (!table) return
|
|
584
|
+
const table = getTable(editor, tablePos);
|
|
585
|
+
if (!table) return;
|
|
618
586
|
|
|
619
|
-
const { state } = editor
|
|
620
|
-
const { map } = table
|
|
587
|
+
const { state } = editor;
|
|
588
|
+
const { map } = table;
|
|
621
589
|
|
|
622
590
|
if (orientation === "row") {
|
|
623
591
|
if (newIndex >= 0 && newIndex < map.height) {
|
|
624
|
-
const startCol = 0
|
|
625
|
-
const endCol = map.width - 1
|
|
626
|
-
|
|
627
|
-
const startCellPos =
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
const
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
state.doc,
|
|
637
|
-
$start.pos,
|
|
638
|
-
$end.pos
|
|
639
|
-
)
|
|
640
|
-
const tr = state.tr.setSelection(newSelection)
|
|
641
|
-
editor.view.dispatch(tr)
|
|
592
|
+
const startCol = 0;
|
|
593
|
+
const endCol = map.width - 1;
|
|
594
|
+
|
|
595
|
+
const startCellPos = table.start + map.positionAt(newIndex, startCol, table.node);
|
|
596
|
+
const endCellPos = table.start + map.positionAt(newIndex, endCol, table.node);
|
|
597
|
+
|
|
598
|
+
const $start = state.doc.resolve(startCellPos);
|
|
599
|
+
const $end = state.doc.resolve(endCellPos);
|
|
600
|
+
|
|
601
|
+
const newSelection = CellSelection.create(state.doc, $start.pos, $end.pos);
|
|
602
|
+
const tr = state.tr.setSelection(newSelection);
|
|
603
|
+
editor.view.dispatch(tr);
|
|
642
604
|
}
|
|
643
605
|
} else if (orientation === "column") {
|
|
644
606
|
if (newIndex >= 0 && newIndex < map.width) {
|
|
645
|
-
const startRow = 0
|
|
646
|
-
const endRow = map.height - 1
|
|
647
|
-
|
|
648
|
-
const startCellPos =
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
const
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
state.doc,
|
|
658
|
-
$start.pos,
|
|
659
|
-
$end.pos
|
|
660
|
-
)
|
|
661
|
-
const tr = state.tr.setSelection(newSelection)
|
|
662
|
-
editor.view.dispatch(tr)
|
|
607
|
+
const startRow = 0;
|
|
608
|
+
const endRow = map.height - 1;
|
|
609
|
+
|
|
610
|
+
const startCellPos = table.start + map.positionAt(startRow, newIndex, table.node);
|
|
611
|
+
const endCellPos = table.start + map.positionAt(endRow, newIndex, table.node);
|
|
612
|
+
|
|
613
|
+
const $start = state.doc.resolve(startCellPos);
|
|
614
|
+
const $end = state.doc.resolve(endCellPos);
|
|
615
|
+
|
|
616
|
+
const newSelection = CellSelection.create(state.doc, $start.pos, $end.pos);
|
|
617
|
+
const tr = state.tr.setSelection(newSelection);
|
|
618
|
+
editor.view.dispatch(tr);
|
|
663
619
|
}
|
|
664
620
|
}
|
|
665
621
|
} catch (error) {
|
|
666
|
-
console.warn("Failed to update selection after move:", error)
|
|
622
|
+
console.warn("Failed to update selection after move:", error);
|
|
667
623
|
}
|
|
668
624
|
}
|
|
669
625
|
|
|
@@ -674,50 +630,42 @@ export function updateSelectionAfterAction(
|
|
|
674
630
|
*
|
|
675
631
|
* @public
|
|
676
632
|
*/
|
|
677
|
-
export function setCellAttr(attrs: Record<string, unknown>): Command
|
|
678
|
-
export function setCellAttr(name: string, value: unknown): Command
|
|
679
|
-
export function setCellAttr(
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
const $cell = selectionCell(state)
|
|
686
|
-
|
|
687
|
-
const attrs =
|
|
688
|
-
typeof nameOrAttrs === "string" ? { [nameOrAttrs]: value } : nameOrAttrs
|
|
633
|
+
export function setCellAttr(attrs: Record<string, unknown>): Command;
|
|
634
|
+
export function setCellAttr(name: string, value: unknown): Command;
|
|
635
|
+
export function setCellAttr(nameOrAttrs: string | Record<string, unknown>, value?: unknown): Command {
|
|
636
|
+
return (state, dispatch) => {
|
|
637
|
+
if (!isInTable(state)) return false;
|
|
638
|
+
const $cell = selectionCell(state);
|
|
639
|
+
|
|
640
|
+
const attrs = typeof nameOrAttrs === "string" ? { [nameOrAttrs]: value } : nameOrAttrs;
|
|
689
641
|
|
|
690
642
|
if (dispatch) {
|
|
691
|
-
const tr = state.tr
|
|
643
|
+
const tr = state.tr;
|
|
692
644
|
if (state.selection instanceof CellSelection) {
|
|
693
645
|
state.selection.forEachCell((node, pos) => {
|
|
694
|
-
const needsUpdate = Object.entries(attrs).some(
|
|
695
|
-
([name, val]) => node.attrs[name] !== val
|
|
696
|
-
)
|
|
646
|
+
const needsUpdate = Object.entries(attrs).some(([name, val]) => node.attrs[name] !== val);
|
|
697
647
|
|
|
698
648
|
if (needsUpdate) {
|
|
699
649
|
tr.setNodeMarkup(pos, null, {
|
|
700
650
|
...node.attrs,
|
|
701
651
|
...attrs,
|
|
702
|
-
})
|
|
652
|
+
});
|
|
703
653
|
}
|
|
704
|
-
})
|
|
654
|
+
});
|
|
705
655
|
} else {
|
|
706
|
-
const needsUpdate = Object.entries(attrs).some(
|
|
707
|
-
([name, val]) => $cell.nodeAfter!.attrs[name] !== val
|
|
708
|
-
)
|
|
656
|
+
const needsUpdate = Object.entries(attrs).some(([name, val]) => $cell.nodeAfter!.attrs[name] !== val);
|
|
709
657
|
|
|
710
658
|
if (needsUpdate) {
|
|
711
659
|
tr.setNodeMarkup($cell.pos, null, {
|
|
712
660
|
...$cell.nodeAfter!.attrs,
|
|
713
661
|
...attrs,
|
|
714
|
-
})
|
|
662
|
+
});
|
|
715
663
|
}
|
|
716
664
|
}
|
|
717
|
-
dispatch(tr)
|
|
665
|
+
dispatch(tr);
|
|
718
666
|
}
|
|
719
|
-
return true
|
|
720
|
-
}
|
|
667
|
+
return true;
|
|
668
|
+
};
|
|
721
669
|
}
|
|
722
670
|
|
|
723
671
|
/**
|
|
@@ -733,11 +681,8 @@ export function setCellAttr(
|
|
|
733
681
|
* @param target - The table node instance to analyze (must be the same reference as in the doc)
|
|
734
682
|
* @returns The number of trailing empty rows (0 if table not found)
|
|
735
683
|
*/
|
|
736
|
-
export function countEmptyRowsFromEnd(
|
|
737
|
-
editor
|
|
738
|
-
tablePos: number
|
|
739
|
-
): number {
|
|
740
|
-
return countEmptyCellsFromEnd(editor, tablePos, "row")
|
|
684
|
+
export function countEmptyRowsFromEnd(editor: Editor, tablePos: number): number {
|
|
685
|
+
return countEmptyCellsFromEnd(editor, tablePos, "row");
|
|
741
686
|
}
|
|
742
687
|
|
|
743
688
|
/**
|
|
@@ -752,11 +697,8 @@ export function countEmptyRowsFromEnd(
|
|
|
752
697
|
* @param target - The table node instance to analyze (must be the same reference as in the doc)
|
|
753
698
|
* @returns The number of trailing empty columns (0 if table not found)
|
|
754
699
|
*/
|
|
755
|
-
export function countEmptyColumnsFromEnd(
|
|
756
|
-
editor
|
|
757
|
-
tablePos: number
|
|
758
|
-
): number {
|
|
759
|
-
return countEmptyCellsFromEnd(editor, tablePos, "column")
|
|
700
|
+
export function countEmptyColumnsFromEnd(editor: Editor, tablePos: number): number {
|
|
701
|
+
return countEmptyCellsFromEnd(editor, tablePos, "column");
|
|
760
702
|
}
|
|
761
703
|
|
|
762
704
|
/**
|
|
@@ -773,14 +715,14 @@ export function countEmptyColumnsFromEnd(
|
|
|
773
715
|
* @returns The rounded value using the dead-zone heuristic
|
|
774
716
|
*/
|
|
775
717
|
export function marginRound(num: number, margin = 0.3): number {
|
|
776
|
-
const floor = Math.floor(num)
|
|
777
|
-
const ceil = Math.ceil(num)
|
|
778
|
-
const lowerBound = floor + margin
|
|
779
|
-
const upperBound = ceil - margin
|
|
780
|
-
|
|
781
|
-
if (num < lowerBound) return floor
|
|
782
|
-
if (num > upperBound) return ceil
|
|
783
|
-
return Math.round(num)
|
|
718
|
+
const floor = Math.floor(num);
|
|
719
|
+
const ceil = Math.ceil(num);
|
|
720
|
+
const lowerBound = floor + margin;
|
|
721
|
+
const upperBound = ceil - margin;
|
|
722
|
+
|
|
723
|
+
if (num < lowerBound) return floor;
|
|
724
|
+
if (num > upperBound) return ceil;
|
|
725
|
+
return Math.round(num);
|
|
784
726
|
}
|
|
785
727
|
|
|
786
728
|
/**
|
|
@@ -794,14 +736,9 @@ export function marginRound(num: number, margin = 0.3): number {
|
|
|
794
736
|
* @returns true if both rects are equal or both are undefined; false otherwise
|
|
795
737
|
*/
|
|
796
738
|
export function rectEq(a?: DOMRect | null, b?: DOMRect | null): boolean {
|
|
797
|
-
if (!a && !b) return true
|
|
798
|
-
if (!a || !b) return false
|
|
799
|
-
return
|
|
800
|
-
a.left === b.left &&
|
|
801
|
-
a.top === b.top &&
|
|
802
|
-
a.width === b.width &&
|
|
803
|
-
a.height === b.height
|
|
804
|
-
)
|
|
739
|
+
if (!a && !b) return true;
|
|
740
|
+
if (!a || !b) return false;
|
|
741
|
+
return a.left === b.left && a.top === b.top && a.width === b.width && a.height === b.height;
|
|
805
742
|
}
|
|
806
743
|
|
|
807
744
|
/**
|
|
@@ -810,24 +747,24 @@ export function rectEq(a?: DOMRect | null, b?: DOMRect | null): boolean {
|
|
|
810
747
|
function applySelectionWithMode(
|
|
811
748
|
state: EditorState,
|
|
812
749
|
transaction: Transaction,
|
|
813
|
-
options: BaseSelectionOptions | DispatchSelectionOptions
|
|
814
|
-
): EditorState | Transaction |
|
|
815
|
-
const mode: SelectionReturnMode = options.mode ?? "state"
|
|
750
|
+
options: BaseSelectionOptions | DispatchSelectionOptions,
|
|
751
|
+
): EditorState | Transaction | undefined {
|
|
752
|
+
const mode: SelectionReturnMode = options.mode ?? "state";
|
|
816
753
|
|
|
817
754
|
switch (mode) {
|
|
818
755
|
case "dispatch": {
|
|
819
|
-
const dispatchOptions = options as DispatchSelectionOptions
|
|
756
|
+
const dispatchOptions = options as DispatchSelectionOptions;
|
|
820
757
|
if (typeof dispatchOptions.dispatch === "function") {
|
|
821
|
-
dispatchOptions.dispatch(transaction)
|
|
758
|
+
dispatchOptions.dispatch(transaction);
|
|
822
759
|
}
|
|
823
|
-
return
|
|
760
|
+
return;
|
|
824
761
|
}
|
|
825
762
|
|
|
826
763
|
case "transaction":
|
|
827
|
-
return transaction
|
|
764
|
+
return transaction;
|
|
828
765
|
|
|
829
766
|
default: // "state"
|
|
830
|
-
return state.apply(transaction)
|
|
767
|
+
return state.apply(transaction);
|
|
831
768
|
}
|
|
832
769
|
}
|
|
833
770
|
|
|
@@ -860,65 +797,55 @@ export function createTableCellSelection(
|
|
|
860
797
|
tablePosition: number,
|
|
861
798
|
startCell: CellCoordinates,
|
|
862
799
|
endCell?: CellCoordinates,
|
|
863
|
-
options?: StateSelectionOptions
|
|
864
|
-
): EditorState
|
|
800
|
+
options?: StateSelectionOptions,
|
|
801
|
+
): EditorState | undefined;
|
|
865
802
|
export function createTableCellSelection(
|
|
866
803
|
state: EditorState,
|
|
867
804
|
tablePosition: number,
|
|
868
805
|
startCell: CellCoordinates,
|
|
869
806
|
endCell: CellCoordinates | undefined,
|
|
870
|
-
options: TransactionSelectionOptions
|
|
871
|
-
): Transaction
|
|
807
|
+
options: TransactionSelectionOptions,
|
|
808
|
+
): Transaction | undefined;
|
|
872
809
|
export function createTableCellSelection(
|
|
873
810
|
state: EditorState,
|
|
874
811
|
tablePosition: number,
|
|
875
812
|
startCell: CellCoordinates,
|
|
876
813
|
endCell: CellCoordinates | undefined,
|
|
877
|
-
options: DispatchSelectionOptions
|
|
878
|
-
):
|
|
814
|
+
options: DispatchSelectionOptions,
|
|
815
|
+
): undefined;
|
|
879
816
|
|
|
880
817
|
export function createTableCellSelection(
|
|
881
818
|
state: EditorState,
|
|
882
819
|
tablePosition: number,
|
|
883
820
|
startCell: CellCoordinates,
|
|
884
821
|
endCell: CellCoordinates = startCell,
|
|
885
|
-
options: BaseSelectionOptions | DispatchSelectionOptions = { mode: "state" }
|
|
886
|
-
): EditorState | Transaction |
|
|
887
|
-
const startCellPosition = getCellPosition(state, tablePosition, startCell)
|
|
888
|
-
const endCellPosition = getCellPosition(state, tablePosition, endCell)
|
|
822
|
+
options: BaseSelectionOptions | DispatchSelectionOptions = { mode: "state" },
|
|
823
|
+
): EditorState | Transaction | undefined {
|
|
824
|
+
const startCellPosition = getCellPosition(state, tablePosition, startCell);
|
|
825
|
+
const endCellPosition = getCellPosition(state, tablePosition, endCell);
|
|
889
826
|
|
|
890
827
|
if (!startCellPosition || !endCellPosition) {
|
|
891
|
-
return
|
|
828
|
+
return;
|
|
892
829
|
}
|
|
893
830
|
|
|
894
|
-
const transaction = state.tr.setSelection(
|
|
895
|
-
new CellSelection(startCellPosition, endCellPosition)
|
|
896
|
-
)
|
|
831
|
+
const transaction = state.tr.setSelection(new CellSelection(startCellPosition, endCellPosition));
|
|
897
832
|
|
|
898
|
-
return applySelectionWithMode(state, transaction, options)
|
|
833
|
+
return applySelectionWithMode(state, transaction, options);
|
|
899
834
|
}
|
|
900
835
|
|
|
901
836
|
/**
|
|
902
837
|
* Get the position of a cell inside a table by relative row/col indices.
|
|
903
838
|
* Returns the position *before* the cell, which is what `CellSelection` expects.
|
|
904
839
|
*/
|
|
905
|
-
export function getCellPosition(
|
|
906
|
-
state
|
|
907
|
-
|
|
908
|
-
cellCoordinates
|
|
909
|
-
|
|
910
|
-
const
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
const resolvedColPosition = state.doc.resolve(
|
|
915
|
-
resolvedRowPosition.posAtIndex(cellCoordinates.col)
|
|
916
|
-
)
|
|
917
|
-
|
|
918
|
-
const $cell = cellAround(resolvedColPosition)
|
|
919
|
-
if (!$cell) return null
|
|
920
|
-
|
|
921
|
-
return resolvedColPosition
|
|
840
|
+
export function getCellPosition(state: EditorState, tablePosition: number, cellCoordinates: CellCoordinates) {
|
|
841
|
+
const resolvedTablePosition = state.doc.resolve(tablePosition);
|
|
842
|
+
const resolvedRowPosition = state.doc.resolve(resolvedTablePosition.posAtIndex(cellCoordinates.row) + 1);
|
|
843
|
+
const resolvedColPosition = state.doc.resolve(resolvedRowPosition.posAtIndex(cellCoordinates.col));
|
|
844
|
+
|
|
845
|
+
const $cell = cellAround(resolvedColPosition);
|
|
846
|
+
if (!$cell) return null;
|
|
847
|
+
|
|
848
|
+
return resolvedColPosition;
|
|
922
849
|
}
|
|
923
850
|
|
|
924
851
|
/**
|
|
@@ -938,108 +865,105 @@ export function selectCellsByCoords(
|
|
|
938
865
|
editor: Editor | null,
|
|
939
866
|
tablePos: number,
|
|
940
867
|
coords: { row: number; col: number }[],
|
|
941
|
-
options?: StateSelectionOptions
|
|
942
|
-
): EditorState
|
|
868
|
+
options?: StateSelectionOptions,
|
|
869
|
+
): EditorState | undefined;
|
|
943
870
|
export function selectCellsByCoords(
|
|
944
871
|
editor: Editor | null,
|
|
945
872
|
tablePos: number,
|
|
946
873
|
coords: { row: number; col: number }[],
|
|
947
|
-
options: TransactionSelectionOptions
|
|
948
|
-
): Transaction
|
|
874
|
+
options: TransactionSelectionOptions,
|
|
875
|
+
): Transaction | undefined;
|
|
949
876
|
export function selectCellsByCoords(
|
|
950
877
|
editor: Editor | null,
|
|
951
878
|
tablePos: number,
|
|
952
879
|
coords: { row: number; col: number }[],
|
|
953
|
-
options: DispatchSelectionOptions
|
|
954
|
-
):
|
|
880
|
+
options: DispatchSelectionOptions,
|
|
881
|
+
): undefined;
|
|
955
882
|
export function selectCellsByCoords(
|
|
956
883
|
editor: Editor | null,
|
|
957
884
|
tablePos: number,
|
|
958
885
|
coords: { row: number; col: number }[],
|
|
959
|
-
options: BaseSelectionOptions | DispatchSelectionOptions = { mode: "state" }
|
|
960
|
-
): EditorState | Transaction |
|
|
961
|
-
if (!editor) return
|
|
886
|
+
options: BaseSelectionOptions | DispatchSelectionOptions = { mode: "state" },
|
|
887
|
+
): EditorState | Transaction | undefined {
|
|
888
|
+
if (!editor) return;
|
|
962
889
|
|
|
963
|
-
const table = getTable(editor, tablePos)
|
|
964
|
-
if (!table) return
|
|
890
|
+
const table = getTable(editor, tablePos);
|
|
891
|
+
if (!table) return;
|
|
965
892
|
|
|
966
|
-
const { state } = editor
|
|
967
|
-
const tableMap = table.map
|
|
893
|
+
const { state } = editor;
|
|
894
|
+
const tableMap = table.map;
|
|
968
895
|
|
|
969
896
|
const cleanedCoords = coords
|
|
970
897
|
.map((coord) => ({
|
|
971
898
|
row: clamp(coord.row, 0, tableMap.height - 1),
|
|
972
899
|
col: clamp(coord.col, 0, tableMap.width - 1),
|
|
973
900
|
}))
|
|
974
|
-
.filter((coord) => isWithinBounds(coord.row, coord.col, tableMap))
|
|
901
|
+
.filter((coord) => isWithinBounds(coord.row, coord.col, tableMap));
|
|
975
902
|
|
|
976
903
|
if (cleanedCoords.length === 0) {
|
|
977
|
-
return
|
|
904
|
+
return;
|
|
978
905
|
}
|
|
979
906
|
|
|
980
907
|
// --- Find the smallest rectangle that contains all our coordinates ---
|
|
981
|
-
const allRows = cleanedCoords.map((coord) => coord.row)
|
|
982
|
-
const topRow = Math.min(...allRows)
|
|
983
|
-
const bottomRow = Math.max(...allRows)
|
|
908
|
+
const allRows = cleanedCoords.map((coord) => coord.row);
|
|
909
|
+
const topRow = Math.min(...allRows);
|
|
910
|
+
const bottomRow = Math.max(...allRows);
|
|
984
911
|
|
|
985
|
-
const allCols = cleanedCoords.map((coord) => coord.col)
|
|
986
|
-
const leftCol = Math.min(...allCols)
|
|
987
|
-
const rightCol = Math.max(...allCols)
|
|
912
|
+
const allCols = cleanedCoords.map((coord) => coord.col);
|
|
913
|
+
const leftCol = Math.min(...allCols);
|
|
914
|
+
const rightCol = Math.max(...allCols);
|
|
988
915
|
|
|
989
916
|
// --- Convert visual coordinates to document positions ---
|
|
990
917
|
// Use TableMap.map array directly to handle merged cells correctly
|
|
991
918
|
const getCellPositionFromMap = (row: number, col: number): number | null => {
|
|
992
919
|
// TableMap.map is a flat array where each entry represents a cell
|
|
993
920
|
// For merged cells, the same offset appears multiple times
|
|
994
|
-
const cellOffset = tableMap.map[row * tableMap.width + col]
|
|
995
|
-
if (cellOffset === undefined) return null
|
|
921
|
+
const cellOffset = tableMap.map[row * tableMap.width + col];
|
|
922
|
+
if (cellOffset === undefined) return null;
|
|
996
923
|
|
|
997
924
|
// Convert the relative offset to an absolute position in the document
|
|
998
925
|
// tablePos + 1 skips the table opening tag
|
|
999
|
-
return tablePos + 1 + cellOffset
|
|
1000
|
-
}
|
|
926
|
+
return tablePos + 1 + cellOffset;
|
|
927
|
+
};
|
|
1001
928
|
|
|
1002
929
|
// Anchor = where the selection starts (top-left of bounding box)
|
|
1003
|
-
const anchorPosition = getCellPositionFromMap(topRow, leftCol)
|
|
1004
|
-
if (anchorPosition === null) return
|
|
930
|
+
const anchorPosition = getCellPositionFromMap(topRow, leftCol);
|
|
931
|
+
if (anchorPosition === null) return;
|
|
1005
932
|
|
|
1006
933
|
// Head = where the selection ends (usually bottom-right of bounding box)
|
|
1007
|
-
let headPosition = getCellPositionFromMap(bottomRow, rightCol)
|
|
1008
|
-
if (headPosition === null) return
|
|
934
|
+
let headPosition = getCellPositionFromMap(bottomRow, rightCol);
|
|
935
|
+
if (headPosition === null) return;
|
|
1009
936
|
|
|
1010
|
-
// --- Handle edge case with merged cells ---
|
|
1011
|
-
// If anchor and head point to the same cell, we need to find a different head
|
|
1012
|
-
// This happens when selecting a single merged cell or when all coords point to one cell
|
|
1013
937
|
if (headPosition === anchorPosition) {
|
|
1014
|
-
|
|
938
|
+
// --- Handle edge case with merged cells ---
|
|
939
|
+
// If anchor and head point to the same cell, we need to find a different head
|
|
940
|
+
// This happens when selecting a single merged cell or when all coords point to one cell
|
|
941
|
+
let foundDifferentCell = false;
|
|
1015
942
|
|
|
1016
943
|
// Search backwards from bottom-right to find a cell with a different position
|
|
1017
944
|
for (let row = bottomRow; row >= topRow && !foundDifferentCell; row--) {
|
|
1018
945
|
for (let col = rightCol; col >= leftCol && !foundDifferentCell; col--) {
|
|
1019
|
-
const candidatePosition = getCellPositionFromMap(row, col)
|
|
1020
|
-
|
|
1021
|
-
if (
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
) {
|
|
1025
|
-
headPosition = candidatePosition
|
|
1026
|
-
foundDifferentCell = true
|
|
946
|
+
const candidatePosition = getCellPositionFromMap(row, col);
|
|
947
|
+
|
|
948
|
+
if (candidatePosition !== null && candidatePosition !== anchorPosition) {
|
|
949
|
+
headPosition = candidatePosition;
|
|
950
|
+
foundDifferentCell = true;
|
|
1027
951
|
}
|
|
1028
952
|
}
|
|
1029
953
|
}
|
|
1030
954
|
}
|
|
1031
955
|
|
|
1032
956
|
try {
|
|
1033
|
-
const anchorRef = state.doc.resolve(anchorPosition)
|
|
1034
|
-
const headRef = state.doc.resolve(headPosition)
|
|
957
|
+
const anchorRef = state.doc.resolve(anchorPosition);
|
|
958
|
+
const headRef = state.doc.resolve(headPosition);
|
|
1035
959
|
|
|
1036
|
-
const cellSelection = new CellSelection(anchorRef, headRef)
|
|
1037
|
-
const transaction = state.tr.setSelection(cellSelection)
|
|
960
|
+
const cellSelection = new CellSelection(anchorRef, headRef);
|
|
961
|
+
const transaction = state.tr.setSelection(cellSelection);
|
|
1038
962
|
|
|
1039
|
-
return applySelectionWithMode(state, transaction, options)
|
|
963
|
+
return applySelectionWithMode(state, transaction, options);
|
|
1040
964
|
} catch (error) {
|
|
1041
|
-
console.error("Failed to create cell selection:", error)
|
|
1042
|
-
return
|
|
965
|
+
console.error("Failed to create cell selection:", error);
|
|
966
|
+
return;
|
|
1043
967
|
}
|
|
1044
968
|
}
|
|
1045
969
|
|
|
@@ -1059,37 +983,37 @@ export function selectCellAt({
|
|
|
1059
983
|
tablePos,
|
|
1060
984
|
dispatch,
|
|
1061
985
|
}: {
|
|
1062
|
-
editor: Editor | null
|
|
1063
|
-
row: number
|
|
1064
|
-
col: number
|
|
1065
|
-
tablePos?: number
|
|
1066
|
-
dispatch?: (tr: Transaction) => void
|
|
986
|
+
editor: Editor | null;
|
|
987
|
+
row: number;
|
|
988
|
+
col: number;
|
|
989
|
+
tablePos?: number;
|
|
990
|
+
dispatch?: (tr: Transaction) => void;
|
|
1067
991
|
}): boolean {
|
|
1068
|
-
if (!editor) return false
|
|
992
|
+
if (!editor) return false;
|
|
1069
993
|
|
|
1070
|
-
const { state, view } = editor
|
|
1071
|
-
const found = getTable(editor, tablePos)
|
|
1072
|
-
if (!found) return false
|
|
994
|
+
const { state, view } = editor;
|
|
995
|
+
const found = getTable(editor, tablePos);
|
|
996
|
+
if (!found) return false;
|
|
1073
997
|
|
|
1074
|
-
// Bounds check
|
|
1075
998
|
if (!isWithinBounds(row, col, found.map)) {
|
|
1076
|
-
|
|
999
|
+
// Bounds check
|
|
1000
|
+
return false;
|
|
1077
1001
|
}
|
|
1078
1002
|
|
|
1079
|
-
const relCellPos = found.map.positionAt(row, col, found.node)
|
|
1080
|
-
const absCellPos = found.start + relCellPos
|
|
1003
|
+
const relCellPos = found.map.positionAt(row, col, found.node);
|
|
1004
|
+
const absCellPos = found.start + relCellPos;
|
|
1081
1005
|
|
|
1082
|
-
const $abs = state.doc.resolve(absCellPos)
|
|
1083
|
-
const $cell = cellAround($abs)
|
|
1084
|
-
const cellPos = $cell ? $cell.pos : absCellPos
|
|
1006
|
+
const $abs = state.doc.resolve(absCellPos);
|
|
1007
|
+
const $cell = cellAround($abs);
|
|
1008
|
+
const cellPos = $cell ? $cell.pos : absCellPos;
|
|
1085
1009
|
|
|
1086
|
-
const sel = CellSelection.create(state.doc, cellPos)
|
|
1010
|
+
const sel = CellSelection.create(state.doc, cellPos);
|
|
1087
1011
|
|
|
1088
|
-
const doDispatch = dispatch ?? view?.dispatch
|
|
1089
|
-
if (!doDispatch) return false
|
|
1012
|
+
const doDispatch = dispatch ?? view?.dispatch;
|
|
1013
|
+
if (!doDispatch) return false;
|
|
1090
1014
|
|
|
1091
|
-
doDispatch(state.tr.setSelection(sel))
|
|
1092
|
-
return true
|
|
1015
|
+
doDispatch(state.tr.setSelection(sel));
|
|
1016
|
+
return true;
|
|
1093
1017
|
}
|
|
1094
1018
|
|
|
1095
1019
|
/**
|
|
@@ -1106,38 +1030,33 @@ export function selectCellAt({
|
|
|
1106
1030
|
* @param orientation "row" to select bottom-left, "column" to select top-right
|
|
1107
1031
|
* @returns true if the selection was successful; false otherwise
|
|
1108
1032
|
*/
|
|
1109
|
-
export function selectLastCell(
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
tablePos: number,
|
|
1113
|
-
orientation: Orientation
|
|
1114
|
-
) {
|
|
1115
|
-
const map = TableMap.get(tableNode)
|
|
1116
|
-
const isRow = orientation === "row"
|
|
1033
|
+
export function selectLastCell(editor: Editor, tableNode: Node, tablePos: number, orientation: Orientation) {
|
|
1034
|
+
const map = TableMap.get(tableNode);
|
|
1035
|
+
const isRow = orientation === "row";
|
|
1117
1036
|
|
|
1118
1037
|
// For rows, select bottom-left cell; for columns, select top-right cell
|
|
1119
|
-
const row = isRow ? map.height - 1 : 0
|
|
1120
|
-
const col = isRow ? 0 : map.width - 1
|
|
1038
|
+
const row = isRow ? map.height - 1 : 0;
|
|
1039
|
+
const col = isRow ? 0 : map.width - 1;
|
|
1121
1040
|
|
|
1122
1041
|
// Calculate the index in the table map
|
|
1123
|
-
const index = row * map.width + col
|
|
1042
|
+
const index = row * map.width + col;
|
|
1124
1043
|
|
|
1125
1044
|
// Get the actual cell position from the map (handles merged cells)
|
|
1126
|
-
const cellPos = map.map[index]
|
|
1045
|
+
const cellPos = map.map[index];
|
|
1127
1046
|
if (!cellPos && cellPos !== 0) {
|
|
1128
1047
|
console.warn("selectLastCell: cell position not found in map", {
|
|
1129
1048
|
index,
|
|
1130
1049
|
row,
|
|
1131
1050
|
col,
|
|
1132
1051
|
map,
|
|
1133
|
-
})
|
|
1134
|
-
return false
|
|
1052
|
+
});
|
|
1053
|
+
return false;
|
|
1135
1054
|
}
|
|
1136
1055
|
|
|
1137
1056
|
// Find the row and column of the actual cell
|
|
1138
|
-
const cellIndex = map.map.indexOf(cellPos)
|
|
1139
|
-
const actualRow = cellIndex >= 0 ? Math.floor(cellIndex / map.width) : 0
|
|
1140
|
-
const actualCol = cellIndex >= 0 ? cellIndex % map.width : 0
|
|
1057
|
+
const cellIndex = map.map.indexOf(cellPos);
|
|
1058
|
+
const actualRow = cellIndex >= 0 ? Math.floor(cellIndex / map.width) : 0;
|
|
1059
|
+
const actualCol = cellIndex >= 0 ? cellIndex % map.width : 0;
|
|
1141
1060
|
|
|
1142
1061
|
return selectCellAt({
|
|
1143
1062
|
editor,
|
|
@@ -1145,7 +1064,7 @@ export function selectLastCell(
|
|
|
1145
1064
|
col: actualCol,
|
|
1146
1065
|
tablePos,
|
|
1147
1066
|
dispatch: editor.view.dispatch.bind(editor.view),
|
|
1148
|
-
})
|
|
1067
|
+
});
|
|
1149
1068
|
}
|
|
1150
1069
|
|
|
1151
1070
|
/**
|
|
@@ -1170,26 +1089,26 @@ export function getIndexCoordinates({
|
|
|
1170
1089
|
orientation,
|
|
1171
1090
|
tablePos,
|
|
1172
1091
|
}: {
|
|
1173
|
-
editor: Editor | null
|
|
1174
|
-
index: number
|
|
1175
|
-
orientation?: Orientation
|
|
1176
|
-
tablePos?: number
|
|
1092
|
+
editor: Editor | null;
|
|
1093
|
+
index: number;
|
|
1094
|
+
orientation?: Orientation;
|
|
1095
|
+
tablePos?: number;
|
|
1177
1096
|
}): { row: number; col: number }[] | null {
|
|
1178
|
-
if (!editor) return null
|
|
1097
|
+
if (!editor) return null;
|
|
1179
1098
|
|
|
1180
|
-
const table = getTable(editor, tablePos)
|
|
1181
|
-
if (!table) return null
|
|
1099
|
+
const table = getTable(editor, tablePos);
|
|
1100
|
+
if (!table) return null;
|
|
1182
1101
|
|
|
1183
|
-
const { map } = table
|
|
1184
|
-
const { width, height } = map
|
|
1102
|
+
const { map } = table;
|
|
1103
|
+
const { width, height } = map;
|
|
1185
1104
|
|
|
1186
|
-
if (index < 0) return null
|
|
1187
|
-
if (orientation === "row" && index >= height) return null
|
|
1188
|
-
if (orientation === "column" && index >= width) return null
|
|
1105
|
+
if (index < 0) return null;
|
|
1106
|
+
if (orientation === "row" && index >= height) return null;
|
|
1107
|
+
if (orientation === "column" && index >= width) return null;
|
|
1189
1108
|
|
|
1190
1109
|
return orientation === "row"
|
|
1191
1110
|
? Array.from({ length: map.width }, (_, col) => ({ row: index, col }))
|
|
1192
|
-
: Array.from({ length: map.height }, (_, row) => ({ row, col: index }))
|
|
1111
|
+
: Array.from({ length: map.height }, (_, row) => ({ row, col: index }));
|
|
1193
1112
|
}
|
|
1194
1113
|
|
|
1195
1114
|
/**
|
|
@@ -1213,33 +1132,33 @@ export function getIndexCoordinates({
|
|
|
1213
1132
|
export function getCellIndicesFromDOM(
|
|
1214
1133
|
cell: HTMLTableCellElement,
|
|
1215
1134
|
tableNode: Node | null,
|
|
1216
|
-
editor: Editor
|
|
1135
|
+
editor: Editor,
|
|
1217
1136
|
): { rowIndex: number; colIndex: number } | null {
|
|
1218
|
-
if (!tableNode) return null
|
|
1137
|
+
if (!tableNode) return null;
|
|
1219
1138
|
|
|
1220
1139
|
try {
|
|
1221
|
-
const cellPos = editor.view.posAtDOM(cell, 0)
|
|
1222
|
-
const $cellPos = editor.view.state.doc.resolve(cellPos)
|
|
1140
|
+
const cellPos = editor.view.posAtDOM(cell, 0);
|
|
1141
|
+
const $cellPos = editor.view.state.doc.resolve(cellPos);
|
|
1223
1142
|
|
|
1224
1143
|
for (let d = $cellPos.depth; d > 0; d--) {
|
|
1225
|
-
const node = $cellPos.node(d)
|
|
1144
|
+
const node = $cellPos.node(d);
|
|
1226
1145
|
if (node.type.name === "tableCell" || node.type.name === "tableHeader") {
|
|
1227
|
-
const tableMap = TableMap.get(tableNode)
|
|
1228
|
-
const cellNodePos = $cellPos.before(d)
|
|
1229
|
-
const tableStart = $cellPos.start(d - 2)
|
|
1230
|
-
const cellOffset = cellNodePos - tableStart
|
|
1231
|
-
const cellIndex = tableMap.map.indexOf(cellOffset)
|
|
1146
|
+
const tableMap = TableMap.get(tableNode);
|
|
1147
|
+
const cellNodePos = $cellPos.before(d);
|
|
1148
|
+
const tableStart = $cellPos.start(d - 2);
|
|
1149
|
+
const cellOffset = cellNodePos - tableStart;
|
|
1150
|
+
const cellIndex = tableMap.map.indexOf(cellOffset);
|
|
1232
1151
|
|
|
1233
1152
|
return {
|
|
1234
1153
|
rowIndex: Math.floor(cellIndex / tableMap.width),
|
|
1235
1154
|
colIndex: cellIndex % tableMap.width,
|
|
1236
|
-
}
|
|
1155
|
+
};
|
|
1237
1156
|
}
|
|
1238
1157
|
}
|
|
1239
1158
|
} catch (error) {
|
|
1240
|
-
console.warn("Could not get cell position:", error)
|
|
1159
|
+
console.warn("Could not get cell position:", error);
|
|
1241
1160
|
}
|
|
1242
|
-
return null
|
|
1161
|
+
return null;
|
|
1243
1162
|
}
|
|
1244
1163
|
|
|
1245
1164
|
/**
|
|
@@ -1258,32 +1177,26 @@ export function getCellIndicesFromDOM(
|
|
|
1258
1177
|
* @param editor The Tiptap editor instance
|
|
1259
1178
|
* @returns An object with { node: tableNode, pos: tablePos } or null if not found
|
|
1260
1179
|
*/
|
|
1261
|
-
export function getTableFromDOM(
|
|
1262
|
-
tableElement: HTMLElement,
|
|
1263
|
-
editor: Editor
|
|
1264
|
-
): { node: Node; pos: number } | null {
|
|
1180
|
+
export function getTableFromDOM(tableElement: HTMLElement, editor: Editor): { node: Node; pos: number } | null {
|
|
1265
1181
|
try {
|
|
1266
|
-
const pos = editor.view.posAtDOM(tableElement, 0)
|
|
1267
|
-
const $pos = editor.view.state.doc.resolve(pos)
|
|
1182
|
+
const pos = editor.view.posAtDOM(tableElement, 0);
|
|
1183
|
+
const $pos = editor.view.state.doc.resolve(pos);
|
|
1268
1184
|
|
|
1269
1185
|
for (let d = $pos.depth; d >= 0; d--) {
|
|
1270
|
-
const node = $pos.node(d)
|
|
1186
|
+
const node = $pos.node(d);
|
|
1271
1187
|
if (isTableNode(node)) {
|
|
1272
|
-
return { node, pos: d === 0 ? 0 : $pos.before(d) }
|
|
1188
|
+
return { node, pos: d === 0 ? 0 : $pos.before(d) };
|
|
1273
1189
|
}
|
|
1274
1190
|
}
|
|
1275
1191
|
} catch (error) {
|
|
1276
|
-
console.warn("Could not get table from DOM:", error)
|
|
1192
|
+
console.warn("Could not get table from DOM:", error);
|
|
1277
1193
|
}
|
|
1278
|
-
return null
|
|
1194
|
+
return null;
|
|
1279
1195
|
}
|
|
1280
1196
|
|
|
1281
1197
|
/**
|
|
1282
1198
|
* Checks if a node is a table node
|
|
1283
1199
|
*/
|
|
1284
1200
|
export function isTableNode(node: Node | null | undefined): node is Node {
|
|
1285
|
-
return (
|
|
1286
|
-
!!node &&
|
|
1287
|
-
(node.type.name === "table" || node.type.spec.tableRole === "table")
|
|
1288
|
-
)
|
|
1201
|
+
return !!node && (node.type.name === "table" || node.type.spec.tableRole === "table");
|
|
1289
1202
|
}
|