@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.
Files changed (192) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +11 -0
  3. package/lib/__internal__/components/icon.d.ts +24 -0
  4. package/lib/__internal__/components/icon.d.ts.map +1 -0
  5. package/lib/__internal__/components/image-input.d.ts +17 -0
  6. package/lib/__internal__/components/image-input.d.ts.map +1 -0
  7. package/lib/__internal__/keep-alive.d.ts +2 -0
  8. package/lib/__internal__/keep-alive.d.ts.map +1 -0
  9. package/lib/__internal__/meta.d.ts +3 -0
  10. package/lib/__internal__/meta.d.ts.map +1 -0
  11. package/lib/__tests__/setup.d.ts +2 -0
  12. package/lib/__tests__/setup.d.ts.map +1 -0
  13. package/lib/code-block/config.d.ts +23 -0
  14. package/lib/code-block/config.d.ts.map +1 -0
  15. package/lib/code-block/index.d.ts +5 -0
  16. package/lib/code-block/index.d.ts.map +1 -0
  17. package/lib/code-block/index.js +4160 -0
  18. package/lib/code-block/index.js.map +1 -0
  19. package/lib/code-block/view/components/code-block.d.ts +16 -0
  20. package/lib/code-block/view/components/code-block.d.ts.map +1 -0
  21. package/lib/code-block/view/components/copy-button.d.ts +9 -0
  22. package/lib/code-block/view/components/copy-button.d.ts.map +1 -0
  23. package/lib/code-block/view/components/language-picker.d.ts +5 -0
  24. package/lib/code-block/view/components/language-picker.d.ts.map +1 -0
  25. package/lib/code-block/view/components/preview-panel.d.ts +9 -0
  26. package/lib/code-block/view/components/preview-panel.d.ts.map +1 -0
  27. package/lib/code-block/view/index.d.ts +3 -0
  28. package/lib/code-block/view/index.d.ts.map +1 -0
  29. package/lib/code-block/view/loader.d.ts +13 -0
  30. package/lib/code-block/view/loader.d.ts.map +1 -0
  31. package/lib/code-block/view/node-view.d.ts +40 -0
  32. package/lib/code-block/view/node-view.d.ts.map +1 -0
  33. package/lib/image-block/config.d.ts +16 -0
  34. package/lib/image-block/config.d.ts.map +1 -0
  35. package/lib/image-block/index.d.ts +7 -0
  36. package/lib/image-block/index.d.ts.map +1 -0
  37. package/lib/image-block/index.js +660 -0
  38. package/lib/image-block/index.js.map +1 -0
  39. package/lib/image-block/remark-plugin.d.ts +2 -0
  40. package/lib/image-block/remark-plugin.d.ts.map +1 -0
  41. package/lib/image-block/schema.d.ts +3 -0
  42. package/lib/image-block/schema.d.ts.map +1 -0
  43. package/lib/image-block/view/components/__tests__/image-viewer.onImageLoadError.spec.d.ts +2 -0
  44. package/lib/image-block/view/components/__tests__/image-viewer.onImageLoadError.spec.d.ts.map +1 -0
  45. package/lib/image-block/view/components/image-block.d.ts +18 -0
  46. package/lib/image-block/view/components/image-block.d.ts.map +1 -0
  47. package/lib/image-block/view/components/image-viewer.d.ts +3 -0
  48. package/lib/image-block/view/components/image-viewer.d.ts.map +1 -0
  49. package/lib/image-block/view/index.d.ts +3 -0
  50. package/lib/image-block/view/index.d.ts.map +1 -0
  51. package/lib/image-inline/components/image-inline.d.ts +18 -0
  52. package/lib/image-inline/components/image-inline.d.ts.map +1 -0
  53. package/lib/image-inline/config.d.ts +11 -0
  54. package/lib/image-inline/config.d.ts.map +1 -0
  55. package/lib/image-inline/index.d.ts +5 -0
  56. package/lib/image-inline/index.d.ts.map +1 -0
  57. package/lib/image-inline/index.js +377 -0
  58. package/lib/image-inline/index.js.map +1 -0
  59. package/lib/image-inline/view.d.ts +3 -0
  60. package/lib/image-inline/view.d.ts.map +1 -0
  61. package/lib/index.d.ts +2 -0
  62. package/lib/index.d.ts.map +1 -0
  63. package/lib/index.js +35 -0
  64. package/lib/index.js.map +1 -0
  65. package/lib/link-tooltip/command.d.ts +2 -0
  66. package/lib/link-tooltip/command.d.ts.map +1 -0
  67. package/lib/link-tooltip/configure.d.ts +3 -0
  68. package/lib/link-tooltip/configure.d.ts.map +1 -0
  69. package/lib/link-tooltip/edit/component.d.ts +11 -0
  70. package/lib/link-tooltip/edit/component.d.ts.map +1 -0
  71. package/lib/link-tooltip/edit/edit-configure.d.ts +3 -0
  72. package/lib/link-tooltip/edit/edit-configure.d.ts.map +1 -0
  73. package/lib/link-tooltip/edit/edit-view.d.ts +15 -0
  74. package/lib/link-tooltip/edit/edit-view.d.ts.map +1 -0
  75. package/lib/link-tooltip/index.d.ts +7 -0
  76. package/lib/link-tooltip/index.d.ts.map +1 -0
  77. package/lib/link-tooltip/index.js +2526 -0
  78. package/lib/link-tooltip/index.js.map +1 -0
  79. package/lib/link-tooltip/preview/component.d.ts +11 -0
  80. package/lib/link-tooltip/preview/component.d.ts.map +1 -0
  81. package/lib/link-tooltip/preview/preview-configure.d.ts +3 -0
  82. package/lib/link-tooltip/preview/preview-configure.d.ts.map +1 -0
  83. package/lib/link-tooltip/preview/preview-view.d.ts +14 -0
  84. package/lib/link-tooltip/preview/preview-view.d.ts.map +1 -0
  85. package/lib/link-tooltip/slices.d.ts +34 -0
  86. package/lib/link-tooltip/slices.d.ts.map +1 -0
  87. package/lib/link-tooltip/tooltips.d.ts +3 -0
  88. package/lib/link-tooltip/tooltips.d.ts.map +1 -0
  89. package/lib/link-tooltip/utils.d.ts +14 -0
  90. package/lib/link-tooltip/utils.d.ts.map +1 -0
  91. package/lib/list-item-block/component.d.ts +19 -0
  92. package/lib/list-item-block/component.d.ts.map +1 -0
  93. package/lib/list-item-block/config.d.ts +13 -0
  94. package/lib/list-item-block/config.d.ts.map +1 -0
  95. package/lib/list-item-block/index.d.ts +6 -0
  96. package/lib/list-item-block/index.d.ts.map +1 -0
  97. package/lib/list-item-block/index.js +2149 -0
  98. package/lib/list-item-block/index.js.map +1 -0
  99. package/lib/list-item-block/view.d.ts +3 -0
  100. package/lib/list-item-block/view.d.ts.map +1 -0
  101. package/lib/table-block/config.d.ts +8 -0
  102. package/lib/table-block/config.d.ts.map +1 -0
  103. package/lib/table-block/dnd/calc-drag-over.d.ts +3 -0
  104. package/lib/table-block/dnd/calc-drag-over.d.ts.map +1 -0
  105. package/lib/table-block/dnd/create-drag-handler.d.ts +5 -0
  106. package/lib/table-block/dnd/create-drag-handler.d.ts.map +1 -0
  107. package/lib/table-block/dnd/drag-over-handler.d.ts +3 -0
  108. package/lib/table-block/dnd/drag-over-handler.d.ts.map +1 -0
  109. package/lib/table-block/dnd/prepare-dnd-context.d.ts +3 -0
  110. package/lib/table-block/dnd/prepare-dnd-context.d.ts.map +1 -0
  111. package/lib/table-block/dnd/preview.d.ts +3 -0
  112. package/lib/table-block/dnd/preview.d.ts.map +1 -0
  113. package/lib/table-block/index.d.ts +5 -0
  114. package/lib/table-block/index.d.ts.map +1 -0
  115. package/lib/table-block/index.js +3961 -0
  116. package/lib/table-block/index.js.map +1 -0
  117. package/lib/table-block/view/component.d.ts +16 -0
  118. package/lib/table-block/view/component.d.ts.map +1 -0
  119. package/lib/table-block/view/drag.d.ts +7 -0
  120. package/lib/table-block/view/drag.d.ts.map +1 -0
  121. package/lib/table-block/view/index.d.ts +2 -0
  122. package/lib/table-block/view/index.d.ts.map +1 -0
  123. package/lib/table-block/view/operation.d.ts +11 -0
  124. package/lib/table-block/view/operation.d.ts.map +1 -0
  125. package/lib/table-block/view/pointer.d.ts +7 -0
  126. package/lib/table-block/view/pointer.d.ts.map +1 -0
  127. package/lib/table-block/view/types.d.ts +32 -0
  128. package/lib/table-block/view/types.d.ts.map +1 -0
  129. package/lib/table-block/view/utils.d.ts +21 -0
  130. package/lib/table-block/view/utils.d.ts.map +1 -0
  131. package/lib/table-block/view/view.d.ts +22 -0
  132. package/lib/table-block/view/view.d.ts.map +1 -0
  133. package/lib/tsconfig.tsbuildinfo +1 -0
  134. package/package.json +110 -0
  135. package/src/__internal__/components/icon.tsx +38 -0
  136. package/src/__internal__/components/image-input.tsx +182 -0
  137. package/src/__internal__/keep-alive.ts +3 -0
  138. package/src/__internal__/meta.ts +15 -0
  139. package/src/__tests__/setup.ts +6 -0
  140. package/src/code-block/config.ts +54 -0
  141. package/src/code-block/index.ts +12 -0
  142. package/src/code-block/view/components/code-block.tsx +170 -0
  143. package/src/code-block/view/components/copy-button.tsx +96 -0
  144. package/src/code-block/view/components/language-picker.tsx +239 -0
  145. package/src/code-block/view/components/preview-panel.tsx +79 -0
  146. package/src/code-block/view/index.ts +24 -0
  147. package/src/code-block/view/loader.ts +40 -0
  148. package/src/code-block/view/node-view.ts +310 -0
  149. package/src/image-block/config.ts +37 -0
  150. package/src/image-block/index.ts +18 -0
  151. package/src/image-block/remark-plugin.ts +51 -0
  152. package/src/image-block/schema.ts +71 -0
  153. package/src/image-block/view/components/__tests__/image-viewer.onImageLoadError.spec.tsx +42 -0
  154. package/src/image-block/view/components/image-block.tsx +80 -0
  155. package/src/image-block/view/components/image-viewer.tsx +186 -0
  156. package/src/image-block/view/index.ts +111 -0
  157. package/src/image-inline/components/image-inline.tsx +85 -0
  158. package/src/image-inline/config.ts +30 -0
  159. package/src/image-inline/index.ts +12 -0
  160. package/src/image-inline/view.ts +109 -0
  161. package/src/index.ts +1 -0
  162. package/src/link-tooltip/command.ts +19 -0
  163. package/src/link-tooltip/configure.ts +9 -0
  164. package/src/link-tooltip/edit/component.tsx +82 -0
  165. package/src/link-tooltip/edit/edit-configure.ts +29 -0
  166. package/src/link-tooltip/edit/edit-view.ts +165 -0
  167. package/src/link-tooltip/index.ts +19 -0
  168. package/src/link-tooltip/preview/component.tsx +87 -0
  169. package/src/link-tooltip/preview/preview-configure.ts +65 -0
  170. package/src/link-tooltip/preview/preview-view.ts +101 -0
  171. package/src/link-tooltip/slices.ts +69 -0
  172. package/src/link-tooltip/tooltips.ts +22 -0
  173. package/src/link-tooltip/utils.ts +56 -0
  174. package/src/list-item-block/component.tsx +133 -0
  175. package/src/list-item-block/config.ts +39 -0
  176. package/src/list-item-block/index.ts +13 -0
  177. package/src/list-item-block/view.ts +130 -0
  178. package/src/table-block/config.ts +53 -0
  179. package/src/table-block/dnd/calc-drag-over.ts +46 -0
  180. package/src/table-block/dnd/create-drag-handler.ts +99 -0
  181. package/src/table-block/dnd/drag-over-handler.ts +113 -0
  182. package/src/table-block/dnd/prepare-dnd-context.ts +46 -0
  183. package/src/table-block/dnd/preview.ts +58 -0
  184. package/src/table-block/index.ts +9 -0
  185. package/src/table-block/view/component.tsx +219 -0
  186. package/src/table-block/view/drag.ts +121 -0
  187. package/src/table-block/view/index.ts +1 -0
  188. package/src/table-block/view/operation.ts +148 -0
  189. package/src/table-block/view/pointer.ts +165 -0
  190. package/src/table-block/view/types.ts +35 -0
  191. package/src/table-block/view/utils.ts +192 -0
  192. 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
+ }