@milkdown/preset-gfm 7.4.0 → 7.5.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 (39) hide show
  1. package/lib/composed/commands.d.ts +5 -1
  2. package/lib/composed/commands.d.ts.map +1 -1
  3. package/lib/composed/schema.d.ts.map +1 -1
  4. package/lib/index.es.js +544 -481
  5. package/lib/index.es.js.map +1 -1
  6. package/lib/node/table/command.d.ts +33 -0
  7. package/lib/node/table/command.d.ts.map +1 -0
  8. package/lib/node/table/index.d.ts +3 -30
  9. package/lib/node/table/index.d.ts.map +1 -1
  10. package/lib/node/table/input.d.ts +3 -0
  11. package/lib/node/table/input.d.ts.map +1 -0
  12. package/lib/node/table/schema.d.ts +6 -0
  13. package/lib/node/table/schema.d.ts.map +1 -0
  14. package/lib/node/table/utils.d.ts +21 -7
  15. package/lib/node/table/utils.d.ts.map +1 -1
  16. package/lib/plugin/auto-insert-span-plugin.d.ts +2 -0
  17. package/lib/plugin/auto-insert-span-plugin.d.ts.map +1 -0
  18. package/lib/plugin/index.d.ts +2 -1
  19. package/lib/plugin/index.d.ts.map +1 -1
  20. package/lib/plugin/keep-table-align-plugin.d.ts +2 -0
  21. package/lib/plugin/keep-table-align-plugin.d.ts.map +1 -0
  22. package/lib/plugin/table-editing-plugin.d.ts.map +1 -1
  23. package/package.json +12 -17
  24. package/src/composed/commands.ts +2 -2
  25. package/src/composed/plugins.ts +4 -4
  26. package/src/composed/schema.ts +11 -1
  27. package/src/index.ts +1 -1
  28. package/src/node/table/command.ts +228 -0
  29. package/src/node/table/index.ts +3 -450
  30. package/src/node/table/input.ts +73 -0
  31. package/src/node/table/schema.ts +216 -0
  32. package/src/node/table/utils.ts +49 -18
  33. package/src/plugin/auto-insert-span-plugin.ts +12 -0
  34. package/src/plugin/index.ts +2 -1
  35. package/src/plugin/keep-table-align-plugin.ts +60 -0
  36. package/src/plugin/table-editing-plugin.ts +1 -1
  37. package/lib/plugin/auto-insert-zero-space-plugin.d.ts +0 -2
  38. package/lib/plugin/auto-insert-zero-space-plugin.d.ts.map +0 -1
  39. package/src/plugin/auto-insert-zero-space-plugin.ts +0 -55
@@ -0,0 +1,216 @@
1
+ import { tableNodes } from '@milkdown/prose/tables'
2
+ import { $nodeSchema } from '@milkdown/utils'
3
+ import type { MarkdownNode } from '@milkdown/transformer'
4
+ import type { NodeType } from '@milkdown/prose/model'
5
+ import { withMeta } from '../../__internal__'
6
+
7
+ const originalSchema = tableNodes({
8
+ tableGroup: 'block',
9
+ cellContent: 'paragraph',
10
+ cellAttributes: {
11
+ alignment: {
12
+ default: 'left',
13
+ getFromDOM: dom => (dom).style.textAlign || 'left',
14
+ setDOMAttr: (value, attrs) => {
15
+ attrs.style = `text-align: ${value || 'left'}`
16
+ },
17
+ },
18
+ },
19
+ })
20
+
21
+ /// Schema for table node.
22
+ export const tableSchema = $nodeSchema('table', () => ({
23
+ ...originalSchema.table,
24
+ content: 'table_header_row table_row+',
25
+ disableDropCursor: true,
26
+ parseMarkdown: {
27
+ match: node => node.type === 'table',
28
+ runner: (state, node, type) => {
29
+ const align = node.align as (string | null)[]
30
+ const children = (node.children as MarkdownNode[]).map((x, i) => ({
31
+ ...x,
32
+ align,
33
+ isHeader: i === 0,
34
+ }))
35
+ state.openNode(type)
36
+ state.next(children)
37
+ state.closeNode()
38
+ },
39
+ },
40
+ toMarkdown: {
41
+ match: node => node.type.name === 'table',
42
+ runner: (state, node) => {
43
+ const firstLine = node.content.firstChild?.content
44
+ if (!firstLine)
45
+ return
46
+
47
+ const align: (string | null)[] = []
48
+ firstLine.forEach((cell) => {
49
+ align.push(cell.attrs.alignment)
50
+ })
51
+ state.openNode('table', undefined, { align })
52
+ state.next(node.content)
53
+ state.closeNode()
54
+ },
55
+ },
56
+ }))
57
+
58
+ withMeta(tableSchema.node, {
59
+ displayName: 'NodeSchema<table>',
60
+ group: 'Table',
61
+ })
62
+
63
+ withMeta(tableSchema.ctx, {
64
+ displayName: 'NodeSchemaCtx<table>',
65
+ group: 'Table',
66
+ })
67
+
68
+ /// Schema for table header row node.
69
+ export const tableHeaderRowSchema = $nodeSchema('table_header_row', () => ({
70
+ ...originalSchema.table_row,
71
+ disableDropCursor: true,
72
+ content: '(table_header)*',
73
+ parseDOM: [{ tag: 'tr[data-is-header]' }],
74
+ toDOM() {
75
+ return ['tr', { 'data-is-header': true }, 0]
76
+ },
77
+ parseMarkdown: {
78
+ match: node => Boolean(node.type === 'tableRow' && node.isHeader),
79
+ runner: (state, node, type) => {
80
+ const align = node.align as (string | null)[]
81
+ const children = (node.children as MarkdownNode[]).map((x, i) => ({
82
+ ...x,
83
+ align: align[i],
84
+ isHeader: node.isHeader,
85
+ }))
86
+ state.openNode(type)
87
+ state.next(children)
88
+ state.closeNode()
89
+ },
90
+ },
91
+ toMarkdown: {
92
+ match: node => node.type.name === 'table_header_row',
93
+ runner: (state, node) => {
94
+ state.openNode('tableRow', undefined, { isHeader: true })
95
+ state.next(node.content)
96
+ state.closeNode()
97
+ },
98
+ },
99
+ }))
100
+
101
+ withMeta(tableHeaderRowSchema.node, {
102
+ displayName: 'NodeSchema<tableHeaderRow>',
103
+ group: 'Table',
104
+ })
105
+
106
+ withMeta(tableHeaderRowSchema.ctx, {
107
+ displayName: 'NodeSchemaCtx<tableHeaderRow>',
108
+ group: 'Table',
109
+ })
110
+
111
+ /// Schema for table row node.
112
+ export const tableRowSchema = $nodeSchema('table_row', () => ({
113
+ ...originalSchema.table_row,
114
+ disableDropCursor: true,
115
+ content: '(table_cell)*',
116
+ parseMarkdown: {
117
+ match: node => node.type === 'tableRow',
118
+ runner: (state, node, type) => {
119
+ const align = node.align as (string | null)[]
120
+ const children = (node.children as MarkdownNode[]).map((x, i) => ({
121
+ ...x,
122
+ align: align[i],
123
+ }))
124
+ state.openNode(type)
125
+ state.next(children)
126
+ state.closeNode()
127
+ },
128
+ },
129
+ toMarkdown: {
130
+ match: node => node.type.name === 'table_row',
131
+ runner: (state, node) => {
132
+ state.openNode('tableRow')
133
+ state.next(node.content)
134
+ state.closeNode()
135
+ },
136
+ },
137
+ }))
138
+
139
+ withMeta(tableRowSchema.node, {
140
+ displayName: 'NodeSchema<tableRow>',
141
+ group: 'Table',
142
+ })
143
+
144
+ withMeta(tableRowSchema.ctx, {
145
+ displayName: 'NodeSchemaCtx<tableRow>',
146
+ group: 'Table',
147
+ })
148
+
149
+ /// Schema for table cell node.
150
+ export const tableCellSchema = $nodeSchema('table_cell', () => ({
151
+ ...originalSchema.table_cell,
152
+ disableDropCursor: true,
153
+ parseMarkdown: {
154
+ match: node => node.type === 'tableCell' && !node.isHeader,
155
+ runner: (state, node, type) => {
156
+ const align = node.align as string
157
+ state
158
+ .openNode(type, { alignment: align })
159
+ .openNode(state.schema.nodes.paragraph as NodeType)
160
+ .next(node.children)
161
+ .closeNode()
162
+ .closeNode()
163
+ },
164
+ },
165
+ toMarkdown: {
166
+ match: node => node.type.name === 'table_cell',
167
+ runner: (state, node) => {
168
+ state.openNode('tableCell').next(node.content).closeNode()
169
+ },
170
+ },
171
+ }))
172
+
173
+ withMeta(tableCellSchema.node, {
174
+ displayName: 'NodeSchema<tableCell>',
175
+ group: 'Table',
176
+ })
177
+
178
+ withMeta(tableCellSchema.ctx, {
179
+ displayName: 'NodeSchemaCtx<tableCell>',
180
+ group: 'Table',
181
+ })
182
+
183
+ /// Schema for table header node.
184
+ export const tableHeaderSchema = $nodeSchema('table_header', () => ({
185
+ ...originalSchema.table_header,
186
+ disableDropCursor: true,
187
+ parseMarkdown: {
188
+ match: node => node.type === 'tableCell' && !!node.isHeader,
189
+ runner: (state, node, type) => {
190
+ const align = node.align as string
191
+ state.openNode(type, { alignment: align })
192
+ state.openNode(state.schema.nodes.paragraph as NodeType)
193
+ state.next(node.children)
194
+ state.closeNode()
195
+ state.closeNode()
196
+ },
197
+ },
198
+ toMarkdown: {
199
+ match: node => node.type.name === 'table_header',
200
+ runner: (state, node) => {
201
+ state.openNode('tableCell')
202
+ state.next(node.content)
203
+ state.closeNode()
204
+ },
205
+ },
206
+ }))
207
+
208
+ withMeta(tableHeaderSchema.node, {
209
+ displayName: 'NodeSchema<tableHeader>',
210
+ group: 'Table',
211
+ })
212
+
213
+ withMeta(tableHeaderSchema.ctx, {
214
+ displayName: 'NodeSchemaCtx<tableHeader>',
215
+ group: 'Table',
216
+ })
@@ -1,12 +1,12 @@
1
1
  import type { ContentNodeWithPos } from '@milkdown/prose'
2
- import { cloneTr, findParentNode } from '@milkdown/prose'
3
- import type { Node } from '@milkdown/prose/model'
2
+ import { cloneTr, findParentNodeClosestToPos } from '@milkdown/prose'
3
+ import type { Node, ResolvedPos } from '@milkdown/prose/model'
4
4
  import type { Selection, Transaction } from '@milkdown/prose/state'
5
5
  import type { TableRect } from '@milkdown/prose/tables'
6
6
  import { CellSelection, TableMap } from '@milkdown/prose/tables'
7
7
 
8
8
  import type { Ctx } from '@milkdown/ctx'
9
- import { tableCellSchema, tableHeaderSchema, tableRowSchema, tableSchema } from '.'
9
+ import { tableCellSchema, tableHeaderRowSchema, tableHeaderSchema, tableRowSchema, tableSchema } from './schema'
10
10
 
11
11
  /// @internal
12
12
  export interface CellPos {
@@ -27,19 +27,21 @@ export function createTable(ctx: Ctx, rowsCount = 3, colsCount = 3): Node {
27
27
 
28
28
  const rows = Array(rowsCount)
29
29
  .fill(0)
30
- .map((_, i) => tableRowSchema.type(ctx).create(null, i === 0 ? headerCells : cells))
30
+ .map((_, i) => i === 0
31
+ ? tableHeaderRowSchema.type(ctx).create(null, headerCells)
32
+ : tableRowSchema.type(ctx).create(null, cells))
31
33
 
32
34
  return tableSchema.type(ctx).create(null, rows)
33
35
  }
34
36
 
35
- /// Find the table node with position information for current selection.
36
- export function findTable(selection: Selection) {
37
- return findParentNode(node => node.type.spec.tableRole === 'table')(selection)
37
+ /// Find the table node with position information for target pos.
38
+ export function findTable($pos: ResolvedPos) {
39
+ return findParentNodeClosestToPos(node => node.type.spec.tableRole === 'table')($pos)
38
40
  }
39
41
 
40
42
  /// Get cells in a column of a table.
41
43
  export function getCellsInCol(columnIndex: number, selection: Selection): CellPos[] | undefined {
42
- const table = findTable(selection)
44
+ const table = findTable(selection.$from)
43
45
  if (!table)
44
46
  return undefined
45
47
  const map = TableMap.get(table.node)
@@ -64,7 +66,7 @@ export function getCellsInCol(columnIndex: number, selection: Selection): CellPo
64
66
 
65
67
  /// Get cells in a row of a table.
66
68
  export function getCellsInRow(rowIndex: number, selection: Selection): CellPos[] | undefined {
67
- const table = findTable(selection)
69
+ const table = findTable(selection.$from)
68
70
  if (!table)
69
71
  return undefined
70
72
  const map = TableMap.get(table.node)
@@ -89,7 +91,7 @@ export function getCellsInRow(rowIndex: number, selection: Selection): CellPos[]
89
91
 
90
92
  /// Get all cells in a table.
91
93
  export function getAllCellsInTable(selection: Selection) {
92
- const table = findTable(selection)
94
+ const table = findTable(selection.$from)
93
95
  if (!table)
94
96
  return
95
97
 
@@ -142,8 +144,17 @@ export function addRowWithAlignment(ctx: Ctx, tr: Transaction, { map, tableStart
142
144
 
143
145
  /// @internal
144
146
  export function selectLine(type: 'row' | 'col') {
145
- return (index: number) => (tr: Transaction) => {
146
- const table = findTable(tr.selection)
147
+ return (index: number, pos?: number) => (tr: Transaction) => {
148
+ pos = pos ?? tr.selection.from
149
+ const $pos = tr.doc.resolve(pos)
150
+ const $node = findParentNodeClosestToPos(node => node.type.name === 'table')($pos)
151
+ const table = $node
152
+ ? {
153
+ node: $node.node,
154
+ from: $node.start,
155
+ }
156
+ : undefined
157
+
147
158
  const isRowSelection = type === 'row'
148
159
  if (table) {
149
160
  const map = TableMap.get(table.node)
@@ -155,12 +166,12 @@ export function selectLine(type: 'row' | 'col') {
155
166
  isRowSelection ? map.width - 1 : index,
156
167
  table.node,
157
168
  )
158
- const $lastCell = tr.doc.resolve(table.start + lastCell)
169
+ const $lastCell = tr.doc.resolve(table.from + lastCell)
159
170
 
160
171
  const createCellSelection = isRowSelection ? CellSelection.rowSelection : CellSelection.colSelection
161
172
 
162
173
  const firstCell = map.positionAt(isRowSelection ? index : 0, isRowSelection ? 0 : index, table.node)
163
- const $firstCell = tr.doc.resolve(table.start + firstCell)
174
+ const $firstCell = tr.doc.resolve(table.from + firstCell)
164
175
  return cloneTr(tr.setSelection(createCellSelection($lastCell, $firstCell) as unknown as Selection))
165
176
  }
166
177
  }
@@ -407,11 +418,21 @@ function getSelectionRangeInRow(rowIndex: number, tr: Transaction) {
407
418
  return { $anchor, $head, indexes }
408
419
  }
409
420
 
421
+ export interface MoveColParams {
422
+ tr: Transaction
423
+ origin: number
424
+ target: number
425
+ select?: boolean
426
+ pos?: number
427
+ }
428
+
410
429
  /// If the selection is in a table,
411
430
  /// Move the columns at `origin` to `target` in current table.
412
431
  /// The `select` is true by default, which means the selection will be set to the moved column.
413
- export function moveCol(tr: Transaction, origin: number, target: number, select = true) {
414
- const table = findTable(tr.selection)
432
+ export function moveCol(moveColParams: MoveColParams) {
433
+ const { tr, origin, target, select = true, pos } = moveColParams
434
+ const $pos = pos != null ? tr.doc.resolve(pos) : tr.selection.$from
435
+ const table = findTable($pos)
415
436
  if (!table)
416
437
  return tr
417
438
 
@@ -451,11 +472,21 @@ export function moveCol(tr: Transaction, origin: number, target: number, select
451
472
  return _tr.setSelection(createCellSelection($lastCell, $firstCell))
452
473
  }
453
474
 
475
+ export interface MoveRowParams {
476
+ tr: Transaction
477
+ origin: number
478
+ target: number
479
+ select?: boolean
480
+ pos?: number
481
+ }
482
+
454
483
  /// If the selection is in a table,
455
484
  /// Move the rows at `origin` and `target` in current table.
456
485
  /// The `select` is true by default, which means the selection will be set to the moved row.
457
- export function moveRow(tr: Transaction, origin: number, target: number, select = true) {
458
- const table = findTable(tr.selection)
486
+ export function moveRow(moveRowParams: MoveRowParams) {
487
+ const { tr, origin, target, select = true, pos } = moveRowParams
488
+ const $pos = pos != null ? tr.doc.resolve(pos) : tr.selection.$from
489
+ const table = findTable($pos)
459
490
  if (!table)
460
491
  return tr
461
492
 
@@ -0,0 +1,12 @@
1
+ import { $prose } from '@milkdown/utils'
2
+ import { imeSpan } from 'prosemirror-safari-ime-span'
3
+ import { withMeta } from '../__internal__'
4
+
5
+ /// This plugin is used to fix the bug of IME composing in table in Safari browser.
6
+ /// original discussion in https://discuss.prosemirror.net/t/ime-composing-problems-on-td-or-th-element-in-safari-browser/4501
7
+ export const autoInsertSpanPlugin = $prose(() => imeSpan)
8
+
9
+ withMeta(autoInsertSpanPlugin, {
10
+ displayName: 'Prose<autoInsertSpanPlugin>',
11
+ group: 'Prose',
12
+ })
@@ -1,4 +1,5 @@
1
- export * from './auto-insert-zero-space-plugin'
1
+ export * from './auto-insert-span-plugin'
2
2
  export * from './column-resizing-plugin'
3
3
  export * from './table-editing-plugin'
4
4
  export * from './remark-gfm-plugin'
5
+ export * from './keep-table-align-plugin'
@@ -0,0 +1,60 @@
1
+ import type { Transaction } from '@milkdown/prose/state'
2
+ import { Plugin, PluginKey } from '@milkdown/prose/state'
3
+ import type { Node } from '@milkdown/prose/model'
4
+ import { $prose } from '@milkdown/utils'
5
+ import { withMeta } from '../__internal__'
6
+
7
+ const pluginKey = new PluginKey('MILKDOWN_KEEP_TABLE_ALIGN_PLUGIN')
8
+
9
+ function getChildIndex(node: Node, parent: Node) {
10
+ let index = 0
11
+ parent.forEach((child, _offset, i) => {
12
+ if (child === node)
13
+ index = i
14
+ })
15
+ return index
16
+ }
17
+
18
+ export const keepTableAlignPlugin = $prose(() => {
19
+ return new Plugin({
20
+ key: pluginKey,
21
+ appendTransaction: (_tr, oldState, state) => {
22
+ let tr: Transaction | undefined
23
+ const check = (node: Node, pos: number) => {
24
+ if (!tr)
25
+ tr = state.tr
26
+
27
+ if (node.type.name !== 'table_cell')
28
+ return
29
+
30
+ const $pos = state.doc.resolve(pos)
31
+ const tableRow = $pos.node($pos.depth)
32
+ const table = $pos.node($pos.depth - 1)
33
+ const tableHeaderRow = table.firstChild
34
+ // TODO: maybe consider add a header row
35
+ if (!tableHeaderRow)
36
+ return
37
+
38
+ const index = getChildIndex(node, tableRow)
39
+ const headerCell = tableHeaderRow.maybeChild(index)
40
+ if (!headerCell)
41
+ return
42
+ const align = headerCell.attrs.alignment
43
+ const currentAlign = node.attrs.alignment
44
+ if (align === currentAlign)
45
+ return
46
+
47
+ tr.setNodeMarkup(pos, undefined, { ...node.attrs, alignment: align })
48
+ }
49
+ if (oldState.doc !== state.doc)
50
+ state.doc.descendants(check)
51
+
52
+ return tr
53
+ },
54
+ })
55
+ })
56
+
57
+ withMeta(keepTableAlignPlugin, {
58
+ displayName: 'Prose<keepTableAlignPlugin>',
59
+ group: 'Prose',
60
+ })
@@ -3,7 +3,7 @@ import { $prose } from '@milkdown/utils'
3
3
  import { withMeta } from '../__internal__'
4
4
 
5
5
  /// This plugin is wrapping the `tableEditing` plugin from [prosemirror-tables](https://github.com/ProseMirror/prosemirror-tables).
6
- export const tableEditingPlugin = $prose(() => tableEditing())
6
+ export const tableEditingPlugin = $prose(() => tableEditing({ allowTableNodeSelection: true }))
7
7
 
8
8
  withMeta(tableEditingPlugin, {
9
9
  displayName: 'Prose<tableEditingPlugin>',
@@ -1,2 +0,0 @@
1
- export declare const autoInsertZeroSpaceInTablePlugin: import("@milkdown/utils").$Prose;
2
- //# sourceMappingURL=auto-insert-zero-space-plugin.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"auto-insert-zero-space-plugin.d.ts","sourceRoot":"","sources":["../../src/plugin/auto-insert-zero-space-plugin.ts"],"names":[],"mappings":"AAUA,eAAO,MAAM,gCAAgC,kCAuC3C,CAAA"}
@@ -1,55 +0,0 @@
1
- import { browser } from '@milkdown/prose'
2
- import type { Node } from '@milkdown/prose/model'
3
- import { isInTable } from '@milkdown/prose/tables'
4
- import { Plugin, PluginKey } from '@milkdown/prose/state'
5
- import { paragraphSchema } from '@milkdown/preset-commonmark'
6
- import { $prose } from '@milkdown/utils'
7
- import { withMeta } from '../__internal__'
8
-
9
- /// This plugin is used to fix the bug of IME composing in table in Safari browser.
10
- /// original discussion in https://discuss.prosemirror.net/t/ime-composing-problems-on-td-or-th-element-in-safari-browser/4501
11
- export const autoInsertZeroSpaceInTablePlugin = $prose((ctx) => {
12
- const pluginKey = new PluginKey('MILKDOWN_AUTO_INSERT_ZERO_SPACE')
13
-
14
- const isParagraph = (node: Node) => node.type === paragraphSchema.type(ctx)
15
-
16
- const isEmptyParagraph = (node: Node) => isParagraph(node) && node.nodeSize === 2
17
-
18
- return new Plugin({
19
- key: pluginKey,
20
- props: {
21
- handleDOMEvents: {
22
- compositionstart(view) {
23
- const { state, dispatch } = view
24
- const { tr, selection } = state
25
- const { $from } = selection
26
- if (browser.safari && isInTable(state) && selection.empty && isEmptyParagraph($from.parent))
27
- dispatch(tr.insertText('\u2060', $from.start()))
28
-
29
- return false
30
- },
31
- compositionend(view) {
32
- const { state, dispatch } = view
33
- const { tr, selection } = state
34
- const { $from } = selection
35
-
36
- if (
37
- browser.safari
38
- && isInTable(state)
39
- && selection.empty
40
- && isParagraph($from.parent)
41
- && $from.parent.textContent.startsWith('\u2060')
42
- )
43
- dispatch(tr.delete($from.start(), $from.start() + 1))
44
-
45
- return false
46
- },
47
- },
48
- },
49
- })
50
- })
51
-
52
- withMeta(autoInsertZeroSpaceInTablePlugin, {
53
- displayName: 'Prose<autoInsertZeroSpaceInTablePlugin>',
54
- group: 'Prose',
55
- })