@milkdown/preset-gfm 6.5.4 → 7.0.0-next.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/lib/composed/commands.d.ts +8 -0
- package/lib/composed/commands.d.ts.map +1 -0
- package/lib/composed/index.d.ts +6 -0
- package/lib/composed/index.d.ts.map +1 -0
- package/lib/composed/inputrules.d.ts +3 -0
- package/lib/composed/inputrules.d.ts.map +1 -0
- package/lib/composed/keymap.d.ts +3 -0
- package/lib/composed/keymap.d.ts.map +1 -0
- package/lib/composed/plugins.d.ts +3 -0
- package/lib/composed/plugins.d.ts.map +1 -0
- package/lib/composed/schema.d.ts +3 -0
- package/lib/composed/schema.d.ts.map +1 -0
- package/lib/index.d.ts +4 -34
- package/lib/index.d.ts.map +1 -1
- package/lib/index.es.js +634 -2328
- package/lib/index.es.js.map +1 -1
- package/lib/mark/index.d.ts +2 -0
- package/lib/mark/index.d.ts.map +1 -0
- package/lib/mark/strike-through.d.ts +5 -0
- package/lib/mark/strike-through.d.ts.map +1 -0
- package/lib/node/footnote/definition.d.ts +2 -0
- package/lib/node/footnote/definition.d.ts.map +1 -0
- package/lib/{footnote → node/footnote}/index.d.ts +0 -0
- package/lib/node/footnote/index.d.ts.map +1 -0
- package/lib/node/footnote/reference.d.ts +2 -0
- package/lib/node/footnote/reference.d.ts.map +1 -0
- package/lib/node/index.d.ts +4 -0
- package/lib/node/index.d.ts.map +1 -0
- package/lib/node/table/index.d.ts +32 -0
- package/lib/node/table/index.d.ts.map +1 -0
- package/lib/node/table/utils.d.ts +26 -0
- package/lib/node/table/utils.d.ts.map +1 -0
- package/lib/node/task-list-item.d.ts +2 -0
- package/lib/node/task-list-item.d.ts.map +1 -0
- package/lib/plugin/auto-insert-zero-space-plugin.d.ts +2 -0
- package/lib/plugin/auto-insert-zero-space-plugin.d.ts.map +1 -0
- package/lib/plugin/column-resizing-plugin.d.ts +2 -0
- package/lib/plugin/column-resizing-plugin.d.ts.map +1 -0
- package/lib/plugin/index.d.ts +5 -0
- package/lib/plugin/index.d.ts.map +1 -0
- package/lib/plugin/remark-gfm-plugin.d.ts +2 -0
- package/lib/plugin/remark-gfm-plugin.d.ts.map +1 -0
- package/lib/plugin/table-editing-plugin.d.ts +2 -0
- package/lib/plugin/table-editing-plugin.d.ts.map +1 -0
- package/package.json +13 -10
- package/src/composed/commands.ts +24 -0
- package/src/composed/index.ts +6 -0
- package/src/composed/inputrules.ts +8 -0
- package/src/composed/keymap.ts +10 -0
- package/src/composed/plugins.ts +11 -0
- package/src/composed/schema.ts +20 -0
- package/src/index.ts +6 -89
- package/src/mark/index.ts +2 -0
- package/src/mark/strike-through.ts +46 -0
- package/src/node/footnote/definition.ts +70 -0
- package/src/{footnote → node/footnote}/index.ts +0 -0
- package/src/node/footnote/reference.ts +60 -0
- package/src/node/index.ts +4 -0
- package/src/node/table/index.ts +318 -0
- package/src/node/table/utils.ts +509 -0
- package/src/node/task-list-item.ts +88 -0
- package/src/{table/plugin/auto-insert-zero-space.ts → plugin/auto-insert-zero-space-plugin.ts} +14 -15
- package/src/plugin/column-resizing-plugin.ts +6 -0
- package/src/plugin/index.ts +5 -0
- package/src/plugin/remark-gfm-plugin.ts +6 -0
- package/src/plugin/table-editing-plugin.ts +6 -0
- package/lib/footnote/definition.d.ts +0 -3
- package/lib/footnote/definition.d.ts.map +0 -1
- package/lib/footnote/index.d.ts.map +0 -1
- package/lib/footnote/reference.d.ts +0 -3
- package/lib/footnote/reference.d.ts.map +0 -1
- package/lib/footnote/utils.d.ts +0 -3
- package/lib/footnote/utils.d.ts.map +0 -1
- package/lib/strike-through.d.ts +0 -3
- package/lib/strike-through.d.ts.map +0 -1
- package/lib/supported-keys.d.ts +0 -28
- package/lib/supported-keys.d.ts.map +0 -1
- package/lib/table/command.d.ts +0 -4
- package/lib/table/command.d.ts.map +0 -1
- package/lib/table/index.d.ts +0 -10
- package/lib/table/index.d.ts.map +0 -1
- package/lib/table/nodes/index.d.ts +0 -12
- package/lib/table/nodes/index.d.ts.map +0 -1
- package/lib/table/operator-plugin/actions.d.ts +0 -20
- package/lib/table/operator-plugin/actions.d.ts.map +0 -1
- package/lib/table/operator-plugin/calc-pos.d.ts +0 -3
- package/lib/table/operator-plugin/calc-pos.d.ts.map +0 -1
- package/lib/table/operator-plugin/constant.d.ts +0 -6
- package/lib/table/operator-plugin/constant.d.ts.map +0 -1
- package/lib/table/operator-plugin/helper.d.ts +0 -7
- package/lib/table/operator-plugin/helper.d.ts.map +0 -1
- package/lib/table/operator-plugin/index.d.ts +0 -6
- package/lib/table/operator-plugin/index.d.ts.map +0 -1
- package/lib/table/operator-plugin/style.d.ts +0 -3
- package/lib/table/operator-plugin/style.d.ts.map +0 -1
- package/lib/table/operator-plugin/widget.d.ts +0 -8
- package/lib/table/operator-plugin/widget.d.ts.map +0 -1
- package/lib/table/plugin/auto-insert-zero-space.d.ts +0 -3
- package/lib/table/plugin/auto-insert-zero-space.d.ts.map +0 -1
- package/lib/table/plugin/cell-selection.d.ts +0 -40
- package/lib/table/plugin/cell-selection.d.ts.map +0 -1
- package/lib/table/plugin/column-resizing.d.ts +0 -18
- package/lib/table/plugin/column-resizing.d.ts.map +0 -1
- package/lib/table/plugin/commands.d.ts +0 -30
- package/lib/table/plugin/commands.d.ts.map +0 -1
- package/lib/table/plugin/copy-paste.d.ts +0 -14
- package/lib/table/plugin/copy-paste.d.ts.map +0 -1
- package/lib/table/plugin/fix-tables.d.ts +0 -7
- package/lib/table/plugin/fix-tables.d.ts.map +0 -1
- package/lib/table/plugin/index.d.ts +0 -4
- package/lib/table/plugin/index.d.ts.map +0 -1
- package/lib/table/plugin/schema.d.ts +0 -4
- package/lib/table/plugin/schema.d.ts.map +0 -1
- package/lib/table/plugin/table-editing.d.ts +0 -9
- package/lib/table/plugin/table-editing.d.ts.map +0 -1
- package/lib/table/plugin/table-map.d.ts +0 -44
- package/lib/table/plugin/table-map.d.ts.map +0 -1
- package/lib/table/plugin/table-view.d.ts +0 -15
- package/lib/table/plugin/table-view.d.ts.map +0 -1
- package/lib/table/plugin/types.d.ts +0 -15
- package/lib/table/plugin/types.d.ts.map +0 -1
- package/lib/table/plugin/util.d.ts +0 -16
- package/lib/table/plugin/util.d.ts.map +0 -1
- package/lib/table/utils.d.ts +0 -21
- package/lib/table/utils.d.ts.map +0 -1
- package/lib/task-list-item.d.ts +0 -9
- package/lib/task-list-item.d.ts.map +0 -1
- package/src/footnote/definition.ts +0 -187
- package/src/footnote/reference.ts +0 -178
- package/src/footnote/utils.ts +0 -4
- package/src/strike-through.ts +0 -43
- package/src/supported-keys.ts +0 -13
- package/src/table/command.ts +0 -20
- package/src/table/index.ts +0 -13
- package/src/table/nodes/index.ts +0 -189
- package/src/table/operator-plugin/actions.ts +0 -116
- package/src/table/operator-plugin/calc-pos.ts +0 -36
- package/src/table/operator-plugin/constant.ts +0 -7
- package/src/table/operator-plugin/helper.ts +0 -39
- package/src/table/operator-plugin/index.ts +0 -110
- package/src/table/operator-plugin/style.ts +0 -123
- package/src/table/operator-plugin/widget.ts +0 -57
- package/src/table/plugin/cell-selection.ts +0 -381
- package/src/table/plugin/column-resizing.ts +0 -288
- package/src/table/plugin/commands.ts +0 -594
- package/src/table/plugin/copy-paste.ts +0 -322
- package/src/table/plugin/fix-tables.ts +0 -132
- package/src/table/plugin/index.ts +0 -4
- package/src/table/plugin/schema.ts +0 -120
- package/src/table/plugin/table-editing.ts +0 -369
- package/src/table/plugin/table-map.ts +0 -345
- package/src/table/plugin/table-view.ts +0 -80
- package/src/table/plugin/types.ts +0 -16
- package/src/table/plugin/util.ts +0 -119
- package/src/table/utils.ts +0 -165
- package/src/task-list-item.ts +0 -159
|
@@ -1,369 +0,0 @@
|
|
|
1
|
-
/* Copyright 2021, Milkdown by Mirone. */
|
|
2
|
-
|
|
3
|
-
import { keydownHandler } from '@milkdown/prose/keymap'
|
|
4
|
-
import type { ResolvedPos } from '@milkdown/prose/model'
|
|
5
|
-
import { Fragment, Slice } from '@milkdown/prose/model'
|
|
6
|
-
import type { Command, EditorState, Transaction } from '@milkdown/prose/state'
|
|
7
|
-
import {
|
|
8
|
-
Plugin,
|
|
9
|
-
PluginKey,
|
|
10
|
-
Selection,
|
|
11
|
-
TextSelection,
|
|
12
|
-
} from '@milkdown/prose/state'
|
|
13
|
-
import type { EditorView } from '@milkdown/prose/view'
|
|
14
|
-
|
|
15
|
-
import {
|
|
16
|
-
CellSelection,
|
|
17
|
-
drawCellSelection,
|
|
18
|
-
normalizeSelection,
|
|
19
|
-
} from './cell-selection'
|
|
20
|
-
import { clipCells, fitSlice, insertCells, pastedCells } from './copy-paste'
|
|
21
|
-
import { fixTables } from './fix-tables'
|
|
22
|
-
import { tableNodeTypes } from './schema'
|
|
23
|
-
import { TableMap } from './table-map'
|
|
24
|
-
import {
|
|
25
|
-
cellAround,
|
|
26
|
-
inSameTable,
|
|
27
|
-
isInTable,
|
|
28
|
-
nextCell,
|
|
29
|
-
selectionCell,
|
|
30
|
-
} from './util'
|
|
31
|
-
|
|
32
|
-
export const tableEditingKey = new PluginKey('selectingCells')
|
|
33
|
-
|
|
34
|
-
function domInCell(view: EditorView, dom: EventTarget | null) {
|
|
35
|
-
for (; dom && dom != view.dom; dom = (dom as Element).parentNode as Element) {
|
|
36
|
-
if ((dom as Element).nodeName == 'TD' || (dom as Element).nodeName == 'TH')
|
|
37
|
-
return dom
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
return undefined
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function cellUnderMouse(view: EditorView, event: MouseEvent) {
|
|
44
|
-
const mousePos = view.posAtCoords({
|
|
45
|
-
left: event.clientX,
|
|
46
|
-
top: event.clientY,
|
|
47
|
-
})
|
|
48
|
-
if (!mousePos)
|
|
49
|
-
return null
|
|
50
|
-
return mousePos ? cellAround(view.state.doc.resolve(mousePos.pos)) : null
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function handleMouseDown(view: EditorView, event: Event) {
|
|
54
|
-
const startEvent = event as MouseEvent
|
|
55
|
-
if (startEvent.ctrlKey || startEvent.metaKey)
|
|
56
|
-
return
|
|
57
|
-
|
|
58
|
-
const startDOMCell = domInCell(view, startEvent.target as Element)
|
|
59
|
-
let $anchor
|
|
60
|
-
if (startEvent.shiftKey && view.state.selection instanceof CellSelection) {
|
|
61
|
-
// Adding to an existing cell selection
|
|
62
|
-
setCellSelection(view.state.selection.$anchorCell, startEvent)
|
|
63
|
-
startEvent.preventDefault()
|
|
64
|
-
}
|
|
65
|
-
else if (
|
|
66
|
-
startEvent.shiftKey
|
|
67
|
-
&& startDOMCell
|
|
68
|
-
// eslint-disable-next-line no-cond-assign
|
|
69
|
-
&& ($anchor = cellAround(view.state.selection.$anchor)) != null
|
|
70
|
-
&& cellUnderMouse(view, startEvent)!.pos != $anchor.pos
|
|
71
|
-
) {
|
|
72
|
-
// Adding to a selection that starts in another cell (causing a
|
|
73
|
-
// cell selection to be created).
|
|
74
|
-
setCellSelection($anchor, startEvent)
|
|
75
|
-
startEvent.preventDefault()
|
|
76
|
-
}
|
|
77
|
-
else if (!startDOMCell) {
|
|
78
|
-
// Not in a cell, let the default behavior happen.
|
|
79
|
-
return
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// Create and dispatch a cell selection between the given anchor and
|
|
83
|
-
// the position under the mouse.
|
|
84
|
-
function setCellSelection($anchor: ResolvedPos, event: MouseEvent) {
|
|
85
|
-
let $head = cellUnderMouse(view, event)
|
|
86
|
-
const starting = tableEditingKey.getState(view.state) == null
|
|
87
|
-
if (!$head || !inSameTable($anchor, $head)) {
|
|
88
|
-
if (starting)
|
|
89
|
-
$head = $anchor
|
|
90
|
-
else return
|
|
91
|
-
}
|
|
92
|
-
const selection = new CellSelection($anchor, $head)
|
|
93
|
-
if (starting || !view.state.selection.eq(selection)) {
|
|
94
|
-
const tr = view.state.tr.setSelection(selection)
|
|
95
|
-
if (starting)
|
|
96
|
-
tr.setMeta(tableEditingKey, $anchor.pos)
|
|
97
|
-
view.dispatch(tr)
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// Stop listening to mouse motion events.
|
|
102
|
-
function stop() {
|
|
103
|
-
view.root.removeEventListener('mouseup', stop)
|
|
104
|
-
view.root.removeEventListener('dragstart', stop)
|
|
105
|
-
view.root.removeEventListener('mousemove', move)
|
|
106
|
-
if (tableEditingKey.getState(view.state) != null)
|
|
107
|
-
view.dispatch(view.state.tr.setMeta(tableEditingKey, -1))
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
function move(event: Event) {
|
|
111
|
-
const anchor = tableEditingKey.getState(view.state)
|
|
112
|
-
let $anchor
|
|
113
|
-
if (anchor != null) {
|
|
114
|
-
// Continuing an existing cross-cell selection
|
|
115
|
-
$anchor = view.state.doc.resolve(anchor)
|
|
116
|
-
}
|
|
117
|
-
else if (domInCell(view, event.target) != startDOMCell) {
|
|
118
|
-
// Moving out of the initial cell -- start a new cell selection
|
|
119
|
-
$anchor = cellUnderMouse(view, startEvent)
|
|
120
|
-
if (!$anchor)
|
|
121
|
-
return stop()
|
|
122
|
-
}
|
|
123
|
-
if ($anchor)
|
|
124
|
-
setCellSelection($anchor, event as MouseEvent)
|
|
125
|
-
}
|
|
126
|
-
view.root.addEventListener('mouseup', stop)
|
|
127
|
-
view.root.addEventListener('dragstart', stop)
|
|
128
|
-
view.root.addEventListener('mousemove', move)
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
function handleTripleClick(view: EditorView, pos: number) {
|
|
132
|
-
const doc = view.state.doc
|
|
133
|
-
const $cell = cellAround(doc.resolve(pos))
|
|
134
|
-
if (!$cell)
|
|
135
|
-
return false
|
|
136
|
-
view.dispatch(view.state.tr.setSelection(new CellSelection($cell)))
|
|
137
|
-
return true
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
function maybeSetSelection(
|
|
141
|
-
state: EditorState,
|
|
142
|
-
dispatch: undefined | ((tr: Transaction) => void),
|
|
143
|
-
selection: Selection,
|
|
144
|
-
) {
|
|
145
|
-
if (selection.eq(state.selection))
|
|
146
|
-
return false
|
|
147
|
-
if (dispatch)
|
|
148
|
-
dispatch(state.tr.setSelection(selection).scrollIntoView())
|
|
149
|
-
return true
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
function atEndOfCell(view: EditorView, axis: string, dir: number) {
|
|
153
|
-
if (!(view.state.selection instanceof TextSelection))
|
|
154
|
-
return null
|
|
155
|
-
const { $head } = view.state.selection
|
|
156
|
-
for (let d = $head.depth - 1; d >= 0; d--) {
|
|
157
|
-
const parent = $head.node(d)
|
|
158
|
-
const index = dir < 0 ? $head.index(d) : $head.indexAfter(d)
|
|
159
|
-
if (index != (dir < 0 ? 0 : parent.childCount))
|
|
160
|
-
return null
|
|
161
|
-
if (
|
|
162
|
-
parent.type.spec.tableRole == 'cell'
|
|
163
|
-
|| parent.type.spec.tableRole == 'header_cell'
|
|
164
|
-
) {
|
|
165
|
-
const cellPos = $head.before(d)
|
|
166
|
-
const dirStr
|
|
167
|
-
= axis == 'vert' ? (dir > 0 ? 'down' : 'up') : dir > 0 ? 'right' : 'left'
|
|
168
|
-
return view.endOfTextblock(dirStr) ? cellPos : null
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
return null
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
function arrow(axis: string, dir: number): Command {
|
|
175
|
-
return (state, dispatch, view) => {
|
|
176
|
-
const sel = state.selection
|
|
177
|
-
if (sel instanceof CellSelection) {
|
|
178
|
-
return maybeSetSelection(
|
|
179
|
-
state,
|
|
180
|
-
dispatch,
|
|
181
|
-
Selection.near(sel.$headCell, dir),
|
|
182
|
-
)
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
if (axis != 'horiz' && !sel.empty)
|
|
186
|
-
return false
|
|
187
|
-
const end = atEndOfCell(view as EditorView, axis, dir)
|
|
188
|
-
if (end == null)
|
|
189
|
-
return false
|
|
190
|
-
if (axis == 'horiz') {
|
|
191
|
-
return maybeSetSelection(
|
|
192
|
-
state,
|
|
193
|
-
dispatch,
|
|
194
|
-
Selection.near(state.doc.resolve(sel.head + dir), dir),
|
|
195
|
-
)
|
|
196
|
-
}
|
|
197
|
-
else {
|
|
198
|
-
const $cell = state.doc.resolve(end)
|
|
199
|
-
const $next = nextCell($cell, axis, dir)
|
|
200
|
-
let newSel
|
|
201
|
-
if ($next)
|
|
202
|
-
newSel = Selection.near($next, 1)
|
|
203
|
-
else if (dir < 0)
|
|
204
|
-
newSel = Selection.near(state.doc.resolve($cell.before(-1)), -1)
|
|
205
|
-
else newSel = Selection.near(state.doc.resolve($cell.after(-1)), 1)
|
|
206
|
-
return maybeSetSelection(state, dispatch, newSel)
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
function shiftArrow(axis: string, dir: number): Command {
|
|
212
|
-
return (state, dispatch, view) => {
|
|
213
|
-
let sel = state.selection
|
|
214
|
-
if (!(sel instanceof CellSelection)) {
|
|
215
|
-
const end = atEndOfCell(view as EditorView, axis, dir)
|
|
216
|
-
if (end == null)
|
|
217
|
-
return false
|
|
218
|
-
sel = new CellSelection(state.doc.resolve(end))
|
|
219
|
-
}
|
|
220
|
-
const $head = nextCell((sel as CellSelection).$headCell, axis, dir)
|
|
221
|
-
if (!$head)
|
|
222
|
-
return false
|
|
223
|
-
return maybeSetSelection(
|
|
224
|
-
state,
|
|
225
|
-
dispatch,
|
|
226
|
-
new CellSelection((sel as CellSelection).$anchorCell, $head),
|
|
227
|
-
)
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
function deleteCellSelection(
|
|
232
|
-
state: EditorState,
|
|
233
|
-
dispatch?: (tr: Transaction) => void,
|
|
234
|
-
) {
|
|
235
|
-
const sel = state.selection
|
|
236
|
-
if (!(sel instanceof CellSelection))
|
|
237
|
-
return false
|
|
238
|
-
if (dispatch) {
|
|
239
|
-
const tr = state.tr
|
|
240
|
-
const baseContent = tableNodeTypes(state.schema).cell.createAndFill()
|
|
241
|
-
.content
|
|
242
|
-
sel.forEachCell((cell, pos) => {
|
|
243
|
-
if (!cell.content.eq(baseContent)) {
|
|
244
|
-
tr.replace(
|
|
245
|
-
tr.mapping.map(pos + 1),
|
|
246
|
-
tr.mapping.map(pos + cell.nodeSize - 1),
|
|
247
|
-
new Slice(baseContent, 0, 0),
|
|
248
|
-
)
|
|
249
|
-
}
|
|
250
|
-
})
|
|
251
|
-
if (tr.docChanged)
|
|
252
|
-
dispatch(tr)
|
|
253
|
-
}
|
|
254
|
-
return true
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
const handleKeyDown = keydownHandler({
|
|
258
|
-
'ArrowLeft': arrow('horiz', -1),
|
|
259
|
-
'ArrowRight': arrow('horiz', 1),
|
|
260
|
-
'ArrowUp': arrow('vert', -1),
|
|
261
|
-
'ArrowDown': arrow('vert', 1),
|
|
262
|
-
|
|
263
|
-
'Shift-ArrowLeft': shiftArrow('horiz', -1),
|
|
264
|
-
'Shift-ArrowRight': shiftArrow('horiz', 1),
|
|
265
|
-
'Shift-ArrowUp': shiftArrow('vert', -1),
|
|
266
|
-
'Shift-ArrowDown': shiftArrow('vert', 1),
|
|
267
|
-
|
|
268
|
-
'Backspace': deleteCellSelection,
|
|
269
|
-
'Mod-Backspace': deleteCellSelection,
|
|
270
|
-
'Delete': deleteCellSelection,
|
|
271
|
-
'Mod-Delete': deleteCellSelection,
|
|
272
|
-
})
|
|
273
|
-
|
|
274
|
-
export function handlePaste(view: EditorView, _: Event, slice: Slice) {
|
|
275
|
-
if (!isInTable(view.state))
|
|
276
|
-
return false
|
|
277
|
-
let cells = pastedCells(slice)
|
|
278
|
-
const sel = view.state.selection
|
|
279
|
-
if (sel instanceof CellSelection) {
|
|
280
|
-
if (!cells) {
|
|
281
|
-
cells = {
|
|
282
|
-
width: 1,
|
|
283
|
-
height: 1,
|
|
284
|
-
rows: [
|
|
285
|
-
Fragment.from(
|
|
286
|
-
fitSlice(tableNodeTypes(view.state.schema).cell, slice),
|
|
287
|
-
),
|
|
288
|
-
],
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
const table = sel.$anchorCell.node(-1)
|
|
292
|
-
const start = sel.$anchorCell.start(-1)
|
|
293
|
-
const rect = TableMap.get(table).rectBetween(
|
|
294
|
-
sel.$anchorCell.pos - start,
|
|
295
|
-
sel.$headCell.pos - start,
|
|
296
|
-
)
|
|
297
|
-
cells = clipCells(cells, rect.right - rect.left, rect.bottom - rect.top)
|
|
298
|
-
insertCells(view.state, view.dispatch, start, rect, cells)
|
|
299
|
-
return true
|
|
300
|
-
}
|
|
301
|
-
else if (cells) {
|
|
302
|
-
const $cell = selectionCell(view.state) as ResolvedPos
|
|
303
|
-
const start = $cell.start(-1)
|
|
304
|
-
insertCells(
|
|
305
|
-
view.state,
|
|
306
|
-
view.dispatch,
|
|
307
|
-
start,
|
|
308
|
-
TableMap.get($cell.node(-1)).findCell($cell.pos - start),
|
|
309
|
-
cells,
|
|
310
|
-
)
|
|
311
|
-
return true
|
|
312
|
-
}
|
|
313
|
-
else {
|
|
314
|
-
return false
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
export function tableEditing({ allowTableNodeSelection = false } = {}) {
|
|
319
|
-
return new Plugin({
|
|
320
|
-
key: tableEditingKey,
|
|
321
|
-
|
|
322
|
-
// This piece of state is used to remember when a mouse-drag
|
|
323
|
-
// cell-selection is happening, so that it can continue even as
|
|
324
|
-
// transactions (which might move its anchor cell) come in.
|
|
325
|
-
state: {
|
|
326
|
-
init() {
|
|
327
|
-
return null
|
|
328
|
-
},
|
|
329
|
-
apply(tr, cur) {
|
|
330
|
-
const set = tr.getMeta(tableEditingKey)
|
|
331
|
-
if (set != null)
|
|
332
|
-
return set == -1 ? null : set
|
|
333
|
-
if (cur == null || !tr.docChanged)
|
|
334
|
-
return cur
|
|
335
|
-
const { deleted, pos } = tr.mapping.mapResult(cur)
|
|
336
|
-
return deleted ? null : pos
|
|
337
|
-
},
|
|
338
|
-
},
|
|
339
|
-
|
|
340
|
-
props: {
|
|
341
|
-
decorations: drawCellSelection,
|
|
342
|
-
|
|
343
|
-
handleDOMEvents: {
|
|
344
|
-
mousedown: handleMouseDown,
|
|
345
|
-
},
|
|
346
|
-
|
|
347
|
-
createSelectionBetween(view) {
|
|
348
|
-
if (tableEditingKey.getState(view.state) != null)
|
|
349
|
-
return view.state.selection
|
|
350
|
-
|
|
351
|
-
return null
|
|
352
|
-
},
|
|
353
|
-
|
|
354
|
-
handleTripleClick,
|
|
355
|
-
|
|
356
|
-
handleKeyDown,
|
|
357
|
-
|
|
358
|
-
handlePaste,
|
|
359
|
-
},
|
|
360
|
-
|
|
361
|
-
appendTransaction(_, oldState, state) {
|
|
362
|
-
return normalizeSelection(
|
|
363
|
-
state,
|
|
364
|
-
fixTables(state, oldState),
|
|
365
|
-
allowTableNodeSelection,
|
|
366
|
-
)
|
|
367
|
-
},
|
|
368
|
-
})
|
|
369
|
-
}
|
|
@@ -1,345 +0,0 @@
|
|
|
1
|
-
/* Copyright 2021, Milkdown by Mirone. */
|
|
2
|
-
|
|
3
|
-
import type { Attrs, Node } from '@milkdown/prose/model'
|
|
4
|
-
|
|
5
|
-
const cache = new WeakMap<Node, TableMap>()
|
|
6
|
-
const readFromCache = (key: Node) => cache.get(key)
|
|
7
|
-
const addToCache = (key: Node, value: TableMap) => {
|
|
8
|
-
cache.set(key, value)
|
|
9
|
-
return value
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export class Rect {
|
|
13
|
-
public tableStart?: number
|
|
14
|
-
public map?: TableMap
|
|
15
|
-
public table?: Node
|
|
16
|
-
constructor(
|
|
17
|
-
public left: number,
|
|
18
|
-
public top: number,
|
|
19
|
-
public right: number,
|
|
20
|
-
public bottom: number,
|
|
21
|
-
) {}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// ::- A table map describes the structore of a given table. To avoid
|
|
25
|
-
// recomputing them all the time, they are cached per table node. To
|
|
26
|
-
// be able to do that, positions saved in the map are relative to the
|
|
27
|
-
// start of the table, rather than the start of the document.
|
|
28
|
-
export class TableMap {
|
|
29
|
-
constructor(
|
|
30
|
-
public width: number,
|
|
31
|
-
public height: number,
|
|
32
|
-
public map: number[],
|
|
33
|
-
public problems?: Problem[],
|
|
34
|
-
) {
|
|
35
|
-
// :: number The width of the table
|
|
36
|
-
this.width = width
|
|
37
|
-
// :: number The table's height
|
|
38
|
-
this.height = height
|
|
39
|
-
// :: [number] A width * height array with the start position of
|
|
40
|
-
// the cell covering that part of the table in each slot
|
|
41
|
-
this.map = map
|
|
42
|
-
// An optional array of problems (cell overlap or non-rectangular
|
|
43
|
-
// shape) for the table, used by the table normalizer.
|
|
44
|
-
this.problems = problems
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// Find the dimensions of the cell at the given position.
|
|
48
|
-
findCell(pos: number): Rect {
|
|
49
|
-
for (let i = 0; i < this.map.length; i++) {
|
|
50
|
-
const curPos = this.map[i]
|
|
51
|
-
if (curPos != pos)
|
|
52
|
-
continue
|
|
53
|
-
const left = i % this.width
|
|
54
|
-
const top = (i / this.width) | 0
|
|
55
|
-
let right = left + 1
|
|
56
|
-
let bottom = top + 1
|
|
57
|
-
for (let j = 1; right < this.width && this.map[i + j] == curPos; j++)
|
|
58
|
-
right++
|
|
59
|
-
for (
|
|
60
|
-
let j = 1;
|
|
61
|
-
bottom < this.height && this.map[i + this.width * j] == curPos;
|
|
62
|
-
j++
|
|
63
|
-
)
|
|
64
|
-
bottom++
|
|
65
|
-
return new Rect(left, top, right, bottom)
|
|
66
|
-
}
|
|
67
|
-
throw new RangeError(`No cell with offset ${pos} found`)
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
colCount(pos: number): number {
|
|
71
|
-
for (let i = 0; i < this.map.length; i++) {
|
|
72
|
-
if (this.map[i] == pos)
|
|
73
|
-
return i % this.width
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
throw new RangeError(`No cell with offset ${pos} found`)
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// :: (number, string, number) → ?number
|
|
80
|
-
// Find the next cell in the given direction, starting from the cell
|
|
81
|
-
// at `pos`, if any.
|
|
82
|
-
nextCell(pos: number, axis: string, dir: number): number | undefined {
|
|
83
|
-
const { left, right, top, bottom } = this.findCell(pos)
|
|
84
|
-
if (axis == 'horiz') {
|
|
85
|
-
if (dir < 0 ? left == 0 : right == this.width)
|
|
86
|
-
return undefined
|
|
87
|
-
return this.map[top * this.width + (dir < 0 ? left - 1 : right)]
|
|
88
|
-
}
|
|
89
|
-
else {
|
|
90
|
-
if (dir < 0 ? top == 0 : bottom == this.height)
|
|
91
|
-
return undefined
|
|
92
|
-
return this.map[left + this.width * (dir < 0 ? top - 1 : bottom)]
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// :: (number, number) → Rect
|
|
97
|
-
// Get the rectangle spanning the two given cells.
|
|
98
|
-
rectBetween(a: number, b: number): Rect {
|
|
99
|
-
const {
|
|
100
|
-
left: leftA,
|
|
101
|
-
right: rightA,
|
|
102
|
-
top: topA,
|
|
103
|
-
bottom: bottomA,
|
|
104
|
-
} = this.findCell(a)
|
|
105
|
-
const {
|
|
106
|
-
left: leftB,
|
|
107
|
-
right: rightB,
|
|
108
|
-
top: topB,
|
|
109
|
-
bottom: bottomB,
|
|
110
|
-
} = this.findCell(b)
|
|
111
|
-
return new Rect(
|
|
112
|
-
Math.min(leftA, leftB),
|
|
113
|
-
Math.min(topA, topB),
|
|
114
|
-
Math.max(rightA, rightB),
|
|
115
|
-
Math.max(bottomA, bottomB),
|
|
116
|
-
)
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// :: (Rect) → [number]
|
|
120
|
-
// Return the position of all cells that have the top left corner in
|
|
121
|
-
// the given rectangle.
|
|
122
|
-
cellsInRect(rect: Rect): number[] {
|
|
123
|
-
const result: number[] = []
|
|
124
|
-
const seen: Record<number, boolean> = {}
|
|
125
|
-
for (let row = rect.top; row < rect.bottom; row++) {
|
|
126
|
-
for (let col = rect.left; col < rect.right; col++) {
|
|
127
|
-
const index = row * this.width + col
|
|
128
|
-
const pos = this.map[index] as number
|
|
129
|
-
if (seen[pos])
|
|
130
|
-
continue
|
|
131
|
-
seen[pos] = true
|
|
132
|
-
if (
|
|
133
|
-
(col != rect.left || !col || this.map[index - 1] != pos)
|
|
134
|
-
&& (row != rect.top || !row || this.map[index - this.width] != pos)
|
|
135
|
-
)
|
|
136
|
-
result.push(pos)
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
return result
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// :: (number, number, Node) → number
|
|
143
|
-
// Return the position at which the cell at the given row and column
|
|
144
|
-
// starts, or would start, if a cell started there.
|
|
145
|
-
positionAt(row: number, col: number, table: Node): number {
|
|
146
|
-
for (let i = 0, rowStart = 0; ; i++) {
|
|
147
|
-
const rowEnd = rowStart + table.child(i).nodeSize
|
|
148
|
-
if (i == row) {
|
|
149
|
-
let index = col + row * this.width
|
|
150
|
-
const rowEndIndex = (row + 1) * this.width
|
|
151
|
-
// Skip past cells from previous rows (via rowspan)
|
|
152
|
-
while (index < rowEndIndex && (this.map[index] as number) < rowStart)
|
|
153
|
-
index++
|
|
154
|
-
return index == rowEndIndex ? rowEnd - 1 : (this.map[index] as number)
|
|
155
|
-
}
|
|
156
|
-
rowStart = rowEnd
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// Find the table map for the given table node.
|
|
161
|
-
static get(table: Node): TableMap {
|
|
162
|
-
return readFromCache(table) || addToCache(table, computeMap(table))
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
export type Problem =
|
|
167
|
-
| {
|
|
168
|
-
type: 'missing'
|
|
169
|
-
row: number
|
|
170
|
-
n: number
|
|
171
|
-
}
|
|
172
|
-
| {
|
|
173
|
-
type: 'overlong_rowspan'
|
|
174
|
-
pos: number
|
|
175
|
-
n: number
|
|
176
|
-
}
|
|
177
|
-
| {
|
|
178
|
-
type: 'collision'
|
|
179
|
-
row: number
|
|
180
|
-
pos: number
|
|
181
|
-
n: number
|
|
182
|
-
}
|
|
183
|
-
| {
|
|
184
|
-
type: 'colwidth mismatch'
|
|
185
|
-
pos: number
|
|
186
|
-
colwidth: boolean
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
// Compute a table map.
|
|
190
|
-
function computeMap(table: Node) {
|
|
191
|
-
if (table.type.spec.tableRole != 'table')
|
|
192
|
-
throw new RangeError(`Not a table node: ${table.type.name}`)
|
|
193
|
-
const width = findWidth(table)
|
|
194
|
-
const height = table.childCount
|
|
195
|
-
const map: number[] = []
|
|
196
|
-
const colWidths: number[] = []
|
|
197
|
-
let mapPos = 0
|
|
198
|
-
let problems: Problem[] | undefined
|
|
199
|
-
for (let i = 0, e = width * height; i < e; i++) map[i] = 0
|
|
200
|
-
|
|
201
|
-
for (let row = 0, pos = 0; row < height; row++) {
|
|
202
|
-
const rowNode = table.child(row)
|
|
203
|
-
pos++
|
|
204
|
-
for (let i = 0; ; i++) {
|
|
205
|
-
while (mapPos < map.length && map[mapPos] != 0) mapPos++
|
|
206
|
-
if (i == rowNode.childCount)
|
|
207
|
-
break
|
|
208
|
-
const cellNode = rowNode.child(i)
|
|
209
|
-
const { colspan, rowspan, colwidth } = cellNode.attrs
|
|
210
|
-
for (let h = 0; h < rowspan; h++) {
|
|
211
|
-
if (h + row >= height) {
|
|
212
|
-
(problems || (problems = [])).push({
|
|
213
|
-
type: 'overlong_rowspan',
|
|
214
|
-
pos,
|
|
215
|
-
n: rowspan - h,
|
|
216
|
-
})
|
|
217
|
-
break
|
|
218
|
-
}
|
|
219
|
-
const start = mapPos + h * width
|
|
220
|
-
for (let w = 0; w < colspan; w++) {
|
|
221
|
-
if (map[start + w] == 0) {
|
|
222
|
-
map[start + w] = pos
|
|
223
|
-
}
|
|
224
|
-
else {
|
|
225
|
-
(problems || (problems = [])).push({
|
|
226
|
-
type: 'collision',
|
|
227
|
-
row,
|
|
228
|
-
pos,
|
|
229
|
-
n: colspan - w,
|
|
230
|
-
})
|
|
231
|
-
}
|
|
232
|
-
const colW = colwidth && colwidth[w]
|
|
233
|
-
if (colW) {
|
|
234
|
-
const widthIndex = ((start + w) % width) * 2
|
|
235
|
-
const prev = colWidths[widthIndex]
|
|
236
|
-
if (
|
|
237
|
-
prev == null
|
|
238
|
-
|| (prev != colW && colWidths[widthIndex + 1] == 1)
|
|
239
|
-
) {
|
|
240
|
-
colWidths[widthIndex] = colW
|
|
241
|
-
colWidths[widthIndex + 1] = 1
|
|
242
|
-
}
|
|
243
|
-
else if (prev == colW) {
|
|
244
|
-
colWidths[widthIndex + 1]++
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
mapPos += colspan
|
|
250
|
-
pos += cellNode.nodeSize
|
|
251
|
-
}
|
|
252
|
-
const expectedPos = (row + 1) * width
|
|
253
|
-
let missing = 0
|
|
254
|
-
while (mapPos < expectedPos) {
|
|
255
|
-
if (map[mapPos++] == 0)
|
|
256
|
-
missing++
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
if (missing)
|
|
260
|
-
(problems || (problems = [])).push({ type: 'missing', row, n: missing })
|
|
261
|
-
pos++
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
const tableMap = new TableMap(width, height, map, problems)
|
|
265
|
-
let badWidths = false
|
|
266
|
-
|
|
267
|
-
// For columns that have defined widths, but whose widths disagree
|
|
268
|
-
// between rows, fix up the cells whose width doesn't match the
|
|
269
|
-
// computed one.
|
|
270
|
-
for (let i = 0; !badWidths && i < colWidths.length; i += 2) {
|
|
271
|
-
if (colWidths[i] != null && (colWidths[i + 1] as number) < height)
|
|
272
|
-
badWidths = true
|
|
273
|
-
}
|
|
274
|
-
if (badWidths)
|
|
275
|
-
findBadColWidths(tableMap, colWidths, table)
|
|
276
|
-
|
|
277
|
-
return tableMap
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
function findWidth(table: Node) {
|
|
281
|
-
let width = -1
|
|
282
|
-
let hasRowSpan = false
|
|
283
|
-
for (let row = 0; row < table.childCount; row++) {
|
|
284
|
-
const rowNode = table.child(row)
|
|
285
|
-
let rowWidth = 0
|
|
286
|
-
if (hasRowSpan) {
|
|
287
|
-
for (let j = 0; j < row; j++) {
|
|
288
|
-
const prevRow = table.child(j)
|
|
289
|
-
for (let i = 0; i < prevRow.childCount; i++) {
|
|
290
|
-
const cell = prevRow.child(i)
|
|
291
|
-
if (j + cell.attrs.rowspan > row)
|
|
292
|
-
rowWidth += cell.attrs.colspan
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
for (let i = 0; i < rowNode.childCount; i++) {
|
|
297
|
-
const cell = rowNode.child(i)
|
|
298
|
-
rowWidth += cell.attrs.colspan
|
|
299
|
-
if (cell.attrs.rowspan > 1)
|
|
300
|
-
hasRowSpan = true
|
|
301
|
-
}
|
|
302
|
-
if (width == -1)
|
|
303
|
-
width = rowWidth
|
|
304
|
-
else if (width != rowWidth)
|
|
305
|
-
width = Math.max(width, rowWidth)
|
|
306
|
-
}
|
|
307
|
-
return width
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
function findBadColWidths(map: TableMap, colWidths: number[], table: Node) {
|
|
311
|
-
if (!map.problems)
|
|
312
|
-
map.problems = []
|
|
313
|
-
for (let i = 0, seen: Record<number, boolean> = {}; i < map.map.length; i++) {
|
|
314
|
-
const pos = map.map[i] as number
|
|
315
|
-
if (seen[pos])
|
|
316
|
-
continue
|
|
317
|
-
seen[pos] = true
|
|
318
|
-
const node = table.nodeAt(pos) as Node
|
|
319
|
-
let updated = null
|
|
320
|
-
for (let j = 0; j < node.attrs.colspan; j++) {
|
|
321
|
-
const col = (i + j) % map.width
|
|
322
|
-
const colWidth = colWidths[col * 2]
|
|
323
|
-
if (
|
|
324
|
-
colWidth != null
|
|
325
|
-
&& (!node.attrs.colwidth || node.attrs.colwidth[j] != colWidth)
|
|
326
|
-
)
|
|
327
|
-
(updated || (updated = freshColWidth(node.attrs)))[j] = colWidth
|
|
328
|
-
}
|
|
329
|
-
if (updated) {
|
|
330
|
-
map.problems.unshift({
|
|
331
|
-
type: 'colwidth mismatch',
|
|
332
|
-
pos,
|
|
333
|
-
colwidth: updated,
|
|
334
|
-
})
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
function freshColWidth(attrs: Attrs) {
|
|
340
|
-
if (attrs.colwidth)
|
|
341
|
-
return attrs.colwidth.slice()
|
|
342
|
-
const result = []
|
|
343
|
-
for (let i = 0; i < attrs.colspan; i++) result.push(0)
|
|
344
|
-
return result
|
|
345
|
-
}
|