@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,101 @@
|
|
|
1
|
+
import type { Ctx, Slice } from '@jvs-milkdown/ctx'
|
|
2
|
+
import type { Mark } from '@jvs-milkdown/prose/model'
|
|
3
|
+
import type { PluginView } from '@jvs-milkdown/prose/state'
|
|
4
|
+
import type { EditorView } from '@jvs-milkdown/prose/view'
|
|
5
|
+
|
|
6
|
+
import { TooltipProvider } from '@jvs-milkdown/plugin-tooltip'
|
|
7
|
+
import { createApp, ref, type App, type Ref } from 'vue'
|
|
8
|
+
|
|
9
|
+
import type { LinkTooltipConfig, LinkToolTipState } from '../slices'
|
|
10
|
+
|
|
11
|
+
import { linkTooltipAPI, linkTooltipConfig, linkTooltipState } from '../slices'
|
|
12
|
+
import { PreviewLink } from './component'
|
|
13
|
+
|
|
14
|
+
export class LinkPreviewTooltip implements PluginView {
|
|
15
|
+
#content: HTMLElement
|
|
16
|
+
#provider: TooltipProvider
|
|
17
|
+
#slice: Slice<LinkToolTipState>
|
|
18
|
+
#config: Ref<LinkTooltipConfig>
|
|
19
|
+
#src = ref('')
|
|
20
|
+
#onEdit = ref(() => {})
|
|
21
|
+
#onRemove = ref(() => {})
|
|
22
|
+
#app: App
|
|
23
|
+
#editorView: EditorView
|
|
24
|
+
|
|
25
|
+
#hovering = false
|
|
26
|
+
|
|
27
|
+
constructor(
|
|
28
|
+
readonly ctx: Ctx,
|
|
29
|
+
view: EditorView
|
|
30
|
+
) {
|
|
31
|
+
this.#editorView = view
|
|
32
|
+
this.#config = ref(this.ctx.get(linkTooltipConfig.key))
|
|
33
|
+
this.#app = createApp(PreviewLink, {
|
|
34
|
+
config: this.#config,
|
|
35
|
+
src: this.#src,
|
|
36
|
+
onEdit: this.#onEdit,
|
|
37
|
+
onRemove: this.#onRemove,
|
|
38
|
+
})
|
|
39
|
+
this.#content = document.createElement('div')
|
|
40
|
+
this.#content.className = 'milkdown-link-preview'
|
|
41
|
+
this.#app.mount(this.#content)
|
|
42
|
+
|
|
43
|
+
this.#provider = new TooltipProvider({
|
|
44
|
+
debounce: 0,
|
|
45
|
+
content: this.#content,
|
|
46
|
+
shouldShow: () => false,
|
|
47
|
+
})
|
|
48
|
+
this.#provider.update(view)
|
|
49
|
+
this.#slice = ctx.use(linkTooltipState.key)
|
|
50
|
+
this.#slice.on(this.#onStateChange)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
#onStateChange = ({ mode }: LinkToolTipState) => {
|
|
54
|
+
if (mode === 'edit') this.#hide()
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
#onMouseEnter = () => {
|
|
58
|
+
this.#hovering = true
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
#onMouseLeave = () => {
|
|
62
|
+
this.#hovering = false
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
#hide = () => {
|
|
66
|
+
this.#provider.hide()
|
|
67
|
+
this.#provider.element.removeEventListener('mouseenter', this.#onMouseEnter)
|
|
68
|
+
this.#provider.element.removeEventListener('mouseleave', this.#onMouseLeave)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
show = (mark: Mark, from: number, to: number, rect: DOMRect) => {
|
|
72
|
+
this.#config.value = this.ctx.get(linkTooltipConfig.key)
|
|
73
|
+
this.#src.value = mark.attrs.href
|
|
74
|
+
this.#onEdit.value = () => {
|
|
75
|
+
this.ctx.get(linkTooltipAPI.key).editLink(mark, from, to)
|
|
76
|
+
}
|
|
77
|
+
this.#onRemove.value = () => {
|
|
78
|
+
this.ctx.get(linkTooltipAPI.key).removeLink(from, to)
|
|
79
|
+
this.#hide()
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
this.#provider.show({ getBoundingClientRect: () => rect }, this.#editorView)
|
|
83
|
+
this.#provider.element.addEventListener('mouseenter', this.#onMouseEnter)
|
|
84
|
+
this.#provider.element.addEventListener('mouseleave', this.#onMouseLeave)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
hide = () => {
|
|
88
|
+
if (this.#hovering) return
|
|
89
|
+
|
|
90
|
+
this.#hide()
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
update = () => {}
|
|
94
|
+
|
|
95
|
+
destroy = () => {
|
|
96
|
+
this.#app.unmount()
|
|
97
|
+
this.#slice.off(this.#onStateChange)
|
|
98
|
+
this.#provider.destroy()
|
|
99
|
+
this.#content.remove()
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { Mark } from '@jvs-milkdown/prose/model'
|
|
2
|
+
|
|
3
|
+
import { $ctx } from '@jvs-milkdown/utils'
|
|
4
|
+
|
|
5
|
+
import { withMeta } from '../__internal__/meta'
|
|
6
|
+
|
|
7
|
+
export interface LinkToolTipState {
|
|
8
|
+
mode: 'preview' | 'edit'
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const defaultState: LinkToolTipState = {
|
|
12
|
+
mode: 'preview',
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const linkTooltipState = $ctx({ ...defaultState }, 'linkTooltipStateCtx')
|
|
16
|
+
|
|
17
|
+
withMeta(linkTooltipState, {
|
|
18
|
+
displayName: 'State<link-tooltip>',
|
|
19
|
+
group: 'LinkTooltip',
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
export interface LinkTooltipAPI {
|
|
23
|
+
addLink: (from: number, to: number) => void
|
|
24
|
+
editLink: (mark: Mark, from: number, to: number) => void
|
|
25
|
+
removeLink: (from: number, to: number) => void
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const defaultAPI: LinkTooltipAPI = {
|
|
29
|
+
addLink: () => {},
|
|
30
|
+
editLink: () => {},
|
|
31
|
+
removeLink: () => {},
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const linkTooltipAPI = $ctx({ ...defaultAPI }, 'linkTooltipAPICtx')
|
|
35
|
+
|
|
36
|
+
withMeta(linkTooltipState, {
|
|
37
|
+
displayName: 'API<link-tooltip>',
|
|
38
|
+
group: 'LinkTooltip',
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
export interface LinkTooltipConfig {
|
|
42
|
+
linkIcon: string
|
|
43
|
+
editButton: string
|
|
44
|
+
confirmButton: string
|
|
45
|
+
removeButton: string
|
|
46
|
+
onCopyLink: (link: string) => void
|
|
47
|
+
inputPlaceholder: string
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const defaultConfig: LinkTooltipConfig = {
|
|
51
|
+
linkIcon: '🔗',
|
|
52
|
+
editButton: '✎',
|
|
53
|
+
removeButton: '⌫',
|
|
54
|
+
confirmButton: 'Confirm ⏎',
|
|
55
|
+
onCopyLink: () => {},
|
|
56
|
+
inputPlaceholder: 'Paste link...',
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export const linkTooltipConfig = $ctx(
|
|
60
|
+
{
|
|
61
|
+
...defaultConfig,
|
|
62
|
+
},
|
|
63
|
+
'linkTooltipConfigCtx'
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
withMeta(linkTooltipState, {
|
|
67
|
+
displayName: 'Config<link-tooltip>',
|
|
68
|
+
group: 'LinkTooltip',
|
|
69
|
+
})
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { tooltipFactory } from '@jvs-milkdown/plugin-tooltip'
|
|
2
|
+
|
|
3
|
+
import { withMeta } from '../__internal__/meta'
|
|
4
|
+
|
|
5
|
+
export const linkPreviewTooltip = tooltipFactory('LINK_PREVIEW')
|
|
6
|
+
withMeta(linkPreviewTooltip[0], {
|
|
7
|
+
displayName: 'PreviewTooltipSpec<link-tooltip>',
|
|
8
|
+
group: 'LinkTooltip',
|
|
9
|
+
})
|
|
10
|
+
withMeta(linkPreviewTooltip[1], {
|
|
11
|
+
displayName: 'PreviewTooltipPlugin<link-tooltip>',
|
|
12
|
+
group: 'LinkTooltip',
|
|
13
|
+
})
|
|
14
|
+
export const linkEditTooltip = tooltipFactory('LINK_EDIT')
|
|
15
|
+
withMeta(linkEditTooltip[0], {
|
|
16
|
+
displayName: 'EditTooltipSpec<link-tooltip>',
|
|
17
|
+
group: 'LinkTooltip',
|
|
18
|
+
})
|
|
19
|
+
withMeta(linkEditTooltip[1], {
|
|
20
|
+
displayName: 'EditTooltipPlugin<link-tooltip>',
|
|
21
|
+
group: 'LinkTooltip',
|
|
22
|
+
})
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { Ctx } from '@jvs-milkdown/ctx'
|
|
2
|
+
import type { Mark, Node } from '@jvs-milkdown/prose/model'
|
|
3
|
+
import type { EditorView } from '@jvs-milkdown/prose/view'
|
|
4
|
+
|
|
5
|
+
import { linkSchema } from '@jvs-milkdown/preset-commonmark'
|
|
6
|
+
|
|
7
|
+
import { linkPreviewTooltip } from './tooltips'
|
|
8
|
+
|
|
9
|
+
export function findMarkPosition(
|
|
10
|
+
mark: Mark,
|
|
11
|
+
node: Node,
|
|
12
|
+
doc: Node,
|
|
13
|
+
from: number,
|
|
14
|
+
to: number
|
|
15
|
+
) {
|
|
16
|
+
let markPos = { start: -1, end: -1 }
|
|
17
|
+
doc.nodesBetween(from, to, (n, pos) => {
|
|
18
|
+
// stop recursive finding if result is found
|
|
19
|
+
if (markPos.start > -1) return false
|
|
20
|
+
|
|
21
|
+
if (markPos.start === -1 && mark.isInSet(n.marks) && node === n) {
|
|
22
|
+
markPos = {
|
|
23
|
+
start: pos,
|
|
24
|
+
end: pos + Math.max(n.textContent.length, 1),
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return undefined
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
return markPos
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function shouldShowPreviewWhenHover(
|
|
35
|
+
ctx: Ctx,
|
|
36
|
+
view: EditorView,
|
|
37
|
+
event: MouseEvent
|
|
38
|
+
) {
|
|
39
|
+
const $pos = view.posAtCoords({ left: event.clientX, top: event.clientY })
|
|
40
|
+
if (!$pos) return
|
|
41
|
+
|
|
42
|
+
const { pos } = $pos
|
|
43
|
+
const node = view.state.doc.nodeAt(pos)
|
|
44
|
+
|
|
45
|
+
if (!node) return
|
|
46
|
+
|
|
47
|
+
const mark = node.marks.find(
|
|
48
|
+
(mark) => mark.type === linkSchema.mark.type(ctx)
|
|
49
|
+
)
|
|
50
|
+
if (!mark) return
|
|
51
|
+
|
|
52
|
+
const key = linkPreviewTooltip.pluginKey()
|
|
53
|
+
if (!key) return
|
|
54
|
+
|
|
55
|
+
return { show: true, pos, node, mark }
|
|
56
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import clsx from 'clsx'
|
|
2
|
+
import { computed, defineComponent, type Ref, h, type VNodeRef } from 'vue'
|
|
3
|
+
|
|
4
|
+
import type { ListItemBlockConfig } from './config'
|
|
5
|
+
|
|
6
|
+
import { Icon } from '../__internal__/components/icon'
|
|
7
|
+
import { keepAlive } from '../__internal__/keep-alive'
|
|
8
|
+
|
|
9
|
+
keepAlive(h)
|
|
10
|
+
|
|
11
|
+
interface Attrs {
|
|
12
|
+
label: string
|
|
13
|
+
checked: boolean
|
|
14
|
+
listType: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
type ListItemProps = {
|
|
18
|
+
[P in keyof Attrs]: Ref<Attrs[P]>
|
|
19
|
+
} & {
|
|
20
|
+
config: ListItemBlockConfig
|
|
21
|
+
readonly: Ref<boolean>
|
|
22
|
+
selected: Ref<boolean>
|
|
23
|
+
setAttr: <T extends keyof Attrs>(attr: T, value: Attrs[T]) => void
|
|
24
|
+
onMount: (div: Element) => void
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const ListItem = defineComponent<ListItemProps>({
|
|
28
|
+
props: {
|
|
29
|
+
label: {
|
|
30
|
+
type: Object,
|
|
31
|
+
required: true,
|
|
32
|
+
},
|
|
33
|
+
checked: {
|
|
34
|
+
type: Object,
|
|
35
|
+
required: true,
|
|
36
|
+
},
|
|
37
|
+
listType: {
|
|
38
|
+
type: Object,
|
|
39
|
+
required: true,
|
|
40
|
+
},
|
|
41
|
+
config: {
|
|
42
|
+
type: Object,
|
|
43
|
+
required: true,
|
|
44
|
+
},
|
|
45
|
+
readonly: {
|
|
46
|
+
type: Object,
|
|
47
|
+
required: true,
|
|
48
|
+
},
|
|
49
|
+
selected: {
|
|
50
|
+
type: Object,
|
|
51
|
+
required: true,
|
|
52
|
+
},
|
|
53
|
+
setAttr: {
|
|
54
|
+
type: Function,
|
|
55
|
+
required: true,
|
|
56
|
+
},
|
|
57
|
+
onMount: {
|
|
58
|
+
type: Function,
|
|
59
|
+
required: true,
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
setup({
|
|
63
|
+
label,
|
|
64
|
+
checked,
|
|
65
|
+
listType,
|
|
66
|
+
config,
|
|
67
|
+
readonly,
|
|
68
|
+
setAttr,
|
|
69
|
+
onMount,
|
|
70
|
+
selected,
|
|
71
|
+
}) {
|
|
72
|
+
const contentWrapperRef: VNodeRef = (div) => {
|
|
73
|
+
if (div == null) return
|
|
74
|
+
if (div instanceof Element) {
|
|
75
|
+
onMount(div)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const onClickLabel = (e: Event) => {
|
|
80
|
+
e.stopPropagation()
|
|
81
|
+
e.preventDefault()
|
|
82
|
+
|
|
83
|
+
if (checked.value == null) return
|
|
84
|
+
setAttr('checked', !checked.value)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const icon = computed(() => {
|
|
88
|
+
return config.renderLabel({
|
|
89
|
+
label: label.value,
|
|
90
|
+
listType: listType.value,
|
|
91
|
+
checked: checked.value,
|
|
92
|
+
readonly: readonly.value,
|
|
93
|
+
})
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
const labelClass = computed(() => {
|
|
97
|
+
if (checked.value == null) {
|
|
98
|
+
if (listType.value === 'bullet') return 'bullet'
|
|
99
|
+
return 'ordered'
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (checked.value) return 'checked'
|
|
103
|
+
return 'unchecked'
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
return () => {
|
|
107
|
+
return (
|
|
108
|
+
<li
|
|
109
|
+
class={clsx(
|
|
110
|
+
'list-item',
|
|
111
|
+
selected.value && 'ProseMirror-selectednode'
|
|
112
|
+
)}
|
|
113
|
+
>
|
|
114
|
+
<div
|
|
115
|
+
class="label-wrapper"
|
|
116
|
+
onPointerdown={onClickLabel}
|
|
117
|
+
contenteditable={false}
|
|
118
|
+
>
|
|
119
|
+
<Icon
|
|
120
|
+
class={clsx(
|
|
121
|
+
'label',
|
|
122
|
+
readonly.value && 'readonly',
|
|
123
|
+
labelClass.value
|
|
124
|
+
)}
|
|
125
|
+
icon={icon.value}
|
|
126
|
+
/>
|
|
127
|
+
</div>
|
|
128
|
+
<div class="children" ref={contentWrapperRef} />
|
|
129
|
+
</li>
|
|
130
|
+
)
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
})
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { $ctx } from '@jvs-milkdown/utils'
|
|
2
|
+
|
|
3
|
+
import { withMeta } from '../__internal__/meta'
|
|
4
|
+
|
|
5
|
+
interface RenderLabelProps {
|
|
6
|
+
label: string
|
|
7
|
+
listType: string
|
|
8
|
+
readonly?: boolean
|
|
9
|
+
checked?: boolean
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface ListItemBlockConfig {
|
|
13
|
+
renderLabel: (props: RenderLabelProps) => string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const defaultListItemBlockConfig: ListItemBlockConfig = {
|
|
17
|
+
renderLabel: ({ label, listType, checked }: RenderLabelProps) => {
|
|
18
|
+
const content =
|
|
19
|
+
checked == null
|
|
20
|
+
? listType === 'bullet'
|
|
21
|
+
? '⦿'
|
|
22
|
+
: label
|
|
23
|
+
: checked
|
|
24
|
+
? '☑'
|
|
25
|
+
: '□'
|
|
26
|
+
|
|
27
|
+
return content
|
|
28
|
+
},
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export const listItemBlockConfig = $ctx(
|
|
32
|
+
defaultListItemBlockConfig,
|
|
33
|
+
'listItemBlockConfigCtx'
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
withMeta(listItemBlockConfig, {
|
|
37
|
+
displayName: 'Config<list-item-block>',
|
|
38
|
+
group: 'ListItemBlock',
|
|
39
|
+
})
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { MilkdownPlugin } from '@jvs-milkdown/ctx'
|
|
2
|
+
|
|
3
|
+
import { listItemBlockConfig } from './config'
|
|
4
|
+
import { listItemBlockView } from './view'
|
|
5
|
+
|
|
6
|
+
export * from './component'
|
|
7
|
+
export * from './config'
|
|
8
|
+
export * from './view'
|
|
9
|
+
|
|
10
|
+
export const listItemBlockComponent: MilkdownPlugin[] = [
|
|
11
|
+
listItemBlockConfig,
|
|
12
|
+
listItemBlockView,
|
|
13
|
+
]
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import type { Node } from '@jvs-milkdown/prose/model'
|
|
2
|
+
import type { NodeViewConstructor } from '@jvs-milkdown/prose/view'
|
|
3
|
+
|
|
4
|
+
import { listItemSchema } from '@jvs-milkdown/preset-commonmark'
|
|
5
|
+
import { TextSelection } from '@jvs-milkdown/prose/state'
|
|
6
|
+
import { $view } from '@jvs-milkdown/utils'
|
|
7
|
+
import { createApp, ref, watchEffect } from 'vue'
|
|
8
|
+
|
|
9
|
+
import { withMeta } from '../__internal__/meta'
|
|
10
|
+
import { ListItem } from './component'
|
|
11
|
+
import { listItemBlockConfig } from './config'
|
|
12
|
+
|
|
13
|
+
export const listItemBlockView = $view(
|
|
14
|
+
listItemSchema.node,
|
|
15
|
+
(ctx): NodeViewConstructor => {
|
|
16
|
+
return (initialNode, view, getPos) => {
|
|
17
|
+
const dom = document.createElement('div')
|
|
18
|
+
dom.className = 'milkdown-list-item-block'
|
|
19
|
+
|
|
20
|
+
const contentDOM = document.createElement('div')
|
|
21
|
+
contentDOM.setAttribute('data-content-dom', 'true')
|
|
22
|
+
contentDOM.classList.add('content-dom')
|
|
23
|
+
|
|
24
|
+
const label = ref(initialNode.attrs.label)
|
|
25
|
+
const checked = ref(initialNode.attrs.checked)
|
|
26
|
+
const listType = ref(initialNode.attrs.listType)
|
|
27
|
+
const readonly = ref(!view.editable)
|
|
28
|
+
const config = ctx.get(listItemBlockConfig.key)
|
|
29
|
+
const selected = ref(false)
|
|
30
|
+
const setAttr = (attr: string, value: unknown) => {
|
|
31
|
+
if (!view.editable) return
|
|
32
|
+
const pos = getPos()
|
|
33
|
+
if (pos == null) return
|
|
34
|
+
|
|
35
|
+
if (!view.hasFocus()) view.focus()
|
|
36
|
+
|
|
37
|
+
view.dispatch(view.state.tr.setNodeAttribute(pos, attr, value))
|
|
38
|
+
}
|
|
39
|
+
const disposeSelectedWatcher = watchEffect(() => {
|
|
40
|
+
const isSelected = selected.value
|
|
41
|
+
if (isSelected) {
|
|
42
|
+
dom.classList.add('selected')
|
|
43
|
+
} else {
|
|
44
|
+
dom.classList.remove('selected')
|
|
45
|
+
}
|
|
46
|
+
})
|
|
47
|
+
let raf = 0
|
|
48
|
+
const onMount = (div: HTMLElement) => {
|
|
49
|
+
const { anchor, head } = view.state.selection
|
|
50
|
+
div.appendChild(contentDOM)
|
|
51
|
+
// put the cursor to the new created list item
|
|
52
|
+
const anchorPos = view.state.doc.resolve(anchor)
|
|
53
|
+
const headPos = view.state.doc.resolve(head)
|
|
54
|
+
raf = requestAnimationFrame(() => {
|
|
55
|
+
cancelAnimationFrame(raf)
|
|
56
|
+
if (!anchorPos.doc.eq(view.state.doc)) return
|
|
57
|
+
const selection = new TextSelection(anchorPos, headPos)
|
|
58
|
+
view.dispatch(view.state.tr.setSelection(selection))
|
|
59
|
+
})
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const app = createApp(ListItem, {
|
|
63
|
+
label,
|
|
64
|
+
checked,
|
|
65
|
+
listType,
|
|
66
|
+
readonly,
|
|
67
|
+
config,
|
|
68
|
+
selected,
|
|
69
|
+
setAttr,
|
|
70
|
+
onMount,
|
|
71
|
+
})
|
|
72
|
+
app.mount(dom)
|
|
73
|
+
const bindAttrs = (node: Node) => {
|
|
74
|
+
listType.value = node.attrs.listType
|
|
75
|
+
label.value = node.attrs.label
|
|
76
|
+
checked.value = node.attrs.checked
|
|
77
|
+
readonly.value = !view.editable
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
bindAttrs(initialNode)
|
|
81
|
+
let node = initialNode
|
|
82
|
+
return {
|
|
83
|
+
dom,
|
|
84
|
+
contentDOM,
|
|
85
|
+
update: (updatedNode) => {
|
|
86
|
+
if (updatedNode.type !== initialNode.type) return false
|
|
87
|
+
|
|
88
|
+
if (
|
|
89
|
+
updatedNode.sameMarkup(node) &&
|
|
90
|
+
updatedNode.content.eq(node.content)
|
|
91
|
+
)
|
|
92
|
+
return true
|
|
93
|
+
|
|
94
|
+
node = updatedNode
|
|
95
|
+
bindAttrs(updatedNode)
|
|
96
|
+
return true
|
|
97
|
+
},
|
|
98
|
+
ignoreMutation: (mutation) => {
|
|
99
|
+
if (!dom || !contentDOM) return true
|
|
100
|
+
|
|
101
|
+
if ((mutation.type as unknown) === 'selection') return false
|
|
102
|
+
|
|
103
|
+
if (contentDOM === mutation.target && mutation.type === 'attributes')
|
|
104
|
+
return true
|
|
105
|
+
|
|
106
|
+
if (contentDOM.contains(mutation.target)) return false
|
|
107
|
+
|
|
108
|
+
return true
|
|
109
|
+
},
|
|
110
|
+
selectNode: () => {
|
|
111
|
+
selected.value = true
|
|
112
|
+
},
|
|
113
|
+
deselectNode: () => {
|
|
114
|
+
selected.value = false
|
|
115
|
+
},
|
|
116
|
+
destroy: () => {
|
|
117
|
+
disposeSelectedWatcher()
|
|
118
|
+
app.unmount()
|
|
119
|
+
dom.remove()
|
|
120
|
+
contentDOM.remove()
|
|
121
|
+
},
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
withMeta(listItemBlockView, {
|
|
128
|
+
displayName: 'NodeView<list-item-block>',
|
|
129
|
+
group: 'ListItemBlock',
|
|
130
|
+
})
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { $ctx } from '@jvs-milkdown/utils'
|
|
2
|
+
|
|
3
|
+
import { withMeta } from '../__internal__/meta'
|
|
4
|
+
|
|
5
|
+
export type RenderType =
|
|
6
|
+
| 'add_row'
|
|
7
|
+
| 'add_col'
|
|
8
|
+
| 'delete_row'
|
|
9
|
+
| 'delete_col'
|
|
10
|
+
| 'align_col_left'
|
|
11
|
+
| 'align_col_center'
|
|
12
|
+
| 'align_col_right'
|
|
13
|
+
| 'col_drag_handle'
|
|
14
|
+
| 'row_drag_handle'
|
|
15
|
+
|
|
16
|
+
export interface TableBlockConfig {
|
|
17
|
+
renderButton: (renderType: RenderType) => string
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const defaultTableBlockConfig: TableBlockConfig = {
|
|
21
|
+
renderButton: (renderType) => {
|
|
22
|
+
switch (renderType) {
|
|
23
|
+
case 'add_row':
|
|
24
|
+
return '+'
|
|
25
|
+
case 'add_col':
|
|
26
|
+
return '+'
|
|
27
|
+
case 'delete_row':
|
|
28
|
+
return '-'
|
|
29
|
+
case 'delete_col':
|
|
30
|
+
return '-'
|
|
31
|
+
case 'align_col_left':
|
|
32
|
+
return 'left'
|
|
33
|
+
case 'align_col_center':
|
|
34
|
+
return 'center'
|
|
35
|
+
case 'align_col_right':
|
|
36
|
+
return 'right'
|
|
37
|
+
case 'col_drag_handle':
|
|
38
|
+
return '='
|
|
39
|
+
case 'row_drag_handle':
|
|
40
|
+
return '='
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export const tableBlockConfig = $ctx(
|
|
46
|
+
{ ...defaultTableBlockConfig },
|
|
47
|
+
'tableBlockConfigCtx'
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
withMeta(tableBlockConfig, {
|
|
51
|
+
displayName: 'Config<table-block>',
|
|
52
|
+
group: 'TableBlock',
|
|
53
|
+
})
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
function findDragOverElement(
|
|
2
|
+
elements: Element[],
|
|
3
|
+
pointer: number,
|
|
4
|
+
axis: 'x' | 'y'
|
|
5
|
+
): [Element, number] | undefined {
|
|
6
|
+
const startProp = axis === 'x' ? 'left' : 'top'
|
|
7
|
+
const endProp = axis === 'x' ? 'right' : 'bottom'
|
|
8
|
+
const lastIndex = elements.length - 1
|
|
9
|
+
|
|
10
|
+
const index = elements.findIndex((el, index) => {
|
|
11
|
+
const rect = el.getBoundingClientRect()
|
|
12
|
+
const boundaryStart = rect[startProp]
|
|
13
|
+
const boundaryEnd = rect[endProp]
|
|
14
|
+
|
|
15
|
+
// The pointer is within the boundary of the current element.
|
|
16
|
+
if (boundaryStart <= pointer && pointer <= boundaryEnd) return true
|
|
17
|
+
// The pointer is beyond the last element.
|
|
18
|
+
if (index === lastIndex && pointer > boundaryEnd) return true
|
|
19
|
+
// The pointer is before the first element.
|
|
20
|
+
if (index === 0 && pointer < boundaryStart) return true
|
|
21
|
+
|
|
22
|
+
return false
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
const element = elements[index]
|
|
26
|
+
|
|
27
|
+
return element ? [element, index] : undefined
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function getDragOverColumn(
|
|
31
|
+
table: Element,
|
|
32
|
+
pointerX: number
|
|
33
|
+
): [element: Element, index: number] | undefined {
|
|
34
|
+
const firstRow = table.querySelector('tr')
|
|
35
|
+
if (!firstRow) return
|
|
36
|
+
const cells = Array.from(firstRow.children)
|
|
37
|
+
return findDragOverElement(cells, pointerX, 'x')
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function getDragOverRow(
|
|
41
|
+
table: Element,
|
|
42
|
+
pointerY: number
|
|
43
|
+
): [element: Element, index: number] | undefined {
|
|
44
|
+
const rows = Array.from(table.querySelectorAll('tr'))
|
|
45
|
+
return findDragOverElement(rows, pointerY, 'y')
|
|
46
|
+
}
|