@jvs-milkdown/components 1.0.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 +11 -0
- package/lib/__internal__/components/icon.d.ts +24 -0
- package/lib/__internal__/components/icon.d.ts.map +1 -0
- package/lib/__internal__/components/image-input.d.ts +17 -0
- package/lib/__internal__/components/image-input.d.ts.map +1 -0
- package/lib/__internal__/keep-alive.d.ts +2 -0
- package/lib/__internal__/keep-alive.d.ts.map +1 -0
- package/lib/__internal__/meta.d.ts +3 -0
- package/lib/__internal__/meta.d.ts.map +1 -0
- package/lib/__tests__/setup.d.ts +2 -0
- package/lib/__tests__/setup.d.ts.map +1 -0
- package/lib/code-block/config.d.ts +23 -0
- package/lib/code-block/config.d.ts.map +1 -0
- package/lib/code-block/index.d.ts +5 -0
- package/lib/code-block/index.d.ts.map +1 -0
- package/lib/code-block/index.js +4160 -0
- package/lib/code-block/index.js.map +1 -0
- package/lib/code-block/view/components/code-block.d.ts +16 -0
- package/lib/code-block/view/components/code-block.d.ts.map +1 -0
- package/lib/code-block/view/components/copy-button.d.ts +9 -0
- package/lib/code-block/view/components/copy-button.d.ts.map +1 -0
- package/lib/code-block/view/components/language-picker.d.ts +5 -0
- package/lib/code-block/view/components/language-picker.d.ts.map +1 -0
- package/lib/code-block/view/components/preview-panel.d.ts +9 -0
- package/lib/code-block/view/components/preview-panel.d.ts.map +1 -0
- package/lib/code-block/view/index.d.ts +3 -0
- package/lib/code-block/view/index.d.ts.map +1 -0
- package/lib/code-block/view/loader.d.ts +13 -0
- package/lib/code-block/view/loader.d.ts.map +1 -0
- package/lib/code-block/view/node-view.d.ts +40 -0
- package/lib/code-block/view/node-view.d.ts.map +1 -0
- package/lib/image-block/config.d.ts +16 -0
- package/lib/image-block/config.d.ts.map +1 -0
- package/lib/image-block/index.d.ts +7 -0
- package/lib/image-block/index.d.ts.map +1 -0
- package/lib/image-block/index.js +660 -0
- package/lib/image-block/index.js.map +1 -0
- package/lib/image-block/remark-plugin.d.ts +2 -0
- package/lib/image-block/remark-plugin.d.ts.map +1 -0
- package/lib/image-block/schema.d.ts +3 -0
- package/lib/image-block/schema.d.ts.map +1 -0
- package/lib/image-block/view/components/__tests__/image-viewer.onImageLoadError.spec.d.ts +2 -0
- package/lib/image-block/view/components/__tests__/image-viewer.onImageLoadError.spec.d.ts.map +1 -0
- package/lib/image-block/view/components/image-block.d.ts +18 -0
- package/lib/image-block/view/components/image-block.d.ts.map +1 -0
- package/lib/image-block/view/components/image-viewer.d.ts +3 -0
- package/lib/image-block/view/components/image-viewer.d.ts.map +1 -0
- package/lib/image-block/view/index.d.ts +3 -0
- package/lib/image-block/view/index.d.ts.map +1 -0
- package/lib/image-inline/components/image-inline.d.ts +18 -0
- package/lib/image-inline/components/image-inline.d.ts.map +1 -0
- package/lib/image-inline/config.d.ts +11 -0
- package/lib/image-inline/config.d.ts.map +1 -0
- package/lib/image-inline/index.d.ts +5 -0
- package/lib/image-inline/index.d.ts.map +1 -0
- package/lib/image-inline/index.js +377 -0
- package/lib/image-inline/index.js.map +1 -0
- package/lib/image-inline/view.d.ts +3 -0
- package/lib/image-inline/view.d.ts.map +1 -0
- package/lib/index.d.ts +2 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +35 -0
- package/lib/index.js.map +1 -0
- package/lib/link-tooltip/command.d.ts +2 -0
- package/lib/link-tooltip/command.d.ts.map +1 -0
- package/lib/link-tooltip/configure.d.ts +3 -0
- package/lib/link-tooltip/configure.d.ts.map +1 -0
- package/lib/link-tooltip/edit/component.d.ts +11 -0
- package/lib/link-tooltip/edit/component.d.ts.map +1 -0
- package/lib/link-tooltip/edit/edit-configure.d.ts +3 -0
- package/lib/link-tooltip/edit/edit-configure.d.ts.map +1 -0
- package/lib/link-tooltip/edit/edit-view.d.ts +15 -0
- package/lib/link-tooltip/edit/edit-view.d.ts.map +1 -0
- package/lib/link-tooltip/index.d.ts +7 -0
- package/lib/link-tooltip/index.d.ts.map +1 -0
- package/lib/link-tooltip/index.js +2526 -0
- package/lib/link-tooltip/index.js.map +1 -0
- package/lib/link-tooltip/preview/component.d.ts +11 -0
- package/lib/link-tooltip/preview/component.d.ts.map +1 -0
- package/lib/link-tooltip/preview/preview-configure.d.ts +3 -0
- package/lib/link-tooltip/preview/preview-configure.d.ts.map +1 -0
- package/lib/link-tooltip/preview/preview-view.d.ts +14 -0
- package/lib/link-tooltip/preview/preview-view.d.ts.map +1 -0
- package/lib/link-tooltip/slices.d.ts +34 -0
- package/lib/link-tooltip/slices.d.ts.map +1 -0
- package/lib/link-tooltip/tooltips.d.ts +3 -0
- package/lib/link-tooltip/tooltips.d.ts.map +1 -0
- package/lib/link-tooltip/utils.d.ts +14 -0
- package/lib/link-tooltip/utils.d.ts.map +1 -0
- package/lib/list-item-block/component.d.ts +19 -0
- package/lib/list-item-block/component.d.ts.map +1 -0
- package/lib/list-item-block/config.d.ts +13 -0
- package/lib/list-item-block/config.d.ts.map +1 -0
- package/lib/list-item-block/index.d.ts +6 -0
- package/lib/list-item-block/index.d.ts.map +1 -0
- package/lib/list-item-block/index.js +2149 -0
- package/lib/list-item-block/index.js.map +1 -0
- package/lib/list-item-block/view.d.ts +3 -0
- package/lib/list-item-block/view.d.ts.map +1 -0
- package/lib/table-block/config.d.ts +8 -0
- package/lib/table-block/config.d.ts.map +1 -0
- package/lib/table-block/dnd/calc-drag-over.d.ts +3 -0
- package/lib/table-block/dnd/calc-drag-over.d.ts.map +1 -0
- package/lib/table-block/dnd/create-drag-handler.d.ts +5 -0
- package/lib/table-block/dnd/create-drag-handler.d.ts.map +1 -0
- package/lib/table-block/dnd/drag-over-handler.d.ts +3 -0
- package/lib/table-block/dnd/drag-over-handler.d.ts.map +1 -0
- package/lib/table-block/dnd/prepare-dnd-context.d.ts +3 -0
- package/lib/table-block/dnd/prepare-dnd-context.d.ts.map +1 -0
- package/lib/table-block/dnd/preview.d.ts +3 -0
- package/lib/table-block/dnd/preview.d.ts.map +1 -0
- package/lib/table-block/index.d.ts +5 -0
- package/lib/table-block/index.d.ts.map +1 -0
- package/lib/table-block/index.js +3961 -0
- package/lib/table-block/index.js.map +1 -0
- package/lib/table-block/view/component.d.ts +16 -0
- package/lib/table-block/view/component.d.ts.map +1 -0
- package/lib/table-block/view/drag.d.ts +7 -0
- package/lib/table-block/view/drag.d.ts.map +1 -0
- package/lib/table-block/view/index.d.ts +2 -0
- package/lib/table-block/view/index.d.ts.map +1 -0
- package/lib/table-block/view/operation.d.ts +11 -0
- package/lib/table-block/view/operation.d.ts.map +1 -0
- package/lib/table-block/view/pointer.d.ts +7 -0
- package/lib/table-block/view/pointer.d.ts.map +1 -0
- package/lib/table-block/view/types.d.ts +32 -0
- package/lib/table-block/view/types.d.ts.map +1 -0
- package/lib/table-block/view/utils.d.ts +21 -0
- package/lib/table-block/view/utils.d.ts.map +1 -0
- package/lib/table-block/view/view.d.ts +22 -0
- package/lib/table-block/view/view.d.ts.map +1 -0
- package/lib/tsconfig.tsbuildinfo +1 -0
- package/package.json +110 -0
- package/src/__internal__/components/icon.tsx +38 -0
- package/src/__internal__/components/image-input.tsx +182 -0
- package/src/__internal__/keep-alive.ts +3 -0
- package/src/__internal__/meta.ts +15 -0
- package/src/__tests__/setup.ts +6 -0
- package/src/code-block/config.ts +54 -0
- package/src/code-block/index.ts +12 -0
- package/src/code-block/view/components/code-block.tsx +170 -0
- package/src/code-block/view/components/copy-button.tsx +96 -0
- package/src/code-block/view/components/language-picker.tsx +239 -0
- package/src/code-block/view/components/preview-panel.tsx +79 -0
- package/src/code-block/view/index.ts +24 -0
- package/src/code-block/view/loader.ts +40 -0
- package/src/code-block/view/node-view.ts +310 -0
- package/src/image-block/config.ts +37 -0
- package/src/image-block/index.ts +18 -0
- package/src/image-block/remark-plugin.ts +51 -0
- package/src/image-block/schema.ts +71 -0
- package/src/image-block/view/components/__tests__/image-viewer.onImageLoadError.spec.tsx +42 -0
- package/src/image-block/view/components/image-block.tsx +80 -0
- package/src/image-block/view/components/image-viewer.tsx +186 -0
- package/src/image-block/view/index.ts +111 -0
- package/src/image-inline/components/image-inline.tsx +85 -0
- package/src/image-inline/config.ts +30 -0
- package/src/image-inline/index.ts +12 -0
- package/src/image-inline/view.ts +109 -0
- package/src/index.ts +1 -0
- package/src/link-tooltip/command.ts +19 -0
- package/src/link-tooltip/configure.ts +9 -0
- package/src/link-tooltip/edit/component.tsx +82 -0
- package/src/link-tooltip/edit/edit-configure.ts +29 -0
- package/src/link-tooltip/edit/edit-view.ts +165 -0
- package/src/link-tooltip/index.ts +19 -0
- package/src/link-tooltip/preview/component.tsx +87 -0
- package/src/link-tooltip/preview/preview-configure.ts +65 -0
- package/src/link-tooltip/preview/preview-view.ts +101 -0
- package/src/link-tooltip/slices.ts +69 -0
- package/src/link-tooltip/tooltips.ts +22 -0
- package/src/link-tooltip/utils.ts +56 -0
- package/src/list-item-block/component.tsx +133 -0
- package/src/list-item-block/config.ts +39 -0
- package/src/list-item-block/index.ts +13 -0
- package/src/list-item-block/view.ts +130 -0
- package/src/table-block/config.ts +53 -0
- package/src/table-block/dnd/calc-drag-over.ts +46 -0
- package/src/table-block/dnd/create-drag-handler.ts +99 -0
- package/src/table-block/dnd/drag-over-handler.ts +113 -0
- package/src/table-block/dnd/prepare-dnd-context.ts +46 -0
- package/src/table-block/dnd/preview.ts +58 -0
- package/src/table-block/index.ts +9 -0
- package/src/table-block/view/component.tsx +219 -0
- package/src/table-block/view/drag.ts +121 -0
- package/src/table-block/view/index.ts +1 -0
- package/src/table-block/view/operation.ts +148 -0
- package/src/table-block/view/pointer.ts +165 -0
- package/src/table-block/view/types.ts +35 -0
- package/src/table-block/view/utils.ts +192 -0
- package/src/table-block/view/view.ts +165 -0
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import type { Ctx } from '@jvs-milkdown/ctx'
|
|
2
|
+
|
|
3
|
+
import { commandsCtx, editorViewCtx } from '@jvs-milkdown/core'
|
|
4
|
+
import {
|
|
5
|
+
addColAfterCommand,
|
|
6
|
+
addColBeforeCommand,
|
|
7
|
+
addRowAfterCommand,
|
|
8
|
+
addRowBeforeCommand,
|
|
9
|
+
deleteSelectedCellsCommand,
|
|
10
|
+
selectColCommand,
|
|
11
|
+
selectRowCommand,
|
|
12
|
+
setAlignCommand,
|
|
13
|
+
} from '@jvs-milkdown/preset-gfm'
|
|
14
|
+
|
|
15
|
+
import type { Refs } from './types'
|
|
16
|
+
|
|
17
|
+
export function useOperation(
|
|
18
|
+
refs: Refs,
|
|
19
|
+
ctx?: Ctx,
|
|
20
|
+
getPos?: () => number | undefined
|
|
21
|
+
) {
|
|
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
|
|
35
|
+
|
|
36
|
+
const [rowIndex] = lineHoverIndex.value!
|
|
37
|
+
if (rowIndex < 0) return
|
|
38
|
+
|
|
39
|
+
if (!ctx.get(editorViewCtx).editable) return
|
|
40
|
+
|
|
41
|
+
const rows = Array.from(
|
|
42
|
+
contentWrapperRef.value?.querySelectorAll('tr') ?? []
|
|
43
|
+
)
|
|
44
|
+
const commands = ctx.get(commandsCtx)
|
|
45
|
+
const pos = (getPos?.() ?? 0) + 1
|
|
46
|
+
if (rows.length === rowIndex) {
|
|
47
|
+
commands.call(selectRowCommand.key, { pos, index: rowIndex - 1 })
|
|
48
|
+
commands.call(addRowAfterCommand.key)
|
|
49
|
+
} else {
|
|
50
|
+
commands.call(selectRowCommand.key, { pos, index: rowIndex })
|
|
51
|
+
commands.call(addRowBeforeCommand.key)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
commands.call(selectRowCommand.key, { pos, index: rowIndex })
|
|
55
|
+
xHandle.dataset.show = 'false'
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const onAddCol = () => {
|
|
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
|
+
|
|
68
|
+
const cols = Array.from(
|
|
69
|
+
contentWrapperRef.value?.querySelector('tr')?.children ?? []
|
|
70
|
+
)
|
|
71
|
+
const commands = ctx.get(commandsCtx)
|
|
72
|
+
|
|
73
|
+
const pos = (getPos?.() ?? 0) + 1
|
|
74
|
+
if (cols.length === colIndex) {
|
|
75
|
+
commands.call(selectColCommand.key, { pos, index: colIndex - 1 })
|
|
76
|
+
commands.call(addColAfterCommand.key)
|
|
77
|
+
} else {
|
|
78
|
+
commands.call(selectColCommand.key, { pos, index: colIndex })
|
|
79
|
+
commands.call(addColBeforeCommand.key)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
commands.call(selectColCommand.key, { pos, index: colIndex })
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const selectCol = () => {
|
|
86
|
+
if (!ctx) return
|
|
87
|
+
const [_, colIndex] = hoverIndex.value!
|
|
88
|
+
const commands = ctx.get(commandsCtx)
|
|
89
|
+
const pos = (getPos?.() ?? 0) + 1
|
|
90
|
+
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
|
+
}
|
|
97
|
+
|
|
98
|
+
const selectRow = () => {
|
|
99
|
+
if (!ctx) return
|
|
100
|
+
const [rowIndex, _] = hoverIndex.value!
|
|
101
|
+
const commands = ctx.get(commandsCtx)
|
|
102
|
+
const pos = (getPos?.() ?? 0) + 1
|
|
103
|
+
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
|
+
}
|
|
110
|
+
|
|
111
|
+
const deleteSelected = (e: PointerEvent) => {
|
|
112
|
+
if (!ctx) return
|
|
113
|
+
|
|
114
|
+
if (!ctx.get(editorViewCtx).editable) return
|
|
115
|
+
|
|
116
|
+
e.preventDefault()
|
|
117
|
+
e.stopPropagation()
|
|
118
|
+
const commands = ctx.get(commandsCtx)
|
|
119
|
+
commands.call(deleteSelectedCellsCommand.key)
|
|
120
|
+
requestAnimationFrame(() => {
|
|
121
|
+
ctx.get(editorViewCtx).focus()
|
|
122
|
+
})
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const onAlign =
|
|
126
|
+
(direction: 'left' | 'center' | 'right') => (e: PointerEvent) => {
|
|
127
|
+
if (!ctx) return
|
|
128
|
+
|
|
129
|
+
if (!ctx.get(editorViewCtx).editable) return
|
|
130
|
+
|
|
131
|
+
e.preventDefault()
|
|
132
|
+
e.stopPropagation()
|
|
133
|
+
const commands = ctx.get(commandsCtx)
|
|
134
|
+
commands.call(setAlignCommand.key, direction)
|
|
135
|
+
requestAnimationFrame(() => {
|
|
136
|
+
ctx.get(editorViewCtx).focus()
|
|
137
|
+
})
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return {
|
|
141
|
+
onAddRow,
|
|
142
|
+
onAddCol,
|
|
143
|
+
selectCol,
|
|
144
|
+
selectRow,
|
|
145
|
+
deleteSelected,
|
|
146
|
+
onAlign,
|
|
147
|
+
}
|
|
148
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import type { EditorView } from '@jvs-milkdown/prose/view'
|
|
2
|
+
|
|
3
|
+
import { computePosition, offset } from '@floating-ui/dom'
|
|
4
|
+
import { throttle } from 'lodash-es'
|
|
5
|
+
|
|
6
|
+
import type { Refs } from './types'
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
computeColHandlePositionByIndex,
|
|
10
|
+
computeRowHandlePositionByIndex,
|
|
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)
|
|
160
|
+
|
|
161
|
+
return {
|
|
162
|
+
pointerMove,
|
|
163
|
+
pointerLeave,
|
|
164
|
+
}
|
|
165
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { Ref } from 'vue'
|
|
2
|
+
|
|
3
|
+
export type CellIndex = [row: number, col: number]
|
|
4
|
+
|
|
5
|
+
export interface DragInfo {
|
|
6
|
+
startCoords: [x: number, y: number]
|
|
7
|
+
startIndex: number
|
|
8
|
+
endIndex: number
|
|
9
|
+
type: 'row' | 'col'
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface DragContext {
|
|
13
|
+
preview: HTMLDivElement
|
|
14
|
+
previewRoot: HTMLTableSectionElement
|
|
15
|
+
wrapper: HTMLDivElement
|
|
16
|
+
content: HTMLElement
|
|
17
|
+
contentRoot: HTMLTableSectionElement
|
|
18
|
+
yHandle: HTMLDivElement
|
|
19
|
+
xHandle: HTMLDivElement
|
|
20
|
+
colHandle: HTMLDivElement
|
|
21
|
+
rowHandle: HTMLDivElement
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface Refs {
|
|
25
|
+
dragPreviewRef: Ref<HTMLDivElement | undefined>
|
|
26
|
+
tableWrapperRef: Ref<HTMLDivElement | undefined>
|
|
27
|
+
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
|
+
hoverIndex: Ref<CellIndex>
|
|
33
|
+
lineHoverIndex: Ref<CellIndex>
|
|
34
|
+
dragInfo: Ref<DragInfo | undefined>
|
|
35
|
+
}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import type { Node } from '@jvs-milkdown/prose/model'
|
|
2
|
+
import type { EditorView } from '@jvs-milkdown/prose/view'
|
|
3
|
+
import type { Ref } from 'vue'
|
|
4
|
+
|
|
5
|
+
import { computePosition } from '@floating-ui/dom'
|
|
6
|
+
import { findParent } from '@jvs-milkdown/prose'
|
|
7
|
+
import { CellSelection, findTable } from '@jvs-milkdown/prose/tables'
|
|
8
|
+
|
|
9
|
+
import type { CellIndex, Refs } from './types'
|
|
10
|
+
|
|
11
|
+
function findNodeIndex(parent: Node, child: Node) {
|
|
12
|
+
for (let i = 0; i < parent.childCount; i++) {
|
|
13
|
+
if (parent.child(i) === child) return i
|
|
14
|
+
}
|
|
15
|
+
return -1
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function findPointerIndex(
|
|
19
|
+
event: PointerEvent,
|
|
20
|
+
view?: EditorView
|
|
21
|
+
): CellIndex | undefined {
|
|
22
|
+
if (!view) return
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
const posAtCoords = view.posAtCoords({
|
|
26
|
+
left: event.clientX,
|
|
27
|
+
top: event.clientY,
|
|
28
|
+
})
|
|
29
|
+
if (!posAtCoords) return
|
|
30
|
+
const pos = posAtCoords?.inside
|
|
31
|
+
if (pos == null || pos < 0) return
|
|
32
|
+
|
|
33
|
+
const $pos = view.state.doc.resolve(pos)
|
|
34
|
+
const node = view.state.doc.nodeAt(pos)
|
|
35
|
+
if (!node) return
|
|
36
|
+
|
|
37
|
+
const cellType = ['table_cell', 'table_header']
|
|
38
|
+
const rowType = ['table_row', 'table_header_row']
|
|
39
|
+
|
|
40
|
+
const cell = cellType.includes(node.type.name)
|
|
41
|
+
? node
|
|
42
|
+
: findParent((node) => cellType.includes(node.type.name))($pos)?.node
|
|
43
|
+
const row = findParent((node) => rowType.includes(node.type.name))(
|
|
44
|
+
$pos
|
|
45
|
+
)?.node
|
|
46
|
+
const table = findParent((node) => node.type.name === 'table')($pos)?.node
|
|
47
|
+
if (!cell || !row || !table) return
|
|
48
|
+
|
|
49
|
+
const columnIndex = findNodeIndex(row, cell)
|
|
50
|
+
const rowIndex = findNodeIndex(table, row)
|
|
51
|
+
|
|
52
|
+
return [rowIndex, columnIndex]
|
|
53
|
+
} catch {
|
|
54
|
+
return undefined
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function getRelatedDOM(
|
|
59
|
+
contentWrapperRef: Ref<HTMLElement | undefined>,
|
|
60
|
+
[rowIndex, columnIndex]: CellIndex
|
|
61
|
+
) {
|
|
62
|
+
const content = contentWrapperRef.value
|
|
63
|
+
if (!content) return
|
|
64
|
+
const rows = content.querySelectorAll('tr')
|
|
65
|
+
const row = rows[rowIndex]
|
|
66
|
+
if (!row) return
|
|
67
|
+
|
|
68
|
+
const firstRow = rows[0]
|
|
69
|
+
if (!firstRow) return
|
|
70
|
+
|
|
71
|
+
const headerCol = firstRow.children[columnIndex]
|
|
72
|
+
if (!headerCol) return
|
|
73
|
+
|
|
74
|
+
const col = row.children[columnIndex]
|
|
75
|
+
if (!col) return
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
row,
|
|
79
|
+
col,
|
|
80
|
+
headerCol,
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function recoveryStateBetweenUpdate(
|
|
85
|
+
refs: Refs,
|
|
86
|
+
view?: EditorView,
|
|
87
|
+
node?: Node
|
|
88
|
+
) {
|
|
89
|
+
if (!node) return
|
|
90
|
+
if (!view) return
|
|
91
|
+
const { selection } = view.state
|
|
92
|
+
if (!(selection instanceof CellSelection)) return
|
|
93
|
+
|
|
94
|
+
const { $from } = selection
|
|
95
|
+
const table = findTable($from)
|
|
96
|
+
if (!table || table.node !== node) return
|
|
97
|
+
|
|
98
|
+
if (selection.isColSelection()) {
|
|
99
|
+
const { $head } = selection
|
|
100
|
+
const colIndex = $head.index($head.depth - 1)
|
|
101
|
+
computeColHandlePositionByIndex({
|
|
102
|
+
refs,
|
|
103
|
+
index: [0, colIndex],
|
|
104
|
+
before: (handleDOM) => {
|
|
105
|
+
handleDOM
|
|
106
|
+
.querySelector('.button-group')
|
|
107
|
+
?.setAttribute('data-show', 'true')
|
|
108
|
+
},
|
|
109
|
+
})
|
|
110
|
+
return
|
|
111
|
+
}
|
|
112
|
+
if (selection.isRowSelection()) {
|
|
113
|
+
const { $head } = selection
|
|
114
|
+
const rowNode = findParent(
|
|
115
|
+
(node) =>
|
|
116
|
+
node.type.name === 'table_row' || node.type.name === 'table_header_row'
|
|
117
|
+
)($head)
|
|
118
|
+
if (!rowNode) return
|
|
119
|
+
const rowIndex = findNodeIndex(table.node, rowNode.node)
|
|
120
|
+
computeRowHandlePositionByIndex({
|
|
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
|
+
})
|
|
130
|
+
}
|
|
131
|
+
}
|
|
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
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import type { Ctx } from '@jvs-milkdown/ctx'
|
|
2
|
+
import type { Node } from '@jvs-milkdown/prose/model'
|
|
3
|
+
import type {
|
|
4
|
+
EditorView,
|
|
5
|
+
NodeView,
|
|
6
|
+
NodeViewConstructor,
|
|
7
|
+
ViewMutationRecord,
|
|
8
|
+
} from '@jvs-milkdown/prose/view'
|
|
9
|
+
|
|
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
|
+
import { $view } from '@jvs-milkdown/utils'
|
|
15
|
+
import { createApp, shallowRef, type App, type ShallowRef } from 'vue'
|
|
16
|
+
|
|
17
|
+
import { withMeta } from '../../__internal__/meta'
|
|
18
|
+
import { tableBlockConfig } from '../config'
|
|
19
|
+
import { TableBlock } from './component'
|
|
20
|
+
|
|
21
|
+
export class TableNodeView implements NodeView {
|
|
22
|
+
dom: HTMLElement
|
|
23
|
+
contentDOM: HTMLElement
|
|
24
|
+
app: App
|
|
25
|
+
|
|
26
|
+
nodeRef: ShallowRef<Node>
|
|
27
|
+
|
|
28
|
+
constructor(
|
|
29
|
+
public ctx: Ctx,
|
|
30
|
+
public node: Node,
|
|
31
|
+
public view: EditorView,
|
|
32
|
+
public getPos: () => number | undefined
|
|
33
|
+
) {
|
|
34
|
+
const dom = document.createElement('div')
|
|
35
|
+
dom.className = 'milkdown-table-block'
|
|
36
|
+
|
|
37
|
+
const contentDOM = document.createElement('tbody')
|
|
38
|
+
this.contentDOM = contentDOM
|
|
39
|
+
contentDOM.setAttribute('data-content-dom', 'true')
|
|
40
|
+
contentDOM.classList.add('content-dom')
|
|
41
|
+
this.nodeRef = shallowRef(node)
|
|
42
|
+
|
|
43
|
+
const app = createApp(TableBlock, {
|
|
44
|
+
view,
|
|
45
|
+
ctx,
|
|
46
|
+
getPos,
|
|
47
|
+
config: ctx.get(tableBlockConfig.key),
|
|
48
|
+
onMount: (div: Element) => {
|
|
49
|
+
div.appendChild(contentDOM)
|
|
50
|
+
},
|
|
51
|
+
node: this.nodeRef,
|
|
52
|
+
})
|
|
53
|
+
app.mount(dom)
|
|
54
|
+
this.app = app
|
|
55
|
+
|
|
56
|
+
this.dom = dom
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
update(node: Node) {
|
|
60
|
+
if (node.type !== this.node.type) return false
|
|
61
|
+
|
|
62
|
+
if (node.sameMarkup(this.node) && node.content.eq(this.node.content))
|
|
63
|
+
return false
|
|
64
|
+
|
|
65
|
+
this.node = node
|
|
66
|
+
this.nodeRef.value = node
|
|
67
|
+
|
|
68
|
+
return true
|
|
69
|
+
}
|
|
70
|
+
|
|
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
|
+
stopEvent(e: Event) {
|
|
115
|
+
if (e.type === 'drop' || e.type.startsWith('drag')) return true
|
|
116
|
+
|
|
117
|
+
if (e.type === 'mousedown' || e.type === 'pointerdown') {
|
|
118
|
+
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
|
+
}
|
|
129
|
+
|
|
130
|
+
return false
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
ignoreMutation(mutation: ViewMutationRecord) {
|
|
134
|
+
if (!this.dom || !this.contentDOM) return true
|
|
135
|
+
|
|
136
|
+
if ((mutation.type as unknown) === 'selection') return false
|
|
137
|
+
|
|
138
|
+
if (this.contentDOM === mutation.target && mutation.type === 'attributes')
|
|
139
|
+
return true
|
|
140
|
+
|
|
141
|
+
if (this.contentDOM.contains(mutation.target)) return false
|
|
142
|
+
|
|
143
|
+
return true
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
destroy() {
|
|
147
|
+
this.app.unmount()
|
|
148
|
+
this.dom.remove()
|
|
149
|
+
this.contentDOM.remove()
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export const tableBlockView = $view(
|
|
154
|
+
tableSchema.node,
|
|
155
|
+
(ctx): NodeViewConstructor => {
|
|
156
|
+
return (initialNode, view, getPos) => {
|
|
157
|
+
return new TableNodeView(ctx, initialNode, view, getPos)
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
withMeta(tableBlockView, {
|
|
163
|
+
displayName: 'NodeView<table-block>',
|
|
164
|
+
group: 'TableBlock',
|
|
165
|
+
})
|