@tiptap/core 2.0.0-beta.153 → 2.0.0-beta.157

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tiptap/core",
3
3
  "description": "headless rich text editor",
4
- "version": "2.0.0-beta.153",
4
+ "version": "2.0.0-beta.157",
5
5
  "homepage": "https://tiptap.dev",
6
6
  "keywords": [
7
7
  "tiptap",
@@ -29,7 +29,7 @@
29
29
  "@types/prosemirror-model": "^1.13.2",
30
30
  "@types/prosemirror-schema-list": "^1.0.3",
31
31
  "@types/prosemirror-state": "^1.2.8",
32
- "@types/prosemirror-transform": "^1.1.4",
32
+ "@types/prosemirror-transform": "^1.1.5",
33
33
  "@types/prosemirror-view": "^1.19.2",
34
34
  "prosemirror-commands": "^1.1.12",
35
35
  "prosemirror-keymap": "^1.1.5",
@@ -44,5 +44,5 @@
44
44
  "url": "https://github.com/ueberdosis/tiptap",
45
45
  "directory": "packages/core"
46
46
  },
47
- "gitHead": "113133b74d5ef2852e80bc257defe2a1d675ebbe"
47
+ "gitHead": "179b3e23a95b9c06c13afb5fcdf7636cf982da78"
48
48
  }
@@ -16,15 +16,20 @@ export const clearNodes: RawCommands['clearNodes'] = () => ({ state, tr, dispatc
16
16
  const { selection } = tr
17
17
  const { ranges } = selection
18
18
 
19
- ranges.forEach(range => {
20
- state.doc.nodesBetween(range.$from.pos, range.$to.pos, (node, pos) => {
19
+ if (!dispatch) {
20
+ return true
21
+ }
22
+
23
+ ranges.forEach(({ $from, $to }) => {
24
+ state.doc.nodesBetween($from.pos, $to.pos, (node, pos) => {
21
25
  if (node.type.isText) {
22
26
  return
23
27
  }
24
28
 
25
- const $fromPos = tr.doc.resolve(tr.mapping.map(pos))
26
- const $toPos = tr.doc.resolve(tr.mapping.map(pos + node.nodeSize))
27
- const nodeRange = $fromPos.blockRange($toPos)
29
+ const { doc, mapping } = tr
30
+ const $mappedFrom = doc.resolve(mapping.map(pos))
31
+ const $mappedTo = doc.resolve(mapping.map(pos + node.nodeSize))
32
+ const nodeRange = $mappedFrom.blockRange($mappedTo)
28
33
 
29
34
  if (!nodeRange) {
30
35
  return
@@ -32,13 +37,13 @@ export const clearNodes: RawCommands['clearNodes'] = () => ({ state, tr, dispatc
32
37
 
33
38
  const targetLiftDepth = liftTarget(nodeRange)
34
39
 
35
- if (node.type.isTextblock && dispatch) {
36
- const { defaultType } = $fromPos.parent.contentMatchAt($fromPos.index())
40
+ if (node.type.isTextblock) {
41
+ const { defaultType } = $mappedFrom.parent.contentMatchAt($mappedFrom.index())
37
42
 
38
43
  tr.setNodeMarkup(nodeRange.start, defaultType)
39
44
  }
40
45
 
41
- if ((targetLiftDepth || targetLiftDepth === 0) && dispatch) {
46
+ if (targetLiftDepth || targetLiftDepth === 0) {
42
47
  tr.lift(nodeRange, targetLiftDepth)
43
48
  }
44
49
  })
@@ -24,14 +24,17 @@ export const setNode: RawCommands['setNode'] = (typeOrName, attributes = {}) =>
24
24
  return false
25
25
  }
26
26
 
27
- const canSetBlock = setBlockType(type, attributes)(state)
27
+ return chain()
28
+ // try to convert node to default node if needed
29
+ .command(({ commands }) => {
30
+ const canSetBlock = setBlockType(type, attributes)(state)
28
31
 
29
- if (canSetBlock) {
30
- return setBlockType(type, attributes)(state, dispatch)
31
- }
32
+ if (canSetBlock) {
33
+ return true
34
+ }
32
35
 
33
- return chain()
34
- .clearNodes()
36
+ return commands.clearNodes()
37
+ })
35
38
  .command(({ state: updatedState }) => {
36
39
  return setBlockType(type, attributes)(updatedState, dispatch)
37
40
  })
@@ -14,6 +14,11 @@ const joinListBackwards = (tr: Transaction, listType: NodeType): boolean => {
14
14
  }
15
15
 
16
16
  const before = tr.doc.resolve(Math.max(0, list.pos - 1)).before(list.depth)
17
+
18
+ if (before === undefined) {
19
+ return true
20
+ }
21
+
17
22
  const nodeBefore = tr.doc.nodeAt(before)
18
23
  const canJoinBackwards = list.node.type === nodeBefore?.type
19
24
  && canJoin(tr.doc, list.pos)
@@ -35,6 +40,11 @@ const joinListForwards = (tr: Transaction, listType: NodeType): boolean => {
35
40
  }
36
41
 
37
42
  const after = tr.doc.resolve(list.start).after(list.depth)
43
+
44
+ if (after === undefined) {
45
+ return true
46
+ }
47
+
38
48
  const nodeAfter = tr.doc.nodeAt(after)
39
49
  const canJoinForwards = list.node.type === nodeAfter?.type
40
50
  && canJoin(tr.doc, after)
@@ -1,3 +1,6 @@
1
+ import { Plugin, PluginKey, Selection } from 'prosemirror-state'
2
+ import { createChainableState } from '../helpers/createChainableState'
3
+ import { CommandManager } from '../CommandManager'
1
4
  import { Extension } from '../Extension'
2
5
 
3
6
  export const Keymap = Extension.create({
@@ -6,6 +9,24 @@ export const Keymap = Extension.create({
6
9
  addKeyboardShortcuts() {
7
10
  const handleBackspace = () => this.editor.commands.first(({ commands }) => [
8
11
  () => commands.undoInputRule(),
12
+ // maybe convert first text block node to default node
13
+ () => commands.command(({ tr }) => {
14
+ const { selection, doc } = tr
15
+ const { empty, $anchor } = selection
16
+ const { pos, parent } = $anchor
17
+ const isAtStart = Selection.atStart(doc).from === pos
18
+
19
+ if (
20
+ !empty
21
+ || !isAtStart
22
+ || !parent.type.isTextblock
23
+ || parent.textContent.length
24
+ ) {
25
+ return false
26
+ }
27
+
28
+ return commands.clearNodes()
29
+ }),
9
30
  () => commands.deleteSelection(),
10
31
  () => commands.joinBackward(),
11
32
  () => commands.selectNodeBackward(),
@@ -33,4 +54,53 @@ export const Keymap = Extension.create({
33
54
  'Mod-a': () => this.editor.commands.selectAll(),
34
55
  }
35
56
  },
57
+
58
+ addProseMirrorPlugins() {
59
+ return [
60
+ // With this plugin we check if the whole document was selected and deleted.
61
+ // In this case we will additionally call `clearNodes()` to convert e.g. a heading
62
+ // to a paragraph if necessary.
63
+ // This is an alternative to ProseMirror's `AllSelection`, which doesn’t work well
64
+ // with many other commands.
65
+ new Plugin({
66
+ key: new PluginKey('clearDocument'),
67
+ appendTransaction: (transactions, oldState, newState) => {
68
+ const docChanges = transactions.some(transaction => transaction.docChanged)
69
+ && !oldState.doc.eq(newState.doc)
70
+
71
+ if (!docChanges) {
72
+ return
73
+ }
74
+
75
+ const { empty, from, to } = oldState.selection
76
+ const allFrom = Selection.atStart(oldState.doc).from
77
+ const allEnd = Selection.atEnd(oldState.doc).to
78
+ const allWasSelected = from === allFrom && to === allEnd
79
+ const isEmpty = newState.doc.textBetween(0, newState.doc.content.size, ' ', ' ').length === 0
80
+
81
+ if (empty || !allWasSelected || !isEmpty) {
82
+ return
83
+ }
84
+
85
+ const tr = newState.tr
86
+ const state = createChainableState({
87
+ state: newState,
88
+ transaction: tr,
89
+ })
90
+ const { commands } = new CommandManager({
91
+ editor: this.editor,
92
+ state,
93
+ })
94
+
95
+ commands.clearNodes()
96
+
97
+ if (!tr.steps.length) {
98
+ return
99
+ }
100
+
101
+ return tr
102
+ },
103
+ }),
104
+ ]
105
+ },
36
106
  })