@prosekit/core 0.8.7 → 0.10.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/dist/{editor-M9OimMiI.d.ts → editor-BULC1zqX.d.ts} +31 -71
- package/dist/editor-BULC1zqX.d.ts.map +1 -0
- package/dist/{editor-B0L9BgMi.js → editor-g-Rqn-ZE.js} +119 -136
- package/dist/editor-g-Rqn-ZE.js.map +1 -0
- package/dist/prosekit-core-test.d.ts +1 -2
- package/dist/prosekit-core-test.d.ts.map +1 -1
- package/dist/prosekit-core-test.js +1 -1
- package/dist/prosekit-core-test.js.map +1 -1
- package/dist/prosekit-core.d.ts +182 -202
- package/dist/prosekit-core.d.ts.map +1 -1
- package/dist/prosekit-core.js +543 -549
- package/dist/prosekit-core.js.map +1 -1
- package/package.json +9 -12
- package/src/commands/add-mark.ts +1 -4
- package/src/commands/expand-mark.ts +2 -9
- package/src/commands/insert-default-block.spec.ts +1 -5
- package/src/commands/insert-default-block.ts +1 -4
- package/src/commands/insert-node.ts +4 -8
- package/src/commands/remove-mark.ts +1 -4
- package/src/commands/remove-node.ts +2 -2
- package/src/commands/select-all.ts +3 -8
- package/src/commands/select-block.spec.ts +81 -0
- package/src/commands/select-block.ts +56 -0
- package/src/commands/set-block-type.ts +1 -4
- package/src/commands/set-node-attrs-between.spec.ts +221 -0
- package/src/commands/set-node-attrs-between.ts +77 -0
- package/src/commands/set-node-attrs.spec.ts +129 -0
- package/src/commands/set-node-attrs.ts +25 -26
- package/src/commands/toggle-mark.ts +1 -4
- package/src/commands/toggle-node.ts +2 -5
- package/src/commands/toggle-wrap.spec.ts +1 -5
- package/src/commands/toggle-wrap.ts +1 -4
- package/src/commands/unset-block-type.spec.ts +1 -5
- package/src/commands/unset-block-type.ts +2 -8
- package/src/commands/unset-mark.spec.ts +1 -5
- package/src/commands/wrap.ts +2 -10
- package/src/editor/action.spec.ts +2 -6
- package/src/editor/action.ts +2 -19
- package/src/editor/editor.spec.ts +2 -9
- package/src/editor/editor.ts +31 -77
- package/src/editor/union.spec.ts +1 -5
- package/src/editor/union.ts +1 -4
- package/src/extensions/clipboard-serializer.ts +4 -16
- package/src/extensions/command.ts +20 -48
- package/src/extensions/default-state.spec.ts +1 -9
- package/src/extensions/default-state.ts +6 -32
- package/src/extensions/events/dom-event.spec.ts +1 -6
- package/src/extensions/events/dom-event.ts +5 -20
- package/src/extensions/events/editor-event.ts +5 -17
- package/src/extensions/events/focus.spec.ts +1 -6
- package/src/extensions/events/plugin-view.ts +2 -9
- package/src/extensions/history.ts +3 -10
- package/src/extensions/keymap-base.spec.ts +89 -0
- package/src/extensions/keymap-base.ts +34 -13
- package/src/extensions/keymap.spec.ts +12 -22
- package/src/extensions/keymap.ts +16 -69
- package/src/extensions/mark-spec.spec.ts +5 -20
- package/src/extensions/mark-spec.ts +33 -41
- package/src/extensions/mark-view-effect.ts +3 -9
- package/src/extensions/mark-view.ts +2 -8
- package/src/extensions/node-spec.spec.ts +5 -21
- package/src/extensions/node-spec.ts +31 -33
- package/src/extensions/node-view-effect.ts +3 -9
- package/src/extensions/node-view.ts +2 -8
- package/src/extensions/plugin.spec.ts +3 -16
- package/src/extensions/plugin.ts +4 -14
- package/src/facets/base-extension.ts +1 -4
- package/src/facets/command.ts +10 -10
- package/src/facets/facet-extension.spec.ts +4 -15
- package/src/facets/facet-node.spec.ts +2 -9
- package/src/facets/facet-node.ts +4 -9
- package/src/facets/facet.spec.ts +1 -4
- package/src/facets/schema-spec.ts +2 -9
- package/src/facets/schema.ts +3 -12
- package/src/facets/state.spec.ts +8 -15
- package/src/facets/state.ts +5 -20
- package/src/facets/union-extension.ts +2 -8
- package/src/index.ts +42 -188
- package/src/test/index.ts +1 -4
- package/src/test/test-builder.ts +1 -4
- package/src/test/test-editor.spec.ts +1 -5
- package/src/test/test-editor.ts +5 -24
- package/src/testing/index.ts +18 -14
- package/src/types/extension-command.ts +0 -7
- package/src/types/extension.spec.ts +1 -4
- package/src/types/extension.ts +3 -29
- package/src/types/simplify-union.ts +1 -4
- package/src/utils/array-grouping.spec.ts +2 -15
- package/src/utils/array-grouping.ts +1 -14
- package/src/utils/array.ts +0 -4
- package/src/utils/attrs-match.ts +1 -5
- package/src/utils/clsx.spec.ts +1 -4
- package/src/utils/combine-event-handlers.spec.ts +1 -5
- package/src/utils/combine-event-handlers.ts +4 -6
- package/src/utils/default-block-at.ts +1 -4
- package/src/utils/editor-content.spec.ts +1 -4
- package/src/utils/editor-content.ts +7 -19
- package/src/utils/find-node.ts +65 -0
- package/src/utils/find-parent-node-of-type.ts +6 -12
- package/src/utils/find-parent-node.spec.ts +1 -5
- package/src/utils/find-parent-node.ts +1 -4
- package/src/utils/get-custom-selection.ts +1 -5
- package/src/utils/get-mark-type.ts +1 -4
- package/src/utils/get-node-type.ts +1 -4
- package/src/utils/get-node-types.ts +1 -4
- package/src/utils/includes-mark.ts +1 -5
- package/src/utils/is-at-block-start.ts +1 -4
- package/src/utils/is-mark-absent.spec.ts +1 -4
- package/src/utils/is-mark-absent.ts +1 -5
- package/src/utils/is-mark-active.ts +1 -4
- package/src/utils/is-node-active.spec.ts +109 -0
- package/src/utils/is-node-active.ts +17 -8
- package/src/utils/is-subset.spec.ts +1 -4
- package/src/utils/maybe-run.spec.ts +1 -5
- package/src/utils/merge-objects.spec.ts +1 -4
- package/src/utils/merge-objects.ts +2 -1
- package/src/utils/merge-specs.ts +1 -4
- package/src/utils/object-equal.spec.ts +1 -4
- package/src/utils/output-spec.test.ts +1 -5
- package/src/utils/output-spec.ts +12 -9
- package/src/utils/parse.spec.ts +2 -11
- package/src/utils/parse.ts +12 -24
- package/src/utils/remove-undefined-values.spec.ts +1 -4
- package/src/utils/set-selection-around.ts +1 -4
- package/src/utils/type-assertion.ts +2 -21
- package/src/utils/unicode.spec.ts +1 -4
- package/dist/editor-B0L9BgMi.js.map +0 -1
- package/dist/editor-M9OimMiI.d.ts.map +0 -1
- package/src/extensions/doc.ts +0 -31
- package/src/extensions/paragraph.ts +0 -61
- package/src/extensions/text.ts +0 -34
- package/src/types/base-node-view-options.ts +0 -33
- package/src/types/object-entries.ts +0 -13
- package/src/utils/collect-children.ts +0 -21
- package/src/utils/collect-nodes.ts +0 -37
- package/src/utils/deep-equals.spec.ts +0 -26
- package/src/utils/deep-equals.ts +0 -29
- package/src/utils/get-id.spec.ts +0 -14
- package/src/utils/get-id.ts +0 -13
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
|
|
3
|
+
import { setupTest } from '../testing'
|
|
4
|
+
|
|
5
|
+
import { setNodeAttrs } from './set-node-attrs'
|
|
6
|
+
|
|
7
|
+
describe('setNodeAttrs', () => {
|
|
8
|
+
it('should set attributes on a code block node', () => {
|
|
9
|
+
const { editor, n } = setupTest()
|
|
10
|
+
|
|
11
|
+
editor.set(n.doc(n.codeBlock('const x = 1')))
|
|
12
|
+
|
|
13
|
+
// Get the initial language attribute (should be empty string by default)
|
|
14
|
+
expect(editor.state.doc.firstChild?.attrs.language).toBe('')
|
|
15
|
+
|
|
16
|
+
// Set the language attribute
|
|
17
|
+
const command = setNodeAttrs({
|
|
18
|
+
type: 'codeBlock',
|
|
19
|
+
attrs: { language: 'typescript' },
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
expect(editor.exec(command)).toBe(true)
|
|
23
|
+
expect(editor.state.doc.firstChild?.attrs.language).toBe('typescript')
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
it('should set multiple attributes at once', () => {
|
|
27
|
+
const { editor, n } = setupTest()
|
|
28
|
+
|
|
29
|
+
editor.set(n.doc(n.codeBlock('const x = 1')))
|
|
30
|
+
|
|
31
|
+
// Verify initial state
|
|
32
|
+
expect(editor.state.doc.firstChild?.attrs).toMatchObject({
|
|
33
|
+
language: '',
|
|
34
|
+
lineNumbers: false,
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
// Set multiple attributes at once
|
|
38
|
+
const command = setNodeAttrs({
|
|
39
|
+
type: 'codeBlock',
|
|
40
|
+
attrs: { language: 'javascript', lineNumbers: true },
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
editor.exec(command)
|
|
44
|
+
|
|
45
|
+
// Verify both attributes were set
|
|
46
|
+
expect(editor.state.doc.firstChild?.attrs).toMatchObject({
|
|
47
|
+
language: 'javascript',
|
|
48
|
+
lineNumbers: true,
|
|
49
|
+
})
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
it('should return false when node type does not match', () => {
|
|
53
|
+
const { editor, n } = setupTest()
|
|
54
|
+
|
|
55
|
+
editor.set(n.doc(n.paragraph('Hello world')))
|
|
56
|
+
|
|
57
|
+
const command = setNodeAttrs({
|
|
58
|
+
type: 'codeBlock',
|
|
59
|
+
attrs: { language: 'typescript' },
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
// Should return false because paragraph is not a codeBlock
|
|
63
|
+
expect(editor.exec(command)).toBe(false)
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
it('should set attributes at a specific position', () => {
|
|
67
|
+
const { editor, n } = setupTest()
|
|
68
|
+
|
|
69
|
+
editor.set(
|
|
70
|
+
n.doc(
|
|
71
|
+
/*0*/
|
|
72
|
+
n.codeBlock(/*1*/ 'A' /*2*/),
|
|
73
|
+
/*3*/
|
|
74
|
+
n.codeBlock(/*4*/ 'B' /*5*/),
|
|
75
|
+
/*6*/
|
|
76
|
+
),
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
// Set attribute on the second code block (position after first block)
|
|
80
|
+
const command = setNodeAttrs({
|
|
81
|
+
type: 'codeBlock',
|
|
82
|
+
attrs: { language: 'python' },
|
|
83
|
+
pos: 3, // Position of second code block
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
editor.exec(command)
|
|
87
|
+
|
|
88
|
+
// First block should still have default language
|
|
89
|
+
expect(editor.state.doc.child(0).attrs.language).toBe('')
|
|
90
|
+
// Second block should have the new language
|
|
91
|
+
expect(editor.state.doc.child(1).attrs.language).toBe('python')
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
it('should handle cursor inside a node', () => {
|
|
95
|
+
const { editor, n } = setupTest()
|
|
96
|
+
|
|
97
|
+
editor.set(n.doc(n.codeBlock('const<a> x = 1')))
|
|
98
|
+
|
|
99
|
+
const command = setNodeAttrs({
|
|
100
|
+
type: 'codeBlock',
|
|
101
|
+
attrs: { language: 'typescript' },
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
editor.exec(command)
|
|
105
|
+
|
|
106
|
+
expect(editor.state.doc.firstChild?.attrs.language).toBe('typescript')
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
it('should set attrs on wrapping node containing selection', () => {
|
|
110
|
+
const { editor, n } = setupTest()
|
|
111
|
+
|
|
112
|
+
editor.set(
|
|
113
|
+
n.doc(
|
|
114
|
+
n.blockquote(
|
|
115
|
+
n.paragraph('Hello<a> world<b>'),
|
|
116
|
+
),
|
|
117
|
+
),
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
const command = setNodeAttrs({
|
|
121
|
+
type: 'blockquote',
|
|
122
|
+
attrs: { variant: 'fancy' },
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
// Should find the blockquote wrapping the selection
|
|
126
|
+
expect(editor.exec(command)).toBe(true)
|
|
127
|
+
expect(editor.state.doc.firstChild?.attrs.variant).toBe('fancy')
|
|
128
|
+
})
|
|
129
|
+
})
|
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
Attrs,
|
|
3
|
-
NodeType,
|
|
4
|
-
} from '@prosekit/pm/model'
|
|
1
|
+
import type { Attrs, NodeType } from '@prosekit/pm/model'
|
|
5
2
|
import type { Command } from '@prosekit/pm/state'
|
|
6
3
|
|
|
4
|
+
import { findParentNodeOfType } from '../utils/find-parent-node-of-type'
|
|
7
5
|
import { getNodeTypes } from '../utils/get-node-types'
|
|
8
6
|
|
|
9
7
|
/**
|
|
@@ -12,8 +10,6 @@ import { getNodeTypes } from '../utils/get-node-types'
|
|
|
12
10
|
export interface SetNodeAttrsOptions {
|
|
13
11
|
/**
|
|
14
12
|
* The type of node to set the attributes of.
|
|
15
|
-
*
|
|
16
|
-
* If current node is not of this type, the command will do nothing.
|
|
17
13
|
*/
|
|
18
14
|
type: string | NodeType | string[] | NodeType[]
|
|
19
15
|
|
|
@@ -23,43 +19,46 @@ export interface SetNodeAttrsOptions {
|
|
|
23
19
|
attrs: Attrs
|
|
24
20
|
|
|
25
21
|
/**
|
|
26
|
-
* The position of the node
|
|
27
|
-
*
|
|
22
|
+
* The document position of the node to update. If not provided, the command
|
|
23
|
+
* will find the closest ancestor node that matches the type based on the
|
|
24
|
+
* anchor position of the selection.
|
|
28
25
|
*/
|
|
29
26
|
pos?: number
|
|
30
27
|
}
|
|
31
28
|
|
|
32
29
|
/**
|
|
33
|
-
* Returns a command that
|
|
30
|
+
* Returns a command that sets the attributes of the current node.
|
|
31
|
+
*
|
|
32
|
+
* @param options
|
|
34
33
|
*
|
|
35
34
|
* @public
|
|
36
35
|
*/
|
|
37
|
-
export function setNodeAttrs(
|
|
36
|
+
export function setNodeAttrs({ type, attrs, pos }: SetNodeAttrsOptions): Command {
|
|
38
37
|
return (state, dispatch) => {
|
|
39
|
-
|
|
40
|
-
const from = options.pos ?? state.selection.from
|
|
41
|
-
const to = options.pos ?? state.selection.to
|
|
42
|
-
const positions: number[] = []
|
|
38
|
+
let updatePos: number
|
|
43
39
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
40
|
+
if (pos == null) {
|
|
41
|
+
const found = findParentNodeOfType(type, state.selection.$anchor)
|
|
42
|
+
if (!found) {
|
|
43
|
+
return false
|
|
47
44
|
}
|
|
48
|
-
|
|
45
|
+
updatePos = found.pos
|
|
46
|
+
} else {
|
|
47
|
+
const found = state.doc.nodeAt(pos)
|
|
48
|
+
if (!found) {
|
|
49
49
|
return false
|
|
50
50
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
51
|
+
const nodeTypes = getNodeTypes(state.schema, type)
|
|
52
|
+
if (!nodeTypes.includes(found.type)) {
|
|
53
|
+
return false
|
|
54
|
+
}
|
|
55
|
+
updatePos = pos
|
|
55
56
|
}
|
|
56
57
|
|
|
57
58
|
if (dispatch) {
|
|
58
59
|
const { tr } = state
|
|
59
|
-
for (const
|
|
60
|
-
|
|
61
|
-
tr.setNodeAttribute(pos, key, value)
|
|
62
|
-
}
|
|
60
|
+
for (const [key, value] of Object.entries(attrs)) {
|
|
61
|
+
tr.setNodeAttribute(updatePos, key, value)
|
|
63
62
|
}
|
|
64
63
|
dispatch(tr)
|
|
65
64
|
}
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import { toggleMark as baseToggleMark } from '@prosekit/pm/commands'
|
|
2
|
-
import type {
|
|
3
|
-
Attrs,
|
|
4
|
-
MarkType,
|
|
5
|
-
} from '@prosekit/pm/model'
|
|
2
|
+
import type { Attrs, MarkType } from '@prosekit/pm/model'
|
|
6
3
|
import type { Command } from '@prosekit/pm/state'
|
|
7
4
|
|
|
8
5
|
import type { CommandCreator } from '../types/extension-command'
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import { setBlockType } from '@prosekit/pm/commands'
|
|
2
|
-
import type {
|
|
3
|
-
Attrs,
|
|
4
|
-
NodeType,
|
|
5
|
-
} from '@prosekit/pm/model'
|
|
2
|
+
import type { Attrs, NodeType } from '@prosekit/pm/model'
|
|
6
3
|
import type { Command } from '@prosekit/pm/state'
|
|
7
4
|
|
|
8
5
|
import { getNodeType } from '../utils/get-node-type'
|
|
@@ -24,7 +21,7 @@ export interface ToggleNodeOptions {
|
|
|
24
21
|
}
|
|
25
22
|
|
|
26
23
|
/**
|
|
27
|
-
* Returns a command that
|
|
24
|
+
* Returns a command that sets the selected textblocks to the given node type
|
|
28
25
|
* with the given attributes.
|
|
29
26
|
*
|
|
30
27
|
* @param options
|
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import { lift } from '@prosekit/pm/commands'
|
|
2
|
-
import type {
|
|
3
|
-
Attrs,
|
|
4
|
-
NodeType,
|
|
5
|
-
} from '@prosekit/pm/model'
|
|
2
|
+
import type { Attrs, NodeType } from '@prosekit/pm/model'
|
|
6
3
|
import type { Command } from '@prosekit/pm/state'
|
|
7
4
|
|
|
8
5
|
import { isNodeActive } from '../utils/is-node-active'
|
|
@@ -1,11 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
Slice,
|
|
4
|
-
} from '@prosekit/pm/model'
|
|
5
|
-
import type {
|
|
6
|
-
Command,
|
|
7
|
-
Transaction,
|
|
8
|
-
} from '@prosekit/pm/state'
|
|
1
|
+
import { Fragment, Slice } from '@prosekit/pm/model'
|
|
2
|
+
import type { Command, Transaction } from '@prosekit/pm/state'
|
|
9
3
|
import { ReplaceAroundStep } from '@prosekit/pm/transform'
|
|
10
4
|
|
|
11
5
|
import type { CommandCreator } from '../types/extension-command'
|
package/src/commands/wrap.ts
CHANGED
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
Attrs,
|
|
3
|
-
NodeType,
|
|
4
|
-
} from '@prosekit/pm/model'
|
|
1
|
+
import type { Attrs, NodeType } from '@prosekit/pm/model'
|
|
5
2
|
import type { Command } from '@prosekit/pm/state'
|
|
6
3
|
import { findWrapping } from '@prosekit/pm/transform'
|
|
7
4
|
|
|
@@ -16,11 +13,6 @@ export interface WrapOptions {
|
|
|
16
13
|
*/
|
|
17
14
|
type: NodeType | string
|
|
18
15
|
|
|
19
|
-
/**
|
|
20
|
-
* @deprecated Use `nodeSpec` instead.
|
|
21
|
-
*/
|
|
22
|
-
nodeType?: NodeType
|
|
23
|
-
|
|
24
16
|
/**
|
|
25
17
|
* Optional attributes to apply to the node.
|
|
26
18
|
*/
|
|
@@ -40,7 +32,7 @@ export function wrap(options: WrapOptions): Command {
|
|
|
40
32
|
const range = $from.blockRange($to)
|
|
41
33
|
if (!range) return false
|
|
42
34
|
|
|
43
|
-
const nodeType = getNodeType(state.schema, options.
|
|
35
|
+
const nodeType = getNodeType(state.schema, options.type)
|
|
44
36
|
const wrapping = findWrapping(range, nodeType, options.attrs)
|
|
45
37
|
if (!wrapping) return false
|
|
46
38
|
|
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
describe,
|
|
3
|
-
expect,
|
|
4
|
-
it,
|
|
5
|
-
} from 'vitest'
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
6
2
|
|
|
7
3
|
import { setupTest } from '../testing'
|
|
8
4
|
import { jsonFromNode } from '../utils/parse'
|
|
@@ -22,7 +18,7 @@ describe('NodeAction', () => {
|
|
|
22
18
|
it('can apply node with attrs', () => {
|
|
23
19
|
expect(n.codeBlock({ language: 'javascript' }, 'foo').toJSON()).toEqual({
|
|
24
20
|
type: 'codeBlock',
|
|
25
|
-
attrs: { language: 'javascript' },
|
|
21
|
+
attrs: { language: 'javascript', lineNumbers: false },
|
|
26
22
|
content: [{ text: 'foo', type: 'text' }],
|
|
27
23
|
})
|
|
28
24
|
})
|
package/src/editor/action.ts
CHANGED
|
@@ -1,13 +1,6 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
Mark,
|
|
4
|
-
MarkType,
|
|
5
|
-
NodeType,
|
|
6
|
-
ProseMirrorNode,
|
|
7
|
-
Schema,
|
|
8
|
-
} from '@prosekit/pm/model'
|
|
1
|
+
import { mapValues } from '@ocavue/utils'
|
|
2
|
+
import type { Attrs, Mark, MarkType, NodeType, ProseMirrorNode, Schema } from '@prosekit/pm/model'
|
|
9
3
|
import type { EditorState } from '@prosekit/pm/state'
|
|
10
|
-
import mapValues from 'just-map-values'
|
|
11
4
|
|
|
12
5
|
import { ProseKitError } from '../error'
|
|
13
6
|
import type { AnyAttrs } from '../types/attrs'
|
|
@@ -79,16 +72,6 @@ export interface MarkAction<Attrs extends AnyAttrs = AnyAttrs> {
|
|
|
79
72
|
isActive: (attrs?: Attrs) => boolean
|
|
80
73
|
}
|
|
81
74
|
|
|
82
|
-
/**
|
|
83
|
-
* @deprecated Use type {@link NodeAction} instead.
|
|
84
|
-
*/
|
|
85
|
-
export type NodeBuilder = NodeAction
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* @deprecated Use type {@link MarkAction} instead.
|
|
89
|
-
*/
|
|
90
|
-
export type MarkBuilder = MarkAction
|
|
91
|
-
|
|
92
75
|
/**
|
|
93
76
|
* @internal
|
|
94
77
|
*/
|
|
@@ -1,15 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
describe,
|
|
3
|
-
expect,
|
|
4
|
-
it,
|
|
5
|
-
} from 'vitest'
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
6
2
|
|
|
7
3
|
import { insertText } from '../commands/insert-text'
|
|
8
4
|
import { wrap } from '../commands/wrap'
|
|
9
|
-
import {
|
|
10
|
-
defineTestExtension,
|
|
11
|
-
setupTest,
|
|
12
|
-
} from '../testing'
|
|
5
|
+
import { defineTestExtension, setupTest } from '../testing'
|
|
13
6
|
import type { NodeJSON } from '../types/model'
|
|
14
7
|
|
|
15
8
|
import { createEditor } from './editor'
|
package/src/editor/editor.ts
CHANGED
|
@@ -1,28 +1,12 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
} from '@prosekit/pm/
|
|
5
|
-
import {
|
|
6
|
-
EditorState,
|
|
7
|
-
type Command,
|
|
8
|
-
type Plugin,
|
|
9
|
-
type Selection,
|
|
10
|
-
type Transaction,
|
|
11
|
-
} from '@prosekit/pm/state'
|
|
12
|
-
import {
|
|
13
|
-
EditorView,
|
|
14
|
-
type DirectEditorProps,
|
|
15
|
-
type EditorProps,
|
|
16
|
-
} from '@prosekit/pm/view'
|
|
1
|
+
import { isDeepEqual } from '@ocavue/utils'
|
|
2
|
+
import type { ProseMirrorNode, Schema } from '@prosekit/pm/model'
|
|
3
|
+
import { EditorState, type Command, type Plugin, type Selection, type Transaction } from '@prosekit/pm/state'
|
|
4
|
+
import { EditorView, type DirectEditorProps, type EditorProps } from '@prosekit/pm/view'
|
|
17
5
|
|
|
18
6
|
import { ProseKitError } from '../error'
|
|
19
7
|
import { defineDefaultState } from '../extensions/default-state'
|
|
20
8
|
import type { BaseExtension } from '../facets/base-extension'
|
|
21
|
-
import {
|
|
22
|
-
subtractFacetNode,
|
|
23
|
-
unionFacetNode,
|
|
24
|
-
type FacetNode,
|
|
25
|
-
} from '../facets/facet-node'
|
|
9
|
+
import { subtractFacetNode, unionFacetNode, type FacetNode } from '../facets/facet-node'
|
|
26
10
|
import type {
|
|
27
11
|
Extension,
|
|
28
12
|
ExtractCommandActions,
|
|
@@ -31,32 +15,13 @@ import type {
|
|
|
31
15
|
ExtractNodeActions,
|
|
32
16
|
ExtractNodeNames,
|
|
33
17
|
} from '../types/extension'
|
|
34
|
-
import type {
|
|
35
|
-
|
|
36
|
-
CommandCreator,
|
|
37
|
-
} from '../types/extension-command'
|
|
38
|
-
import type {
|
|
39
|
-
NodeJSON,
|
|
40
|
-
SelectionJSON,
|
|
41
|
-
} from '../types/model'
|
|
18
|
+
import type { CommandAction, CommandCreator } from '../types/extension-command'
|
|
19
|
+
import type { NodeJSON, SelectionJSON } from '../types/model'
|
|
42
20
|
import { assert } from '../utils/assert'
|
|
43
|
-
import {
|
|
44
|
-
import {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
} from '../utils/editor-content'
|
|
48
|
-
import {
|
|
49
|
-
htmlFromNode,
|
|
50
|
-
jsonFromNode,
|
|
51
|
-
type DOMDocumentOptions,
|
|
52
|
-
} from '../utils/parse'
|
|
53
|
-
|
|
54
|
-
import {
|
|
55
|
-
createMarkActions,
|
|
56
|
-
createNodeActions,
|
|
57
|
-
type MarkAction,
|
|
58
|
-
type NodeAction,
|
|
59
|
-
} from './action'
|
|
21
|
+
import { getEditorContentDoc, getEditorSelection } from '../utils/editor-content'
|
|
22
|
+
import { htmlFromNode, jsonFromNode, type DOMDocumentOptions } from '../utils/parse'
|
|
23
|
+
|
|
24
|
+
import { createMarkActions, createNodeActions, type MarkAction, type NodeAction } from './action'
|
|
60
25
|
import { union } from './union'
|
|
61
26
|
|
|
62
27
|
/**
|
|
@@ -70,25 +35,9 @@ export interface EditorOptions<E extends Extension> {
|
|
|
70
35
|
|
|
71
36
|
/**
|
|
72
37
|
* The starting document to use when creating the editor. It can be a
|
|
73
|
-
* ProseMirror node JSON object,
|
|
74
|
-
*/
|
|
75
|
-
defaultContent?: NodeJSON | string | HTMLElement
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* A JSON object representing the starting document to use when creating the
|
|
79
|
-
* editor.
|
|
80
|
-
*
|
|
81
|
-
* @deprecated Use `defaultContent` instead.
|
|
82
|
-
*/
|
|
83
|
-
defaultDoc?: NodeJSON
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* A HTML element or a HTML string representing the starting document to use
|
|
87
|
-
* when creating the editor.
|
|
88
|
-
*
|
|
89
|
-
* @deprecated Use `defaultContent` instead.
|
|
38
|
+
* ProseMirror node JSON object, an HTML string, or a DOM element instance.
|
|
90
39
|
*/
|
|
91
|
-
|
|
40
|
+
defaultContent?: NodeJSON | string | Element
|
|
92
41
|
|
|
93
42
|
/**
|
|
94
43
|
* A JSON object representing the starting selection to use when creating the
|
|
@@ -108,7 +57,7 @@ export interface getDocHTMLOptions extends DOMDocumentOptions {}
|
|
|
108
57
|
export function setupEditorExtension<E extends Extension>(
|
|
109
58
|
options: EditorOptions<E>,
|
|
110
59
|
): E {
|
|
111
|
-
if (options.defaultContent
|
|
60
|
+
if (options.defaultContent) {
|
|
112
61
|
return union(
|
|
113
62
|
options.extension,
|
|
114
63
|
defineDefaultState(options),
|
|
@@ -176,7 +125,7 @@ export class EditorInstance {
|
|
|
176
125
|
return this.getState().doc
|
|
177
126
|
}
|
|
178
127
|
|
|
179
|
-
private getProp<PropName extends keyof EditorProps>(propName: PropName): EditorProps[PropName]
|
|
128
|
+
private getProp<PropName extends keyof EditorProps>(propName: PropName): Partial<EditorProps>[PropName] {
|
|
180
129
|
return this.view?.someProp(propName) ?? this.directEditorProps[propName]
|
|
181
130
|
}
|
|
182
131
|
|
|
@@ -197,7 +146,7 @@ export class EditorInstance {
|
|
|
197
146
|
}
|
|
198
147
|
|
|
199
148
|
public setContent(
|
|
200
|
-
content: NodeJSON | string |
|
|
149
|
+
content: NodeJSON | string | Element | ProseMirrorNode,
|
|
201
150
|
selection?: SelectionJSON | Selection | 'start' | 'end',
|
|
202
151
|
): void {
|
|
203
152
|
const doc = getEditorContentDoc(this.schema, content)
|
|
@@ -226,7 +175,7 @@ export class EditorInstance {
|
|
|
226
175
|
}
|
|
227
176
|
|
|
228
177
|
/**
|
|
229
|
-
* Return
|
|
178
|
+
* Return an HTML string representing the editor's current document.
|
|
230
179
|
*/
|
|
231
180
|
public getDocHTML = (options?: getDocHTMLOptions): string => {
|
|
232
181
|
const serializer = this.getProp('clipboardSerializer')
|
|
@@ -264,14 +213,14 @@ export class EditorInstance {
|
|
|
264
213
|
const newPayload = this.tree.getRootOutput()
|
|
265
214
|
const newPlugins = [...(newPayload?.state?.plugins ?? [])]
|
|
266
215
|
|
|
267
|
-
if (!
|
|
216
|
+
if (!isDeepEqual(oldPlugins, newPlugins)) {
|
|
268
217
|
const state = view.state.reconfigure({ plugins: newPlugins })
|
|
269
218
|
view.updateState(state)
|
|
270
219
|
}
|
|
271
220
|
|
|
272
221
|
if (
|
|
273
222
|
newPayload?.commands
|
|
274
|
-
&& !
|
|
223
|
+
&& !isDeepEqual(oldPayload?.commands, newPayload?.commands)
|
|
275
224
|
) {
|
|
276
225
|
const commands = newPayload.commands
|
|
277
226
|
const names = Object.keys(commands)
|
|
@@ -306,10 +255,14 @@ export class EditorInstance {
|
|
|
306
255
|
|
|
307
256
|
public mount(place: HTMLElement): void {
|
|
308
257
|
if (this.view) {
|
|
258
|
+
// If the editor is already mounted to the same DOM element, do nothing
|
|
259
|
+
if (this.view.dom === place) return
|
|
260
|
+
// If the editor is already mounted to a different element, throw an error
|
|
309
261
|
throw new ProseKitError('Editor is already mounted')
|
|
310
262
|
}
|
|
311
263
|
this.view = new EditorView({ mount: place }, this.directEditorProps)
|
|
312
264
|
this.afterMounted.forEach((callback) => callback())
|
|
265
|
+
this.afterMounted.length = 0
|
|
313
266
|
}
|
|
314
267
|
|
|
315
268
|
public unmount(): void {
|
|
@@ -374,7 +327,6 @@ export class EditorInstance {
|
|
|
374
327
|
return this.canExec(command)
|
|
375
328
|
}
|
|
376
329
|
|
|
377
|
-
action.canApply = canExec
|
|
378
330
|
action.canExec = canExec
|
|
379
331
|
|
|
380
332
|
this.commands[name] = action as CommandAction
|
|
@@ -437,12 +389,14 @@ export class Editor<E extends Extension = any> {
|
|
|
437
389
|
}
|
|
438
390
|
|
|
439
391
|
/**
|
|
440
|
-
* Mount the editor to the given HTML element.
|
|
441
|
-
*
|
|
392
|
+
* Mount the editor to the given HTML element. Pass `null` or `undefined` to
|
|
393
|
+
* unmount the editor. When an element is passed, this method returns a
|
|
394
|
+
* function to unmount the editor.
|
|
442
395
|
*/
|
|
443
|
-
mount = (place: HTMLElement | null | undefined): void => {
|
|
396
|
+
mount = (place: HTMLElement | null | undefined): void | VoidFunction => {
|
|
444
397
|
if (place) {
|
|
445
398
|
this.instance.mount(place)
|
|
399
|
+
return this.unmount
|
|
446
400
|
} else {
|
|
447
401
|
this.instance.unmount()
|
|
448
402
|
}
|
|
@@ -496,7 +450,7 @@ export class Editor<E extends Extension = any> {
|
|
|
496
450
|
* - A ProseMirror node instance
|
|
497
451
|
* - A ProseMirror node JSON object
|
|
498
452
|
* - An HTML string
|
|
499
|
-
* -
|
|
453
|
+
* - A DOM element instance
|
|
500
454
|
* @param selection - Optional. Specifies the new selection. It can be one of the following:
|
|
501
455
|
* - A ProseMirror selection instance
|
|
502
456
|
* - A ProseMirror selection JSON object
|
|
@@ -504,7 +458,7 @@ export class Editor<E extends Extension = any> {
|
|
|
504
458
|
* - The string "end" (to set selection at the end)
|
|
505
459
|
*/
|
|
506
460
|
setContent = (
|
|
507
|
-
content: ProseMirrorNode | NodeJSON | string |
|
|
461
|
+
content: ProseMirrorNode | NodeJSON | string | Element,
|
|
508
462
|
selection?: SelectionJSON | Selection | 'start' | 'end',
|
|
509
463
|
): void => {
|
|
510
464
|
return this.instance.setContent(content, selection)
|
|
@@ -518,7 +472,7 @@ export class Editor<E extends Extension = any> {
|
|
|
518
472
|
}
|
|
519
473
|
|
|
520
474
|
/**
|
|
521
|
-
* Return
|
|
475
|
+
* Return an HTML string representing the editor's current document.
|
|
522
476
|
*/
|
|
523
477
|
public getDocHTML = (options?: getDocHTMLOptions): string => {
|
|
524
478
|
return this.instance.getDocHTML(options)
|
package/src/editor/union.spec.ts
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
import type { Attrs } from '@prosekit/pm/model'
|
|
2
2
|
import type { Command } from '@prosekit/pm/state'
|
|
3
|
-
import {
|
|
4
|
-
describe,
|
|
5
|
-
expectTypeOf,
|
|
6
|
-
it,
|
|
7
|
-
} from 'vitest'
|
|
3
|
+
import { describe, expectTypeOf, it } from 'vitest'
|
|
8
4
|
|
|
9
5
|
import { defineCommands } from '../extensions/command'
|
|
10
6
|
import { defineMarkSpec } from '../extensions/mark-spec'
|
package/src/editor/union.ts
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
import type { BaseExtension } from '../facets/base-extension'
|
|
2
2
|
import { UnionExtensionImpl } from '../facets/union-extension'
|
|
3
|
-
import type {
|
|
4
|
-
Extension,
|
|
5
|
-
Union,
|
|
6
|
-
} from '../types/extension'
|
|
3
|
+
import type { Extension, Union } from '../types/extension'
|
|
7
4
|
import { assert } from '../utils/assert'
|
|
8
5
|
|
|
9
6
|
/**
|
|
@@ -1,25 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
type Mark,
|
|
5
|
-
type ProseMirrorNode,
|
|
6
|
-
type Schema,
|
|
7
|
-
} from '@prosekit/pm/model'
|
|
8
|
-
import {
|
|
9
|
-
PluginKey,
|
|
10
|
-
ProseMirrorPlugin,
|
|
11
|
-
} from '@prosekit/pm/state'
|
|
1
|
+
import { isNotNullish } from '@ocavue/utils'
|
|
2
|
+
import { DOMSerializer, type DOMOutputSpec, type Mark, type ProseMirrorNode, type Schema } from '@prosekit/pm/model'
|
|
3
|
+
import { PluginKey, ProseMirrorPlugin } from '@prosekit/pm/state'
|
|
12
4
|
|
|
13
5
|
import { defineFacet } from '../facets/facet'
|
|
14
6
|
import { defineFacetPayload } from '../facets/facet-extension'
|
|
15
7
|
import type { AnyFunction } from '../types/any-function'
|
|
16
8
|
import type { PlainExtension } from '../types/extension'
|
|
17
|
-
import { isNotNullish } from '../utils/type-assertion'
|
|
18
9
|
|
|
19
|
-
import {
|
|
20
|
-
pluginFacet,
|
|
21
|
-
type PluginPayload,
|
|
22
|
-
} from './plugin'
|
|
10
|
+
import { pluginFacet, type PluginPayload } from './plugin'
|
|
23
11
|
|
|
24
12
|
type SerializeFragmentFunction = typeof DOMSerializer.prototype.serializeFragment
|
|
25
13
|
type SerializeNodeFunction = typeof DOMSerializer.prototype.serializeNode
|