@ctzhian/tiptap 1.13.8 → 2.0.0
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/Editor/demo.js +3 -5
- package/dist/Editor/index.js +17 -0
- package/dist/component/CustomBubbleMenu/index.js +1 -1
- package/dist/component/CustomDragHandle/index.js +3 -59
- package/dist/component/Icons/delete-back-2-line-icon.d.ts +6 -0
- package/dist/component/Icons/delete-back-2-line-icon.js +13 -0
- package/dist/component/Menu/index.js +5 -1
- package/dist/contants/enums.d.ts +9 -0
- package/dist/contants/enums.js +61 -1
- package/dist/extension/component/Link/Insert.js +1 -1
- package/dist/extension/component/TableCellHandleMenu/index.d.ts +9 -0
- package/dist/extension/component/TableCellHandleMenu/index.js +443 -0
- package/dist/extension/component/TableExtendButton/TableExtendButton.css +30 -0
- package/dist/extension/component/TableExtendButton/index.d.ts +23 -0
- package/dist/extension/component/TableExtendButton/index.js +201 -0
- package/dist/extension/component/TableExtendButton/use-table-extend-row-column.d.ts +15 -0
- package/dist/extension/component/TableExtendButton/use-table-extend-row-column.js +87 -0
- package/dist/extension/component/TableHandle/TableHandleMenu.css +36 -0
- package/dist/extension/component/TableHandle/TableHandleMenu.d.ts +17 -0
- package/dist/extension/component/TableHandle/TableHandleMenu.js +685 -0
- package/dist/extension/component/TableHandle/index.d.ts +28 -0
- package/dist/extension/component/TableHandle/index.js +93 -0
- package/dist/extension/component/TableHandle/use-table-handle-positioning.d.ts +40 -0
- package/dist/extension/component/TableHandle/use-table-handle-positioning.js +193 -0
- package/dist/extension/component/TableHandle/use-table-handle-state.d.ts +22 -0
- package/dist/extension/component/TableHandle/use-table-handle-state.js +45 -0
- package/dist/extension/component/TableSelectionOverlay/index.d.ts +16 -0
- package/dist/extension/component/TableSelectionOverlay/index.js +460 -0
- package/dist/extension/node/FileHandler.d.ts +1 -1
- package/dist/extension/node/Link/index.js +4 -4
- package/dist/extension/node/Table.js +226 -43
- package/dist/extension/node/TableHandler/create-image.d.ts +9 -0
- package/dist/extension/node/TableHandler/create-image.js +235 -0
- package/dist/extension/node/TableHandler/index.d.ts +15 -0
- package/dist/extension/node/TableHandler/index.js +33 -0
- package/dist/extension/node/TableHandler/plugin.d.ts +49 -0
- package/dist/extension/node/TableHandler/plugin.js +1030 -0
- package/dist/index.css +29 -10
- package/dist/type/index.d.ts +2 -0
- package/dist/util/table-utils.d.ts +161 -0
- package/dist/util/table-utils.js +605 -0
- package/package.json +34 -33
- package/dist/extension/component/Table/ContextMenu.d.ts +0 -11
- package/dist/extension/component/Table/ContextMenu.js +0 -186
- package/dist/extension/component/Table/TableContextMenuPlugin.d.ts +0 -9
- package/dist/extension/component/Table/TableContextMenuPlugin.js +0 -336
- package/dist/extension/component/Table/index.d.ts +0 -2
- package/dist/extension/component/Table/index.js +0 -2
|
@@ -0,0 +1,605 @@
|
|
|
1
|
+
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
2
|
+
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
|
|
3
|
+
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
|
4
|
+
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
|
|
5
|
+
function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); }
|
|
6
|
+
function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
|
|
7
|
+
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
|
|
8
|
+
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
9
|
+
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
10
|
+
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
11
|
+
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
|
|
12
|
+
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
13
|
+
import { Selection } from '@tiptap/pm/state';
|
|
14
|
+
import { cellAround, CellSelection, findTable, selectedRect, selectionCell, TableMap } from '@tiptap/pm/tables';
|
|
15
|
+
import { Mapping } from '@tiptap/pm/transform';
|
|
16
|
+
export var RESIZE_MIN_WIDTH = 35;
|
|
17
|
+
export var EMPTY_CELL_WIDTH = 120;
|
|
18
|
+
export var EMPTY_CELL_HEIGHT = 40;
|
|
19
|
+
// ============================================================================
|
|
20
|
+
// HELPER CONSTANTS & UTILITIES
|
|
21
|
+
// ============================================================================
|
|
22
|
+
|
|
23
|
+
var EMPTY_CELLS_RESULT = {
|
|
24
|
+
cells: [],
|
|
25
|
+
mergedCells: []
|
|
26
|
+
};
|
|
27
|
+
export function isHTMLElement(n) {
|
|
28
|
+
return n instanceof HTMLElement;
|
|
29
|
+
}
|
|
30
|
+
export function safeClosest(start, selector) {
|
|
31
|
+
var _ref, _start$closest;
|
|
32
|
+
return (_ref = start === null || start === void 0 || (_start$closest = start.closest) === null || _start$closest === void 0 ? void 0 : _start$closest.call(start, selector)) !== null && _ref !== void 0 ? _ref : null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Walk up from an element until we find a TD/TH or the table wrapper.
|
|
37
|
+
* Returns the found element plus its tbody (if present).
|
|
38
|
+
*/
|
|
39
|
+
export function domCellAround(target) {
|
|
40
|
+
var current = target;
|
|
41
|
+
while (current && current.tagName !== 'TD' && current.tagName !== 'TH' && !current.classList.contains('tableWrapper')) {
|
|
42
|
+
if (current.classList.contains('ProseMirror')) return undefined;
|
|
43
|
+
current = isHTMLElement(current.parentNode) ? current.parentNode : null;
|
|
44
|
+
}
|
|
45
|
+
if (!current) return undefined;
|
|
46
|
+
if (current.tagName === 'TD' || current.tagName === 'TH') {
|
|
47
|
+
return {
|
|
48
|
+
type: 'cell',
|
|
49
|
+
domNode: current,
|
|
50
|
+
tbodyNode: safeClosest(current, 'tbody')
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
type: 'wrapper',
|
|
55
|
+
domNode: current,
|
|
56
|
+
tbodyNode: current.querySelector('tbody')
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Clamps a value between min and max bounds
|
|
62
|
+
*/
|
|
63
|
+
export function clamp(value, min, max) {
|
|
64
|
+
return Math.max(min, Math.min(value, max));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Validates if row/col indices are within table bounds
|
|
69
|
+
*/
|
|
70
|
+
function isWithinBounds(row, col, map) {
|
|
71
|
+
return row >= 0 && row < map.height && col >= 0 && col < map.width;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Resolves the index for a row or column based on current selection or provided value
|
|
76
|
+
*/
|
|
77
|
+
function resolveOrientationIndex(state, table, orientation, providedIndex) {
|
|
78
|
+
var _cellAround;
|
|
79
|
+
if (typeof providedIndex === 'number') {
|
|
80
|
+
return providedIndex;
|
|
81
|
+
}
|
|
82
|
+
if (state.selection instanceof CellSelection) {
|
|
83
|
+
var _rect = selectedRect(state);
|
|
84
|
+
return orientation === 'row' ? _rect.top : _rect.left;
|
|
85
|
+
}
|
|
86
|
+
var $cell = (_cellAround = cellAround(state.selection.$anchor)) !== null && _cellAround !== void 0 ? _cellAround : selectionCell(state);
|
|
87
|
+
if (!$cell) return null;
|
|
88
|
+
var rel = $cell.pos - table.start;
|
|
89
|
+
var rect = table.map.findCell(rel);
|
|
90
|
+
return orientation === 'row' ? rect.top : rect.left;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Creates a CellInfo object from position data
|
|
95
|
+
*/
|
|
96
|
+
function createCellInfo(row, column, cellPos, cellNode) {
|
|
97
|
+
return {
|
|
98
|
+
row: row,
|
|
99
|
+
column: column,
|
|
100
|
+
pos: cellPos,
|
|
101
|
+
node: cellNode,
|
|
102
|
+
start: cellPos + 1,
|
|
103
|
+
depth: cellNode ? cellNode.content.size : 0
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Checks if a cell is merged (has colspan or rowspan > 1)
|
|
109
|
+
*/
|
|
110
|
+
export function isCellMerged(node) {
|
|
111
|
+
var _node$attrs$colspan, _node$attrs$rowspan;
|
|
112
|
+
if (!node) return false;
|
|
113
|
+
var colspan = (_node$attrs$colspan = node.attrs.colspan) !== null && _node$attrs$colspan !== void 0 ? _node$attrs$colspan : 1;
|
|
114
|
+
var rowspan = (_node$attrs$rowspan = node.attrs.rowspan) !== null && _node$attrs$rowspan !== void 0 ? _node$attrs$rowspan : 1;
|
|
115
|
+
return colspan > 1 || rowspan > 1;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Generic function to collect cells along a row or column
|
|
120
|
+
*/
|
|
121
|
+
function collectCells(editor, orientation, index, tablePos) {
|
|
122
|
+
if (!editor) return EMPTY_CELLS_RESULT;
|
|
123
|
+
var state = editor.state;
|
|
124
|
+
var table = getTable(editor, tablePos);
|
|
125
|
+
if (!table) return EMPTY_CELLS_RESULT;
|
|
126
|
+
var tableStart = table.start;
|
|
127
|
+
var tableNode = table.node;
|
|
128
|
+
var map = table.map;
|
|
129
|
+
var resolvedIndex = resolveOrientationIndex(state, table, orientation, index);
|
|
130
|
+
if (resolvedIndex === null) return EMPTY_CELLS_RESULT;
|
|
131
|
+
|
|
132
|
+
// Bounds check
|
|
133
|
+
var maxIndex = orientation === 'row' ? map.height : map.width;
|
|
134
|
+
if (resolvedIndex < 0 || resolvedIndex >= maxIndex) {
|
|
135
|
+
return EMPTY_CELLS_RESULT;
|
|
136
|
+
}
|
|
137
|
+
var cells = [];
|
|
138
|
+
var mergedCells = [];
|
|
139
|
+
var seenMerged = new Set();
|
|
140
|
+
var iterationCount = orientation === 'row' ? map.width : map.height;
|
|
141
|
+
for (var i = 0; i < iterationCount; i++) {
|
|
142
|
+
var row = orientation === 'row' ? resolvedIndex : i;
|
|
143
|
+
var col = orientation === 'row' ? i : resolvedIndex;
|
|
144
|
+
var cellIndex = row * map.width + col;
|
|
145
|
+
var mapCell = map.map[cellIndex];
|
|
146
|
+
if (mapCell === undefined) continue;
|
|
147
|
+
var cellPos = tableStart + mapCell;
|
|
148
|
+
var cellNode = tableNode.nodeAt(mapCell);
|
|
149
|
+
if (!cellNode) continue;
|
|
150
|
+
var cell = createCellInfo(row, col, cellPos, cellNode);
|
|
151
|
+
if (isCellMerged(cellNode)) {
|
|
152
|
+
if (!seenMerged.has(cellPos)) {
|
|
153
|
+
mergedCells.push(cell);
|
|
154
|
+
seenMerged.add(cellPos);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
cells.push(cell);
|
|
158
|
+
}
|
|
159
|
+
return {
|
|
160
|
+
cells: cells,
|
|
161
|
+
mergedCells: mergedCells
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Generic function to count empty cells from the end of a row or column
|
|
167
|
+
*/
|
|
168
|
+
function countEmptyCellsFromEnd(editor, tablePos, orientation) {
|
|
169
|
+
var table = getTable(editor, tablePos);
|
|
170
|
+
if (!table) return 0;
|
|
171
|
+
var doc = editor.state.doc;
|
|
172
|
+
var maxIndex = orientation === 'row' ? table.map.height : table.map.width;
|
|
173
|
+
var emptyCount = 0;
|
|
174
|
+
for (var idx = maxIndex - 1; idx >= 0; idx--) {
|
|
175
|
+
var seen = new Set();
|
|
176
|
+
var isLineEmpty = true;
|
|
177
|
+
var iterationCount = orientation === 'row' ? table.map.width : table.map.height;
|
|
178
|
+
for (var i = 0; i < iterationCount; i++) {
|
|
179
|
+
var row = orientation === 'row' ? idx : i;
|
|
180
|
+
var col = orientation === 'row' ? i : idx;
|
|
181
|
+
var rel = table.map.positionAt(row, col, table.node);
|
|
182
|
+
if (seen.has(rel)) continue;
|
|
183
|
+
seen.add(rel);
|
|
184
|
+
var abs = tablePos + 1 + rel;
|
|
185
|
+
var cell = doc.nodeAt(abs);
|
|
186
|
+
if (!cell) continue;
|
|
187
|
+
if (!isCellEmpty(cell)) {
|
|
188
|
+
isLineEmpty = false;
|
|
189
|
+
break;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
if (isLineEmpty) emptyCount++;else break;
|
|
193
|
+
}
|
|
194
|
+
return emptyCount;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Get information about the table at the current selection or a specific position.
|
|
199
|
+
*/
|
|
200
|
+
export function getTable(editor, tablePos) {
|
|
201
|
+
if (!editor) return null;
|
|
202
|
+
var table = null;
|
|
203
|
+
if (typeof tablePos === 'number') {
|
|
204
|
+
var tableNode = editor.state.doc.nodeAt(tablePos);
|
|
205
|
+
if ((tableNode === null || tableNode === void 0 ? void 0 : tableNode.type.name) === 'table') {
|
|
206
|
+
table = {
|
|
207
|
+
node: tableNode,
|
|
208
|
+
pos: tablePos,
|
|
209
|
+
start: tablePos + 1,
|
|
210
|
+
depth: editor.state.doc.resolve(tablePos).depth
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
if (!table) {
|
|
215
|
+
var state = editor.state;
|
|
216
|
+
var $from = state.doc.resolve(state.selection.from);
|
|
217
|
+
table = findTable($from);
|
|
218
|
+
}
|
|
219
|
+
if (!table) return null;
|
|
220
|
+
var tableMap = TableMap.get(table.node);
|
|
221
|
+
if (!tableMap) return null;
|
|
222
|
+
return _objectSpread(_objectSpread({}, table), {}, {
|
|
223
|
+
map: tableMap
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Checks if the current text selection is inside a table cell.
|
|
229
|
+
*/
|
|
230
|
+
export function isSelectionInCell(state) {
|
|
231
|
+
var selection = state.selection;
|
|
232
|
+
var $from = selection.$from;
|
|
233
|
+
for (var depth = $from.depth; depth > 0; depth--) {
|
|
234
|
+
var node = $from.node(depth);
|
|
235
|
+
if (node.type.name === 'tableCell' || node.type.name === 'tableHeader') {
|
|
236
|
+
return true;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
return false;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Runs a function while preserving the editor's selection.
|
|
244
|
+
*/
|
|
245
|
+
export function runPreservingCursor(editor, fn) {
|
|
246
|
+
var view = editor.view;
|
|
247
|
+
var startSel = view.state.selection;
|
|
248
|
+
var bookmark = startSel.getBookmark();
|
|
249
|
+
var mapping = new Mapping();
|
|
250
|
+
var originalDispatch = view.dispatch;
|
|
251
|
+
view.dispatch = function (tr) {
|
|
252
|
+
mapping.appendMapping(tr.mapping);
|
|
253
|
+
originalDispatch(tr);
|
|
254
|
+
};
|
|
255
|
+
try {
|
|
256
|
+
fn();
|
|
257
|
+
} finally {
|
|
258
|
+
view.dispatch = originalDispatch;
|
|
259
|
+
}
|
|
260
|
+
try {
|
|
261
|
+
var sel = bookmark.map(mapping).resolve(view.state.doc);
|
|
262
|
+
view.dispatch(view.state.tr.setSelection(sel));
|
|
263
|
+
return true;
|
|
264
|
+
} catch (_unused) {
|
|
265
|
+
// Fallback: if the exact spot vanished (e.g., cell deleted),
|
|
266
|
+
// go to the nearest valid position.
|
|
267
|
+
var mappedPos = mapping.map(startSel.from, -1);
|
|
268
|
+
var clamped = clamp(mappedPos, 0, view.state.doc.content.size);
|
|
269
|
+
var near = Selection.near(view.state.doc.resolve(clamped), -1);
|
|
270
|
+
view.dispatch(view.state.tr.setSelection(near));
|
|
271
|
+
return false;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Determines whether a table cell is effectively empty.
|
|
277
|
+
*/
|
|
278
|
+
export function isCellEmpty(cellNode) {
|
|
279
|
+
if (cellNode.childCount === 0) return true;
|
|
280
|
+
var isEmpty = true;
|
|
281
|
+
cellNode.descendants(function (n) {
|
|
282
|
+
var _n$text;
|
|
283
|
+
if (n.isText && (_n$text = n.text) !== null && _n$text !== void 0 && _n$text.trim()) {
|
|
284
|
+
isEmpty = false;
|
|
285
|
+
return false;
|
|
286
|
+
}
|
|
287
|
+
if (n.isLeaf && !n.isText) {
|
|
288
|
+
isEmpty = false;
|
|
289
|
+
return false;
|
|
290
|
+
}
|
|
291
|
+
return true;
|
|
292
|
+
});
|
|
293
|
+
return isEmpty;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Counts how many consecutive empty rows exist at the bottom of a given table.
|
|
298
|
+
*/
|
|
299
|
+
export function countEmptyRowsFromEnd(editor, tablePos) {
|
|
300
|
+
return countEmptyCellsFromEnd(editor, tablePos, 'row');
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Counts how many consecutive empty columns exist at the right edge of a given table.
|
|
305
|
+
*/
|
|
306
|
+
export function countEmptyColumnsFromEnd(editor, tablePos) {
|
|
307
|
+
return countEmptyCellsFromEnd(editor, tablePos, 'column');
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Rounds a number with a symmetric "dead-zone" around integer boundaries.
|
|
312
|
+
*/
|
|
313
|
+
export function marginRound(num) {
|
|
314
|
+
var margin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0.3;
|
|
315
|
+
var floor = Math.floor(num);
|
|
316
|
+
var ceil = Math.ceil(num);
|
|
317
|
+
var lowerBound = floor + margin;
|
|
318
|
+
var upperBound = ceil - margin;
|
|
319
|
+
if (num < lowerBound) return floor;
|
|
320
|
+
if (num > upperBound) return ceil;
|
|
321
|
+
return Math.round(num);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Applies the transaction based on the specified mode
|
|
326
|
+
*/
|
|
327
|
+
function applySelectionWithMode(state, transaction, options) {
|
|
328
|
+
var _options$mode;
|
|
329
|
+
var mode = (_options$mode = options.mode) !== null && _options$mode !== void 0 ? _options$mode : 'state';
|
|
330
|
+
switch (mode) {
|
|
331
|
+
case 'dispatch':
|
|
332
|
+
{
|
|
333
|
+
var dispatchOptions = options;
|
|
334
|
+
if (typeof dispatchOptions.dispatch === 'function') {
|
|
335
|
+
dispatchOptions.dispatch(transaction);
|
|
336
|
+
}
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
case 'transaction':
|
|
340
|
+
return transaction;
|
|
341
|
+
default:
|
|
342
|
+
// "state"
|
|
343
|
+
return state.apply(transaction);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Selects table cells by their (row, col) coordinates.
|
|
349
|
+
*/
|
|
350
|
+
export function selectCellsByCoords(editor, tablePos, coords) {
|
|
351
|
+
var options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {
|
|
352
|
+
mode: 'state'
|
|
353
|
+
};
|
|
354
|
+
if (!editor) return;
|
|
355
|
+
var table = getTable(editor, tablePos);
|
|
356
|
+
if (!table) return;
|
|
357
|
+
var state = editor.state;
|
|
358
|
+
var tableMap = table.map;
|
|
359
|
+
var cleanedCoords = coords.map(function (coord) {
|
|
360
|
+
return {
|
|
361
|
+
row: clamp(coord.row, 0, tableMap.height - 1),
|
|
362
|
+
col: clamp(coord.col, 0, tableMap.width - 1)
|
|
363
|
+
};
|
|
364
|
+
}).filter(function (coord) {
|
|
365
|
+
return isWithinBounds(coord.row, coord.col, tableMap);
|
|
366
|
+
});
|
|
367
|
+
if (cleanedCoords.length === 0) {
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// --- Find the smallest rectangle that contains all our coordinates ---
|
|
372
|
+
var allRows = cleanedCoords.map(function (coord) {
|
|
373
|
+
return coord.row;
|
|
374
|
+
});
|
|
375
|
+
var topRow = Math.min.apply(Math, _toConsumableArray(allRows));
|
|
376
|
+
var bottomRow = Math.max.apply(Math, _toConsumableArray(allRows));
|
|
377
|
+
var allCols = cleanedCoords.map(function (coord) {
|
|
378
|
+
return coord.col;
|
|
379
|
+
});
|
|
380
|
+
var leftCol = Math.min.apply(Math, _toConsumableArray(allCols));
|
|
381
|
+
var rightCol = Math.max.apply(Math, _toConsumableArray(allCols));
|
|
382
|
+
|
|
383
|
+
// --- Convert visual coordinates to document positions ---
|
|
384
|
+
var getCellPositionFromMap = function getCellPositionFromMap(row, col) {
|
|
385
|
+
var cellOffset = tableMap.map[row * tableMap.width + col];
|
|
386
|
+
if (cellOffset === undefined) return null;
|
|
387
|
+
return tablePos + 1 + cellOffset;
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
// Anchor = where the selection starts (top-left of bounding box)
|
|
391
|
+
var anchorPosition = getCellPositionFromMap(topRow, leftCol);
|
|
392
|
+
if (anchorPosition === null) return;
|
|
393
|
+
|
|
394
|
+
// Head = where the selection ends (usually bottom-right of bounding box)
|
|
395
|
+
var headPosition = getCellPositionFromMap(bottomRow, rightCol);
|
|
396
|
+
if (headPosition === null) return;
|
|
397
|
+
|
|
398
|
+
// --- Handle edge case with merged cells ---
|
|
399
|
+
if (headPosition === anchorPosition) {
|
|
400
|
+
var foundDifferentCell = false;
|
|
401
|
+
for (var row = bottomRow; row >= topRow && !foundDifferentCell; row--) {
|
|
402
|
+
for (var col = rightCol; col >= leftCol && !foundDifferentCell; col--) {
|
|
403
|
+
var candidatePosition = getCellPositionFromMap(row, col);
|
|
404
|
+
if (candidatePosition !== null && candidatePosition !== anchorPosition) {
|
|
405
|
+
headPosition = candidatePosition;
|
|
406
|
+
foundDifferentCell = true;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
try {
|
|
412
|
+
var anchorRef = state.doc.resolve(anchorPosition);
|
|
413
|
+
var headRef = state.doc.resolve(headPosition);
|
|
414
|
+
var cellSelection = new CellSelection(anchorRef, headRef);
|
|
415
|
+
var transaction = state.tr.setSelection(cellSelection);
|
|
416
|
+
return applySelectionWithMode(state, transaction, options);
|
|
417
|
+
} catch (error) {
|
|
418
|
+
console.error('Failed to create cell selection:', error);
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* Select the cell at (row, col) using `cellAround` to respect merged cells.
|
|
425
|
+
*/
|
|
426
|
+
export function selectCellAt(_ref2) {
|
|
427
|
+
var editor = _ref2.editor,
|
|
428
|
+
row = _ref2.row,
|
|
429
|
+
col = _ref2.col,
|
|
430
|
+
tablePos = _ref2.tablePos,
|
|
431
|
+
dispatch = _ref2.dispatch;
|
|
432
|
+
if (!editor) return false;
|
|
433
|
+
var state = editor.state,
|
|
434
|
+
view = editor.view;
|
|
435
|
+
var found = getTable(editor, tablePos);
|
|
436
|
+
if (!found) return false;
|
|
437
|
+
|
|
438
|
+
// Bounds check
|
|
439
|
+
if (!isWithinBounds(row, col, found.map)) {
|
|
440
|
+
return false;
|
|
441
|
+
}
|
|
442
|
+
var relCellPos = found.map.positionAt(row, col, found.node);
|
|
443
|
+
var absCellPos = found.start + relCellPos;
|
|
444
|
+
var $abs = state.doc.resolve(absCellPos);
|
|
445
|
+
var $cell = cellAround($abs);
|
|
446
|
+
var cellPos = $cell ? $cell.pos : absCellPos;
|
|
447
|
+
var sel = CellSelection.create(state.doc, cellPos);
|
|
448
|
+
var doDispatch = dispatch !== null && dispatch !== void 0 ? dispatch : view === null || view === void 0 ? void 0 : view.dispatch;
|
|
449
|
+
if (!doDispatch) return false;
|
|
450
|
+
doDispatch(state.tr.setSelection(sel));
|
|
451
|
+
return true;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/**
|
|
455
|
+
* Selects a boundary cell of the table based on orientation.
|
|
456
|
+
*/
|
|
457
|
+
export function selectLastCell(editor, tableNode, tablePos, orientation) {
|
|
458
|
+
var map = TableMap.get(tableNode);
|
|
459
|
+
var isRow = orientation === 'row';
|
|
460
|
+
|
|
461
|
+
// For rows, select bottom-left cell; for columns, select top-right cell
|
|
462
|
+
var row = isRow ? map.height - 1 : 0;
|
|
463
|
+
var col = isRow ? 0 : map.width - 1;
|
|
464
|
+
|
|
465
|
+
// Calculate the index in the table map
|
|
466
|
+
var index = row * map.width + col;
|
|
467
|
+
|
|
468
|
+
// Get the actual cell position from the map (handles merged cells)
|
|
469
|
+
var cellPos = map.map[index];
|
|
470
|
+
if (!cellPos && cellPos !== 0) {
|
|
471
|
+
console.warn('selectLastCell: cell position not found in map', {
|
|
472
|
+
index: index,
|
|
473
|
+
row: row,
|
|
474
|
+
col: col,
|
|
475
|
+
map: map
|
|
476
|
+
});
|
|
477
|
+
return false;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
// Find the row and column of the actual cell
|
|
481
|
+
var cellIndex = map.map.indexOf(cellPos);
|
|
482
|
+
var actualRow = cellIndex >= 0 ? Math.floor(cellIndex / map.width) : 0;
|
|
483
|
+
var actualCol = cellIndex >= 0 ? cellIndex % map.width : 0;
|
|
484
|
+
return selectCellAt({
|
|
485
|
+
editor: editor,
|
|
486
|
+
row: actualRow,
|
|
487
|
+
col: actualCol,
|
|
488
|
+
tablePos: tablePos,
|
|
489
|
+
dispatch: editor.view.dispatch.bind(editor.view)
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* Get all (row, col) coordinates for a given row or column index.
|
|
495
|
+
*/
|
|
496
|
+
export function getIndexCoordinates(_ref3) {
|
|
497
|
+
var editor = _ref3.editor,
|
|
498
|
+
index = _ref3.index,
|
|
499
|
+
orientation = _ref3.orientation,
|
|
500
|
+
tablePos = _ref3.tablePos;
|
|
501
|
+
if (!editor) return null;
|
|
502
|
+
var table = getTable(editor, tablePos);
|
|
503
|
+
if (!table) return null;
|
|
504
|
+
var map = table.map;
|
|
505
|
+
var width = map.width,
|
|
506
|
+
height = map.height;
|
|
507
|
+
if (index < 0) return null;
|
|
508
|
+
if (orientation === 'row' && index >= height) return null;
|
|
509
|
+
if (orientation === 'column' && index >= width) return null;
|
|
510
|
+
return orientation === 'row' ? Array.from({
|
|
511
|
+
length: map.width
|
|
512
|
+
}, function (_, col) {
|
|
513
|
+
return {
|
|
514
|
+
row: index,
|
|
515
|
+
col: col
|
|
516
|
+
};
|
|
517
|
+
}) : Array.from({
|
|
518
|
+
length: map.height
|
|
519
|
+
}, function (_, row) {
|
|
520
|
+
return {
|
|
521
|
+
row: row,
|
|
522
|
+
col: index
|
|
523
|
+
};
|
|
524
|
+
});
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* Given a DOM cell element, find its (row, col) indices within the table.
|
|
529
|
+
*/
|
|
530
|
+
export function getCellIndicesFromDOM(cell, tableNode, editor) {
|
|
531
|
+
if (!tableNode) return null;
|
|
532
|
+
try {
|
|
533
|
+
var cellPos = editor.view.posAtDOM(cell, 0);
|
|
534
|
+
var $cellPos = editor.view.state.doc.resolve(cellPos);
|
|
535
|
+
for (var d = $cellPos.depth; d > 0; d--) {
|
|
536
|
+
var node = $cellPos.node(d);
|
|
537
|
+
if (node.type.name === 'tableCell' || node.type.name === 'tableHeader') {
|
|
538
|
+
var tableMap = TableMap.get(tableNode);
|
|
539
|
+
var cellNodePos = $cellPos.before(d);
|
|
540
|
+
var tableStart = $cellPos.start(d - 2);
|
|
541
|
+
var cellOffset = cellNodePos - tableStart;
|
|
542
|
+
var cellIndex = tableMap.map.indexOf(cellOffset);
|
|
543
|
+
return {
|
|
544
|
+
rowIndex: Math.floor(cellIndex / tableMap.width),
|
|
545
|
+
colIndex: cellIndex % tableMap.width
|
|
546
|
+
};
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
} catch (error) {
|
|
550
|
+
console.warn('Could not get cell position:', error);
|
|
551
|
+
}
|
|
552
|
+
return null;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
/**
|
|
556
|
+
* Given a DOM element inside a table, find the corresponding table node and its position.
|
|
557
|
+
*/
|
|
558
|
+
export function getTableFromDOM(tableElement, editor) {
|
|
559
|
+
try {
|
|
560
|
+
var pos = editor.view.posAtDOM(tableElement, 0);
|
|
561
|
+
var $pos = editor.view.state.doc.resolve(pos);
|
|
562
|
+
for (var d = $pos.depth; d >= 0; d--) {
|
|
563
|
+
var node = $pos.node(d);
|
|
564
|
+
if (isTableNode(node)) {
|
|
565
|
+
return {
|
|
566
|
+
node: node,
|
|
567
|
+
pos: d === 0 ? 0 : $pos.before(d)
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
} catch (error) {
|
|
572
|
+
console.warn('Could not get table from DOM:', error);
|
|
573
|
+
}
|
|
574
|
+
return null;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
/**
|
|
578
|
+
* Checks if a node is a table node
|
|
579
|
+
*/
|
|
580
|
+
export function isTableNode(node) {
|
|
581
|
+
return !!node && (node.type.name === 'table' || node.type.spec.tableRole === 'table');
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
/**
|
|
585
|
+
* Get all cells (and unique merged cells) from a specific row.
|
|
586
|
+
*/
|
|
587
|
+
export function getRowCells(editor, rowIndex, tablePos) {
|
|
588
|
+
return collectCells(editor, 'row', rowIndex, tablePos);
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
/**
|
|
592
|
+
* Collect cells (and unique merged cells) from the current table.
|
|
593
|
+
*/
|
|
594
|
+
export function getColumnCells(editor, columnIndex, tablePos) {
|
|
595
|
+
return collectCells(editor, 'column', columnIndex, tablePos);
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
/**
|
|
599
|
+
* Compare two DOMRects for equality (within a small tolerance)
|
|
600
|
+
*/
|
|
601
|
+
export function rectEq(rect1, rect2) {
|
|
602
|
+
if (!rect1 || !rect2) return rect1 === rect2;
|
|
603
|
+
var tolerance = 0.5;
|
|
604
|
+
return Math.abs(rect1.left - rect2.left) < tolerance && Math.abs(rect1.top - rect2.top) < tolerance && Math.abs(rect1.width - rect2.width) < tolerance && Math.abs(rect1.height - rect2.height) < tolerance;
|
|
605
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ctzhian/tiptap",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "基于 Tiptap 二次开发的编辑器组件",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.js",
|
|
@@ -81,38 +81,39 @@
|
|
|
81
81
|
},
|
|
82
82
|
"dependencies": {
|
|
83
83
|
"@floating-ui/dom": "^1.7.2",
|
|
84
|
-
"@
|
|
85
|
-
"@tiptap/
|
|
86
|
-
"@tiptap/extension-
|
|
87
|
-
"@tiptap/extension-code
|
|
88
|
-
"@tiptap/extension-
|
|
89
|
-
"@tiptap/extension-
|
|
90
|
-
"@tiptap/extension-
|
|
91
|
-
"@tiptap/extension-
|
|
92
|
-
"@tiptap/extension-
|
|
93
|
-
"@tiptap/extension-
|
|
94
|
-
"@tiptap/extension-
|
|
95
|
-
"@tiptap/extension-
|
|
96
|
-
"@tiptap/extension-
|
|
97
|
-
"@tiptap/extension-
|
|
98
|
-
"@tiptap/extension-
|
|
99
|
-
"@tiptap/extension-
|
|
100
|
-
"@tiptap/extension-
|
|
101
|
-
"@tiptap/extension-
|
|
102
|
-
"@tiptap/extension-
|
|
103
|
-
"@tiptap/extension-table
|
|
104
|
-
"@tiptap/extension-
|
|
105
|
-
"@tiptap/extension-text-
|
|
106
|
-
"@tiptap/extension-
|
|
107
|
-
"@tiptap/extension-
|
|
108
|
-
"@tiptap/
|
|
109
|
-
"@tiptap/
|
|
110
|
-
"@tiptap/
|
|
111
|
-
"@tiptap/
|
|
112
|
-
"@tiptap/
|
|
113
|
-
"@tiptap/
|
|
114
|
-
"@tiptap/
|
|
115
|
-
"@tiptap/
|
|
84
|
+
"@floating-ui/react": "^0.27.16",
|
|
85
|
+
"@tiptap/core": "^3.11.0",
|
|
86
|
+
"@tiptap/extension-bubble-menu": "^3.11.0",
|
|
87
|
+
"@tiptap/extension-code": "^3.11.0",
|
|
88
|
+
"@tiptap/extension-code-block-lowlight": "^3.11.0",
|
|
89
|
+
"@tiptap/extension-details": "^3.11.0",
|
|
90
|
+
"@tiptap/extension-drag-handle-react": "^3.11.0",
|
|
91
|
+
"@tiptap/extension-emoji": "^3.11.0",
|
|
92
|
+
"@tiptap/extension-file-handler": "^3.11.0",
|
|
93
|
+
"@tiptap/extension-highlight": "^3.11.0",
|
|
94
|
+
"@tiptap/extension-horizontal-rule": "^3.11.0",
|
|
95
|
+
"@tiptap/extension-image": "^3.11.0",
|
|
96
|
+
"@tiptap/extension-invisible-characters": "^3.11.0",
|
|
97
|
+
"@tiptap/extension-link": "^3.11.0",
|
|
98
|
+
"@tiptap/extension-list": "^3.11.0",
|
|
99
|
+
"@tiptap/extension-mathematics": "^3.11.0",
|
|
100
|
+
"@tiptap/extension-mention": "^3.11.0",
|
|
101
|
+
"@tiptap/extension-subscript": "^3.11.0",
|
|
102
|
+
"@tiptap/extension-superscript": "^3.11.0",
|
|
103
|
+
"@tiptap/extension-table": "^3.11.0",
|
|
104
|
+
"@tiptap/extension-table-of-contents": "^3.11.0",
|
|
105
|
+
"@tiptap/extension-text-align": "^3.11.0",
|
|
106
|
+
"@tiptap/extension-text-style": "^3.11.0",
|
|
107
|
+
"@tiptap/extension-unique-id": "^3.11.0",
|
|
108
|
+
"@tiptap/extension-youtube": "^3.11.0",
|
|
109
|
+
"@tiptap/extensions": "^3.11.0",
|
|
110
|
+
"@tiptap/html": "^3.11.0",
|
|
111
|
+
"@tiptap/markdown": "^3.11.0",
|
|
112
|
+
"@tiptap/pm": "^3.11.0",
|
|
113
|
+
"@tiptap/react": "^3.11.0",
|
|
114
|
+
"@tiptap/starter-kit": "^3.11.0",
|
|
115
|
+
"@tiptap/static-renderer": "^3.11.0",
|
|
116
|
+
"@tiptap/suggestion": "^3.11.0",
|
|
116
117
|
"ace-builds": "^1.43.4",
|
|
117
118
|
"core-js": "^3.46.0",
|
|
118
119
|
"diff-match-patch": "^1.0.5",
|