@kerebron/extension-tables 0.0.1
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/LICENSE +23 -0
- package/README.md +34 -0
- package/assets/tables.css +48 -0
- package/esm/ExtensionTables.d.ts +10 -0
- package/esm/ExtensionTables.d.ts.map +1 -0
- package/esm/ExtensionTables.js +27 -0
- package/esm/NodeTable.d.ts +24 -0
- package/esm/NodeTable.d.ts.map +1 -0
- package/esm/NodeTable.js +115 -0
- package/esm/NodeTableCell.d.ts +16 -0
- package/esm/NodeTableCell.d.ts.map +1 -0
- package/esm/NodeTableCell.js +77 -0
- package/esm/NodeTableHeader.d.ts +16 -0
- package/esm/NodeTableHeader.d.ts.map +1 -0
- package/esm/NodeTableHeader.js +75 -0
- package/esm/NodeTableRow.d.ts +16 -0
- package/esm/NodeTableRow.d.ts.map +1 -0
- package/esm/NodeTableRow.js +47 -0
- package/esm/_dnt.shims.d.ts +6 -0
- package/esm/_dnt.shims.d.ts.map +1 -0
- package/esm/_dnt.shims.js +61 -0
- package/esm/package.json +3 -0
- package/esm/utilities/CellSelection.d.ts +53 -0
- package/esm/utilities/CellSelection.d.ts.map +1 -0
- package/esm/utilities/CellSelection.js +382 -0
- package/esm/utilities/TableMap.d.ts +92 -0
- package/esm/utilities/TableMap.d.ts.map +1 -0
- package/esm/utilities/TableMap.js +335 -0
- package/esm/utilities/TableView.d.ts +21 -0
- package/esm/utilities/TableView.d.ts.map +1 -0
- package/esm/utilities/TableView.js +108 -0
- package/esm/utilities/columnResizing.d.ts +50 -0
- package/esm/utilities/columnResizing.d.ts.map +1 -0
- package/esm/utilities/columnResizing.js +307 -0
- package/esm/utilities/commands.d.ts +166 -0
- package/esm/utilities/commands.d.ts.map +1 -0
- package/esm/utilities/commands.js +702 -0
- package/esm/utilities/copypaste.d.ts +35 -0
- package/esm/utilities/copypaste.d.ts.map +1 -0
- package/esm/utilities/copypaste.js +283 -0
- package/esm/utilities/createCell.d.ts +3 -0
- package/esm/utilities/createCell.d.ts.map +1 -0
- package/esm/utilities/createCell.js +6 -0
- package/esm/utilities/createTable.d.ts +3 -0
- package/esm/utilities/createTable.d.ts.map +1 -0
- package/esm/utilities/createTable.js +30 -0
- package/esm/utilities/fixTables.d.ts +18 -0
- package/esm/utilities/fixTables.d.ts.map +1 -0
- package/esm/utilities/fixTables.js +146 -0
- package/esm/utilities/getTableNodeTypes.d.ts +5 -0
- package/esm/utilities/getTableNodeTypes.d.ts.map +1 -0
- package/esm/utilities/getTableNodeTypes.js +14 -0
- package/esm/utilities/input.d.ts +21 -0
- package/esm/utilities/input.d.ts.map +1 -0
- package/esm/utilities/input.js +241 -0
- package/esm/utilities/tableEditing.d.ts +23 -0
- package/esm/utilities/tableEditing.d.ts.map +1 -0
- package/esm/utilities/tableEditing.js +63 -0
- package/esm/utilities/tableNodeTypes.d.ts +14 -0
- package/esm/utilities/tableNodeTypes.d.ts.map +1 -0
- package/esm/utilities/tableNodeTypes.js +16 -0
- package/esm/utilities/util.d.ts +73 -0
- package/esm/utilities/util.d.ts.map +1 -0
- package/esm/utilities/util.js +155 -0
- package/package.json +30 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Fragment, Node, NodeType, Slice } from 'prosemirror-model';
|
|
2
|
+
import { EditorState, Transaction } from 'prosemirror-state';
|
|
3
|
+
import { Rect } from './TableMap.js';
|
|
4
|
+
/**
|
|
5
|
+
* @internal
|
|
6
|
+
*/
|
|
7
|
+
export type Area = {
|
|
8
|
+
width: number;
|
|
9
|
+
height: number;
|
|
10
|
+
rows: Fragment[];
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Get a rectangular area of cells from a slice, or null if the outer
|
|
14
|
+
* nodes of the slice aren't table cells or rows.
|
|
15
|
+
*
|
|
16
|
+
* @internal
|
|
17
|
+
*/
|
|
18
|
+
export declare function pastedCells(slice: Slice): Area | null;
|
|
19
|
+
export declare function fitSlice(nodeType: NodeType, slice: Slice): Node;
|
|
20
|
+
/**
|
|
21
|
+
* Clip or extend (repeat) the given set of cells to cover the given
|
|
22
|
+
* width and height. Will clip rowspan/colspan cells at the edges when
|
|
23
|
+
* they stick out.
|
|
24
|
+
*
|
|
25
|
+
* @internal
|
|
26
|
+
*/
|
|
27
|
+
export declare function clipCells({ width, height, rows }: Area, newWidth: number, newHeight: number): Area;
|
|
28
|
+
/**
|
|
29
|
+
* Insert the given set of cells (as returned by `pastedCells`) into a
|
|
30
|
+
* table, at the position pointed at by rect.
|
|
31
|
+
*
|
|
32
|
+
* @internal
|
|
33
|
+
*/
|
|
34
|
+
export declare function insertCells(state: EditorState, dispatch: (tr: Transaction) => void, tableStart: number, rect: Rect, cells: Area): void;
|
|
35
|
+
//# sourceMappingURL=copypaste.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"copypaste.d.ts","sourceRoot":"","sources":["../../src/utilities/copypaste.ts"],"names":[],"mappings":"AAYA,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAU,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAG5E,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAG7D,OAAO,EAAa,IAAI,EAAY,MAAM,eAAe,CAAC;AAG1D;;GAEG;AACH,MAAM,MAAM,IAAI,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,QAAQ,EAAE,CAAA;CAAE,CAAC;AAIvE;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,GAAG,IAAI,CA0CrD;AA+BD,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI,CAI/D;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CACvB,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,IAAI,EAC7B,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,GAChB,IAAI,CAwDN;AA2ID;;;;;GAKG;AACH,wBAAgB,WAAW,CACzB,KAAK,EAAE,WAAW,EAClB,QAAQ,EAAE,CAAC,EAAE,EAAE,WAAW,KAAK,IAAI,EACnC,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,IAAI,EACV,KAAK,EAAE,IAAI,GACV,IAAI,CA8DN"}
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
// Utilities used for copy/paste handling.
|
|
2
|
+
//
|
|
3
|
+
// This module handles pasting cell content into tables, or pasting
|
|
4
|
+
// anything into a cell selection, as replacing a block of cells with
|
|
5
|
+
// the content of the selection. When pasting cells into a cell, that
|
|
6
|
+
// involves placing the block of pasted content so that its top left
|
|
7
|
+
// aligns with the selection cell, optionally extending the table to
|
|
8
|
+
// the right or bottom to make sure it is large enough. Pasting into a
|
|
9
|
+
// cell selection is different, here the cells in the selection are
|
|
10
|
+
// clipped to the selection's rectangle, optionally repeating the
|
|
11
|
+
// pasted cells when they are smaller than the selection.
|
|
12
|
+
import { Fragment, Slice } from 'prosemirror-model';
|
|
13
|
+
import { Transform } from 'prosemirror-transform';
|
|
14
|
+
import { CellSelection } from './CellSelection.js';
|
|
15
|
+
import { tableNodeTypes } from './tableNodeTypes.js';
|
|
16
|
+
import { TableMap } from './TableMap.js';
|
|
17
|
+
import { removeColSpan } from './util.js';
|
|
18
|
+
// Utilities to help with copying and pasting table cells
|
|
19
|
+
/**
|
|
20
|
+
* Get a rectangular area of cells from a slice, or null if the outer
|
|
21
|
+
* nodes of the slice aren't table cells or rows.
|
|
22
|
+
*
|
|
23
|
+
* @internal
|
|
24
|
+
*/
|
|
25
|
+
export function pastedCells(slice) {
|
|
26
|
+
if (!slice.size)
|
|
27
|
+
return null;
|
|
28
|
+
let { content, openStart, openEnd } = slice;
|
|
29
|
+
while (content.childCount == 1 &&
|
|
30
|
+
((openStart > 0 && openEnd > 0) ||
|
|
31
|
+
content.child(0).type.spec.tableRole == 'table')) {
|
|
32
|
+
openStart--;
|
|
33
|
+
openEnd--;
|
|
34
|
+
content = content.child(0).content;
|
|
35
|
+
}
|
|
36
|
+
const first = content.child(0);
|
|
37
|
+
const role = first.type.spec.tableRole;
|
|
38
|
+
const schema = first.type.schema, rows = [];
|
|
39
|
+
if (role == 'row') {
|
|
40
|
+
for (let i = 0; i < content.childCount; i++) {
|
|
41
|
+
let cells = content.child(i).content;
|
|
42
|
+
const left = i ? 0 : Math.max(0, openStart - 1);
|
|
43
|
+
const right = i < content.childCount - 1 ? 0 : Math.max(0, openEnd - 1);
|
|
44
|
+
if (left || right) {
|
|
45
|
+
cells = fitSlice(tableNodeTypes(schema).row, new Slice(cells, left, right)).content;
|
|
46
|
+
}
|
|
47
|
+
rows.push(cells);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
else if (role == 'cell' || role == 'header_cell') {
|
|
51
|
+
rows.push(openStart || openEnd
|
|
52
|
+
? fitSlice(tableNodeTypes(schema).row, new Slice(content, openStart, openEnd)).content
|
|
53
|
+
: content);
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
return ensureRectangular(schema, rows);
|
|
59
|
+
}
|
|
60
|
+
// Compute the width and height of a set of cells, and make sure each
|
|
61
|
+
// row has the same number of cells.
|
|
62
|
+
function ensureRectangular(schema, rows) {
|
|
63
|
+
const widths = [];
|
|
64
|
+
for (let i = 0; i < rows.length; i++) {
|
|
65
|
+
const row = rows[i];
|
|
66
|
+
for (let j = row.childCount - 1; j >= 0; j--) {
|
|
67
|
+
const { rowspan, colspan } = row.child(j).attrs;
|
|
68
|
+
for (let r = i; r < i + rowspan; r++) {
|
|
69
|
+
widths[r] = (widths[r] || 0) + colspan;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
let width = 0;
|
|
74
|
+
for (let r = 0; r < widths.length; r++)
|
|
75
|
+
width = Math.max(width, widths[r]);
|
|
76
|
+
for (let r = 0; r < widths.length; r++) {
|
|
77
|
+
if (r >= rows.length)
|
|
78
|
+
rows.push(Fragment.empty);
|
|
79
|
+
if (widths[r] < width) {
|
|
80
|
+
const empty = tableNodeTypes(schema).cell.createAndFill();
|
|
81
|
+
const cells = [];
|
|
82
|
+
for (let i = widths[r]; i < width; i++) {
|
|
83
|
+
cells.push(empty);
|
|
84
|
+
}
|
|
85
|
+
rows[r] = rows[r].append(Fragment.from(cells));
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return { height: rows.length, width, rows };
|
|
89
|
+
}
|
|
90
|
+
export function fitSlice(nodeType, slice) {
|
|
91
|
+
const node = nodeType.createAndFill();
|
|
92
|
+
const tr = new Transform(node).replace(0, node.content.size, slice);
|
|
93
|
+
return tr.doc;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Clip or extend (repeat) the given set of cells to cover the given
|
|
97
|
+
* width and height. Will clip rowspan/colspan cells at the edges when
|
|
98
|
+
* they stick out.
|
|
99
|
+
*
|
|
100
|
+
* @internal
|
|
101
|
+
*/
|
|
102
|
+
export function clipCells({ width, height, rows }, newWidth, newHeight) {
|
|
103
|
+
if (width != newWidth) {
|
|
104
|
+
const added = [];
|
|
105
|
+
const newRows = [];
|
|
106
|
+
for (let row = 0; row < rows.length; row++) {
|
|
107
|
+
const frag = rows[row], cells = [];
|
|
108
|
+
for (let col = added[row] || 0, i = 0; col < newWidth; i++) {
|
|
109
|
+
let cell = frag.child(i % frag.childCount);
|
|
110
|
+
if (col + cell.attrs.colspan > newWidth) {
|
|
111
|
+
cell = cell.type.createChecked(removeColSpan(cell.attrs, cell.attrs.colspan, col + cell.attrs.colspan - newWidth), cell.content);
|
|
112
|
+
}
|
|
113
|
+
cells.push(cell);
|
|
114
|
+
col += cell.attrs.colspan;
|
|
115
|
+
for (let j = 1; j < cell.attrs.rowspan; j++) {
|
|
116
|
+
added[row + j] = (added[row + j] || 0) + cell.attrs.colspan;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
newRows.push(Fragment.from(cells));
|
|
120
|
+
}
|
|
121
|
+
rows = newRows;
|
|
122
|
+
width = newWidth;
|
|
123
|
+
}
|
|
124
|
+
if (height != newHeight) {
|
|
125
|
+
const newRows = [];
|
|
126
|
+
for (let row = 0, i = 0; row < newHeight; row++, i++) {
|
|
127
|
+
const cells = [], source = rows[i % height];
|
|
128
|
+
for (let j = 0; j < source.childCount; j++) {
|
|
129
|
+
let cell = source.child(j);
|
|
130
|
+
if (row + cell.attrs.rowspan > newHeight) {
|
|
131
|
+
cell = cell.type.create({
|
|
132
|
+
...cell.attrs,
|
|
133
|
+
rowspan: Math.max(1, newHeight - cell.attrs.rowspan),
|
|
134
|
+
}, cell.content);
|
|
135
|
+
}
|
|
136
|
+
cells.push(cell);
|
|
137
|
+
}
|
|
138
|
+
newRows.push(Fragment.from(cells));
|
|
139
|
+
}
|
|
140
|
+
rows = newRows;
|
|
141
|
+
height = newHeight;
|
|
142
|
+
}
|
|
143
|
+
return { width, height, rows };
|
|
144
|
+
}
|
|
145
|
+
// Make sure a table has at least the given width and height. Return
|
|
146
|
+
// true if something was changed.
|
|
147
|
+
function growTable(tr, map, table, start, width, height, mapFrom) {
|
|
148
|
+
const schema = tr.doc.type.schema;
|
|
149
|
+
const types = tableNodeTypes(schema);
|
|
150
|
+
let empty;
|
|
151
|
+
let emptyHead;
|
|
152
|
+
if (width > map.width) {
|
|
153
|
+
for (let row = 0, rowEnd = 0; row < map.height; row++) {
|
|
154
|
+
const rowNode = table.child(row);
|
|
155
|
+
rowEnd += rowNode.nodeSize;
|
|
156
|
+
const cells = [];
|
|
157
|
+
let add;
|
|
158
|
+
if (rowNode.lastChild == null || rowNode.lastChild.type == types.cell) {
|
|
159
|
+
add = empty || (empty = types.cell.createAndFill());
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
add = emptyHead ||
|
|
163
|
+
(emptyHead = types.header_cell.createAndFill());
|
|
164
|
+
}
|
|
165
|
+
for (let i = map.width; i < width; i++)
|
|
166
|
+
cells.push(add);
|
|
167
|
+
tr.insert(tr.mapping.slice(mapFrom).map(rowEnd - 1 + start), cells);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
if (height > map.height) {
|
|
171
|
+
const cells = [];
|
|
172
|
+
for (let i = 0, start = (map.height - 1) * map.width; i < Math.max(map.width, width); i++) {
|
|
173
|
+
const header = i >= map.width
|
|
174
|
+
? false
|
|
175
|
+
: table.nodeAt(map.map[start + i]).type == types.header_cell;
|
|
176
|
+
cells.push(header
|
|
177
|
+
? emptyHead || (emptyHead = types.header_cell.createAndFill())
|
|
178
|
+
: empty || (empty = types.cell.createAndFill()));
|
|
179
|
+
}
|
|
180
|
+
const emptyRow = types.row.create(null, Fragment.from(cells)), rows = [];
|
|
181
|
+
for (let i = map.height; i < height; i++)
|
|
182
|
+
rows.push(emptyRow);
|
|
183
|
+
tr.insert(tr.mapping.slice(mapFrom).map(start + table.nodeSize - 2), rows);
|
|
184
|
+
}
|
|
185
|
+
return !!(empty || emptyHead);
|
|
186
|
+
}
|
|
187
|
+
// Make sure the given line (left, top) to (right, top) doesn't cross
|
|
188
|
+
// any rowspan cells by splitting cells that cross it. Return true if
|
|
189
|
+
// something changed.
|
|
190
|
+
function isolateHorizontal(tr, map, table, start, left, right, top, mapFrom) {
|
|
191
|
+
if (top == 0 || top == map.height)
|
|
192
|
+
return false;
|
|
193
|
+
let found = false;
|
|
194
|
+
for (let col = left; col < right; col++) {
|
|
195
|
+
const index = top * map.width + col, pos = map.map[index];
|
|
196
|
+
if (map.map[index - map.width] == pos) {
|
|
197
|
+
found = true;
|
|
198
|
+
const cell = table.nodeAt(pos);
|
|
199
|
+
const { top: cellTop, left: cellLeft } = map.findCell(pos);
|
|
200
|
+
tr.setNodeMarkup(tr.mapping.slice(mapFrom).map(pos + start), null, {
|
|
201
|
+
...cell.attrs,
|
|
202
|
+
rowspan: top - cellTop,
|
|
203
|
+
});
|
|
204
|
+
tr.insert(tr.mapping.slice(mapFrom).map(map.positionAt(top, cellLeft, table)), cell.type.createAndFill({
|
|
205
|
+
...cell.attrs,
|
|
206
|
+
rowspan: cellTop + cell.attrs.rowspan - top,
|
|
207
|
+
}));
|
|
208
|
+
col += cell.attrs.colspan - 1;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return found;
|
|
212
|
+
}
|
|
213
|
+
// Make sure the given line (left, top) to (left, bottom) doesn't
|
|
214
|
+
// cross any colspan cells by splitting cells that cross it. Return
|
|
215
|
+
// true if something changed.
|
|
216
|
+
function isolateVertical(tr, map, table, start, top, bottom, left, mapFrom) {
|
|
217
|
+
if (left == 0 || left == map.width)
|
|
218
|
+
return false;
|
|
219
|
+
let found = false;
|
|
220
|
+
for (let row = top; row < bottom; row++) {
|
|
221
|
+
const index = row * map.width + left, pos = map.map[index];
|
|
222
|
+
if (map.map[index - 1] == pos) {
|
|
223
|
+
found = true;
|
|
224
|
+
const cell = table.nodeAt(pos);
|
|
225
|
+
const cellLeft = map.colCount(pos);
|
|
226
|
+
const updatePos = tr.mapping.slice(mapFrom).map(pos + start);
|
|
227
|
+
tr.setNodeMarkup(updatePos, null, removeColSpan(cell.attrs, left - cellLeft, cell.attrs.colspan - (left - cellLeft)));
|
|
228
|
+
tr.insert(updatePos + cell.nodeSize, cell.type.createAndFill(removeColSpan(cell.attrs, 0, left - cellLeft)));
|
|
229
|
+
row += cell.attrs.rowspan - 1;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
return found;
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Insert the given set of cells (as returned by `pastedCells`) into a
|
|
236
|
+
* table, at the position pointed at by rect.
|
|
237
|
+
*
|
|
238
|
+
* @internal
|
|
239
|
+
*/
|
|
240
|
+
export function insertCells(state, dispatch, tableStart, rect, cells) {
|
|
241
|
+
let table = tableStart ? state.doc.nodeAt(tableStart - 1) : state.doc;
|
|
242
|
+
if (!table) {
|
|
243
|
+
throw new Error('No table found');
|
|
244
|
+
}
|
|
245
|
+
let map = TableMap.get(table);
|
|
246
|
+
const { top, left } = rect;
|
|
247
|
+
const right = left + cells.width, bottom = top + cells.height;
|
|
248
|
+
const tr = state.tr;
|
|
249
|
+
let mapFrom = 0;
|
|
250
|
+
function recomp() {
|
|
251
|
+
table = tableStart ? tr.doc.nodeAt(tableStart - 1) : tr.doc;
|
|
252
|
+
if (!table) {
|
|
253
|
+
throw new Error('No table found');
|
|
254
|
+
}
|
|
255
|
+
map = TableMap.get(table);
|
|
256
|
+
mapFrom = tr.mapping.maps.length;
|
|
257
|
+
}
|
|
258
|
+
// Prepare the table to be large enough and not have any cells
|
|
259
|
+
// crossing the boundaries of the rectangle that we want to
|
|
260
|
+
// insert into. If anything about it changes, recompute the table
|
|
261
|
+
// map so that subsequent operations can see the current shape.
|
|
262
|
+
if (growTable(tr, map, table, tableStart, right, bottom, mapFrom))
|
|
263
|
+
recomp();
|
|
264
|
+
if (isolateHorizontal(tr, map, table, tableStart, left, right, top, mapFrom)) {
|
|
265
|
+
recomp();
|
|
266
|
+
}
|
|
267
|
+
if (isolateHorizontal(tr, map, table, tableStart, left, right, bottom, mapFrom)) {
|
|
268
|
+
recomp();
|
|
269
|
+
}
|
|
270
|
+
if (isolateVertical(tr, map, table, tableStart, top, bottom, left, mapFrom)) {
|
|
271
|
+
recomp();
|
|
272
|
+
}
|
|
273
|
+
if (isolateVertical(tr, map, table, tableStart, top, bottom, right, mapFrom)) {
|
|
274
|
+
recomp();
|
|
275
|
+
}
|
|
276
|
+
for (let row = top; row < bottom; row++) {
|
|
277
|
+
const from = map.positionAt(row, left, table), to = map.positionAt(row, right, table);
|
|
278
|
+
tr.replace(tr.mapping.slice(mapFrom).map(from + tableStart), tr.mapping.slice(mapFrom).map(to + tableStart), new Slice(cells.rows[row - top], 0, 0));
|
|
279
|
+
}
|
|
280
|
+
recomp();
|
|
281
|
+
tr.setSelection(new CellSelection(tr.doc.resolve(tableStart + map.positionAt(top, left, table)), tr.doc.resolve(tableStart + map.positionAt(bottom - 1, right - 1, table))));
|
|
282
|
+
dispatch(tr);
|
|
283
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { Fragment, Node as ProsemirrorNode, NodeType } from 'prosemirror-model';
|
|
2
|
+
export declare function createCell(cellType: NodeType, cellContent?: Fragment | ProsemirrorNode | Array<ProsemirrorNode>): ProsemirrorNode | null | undefined;
|
|
3
|
+
//# sourceMappingURL=createCell.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createCell.d.ts","sourceRoot":"","sources":["../../src/utilities/createCell.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,IAAI,IAAI,eAAe,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAEhF,wBAAgB,UAAU,CACxB,QAAQ,EAAE,QAAQ,EAClB,WAAW,CAAC,EAAE,QAAQ,GAAG,eAAe,GAAG,KAAK,CAAC,eAAe,CAAC,GAChE,eAAe,GAAG,IAAI,GAAG,SAAS,CAMpC"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { Fragment, Node as ProsemirrorNode, Schema } from 'prosemirror-model';
|
|
2
|
+
export declare function createTable(schema: Schema, rowsCount: number, colsCount: number, withHeaderRow: boolean, cellContent?: Fragment | ProsemirrorNode | Array<ProsemirrorNode>): ProsemirrorNode;
|
|
3
|
+
//# sourceMappingURL=createTable.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createTable.d.ts","sourceRoot":"","sources":["../../src/utilities/createTable.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,IAAI,IAAI,eAAe,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAK9E,wBAAgB,WAAW,CACzB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,OAAO,EACtB,WAAW,CAAC,EAAE,QAAQ,GAAG,eAAe,GAAG,KAAK,CAAC,eAAe,CAAC,GAChE,eAAe,CAwCjB"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { createCell } from './createCell.js';
|
|
2
|
+
import { getTableNodeTypes } from './getTableNodeTypes.js';
|
|
3
|
+
export function createTable(schema, rowsCount, colsCount, withHeaderRow, cellContent) {
|
|
4
|
+
const types = getTableNodeTypes(schema);
|
|
5
|
+
const headerCells = [];
|
|
6
|
+
const cells = [];
|
|
7
|
+
for (let index = 0; index < colsCount; index += 1) {
|
|
8
|
+
const cell = createCell(types.cell, cellContent);
|
|
9
|
+
if (cell) {
|
|
10
|
+
cells.push(cell);
|
|
11
|
+
}
|
|
12
|
+
if (withHeaderRow) {
|
|
13
|
+
const headerCell = createCell(types.header_cell, cellContent);
|
|
14
|
+
if (headerCell) {
|
|
15
|
+
headerCells.push(headerCell);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
const rows = [];
|
|
20
|
+
for (let index = 0; index < rowsCount; index += 1) {
|
|
21
|
+
rows.push(types.row.createChecked(null, withHeaderRow && index === 0 ? headerCells : cells));
|
|
22
|
+
}
|
|
23
|
+
const attrs = {};
|
|
24
|
+
for (const [key, attrSpec] of Object.entries(types.table.attrs)) {
|
|
25
|
+
if ('undefined' !== typeof attrSpec.default) {
|
|
26
|
+
attrs[key] = attrSpec.default;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return types.table.createChecked(attrs, rows);
|
|
30
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Node } from 'prosemirror-model';
|
|
2
|
+
import { EditorState, Transaction } from 'prosemirror-state';
|
|
3
|
+
/**
|
|
4
|
+
* @public
|
|
5
|
+
*/
|
|
6
|
+
export declare const fixTablesKey: any;
|
|
7
|
+
/**
|
|
8
|
+
* Inspect all tables in the given state's document and return a
|
|
9
|
+
* transaction that fixes them, if necessary. If `oldState` was
|
|
10
|
+
* provided, that is assumed to hold a previous, known-good state,
|
|
11
|
+
* which will be used to avoid re-scanning unchanged parts of the
|
|
12
|
+
* document.
|
|
13
|
+
*
|
|
14
|
+
* @public
|
|
15
|
+
*/
|
|
16
|
+
export declare function fixTables(state: EditorState, oldState?: EditorState): Transaction | undefined;
|
|
17
|
+
export declare function fixTable(state: EditorState, table: Node, tablePos: number, tr: Transaction | undefined): Transaction | undefined;
|
|
18
|
+
//# sourceMappingURL=fixTables.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fixTables.d.ts","sourceRoot":"","sources":["../../src/utilities/fixTables.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AACzC,OAAO,EAAE,WAAW,EAAa,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAKxE;;GAEG;AACH,eAAO,MAAM,YAAY,KAAsD,CAAC;AAkChF;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CACvB,KAAK,EAAE,WAAW,EAClB,QAAQ,CAAC,EAAE,WAAW,GACrB,WAAW,GAAG,SAAS,CAYzB;AAID,wBAAgB,QAAQ,CACtB,KAAK,EAAE,WAAW,EAClB,KAAK,EAAE,IAAI,EACX,QAAQ,EAAE,MAAM,EAChB,EAAE,EAAE,WAAW,GAAG,SAAS,GAC1B,WAAW,GAAG,SAAS,CA0EzB"}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
// This file defines helpers for normalizing tables, making sure no
|
|
2
|
+
// cells overlap (which can happen, if you have the wrong col- and
|
|
3
|
+
// rowspans) and that each row has the same width. Uses the problems
|
|
4
|
+
// reported by `TableMap`.
|
|
5
|
+
import { PluginKey } from 'prosemirror-state';
|
|
6
|
+
import { tableNodeTypes } from './tableNodeTypes.js';
|
|
7
|
+
import { TableMap } from './TableMap.js';
|
|
8
|
+
import { removeColSpan } from './util.js';
|
|
9
|
+
/**
|
|
10
|
+
* @public
|
|
11
|
+
*/
|
|
12
|
+
export const fixTablesKey = new PluginKey('fix-tables');
|
|
13
|
+
/**
|
|
14
|
+
* Helper for iterating through the nodes in a document that changed
|
|
15
|
+
* compared to the given previous document. Useful for avoiding
|
|
16
|
+
* duplicate work on each transaction.
|
|
17
|
+
*
|
|
18
|
+
* @public
|
|
19
|
+
*/
|
|
20
|
+
function changedDescendants(old, cur, offset, f) {
|
|
21
|
+
const oldSize = old.childCount, curSize = cur.childCount;
|
|
22
|
+
outer: for (let i = 0, j = 0; i < curSize; i++) {
|
|
23
|
+
const child = cur.child(i);
|
|
24
|
+
for (let scan = j, e = Math.min(oldSize, i + 3); scan < e; scan++) {
|
|
25
|
+
if (old.child(scan) == child) {
|
|
26
|
+
j = scan + 1;
|
|
27
|
+
offset += child.nodeSize;
|
|
28
|
+
continue outer;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
f(child, offset);
|
|
32
|
+
if (j < oldSize && old.child(j).sameMarkup(child)) {
|
|
33
|
+
changedDescendants(old.child(j), child, offset + 1, f);
|
|
34
|
+
}
|
|
35
|
+
else
|
|
36
|
+
child.nodesBetween(0, child.content.size, f, offset + 1);
|
|
37
|
+
offset += child.nodeSize;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Inspect all tables in the given state's document and return a
|
|
42
|
+
* transaction that fixes them, if necessary. If `oldState` was
|
|
43
|
+
* provided, that is assumed to hold a previous, known-good state,
|
|
44
|
+
* which will be used to avoid re-scanning unchanged parts of the
|
|
45
|
+
* document.
|
|
46
|
+
*
|
|
47
|
+
* @public
|
|
48
|
+
*/
|
|
49
|
+
export function fixTables(state, oldState) {
|
|
50
|
+
let tr;
|
|
51
|
+
const check = (node, pos) => {
|
|
52
|
+
if (node.type.spec.tableRole == 'table') {
|
|
53
|
+
tr = fixTable(state, node, pos, tr);
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
if (!oldState)
|
|
57
|
+
state.doc.descendants(check);
|
|
58
|
+
else if (oldState.doc != state.doc) {
|
|
59
|
+
changedDescendants(oldState.doc, state.doc, 0, check);
|
|
60
|
+
}
|
|
61
|
+
return tr;
|
|
62
|
+
}
|
|
63
|
+
// Fix the given table, if necessary. Will append to the transaction
|
|
64
|
+
// it was given, if non-null, or create a new one if necessary.
|
|
65
|
+
export function fixTable(state, table, tablePos, tr) {
|
|
66
|
+
const map = TableMap.get(table);
|
|
67
|
+
if (!map.problems)
|
|
68
|
+
return tr;
|
|
69
|
+
if (!tr)
|
|
70
|
+
tr = state.tr;
|
|
71
|
+
// Track which rows we must add cells to, so that we can adjust that
|
|
72
|
+
// when fixing collisions.
|
|
73
|
+
const mustAdd = [];
|
|
74
|
+
for (let i = 0; i < map.height; i++)
|
|
75
|
+
mustAdd.push(0);
|
|
76
|
+
for (let i = 0; i < map.problems.length; i++) {
|
|
77
|
+
const prob = map.problems[i];
|
|
78
|
+
if (prob.type == 'collision') {
|
|
79
|
+
const cell = table.nodeAt(prob.pos);
|
|
80
|
+
if (!cell)
|
|
81
|
+
continue;
|
|
82
|
+
const attrs = cell.attrs;
|
|
83
|
+
for (let j = 0; j < attrs.rowspan; j++)
|
|
84
|
+
mustAdd[prob.row + j] += prob.n;
|
|
85
|
+
tr.setNodeMarkup(tr.mapping.map(tablePos + 1 + prob.pos), null, removeColSpan(attrs, attrs.colspan - prob.n, prob.n));
|
|
86
|
+
}
|
|
87
|
+
else if (prob.type == 'missing') {
|
|
88
|
+
mustAdd[prob.row] += prob.n;
|
|
89
|
+
}
|
|
90
|
+
else if (prob.type == 'overlong_rowspan') {
|
|
91
|
+
const cell = table.nodeAt(prob.pos);
|
|
92
|
+
if (!cell)
|
|
93
|
+
continue;
|
|
94
|
+
tr.setNodeMarkup(tr.mapping.map(tablePos + 1 + prob.pos), null, {
|
|
95
|
+
...cell.attrs,
|
|
96
|
+
rowspan: cell.attrs.rowspan - prob.n,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
else if (prob.type == 'colwidth mismatch') {
|
|
100
|
+
const cell = table.nodeAt(prob.pos);
|
|
101
|
+
if (!cell)
|
|
102
|
+
continue;
|
|
103
|
+
tr.setNodeMarkup(tr.mapping.map(tablePos + 1 + prob.pos), null, {
|
|
104
|
+
...cell.attrs,
|
|
105
|
+
colwidth: prob.colwidth,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
else if (prob.type == 'zero_sized') {
|
|
109
|
+
const pos = tr.mapping.map(tablePos);
|
|
110
|
+
tr.delete(pos, pos + table.nodeSize);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
let first, last;
|
|
114
|
+
for (let i = 0; i < mustAdd.length; i++) {
|
|
115
|
+
if (mustAdd[i]) {
|
|
116
|
+
if (first == null)
|
|
117
|
+
first = i;
|
|
118
|
+
last = i;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// Add the necessary cells, using a heuristic for whether to add the
|
|
122
|
+
// cells at the start or end of the rows (if it looks like a 'bite'
|
|
123
|
+
// was taken out of the table, add cells at the start of the row
|
|
124
|
+
// after the bite. Otherwise add them at the end).
|
|
125
|
+
for (let i = 0, pos = tablePos + 1; i < map.height; i++) {
|
|
126
|
+
const row = table.child(i);
|
|
127
|
+
const end = pos + row.nodeSize;
|
|
128
|
+
const add = mustAdd[i];
|
|
129
|
+
if (add > 0) {
|
|
130
|
+
let role = 'cell';
|
|
131
|
+
if (row.firstChild) {
|
|
132
|
+
role = row.firstChild.type.spec.tableRole;
|
|
133
|
+
}
|
|
134
|
+
const nodes = [];
|
|
135
|
+
for (let j = 0; j < add; j++) {
|
|
136
|
+
const node = tableNodeTypes(state.schema)[role].createAndFill();
|
|
137
|
+
if (node)
|
|
138
|
+
nodes.push(node);
|
|
139
|
+
}
|
|
140
|
+
const side = (i == 0 || first == i - 1) && last == i ? pos + 1 : end - 1;
|
|
141
|
+
tr.insert(tr.mapping.map(side), nodes);
|
|
142
|
+
}
|
|
143
|
+
pos = end;
|
|
144
|
+
}
|
|
145
|
+
return tr.setMeta(fixTablesKey, { fixTables: true });
|
|
146
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getTableNodeTypes.d.ts","sourceRoot":"","sources":["../../src/utilities/getTableNodeTypes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAErD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,CAAA;CAAE,CAkB7E"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export function getTableNodeTypes(schema) {
|
|
2
|
+
if (schema.cached.tableNodeTypes) {
|
|
3
|
+
return schema.cached.tableNodeTypes;
|
|
4
|
+
}
|
|
5
|
+
const roles = {};
|
|
6
|
+
Object.keys(schema.nodes).forEach((type) => {
|
|
7
|
+
const nodeType = schema.nodes[type];
|
|
8
|
+
if (nodeType.spec.tableRole) {
|
|
9
|
+
roles[nodeType.spec.tableRole] = nodeType;
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
schema.cached.tableNodeTypes = roles;
|
|
13
|
+
return roles;
|
|
14
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Slice } from 'prosemirror-model';
|
|
2
|
+
import { Command } from 'prosemirror-state';
|
|
3
|
+
import { EditorView } from 'prosemirror-view';
|
|
4
|
+
type Axis = 'horiz' | 'vert';
|
|
5
|
+
/**
|
|
6
|
+
* @public
|
|
7
|
+
*/
|
|
8
|
+
export type Direction = -1 | 1;
|
|
9
|
+
export declare const handleKeyDown: any;
|
|
10
|
+
/**
|
|
11
|
+
* @internal
|
|
12
|
+
*/
|
|
13
|
+
export declare function arrow(axis: Axis, dir: Direction): Command;
|
|
14
|
+
export declare function handleTripleClick(view: EditorView, pos: number): boolean;
|
|
15
|
+
/**
|
|
16
|
+
* @public
|
|
17
|
+
*/
|
|
18
|
+
export declare function handlePaste(view: EditorView, _: ClipboardEvent, slice: Slice): boolean;
|
|
19
|
+
export declare function handleMouseDown(view: EditorView, startEvent: MouseEvent): void;
|
|
20
|
+
export {};
|
|
21
|
+
//# sourceMappingURL=input.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"input.d.ts","sourceRoot":"","sources":["../../src/utilities/input.ts"],"names":[],"mappings":"AAIA,OAAO,EAAyB,KAAK,EAAE,MAAM,mBAAmB,CAAC;AACjE,OAAO,EACL,OAAO,EAKR,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAe9C,KAAK,IAAI,GAAG,OAAO,GAAG,MAAM,CAAC;AAE7B;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;AAE/B,eAAO,MAAM,aAAa,KAexB,CAAC;AAYH;;GAEG;AACH,wBAAgB,KAAK,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,SAAS,GAAG,OAAO,CA+BzD;AAyBD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAMxE;AAED;;GAEG;AACH,wBAAgB,WAAW,CACzB,IAAI,EAAE,UAAU,EAChB,CAAC,EAAE,cAAc,EACjB,KAAK,EAAE,KAAK,GACX,OAAO,CAuCT;AAED,wBAAgB,eAAe,CAC7B,IAAI,EAAE,UAAU,EAChB,UAAU,EAAE,UAAU,GACrB,IAAI,CAqEN"}
|