@regal-text-editor/plugin-table 0.1.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/LICENSE +21 -0
- package/README.md +883 -0
- package/dist/index.d.ts +36 -0
- package/dist/index.js +462 -0
- package/dist/index.js.map +1 -0
- package/package.json +58 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Plugin } from '@regal-text-editor/core';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A block-level table, row and cell: schema, row/column/table structural
|
|
5
|
+
* commands, Tab/Shift-Tab cell navigation, and HTML/GFM-Markdown
|
|
6
|
+
* import/export. Deliberately its own package rather than folded into
|
|
7
|
+
* `plugin-basic-blocks` — tables are the heaviest, most structurally
|
|
8
|
+
* involved feature in this editor (nested content model, nested-child
|
|
9
|
+
* commands, dedicated keyboard handling), and plenty of consumers never
|
|
10
|
+
* need them at all.
|
|
11
|
+
*
|
|
12
|
+
* Scope, documented rather than silently missing: there is no rectangular
|
|
13
|
+
* multi-cell *selection* in this editor (see `plugin-image`'s doc comment
|
|
14
|
+
* on the same underlying gap), so there is no interactive "merge cells"
|
|
15
|
+
* command — `colspan`/`rowspan` are still fully understood on import/export
|
|
16
|
+
* for HTML round-tripping (e.g. a pasted table with merged cells keeps its
|
|
17
|
+
* shape), just not something a user can create from inside the editor yet.
|
|
18
|
+
* Markdown (GFM) tables are a plain grid with no span concept at all, so a
|
|
19
|
+
* cell's `colspan`/`rowspan` has no Markdown representation and is not
|
|
20
|
+
* preserved through a Markdown export/import round trip — only the HTML
|
|
21
|
+
* round trip is lossless for spans.
|
|
22
|
+
*
|
|
23
|
+
* Plugin order matters when combined with `plugin-lists`: both bind `Tab`/
|
|
24
|
+
* `Shift-Tab` (see `Editor.keymap`'s doc comment for the chain-of-responsibility
|
|
25
|
+
* resolution this relies on), and a list nested inside a cell is valid content
|
|
26
|
+
* (`tableCell`'s own content is bare `block+`). Register `ListsPlugin` *before*
|
|
27
|
+
* `TablePlugin` in the `plugins` array so Tab indents such a nested list item
|
|
28
|
+
* instead of jumping to the next cell — the reverse order silently prefers
|
|
29
|
+
* cell navigation instead, since each plugin's binding is tried in
|
|
30
|
+
* registration order and `goToNextCell`/`goToPreviousCell` only look for the
|
|
31
|
+
* nearest `tableCell` ancestor, not whether a closer list item sits between it
|
|
32
|
+
* and the cursor.
|
|
33
|
+
*/
|
|
34
|
+
declare function TablePlugin(): Plugin;
|
|
35
|
+
|
|
36
|
+
export { TablePlugin };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,462 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import {
|
|
3
|
+
createElement,
|
|
4
|
+
createText,
|
|
5
|
+
cursor,
|
|
6
|
+
defineNode,
|
|
7
|
+
firstTextEntry,
|
|
8
|
+
getNodeAtPath,
|
|
9
|
+
lastTextEntry,
|
|
10
|
+
normalizeSelection,
|
|
11
|
+
range
|
|
12
|
+
} from "@regal-text-editor/core";
|
|
13
|
+
var TABLE = "table";
|
|
14
|
+
var TABLE_ROW = "tableRow";
|
|
15
|
+
var TABLE_CELL = "tableCell";
|
|
16
|
+
function findTableCellPath(doc, path) {
|
|
17
|
+
for (let length = path.length; length >= 1; length -= 1) {
|
|
18
|
+
const candidate = path.slice(0, length);
|
|
19
|
+
const node = getNodeAtPath(doc, candidate);
|
|
20
|
+
if (node.object === "element" && node.type === TABLE_CELL) return candidate;
|
|
21
|
+
}
|
|
22
|
+
return void 0;
|
|
23
|
+
}
|
|
24
|
+
function createEmptyCell(defaultBlockType, attrs = {}) {
|
|
25
|
+
return createElement(TABLE_CELL, attrs, [createElement(defaultBlockType, {}, [createText("")])]);
|
|
26
|
+
}
|
|
27
|
+
function createEmptyRow(defaultBlockType, columnCount) {
|
|
28
|
+
return createElement(TABLE_ROW, {}, Array.from({ length: columnCount }, () => createEmptyCell(defaultBlockType)));
|
|
29
|
+
}
|
|
30
|
+
function firstTextPointWithin(doc, subtreePath) {
|
|
31
|
+
const node = getNodeAtPath(doc, subtreePath);
|
|
32
|
+
if (node.object !== "element") return void 0;
|
|
33
|
+
const entry = firstTextEntry(node);
|
|
34
|
+
return entry ? { path: [...subtreePath, ...entry.path], offset: 0 } : void 0;
|
|
35
|
+
}
|
|
36
|
+
function lastTextPointWithin(doc, subtreePath) {
|
|
37
|
+
const node = getNodeAtPath(doc, subtreePath);
|
|
38
|
+
if (node.object !== "element") return void 0;
|
|
39
|
+
const entry = lastTextEntry(node);
|
|
40
|
+
return entry ? { path: [...subtreePath, ...entry.path], offset: entry.node.text.length } : void 0;
|
|
41
|
+
}
|
|
42
|
+
function selectAllWithin(doc, subtreePath) {
|
|
43
|
+
const first = firstTextPointWithin(doc, subtreePath);
|
|
44
|
+
const last = lastTextPointWithin(doc, subtreePath);
|
|
45
|
+
if (!first || !last) return void 0;
|
|
46
|
+
return range(first, last);
|
|
47
|
+
}
|
|
48
|
+
function childCountAt(doc, path) {
|
|
49
|
+
if (path.length === 0) return doc.children.length;
|
|
50
|
+
const node = getNodeAtPath(doc, path);
|
|
51
|
+
return node.object === "element" ? node.children.length : 0;
|
|
52
|
+
}
|
|
53
|
+
function locateCell(editor) {
|
|
54
|
+
const selection = editor.getSelection();
|
|
55
|
+
if (!selection) return void 0;
|
|
56
|
+
const cellPath = findTableCellPath(editor.doc, normalizeSelection(selection).start.path);
|
|
57
|
+
if (!cellPath) return void 0;
|
|
58
|
+
const rowPath = cellPath.slice(0, -1);
|
|
59
|
+
const tablePath = rowPath.slice(0, -1);
|
|
60
|
+
return {
|
|
61
|
+
cellPath,
|
|
62
|
+
rowPath,
|
|
63
|
+
tablePath,
|
|
64
|
+
rowIndex: rowPath[rowPath.length - 1],
|
|
65
|
+
colIndex: cellPath[cellPath.length - 1]
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
var insertTableCommand = {
|
|
69
|
+
execute(editor, ...args) {
|
|
70
|
+
const rowCount = typeof args[0] === "number" && Number.isFinite(args[0]) && args[0] > 0 ? Math.floor(args[0]) : 3;
|
|
71
|
+
const colCount = typeof args[1] === "number" && Number.isFinite(args[1]) && args[1] > 0 ? Math.floor(args[1]) : 3;
|
|
72
|
+
const selection = editor.getSelection();
|
|
73
|
+
if (!selection) return false;
|
|
74
|
+
const topIndex = normalizeSelection(selection).start.path[0];
|
|
75
|
+
const table = createElement(
|
|
76
|
+
TABLE,
|
|
77
|
+
{},
|
|
78
|
+
Array.from({ length: rowCount }, () => createEmptyRow(editor.schema.defaultBlockType, colCount))
|
|
79
|
+
);
|
|
80
|
+
const tx = editor.createTransaction({ origin: "command", historyGroup: "structural" });
|
|
81
|
+
tx.insertNode([topIndex + 1], table);
|
|
82
|
+
tx.insertNode([topIndex + 2], createElement(editor.schema.defaultBlockType, {}, [createText("")]));
|
|
83
|
+
const target = selectAllWithin(tx.doc, [topIndex + 1, 0, 0]);
|
|
84
|
+
if (target) tx.setSelection(target);
|
|
85
|
+
editor.dispatch(tx);
|
|
86
|
+
return true;
|
|
87
|
+
},
|
|
88
|
+
canExecute(editor) {
|
|
89
|
+
return editor.getSelection() !== null;
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
function addRowCommand(direction) {
|
|
93
|
+
return {
|
|
94
|
+
execute(editor) {
|
|
95
|
+
const location = locateCell(editor);
|
|
96
|
+
if (!location) return false;
|
|
97
|
+
const { rowPath, tablePath, rowIndex } = location;
|
|
98
|
+
const colCount = childCountAt(editor.doc, rowPath);
|
|
99
|
+
const insertIndex = direction === "before" ? rowIndex : rowIndex + 1;
|
|
100
|
+
const tx = editor.createTransaction({ origin: "command", historyGroup: "structural" });
|
|
101
|
+
tx.insertNode([...tablePath, insertIndex], createEmptyRow(editor.schema.defaultBlockType, colCount));
|
|
102
|
+
const target = selectAllWithin(tx.doc, [...tablePath, insertIndex, 0]);
|
|
103
|
+
if (target) tx.setSelection(target);
|
|
104
|
+
editor.dispatch(tx);
|
|
105
|
+
return true;
|
|
106
|
+
},
|
|
107
|
+
canExecute(editor) {
|
|
108
|
+
return locateCell(editor) !== void 0;
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
function addColumnCommand(direction) {
|
|
113
|
+
return {
|
|
114
|
+
execute(editor) {
|
|
115
|
+
const location = locateCell(editor);
|
|
116
|
+
if (!location) return false;
|
|
117
|
+
const { tablePath, rowIndex, colIndex } = location;
|
|
118
|
+
const insertIndex = direction === "before" ? colIndex : colIndex + 1;
|
|
119
|
+
const rowCount = childCountAt(editor.doc, tablePath);
|
|
120
|
+
const tx = editor.createTransaction({ origin: "command", historyGroup: "structural" });
|
|
121
|
+
for (let r = 0; r < rowCount; r += 1) {
|
|
122
|
+
tx.insertNode([...tablePath, r, insertIndex], createEmptyCell(editor.schema.defaultBlockType));
|
|
123
|
+
}
|
|
124
|
+
const target = selectAllWithin(tx.doc, [...tablePath, rowIndex, insertIndex]);
|
|
125
|
+
if (target) tx.setSelection(target);
|
|
126
|
+
editor.dispatch(tx);
|
|
127
|
+
return true;
|
|
128
|
+
},
|
|
129
|
+
canExecute(editor) {
|
|
130
|
+
return locateCell(editor) !== void 0;
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
var deleteRowCommand = {
|
|
135
|
+
execute(editor) {
|
|
136
|
+
const location = locateCell(editor);
|
|
137
|
+
if (!location) return false;
|
|
138
|
+
const { rowPath, tablePath, rowIndex, colIndex } = location;
|
|
139
|
+
if (childCountAt(editor.doc, tablePath) <= 1) return false;
|
|
140
|
+
const tx = editor.createTransaction({ origin: "command", historyGroup: "structural" });
|
|
141
|
+
tx.removeNode(rowPath);
|
|
142
|
+
const newRowIndex = Math.min(rowIndex, childCountAt(tx.doc, tablePath) - 1);
|
|
143
|
+
const newColIndex = Math.min(colIndex, childCountAt(tx.doc, [...tablePath, newRowIndex]) - 1);
|
|
144
|
+
const target = selectAllWithin(tx.doc, [...tablePath, newRowIndex, newColIndex]);
|
|
145
|
+
if (target) tx.setSelection(target);
|
|
146
|
+
editor.dispatch(tx);
|
|
147
|
+
return true;
|
|
148
|
+
},
|
|
149
|
+
canExecute(editor) {
|
|
150
|
+
const location = locateCell(editor);
|
|
151
|
+
return location !== void 0 && childCountAt(editor.doc, location.tablePath) > 1;
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
var deleteColumnCommand = {
|
|
155
|
+
execute(editor) {
|
|
156
|
+
const location = locateCell(editor);
|
|
157
|
+
if (!location) return false;
|
|
158
|
+
const { tablePath, rowIndex, colIndex } = location;
|
|
159
|
+
const rowCount = childCountAt(editor.doc, tablePath);
|
|
160
|
+
if (childCountAt(editor.doc, [...tablePath, rowIndex]) <= 1) return false;
|
|
161
|
+
const tx = editor.createTransaction({ origin: "command", historyGroup: "structural" });
|
|
162
|
+
for (let r = 0; r < rowCount; r += 1) {
|
|
163
|
+
tx.removeNode([...tablePath, r, colIndex]);
|
|
164
|
+
}
|
|
165
|
+
const newColIndex = Math.min(colIndex, childCountAt(tx.doc, [...tablePath, rowIndex]) - 1);
|
|
166
|
+
const target = selectAllWithin(tx.doc, [...tablePath, rowIndex, newColIndex]);
|
|
167
|
+
if (target) tx.setSelection(target);
|
|
168
|
+
editor.dispatch(tx);
|
|
169
|
+
return true;
|
|
170
|
+
},
|
|
171
|
+
canExecute(editor) {
|
|
172
|
+
const location = locateCell(editor);
|
|
173
|
+
if (!location) return false;
|
|
174
|
+
return childCountAt(editor.doc, [...location.tablePath, location.rowIndex]) > 1;
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
var deleteTableCommand = {
|
|
178
|
+
execute(editor) {
|
|
179
|
+
const location = locateCell(editor);
|
|
180
|
+
if (!location) return false;
|
|
181
|
+
const { tablePath } = location;
|
|
182
|
+
const tableIndex = tablePath[tablePath.length - 1];
|
|
183
|
+
const parentPath = tablePath.slice(0, -1);
|
|
184
|
+
const tx = editor.createTransaction({ origin: "command", historyGroup: "structural" });
|
|
185
|
+
tx.removeNode(tablePath);
|
|
186
|
+
const siblingCount = childCountAt(tx.doc, parentPath);
|
|
187
|
+
const fallbackIndex = Math.min(tableIndex, siblingCount - 1);
|
|
188
|
+
const point = fallbackIndex >= 0 ? firstTextPointWithin(tx.doc, [...parentPath, fallbackIndex]) : void 0;
|
|
189
|
+
tx.setSelection(point ? cursor(point) : null);
|
|
190
|
+
editor.dispatch(tx);
|
|
191
|
+
return true;
|
|
192
|
+
},
|
|
193
|
+
canExecute(editor) {
|
|
194
|
+
return locateCell(editor) !== void 0;
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
function rowIsAllHeader(doc, rowPath) {
|
|
198
|
+
const row = getNodeAtPath(doc, rowPath);
|
|
199
|
+
if (row.object !== "element") return false;
|
|
200
|
+
return row.children.length > 0 && row.children.every((cell) => cell.object === "element" && cell.attrs.header === true);
|
|
201
|
+
}
|
|
202
|
+
var toggleHeaderRowCommand = {
|
|
203
|
+
execute(editor) {
|
|
204
|
+
const location = locateCell(editor);
|
|
205
|
+
if (!location) return false;
|
|
206
|
+
const { tablePath } = location;
|
|
207
|
+
const firstRowPath = [...tablePath, 0];
|
|
208
|
+
const makeHeader = !rowIsAllHeader(editor.doc, firstRowPath);
|
|
209
|
+
const columnCount = childCountAt(editor.doc, firstRowPath);
|
|
210
|
+
const tx = editor.createTransaction({ origin: "command", historyGroup: "structural" });
|
|
211
|
+
for (let c = 0; c < columnCount; c += 1) {
|
|
212
|
+
tx.setNodeAttribute([...firstRowPath, c], { header: makeHeader });
|
|
213
|
+
}
|
|
214
|
+
const selection = editor.getSelection();
|
|
215
|
+
if (selection) tx.setSelection(selection);
|
|
216
|
+
editor.dispatch(tx);
|
|
217
|
+
return true;
|
|
218
|
+
},
|
|
219
|
+
canExecute(editor) {
|
|
220
|
+
return locateCell(editor) !== void 0;
|
|
221
|
+
},
|
|
222
|
+
isActive(editor) {
|
|
223
|
+
const location = locateCell(editor);
|
|
224
|
+
if (!location) return false;
|
|
225
|
+
return rowIsAllHeader(editor.doc, [...location.tablePath, 0]);
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
var goToNextCellCommand = {
|
|
229
|
+
execute(editor) {
|
|
230
|
+
const location = locateCell(editor);
|
|
231
|
+
if (!location) return false;
|
|
232
|
+
const { tablePath, rowPath, rowIndex, colIndex } = location;
|
|
233
|
+
const rowColumnCount = childCountAt(editor.doc, rowPath);
|
|
234
|
+
const rowCount = childCountAt(editor.doc, tablePath);
|
|
235
|
+
const tx = editor.createTransaction({ origin: "keyboard", historyGroup: "structural" });
|
|
236
|
+
let targetCellPath;
|
|
237
|
+
if (colIndex + 1 < rowColumnCount) {
|
|
238
|
+
targetCellPath = [...rowPath, colIndex + 1];
|
|
239
|
+
} else if (rowIndex + 1 < rowCount) {
|
|
240
|
+
targetCellPath = [...tablePath, rowIndex + 1, 0];
|
|
241
|
+
} else {
|
|
242
|
+
tx.insertNode([...tablePath, rowCount], createEmptyRow(editor.schema.defaultBlockType, rowColumnCount));
|
|
243
|
+
targetCellPath = [...tablePath, rowCount, 0];
|
|
244
|
+
}
|
|
245
|
+
const target = selectAllWithin(tx.doc, targetCellPath);
|
|
246
|
+
if (!target) return false;
|
|
247
|
+
tx.setSelection(target);
|
|
248
|
+
editor.dispatch(tx);
|
|
249
|
+
return true;
|
|
250
|
+
},
|
|
251
|
+
canExecute(editor) {
|
|
252
|
+
return locateCell(editor) !== void 0;
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
var goToPreviousCellCommand = {
|
|
256
|
+
execute(editor) {
|
|
257
|
+
const location = locateCell(editor);
|
|
258
|
+
if (!location) return false;
|
|
259
|
+
const { tablePath, rowPath, rowIndex, colIndex } = location;
|
|
260
|
+
let targetCellPath;
|
|
261
|
+
if (colIndex - 1 >= 0) {
|
|
262
|
+
targetCellPath = [...rowPath, colIndex - 1];
|
|
263
|
+
} else if (rowIndex - 1 >= 0) {
|
|
264
|
+
const previousColumnCount = childCountAt(editor.doc, [...tablePath, rowIndex - 1]);
|
|
265
|
+
targetCellPath = [...tablePath, rowIndex - 1, previousColumnCount - 1];
|
|
266
|
+
} else {
|
|
267
|
+
return false;
|
|
268
|
+
}
|
|
269
|
+
const target = selectAllWithin(editor.doc, targetCellPath);
|
|
270
|
+
if (!target) return false;
|
|
271
|
+
const tx = editor.createTransaction({ origin: "keyboard", historyGroup: "structural" });
|
|
272
|
+
tx.setSelection(target);
|
|
273
|
+
editor.dispatch(tx);
|
|
274
|
+
return true;
|
|
275
|
+
},
|
|
276
|
+
canExecute(editor) {
|
|
277
|
+
return locateCell(editor) !== void 0;
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
function parseAlignStyle(el) {
|
|
281
|
+
const style = el.getAttribute("style") ?? "";
|
|
282
|
+
const match = /text-align\s*:\s*([a-z]+)/i.exec(style);
|
|
283
|
+
const raw = match?.[1]?.toLowerCase();
|
|
284
|
+
if (raw === "left") return "start";
|
|
285
|
+
if (raw === "right") return "end";
|
|
286
|
+
return raw === "start" || raw === "center" || raw === "end" ? raw : null;
|
|
287
|
+
}
|
|
288
|
+
function alignStyleAttr(node) {
|
|
289
|
+
const align = node.attrs.align;
|
|
290
|
+
return typeof align === "string" ? ` style="text-align:${align}"` : "";
|
|
291
|
+
}
|
|
292
|
+
function parseTableCell(isHeader) {
|
|
293
|
+
return (el, ctx) => {
|
|
294
|
+
const blocks = ctx.parseBlocks(el);
|
|
295
|
+
const children = blocks.length > 0 ? blocks : [createElement("paragraph", {}, ctx.parseInline(el))];
|
|
296
|
+
const colspan = Number(el.getAttribute("colspan"));
|
|
297
|
+
const rowspan = Number(el.getAttribute("rowspan"));
|
|
298
|
+
const align = parseAlignStyle(el);
|
|
299
|
+
return createElement(
|
|
300
|
+
TABLE_CELL,
|
|
301
|
+
{
|
|
302
|
+
header: isHeader,
|
|
303
|
+
...Number.isInteger(colspan) && colspan > 1 ? { colspan } : {},
|
|
304
|
+
...Number.isInteger(rowspan) && rowspan > 1 ? { rowspan } : {},
|
|
305
|
+
...align ? { align } : {}
|
|
306
|
+
},
|
|
307
|
+
children
|
|
308
|
+
);
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
function splitTableRowCells(line) {
|
|
312
|
+
const withoutEdges = line.trim().replace(/^\|/, "").replace(/\|$/, "");
|
|
313
|
+
const cells = [];
|
|
314
|
+
let current = "";
|
|
315
|
+
for (let i = 0; i < withoutEdges.length; i += 1) {
|
|
316
|
+
const ch = withoutEdges[i];
|
|
317
|
+
if (ch === "\\" && withoutEdges[i + 1] === "|") {
|
|
318
|
+
current += "|";
|
|
319
|
+
i += 1;
|
|
320
|
+
continue;
|
|
321
|
+
}
|
|
322
|
+
if (ch === "|") {
|
|
323
|
+
cells.push(current.trim());
|
|
324
|
+
current = "";
|
|
325
|
+
continue;
|
|
326
|
+
}
|
|
327
|
+
current += ch;
|
|
328
|
+
}
|
|
329
|
+
cells.push(current.trim());
|
|
330
|
+
return cells;
|
|
331
|
+
}
|
|
332
|
+
var TABLE_DELIMITER_ROW = /^\s*\|?\s*:?-+:?\s*(\|\s*:?-+:?\s*)*\|?\s*$/;
|
|
333
|
+
function delimiterCellAlign(cell) {
|
|
334
|
+
const trimmed = cell.trim();
|
|
335
|
+
const left = trimmed.startsWith(":");
|
|
336
|
+
const right = trimmed.endsWith(":");
|
|
337
|
+
if (left && right) return "center";
|
|
338
|
+
if (right) return "end";
|
|
339
|
+
if (left) return "start";
|
|
340
|
+
return null;
|
|
341
|
+
}
|
|
342
|
+
function serializeTableCell(_node, children) {
|
|
343
|
+
return children.join(" ").replace(/\s*\n+\s*/g, " ").trim().replace(/\|/g, "\\|");
|
|
344
|
+
}
|
|
345
|
+
function tableDelimiterRow(node) {
|
|
346
|
+
const firstRow = node.children[0];
|
|
347
|
+
const cells = firstRow && firstRow.object === "element" ? firstRow.children : [];
|
|
348
|
+
if (cells.length === 0) return "| |";
|
|
349
|
+
const parts = cells.map((cell) => {
|
|
350
|
+
const align = cell.object === "element" && typeof cell.attrs.align === "string" ? cell.attrs.align : null;
|
|
351
|
+
return align === "center" ? ":---:" : align === "end" ? "---:" : align === "start" ? ":---" : "---";
|
|
352
|
+
});
|
|
353
|
+
return `| ${parts.join(" | ")} |`;
|
|
354
|
+
}
|
|
355
|
+
function TablePlugin() {
|
|
356
|
+
return {
|
|
357
|
+
name: "table",
|
|
358
|
+
schema: {
|
|
359
|
+
nodes: [
|
|
360
|
+
defineNode({ name: TABLE, group: "block", content: "tableRow+" }),
|
|
361
|
+
defineNode({ name: TABLE_ROW, group: "block", content: "tableCell+" }),
|
|
362
|
+
defineNode({
|
|
363
|
+
name: TABLE_CELL,
|
|
364
|
+
group: "block",
|
|
365
|
+
content: "block+",
|
|
366
|
+
attrs: { header: { default: false }, colspan: { default: 1 }, rowspan: { default: 1 }, align: { default: null } }
|
|
367
|
+
})
|
|
368
|
+
]
|
|
369
|
+
},
|
|
370
|
+
commands: () => ({
|
|
371
|
+
insertTable: insertTableCommand,
|
|
372
|
+
addRowBefore: addRowCommand("before"),
|
|
373
|
+
addRowAfter: addRowCommand("after"),
|
|
374
|
+
addColumnBefore: addColumnCommand("before"),
|
|
375
|
+
addColumnAfter: addColumnCommand("after"),
|
|
376
|
+
deleteRow: deleteRowCommand,
|
|
377
|
+
deleteColumn: deleteColumnCommand,
|
|
378
|
+
deleteTable: deleteTableCommand,
|
|
379
|
+
toggleHeaderRow: toggleHeaderRowCommand,
|
|
380
|
+
goToNextCell: goToNextCellCommand,
|
|
381
|
+
goToPreviousCell: goToPreviousCellCommand
|
|
382
|
+
}),
|
|
383
|
+
keymap: () => ({
|
|
384
|
+
Tab: "goToNextCell",
|
|
385
|
+
"Shift-Tab": "goToPreviousCell"
|
|
386
|
+
}),
|
|
387
|
+
htmlSerializers: {
|
|
388
|
+
// Wrapped in a scroll container so a table wider than the editor
|
|
389
|
+
// (many columns, or one long unbroken cell value) scrolls horizontally
|
|
390
|
+
// in place instead of overflowing the writing area — see the
|
|
391
|
+
// `.rte-table-scroll` rule in `@regal-text-editor/ui`'s stylesheet, and
|
|
392
|
+
// `effectiveChildren`/`domElementToBlockPath` in
|
|
393
|
+
// `@regal-text-editor/browser`'s `dom/positions.ts`, which already
|
|
394
|
+
// treat this wrapper as transparent for caret/selection mapping.
|
|
395
|
+
[TABLE]: (_node, children) => `<div class="rte-table-scroll"><table><tbody>${children.join("")}</tbody></table></div>`,
|
|
396
|
+
[TABLE_ROW]: (_node, children) => `<tr>${children.join("")}</tr>`,
|
|
397
|
+
[TABLE_CELL]: (node, children) => {
|
|
398
|
+
const tag = node.attrs.header === true ? "th" : "td";
|
|
399
|
+
const colspan = typeof node.attrs.colspan === "number" && node.attrs.colspan > 1 ? ` colspan="${node.attrs.colspan}"` : "";
|
|
400
|
+
const rowspan = typeof node.attrs.rowspan === "number" && node.attrs.rowspan > 1 ? ` rowspan="${node.attrs.rowspan}"` : "";
|
|
401
|
+
return `<${tag}${colspan}${rowspan}${alignStyleAttr(node)}>${children.join("")}</${tag}>`;
|
|
402
|
+
}
|
|
403
|
+
},
|
|
404
|
+
markdownSerializers: {
|
|
405
|
+
[TABLE_CELL]: serializeTableCell,
|
|
406
|
+
[TABLE_ROW]: (_node, children) => `| ${children.join(" | ")} |`,
|
|
407
|
+
[TABLE]: (node, children) => [children[0] ?? "", tableDelimiterRow(node), ...children.slice(1)].join("\n")
|
|
408
|
+
},
|
|
409
|
+
htmlParsers: (registry) => {
|
|
410
|
+
registry.registerBlock(TABLE.toLowerCase(), (el, ctx) => {
|
|
411
|
+
const rows = ctx.parseBlocks(el);
|
|
412
|
+
return rows.length > 0 ? createElement(TABLE, {}, rows) : null;
|
|
413
|
+
});
|
|
414
|
+
registry.registerBlock("tr", (el, ctx) => {
|
|
415
|
+
const cells = ctx.parseBlocks(el);
|
|
416
|
+
return cells.length > 0 ? createElement(TABLE_ROW, {}, cells) : null;
|
|
417
|
+
});
|
|
418
|
+
registry.registerBlock("td", parseTableCell(false));
|
|
419
|
+
registry.registerBlock("th", parseTableCell(true));
|
|
420
|
+
},
|
|
421
|
+
markdownParsers: (registry) => {
|
|
422
|
+
registry.registerBlock((lines, index, ctx) => {
|
|
423
|
+
const headerLine = lines[index] ?? "";
|
|
424
|
+
const delimiterLine = lines[index + 1] ?? "";
|
|
425
|
+
if (!headerLine.includes("|") || !TABLE_DELIMITER_ROW.test(delimiterLine)) return null;
|
|
426
|
+
const headerCells = splitTableRowCells(headerLine);
|
|
427
|
+
const delimiterCells = splitTableRowCells(delimiterLine);
|
|
428
|
+
if (headerCells.length === 0 || delimiterCells.length === 0) return null;
|
|
429
|
+
const columnCount = headerCells.length;
|
|
430
|
+
const aligns = Array.from({ length: columnCount }, (_unused, i) => delimiterCellAlign(delimiterCells[i] ?? ""));
|
|
431
|
+
const normalizeRow = (cells) => {
|
|
432
|
+
const row = cells.slice(0, columnCount);
|
|
433
|
+
while (row.length < columnCount) row.push("");
|
|
434
|
+
return row;
|
|
435
|
+
};
|
|
436
|
+
const makeCell = (text, columnIndex, isHeader) => {
|
|
437
|
+
const align = aligns[columnIndex] ?? null;
|
|
438
|
+
return createElement(TABLE_CELL, { header: isHeader, ...align ? { align } : {} }, [
|
|
439
|
+
createElement("paragraph", {}, ctx.parseInline(text))
|
|
440
|
+
]);
|
|
441
|
+
};
|
|
442
|
+
const rows = [createElement(TABLE_ROW, {}, normalizeRow(headerCells).map((text, i) => makeCell(text, i, true)))];
|
|
443
|
+
let cursorIndex = index + 2;
|
|
444
|
+
while (cursorIndex < lines.length) {
|
|
445
|
+
const line = lines[cursorIndex] ?? "";
|
|
446
|
+
if (line.trim().length === 0 || !line.includes("|")) break;
|
|
447
|
+
const cells = normalizeRow(splitTableRowCells(line));
|
|
448
|
+
rows.push(createElement(TABLE_ROW, {}, cells.map((text, i) => makeCell(text, i, false))));
|
|
449
|
+
cursorIndex += 1;
|
|
450
|
+
}
|
|
451
|
+
return { nodes: [createElement(TABLE, {}, rows)], nextIndex: cursorIndex };
|
|
452
|
+
});
|
|
453
|
+
},
|
|
454
|
+
slashMenuItems: (registry) => {
|
|
455
|
+
registry.register({ id: "table", title: "Table", keywords: ["table", "grid"], icon: "table", command: "insertTable", args: [3, 3] });
|
|
456
|
+
}
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
export {
|
|
460
|
+
TablePlugin
|
|
461
|
+
};
|
|
462
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import {\n createElement,\n createText,\n cursor,\n defineNode,\n firstTextEntry,\n getNodeAtPath,\n lastTextEntry,\n normalizeSelection,\n range,\n type Attrs,\n type Command,\n type Editor,\n type EditorDocument,\n type ElementNode,\n type HtmlParseContext,\n type Plugin,\n type Point,\n type Selection\n} from \"@regal-text-editor/core\";\n\nconst TABLE = \"table\";\nconst TABLE_ROW = \"tableRow\";\nconst TABLE_CELL = \"tableCell\";\n\n/** Finds the nearest `tableCell` ancestor of `path`, at any depth — a\n * selection can be arbitrarily deep inside a cell's block content (e.g. a\n * list inside a cell), mirroring `plugin-lists`' own `findListItemPath`. A\n * cell's parent is always its row and a row's parent is always its table\n * (unlike list nesting, a table's shape is fixed, so no further search is\n * needed once the cell itself is found). */\nfunction findTableCellPath(doc: EditorDocument, path: number[]): number[] | undefined {\n for (let length = path.length; length >= 1; length -= 1) {\n const candidate = path.slice(0, length);\n const node = getNodeAtPath(doc, candidate);\n if (node.object === \"element\" && node.type === TABLE_CELL) return candidate;\n }\n return undefined;\n}\n\nfunction createEmptyCell(defaultBlockType: string, attrs: Attrs = {}): ElementNode {\n return createElement(TABLE_CELL, attrs, [createElement(defaultBlockType, {}, [createText(\"\")])]);\n}\n\nfunction createEmptyRow(defaultBlockType: string, columnCount: number): ElementNode {\n return createElement(TABLE_ROW, {}, Array.from({ length: columnCount }, () => createEmptyCell(defaultBlockType)));\n}\n\n/** The first/last text position anywhere inside the element at `subtreePath`\n * (which may be several block levels deep, e.g. a cell containing a list) —\n * built from the existing `firstTextEntry`/`lastTextEntry` walkers, whose\n * returned paths are relative to the node passed in, by re-prefixing with\n * `subtreePath` to get an absolute document path. */\nfunction firstTextPointWithin(doc: EditorDocument, subtreePath: number[]): Point | undefined {\n const node = getNodeAtPath(doc, subtreePath);\n if (node.object !== \"element\") return undefined;\n const entry = firstTextEntry(node);\n return entry ? { path: [...subtreePath, ...entry.path], offset: 0 } : undefined;\n}\n\nfunction lastTextPointWithin(doc: EditorDocument, subtreePath: number[]): Point | undefined {\n const node = getNodeAtPath(doc, subtreePath);\n if (node.object !== \"element\") return undefined;\n const entry = lastTextEntry(node);\n return entry ? { path: [...subtreePath, ...entry.path], offset: entry.node.text.length } : undefined;\n}\n\n/** Selects the entire content of the node at `subtreePath` — used after\n * inserting/entering a cell so the cursor lands ready to type, and (for a\n * cell that already has text) so Tab-navigation behaves like a spreadsheet:\n * the destination cell's content is selected, ready to be replaced. Degrades\n * to a plain collapsed cursor for an empty cell, since `range()` collapses\n * itself when both endpoints are equal. */\nfunction selectAllWithin(doc: EditorDocument, subtreePath: number[]): Selection | undefined {\n const first = firstTextPointWithin(doc, subtreePath);\n const last = lastTextPointWithin(doc, subtreePath);\n if (!first || !last) return undefined;\n return range(first, last);\n}\n\nfunction childCountAt(doc: EditorDocument, path: number[]): number {\n if (path.length === 0) return doc.children.length;\n const node = getNodeAtPath(doc, path);\n return node.object === \"element\" ? node.children.length : 0;\n}\n\ninterface CellLocation {\n cellPath: number[];\n rowPath: number[];\n tablePath: number[];\n rowIndex: number;\n colIndex: number;\n}\n\nfunction locateCell(editor: Editor): CellLocation | undefined {\n const selection = editor.getSelection();\n if (!selection) return undefined;\n const cellPath = findTableCellPath(editor.doc, normalizeSelection(selection).start.path);\n if (!cellPath) return undefined;\n const rowPath = cellPath.slice(0, -1);\n const tablePath = rowPath.slice(0, -1);\n return {\n cellPath,\n rowPath,\n tablePath,\n rowIndex: rowPath[rowPath.length - 1] as number,\n colIndex: cellPath[cellPath.length - 1] as number\n };\n}\n\nconst insertTableCommand: Command = {\n execute(editor: Editor, ...args: unknown[]) {\n const rowCount = typeof args[0] === \"number\" && Number.isFinite(args[0]) && args[0] > 0 ? Math.floor(args[0]) : 3;\n const colCount = typeof args[1] === \"number\" && Number.isFinite(args[1]) && args[1] > 0 ? Math.floor(args[1]) : 3;\n const selection = editor.getSelection();\n if (!selection) return false;\n const topIndex = normalizeSelection(selection).start.path[0] as number;\n\n const table = createElement(\n TABLE,\n {},\n Array.from({ length: rowCount }, () => createEmptyRow(editor.schema.defaultBlockType, colCount))\n );\n\n const tx = editor.createTransaction({ origin: \"command\", historyGroup: \"structural\" });\n tx.insertNode([topIndex + 1], table);\n tx.insertNode([topIndex + 2], createElement(editor.schema.defaultBlockType, {}, [createText(\"\")]));\n const target = selectAllWithin(tx.doc, [topIndex + 1, 0, 0]);\n if (target) tx.setSelection(target);\n editor.dispatch(tx);\n return true;\n },\n canExecute(editor: Editor) {\n return editor.getSelection() !== null;\n }\n};\n\nfunction addRowCommand(direction: \"before\" | \"after\"): Command {\n return {\n execute(editor: Editor) {\n const location = locateCell(editor);\n if (!location) return false;\n const { rowPath, tablePath, rowIndex } = location;\n const colCount = childCountAt(editor.doc, rowPath);\n const insertIndex = direction === \"before\" ? rowIndex : rowIndex + 1;\n\n const tx = editor.createTransaction({ origin: \"command\", historyGroup: \"structural\" });\n tx.insertNode([...tablePath, insertIndex], createEmptyRow(editor.schema.defaultBlockType, colCount));\n const target = selectAllWithin(tx.doc, [...tablePath, insertIndex, 0]);\n if (target) tx.setSelection(target);\n editor.dispatch(tx);\n return true;\n },\n canExecute(editor: Editor) {\n return locateCell(editor) !== undefined;\n }\n };\n}\n\nfunction addColumnCommand(direction: \"before\" | \"after\"): Command {\n return {\n execute(editor: Editor) {\n const location = locateCell(editor);\n if (!location) return false;\n const { tablePath, rowIndex, colIndex } = location;\n const insertIndex = direction === \"before\" ? colIndex : colIndex + 1;\n const rowCount = childCountAt(editor.doc, tablePath);\n\n const tx = editor.createTransaction({ origin: \"command\", historyGroup: \"structural\" });\n for (let r = 0; r < rowCount; r += 1) {\n tx.insertNode([...tablePath, r, insertIndex], createEmptyCell(editor.schema.defaultBlockType));\n }\n const target = selectAllWithin(tx.doc, [...tablePath, rowIndex, insertIndex]);\n if (target) tx.setSelection(target);\n editor.dispatch(tx);\n return true;\n },\n canExecute(editor: Editor) {\n return locateCell(editor) !== undefined;\n }\n };\n}\n\nconst deleteRowCommand: Command = {\n execute(editor: Editor) {\n const location = locateCell(editor);\n if (!location) return false;\n const { rowPath, tablePath, rowIndex, colIndex } = location;\n if (childCountAt(editor.doc, tablePath) <= 1) return false;\n\n const tx = editor.createTransaction({ origin: \"command\", historyGroup: \"structural\" });\n tx.removeNode(rowPath);\n const newRowIndex = Math.min(rowIndex, childCountAt(tx.doc, tablePath) - 1);\n const newColIndex = Math.min(colIndex, childCountAt(tx.doc, [...tablePath, newRowIndex]) - 1);\n const target = selectAllWithin(tx.doc, [...tablePath, newRowIndex, newColIndex]);\n if (target) tx.setSelection(target);\n editor.dispatch(tx);\n return true;\n },\n canExecute(editor: Editor) {\n const location = locateCell(editor);\n return location !== undefined && childCountAt(editor.doc, location.tablePath) > 1;\n }\n};\n\nconst deleteColumnCommand: Command = {\n execute(editor: Editor) {\n const location = locateCell(editor);\n if (!location) return false;\n const { tablePath, rowIndex, colIndex } = location;\n const rowCount = childCountAt(editor.doc, tablePath);\n if (childCountAt(editor.doc, [...tablePath, rowIndex]) <= 1) return false;\n\n const tx = editor.createTransaction({ origin: \"command\", historyGroup: \"structural\" });\n for (let r = 0; r < rowCount; r += 1) {\n tx.removeNode([...tablePath, r, colIndex]);\n }\n const newColIndex = Math.min(colIndex, childCountAt(tx.doc, [...tablePath, rowIndex]) - 1);\n const target = selectAllWithin(tx.doc, [...tablePath, rowIndex, newColIndex]);\n if (target) tx.setSelection(target);\n editor.dispatch(tx);\n return true;\n },\n canExecute(editor: Editor) {\n const location = locateCell(editor);\n if (!location) return false;\n return childCountAt(editor.doc, [...location.tablePath, location.rowIndex]) > 1;\n }\n};\n\nconst deleteTableCommand: Command = {\n execute(editor: Editor) {\n const location = locateCell(editor);\n if (!location) return false;\n const { tablePath } = location;\n const tableIndex = tablePath[tablePath.length - 1] as number;\n const parentPath = tablePath.slice(0, -1);\n\n const tx = editor.createTransaction({ origin: \"command\", historyGroup: \"structural\" });\n tx.removeNode(tablePath);\n const siblingCount = childCountAt(tx.doc, parentPath);\n const fallbackIndex = Math.min(tableIndex, siblingCount - 1);\n // Deleting the document's only content leaves zero siblings here — the\n // transaction's own doc has no valid position at all yet (normalization,\n // which will insert a fresh empty paragraph, hasn't run: that happens\n // later in `dispatch`) — so unlike every other command in this file,\n // there is no point to select and the pre-deletion selection must be\n // explicitly cleared rather than left in place, since it addresses a\n // path this transaction just removed.\n const point = fallbackIndex >= 0 ? firstTextPointWithin(tx.doc, [...parentPath, fallbackIndex]) : undefined;\n tx.setSelection(point ? cursor(point) : null);\n editor.dispatch(tx);\n return true;\n },\n canExecute(editor: Editor) {\n return locateCell(editor) !== undefined;\n }\n};\n\nfunction rowIsAllHeader(doc: EditorDocument, rowPath: number[]): boolean {\n const row = getNodeAtPath(doc, rowPath);\n if (row.object !== \"element\") return false;\n return row.children.length > 0 && row.children.every((cell) => cell.object === \"element\" && cell.attrs.header === true);\n}\n\nconst toggleHeaderRowCommand: Command = {\n execute(editor: Editor) {\n const location = locateCell(editor);\n if (!location) return false;\n const { tablePath } = location;\n const firstRowPath = [...tablePath, 0];\n const makeHeader = !rowIsAllHeader(editor.doc, firstRowPath);\n const columnCount = childCountAt(editor.doc, firstRowPath);\n\n const tx = editor.createTransaction({ origin: \"command\", historyGroup: \"structural\" });\n for (let c = 0; c < columnCount; c += 1) {\n tx.setNodeAttribute([...firstRowPath, c], { header: makeHeader });\n }\n const selection = editor.getSelection();\n if (selection) tx.setSelection(selection);\n editor.dispatch(tx);\n return true;\n },\n canExecute(editor: Editor) {\n return locateCell(editor) !== undefined;\n },\n isActive(editor: Editor) {\n const location = locateCell(editor);\n if (!location) return false;\n return rowIsAllHeader(editor.doc, [...location.tablePath, 0]);\n }\n};\n\nconst goToNextCellCommand: Command = {\n execute(editor: Editor) {\n const location = locateCell(editor);\n if (!location) return false;\n const { tablePath, rowPath, rowIndex, colIndex } = location;\n const rowColumnCount = childCountAt(editor.doc, rowPath);\n const rowCount = childCountAt(editor.doc, tablePath);\n\n const tx = editor.createTransaction({ origin: \"keyboard\", historyGroup: \"structural\" });\n let targetCellPath: number[];\n if (colIndex + 1 < rowColumnCount) {\n targetCellPath = [...rowPath, colIndex + 1];\n } else if (rowIndex + 1 < rowCount) {\n targetCellPath = [...tablePath, rowIndex + 1, 0];\n } else {\n tx.insertNode([...tablePath, rowCount], createEmptyRow(editor.schema.defaultBlockType, rowColumnCount));\n targetCellPath = [...tablePath, rowCount, 0];\n }\n const target = selectAllWithin(tx.doc, targetCellPath);\n if (!target) return false;\n tx.setSelection(target);\n editor.dispatch(tx);\n return true;\n },\n canExecute(editor: Editor) {\n return locateCell(editor) !== undefined;\n }\n};\n\nconst goToPreviousCellCommand: Command = {\n execute(editor: Editor) {\n const location = locateCell(editor);\n if (!location) return false;\n const { tablePath, rowPath, rowIndex, colIndex } = location;\n\n let targetCellPath: number[];\n if (colIndex - 1 >= 0) {\n targetCellPath = [...rowPath, colIndex - 1];\n } else if (rowIndex - 1 >= 0) {\n const previousColumnCount = childCountAt(editor.doc, [...tablePath, rowIndex - 1]);\n targetCellPath = [...tablePath, rowIndex - 1, previousColumnCount - 1];\n } else {\n return false;\n }\n\n const target = selectAllWithin(editor.doc, targetCellPath);\n if (!target) return false;\n const tx = editor.createTransaction({ origin: \"keyboard\", historyGroup: \"structural\" });\n tx.setSelection(target);\n editor.dispatch(tx);\n return true;\n },\n canExecute(editor: Editor) {\n return locateCell(editor) !== undefined;\n }\n};\n\n// ---- HTML ---------------------------------------------------------------\n\nfunction parseAlignStyle(el: Element): string | null {\n const style = el.getAttribute(\"style\") ?? \"\";\n const match = /text-align\\s*:\\s*([a-z]+)/i.exec(style);\n const raw = match?.[1]?.toLowerCase();\n if (raw === \"left\") return \"start\";\n if (raw === \"right\") return \"end\";\n return raw === \"start\" || raw === \"center\" || raw === \"end\" ? raw : null;\n}\n\nfunction alignStyleAttr(node: ElementNode): string {\n const align = node.attrs.align;\n return typeof align === \"string\" ? ` style=\"text-align:${align}\"` : \"\";\n}\n\nfunction parseTableCell(isHeader: boolean) {\n return (el: Element, ctx: HtmlParseContext): ElementNode => {\n const blocks = ctx.parseBlocks(el);\n // Matches `plugin-lists`' own \"li\" HTML parser: `<td>plain text</td>`\n // with no block wrapper is common real-world markup, so fall back to\n // wrapping the cell's inline content in a paragraph ourselves rather\n // than producing a cell with zero children — which, unlike an empty\n // `<blockquote>`, normalization would delete outright (content ends in\n // `+`), silently corrupting the table's column count.\n const children = blocks.length > 0 ? blocks : [createElement(\"paragraph\", {}, ctx.parseInline(el))];\n const colspan = Number(el.getAttribute(\"colspan\"));\n const rowspan = Number(el.getAttribute(\"rowspan\"));\n const align = parseAlignStyle(el);\n return createElement(\n TABLE_CELL,\n {\n header: isHeader,\n ...(Number.isInteger(colspan) && colspan > 1 ? { colspan } : {}),\n ...(Number.isInteger(rowspan) && rowspan > 1 ? { rowspan } : {}),\n ...(align ? { align } : {})\n },\n children\n );\n };\n}\n\n// ---- Markdown (GFM tables) ------------------------------------------------\n\nfunction splitTableRowCells(line: string): string[] {\n const withoutEdges = line.trim().replace(/^\\|/, \"\").replace(/\\|$/, \"\");\n const cells: string[] = [];\n let current = \"\";\n for (let i = 0; i < withoutEdges.length; i += 1) {\n const ch = withoutEdges[i];\n if (ch === \"\\\\\" && withoutEdges[i + 1] === \"|\") {\n current += \"|\";\n i += 1;\n continue;\n }\n if (ch === \"|\") {\n cells.push(current.trim());\n current = \"\";\n continue;\n }\n current += ch;\n }\n cells.push(current.trim());\n return cells;\n}\n\n// A row of only `-`/`:`-filled cells (`---`, `:---`, `---:`, `:---:`),\n// optionally pipe-delimited on either end — the GFM lookahead line that\n// disambiguates a real table from a paragraph that merely contains a `|`.\nconst TABLE_DELIMITER_ROW = /^\\s*\\|?\\s*:?-+:?\\s*(\\|\\s*:?-+:?\\s*)*\\|?\\s*$/;\n\nfunction delimiterCellAlign(cell: string): string | null {\n const trimmed = cell.trim();\n const left = trimmed.startsWith(\":\");\n const right = trimmed.endsWith(\":\");\n if (left && right) return \"center\";\n if (right) return \"end\";\n if (left) return \"start\";\n return null;\n}\n\n/** A cell's Markdown export is inline-content-only (GFM table cells can't\n * hold block structure), so multi-paragraph cell content — possible when a\n * cell was built via HTML import — is flattened onto one line, and a\n * literal `|` is escaped so it can't be misread as a column boundary. */\nfunction serializeTableCell(_node: ElementNode, children: string[]): string {\n return children\n .join(\" \")\n .replace(/\\s*\\n+\\s*/g, \" \")\n .trim()\n .replace(/\\|/g, \"\\\\|\");\n}\n\n/** Reads column alignment from the header row (row 0) — the only row GFM's\n * delimiter syntax can express alignment for — rather than per body cell. */\nfunction tableDelimiterRow(node: ElementNode): string {\n const firstRow = node.children[0];\n const cells = firstRow && firstRow.object === \"element\" ? firstRow.children : [];\n if (cells.length === 0) return \"| |\";\n const parts = cells.map((cell) => {\n const align = cell.object === \"element\" && typeof cell.attrs.align === \"string\" ? cell.attrs.align : null;\n return align === \"center\" ? \":---:\" : align === \"end\" ? \"---:\" : align === \"start\" ? \":---\" : \"---\";\n });\n return `| ${parts.join(\" | \")} |`;\n}\n\n/**\n * A block-level table, row and cell: schema, row/column/table structural\n * commands, Tab/Shift-Tab cell navigation, and HTML/GFM-Markdown\n * import/export. Deliberately its own package rather than folded into\n * `plugin-basic-blocks` — tables are the heaviest, most structurally\n * involved feature in this editor (nested content model, nested-child\n * commands, dedicated keyboard handling), and plenty of consumers never\n * need them at all.\n *\n * Scope, documented rather than silently missing: there is no rectangular\n * multi-cell *selection* in this editor (see `plugin-image`'s doc comment\n * on the same underlying gap), so there is no interactive \"merge cells\"\n * command — `colspan`/`rowspan` are still fully understood on import/export\n * for HTML round-tripping (e.g. a pasted table with merged cells keeps its\n * shape), just not something a user can create from inside the editor yet.\n * Markdown (GFM) tables are a plain grid with no span concept at all, so a\n * cell's `colspan`/`rowspan` has no Markdown representation and is not\n * preserved through a Markdown export/import round trip — only the HTML\n * round trip is lossless for spans.\n *\n * Plugin order matters when combined with `plugin-lists`: both bind `Tab`/\n * `Shift-Tab` (see `Editor.keymap`'s doc comment for the chain-of-responsibility\n * resolution this relies on), and a list nested inside a cell is valid content\n * (`tableCell`'s own content is bare `block+`). Register `ListsPlugin` *before*\n * `TablePlugin` in the `plugins` array so Tab indents such a nested list item\n * instead of jumping to the next cell — the reverse order silently prefers\n * cell navigation instead, since each plugin's binding is tried in\n * registration order and `goToNextCell`/`goToPreviousCell` only look for the\n * nearest `tableCell` ancestor, not whether a closer list item sits between it\n * and the cursor.\n */\nexport function TablePlugin(): Plugin {\n return {\n name: \"table\",\n schema: {\n nodes: [\n defineNode({ name: TABLE, group: \"block\", content: \"tableRow+\" }),\n defineNode({ name: TABLE_ROW, group: \"block\", content: \"tableCell+\" }),\n defineNode({\n name: TABLE_CELL,\n group: \"block\",\n content: \"block+\",\n attrs: { header: { default: false }, colspan: { default: 1 }, rowspan: { default: 1 }, align: { default: null } }\n })\n ]\n },\n commands: () => ({\n insertTable: insertTableCommand,\n addRowBefore: addRowCommand(\"before\"),\n addRowAfter: addRowCommand(\"after\"),\n addColumnBefore: addColumnCommand(\"before\"),\n addColumnAfter: addColumnCommand(\"after\"),\n deleteRow: deleteRowCommand,\n deleteColumn: deleteColumnCommand,\n deleteTable: deleteTableCommand,\n toggleHeaderRow: toggleHeaderRowCommand,\n goToNextCell: goToNextCellCommand,\n goToPreviousCell: goToPreviousCellCommand\n }),\n keymap: () => ({\n Tab: \"goToNextCell\",\n \"Shift-Tab\": \"goToPreviousCell\"\n }),\n htmlSerializers: {\n // Wrapped in a scroll container so a table wider than the editor\n // (many columns, or one long unbroken cell value) scrolls horizontally\n // in place instead of overflowing the writing area — see the\n // `.rte-table-scroll` rule in `@regal-text-editor/ui`'s stylesheet, and\n // `effectiveChildren`/`domElementToBlockPath` in\n // `@regal-text-editor/browser`'s `dom/positions.ts`, which already\n // treat this wrapper as transparent for caret/selection mapping.\n [TABLE]: (_node, children) => `<div class=\"rte-table-scroll\"><table><tbody>${children.join(\"\")}</tbody></table></div>`,\n [TABLE_ROW]: (_node, children) => `<tr>${children.join(\"\")}</tr>`,\n [TABLE_CELL]: (node, children) => {\n const tag = node.attrs.header === true ? \"th\" : \"td\";\n const colspan = typeof node.attrs.colspan === \"number\" && node.attrs.colspan > 1 ? ` colspan=\"${node.attrs.colspan}\"` : \"\";\n const rowspan = typeof node.attrs.rowspan === \"number\" && node.attrs.rowspan > 1 ? ` rowspan=\"${node.attrs.rowspan}\"` : \"\";\n return `<${tag}${colspan}${rowspan}${alignStyleAttr(node)}>${children.join(\"\")}</${tag}>`;\n }\n },\n markdownSerializers: {\n [TABLE_CELL]: serializeTableCell,\n [TABLE_ROW]: (_node, children) => `| ${children.join(\" | \")} |`,\n [TABLE]: (node, children) => [children[0] ?? \"\", tableDelimiterRow(node), ...children.slice(1)].join(\"\\n\")\n },\n htmlParsers: (registry) => {\n registry.registerBlock(TABLE.toLowerCase(), (el, ctx) => {\n // `el`'s direct children may be a real or browser-implied\n // `<tbody>`/`<thead>`/`<tfoot>` — core's generic `parseBlocks`\n // already treats any tag it has no parser for as a transparent\n // container and recurses into it, so it reaches our \"tr\" parser\n // either way without this plugin needing its own DOM-shape logic.\n const rows = ctx.parseBlocks(el);\n return rows.length > 0 ? createElement(TABLE, {}, rows) : null;\n });\n registry.registerBlock(\"tr\", (el, ctx) => {\n const cells = ctx.parseBlocks(el);\n return cells.length > 0 ? createElement(TABLE_ROW, {}, cells) : null;\n });\n registry.registerBlock(\"td\", parseTableCell(false));\n registry.registerBlock(\"th\", parseTableCell(true));\n },\n markdownParsers: (registry) => {\n registry.registerBlock((lines, index, ctx) => {\n const headerLine = lines[index] ?? \"\";\n const delimiterLine = lines[index + 1] ?? \"\";\n if (!headerLine.includes(\"|\") || !TABLE_DELIMITER_ROW.test(delimiterLine)) return null;\n\n const headerCells = splitTableRowCells(headerLine);\n const delimiterCells = splitTableRowCells(delimiterLine);\n if (headerCells.length === 0 || delimiterCells.length === 0) return null;\n\n const columnCount = headerCells.length;\n const aligns = Array.from({ length: columnCount }, (_unused, i) => delimiterCellAlign(delimiterCells[i] ?? \"\"));\n const normalizeRow = (cells: string[]): string[] => {\n const row = cells.slice(0, columnCount);\n while (row.length < columnCount) row.push(\"\");\n return row;\n };\n const makeCell = (text: string, columnIndex: number, isHeader: boolean): ElementNode => {\n const align = aligns[columnIndex] ?? null;\n return createElement(TABLE_CELL, { header: isHeader, ...(align ? { align } : {}) }, [\n createElement(\"paragraph\", {}, ctx.parseInline(text))\n ]);\n };\n\n const rows = [createElement(TABLE_ROW, {}, normalizeRow(headerCells).map((text, i) => makeCell(text, i, true)))];\n let cursorIndex = index + 2;\n while (cursorIndex < lines.length) {\n const line = lines[cursorIndex] ?? \"\";\n if (line.trim().length === 0 || !line.includes(\"|\")) break;\n const cells = normalizeRow(splitTableRowCells(line));\n rows.push(createElement(TABLE_ROW, {}, cells.map((text, i) => makeCell(text, i, false))));\n cursorIndex += 1;\n }\n\n return { nodes: [createElement(TABLE, {}, rows)], nextIndex: cursorIndex };\n });\n },\n slashMenuItems: (registry) => {\n // A fixed 3x3 default, same as clicking the toolbar's `<TableButton>`\n // without dragging the size picker — rows/columns are trivial to add\n // or remove afterward via `tableToolbarItems`, so a slash item (which,\n // unlike that button, can't offer an interactive size picker) doesn't\n // need to.\n registry.register({ id: \"table\", title: \"Table\", keywords: [\"table\", \"grid\"], icon: \"table\", command: \"insertTable\", args: [3, 3] });\n }\n };\n}\n"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@regal-text-editor/plugin-table",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"publishConfig": {
|
|
5
|
+
"access": "public"
|
|
6
|
+
},
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"description": "Table editing: schema, row/column commands, keyboard navigation, and HTML/Markdown import/export.",
|
|
9
|
+
"keywords": [
|
|
10
|
+
"rich-text-editor",
|
|
11
|
+
"wysiwyg",
|
|
12
|
+
"editor",
|
|
13
|
+
"contenteditable",
|
|
14
|
+
"regal-text-editor",
|
|
15
|
+
"table",
|
|
16
|
+
"tables"
|
|
17
|
+
],
|
|
18
|
+
"author": {
|
|
19
|
+
"name": "mohammad-mirzaie-gh",
|
|
20
|
+
"url": "https://github.com/mohammad-mirzaie-gh"
|
|
21
|
+
},
|
|
22
|
+
"repository": {
|
|
23
|
+
"type": "git",
|
|
24
|
+
"url": "git+https://github.com/mohammad-mirzaie-gh/regal-text-editor.git",
|
|
25
|
+
"directory": "packages/plugin-table"
|
|
26
|
+
},
|
|
27
|
+
"bugs": {
|
|
28
|
+
"url": "https://github.com/mohammad-mirzaie-gh/regal-text-editor/issues"
|
|
29
|
+
},
|
|
30
|
+
"homepage": "https://regal-text-editor.vercel.app",
|
|
31
|
+
"type": "module",
|
|
32
|
+
"main": "./dist/index.js",
|
|
33
|
+
"module": "./dist/index.js",
|
|
34
|
+
"types": "./dist/index.d.ts",
|
|
35
|
+
"exports": {
|
|
36
|
+
".": {
|
|
37
|
+
"types": "./dist/index.d.ts",
|
|
38
|
+
"import": "./dist/index.js"
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"files": [
|
|
42
|
+
"dist"
|
|
43
|
+
],
|
|
44
|
+
"sideEffects": false,
|
|
45
|
+
"dependencies": {
|
|
46
|
+
"@regal-text-editor/core": "0.2.0"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"tsup": "^8.3.5",
|
|
50
|
+
"typescript": "^5.7.2",
|
|
51
|
+
"@regal-text-editor/plugin-basic-blocks": "0.2.0",
|
|
52
|
+
"@regal-text-editor/plugin-basic-marks": "0.2.0"
|
|
53
|
+
},
|
|
54
|
+
"scripts": {
|
|
55
|
+
"build": "tsup src/index.ts --format esm --dts --sourcemap --clean",
|
|
56
|
+
"dev": "tsup src/index.ts --format esm --dts --sourcemap --watch"
|
|
57
|
+
}
|
|
58
|
+
}
|