@shadow-garden/bapbong-commands 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 +46 -0
- package/dist/index.cjs +818 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +752 -0
- package/dist/lib/edit.d.ts +4 -0
- package/dist/lib/edit.d.ts.map +1 -0
- package/dist/lib/history.d.ts +6 -0
- package/dist/lib/history.d.ts.map +1 -0
- package/dist/lib/insert.d.ts +11 -0
- package/dist/lib/insert.d.ts.map +1 -0
- package/dist/lib/list.d.ts +14 -0
- package/dist/lib/list.d.ts.map +1 -0
- package/dist/lib/marks.d.ts +53 -0
- package/dist/lib/marks.d.ts.map +1 -0
- package/dist/lib/paragraph.d.ts +25 -0
- package/dist/lib/paragraph.d.ts.map +1 -0
- package/dist/lib/registry.d.ts +15 -0
- package/dist/lib/registry.d.ts.map +1 -0
- package/dist/lib/sections.d.ts +32 -0
- package/dist/lib/sections.d.ts.map +1 -0
- package/dist/lib/table-structure.d.ts +31 -0
- package/dist/lib/table-structure.d.ts.map +1 -0
- package/dist/lib/table.d.ts +36 -0
- package/dist/lib/table.d.ts.map +1 -0
- package/package.json +65 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,752 @@
|
|
|
1
|
+
// packages/commands/src/lib/marks.ts
|
|
2
|
+
import { toggleMark } from "prosemirror-commands";
|
|
3
|
+
var matches = (mark, attrs) => !attrs || Object.entries(attrs).every(([k, v]) => mark.attrs[k] === v);
|
|
4
|
+
function isMarkActive(state, markName, attrs) {
|
|
5
|
+
const type = state.schema.marks[markName];
|
|
6
|
+
if (!type) return false;
|
|
7
|
+
const { from, to, empty, $from } = state.selection;
|
|
8
|
+
if (empty) {
|
|
9
|
+
const mark = type.isInSet(state.storedMarks ?? $from.marks());
|
|
10
|
+
return !!mark && matches(mark, attrs);
|
|
11
|
+
}
|
|
12
|
+
if (!attrs) return state.doc.rangeHasMark(from, to, type);
|
|
13
|
+
let found = false;
|
|
14
|
+
state.doc.nodesBetween(from, to, (node) => {
|
|
15
|
+
if (found || !node.isText) return;
|
|
16
|
+
const mark = node.marks.find((m) => m.type === type);
|
|
17
|
+
if (mark && matches(mark, attrs)) found = true;
|
|
18
|
+
});
|
|
19
|
+
return found;
|
|
20
|
+
}
|
|
21
|
+
function toggleMarkCommand(name, markName = name, attrs) {
|
|
22
|
+
return {
|
|
23
|
+
name,
|
|
24
|
+
run(state, dispatch) {
|
|
25
|
+
const type = state.schema.marks[markName];
|
|
26
|
+
return type ? toggleMark(type, attrs)(state, dispatch) : false;
|
|
27
|
+
},
|
|
28
|
+
isActive: (state) => isMarkActive(state, markName, attrs),
|
|
29
|
+
isEnabled: (state) => !!state.schema.marks[markName]
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function setMarkAttr(markName, attrs) {
|
|
33
|
+
return {
|
|
34
|
+
name: `set-${markName}`,
|
|
35
|
+
run(state, dispatch) {
|
|
36
|
+
const type = state.schema.marks[markName];
|
|
37
|
+
if (!type) return false;
|
|
38
|
+
if (dispatch) {
|
|
39
|
+
const { from, to, empty } = state.selection;
|
|
40
|
+
const tr = state.tr;
|
|
41
|
+
if (empty) {
|
|
42
|
+
tr.removeStoredMark(type);
|
|
43
|
+
if (attrs) tr.addStoredMark(type.create(attrs));
|
|
44
|
+
} else {
|
|
45
|
+
tr.removeMark(from, to, type);
|
|
46
|
+
if (attrs) tr.addMark(from, to, type.create(attrs));
|
|
47
|
+
}
|
|
48
|
+
dispatch(tr.scrollIntoView());
|
|
49
|
+
}
|
|
50
|
+
return true;
|
|
51
|
+
},
|
|
52
|
+
isEnabled: (state) => !!state.schema.marks[markName]
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function activeMarkValue(state, markName, attrKey) {
|
|
56
|
+
const type = state.schema.marks[markName];
|
|
57
|
+
if (!type) return null;
|
|
58
|
+
const { from, to, empty, $from } = state.selection;
|
|
59
|
+
const valueOf = (marks) => {
|
|
60
|
+
const mark = type.isInSet(marks);
|
|
61
|
+
return mark ? mark.attrs[attrKey] : null;
|
|
62
|
+
};
|
|
63
|
+
if (empty) return valueOf(state.storedMarks ?? $from.marks());
|
|
64
|
+
let value;
|
|
65
|
+
let seen = false;
|
|
66
|
+
state.doc.nodesBetween(from, to, (node) => {
|
|
67
|
+
if (!node.isText) return;
|
|
68
|
+
const v = valueOf(node.marks);
|
|
69
|
+
if (!seen) {
|
|
70
|
+
value = v;
|
|
71
|
+
seen = true;
|
|
72
|
+
} else if (value !== v) {
|
|
73
|
+
value = null;
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
return seen ? value : null;
|
|
77
|
+
}
|
|
78
|
+
function setFontSize(pt) {
|
|
79
|
+
return setMarkAttr("fontSize", pt == null ? null : { size: pt });
|
|
80
|
+
}
|
|
81
|
+
function activeFontSize(state) {
|
|
82
|
+
const v = activeMarkValue(state, "fontSize", "size");
|
|
83
|
+
return typeof v === "number" ? v : null;
|
|
84
|
+
}
|
|
85
|
+
function setFontFamily(family) {
|
|
86
|
+
return setMarkAttr("fontFamily", family ? { family } : null);
|
|
87
|
+
}
|
|
88
|
+
function activeFontFamily(state) {
|
|
89
|
+
const v = activeMarkValue(state, "fontFamily", "family");
|
|
90
|
+
return typeof v === "string" ? v : null;
|
|
91
|
+
}
|
|
92
|
+
function setTextColor(color) {
|
|
93
|
+
return setMarkAttr("textColor", color ? { color } : null);
|
|
94
|
+
}
|
|
95
|
+
function activeTextColor(state) {
|
|
96
|
+
const v = activeMarkValue(state, "textColor", "color");
|
|
97
|
+
return typeof v === "string" ? v : null;
|
|
98
|
+
}
|
|
99
|
+
function clearMarks() {
|
|
100
|
+
return {
|
|
101
|
+
name: "clear-format",
|
|
102
|
+
run(state, dispatch) {
|
|
103
|
+
const { from, to, empty } = state.selection;
|
|
104
|
+
if (dispatch) {
|
|
105
|
+
const tr = state.tr;
|
|
106
|
+
if (empty) tr.setStoredMarks([]);
|
|
107
|
+
else tr.removeMark(from, to);
|
|
108
|
+
dispatch(tr.scrollIntoView());
|
|
109
|
+
}
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
function setHighlight(color) {
|
|
115
|
+
return setMarkAttr("highlight", color ? { color } : null);
|
|
116
|
+
}
|
|
117
|
+
function activeHighlight(state) {
|
|
118
|
+
const v = activeMarkValue(state, "highlight", "color");
|
|
119
|
+
return typeof v === "string" ? v : null;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// packages/commands/src/lib/paragraph.ts
|
|
123
|
+
var ALIGNMENTS = ["left", "center", "right", "justify"];
|
|
124
|
+
function activeAlign(state) {
|
|
125
|
+
const { from, to } = state.selection;
|
|
126
|
+
let seen;
|
|
127
|
+
state.doc.nodesBetween(from, to, (node) => {
|
|
128
|
+
if (node.type.name !== "paragraph") return;
|
|
129
|
+
const a = node.attrs["align"] ?? null;
|
|
130
|
+
seen = seen === void 0 ? a : seen === a ? seen : null;
|
|
131
|
+
});
|
|
132
|
+
return seen ?? null;
|
|
133
|
+
}
|
|
134
|
+
function setAlign(align) {
|
|
135
|
+
return {
|
|
136
|
+
name: align ? `align-${align}` : "align-clear",
|
|
137
|
+
run(state, dispatch) {
|
|
138
|
+
const { from, to } = state.selection;
|
|
139
|
+
const tr = state.tr;
|
|
140
|
+
let changed = false;
|
|
141
|
+
state.doc.nodesBetween(from, to, (node, pos) => {
|
|
142
|
+
if (node.type.name !== "paragraph" || node.attrs["align"] === align) return;
|
|
143
|
+
tr.setNodeAttribute(pos, "align", align);
|
|
144
|
+
changed = true;
|
|
145
|
+
});
|
|
146
|
+
if (changed && dispatch) dispatch(tr);
|
|
147
|
+
return changed;
|
|
148
|
+
},
|
|
149
|
+
isActive: (state) => activeAlign(state) === align
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
function selectedParagraphs(state) {
|
|
153
|
+
const { from, to } = state.selection;
|
|
154
|
+
const out = [];
|
|
155
|
+
state.doc.nodesBetween(from, to, (node, pos) => {
|
|
156
|
+
if (node.type.name === "paragraph") out.push({ pos, node });
|
|
157
|
+
});
|
|
158
|
+
return out;
|
|
159
|
+
}
|
|
160
|
+
function toggleHeading(level) {
|
|
161
|
+
return {
|
|
162
|
+
name: `heading-${level}`,
|
|
163
|
+
run(state, dispatch) {
|
|
164
|
+
const paras = selectedParagraphs(state);
|
|
165
|
+
if (paras.length === 0) return false;
|
|
166
|
+
if (dispatch) {
|
|
167
|
+
const allThisLevel = paras.every((p) => p.node.attrs["heading"] === level);
|
|
168
|
+
const next = allThisLevel ? null : level;
|
|
169
|
+
const tr = state.tr;
|
|
170
|
+
for (const p of paras) tr.setNodeAttribute(p.pos, "heading", next);
|
|
171
|
+
dispatch(tr.scrollIntoView());
|
|
172
|
+
}
|
|
173
|
+
return true;
|
|
174
|
+
},
|
|
175
|
+
isActive: (state) => {
|
|
176
|
+
const paras = selectedParagraphs(state);
|
|
177
|
+
return paras.length > 0 && paras.every((p) => p.node.attrs["heading"] === level);
|
|
178
|
+
},
|
|
179
|
+
isEnabled: (state) => selectedParagraphs(state).length > 0
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// packages/commands/src/lib/list.ts
|
|
184
|
+
var NUM_ID = { bullet: "bb-bullet", ordered: "bb-ordered" };
|
|
185
|
+
var DEF = {
|
|
186
|
+
bullet: {
|
|
187
|
+
key: "bb-bullet",
|
|
188
|
+
levels: {
|
|
189
|
+
0: { numFmt: "bullet", lvlText: "\u2022", start: 1 },
|
|
190
|
+
1: { numFmt: "bullet", lvlText: "\u25E6", start: 1 },
|
|
191
|
+
2: { numFmt: "bullet", lvlText: "\u25AA", start: 1 }
|
|
192
|
+
}
|
|
193
|
+
},
|
|
194
|
+
ordered: {
|
|
195
|
+
key: "bb-ordered",
|
|
196
|
+
levels: {
|
|
197
|
+
0: { numFmt: "decimal", lvlText: "%1.", start: 1 },
|
|
198
|
+
1: { numFmt: "lowerLetter", lvlText: "%2.", start: 1 },
|
|
199
|
+
2: { numFmt: "lowerRoman", lvlText: "%3.", start: 1 }
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
function paragraphsInSelection(state) {
|
|
204
|
+
const { from, to } = state.selection;
|
|
205
|
+
const out = [];
|
|
206
|
+
state.doc.nodesBetween(from, to, (node, pos) => {
|
|
207
|
+
if (node.type.name === "paragraph") out.push({ pos, node });
|
|
208
|
+
});
|
|
209
|
+
return out;
|
|
210
|
+
}
|
|
211
|
+
function listNumId(node) {
|
|
212
|
+
return node.attrs["list"]?.numId ?? null;
|
|
213
|
+
}
|
|
214
|
+
function toggleList(kind) {
|
|
215
|
+
const numId = NUM_ID[kind];
|
|
216
|
+
return {
|
|
217
|
+
name: kind === "bullet" ? "bullet-list" : "ordered-list",
|
|
218
|
+
run(state, dispatch) {
|
|
219
|
+
const paras = paragraphsInSelection(state);
|
|
220
|
+
if (paras.length === 0) return false;
|
|
221
|
+
if (dispatch) {
|
|
222
|
+
const allThisKind = paras.every((p) => listNumId(p.node) === numId);
|
|
223
|
+
let tr = state.tr;
|
|
224
|
+
if (allThisKind) {
|
|
225
|
+
for (const p of paras) tr = tr.setNodeAttribute(p.pos, "list", null);
|
|
226
|
+
} else {
|
|
227
|
+
const defs = { ...state.doc.attrs["numbering"] ?? {} };
|
|
228
|
+
if (!defs[numId]) {
|
|
229
|
+
defs[numId] = DEF[kind];
|
|
230
|
+
tr = tr.setDocAttribute("numbering", defs);
|
|
231
|
+
}
|
|
232
|
+
for (const p of paras) {
|
|
233
|
+
const level = p.node.attrs["list"]?.level ?? 0;
|
|
234
|
+
tr = tr.setNodeAttribute(p.pos, "list", { numId, level });
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
dispatch(tr.scrollIntoView());
|
|
238
|
+
}
|
|
239
|
+
return true;
|
|
240
|
+
},
|
|
241
|
+
isActive(state) {
|
|
242
|
+
const paras = paragraphsInSelection(state);
|
|
243
|
+
return paras.length > 0 && paras.every((p) => listNumId(p.node) === numId);
|
|
244
|
+
},
|
|
245
|
+
isEnabled: (state) => paragraphsInSelection(state).length > 0
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// packages/commands/src/lib/table.ts
|
|
250
|
+
function cellAt(state) {
|
|
251
|
+
const $head = state.selection.$head;
|
|
252
|
+
for (let d = $head.depth; d > 0; d--) {
|
|
253
|
+
const node = $head.node(d);
|
|
254
|
+
if (node.type.name === "table_cell") return { pos: $head.before(d), node };
|
|
255
|
+
}
|
|
256
|
+
return null;
|
|
257
|
+
}
|
|
258
|
+
function setCellAttrs(pos, attrs) {
|
|
259
|
+
return {
|
|
260
|
+
name: "cell-attrs",
|
|
261
|
+
run(state, dispatch) {
|
|
262
|
+
const cell = state.doc.nodeAt(pos);
|
|
263
|
+
if (!cell || cell.type.name !== "table_cell") return false;
|
|
264
|
+
if (dispatch) {
|
|
265
|
+
const tr = state.tr;
|
|
266
|
+
for (const [key, value] of Object.entries(attrs)) tr.setNodeAttribute(pos, key, value);
|
|
267
|
+
dispatch(tr);
|
|
268
|
+
}
|
|
269
|
+
return true;
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
function setCellsAttrs(positions, attrs) {
|
|
274
|
+
return {
|
|
275
|
+
name: "cells-attrs",
|
|
276
|
+
run(state, dispatch) {
|
|
277
|
+
const cells = positions.filter((pos) => state.doc.nodeAt(pos)?.type.name === "table_cell");
|
|
278
|
+
if (cells.length === 0) return false;
|
|
279
|
+
if (dispatch) {
|
|
280
|
+
const tr = state.tr;
|
|
281
|
+
for (const pos of cells) {
|
|
282
|
+
for (const [key, value] of Object.entries(attrs)) tr.setNodeAttribute(pos, key, value);
|
|
283
|
+
}
|
|
284
|
+
dispatch(tr);
|
|
285
|
+
}
|
|
286
|
+
return true;
|
|
287
|
+
}
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
function setCellBackground(color) {
|
|
291
|
+
return {
|
|
292
|
+
name: "cell-background",
|
|
293
|
+
run: (state, dispatch) => {
|
|
294
|
+
const cell = cellAt(state);
|
|
295
|
+
return cell ? setCellAttrs(cell.pos, { background: color }).run(state, dispatch) : false;
|
|
296
|
+
},
|
|
297
|
+
isEnabled: (state) => cellAt(state) != null
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
function setColumnWidth(cellPos, width) {
|
|
301
|
+
return {
|
|
302
|
+
name: "column-width",
|
|
303
|
+
run: (state, dispatch) => setCellAttrs(cellPos, { colwidth: [width] }).run(state, dispatch)
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// packages/commands/src/lib/table-structure.ts
|
|
308
|
+
function tableContext(state) {
|
|
309
|
+
const $head = state.selection.$head;
|
|
310
|
+
let cellDepth = -1;
|
|
311
|
+
for (let d = $head.depth; d > 0; d--) {
|
|
312
|
+
if ($head.node(d).type.name === "table_cell") {
|
|
313
|
+
cellDepth = d;
|
|
314
|
+
break;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
if (cellDepth < 2) return null;
|
|
318
|
+
return {
|
|
319
|
+
tablePos: $head.before(cellDepth - 2),
|
|
320
|
+
table: $head.node(cellDepth - 2),
|
|
321
|
+
rowPos: $head.before(cellDepth - 1),
|
|
322
|
+
row: $head.node(cellDepth - 1),
|
|
323
|
+
rowIndex: $head.index(cellDepth - 2),
|
|
324
|
+
cellIndex: $head.index(cellDepth - 1)
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
function emptyCell(state, attrs) {
|
|
328
|
+
const cell = state.schema.nodes["table_cell"];
|
|
329
|
+
const paragraph = state.schema.nodes["paragraph"];
|
|
330
|
+
return cell.create(attrs, paragraph.create());
|
|
331
|
+
}
|
|
332
|
+
function insertRow(below) {
|
|
333
|
+
return {
|
|
334
|
+
name: below ? "insert-row-below" : "insert-row-above",
|
|
335
|
+
run(state, dispatch) {
|
|
336
|
+
const cx = tableContext(state);
|
|
337
|
+
if (!cx) return false;
|
|
338
|
+
if (dispatch) {
|
|
339
|
+
const cells = [];
|
|
340
|
+
cx.row.forEach(
|
|
341
|
+
(cell) => cells.push(emptyCell(state, { colwidth: cell.attrs["colwidth"], colspan: cell.attrs["colspan"] }))
|
|
342
|
+
);
|
|
343
|
+
const newRow = state.schema.nodes["table_row"].create(null, cells);
|
|
344
|
+
const at = below ? cx.rowPos + cx.row.nodeSize : cx.rowPos;
|
|
345
|
+
dispatch(state.tr.insert(at, newRow).scrollIntoView());
|
|
346
|
+
}
|
|
347
|
+
return true;
|
|
348
|
+
},
|
|
349
|
+
isEnabled: (state) => tableContext(state) != null
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
function insertColumn(right) {
|
|
353
|
+
return {
|
|
354
|
+
name: right ? "insert-column-right" : "insert-column-left",
|
|
355
|
+
run(state, dispatch) {
|
|
356
|
+
const cx = tableContext(state);
|
|
357
|
+
if (!cx) return false;
|
|
358
|
+
if (dispatch) {
|
|
359
|
+
const targetIndex = right ? cx.cellIndex + 1 : cx.cellIndex;
|
|
360
|
+
const curCell = cx.row.maybeChild(Math.min(cx.cellIndex, cx.row.childCount - 1));
|
|
361
|
+
const colwidth = curCell?.attrs["colwidth"] ?? null;
|
|
362
|
+
const inserts = [];
|
|
363
|
+
let rowStart = cx.tablePos + 1;
|
|
364
|
+
cx.table.forEach((row) => {
|
|
365
|
+
let pos = rowStart + 1;
|
|
366
|
+
row.forEach((cell, offset, index) => {
|
|
367
|
+
if (index < targetIndex) pos += cell.nodeSize;
|
|
368
|
+
});
|
|
369
|
+
inserts.push(pos);
|
|
370
|
+
rowStart += row.nodeSize;
|
|
371
|
+
});
|
|
372
|
+
let tr = state.tr;
|
|
373
|
+
for (const pos of inserts) {
|
|
374
|
+
tr = tr.insert(tr.mapping.map(pos), emptyCell(state, { colwidth }));
|
|
375
|
+
}
|
|
376
|
+
dispatch(tr.scrollIntoView());
|
|
377
|
+
}
|
|
378
|
+
return true;
|
|
379
|
+
},
|
|
380
|
+
isEnabled: (state) => tableContext(state) != null
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
function deleteRow() {
|
|
384
|
+
return {
|
|
385
|
+
name: "delete-row",
|
|
386
|
+
run(state, dispatch) {
|
|
387
|
+
const cx = tableContext(state);
|
|
388
|
+
if (!cx) return false;
|
|
389
|
+
if (dispatch) {
|
|
390
|
+
const tr = cx.table.childCount <= 1 ? state.tr.delete(cx.tablePos, cx.tablePos + cx.table.nodeSize) : state.tr.delete(cx.rowPos, cx.rowPos + cx.row.nodeSize);
|
|
391
|
+
dispatch(tr.scrollIntoView());
|
|
392
|
+
}
|
|
393
|
+
return true;
|
|
394
|
+
},
|
|
395
|
+
isEnabled: (state) => tableContext(state) != null
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
function deleteColumn() {
|
|
399
|
+
return {
|
|
400
|
+
name: "delete-column",
|
|
401
|
+
run(state, dispatch) {
|
|
402
|
+
const cx = tableContext(state);
|
|
403
|
+
if (!cx) return false;
|
|
404
|
+
if (dispatch) {
|
|
405
|
+
if (cx.row.childCount <= 1) {
|
|
406
|
+
dispatch(state.tr.delete(cx.tablePos, cx.tablePos + cx.table.nodeSize).scrollIntoView());
|
|
407
|
+
return true;
|
|
408
|
+
}
|
|
409
|
+
const dels = [];
|
|
410
|
+
let rowStart = cx.tablePos + 1;
|
|
411
|
+
cx.table.forEach((row) => {
|
|
412
|
+
let pos = rowStart + 1;
|
|
413
|
+
row.forEach((cell2, _offset, index) => {
|
|
414
|
+
if (index < cx.cellIndex) pos += cell2.nodeSize;
|
|
415
|
+
});
|
|
416
|
+
const cell = row.maybeChild(cx.cellIndex);
|
|
417
|
+
if (cell) dels.push({ from: pos, to: pos + cell.nodeSize });
|
|
418
|
+
rowStart += row.nodeSize;
|
|
419
|
+
});
|
|
420
|
+
let tr = state.tr;
|
|
421
|
+
for (const d of dels) {
|
|
422
|
+
tr = tr.delete(tr.mapping.map(d.from), tr.mapping.map(d.to));
|
|
423
|
+
}
|
|
424
|
+
dispatch(tr.scrollIntoView());
|
|
425
|
+
}
|
|
426
|
+
return true;
|
|
427
|
+
},
|
|
428
|
+
isEnabled: (state) => tableContext(state) != null
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
function mergeCells(cells, rows, cols) {
|
|
432
|
+
return {
|
|
433
|
+
name: "merge-cells",
|
|
434
|
+
run(state, dispatch) {
|
|
435
|
+
if (cells.length < 2) return false;
|
|
436
|
+
const located = cells.map((c) => ({ c, node: state.doc.nodeAt(c.pos) })).filter((x) => x.node?.type.name === "table_cell");
|
|
437
|
+
if (located.length < 2) return false;
|
|
438
|
+
const tl = located.find((x) => x.c.row === 0 && x.c.col === 0);
|
|
439
|
+
if (!tl) return false;
|
|
440
|
+
if (dispatch) {
|
|
441
|
+
const cellType = state.schema.nodes["table_cell"];
|
|
442
|
+
let colwidth = [];
|
|
443
|
+
for (const x of located.filter((x2) => x2.c.row === 0).sort((a, b) => a.c.col - b.c.col)) {
|
|
444
|
+
const cw = x.node.attrs["colwidth"];
|
|
445
|
+
if (!cw) {
|
|
446
|
+
colwidth = null;
|
|
447
|
+
break;
|
|
448
|
+
}
|
|
449
|
+
colwidth.push(...cw);
|
|
450
|
+
}
|
|
451
|
+
const content = [];
|
|
452
|
+
tl.node.forEach((child) => content.push(child));
|
|
453
|
+
for (const x of located) {
|
|
454
|
+
if (x === tl) continue;
|
|
455
|
+
x.node.forEach((child) => {
|
|
456
|
+
if (!child.isTextblock || child.textContent.trim().length > 0) content.push(child);
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
const merged = cellType.create({ ...tl.node.attrs, colspan: cols, rowspan: rows, colwidth }, content);
|
|
460
|
+
const ops = located.map((x) => ({ from: x.c.pos, to: x.c.pos + x.node.nodeSize, node: x === tl ? merged : null })).sort((a, b) => b.from - a.from);
|
|
461
|
+
let tr = state.tr;
|
|
462
|
+
for (const op of ops) tr = op.node ? tr.replaceWith(op.from, op.to, op.node) : tr.delete(op.from, op.to);
|
|
463
|
+
dispatch(tr.scrollIntoView());
|
|
464
|
+
}
|
|
465
|
+
return true;
|
|
466
|
+
}
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
function deleteTable() {
|
|
470
|
+
return {
|
|
471
|
+
name: "delete-table",
|
|
472
|
+
run(state, dispatch) {
|
|
473
|
+
const cx = tableContext(state);
|
|
474
|
+
if (!cx) return false;
|
|
475
|
+
if (dispatch) dispatch(state.tr.delete(cx.tablePos, cx.tablePos + cx.table.nodeSize).scrollIntoView());
|
|
476
|
+
return true;
|
|
477
|
+
},
|
|
478
|
+
isEnabled: (state) => tableContext(state) != null
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// packages/commands/src/lib/history.ts
|
|
483
|
+
import { undo, redo, undoDepth, redoDepth } from "prosemirror-history";
|
|
484
|
+
var depth = (fn, state) => {
|
|
485
|
+
try {
|
|
486
|
+
return fn(state);
|
|
487
|
+
} catch {
|
|
488
|
+
return 0;
|
|
489
|
+
}
|
|
490
|
+
};
|
|
491
|
+
function undoCommand() {
|
|
492
|
+
return {
|
|
493
|
+
name: "undo",
|
|
494
|
+
run: (state, dispatch) => undo(state, dispatch),
|
|
495
|
+
isEnabled: (state) => depth(undoDepth, state) > 0
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
function redoCommand() {
|
|
499
|
+
return {
|
|
500
|
+
name: "redo",
|
|
501
|
+
run: (state, dispatch) => redo(state, dispatch),
|
|
502
|
+
isEnabled: (state) => depth(redoDepth, state) > 0
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
// packages/commands/src/lib/insert.ts
|
|
507
|
+
function pageBreakCommand() {
|
|
508
|
+
const paragraphs = (state) => {
|
|
509
|
+
const { from, to } = state.selection;
|
|
510
|
+
const out = [];
|
|
511
|
+
state.doc.nodesBetween(from, to, (node, pos) => {
|
|
512
|
+
if (node.type.name === "paragraph") out.push({ pos, on: !!node.attrs["pageBreakBefore"] });
|
|
513
|
+
});
|
|
514
|
+
return out;
|
|
515
|
+
};
|
|
516
|
+
return {
|
|
517
|
+
name: "page-break",
|
|
518
|
+
run(state, dispatch) {
|
|
519
|
+
const ps = paragraphs(state);
|
|
520
|
+
if (ps.length === 0) return false;
|
|
521
|
+
if (dispatch) {
|
|
522
|
+
const next = !ps[0].on;
|
|
523
|
+
const tr = state.tr;
|
|
524
|
+
for (const p of ps) tr.setNodeAttribute(p.pos, "pageBreakBefore", next);
|
|
525
|
+
dispatch(tr);
|
|
526
|
+
}
|
|
527
|
+
return true;
|
|
528
|
+
},
|
|
529
|
+
isActive: (state) => paragraphs(state).some((p) => p.on)
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
function insertTable(rows = 2, cols = 2) {
|
|
533
|
+
return {
|
|
534
|
+
name: "insert-table",
|
|
535
|
+
run(state, dispatch) {
|
|
536
|
+
const { table, table_row, table_cell, paragraph } = state.schema.nodes;
|
|
537
|
+
if (!table || !table_row || !table_cell || !paragraph) return false;
|
|
538
|
+
const makeRow = () => table_row.create(null, Array.from({ length: cols }, () => table_cell.create(null, paragraph.create())));
|
|
539
|
+
const node = table.create(null, Array.from({ length: rows }, makeRow));
|
|
540
|
+
if (dispatch) dispatch(state.tr.replaceSelectionWith(node).scrollIntoView());
|
|
541
|
+
return true;
|
|
542
|
+
}
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
function insertImage(src, alt = "") {
|
|
546
|
+
return {
|
|
547
|
+
name: "insert-image",
|
|
548
|
+
run(state, dispatch) {
|
|
549
|
+
const image = state.schema.nodes["image"];
|
|
550
|
+
if (!image || !src) return false;
|
|
551
|
+
if (dispatch) dispatch(state.tr.replaceSelectionWith(image.create({ src, alt }), false).scrollIntoView());
|
|
552
|
+
return true;
|
|
553
|
+
}
|
|
554
|
+
};
|
|
555
|
+
}
|
|
556
|
+
function setLink(href) {
|
|
557
|
+
return {
|
|
558
|
+
name: "link",
|
|
559
|
+
run(state, dispatch) {
|
|
560
|
+
const type = state.schema.marks["link"];
|
|
561
|
+
if (!type || state.selection.empty) return false;
|
|
562
|
+
if (dispatch) {
|
|
563
|
+
const { from, to } = state.selection;
|
|
564
|
+
const tr = state.tr.removeMark(from, to, type);
|
|
565
|
+
if (href) tr.addMark(from, to, type.create({ href }));
|
|
566
|
+
dispatch(tr);
|
|
567
|
+
}
|
|
568
|
+
return true;
|
|
569
|
+
},
|
|
570
|
+
isActive: (state) => isMarkActive(state, "link"),
|
|
571
|
+
isEnabled: (state) => !!state.schema.marks["link"] && !state.selection.empty
|
|
572
|
+
};
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
// packages/commands/src/lib/edit.ts
|
|
576
|
+
import { deleteSelection } from "prosemirror-commands";
|
|
577
|
+
function deleteSelectionCommand() {
|
|
578
|
+
return {
|
|
579
|
+
name: "delete",
|
|
580
|
+
run: (state, dispatch) => deleteSelection(state, dispatch),
|
|
581
|
+
isEnabled: (state) => !state.selection.empty
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
// packages/commands/src/lib/sections.ts
|
|
586
|
+
var DEFAULT_GAP = 28;
|
|
587
|
+
function currentSections(state) {
|
|
588
|
+
const raw = state.doc.attrs["sections"];
|
|
589
|
+
if (raw && raw.length) return raw.map((s) => ({ ...s, columns: { ...s.columns } }));
|
|
590
|
+
return [{ blockCount: state.doc.childCount, columns: { count: 1, gap: 0 }, newPage: false }];
|
|
591
|
+
}
|
|
592
|
+
function headBlockIndex(state) {
|
|
593
|
+
return state.selection.$head.index(0);
|
|
594
|
+
}
|
|
595
|
+
function sectionAt(sections, bi) {
|
|
596
|
+
let start = 0;
|
|
597
|
+
for (let i = 0; i < sections.length; i++) {
|
|
598
|
+
if (bi < start + sections[i].blockCount) return { i, start };
|
|
599
|
+
start += sections[i].blockCount;
|
|
600
|
+
}
|
|
601
|
+
const last = sections.length - 1;
|
|
602
|
+
return { i: last, start: start - sections[last].blockCount };
|
|
603
|
+
}
|
|
604
|
+
function insertSectionBreak(opts) {
|
|
605
|
+
return {
|
|
606
|
+
name: opts.newPage ? "section-break-next-page" : "section-break-continuous",
|
|
607
|
+
run(state, dispatch) {
|
|
608
|
+
if (state.doc.childCount < 2) return false;
|
|
609
|
+
const sections = currentSections(state);
|
|
610
|
+
const bi = headBlockIndex(state);
|
|
611
|
+
const { i, start } = sectionAt(sections, bi);
|
|
612
|
+
const S = sections[i];
|
|
613
|
+
const firstCount = bi + 1 - start;
|
|
614
|
+
const secondCount = S.blockCount - firstCount;
|
|
615
|
+
if (secondCount <= 0) return false;
|
|
616
|
+
if (dispatch) {
|
|
617
|
+
const next = [
|
|
618
|
+
...sections.slice(0, i),
|
|
619
|
+
{ blockCount: firstCount, columns: { ...S.columns }, newPage: S.newPage },
|
|
620
|
+
{ blockCount: secondCount, columns: { ...S.columns }, newPage: opts.newPage },
|
|
621
|
+
...sections.slice(i + 1)
|
|
622
|
+
];
|
|
623
|
+
dispatch(state.tr.setDocAttribute("sections", next).scrollIntoView());
|
|
624
|
+
}
|
|
625
|
+
return true;
|
|
626
|
+
},
|
|
627
|
+
isEnabled: (state) => state.doc.childCount > 1
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
function removeSectionBreak(boundaryIndex) {
|
|
631
|
+
return {
|
|
632
|
+
name: "remove-section-break",
|
|
633
|
+
run(state, dispatch) {
|
|
634
|
+
const raw = state.doc.attrs["sections"];
|
|
635
|
+
const b = boundaryIndex;
|
|
636
|
+
if (!raw || b < 0 || b >= raw.length - 1) return false;
|
|
637
|
+
if (dispatch) {
|
|
638
|
+
const upper = raw[b];
|
|
639
|
+
const lower = raw[b + 1];
|
|
640
|
+
const merged = {
|
|
641
|
+
blockCount: upper.blockCount + lower.blockCount,
|
|
642
|
+
columns: { ...lower.columns },
|
|
643
|
+
// following section's layout wins (Word)
|
|
644
|
+
newPage: upper.newPage
|
|
645
|
+
// merged range still begins where the upper did
|
|
646
|
+
};
|
|
647
|
+
const next = [...raw.slice(0, b), merged, ...raw.slice(b + 2)];
|
|
648
|
+
dispatch(state.tr.setDocAttribute("sections", next).scrollIntoView());
|
|
649
|
+
}
|
|
650
|
+
return true;
|
|
651
|
+
}
|
|
652
|
+
};
|
|
653
|
+
}
|
|
654
|
+
function setColumns(count) {
|
|
655
|
+
return {
|
|
656
|
+
name: `columns-${count}`,
|
|
657
|
+
run(state, dispatch) {
|
|
658
|
+
if (state.doc.childCount === 0) return false;
|
|
659
|
+
const sections = currentSections(state);
|
|
660
|
+
const { i } = sectionAt(sections, headBlockIndex(state));
|
|
661
|
+
if (dispatch) {
|
|
662
|
+
const gap = count > 1 ? sections[i].columns.gap || DEFAULT_GAP : sections[i].columns.gap;
|
|
663
|
+
const next = sections.map((s, j) => j === i ? { ...s, columns: { count, gap } } : s);
|
|
664
|
+
dispatch(state.tr.setDocAttribute("sections", next).scrollIntoView());
|
|
665
|
+
}
|
|
666
|
+
return true;
|
|
667
|
+
},
|
|
668
|
+
isActive: (state) => {
|
|
669
|
+
const sections = currentSections(state);
|
|
670
|
+
return sections[sectionAt(sections, headBlockIndex(state)).i].columns.count === count;
|
|
671
|
+
}
|
|
672
|
+
};
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
// packages/commands/src/lib/registry.ts
|
|
676
|
+
import { Collection } from "@shadow-garden/bapbong-contracts";
|
|
677
|
+
function defaultCommands() {
|
|
678
|
+
return new Collection(
|
|
679
|
+
[
|
|
680
|
+
toggleMarkCommand("bold", "strong"),
|
|
681
|
+
toggleMarkCommand("italic", "em"),
|
|
682
|
+
toggleMarkCommand("underline"),
|
|
683
|
+
toggleMarkCommand("strike"),
|
|
684
|
+
toggleMarkCommand("superscript", "vertAlign", { value: "super" }),
|
|
685
|
+
toggleMarkCommand("subscript", "vertAlign", { value: "sub" }),
|
|
686
|
+
clearMarks(),
|
|
687
|
+
setAlign("left"),
|
|
688
|
+
setAlign("center"),
|
|
689
|
+
setAlign("right"),
|
|
690
|
+
setAlign("justify"),
|
|
691
|
+
toggleList("bullet"),
|
|
692
|
+
toggleList("ordered"),
|
|
693
|
+
toggleHeading(1),
|
|
694
|
+
toggleHeading(2),
|
|
695
|
+
toggleHeading(3),
|
|
696
|
+
toggleHeading(4),
|
|
697
|
+
toggleHeading(5),
|
|
698
|
+
toggleHeading(6),
|
|
699
|
+
undoCommand(),
|
|
700
|
+
redoCommand(),
|
|
701
|
+
pageBreakCommand(),
|
|
702
|
+
insertSectionBreak({ newPage: true }),
|
|
703
|
+
insertSectionBreak({ newPage: false }),
|
|
704
|
+
setColumns(1),
|
|
705
|
+
setColumns(2),
|
|
706
|
+
setColumns(3)
|
|
707
|
+
],
|
|
708
|
+
{ idProperty: "name" }
|
|
709
|
+
);
|
|
710
|
+
}
|
|
711
|
+
export {
|
|
712
|
+
ALIGNMENTS,
|
|
713
|
+
activeAlign,
|
|
714
|
+
activeFontFamily,
|
|
715
|
+
activeFontSize,
|
|
716
|
+
activeHighlight,
|
|
717
|
+
activeMarkValue,
|
|
718
|
+
activeTextColor,
|
|
719
|
+
cellAt,
|
|
720
|
+
clearMarks,
|
|
721
|
+
defaultCommands,
|
|
722
|
+
deleteColumn,
|
|
723
|
+
deleteRow,
|
|
724
|
+
deleteSelectionCommand,
|
|
725
|
+
deleteTable,
|
|
726
|
+
insertColumn,
|
|
727
|
+
insertImage,
|
|
728
|
+
insertRow,
|
|
729
|
+
insertSectionBreak,
|
|
730
|
+
insertTable,
|
|
731
|
+
isMarkActive,
|
|
732
|
+
mergeCells,
|
|
733
|
+
pageBreakCommand,
|
|
734
|
+
redoCommand,
|
|
735
|
+
removeSectionBreak,
|
|
736
|
+
setAlign,
|
|
737
|
+
setCellAttrs,
|
|
738
|
+
setCellBackground,
|
|
739
|
+
setCellsAttrs,
|
|
740
|
+
setColumnWidth,
|
|
741
|
+
setColumns,
|
|
742
|
+
setFontFamily,
|
|
743
|
+
setFontSize,
|
|
744
|
+
setHighlight,
|
|
745
|
+
setLink,
|
|
746
|
+
setMarkAttr,
|
|
747
|
+
setTextColor,
|
|
748
|
+
toggleHeading,
|
|
749
|
+
toggleList,
|
|
750
|
+
toggleMarkCommand,
|
|
751
|
+
undoCommand
|
|
752
|
+
};
|