@jvs-milkdown/components 1.0.0 → 1.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/lib/image-block/index.js +47 -8
- package/lib/image-block/index.js.map +1 -1
- package/lib/image-block/view/components/image-viewer.d.ts.map +1 -1
- package/lib/table-block/config.d.ts +1 -1
- package/lib/table-block/config.d.ts.map +1 -1
- package/lib/table-block/index.js +622 -421
- package/lib/table-block/index.js.map +1 -1
- package/lib/table-block/view/component.d.ts.map +1 -1
- package/lib/table-block/view/operation.d.ts +2 -0
- package/lib/table-block/view/operation.d.ts.map +1 -1
- package/lib/table-block/view/view.d.ts.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +80 -54
- package/src/image-block/view/components/image-viewer.tsx +49 -8
- package/src/table-block/config.ts +6 -0
- package/src/table-block/dnd/create-drag-handler.ts +1 -13
- package/src/table-block/dnd/prepare-dnd-context.ts +0 -8
- package/src/table-block/view/component.tsx +484 -83
- package/src/table-block/view/drag.ts +2 -16
- package/src/table-block/view/operation.ts +108 -49
- package/src/table-block/view/pointer.ts +3 -155
- package/src/table-block/view/types.ts +0 -6
- package/src/table-block/view/utils.ts +2 -81
- package/src/table-block/view/view.ts +19 -58
- package/LICENSE +0 -21
|
@@ -7,10 +7,17 @@ import {
|
|
|
7
7
|
addRowAfterCommand,
|
|
8
8
|
addRowBeforeCommand,
|
|
9
9
|
deleteSelectedCellsCommand,
|
|
10
|
+
mergeCellsCommand,
|
|
10
11
|
selectColCommand,
|
|
11
12
|
selectRowCommand,
|
|
12
13
|
setAlignCommand,
|
|
13
14
|
} from '@jvs-milkdown/preset-gfm'
|
|
15
|
+
import { TextSelection } from '@jvs-milkdown/prose/state'
|
|
16
|
+
import {
|
|
17
|
+
CellSelection,
|
|
18
|
+
selectedRect,
|
|
19
|
+
splitCellWithType,
|
|
20
|
+
} from '@jvs-milkdown/prose/tables'
|
|
14
21
|
|
|
15
22
|
import type { Refs } from './types'
|
|
16
23
|
|
|
@@ -19,67 +26,43 @@ export function useOperation(
|
|
|
19
26
|
ctx?: Ctx,
|
|
20
27
|
getPos?: () => number | undefined
|
|
21
28
|
) {
|
|
22
|
-
const {
|
|
23
|
-
xLineHandleRef,
|
|
24
|
-
contentWrapperRef,
|
|
25
|
-
colHandleRef,
|
|
26
|
-
rowHandleRef,
|
|
27
|
-
hoverIndex,
|
|
28
|
-
lineHoverIndex,
|
|
29
|
-
} = refs
|
|
30
|
-
|
|
31
|
-
const onAddRow = () => {
|
|
32
|
-
if (!ctx) return
|
|
33
|
-
const xHandle = xLineHandleRef.value
|
|
34
|
-
if (!xHandle) return
|
|
29
|
+
const { contentWrapperRef, hoverIndex } = refs
|
|
35
30
|
|
|
36
|
-
|
|
37
|
-
if (
|
|
38
|
-
|
|
39
|
-
if (!ctx.get(editorViewCtx).editable) return
|
|
31
|
+
const addRowByIndex = (index: number) => {
|
|
32
|
+
if (!ctx || !ctx.get(editorViewCtx).editable) return
|
|
40
33
|
|
|
41
34
|
const rows = Array.from(
|
|
42
35
|
contentWrapperRef.value?.querySelectorAll('tr') ?? []
|
|
43
36
|
)
|
|
44
37
|
const commands = ctx.get(commandsCtx)
|
|
45
38
|
const pos = (getPos?.() ?? 0) + 1
|
|
46
|
-
if (rows.length ===
|
|
47
|
-
commands.call(selectRowCommand.key, { pos, index:
|
|
39
|
+
if (rows.length === index) {
|
|
40
|
+
commands.call(selectRowCommand.key, { pos, index: index - 1 })
|
|
48
41
|
commands.call(addRowAfterCommand.key)
|
|
49
42
|
} else {
|
|
50
|
-
commands.call(selectRowCommand.key, { pos, index
|
|
43
|
+
commands.call(selectRowCommand.key, { pos, index })
|
|
51
44
|
commands.call(addRowBeforeCommand.key)
|
|
52
45
|
}
|
|
53
46
|
|
|
54
|
-
commands.call(selectRowCommand.key, { pos, index
|
|
55
|
-
xHandle.dataset.show = 'false'
|
|
47
|
+
commands.call(selectRowCommand.key, { pos, index })
|
|
56
48
|
}
|
|
57
49
|
|
|
58
|
-
const
|
|
59
|
-
if (!ctx) return
|
|
60
|
-
const xHandle = xLineHandleRef.value
|
|
61
|
-
if (!xHandle) return
|
|
62
|
-
|
|
63
|
-
const [_, colIndex] = lineHoverIndex.value!
|
|
64
|
-
if (colIndex < 0) return
|
|
65
|
-
|
|
66
|
-
if (!ctx.get(editorViewCtx).editable) return
|
|
67
|
-
|
|
50
|
+
const addColByIndex = (index: number) => {
|
|
51
|
+
if (!ctx || !ctx.get(editorViewCtx).editable) return
|
|
68
52
|
const cols = Array.from(
|
|
69
53
|
contentWrapperRef.value?.querySelector('tr')?.children ?? []
|
|
70
54
|
)
|
|
71
55
|
const commands = ctx.get(commandsCtx)
|
|
72
56
|
|
|
73
57
|
const pos = (getPos?.() ?? 0) + 1
|
|
74
|
-
if (cols.length ===
|
|
75
|
-
commands.call(selectColCommand.key, { pos, index:
|
|
58
|
+
if (cols.length === index) {
|
|
59
|
+
commands.call(selectColCommand.key, { pos, index: index - 1 })
|
|
76
60
|
commands.call(addColAfterCommand.key)
|
|
77
61
|
} else {
|
|
78
|
-
commands.call(selectColCommand.key, { pos, index
|
|
62
|
+
commands.call(selectColCommand.key, { pos, index })
|
|
79
63
|
commands.call(addColBeforeCommand.key)
|
|
80
64
|
}
|
|
81
|
-
|
|
82
|
-
commands.call(selectColCommand.key, { pos, index: colIndex })
|
|
65
|
+
commands.call(selectColCommand.key, { pos, index })
|
|
83
66
|
}
|
|
84
67
|
|
|
85
68
|
const selectCol = () => {
|
|
@@ -88,11 +71,6 @@ export function useOperation(
|
|
|
88
71
|
const commands = ctx.get(commandsCtx)
|
|
89
72
|
const pos = (getPos?.() ?? 0) + 1
|
|
90
73
|
commands.call(selectColCommand.key, { pos, index: colIndex })
|
|
91
|
-
const buttonGroup =
|
|
92
|
-
colHandleRef.value?.querySelector<HTMLElement>('.button-group')
|
|
93
|
-
if (buttonGroup)
|
|
94
|
-
buttonGroup.dataset.show =
|
|
95
|
-
buttonGroup.dataset.show === 'true' ? 'false' : 'true'
|
|
96
74
|
}
|
|
97
75
|
|
|
98
76
|
const selectRow = () => {
|
|
@@ -101,11 +79,6 @@ export function useOperation(
|
|
|
101
79
|
const commands = ctx.get(commandsCtx)
|
|
102
80
|
const pos = (getPos?.() ?? 0) + 1
|
|
103
81
|
commands.call(selectRowCommand.key, { pos, index: rowIndex })
|
|
104
|
-
const buttonGroup =
|
|
105
|
-
rowHandleRef.value?.querySelector<HTMLElement>('.button-group')
|
|
106
|
-
if (buttonGroup && rowIndex > 0)
|
|
107
|
-
buttonGroup.dataset.show =
|
|
108
|
-
buttonGroup.dataset.show === 'true' ? 'false' : 'true'
|
|
109
82
|
}
|
|
110
83
|
|
|
111
84
|
const deleteSelected = (e: PointerEvent) => {
|
|
@@ -137,12 +110,98 @@ export function useOperation(
|
|
|
137
110
|
})
|
|
138
111
|
}
|
|
139
112
|
|
|
113
|
+
const onMergeCells = (e: PointerEvent) => {
|
|
114
|
+
if (!ctx) return
|
|
115
|
+
if (!ctx.get(editorViewCtx).editable) return
|
|
116
|
+
e.preventDefault()
|
|
117
|
+
e.stopPropagation()
|
|
118
|
+
const commands = ctx.get(commandsCtx)
|
|
119
|
+
commands.call(mergeCellsCommand.key)
|
|
120
|
+
requestAnimationFrame(() => {
|
|
121
|
+
ctx.get(editorViewCtx).focus()
|
|
122
|
+
})
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const onSplitCell = (e: PointerEvent) => {
|
|
126
|
+
if (!ctx) return
|
|
127
|
+
if (!ctx.get(editorViewCtx).editable) return
|
|
128
|
+
e.preventDefault()
|
|
129
|
+
e.stopPropagation()
|
|
130
|
+
const view = ctx.get(editorViewCtx)
|
|
131
|
+
const { state } = view
|
|
132
|
+
const { selection } = state
|
|
133
|
+
|
|
134
|
+
// Find the merged cell
|
|
135
|
+
let cellNode: ReturnType<typeof selection.$from.node> | null = null
|
|
136
|
+
let cellPos: number | null = null
|
|
137
|
+
|
|
138
|
+
if (selection instanceof CellSelection) {
|
|
139
|
+
selection.forEachCell((cell, pos) => {
|
|
140
|
+
if (
|
|
141
|
+
!cellNode &&
|
|
142
|
+
((cell.attrs.rowspan ?? 1) > 1 || (cell.attrs.colspan ?? 1) > 1)
|
|
143
|
+
) {
|
|
144
|
+
cellNode = cell
|
|
145
|
+
cellPos = pos
|
|
146
|
+
}
|
|
147
|
+
})
|
|
148
|
+
} else {
|
|
149
|
+
const { $from } = selection
|
|
150
|
+
for (let d = $from.depth; d > 0; d--) {
|
|
151
|
+
const n = $from.node(d)
|
|
152
|
+
if (n.type.name === 'table_cell' || n.type.name === 'table_header') {
|
|
153
|
+
cellNode = n
|
|
154
|
+
cellPos = $from.before(d)
|
|
155
|
+
break
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (!cellNode || cellPos == null) return
|
|
161
|
+
if (
|
|
162
|
+
(cellNode.attrs.colspan ?? 1) == 1 &&
|
|
163
|
+
(cellNode.attrs.rowspan ?? 1) == 1
|
|
164
|
+
)
|
|
165
|
+
return
|
|
166
|
+
|
|
167
|
+
// Set selection to TextSelection inside the merged cell.
|
|
168
|
+
// This avoids the prosemirror-tables bug where splitCell creates
|
|
169
|
+
// a CellSelection using unmapped positions after the split.
|
|
170
|
+
const $cellPos = state.doc.resolve(cellPos + 2)
|
|
171
|
+
view.dispatch(state.tr.setSelection(TextSelection.near($cellPos)))
|
|
172
|
+
|
|
173
|
+
// Use splitCellWithType to produce correct cell types per row.
|
|
174
|
+
// prosemirror-tables' splitCell always uses the merged cell's type
|
|
175
|
+
// (e.g. table_header) for ALL new cells, but milkdown's schema
|
|
176
|
+
// requires table_header in table_header_row and table_cell in
|
|
177
|
+
// table_row. Without this, inserts into body rows fail due to
|
|
178
|
+
// schema mismatch, corrupting the table.
|
|
179
|
+
const newState = view.state
|
|
180
|
+
const { table } = selectedRect(newState)
|
|
181
|
+
const { schema } = newState
|
|
182
|
+
|
|
183
|
+
splitCellWithType(({ row }) => {
|
|
184
|
+
const rowNode = table.child(row)
|
|
185
|
+
return rowNode.type.name === 'table_header_row'
|
|
186
|
+
? schema.nodes.table_header!
|
|
187
|
+
: schema.nodes.table_cell!
|
|
188
|
+
})(newState, (tr) => {
|
|
189
|
+
view.dispatch(tr)
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
requestAnimationFrame(() => {
|
|
193
|
+
view.focus()
|
|
194
|
+
})
|
|
195
|
+
}
|
|
196
|
+
|
|
140
197
|
return {
|
|
141
|
-
|
|
142
|
-
|
|
198
|
+
addRowByIndex,
|
|
199
|
+
addColByIndex,
|
|
143
200
|
selectCol,
|
|
144
201
|
selectRow,
|
|
145
202
|
deleteSelected,
|
|
146
203
|
onAlign,
|
|
204
|
+
onMergeCells,
|
|
205
|
+
onSplitCell,
|
|
147
206
|
}
|
|
148
207
|
}
|
|
@@ -1,162 +1,10 @@
|
|
|
1
1
|
import type { EditorView } from '@jvs-milkdown/prose/view'
|
|
2
2
|
|
|
3
|
-
import { computePosition, offset } from '@floating-ui/dom'
|
|
4
|
-
import { throttle } from 'lodash-es'
|
|
5
|
-
|
|
6
3
|
import type { Refs } from './types'
|
|
7
4
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
findPointerIndex,
|
|
12
|
-
getRelatedDOM,
|
|
13
|
-
} from './utils'
|
|
14
|
-
|
|
15
|
-
function createPointerMoveHandler(
|
|
16
|
-
refs: Refs,
|
|
17
|
-
view?: EditorView
|
|
18
|
-
): (e: PointerEvent) => void {
|
|
19
|
-
return throttle((e: PointerEvent) => {
|
|
20
|
-
if (!view?.editable) return
|
|
21
|
-
const {
|
|
22
|
-
contentWrapperRef,
|
|
23
|
-
yLineHandleRef,
|
|
24
|
-
xLineHandleRef,
|
|
25
|
-
colHandleRef,
|
|
26
|
-
rowHandleRef,
|
|
27
|
-
hoverIndex,
|
|
28
|
-
lineHoverIndex,
|
|
29
|
-
} = refs
|
|
30
|
-
const yHandle = yLineHandleRef.value
|
|
31
|
-
if (!yHandle) return
|
|
32
|
-
const xHandle = xLineHandleRef.value
|
|
33
|
-
if (!xHandle) return
|
|
34
|
-
const content = contentWrapperRef.value
|
|
35
|
-
if (!content) return
|
|
36
|
-
const rowHandle = rowHandleRef.value
|
|
37
|
-
if (!rowHandle) return
|
|
38
|
-
const colHandle = colHandleRef.value
|
|
39
|
-
if (!colHandle) return
|
|
40
|
-
|
|
41
|
-
const index = findPointerIndex(e, view)
|
|
42
|
-
if (!index) return
|
|
43
|
-
|
|
44
|
-
const dom = getRelatedDOM(contentWrapperRef, index)
|
|
45
|
-
if (!dom) return
|
|
46
|
-
|
|
47
|
-
const [rowIndex, colIndex] = index
|
|
48
|
-
const boundary = dom.col.getBoundingClientRect()
|
|
49
|
-
const closeToBoundaryLeft = Math.abs(e.clientX - boundary.left) < 8
|
|
50
|
-
const closeToBoundaryRight = Math.abs(boundary.right - e.clientX) < 8
|
|
51
|
-
const closeToBoundaryTop = Math.abs(e.clientY - boundary.top) < 8
|
|
52
|
-
const closeToBoundaryBottom = Math.abs(boundary.bottom - e.clientY) < 8
|
|
53
|
-
|
|
54
|
-
const closeToBoundary =
|
|
55
|
-
closeToBoundaryLeft ||
|
|
56
|
-
closeToBoundaryRight ||
|
|
57
|
-
closeToBoundaryTop ||
|
|
58
|
-
closeToBoundaryBottom
|
|
59
|
-
|
|
60
|
-
const rowButtonGroup = rowHandle.querySelector<HTMLElement>('.button-group')
|
|
61
|
-
const colButtonGroup = colHandle.querySelector<HTMLElement>('.button-group')
|
|
62
|
-
if (rowButtonGroup) rowButtonGroup.dataset.show = 'false'
|
|
63
|
-
if (colButtonGroup) colButtonGroup.dataset.show = 'false'
|
|
64
|
-
|
|
65
|
-
if (closeToBoundary) {
|
|
66
|
-
const contentBoundary = content.getBoundingClientRect()
|
|
67
|
-
rowHandle.dataset.show = 'false'
|
|
68
|
-
colHandle.dataset.show = 'false'
|
|
69
|
-
xHandle.dataset.displayType = 'tool'
|
|
70
|
-
yHandle.dataset.displayType = 'tool'
|
|
71
|
-
|
|
72
|
-
const yHandleWidth = yHandle.getBoundingClientRect().width
|
|
73
|
-
const xHandleHeight = xHandle.getBoundingClientRect().height
|
|
74
|
-
|
|
75
|
-
// display vertical line handle
|
|
76
|
-
if (closeToBoundaryLeft || closeToBoundaryRight) {
|
|
77
|
-
lineHoverIndex.value![1] = closeToBoundaryLeft ? colIndex : colIndex + 1
|
|
78
|
-
computePosition(dom.col, yHandle, {
|
|
79
|
-
placement: closeToBoundaryLeft ? 'left' : 'right',
|
|
80
|
-
middleware: [offset(closeToBoundaryLeft ? -1 * yHandleWidth : 0)],
|
|
81
|
-
})
|
|
82
|
-
.then(({ x }) => {
|
|
83
|
-
yHandle.dataset.show = 'true'
|
|
84
|
-
Object.assign(yHandle.style, {
|
|
85
|
-
height: `${contentBoundary.height}px`,
|
|
86
|
-
left: `${x}px`,
|
|
87
|
-
})
|
|
88
|
-
})
|
|
89
|
-
.catch(console.error)
|
|
90
|
-
} else {
|
|
91
|
-
yHandle.dataset.show = 'false'
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// display horizontal line handle
|
|
95
|
-
// won't display if the row is the header row
|
|
96
|
-
if (index[0] !== 0 && (closeToBoundaryTop || closeToBoundaryBottom)) {
|
|
97
|
-
lineHoverIndex.value![0] = closeToBoundaryTop ? rowIndex : rowIndex + 1
|
|
98
|
-
computePosition(dom.row, xHandle, {
|
|
99
|
-
placement: closeToBoundaryTop ? 'top' : 'bottom',
|
|
100
|
-
middleware: [offset(closeToBoundaryTop ? -1 * xHandleHeight : 0)],
|
|
101
|
-
})
|
|
102
|
-
.then(({ y }) => {
|
|
103
|
-
xHandle.dataset.show = 'true'
|
|
104
|
-
Object.assign(xHandle.style, {
|
|
105
|
-
width: `${contentBoundary.width}px`,
|
|
106
|
-
top: `${y}px`,
|
|
107
|
-
})
|
|
108
|
-
})
|
|
109
|
-
.catch(console.error)
|
|
110
|
-
} else {
|
|
111
|
-
xHandle.dataset.show = 'false'
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
return
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
lineHoverIndex.value = [-1, -1]
|
|
118
|
-
|
|
119
|
-
yHandle.dataset.show = 'false'
|
|
120
|
-
xHandle.dataset.show = 'false'
|
|
121
|
-
rowHandle.dataset.show = 'true'
|
|
122
|
-
colHandle.dataset.show = 'true'
|
|
123
|
-
|
|
124
|
-
computeRowHandlePositionByIndex({
|
|
125
|
-
refs,
|
|
126
|
-
index,
|
|
127
|
-
})
|
|
128
|
-
computeColHandlePositionByIndex({
|
|
129
|
-
refs,
|
|
130
|
-
index,
|
|
131
|
-
})
|
|
132
|
-
hoverIndex.value = index
|
|
133
|
-
}, 20)
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
function createPointerLeaveHandler(refs: Refs): () => void {
|
|
137
|
-
return () => {
|
|
138
|
-
const { rowHandleRef, colHandleRef, yLineHandleRef, xLineHandleRef } = refs
|
|
139
|
-
setTimeout(() => {
|
|
140
|
-
const rowHandle = rowHandleRef.value
|
|
141
|
-
if (!rowHandle) return
|
|
142
|
-
const colHandle = colHandleRef.value
|
|
143
|
-
if (!colHandle) return
|
|
144
|
-
const yHandle = yLineHandleRef.value
|
|
145
|
-
if (!yHandle) return
|
|
146
|
-
const xHandle = xLineHandleRef.value
|
|
147
|
-
if (!xHandle) return
|
|
148
|
-
|
|
149
|
-
rowHandle.dataset.show = 'false'
|
|
150
|
-
colHandle.dataset.show = 'false'
|
|
151
|
-
yHandle.dataset.show = 'false'
|
|
152
|
-
xHandle.dataset.show = 'false'
|
|
153
|
-
}, 200)
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
export function usePointerHandlers(refs: Refs, view?: EditorView) {
|
|
158
|
-
const pointerMove = createPointerMoveHandler(refs, view)
|
|
159
|
-
const pointerLeave = createPointerLeaveHandler(refs)
|
|
5
|
+
export function usePointerHandlers(_refs: Refs, _view?: EditorView) {
|
|
6
|
+
const pointerMove = (_e: PointerEvent) => {}
|
|
7
|
+
const pointerLeave = () => {}
|
|
160
8
|
|
|
161
9
|
return {
|
|
162
10
|
pointerMove,
|
|
@@ -17,18 +17,12 @@ export interface DragContext {
|
|
|
17
17
|
contentRoot: HTMLTableSectionElement
|
|
18
18
|
yHandle: HTMLDivElement
|
|
19
19
|
xHandle: HTMLDivElement
|
|
20
|
-
colHandle: HTMLDivElement
|
|
21
|
-
rowHandle: HTMLDivElement
|
|
22
20
|
}
|
|
23
21
|
|
|
24
22
|
export interface Refs {
|
|
25
23
|
dragPreviewRef: Ref<HTMLDivElement | undefined>
|
|
26
24
|
tableWrapperRef: Ref<HTMLDivElement | undefined>
|
|
27
25
|
contentWrapperRef: Ref<HTMLElement | undefined>
|
|
28
|
-
yLineHandleRef: Ref<HTMLDivElement | undefined>
|
|
29
|
-
xLineHandleRef: Ref<HTMLDivElement | undefined>
|
|
30
|
-
colHandleRef: Ref<HTMLDivElement | undefined>
|
|
31
|
-
rowHandleRef: Ref<HTMLDivElement | undefined>
|
|
32
26
|
hoverIndex: Ref<CellIndex>
|
|
33
27
|
lineHoverIndex: Ref<CellIndex>
|
|
34
28
|
dragInfo: Ref<DragInfo | undefined>
|
|
@@ -2,7 +2,6 @@ import type { Node } from '@jvs-milkdown/prose/model'
|
|
|
2
2
|
import type { EditorView } from '@jvs-milkdown/prose/view'
|
|
3
3
|
import type { Ref } from 'vue'
|
|
4
4
|
|
|
5
|
-
import { computePosition } from '@floating-ui/dom'
|
|
6
5
|
import { findParent } from '@jvs-milkdown/prose'
|
|
7
6
|
import { CellSelection, findTable } from '@jvs-milkdown/prose/tables'
|
|
8
7
|
|
|
@@ -98,15 +97,7 @@ export function recoveryStateBetweenUpdate(
|
|
|
98
97
|
if (selection.isColSelection()) {
|
|
99
98
|
const { $head } = selection
|
|
100
99
|
const colIndex = $head.index($head.depth - 1)
|
|
101
|
-
|
|
102
|
-
refs,
|
|
103
|
-
index: [0, colIndex],
|
|
104
|
-
before: (handleDOM) => {
|
|
105
|
-
handleDOM
|
|
106
|
-
.querySelector('.button-group')
|
|
107
|
-
?.setAttribute('data-show', 'true')
|
|
108
|
-
},
|
|
109
|
-
})
|
|
100
|
+
refs.hoverIndex.value = [0, colIndex]
|
|
110
101
|
return
|
|
111
102
|
}
|
|
112
103
|
if (selection.isRowSelection()) {
|
|
@@ -117,76 +108,6 @@ export function recoveryStateBetweenUpdate(
|
|
|
117
108
|
)($head)
|
|
118
109
|
if (!rowNode) return
|
|
119
110
|
const rowIndex = findNodeIndex(table.node, rowNode.node)
|
|
120
|
-
|
|
121
|
-
refs,
|
|
122
|
-
index: [rowIndex, 0],
|
|
123
|
-
before: (handleDOM) => {
|
|
124
|
-
if (rowIndex > 0)
|
|
125
|
-
handleDOM
|
|
126
|
-
.querySelector('.button-group')
|
|
127
|
-
?.setAttribute('data-show', 'true')
|
|
128
|
-
},
|
|
129
|
-
})
|
|
111
|
+
refs.hoverIndex.value = [rowIndex, 0]
|
|
130
112
|
}
|
|
131
113
|
}
|
|
132
|
-
|
|
133
|
-
interface ComputeHandlePositionByIndexProps {
|
|
134
|
-
refs: Refs
|
|
135
|
-
index: CellIndex
|
|
136
|
-
before?: (handleDOM: HTMLDivElement) => void
|
|
137
|
-
after?: (handleDOM: HTMLDivElement) => void
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
export function computeColHandlePositionByIndex({
|
|
141
|
-
refs,
|
|
142
|
-
index,
|
|
143
|
-
before,
|
|
144
|
-
after,
|
|
145
|
-
}: ComputeHandlePositionByIndexProps) {
|
|
146
|
-
const { contentWrapperRef, colHandleRef, hoverIndex } = refs
|
|
147
|
-
const colHandle = colHandleRef.value
|
|
148
|
-
if (!colHandle) return
|
|
149
|
-
|
|
150
|
-
hoverIndex.value = index
|
|
151
|
-
const dom = getRelatedDOM(contentWrapperRef, index)
|
|
152
|
-
if (!dom) return
|
|
153
|
-
const { headerCol: col } = dom
|
|
154
|
-
colHandle.dataset.show = 'true'
|
|
155
|
-
if (before) before(colHandle)
|
|
156
|
-
computePosition(col, colHandle, { placement: 'top' })
|
|
157
|
-
.then(({ x, y }) => {
|
|
158
|
-
Object.assign(colHandle.style, {
|
|
159
|
-
left: `${x}px`,
|
|
160
|
-
top: `${y}px`,
|
|
161
|
-
})
|
|
162
|
-
if (after) after(colHandle)
|
|
163
|
-
})
|
|
164
|
-
.catch(console.error)
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
export function computeRowHandlePositionByIndex({
|
|
168
|
-
refs,
|
|
169
|
-
index,
|
|
170
|
-
before,
|
|
171
|
-
after,
|
|
172
|
-
}: ComputeHandlePositionByIndexProps) {
|
|
173
|
-
const { contentWrapperRef, rowHandleRef, hoverIndex } = refs
|
|
174
|
-
const rowHandle = rowHandleRef.value
|
|
175
|
-
if (!rowHandle) return
|
|
176
|
-
|
|
177
|
-
hoverIndex.value = index
|
|
178
|
-
const dom = getRelatedDOM(contentWrapperRef, index)
|
|
179
|
-
if (!dom) return
|
|
180
|
-
const { row } = dom
|
|
181
|
-
rowHandle.dataset.show = 'true'
|
|
182
|
-
if (before) before(rowHandle)
|
|
183
|
-
computePosition(row, rowHandle, { placement: 'left' })
|
|
184
|
-
.then(({ x, y }) => {
|
|
185
|
-
Object.assign(rowHandle.style, {
|
|
186
|
-
left: `${x}px`,
|
|
187
|
-
top: `${y}px`,
|
|
188
|
-
})
|
|
189
|
-
if (after) after(rowHandle)
|
|
190
|
-
})
|
|
191
|
-
.catch(console.error)
|
|
192
|
-
}
|
|
@@ -8,11 +8,14 @@ import type {
|
|
|
8
8
|
} from '@jvs-milkdown/prose/view'
|
|
9
9
|
|
|
10
10
|
import { tableSchema } from '@jvs-milkdown/preset-gfm'
|
|
11
|
-
import { findParent } from '@jvs-milkdown/prose'
|
|
12
|
-
import { NodeSelection, TextSelection } from '@jvs-milkdown/prose/state'
|
|
13
|
-
import { CellSelection } from '@jvs-milkdown/prose/tables'
|
|
14
11
|
import { $view } from '@jvs-milkdown/utils'
|
|
15
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
createApp,
|
|
14
|
+
shallowRef,
|
|
15
|
+
triggerRef,
|
|
16
|
+
type App,
|
|
17
|
+
type ShallowRef,
|
|
18
|
+
} from 'vue'
|
|
16
19
|
|
|
17
20
|
import { withMeta } from '../../__internal__/meta'
|
|
18
21
|
import { tableBlockConfig } from '../config'
|
|
@@ -24,6 +27,7 @@ export class TableNodeView implements NodeView {
|
|
|
24
27
|
app: App
|
|
25
28
|
|
|
26
29
|
nodeRef: ShallowRef<Node>
|
|
30
|
+
#selectionUpdateScheduled = false
|
|
27
31
|
|
|
28
32
|
constructor(
|
|
29
33
|
public ctx: Ctx,
|
|
@@ -59,8 +63,17 @@ export class TableNodeView implements NodeView {
|
|
|
59
63
|
update(node: Node) {
|
|
60
64
|
if (node.type !== this.node.type) return false
|
|
61
65
|
|
|
62
|
-
if (node.sameMarkup(this.node) && node.content.eq(this.node.content))
|
|
63
|
-
|
|
66
|
+
if (node.sameMarkup(this.node) && node.content.eq(this.node.content)) {
|
|
67
|
+
// Content unchanged — still trigger a reactive tick for selection-based UI (cell toolbar)
|
|
68
|
+
if (!this.#selectionUpdateScheduled) {
|
|
69
|
+
this.#selectionUpdateScheduled = true
|
|
70
|
+
queueMicrotask(() => {
|
|
71
|
+
triggerRef(this.nodeRef)
|
|
72
|
+
this.#selectionUpdateScheduled = false
|
|
73
|
+
})
|
|
74
|
+
}
|
|
75
|
+
return true
|
|
76
|
+
}
|
|
64
77
|
|
|
65
78
|
this.node = node
|
|
66
79
|
this.nodeRef.value = node
|
|
@@ -68,63 +81,11 @@ export class TableNodeView implements NodeView {
|
|
|
68
81
|
return true
|
|
69
82
|
}
|
|
70
83
|
|
|
71
|
-
#handleClick(event: PointerEvent) {
|
|
72
|
-
const view = this.view
|
|
73
|
-
if (!view.editable) return false
|
|
74
|
-
|
|
75
|
-
const { state, dispatch } = view
|
|
76
|
-
const pos = view.posAtCoords({ left: event.clientX, top: event.clientY })
|
|
77
|
-
|
|
78
|
-
if (!pos) return false
|
|
79
|
-
|
|
80
|
-
const $pos = state.doc.resolve(pos.inside)
|
|
81
|
-
const node = findParent(
|
|
82
|
-
(node) =>
|
|
83
|
-
node.type.name === 'table_cell' || node.type.name === 'table_header'
|
|
84
|
-
)($pos)
|
|
85
|
-
|
|
86
|
-
if (!node) return false
|
|
87
|
-
|
|
88
|
-
// if the selection is a text selection, and the current node is the same as the node, return false
|
|
89
|
-
if (state.selection instanceof TextSelection) {
|
|
90
|
-
const currentNode = findParent(
|
|
91
|
-
(node) =>
|
|
92
|
-
node.type.name === 'table_cell' || node.type.name === 'table_header'
|
|
93
|
-
)(state.selection.$from)
|
|
94
|
-
if (currentNode?.node === node.node) return false
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
const { from } = node
|
|
98
|
-
|
|
99
|
-
const selection = NodeSelection.create(state.doc, from + 1)
|
|
100
|
-
if (state.selection.eq(selection)) return false
|
|
101
|
-
|
|
102
|
-
if (state.selection instanceof CellSelection) {
|
|
103
|
-
setTimeout(() => {
|
|
104
|
-
dispatch(state.tr.setSelection(selection).scrollIntoView())
|
|
105
|
-
}, 20)
|
|
106
|
-
} else {
|
|
107
|
-
requestAnimationFrame(() => {
|
|
108
|
-
dispatch(state.tr.setSelection(selection).scrollIntoView())
|
|
109
|
-
})
|
|
110
|
-
}
|
|
111
|
-
return true
|
|
112
|
-
}
|
|
113
|
-
|
|
114
84
|
stopEvent(e: Event) {
|
|
115
85
|
if (e.type === 'drop' || e.type.startsWith('drag')) return true
|
|
116
86
|
|
|
117
87
|
if (e.type === 'mousedown' || e.type === 'pointerdown') {
|
|
118
88
|
if (e.target instanceof Element && e.target.closest('button')) return true
|
|
119
|
-
|
|
120
|
-
const target = e.target
|
|
121
|
-
if (
|
|
122
|
-
target instanceof HTMLElement &&
|
|
123
|
-
(target.closest('th') || target.closest('td'))
|
|
124
|
-
) {
|
|
125
|
-
const event = e as PointerEvent
|
|
126
|
-
return this.#handleClick(event)
|
|
127
|
-
}
|
|
128
89
|
}
|
|
129
90
|
|
|
130
91
|
return false
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
The MIT License (MIT)
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2020-present Mirone
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|