@milkdown/preset-commonmark 6.5.4 → 7.0.0-next.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) hide show
  1. package/lib/composed/commands.d.ts +3 -0
  2. package/lib/composed/commands.d.ts.map +1 -0
  3. package/lib/composed/index.d.ts +6 -0
  4. package/lib/composed/index.d.ts.map +1 -0
  5. package/lib/composed/inputrules.d.ts +3 -0
  6. package/lib/composed/inputrules.d.ts.map +1 -0
  7. package/lib/composed/keymap.d.ts +3 -0
  8. package/lib/composed/keymap.d.ts.map +1 -0
  9. package/lib/composed/plugins.d.ts +3 -0
  10. package/lib/composed/plugins.d.ts.map +1 -0
  11. package/lib/composed/schema.d.ts +3 -0
  12. package/lib/composed/schema.d.ts.map +1 -0
  13. package/lib/index.d.ts +4 -29
  14. package/lib/index.d.ts.map +1 -1
  15. package/lib/index.es.js +1099 -1386
  16. package/lib/index.es.js.map +1 -1
  17. package/lib/mark/emphasis.d.ts +5 -0
  18. package/lib/mark/emphasis.d.ts.map +1 -0
  19. package/lib/mark/index.d.ts +3 -4
  20. package/lib/mark/index.d.ts.map +1 -1
  21. package/lib/mark/inline-code.d.ts +5 -0
  22. package/lib/mark/inline-code.d.ts.map +1 -0
  23. package/lib/mark/link.d.ts +8 -10
  24. package/lib/mark/link.d.ts.map +1 -1
  25. package/lib/mark/strong.d.ts +4 -2
  26. package/lib/mark/strong.d.ts.map +1 -1
  27. package/lib/node/blockquote.d.ts +6 -2
  28. package/lib/node/blockquote.d.ts.map +1 -1
  29. package/lib/node/bullet-list.d.ts +5 -2
  30. package/lib/node/bullet-list.d.ts.map +1 -1
  31. package/lib/node/code-block.d.ts +10 -0
  32. package/lib/node/code-block.d.ts.map +1 -0
  33. package/lib/node/doc.d.ts +1 -1
  34. package/lib/node/doc.d.ts.map +1 -1
  35. package/lib/node/hardbreak.d.ts +4 -6
  36. package/lib/node/hardbreak.d.ts.map +1 -1
  37. package/lib/node/heading.d.ts +7 -12
  38. package/lib/node/heading.d.ts.map +1 -1
  39. package/lib/node/hr.d.ts +4 -2
  40. package/lib/node/hr.d.ts.map +1 -1
  41. package/lib/node/image.d.ts +10 -11
  42. package/lib/node/image.d.ts.map +1 -1
  43. package/lib/node/index.d.ts +6 -8
  44. package/lib/node/index.d.ts.map +1 -1
  45. package/lib/node/list-item.d.ts +6 -7
  46. package/lib/node/list-item.d.ts.map +1 -1
  47. package/lib/node/ordered-list.d.ts +5 -2
  48. package/lib/node/ordered-list.d.ts.map +1 -1
  49. package/lib/node/paragraph.d.ts +4 -2
  50. package/lib/node/paragraph.d.ts.map +1 -1
  51. package/lib/node/text.d.ts +1 -1
  52. package/lib/node/text.d.ts.map +1 -1
  53. package/lib/plugin/hardbreak-clear-mark-plugin.d.ts +2 -0
  54. package/lib/plugin/hardbreak-clear-mark-plugin.d.ts.map +1 -0
  55. package/lib/plugin/hardbreak-filter-plugin.d.ts +3 -0
  56. package/lib/plugin/hardbreak-filter-plugin.d.ts.map +1 -0
  57. package/lib/plugin/index.d.ts +9 -3
  58. package/lib/plugin/index.d.ts.map +1 -1
  59. package/lib/plugin/inline-nodes-cursor-plugin.d.ts +2 -0
  60. package/lib/plugin/inline-nodes-cursor-plugin.d.ts.map +1 -0
  61. package/lib/plugin/{inline-sync → inline-sync-plugin}/config.d.ts +2 -2
  62. package/lib/plugin/inline-sync-plugin/config.d.ts.map +1 -0
  63. package/lib/plugin/{inline-sync → inline-sync-plugin}/context.d.ts +1 -2
  64. package/lib/plugin/inline-sync-plugin/context.d.ts.map +1 -0
  65. package/lib/plugin/inline-sync-plugin/index.d.ts +3 -0
  66. package/lib/plugin/inline-sync-plugin/index.d.ts.map +1 -0
  67. package/lib/plugin/inline-sync-plugin/inline-sync-plugin.d.ts +2 -0
  68. package/lib/plugin/inline-sync-plugin/inline-sync-plugin.d.ts.map +1 -0
  69. package/lib/plugin/{inline-sync → inline-sync-plugin}/regexp.d.ts +0 -0
  70. package/lib/plugin/inline-sync-plugin/regexp.d.ts.map +1 -0
  71. package/lib/plugin/{inline-sync → inline-sync-plugin}/replacer.d.ts +1 -1
  72. package/lib/plugin/inline-sync-plugin/replacer.d.ts.map +1 -0
  73. package/lib/plugin/{inline-sync → inline-sync-plugin}/utils.d.ts +0 -0
  74. package/lib/plugin/inline-sync-plugin/utils.d.ts.map +1 -0
  75. package/lib/plugin/remark-add-order-in-list-plugin.d.ts +2 -0
  76. package/lib/plugin/remark-add-order-in-list-plugin.d.ts.map +1 -0
  77. package/lib/plugin/remark-inline-link-plugin.d.ts +2 -0
  78. package/lib/plugin/remark-inline-link-plugin.d.ts.map +1 -0
  79. package/lib/plugin/remark-line-break.d.ts +2 -0
  80. package/lib/plugin/remark-line-break.d.ts.map +1 -0
  81. package/lib/plugin/sync-heading-id-plugin.d.ts +2 -0
  82. package/lib/plugin/sync-heading-id-plugin.d.ts.map +1 -0
  83. package/lib/plugin/sync-list-order-plugin.d.ts +2 -0
  84. package/lib/plugin/sync-list-order-plugin.d.ts.map +1 -0
  85. package/package.json +9 -7
  86. package/src/composed/commands.ts +31 -0
  87. package/src/composed/index.ts +6 -0
  88. package/src/composed/inputrules.ts +13 -0
  89. package/src/composed/keymap.ts +20 -0
  90. package/src/composed/plugins.ts +23 -0
  91. package/src/composed/schema.ts +55 -0
  92. package/src/index.ts +6 -52
  93. package/src/mark/emphasis.ts +47 -0
  94. package/src/mark/index.ts +3 -10
  95. package/src/mark/inline-code.ts +70 -0
  96. package/src/mark/link.ts +96 -247
  97. package/src/mark/strong.ts +41 -36
  98. package/src/node/blockquote.ts +39 -33
  99. package/src/node/bullet-list.ts +62 -55
  100. package/src/node/code-block.ts +103 -0
  101. package/src/node/doc.ts +18 -22
  102. package/src/node/hardbreak.ts +68 -117
  103. package/src/node/heading.ts +175 -284
  104. package/src/node/hr.ts +57 -57
  105. package/src/node/image.ts +113 -209
  106. package/src/node/index.ts +6 -35
  107. package/src/node/list-item.ts +125 -141
  108. package/src/node/ordered-list.ts +70 -65
  109. package/src/node/paragraph.ts +54 -50
  110. package/src/node/text.ts +14 -16
  111. package/src/plugin/hardbreak-clear-mark-plugin.ts +45 -0
  112. package/src/plugin/hardbreak-filter-plugin.ts +33 -0
  113. package/src/plugin/index.ts +12 -14
  114. package/src/plugin/{inline-nodes-cursor.ts → inline-nodes-cursor-plugin.ts} +5 -8
  115. package/src/plugin/{inline-sync → inline-sync-plugin}/config.ts +18 -3
  116. package/src/plugin/{inline-sync → inline-sync-plugin}/context.ts +4 -6
  117. package/src/plugin/inline-sync-plugin/index.ts +4 -0
  118. package/src/plugin/{inline-sync/index.ts → inline-sync-plugin/inline-sync-plugin.ts} +14 -11
  119. package/src/plugin/{inline-sync → inline-sync-plugin}/regexp.ts +0 -0
  120. package/src/plugin/{inline-sync → inline-sync-plugin}/replacer.ts +3 -3
  121. package/src/plugin/{inline-sync → inline-sync-plugin}/utils.ts +0 -0
  122. package/src/plugin/remark-add-order-in-list-plugin.ts +16 -0
  123. package/src/plugin/remark-inline-link-plugin.ts +6 -0
  124. package/src/plugin/remark-line-break.ts +44 -0
  125. package/src/plugin/sync-heading-id-plugin.ts +55 -0
  126. package/src/plugin/sync-list-order-plugin.ts +57 -0
  127. package/lib/mark/code-inline.d.ts +0 -3
  128. package/lib/mark/code-inline.d.ts.map +0 -1
  129. package/lib/mark/em.d.ts +0 -3
  130. package/lib/mark/em.d.ts.map +0 -1
  131. package/lib/node/code-fence.d.ts +0 -7
  132. package/lib/node/code-fence.d.ts.map +0 -1
  133. package/lib/plugin/add-order-in-list.d.ts +0 -3
  134. package/lib/plugin/add-order-in-list.d.ts.map +0 -1
  135. package/lib/plugin/filter-html.d.ts +0 -3
  136. package/lib/plugin/filter-html.d.ts.map +0 -1
  137. package/lib/plugin/inline-nodes-cursor.d.ts +0 -7
  138. package/lib/plugin/inline-nodes-cursor.d.ts.map +0 -1
  139. package/lib/plugin/inline-sync/config.d.ts.map +0 -1
  140. package/lib/plugin/inline-sync/context.d.ts.map +0 -1
  141. package/lib/plugin/inline-sync/index.d.ts +0 -6
  142. package/lib/plugin/inline-sync/index.d.ts.map +0 -1
  143. package/lib/plugin/inline-sync/regexp.d.ts.map +0 -1
  144. package/lib/plugin/inline-sync/replacer.d.ts.map +0 -1
  145. package/lib/plugin/inline-sync/utils.d.ts.map +0 -1
  146. package/lib/supported-keys.d.ts +0 -23
  147. package/lib/supported-keys.d.ts.map +0 -1
  148. package/src/mark/code-inline.ts +0 -66
  149. package/src/mark/em.ts +0 -42
  150. package/src/node/code-fence.ts +0 -245
  151. package/src/plugin/add-order-in-list.ts +0 -19
  152. package/src/plugin/filter-html.ts +0 -43
  153. package/src/supported-keys.ts +0 -25
  154. package/src/types.d.ts +0 -5
@@ -0,0 +1,13 @@
1
+ /* Copyright 2021, Milkdown by Mirone. */
2
+ import type { MilkdownPlugin } from '@milkdown/ctx'
3
+ import { createCodeBlockInputRule, insertHrInputRule, wrapInBlockquoteInputRule, wrapInBulletListInputRule, wrapInHeadingInputRule, wrapInOrderedListInputRule } from '../node'
4
+
5
+ /// @internal
6
+ export const inputrules: MilkdownPlugin[] = [
7
+ wrapInBlockquoteInputRule,
8
+ wrapInBulletListInputRule,
9
+ wrapInOrderedListInputRule,
10
+ createCodeBlockInputRule,
11
+ insertHrInputRule,
12
+ wrapInHeadingInputRule,
13
+ ].flat()
@@ -0,0 +1,20 @@
1
+ /* Copyright 2021, Milkdown by Mirone. */
2
+ import type { MilkdownPlugin } from '@milkdown/ctx'
3
+ import { emphasisKeymap, inlineCodeKeymap, strongKeymap } from '../mark'
4
+ import { blockquoteKeymap, bulletListKeymap, codeBlockKeymap, hardbreakKeymap, headingKeymap, listItemKeymap, orderedListKeymap, paragraphKeymap } from '../node'
5
+
6
+ /// @internal
7
+ export const keymap: MilkdownPlugin[] = [
8
+ blockquoteKeymap,
9
+ codeBlockKeymap,
10
+ hardbreakKeymap,
11
+ headingKeymap,
12
+ listItemKeymap,
13
+ orderedListKeymap,
14
+ bulletListKeymap,
15
+ paragraphKeymap,
16
+
17
+ emphasisKeymap,
18
+ inlineCodeKeymap,
19
+ strongKeymap,
20
+ ].flat()
@@ -0,0 +1,23 @@
1
+ /* Copyright 2021, Milkdown by Mirone. */
2
+
3
+ import type { MilkdownPlugin } from '@milkdown/ctx'
4
+ import { hardbreakClearMarkPlugin, hardbreakFilterNodes, hardbreakFilterPlugin, inlineNodesCursorPlugin, inlineSyncConfig, inlineSyncPlugin, remarkAddOrderInListPlugin, remarkInlineLinkPlugin, remarkLineBreak, syncHeadingIdPlugin, syncListOrderPlugin } from '../plugin'
5
+
6
+ /// @internal
7
+ export const plugins: MilkdownPlugin[] = [
8
+ inlineSyncConfig,
9
+ inlineSyncPlugin,
10
+
11
+ hardbreakClearMarkPlugin,
12
+ hardbreakFilterNodes,
13
+ hardbreakFilterPlugin,
14
+
15
+ inlineNodesCursorPlugin,
16
+
17
+ remarkAddOrderInListPlugin,
18
+ remarkInlineLinkPlugin,
19
+ remarkLineBreak,
20
+
21
+ syncHeadingIdPlugin,
22
+ syncListOrderPlugin,
23
+ ]
@@ -0,0 +1,55 @@
1
+ /* Copyright 2021, Milkdown by Mirone. */
2
+
3
+ import type { MilkdownPlugin } from '@milkdown/ctx'
4
+ import { emphasisAttr, emphasisSchema, inlineCodeAttr, inlineCodeSchema, linkAttr, linkSchema, strongAttr, strongSchema } from '../mark'
5
+ import { blockquoteAttr, blockquoteSchema, bulletListAttr, bulletListSchema, codeBlockAttr, codeBlockSchema, docSchema, hardbreakAttr, hardbreakSchema, headingAttr, headingIdGenerator, headingSchema, hrAttr, hrSchema, imageAttr, imageSchema, listItemAttr, listItemSchema, orderedListAttr, orderedListSchema, paragraphAttr, paragraphSchema, textSchema } from '../node'
6
+
7
+ /// @internal
8
+ export const schema: MilkdownPlugin[] = [
9
+ docSchema,
10
+
11
+ paragraphAttr,
12
+ paragraphSchema,
13
+
14
+ headingIdGenerator,
15
+ headingAttr,
16
+ headingSchema,
17
+
18
+ hardbreakAttr,
19
+ hardbreakSchema,
20
+
21
+ blockquoteAttr,
22
+ blockquoteSchema,
23
+
24
+ codeBlockAttr,
25
+ codeBlockSchema,
26
+
27
+ hrAttr,
28
+ hrSchema,
29
+
30
+ imageAttr,
31
+ imageSchema,
32
+
33
+ bulletListAttr,
34
+ bulletListSchema,
35
+
36
+ orderedListAttr,
37
+ orderedListSchema,
38
+
39
+ listItemAttr,
40
+ listItemSchema,
41
+
42
+ emphasisAttr,
43
+ emphasisSchema,
44
+
45
+ strongAttr,
46
+ strongSchema,
47
+
48
+ inlineCodeAttr,
49
+ inlineCodeSchema,
50
+
51
+ linkAttr,
52
+ linkSchema,
53
+
54
+ textSchema,
55
+ ].flat()
package/src/index.ts CHANGED
@@ -1,57 +1,11 @@
1
1
  /* Copyright 2021, Milkdown by Mirone. */
2
- import { AtomList } from '@milkdown/utils'
3
2
 
4
- import { ModifyLink, ToggleBold, ToggleInlineCode, ToggleItalic, ToggleLink, marks } from './mark'
5
- import {
6
- InsertHardbreak,
7
- InsertHr,
8
- InsertImage,
9
- LiftListItem,
10
- ModifyImage,
11
- SinkListItem,
12
- SplitListItem,
13
- TurnIntoCodeFence,
14
- TurnIntoHeading,
15
- TurnIntoText,
16
- WrapInBlockquote,
17
- WrapInBulletList,
18
- WrapInOrderedList,
19
- nodes,
20
- } from './node'
21
- import { commonmarkPlugins } from './plugin'
3
+ import { commands, inputrules, keymap, plugins, schema } from './composed'
22
4
 
23
- export * from './mark'
24
5
  export * from './node'
25
- export { inlineSyncConfigCtx } from './plugin'
26
- export * from './supported-keys'
27
-
28
- export const commonmarkNodes = AtomList.create([...nodes, ...marks])
29
- export { commonmarkPlugins }
30
- export const commonmark = AtomList.create([...commonmarkPlugins, ...commonmarkNodes])
31
-
32
- export const commands = {
33
- ToggleInlineCode,
34
- ToggleItalic,
35
- ToggleLink,
36
- ToggleBold,
37
-
38
- ModifyLink,
39
- ModifyImage,
40
-
41
- WrapInBlockquote,
42
- WrapInBulletList,
43
- WrapInOrderedList,
44
-
45
- TurnIntoCodeFence,
46
- TurnIntoHeading,
47
- TurnIntoText,
48
-
49
- InsertHardbreak,
50
- InsertHr,
51
- InsertImage,
6
+ export * from './mark'
7
+ export * from './plugin'
8
+ export * from './composed'
52
9
 
53
- SplitListItem,
54
- SinkListItem,
55
- LiftListItem,
56
- } as const
57
- export type Commands = typeof commands
10
+ /// The commonmark preset, includes all the plugins.
11
+ export const commonmark = [schema, inputrules, commands, keymap, plugins].flat()
@@ -0,0 +1,47 @@
1
+ /* Copyright 2021, Milkdown by Mirone. */
2
+ import { commandsCtx } from '@milkdown/core'
3
+ import { toggleMark } from '@milkdown/prose/commands'
4
+ import { $command, $markAttr, $markSchema, $useKeymap } from '@milkdown/utils'
5
+
6
+ /// HTML attributes for the emphasis mark.
7
+ export const emphasisAttr = $markAttr('emphasis')
8
+
9
+ /// Emphasis mark schema.
10
+ export const emphasisSchema = $markSchema('emphasis', ctx => ({
11
+ inclusive: false,
12
+ parseDOM: [
13
+ { tag: 'i' },
14
+ { tag: 'em' },
15
+ { style: 'font-style', getAttrs: value => (value === 'italic') as false },
16
+ ],
17
+ toDOM: mark => ['em', ctx.get(emphasisAttr.key)(mark)],
18
+ parseMarkdown: {
19
+ match: node => node.type === 'emphasis',
20
+ runner: (state, node, markType) => {
21
+ state.openMark(markType)
22
+ state.next(node.children)
23
+ state.closeMark(markType)
24
+ },
25
+ },
26
+ toMarkdown: {
27
+ match: mark => mark.type.name === 'emphasis',
28
+ runner: (state, mark) => {
29
+ state.withMark(mark, 'emphasis')
30
+ },
31
+ },
32
+ }))
33
+
34
+ /// A command to toggle the emphasis mark.
35
+ export const toggleEmphasisCommand = $command('ToggleEmphasis', () => () => toggleMark(emphasisSchema.type()))
36
+
37
+ /// Keymap for the emphasis mark.
38
+ /// - `Mod-i` - Toggle the emphasis mark.
39
+ export const emphasisKeymap = $useKeymap('emphasisKeymap', {
40
+ ToggleEmphasis: {
41
+ shortcuts: 'Mod-i',
42
+ command: (ctx) => {
43
+ const commands = ctx.get(commandsCtx)
44
+ return () => commands.call(toggleEmphasisCommand.key)
45
+ },
46
+ },
47
+ })
package/src/mark/index.ts CHANGED
@@ -1,12 +1,5 @@
1
1
  /* Copyright 2021, Milkdown by Mirone. */
2
- import { codeInline } from './code-inline'
3
- import { em } from './em'
4
- import { link } from './link'
5
- import { strong } from './strong'
6
-
7
- export const marks = [codeInline(), em(), strong(), link()]
8
-
9
- export * from './code-inline'
10
- export * from './em'
11
- export * from './link'
2
+ export * from './emphasis'
12
3
  export * from './strong'
4
+ export * from './inline-code'
5
+ export * from './link'
@@ -0,0 +1,70 @@
1
+ /* Copyright 2021, Milkdown by Mirone. */
2
+ import { commandsCtx } from '@milkdown/core'
3
+ import type { MarkType } from '@milkdown/prose/model'
4
+ import { $command, $markAttr, $markSchema, $useKeymap } from '@milkdown/utils'
5
+
6
+ /// HTML attributes for the inlineCode mark.
7
+ export const inlineCodeAttr = $markAttr('inlineCode')
8
+
9
+ /// InlineCode mark schema.
10
+ export const inlineCodeSchema = $markSchema('inlineCode', ctx => ({
11
+ priority: 100,
12
+ code: true,
13
+ inclusive: false,
14
+ parseDOM: [{ tag: 'code' }],
15
+ toDOM: mark => ['code', ctx.get(inlineCodeAttr.key)(mark)],
16
+ parseMarkdown: {
17
+ match: node => node.type === 'inlineCode',
18
+ runner: (state, node, markType) => {
19
+ state.openMark(markType)
20
+ state.addText(node.value as string)
21
+ state.closeMark(markType)
22
+ },
23
+ },
24
+ toMarkdown: {
25
+ match: mark => mark.type.name === 'inlineCode',
26
+ runner: (state, mark, node) => {
27
+ state.withMark(mark, 'inlineCode', node.text || '')
28
+ },
29
+ },
30
+ }))
31
+
32
+ /// A command to toggle the inlineCode mark.
33
+ export const toggleInlineCodeCommand = $command('ToggleInlineCode', () => () => (state, dispatch) => {
34
+ const { selection, tr } = state
35
+ if (selection.empty)
36
+ return false
37
+ const { from, to } = selection
38
+
39
+ const has = state.doc.rangeHasMark(from, to, inlineCodeSchema.type())
40
+ // remove exists inlineCode mark if have
41
+ if (has) {
42
+ dispatch?.(tr.removeMark(from, to, inlineCodeSchema.type()))
43
+ return true
44
+ }
45
+
46
+ const restMarksName = Object.keys(state.schema.marks).filter(x => x !== inlineCodeSchema.type.name)
47
+
48
+ // remove other marks
49
+ restMarksName
50
+ .map(name => state.schema.marks[name] as MarkType)
51
+ .forEach((t) => {
52
+ tr.removeMark(from, to, t)
53
+ })
54
+
55
+ // add inlineCode mark
56
+ dispatch?.(tr.addMark(from, to, inlineCodeSchema.type().create()))
57
+ return true
58
+ })
59
+
60
+ /// Keymap for the inlineCode mark.
61
+ /// - `Mod-e` - Toggle the inlineCode mark.
62
+ export const inlineCodeKeymap = $useKeymap('inlineCodeKeymap', {
63
+ ToggleInlineCode: {
64
+ shortcuts: 'Mod-e',
65
+ command: (ctx) => {
66
+ const commands = ctx.get(commandsCtx)
67
+ return () => commands.call(toggleInlineCodeCommand.key)
68
+ },
69
+ },
70
+ })
package/src/mark/link.ts CHANGED
@@ -1,253 +1,102 @@
1
1
  /* Copyright 2021, Milkdown by Mirone. */
2
- import type { ThemeInputChipType } from '@milkdown/core'
3
- import { commandsCtx, createCmd, createCmdKey } from '@milkdown/core'
4
- import { expectDomTypeError, missingRootElement } from '@milkdown/exception'
5
- import { calculateTextPosition } from '@milkdown/prose'
2
+ import { expectDomTypeError } from '@milkdown/exception'
6
3
  import { toggleMark } from '@milkdown/prose/commands'
7
4
  import type { Node as ProseNode } from '@milkdown/prose/model'
8
- import { NodeSelection, Plugin, PluginKey, TextSelection } from '@milkdown/prose/state'
9
- import type { EditorView } from '@milkdown/prose/view'
10
- import { createMark } from '@milkdown/utils'
11
-
12
- const key = new PluginKey('MILKDOWN_LINK_INPUT')
13
-
14
- export const ToggleLink = createCmdKey<string>('ToggleLink')
15
- export const ModifyLink = createCmdKey<string>('ModifyLink')
16
- const id = 'link'
17
- export interface LinkOptions {
18
- input: {
19
- placeholder?: string
20
- buttonText?: string
21
- displayWhenSelected?: boolean
22
- }
23
- }
24
- export const link = createMark<string, LinkOptions>((utils, options) => {
25
- return {
26
- id,
27
- schema: () => ({
28
- attrs: {
29
- href: {},
30
- title: { default: null },
31
- },
32
- parseDOM: [
33
- {
34
- tag: 'a[href]',
35
- getAttrs: (dom) => {
36
- if (!(dom instanceof HTMLElement))
37
- throw expectDomTypeError(dom)
38
-
39
- return { href: dom.getAttribute('href'), title: dom.getAttribute('title') }
40
- },
41
- },
42
- ],
43
- toDOM: mark => ['a', { ...mark.attrs, class: utils.getClassName(mark.attrs, id) }],
44
- parseMarkdown: {
45
- match: node => node.type === 'link',
46
- runner: (state, node, markType) => {
47
- const url = node.url as string
48
- const title = node.title as string
49
- state.openMark(markType, { href: url, title })
50
- state.next(node.children)
51
- state.closeMark(markType)
52
- },
53
- },
54
- toMarkdown: {
55
- match: mark => mark.type.name === id,
56
- runner: (state, mark) => {
57
- state.withMark(mark, 'link', undefined, {
58
- title: mark.attrs.title,
59
- url: mark.attrs.href,
60
- })
61
- },
5
+ import { TextSelection } from '@milkdown/prose/state'
6
+ import { $command, $markAttr, $markSchema } from '@milkdown/utils'
7
+
8
+ /// HTML attributes for the link mark.
9
+ export const linkAttr = $markAttr('link')
10
+
11
+ /// Link mark schema.
12
+ export const linkSchema = $markSchema('link', ctx => ({
13
+ attrs: {
14
+ href: {},
15
+ title: { default: null },
16
+ },
17
+ parseDOM: [
18
+ {
19
+ tag: 'a[href]',
20
+ getAttrs: (dom) => {
21
+ if (!(dom instanceof HTMLElement))
22
+ throw expectDomTypeError(dom)
23
+
24
+ return { href: dom.getAttribute('href'), title: dom.getAttribute('title') }
62
25
  },
63
- }),
64
- commands: markType => [
65
- createCmd(ToggleLink, (href = '') => toggleMark(markType, { href })),
66
- createCmd(ModifyLink, (href = '') => (state, dispatch) => {
67
- if (!dispatch)
68
- return false
69
-
70
- const { marks } = state.schema
71
-
72
- let node: ProseNode | undefined
73
- let pos = -1
74
- const { selection } = state
75
- const { from, to } = selection
76
- state.doc.nodesBetween(from, from === to ? to + 1 : to, (n, p) => {
77
- if (marks.link?.isInSet(n.marks)) {
78
- node = n
79
- pos = p
80
- return false
81
- }
82
-
83
- return undefined
84
- })
85
- if (!node)
86
- return false
87
-
88
- const mark = node.marks.find(({ type }) => type === markType)
89
- if (!mark)
90
- return false
91
-
92
- const start = pos
93
- const end = pos + node.nodeSize
94
- const { tr } = state
95
- const linkMark = marks.link?.create({ ...mark.attrs, href })
96
- if (!linkMark)
97
- return false
98
- dispatch(
99
- tr
100
- .removeMark(start, end, mark)
101
- .addMark(start, end, linkMark)
102
- .setSelection(new TextSelection(tr.selection.$anchor))
103
- .scrollIntoView(),
104
- )
105
-
106
- return true
107
- }),
108
- ],
109
- prosePlugins: (type, ctx) => {
110
- let renderOnTop = false
111
- return [
112
- new Plugin({
113
- key,
114
- view: (editorView) => {
115
- const inputChipRenderer = utils.themeManager.get<ThemeInputChipType>('input-chip', {
116
- placeholder: options?.input?.placeholder ?? 'Input Web Link',
117
- buttonText: options?.input?.buttonText,
118
- onUpdate: (value) => {
119
- ctx.get(commandsCtx).call(ModifyLink, value)
120
- },
121
- calculatePosition: (view, input) => {
122
- calculateTextPosition(view, input, (start, end, target, parent) => {
123
- const $editor = view.dom.parentElement
124
- if (!$editor)
125
- throw missingRootElement()
126
-
127
- const selectionWidth = end.left - start.left
128
- let left = start.left - parent.left - (target.width - selectionWidth) / 2
129
- let top = start.bottom - parent.top + 14 + $editor.scrollTop
130
-
131
- if (renderOnTop)
132
- top = start.top - parent.top - target.height - 14 + $editor.scrollTop
133
-
134
- if (left < 0)
135
- left = 0
136
-
137
- const maxLeft = $editor.clientWidth - (target.width + 4)
138
- if (left > maxLeft)
139
- left = maxLeft
140
-
141
- return [top, left]
142
- })
143
- },
144
- })
145
- if (!inputChipRenderer)
146
- return {}
147
- const shouldDisplay = (view: EditorView) => {
148
- const { selection, doc } = view.state
149
- const { from, to } = selection
150
-
151
- if (!view.hasFocus())
152
- return false
153
-
154
- if (
155
- selection instanceof TextSelection
156
- && to < doc.content.size
157
- && from < doc.content.size
158
- && doc.rangeHasMark(from, from === to ? to + 1 : to, type)
159
- ) {
160
- let shouldDisplay = selection.empty
161
- if (options?.input?.displayWhenSelected && !shouldDisplay) {
162
- doc.nodesBetween(from, from === to ? to + 1 : to, (node, pos) => {
163
- if (
164
- node.marks.some(
165
- mark =>
166
- mark.type === type && from >= pos && to <= pos + node.nodeSize,
167
- )
168
- ) {
169
- shouldDisplay = true
170
- return false
171
- }
172
-
173
- return undefined
174
- })
175
- }
176
- if (shouldDisplay) {
177
- renderOnTop = false
178
- return true
179
- }
180
- }
181
-
182
- if (selection instanceof NodeSelection) {
183
- const { node } = selection
184
- if (
185
- node.type.name === 'image'
186
- && node.marks.findIndex(mark => mark.type.name === id) > -1
187
- ) {
188
- renderOnTop = true
189
- return true
190
- }
191
- }
192
-
193
- return false
194
- }
195
- const getCurrentLink = (view: EditorView) => {
196
- const { selection } = view.state
197
- let node: ProseNode | undefined
198
- const { from, to } = selection
199
- view.state.doc.nodesBetween(from, from === to ? to + 1 : to, (n) => {
200
- if (type.isInSet(n.marks)) {
201
- node = n
202
- return false
203
- }
204
-
205
- return undefined
206
- })
207
- if (!node)
208
- return
209
-
210
- const mark = node.marks.find(m => m.type === type)
211
- if (!mark)
212
- return
213
-
214
- const value = mark.attrs.href
215
- return value
216
- }
217
- const renderByView = (view: EditorView) => {
218
- if (!view.editable)
219
- return
220
-
221
- const display = shouldDisplay(view)
222
- if (display) {
223
- inputChipRenderer.show(view)
224
- inputChipRenderer.update(getCurrentLink(view))
225
- }
226
- else {
227
- inputChipRenderer.hide()
228
- }
229
- }
230
- inputChipRenderer.init(editorView)
231
- renderByView(editorView)
232
-
233
- return {
234
- update: (view, prevState) => {
235
- const isEqualSelection
236
- = prevState?.doc.eq(view.state.doc) && prevState.selection.eq(view.state.selection)
237
- if (isEqualSelection)
238
- return
239
-
240
- requestAnimationFrame(() => {
241
- renderByView(view)
242
- })
243
- },
244
- destroy: () => {
245
- inputChipRenderer.destroy()
246
- },
247
- }
248
- },
249
- }),
250
- ]
251
26
  },
252
- }
27
+ ],
28
+ toDOM: mark => ['a', { ...ctx.get(linkAttr.key)(mark), ...mark.attrs }],
29
+ parseMarkdown: {
30
+ match: node => node.type === 'link',
31
+ runner: (state, node, markType) => {
32
+ const url = node.url as string
33
+ const title = node.title as string
34
+ state.openMark(markType, { href: url, title })
35
+ state.next(node.children)
36
+ state.closeMark(markType)
37
+ },
38
+ },
39
+ toMarkdown: {
40
+ match: mark => mark.type.name === 'link',
41
+ runner: (state, mark) => {
42
+ state.withMark(mark, 'link', undefined, {
43
+ title: mark.attrs.title,
44
+ url: mark.attrs.href,
45
+ })
46
+ },
47
+ },
48
+ }))
49
+
50
+ /// @internal
51
+ export type UpdateLinkCommandPayload = {
52
+ href?: string
53
+ title?: string
54
+ }
55
+ /// A command to toggle the link mark.
56
+ /// You can pass the `href` and `title` to the link.
57
+ export const toggleLinkCommand = $command('ToggleLink', () => (payload: UpdateLinkCommandPayload = {}) => toggleMark(linkSchema.type(), payload))
58
+
59
+ /// A command to update the link mark.
60
+ /// You can pass the `href` and `title` to update the link.
61
+ export const updateLinkCommand = $command('UpdateLink', () => (payload: UpdateLinkCommandPayload = {}) => (state, dispatch) => {
62
+ if (!dispatch)
63
+ return false
64
+
65
+ let node: ProseNode | undefined
66
+ let pos = -1
67
+ const { selection } = state
68
+ const { from, to } = selection
69
+ state.doc.nodesBetween(from, from === to ? to + 1 : to, (n, p) => {
70
+ if (linkSchema.type().isInSet(n.marks)) {
71
+ node = n
72
+ pos = p
73
+ return false
74
+ }
75
+
76
+ return undefined
77
+ })
78
+
79
+ if (!node)
80
+ return false
81
+
82
+ const mark = node.marks.find(({ type }) => type === linkSchema.type())
83
+ if (!mark)
84
+ return false
85
+
86
+ const start = pos
87
+ const end = pos + node.nodeSize
88
+ const { tr } = state
89
+ const linkMark = linkSchema.type().create({ ...mark.attrs, ...payload })
90
+ if (!linkMark)
91
+ return false
92
+
93
+ dispatch(
94
+ tr
95
+ .removeMark(start, end, mark)
96
+ .addMark(start, end, linkMark)
97
+ .setSelection(new TextSelection(tr.selection.$anchor))
98
+ .scrollIntoView(),
99
+ )
100
+
101
+ return true
253
102
  })