@tiptap/core 2.0.0-beta.163 → 2.0.0-beta.167

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.163",
4
+ "version": "2.0.0-beta.167",
5
5
  "homepage": "https://tiptap.dev",
6
6
  "keywords": [
7
7
  "tiptap",
@@ -30,14 +30,14 @@
30
30
  "@types/prosemirror-schema-list": "^1.0.3",
31
31
  "@types/prosemirror-state": "^1.2.8",
32
32
  "@types/prosemirror-transform": "^1.1.5",
33
- "@types/prosemirror-view": "^1.19.2",
34
- "prosemirror-commands": "^1.1.12",
33
+ "@types/prosemirror-view": "^1.23.1",
34
+ "prosemirror-commands": "^1.2.1",
35
35
  "prosemirror-keymap": "^1.1.5",
36
36
  "prosemirror-model": "^1.16.1",
37
37
  "prosemirror-schema-list": "^1.1.6",
38
38
  "prosemirror-state": "^1.3.4",
39
39
  "prosemirror-transform": "^1.3.3",
40
- "prosemirror-view": "^1.23.5"
40
+ "prosemirror-view": "^1.23.6"
41
41
  },
42
42
  "repository": {
43
43
  "type": "git",
@@ -45,5 +45,5 @@
45
45
  "directory": "packages/core"
46
46
  },
47
47
  "sideEffects": false,
48
- "gitHead": "d71c27fe99e34d1906b4f094554beb86fc7287e1"
48
+ "gitHead": "82759a898a77aa0bf6a459178021e86b6713ef39"
49
49
  }
package/src/PasteRule.ts CHANGED
@@ -158,24 +158,53 @@ function run(config: {
158
158
  */
159
159
  export function pasteRulesPlugin(props: { editor: Editor, rules: PasteRule[] }): Plugin[] {
160
160
  const { editor, rules } = props
161
- let isProseMirrorHTML = false
161
+ let dragSourceElement: Element | null = null
162
+ let isPastedFromProseMirror = false
163
+ let isDroppedFromProseMirror = false
162
164
 
163
165
  const plugins = rules.map(rule => {
164
166
  return new Plugin({
167
+ // we register a global drag handler to track the current drag source element
168
+ view(view) {
169
+ const handleDragstart = (event: DragEvent) => {
170
+ dragSourceElement = view.dom.parentElement?.contains(event.target as Element)
171
+ ? view.dom.parentElement
172
+ : null
173
+ }
174
+
175
+ window.addEventListener('dragstart', handleDragstart)
176
+
177
+ return {
178
+ destroy() {
179
+ window.removeEventListener('dragstart', handleDragstart)
180
+ },
181
+ }
182
+ },
183
+
165
184
  props: {
166
- handlePaste: (view, event) => {
167
- const html = event.clipboardData?.getData('text/html')
185
+ handleDOMEvents: {
186
+ drop: view => {
187
+ isDroppedFromProseMirror = dragSourceElement === view.dom.parentElement
188
+
189
+ return false
190
+ },
168
191
 
169
- isProseMirrorHTML = !!html?.includes('data-pm-slice')
192
+ paste: (view, event) => {
193
+ const html = event.clipboardData?.getData('text/html')
170
194
 
171
- return false
195
+ isPastedFromProseMirror = !!html?.includes('data-pm-slice')
196
+
197
+ return false
198
+ },
172
199
  },
173
200
  },
201
+
174
202
  appendTransaction: (transactions, oldState, state) => {
175
203
  const transaction = transactions[0]
204
+ const isPaste = transaction.getMeta('uiEvent') === 'paste' && !isPastedFromProseMirror
205
+ const isDrop = transaction.getMeta('uiEvent') === 'drop' && !isDroppedFromProseMirror
176
206
 
177
- // stop if there is not a paste event
178
- if (!transaction.getMeta('paste') || isProseMirrorHTML) {
207
+ if (!isPaste && !isDrop) {
179
208
  return
180
209
  }
181
210
 
@@ -15,6 +15,10 @@ export const blur: RawCommands['blur'] = () => ({ editor, view }) => {
15
15
  requestAnimationFrame(() => {
16
16
  if (!editor.isDestroyed) {
17
17
  (view.dom as HTMLElement).blur()
18
+
19
+ // Browsers should remove the caret on blur but safari does not.
20
+ // See: https://github.com/ueberdosis/tiptap/issues/2405
21
+ window?.getSelection()?.removeAllRanges()
18
22
  }
19
23
  })
20
24
 
@@ -53,6 +53,7 @@ export const insertContentAt: RawCommands['insertContentAt'] = (position, value,
53
53
  ? { from: position, to: position }
54
54
  : position
55
55
 
56
+ let isOnlyTextContent = true
56
57
  let isOnlyBlockContent = true
57
58
  const nodes = isFragment(content)
58
59
  ? content
@@ -62,6 +63,10 @@ export const insertContentAt: RawCommands['insertContentAt'] = (position, value,
62
63
  // check if added node is valid
63
64
  node.check()
64
65
 
66
+ isOnlyTextContent = isOnlyTextContent
67
+ ? node.isText && node.marks.length === 0
68
+ : false
69
+
65
70
  isOnlyBlockContent = isOnlyBlockContent
66
71
  ? node.isBlock
67
72
  : false
@@ -84,7 +89,13 @@ export const insertContentAt: RawCommands['insertContentAt'] = (position, value,
84
89
  }
85
90
  }
86
91
 
87
- tr.replaceWith(from, to, content)
92
+ // if there is only plain text we have to use `insertText`
93
+ // because this will keep the current marks
94
+ if (isOnlyTextContent) {
95
+ tr.insertText(value as string, from, to)
96
+ } else {
97
+ tr.replaceWith(from, to, content)
98
+ }
88
99
 
89
100
  // set cursor at end of inserted content
90
101
  if (options.updateSelection) {
@@ -1,6 +1,6 @@
1
1
  import { RawCommands } from '../types'
2
-
3
- const mac = typeof navigator !== 'undefined' ? /Mac/.test(navigator.platform) : false
2
+ import { isiOS } from '../utilities/isiOS'
3
+ import { isMacOS } from '../utilities/isMacOS'
4
4
 
5
5
  function normalizeKeyName(name: string) {
6
6
  const parts = name.split(/-(?!$)/)
@@ -27,7 +27,7 @@ function normalizeKeyName(name: string) {
27
27
  } else if (/^s(hift)?$/i.test(mod)) {
28
28
  shift = true
29
29
  } else if (/^mod$/i.test(mod)) {
30
- if (mac) {
30
+ if (isiOS() || isMacOS()) {
31
31
  meta = true
32
32
  } else {
33
33
  ctrl = true
@@ -0,0 +1,19 @@
1
+ // @ts-ignore
2
+ // TODO: add types to @types/prosemirror-commands
3
+ import { selectTextblockEnd as originalSelectTextblockEnd } from 'prosemirror-commands'
4
+ import { RawCommands } from '../types'
5
+
6
+ declare module '@tiptap/core' {
7
+ interface Commands<ReturnType> {
8
+ selectTextblockEnd: {
9
+ /**
10
+ * Moves the cursor to the end of current text block.
11
+ */
12
+ selectTextblockEnd: () => ReturnType,
13
+ }
14
+ }
15
+ }
16
+
17
+ export const selectTextblockEnd: RawCommands['selectTextblockEnd'] = () => ({ state, dispatch }) => {
18
+ return originalSelectTextblockEnd(state, dispatch)
19
+ }
@@ -0,0 +1,19 @@
1
+ // @ts-ignore
2
+ // TODO: add types to @types/prosemirror-commands
3
+ import { selectTextblockStart as originalSelectTextblockStart } from 'prosemirror-commands'
4
+ import { RawCommands } from '../types'
5
+
6
+ declare module '@tiptap/core' {
7
+ interface Commands<ReturnType> {
8
+ selectTextblockStart: {
9
+ /**
10
+ * Moves the cursor to the start of current text block.
11
+ */
12
+ selectTextblockStart: () => ReturnType,
13
+ }
14
+ }
15
+ }
16
+
17
+ export const selectTextblockStart: RawCommands['selectTextblockStart'] = () => ({ state, dispatch }) => {
18
+ return originalSelectTextblockStart(state, dispatch)
19
+ }
@@ -28,6 +28,8 @@ import * as selectAll from '../commands/selectAll'
28
28
  import * as selectNodeBackward from '../commands/selectNodeBackward'
29
29
  import * as selectNodeForward from '../commands/selectNodeForward'
30
30
  import * as selectParentNode from '../commands/selectParentNode'
31
+ import * as selectTextblockEnd from '../commands/selectTextblockEnd'
32
+ import * as selectTextblockStart from '../commands/selectTextblockStart'
31
33
  import * as setContent from '../commands/setContent'
32
34
  import * as setMark from '../commands/setMark'
33
35
  import * as setMeta from '../commands/setMeta'
@@ -77,6 +79,8 @@ export { selectAll }
77
79
  export { selectNodeBackward }
78
80
  export { selectNodeForward }
79
81
  export { selectParentNode }
82
+ export { selectTextblockEnd }
83
+ export { selectTextblockStart }
80
84
  export { setContent }
81
85
  export { setMark }
82
86
  export { setMeta }
@@ -131,6 +135,8 @@ export const Commands = Extension.create({
131
135
  ...selectNodeBackward,
132
136
  ...selectNodeForward,
133
137
  ...selectParentNode,
138
+ ...selectTextblockEnd,
139
+ ...selectTextblockStart,
134
140
  ...setContent,
135
141
  ...setMark,
136
142
  ...setMeta,
@@ -1,5 +1,7 @@
1
1
  import { Plugin, PluginKey, Selection } from 'prosemirror-state'
2
2
  import { createChainableState } from '../helpers/createChainableState'
3
+ import { isiOS } from '../utilities/isiOS'
4
+ import { isMacOS } from '../utilities/isMacOS'
3
5
  import { CommandManager } from '../CommandManager'
4
6
  import { Extension } from '../Extension'
5
7
 
@@ -38,13 +40,15 @@ export const Keymap = Extension.create({
38
40
  () => commands.selectNodeForward(),
39
41
  ])
40
42
 
41
- return {
42
- Enter: () => this.editor.commands.first(({ commands }) => [
43
- () => commands.newlineInCode(),
44
- () => commands.createParagraphNear(),
45
- () => commands.liftEmptyBlock(),
46
- () => commands.splitBlock(),
47
- ]),
43
+ const handleEnter = () => this.editor.commands.first(({ commands }) => [
44
+ () => commands.newlineInCode(),
45
+ () => commands.createParagraphNear(),
46
+ () => commands.liftEmptyBlock(),
47
+ () => commands.splitBlock(),
48
+ ])
49
+
50
+ const baseKeymap = {
51
+ Enter: handleEnter,
48
52
  'Mod-Enter': () => this.editor.commands.exitCode(),
49
53
  Backspace: handleBackspace,
50
54
  'Mod-Backspace': handleBackspace,
@@ -53,6 +57,30 @@ export const Keymap = Extension.create({
53
57
  'Mod-Delete': handleDelete,
54
58
  'Mod-a': () => this.editor.commands.selectAll(),
55
59
  }
60
+
61
+ const pcKeymap = {
62
+ ...baseKeymap,
63
+ Home: () => this.editor.commands.selectTextblockStart(),
64
+ End: () => this.editor.commands.selectTextblockStart(),
65
+ }
66
+
67
+ const macKeymap = {
68
+ ...baseKeymap,
69
+ 'Ctrl-h': handleBackspace,
70
+ 'Alt-Backspace': handleBackspace,
71
+ 'Ctrl-d': handleDelete,
72
+ 'Ctrl-Alt-Backspace': handleDelete,
73
+ 'Alt-Delete': handleDelete,
74
+ 'Alt-d': handleDelete,
75
+ 'Ctrl-a': () => this.editor.commands.selectTextblockStart(),
76
+ 'Ctrl-e': () => this.editor.commands.selectTextblockEnd(),
77
+ }
78
+
79
+ if (isiOS() || isMacOS()) {
80
+ return macKeymap
81
+ }
82
+
83
+ return pcKeymap
56
84
  },
57
85
 
58
86
  addProseMirrorPlugins() {
@@ -0,0 +1,5 @@
1
+ export function isMacOS(): boolean {
2
+ return typeof navigator !== 'undefined'
3
+ ? /Mac/.test(navigator.platform)
4
+ : false
5
+ }