@tiptap/core 3.0.0-next.3 → 3.0.0-next.5
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/LICENSE.md +21 -0
- package/README.md +5 -1
- package/dist/index.cjs +2627 -2651
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2423 -2688
- package/dist/index.d.ts +2423 -2688
- package/dist/index.js +2639 -2684
- package/dist/index.js.map +1 -1
- package/dist/jsx-runtime/jsx-runtime.cjs +56 -0
- package/dist/jsx-runtime/jsx-runtime.cjs.map +1 -0
- package/dist/jsx-runtime/jsx-runtime.d.cts +22 -0
- package/dist/jsx-runtime/jsx-runtime.d.ts +22 -0
- package/dist/jsx-runtime/jsx-runtime.js +26 -0
- package/dist/jsx-runtime/jsx-runtime.js.map +1 -0
- package/jsx-runtime/index.cjs +1 -0
- package/jsx-runtime/index.d.cts +1 -0
- package/jsx-runtime/index.d.ts +1 -0
- package/jsx-runtime/index.js +1 -0
- package/package.json +27 -6
- package/src/CommandManager.ts +2 -9
- package/src/Editor.ts +191 -94
- package/src/EventEmitter.ts +7 -10
- package/src/Extendable.ts +483 -0
- package/src/Extension.ts +5 -496
- package/src/ExtensionManager.ts +81 -135
- package/src/InputRule.ts +35 -48
- package/src/Mark.ts +135 -623
- package/src/MarkView.ts +66 -0
- package/src/Node.ts +325 -829
- package/src/NodePos.ts +1 -3
- package/src/NodeView.ts +10 -20
- package/src/PasteRule.ts +43 -55
- package/src/Tracker.ts +7 -9
- package/src/commands/blur.ts +14 -12
- package/src/commands/clearContent.ts +12 -5
- package/src/commands/clearNodes.ts +32 -30
- package/src/commands/command.ts +1 -1
- package/src/commands/createParagraphNear.ts +5 -3
- package/src/commands/cut.ts +12 -10
- package/src/commands/deleteCurrentNode.ts +23 -21
- package/src/commands/deleteNode.ts +18 -16
- package/src/commands/deleteRange.ts +10 -8
- package/src/commands/deleteSelection.ts +5 -3
- package/src/commands/enter.ts +6 -4
- package/src/commands/exitCode.ts +5 -3
- package/src/commands/extendMarkRange.ts +14 -12
- package/src/commands/first.ts +2 -4
- package/src/commands/focus.ts +51 -48
- package/src/commands/forEach.ts +2 -2
- package/src/commands/insertContent.ts +12 -14
- package/src/commands/insertContentAt.ts +105 -98
- package/src/commands/join.ts +20 -12
- package/src/commands/joinItemBackward.ts +16 -18
- package/src/commands/joinItemForward.ts +16 -18
- package/src/commands/joinTextblockBackward.ts +5 -3
- package/src/commands/joinTextblockForward.ts +5 -3
- package/src/commands/keyboardShortcut.ts +29 -34
- package/src/commands/lift.ts +10 -8
- package/src/commands/liftEmptyBlock.ts +6 -4
- package/src/commands/liftListItem.ts +6 -4
- package/src/commands/newlineInCode.ts +5 -3
- package/src/commands/resetAttributes.ts +36 -41
- package/src/commands/scrollIntoView.ts +9 -7
- package/src/commands/selectAll.ts +10 -8
- package/src/commands/selectNodeBackward.ts +5 -3
- package/src/commands/selectNodeForward.ts +5 -3
- package/src/commands/selectParentNode.ts +5 -3
- package/src/commands/selectTextblockEnd.ts +5 -3
- package/src/commands/selectTextblockStart.ts +5 -3
- package/src/commands/setContent.ts +37 -36
- package/src/commands/setMark.ts +55 -57
- package/src/commands/setMeta.ts +7 -5
- package/src/commands/setNode.ts +32 -30
- package/src/commands/setNodeSelection.ts +11 -9
- package/src/commands/setTextSelection.ts +15 -13
- package/src/commands/sinkListItem.ts +6 -4
- package/src/commands/splitBlock.ts +67 -76
- package/src/commands/splitListItem.ts +93 -106
- package/src/commands/toggleList.ts +73 -71
- package/src/commands/toggleMark.ts +11 -9
- package/src/commands/toggleNode.ts +18 -16
- package/src/commands/toggleWrap.ts +10 -8
- package/src/commands/undoInputRule.ts +31 -29
- package/src/commands/unsetAllMarks.ts +16 -14
- package/src/commands/unsetMark.ts +27 -25
- package/src/commands/updateAttributes.ts +92 -100
- package/src/commands/wrapIn.ts +6 -4
- package/src/commands/wrapInList.ts +6 -4
- package/src/extensions/clipboardTextSerializer.ts +2 -4
- package/src/extensions/delete.ts +89 -0
- package/src/extensions/focusEvents.ts +2 -6
- package/src/extensions/index.ts +1 -0
- package/src/extensions/keymap.ts +58 -50
- package/src/extensions/paste.ts +0 -1
- package/src/extensions/tabindex.ts +1 -1
- package/src/helpers/combineTransactionSteps.ts +1 -4
- package/src/helpers/createChainableState.ts +1 -4
- package/src/helpers/createDocument.ts +1 -3
- package/src/helpers/createNodeFromContent.ts +4 -10
- package/src/helpers/findChildrenInRange.ts +1 -5
- package/src/helpers/findParentNode.ts +3 -1
- package/src/helpers/flattenExtensions.ts +30 -0
- package/src/helpers/getAttributes.ts +1 -4
- package/src/helpers/getAttributesFromExtensions.ts +28 -37
- package/src/helpers/getChangedRanges.ts +13 -11
- package/src/helpers/getExtensionField.ts +11 -11
- package/src/helpers/getMarkAttributes.ts +1 -4
- package/src/helpers/getMarkRange.ts +5 -15
- package/src/helpers/getMarkType.ts +1 -3
- package/src/helpers/getNodeAttributes.ts +1 -4
- package/src/helpers/getNodeType.ts +1 -3
- package/src/helpers/getRenderedAttributes.ts +1 -3
- package/src/helpers/getSchema.ts +2 -2
- package/src/helpers/getSchemaByResolvedExtensions.ts +45 -77
- package/src/helpers/getSplittedAttributes.ts +4 -4
- package/src/helpers/getTextContentFromNodes.ts +8 -11
- package/src/helpers/index.ts +4 -0
- package/src/helpers/injectExtensionAttributesToParseRule.ts +1 -1
- package/src/helpers/isActive.ts +1 -5
- package/src/helpers/isExtensionRulesEnabled.ts +1 -3
- package/src/helpers/isNodeEmpty.ts +2 -2
- package/src/helpers/resolveExtensions.ts +25 -0
- package/src/helpers/resolveFocusPosition.ts +3 -14
- package/src/helpers/rewriteUnknownContent.ts +149 -0
- package/src/helpers/sortExtensions.ts +26 -0
- package/src/index.ts +3 -7
- package/src/inputRules/markInputRule.ts +1 -5
- package/src/inputRules/nodeInputRule.ts +2 -9
- package/src/inputRules/textInputRule.ts +1 -4
- package/src/inputRules/textblockTypeInputRule.ts +2 -8
- package/src/inputRules/wrappingInputRule.ts +13 -19
- package/src/jsx-runtime.ts +64 -0
- package/src/pasteRules/markPasteRule.ts +1 -3
- package/src/pasteRules/nodePasteRule.ts +2 -8
- package/src/pasteRules/textPasteRule.ts +1 -4
- package/src/types.ts +529 -174
- package/src/utilities/createStyleTag.ts +3 -1
- package/src/utilities/deleteProps.ts +7 -11
- package/src/utilities/elementFromString.ts +3 -0
- package/src/utilities/findDuplicates.ts +4 -1
- package/src/utilities/index.ts +1 -0
- package/src/utilities/isFunction.ts +1 -0
- package/src/utilities/isMacOS.ts +1 -3
- package/src/utilities/isiOS.ts +5 -10
- package/src/utilities/mergeAttributes.ts +17 -7
- package/src/utilities/removeDuplicates.ts +1 -3
package/src/Editor.ts
CHANGED
|
@@ -1,19 +1,20 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
NodeType,
|
|
5
|
-
Schema,
|
|
6
|
-
} from '@tiptap/pm/model'
|
|
7
|
-
import {
|
|
8
|
-
EditorState, Plugin, PluginKey, Transaction,
|
|
9
|
-
} from '@tiptap/pm/state'
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-empty-object-type */
|
|
2
|
+
import { MarkType, Node as ProseMirrorNode, NodeType, Schema } from '@tiptap/pm/model'
|
|
3
|
+
import { EditorState, Plugin, PluginKey, Transaction } from '@tiptap/pm/state'
|
|
10
4
|
import { EditorView } from '@tiptap/pm/view'
|
|
11
5
|
|
|
12
6
|
import { CommandManager } from './CommandManager.js'
|
|
13
7
|
import { EventEmitter } from './EventEmitter.js'
|
|
14
8
|
import { ExtensionManager } from './ExtensionManager.js'
|
|
15
9
|
import {
|
|
16
|
-
ClipboardTextSerializer,
|
|
10
|
+
ClipboardTextSerializer,
|
|
11
|
+
Commands,
|
|
12
|
+
Delete,
|
|
13
|
+
Drop,
|
|
14
|
+
Editable,
|
|
15
|
+
FocusEvents,
|
|
16
|
+
Keymap,
|
|
17
|
+
Paste,
|
|
17
18
|
Tabindex,
|
|
18
19
|
} from './extensions/index.js'
|
|
19
20
|
import { createDocument } from './helpers/createDocument.js'
|
|
@@ -24,16 +25,19 @@ import { getTextSerializersFromSchema } from './helpers/getTextSerializersFromSc
|
|
|
24
25
|
import { isActive } from './helpers/isActive.js'
|
|
25
26
|
import { isNodeEmpty } from './helpers/isNodeEmpty.js'
|
|
26
27
|
import { resolveFocusPosition } from './helpers/resolveFocusPosition.js'
|
|
28
|
+
import type { Storage } from './index.js'
|
|
27
29
|
import { NodePos } from './NodePos.js'
|
|
28
30
|
import { style } from './style.js'
|
|
29
31
|
import {
|
|
30
32
|
CanCommands,
|
|
31
33
|
ChainedCommands,
|
|
34
|
+
DocumentType,
|
|
32
35
|
EditorEvents,
|
|
33
36
|
EditorOptions,
|
|
34
|
-
|
|
37
|
+
NodeType as TNodeType,
|
|
35
38
|
SingleCommands,
|
|
36
39
|
TextSerializer,
|
|
40
|
+
TextType as TTextType,
|
|
37
41
|
} from './types.js'
|
|
38
42
|
import { createStyleTag } from './utilities/createStyleTag.js'
|
|
39
43
|
import { isFunction } from './utilities/isFunction.js'
|
|
@@ -54,16 +58,18 @@ export class Editor extends EventEmitter<EditorEvents> {
|
|
|
54
58
|
|
|
55
59
|
public schema!: Schema
|
|
56
60
|
|
|
57
|
-
|
|
61
|
+
private editorView: EditorView | null = null
|
|
58
62
|
|
|
59
63
|
public isFocused = false
|
|
60
64
|
|
|
65
|
+
private editorState!: EditorState
|
|
66
|
+
|
|
61
67
|
/**
|
|
62
68
|
* The editor is considered initialized after the `create` event has been emitted.
|
|
63
69
|
*/
|
|
64
70
|
public isInitialized = false
|
|
65
71
|
|
|
66
|
-
public extensionStorage:
|
|
72
|
+
public extensionStorage: Storage = {} as Storage
|
|
67
73
|
|
|
68
74
|
/**
|
|
69
75
|
* A unique ID for this editor instance.
|
|
@@ -71,7 +77,7 @@ export class Editor extends EventEmitter<EditorEvents> {
|
|
|
71
77
|
public instanceId = Math.random().toString(36).slice(2, 9)
|
|
72
78
|
|
|
73
79
|
public options: EditorOptions = {
|
|
74
|
-
element: document.createElement('div'),
|
|
80
|
+
element: typeof document !== 'undefined' ? document.createElement('div') : null,
|
|
75
81
|
content: '',
|
|
76
82
|
injectCSS: true,
|
|
77
83
|
injectNonce: undefined,
|
|
@@ -93,9 +99,12 @@ export class Editor extends EventEmitter<EditorEvents> {
|
|
|
93
99
|
onFocus: () => null,
|
|
94
100
|
onBlur: () => null,
|
|
95
101
|
onDestroy: () => null,
|
|
96
|
-
onContentError: ({ error }) => {
|
|
102
|
+
onContentError: ({ error }) => {
|
|
103
|
+
throw error
|
|
104
|
+
},
|
|
97
105
|
onPaste: () => null,
|
|
98
106
|
onDrop: () => null,
|
|
107
|
+
onDelete: () => null,
|
|
99
108
|
}
|
|
100
109
|
|
|
101
110
|
constructor(options: Partial<EditorOptions> = {}) {
|
|
@@ -107,8 +116,6 @@ export class Editor extends EventEmitter<EditorEvents> {
|
|
|
107
116
|
this.on('beforeCreate', this.options.onBeforeCreate)
|
|
108
117
|
this.emit('beforeCreate', { editor: this })
|
|
109
118
|
this.on('contentError', this.options.onContentError)
|
|
110
|
-
this.createView()
|
|
111
|
-
this.injectCSS()
|
|
112
119
|
this.on('create', this.options.onCreate)
|
|
113
120
|
this.on('update', this.options.onUpdate)
|
|
114
121
|
this.on('selectionUpdate', this.options.onSelectionUpdate)
|
|
@@ -118,6 +125,30 @@ export class Editor extends EventEmitter<EditorEvents> {
|
|
|
118
125
|
this.on('destroy', this.options.onDestroy)
|
|
119
126
|
this.on('drop', ({ event, slice, moved }) => this.options.onDrop(event, slice, moved))
|
|
120
127
|
this.on('paste', ({ event, slice }) => this.options.onPaste(event, slice))
|
|
128
|
+
this.on('delete', this.options.onDelete)
|
|
129
|
+
|
|
130
|
+
const initialDoc = this.createDoc()
|
|
131
|
+
const selection = resolveFocusPosition(initialDoc, this.options.autofocus)
|
|
132
|
+
|
|
133
|
+
// Set editor state immediately, so that it's available independently from the view
|
|
134
|
+
this.editorState = EditorState.create({
|
|
135
|
+
doc: initialDoc,
|
|
136
|
+
schema: this.schema,
|
|
137
|
+
selection: selection || undefined,
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
if (this.options.element) {
|
|
141
|
+
this.mount(this.options.element)
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
public mount(el: NonNullable<EditorOptions['element']> & {}) {
|
|
146
|
+
if (typeof document === 'undefined') {
|
|
147
|
+
throw new Error(
|
|
148
|
+
`[tiptap error]: The editor cannot be mounted because there is no 'document' defined in this environment.`,
|
|
149
|
+
)
|
|
150
|
+
}
|
|
151
|
+
this.createView(el)
|
|
121
152
|
|
|
122
153
|
window.setTimeout(() => {
|
|
123
154
|
if (this.isDestroyed) {
|
|
@@ -133,7 +164,7 @@ export class Editor extends EventEmitter<EditorEvents> {
|
|
|
133
164
|
/**
|
|
134
165
|
* Returns the editor storage.
|
|
135
166
|
*/
|
|
136
|
-
public get storage():
|
|
167
|
+
public get storage(): Storage {
|
|
137
168
|
return this.extensionStorage
|
|
138
169
|
}
|
|
139
170
|
|
|
@@ -162,7 +193,7 @@ export class Editor extends EventEmitter<EditorEvents> {
|
|
|
162
193
|
* Inject CSS styles.
|
|
163
194
|
*/
|
|
164
195
|
private injectCSS(): void {
|
|
165
|
-
if (this.options.injectCSS && document) {
|
|
196
|
+
if (this.options.injectCSS && typeof document !== 'undefined') {
|
|
166
197
|
this.css = createStyleTag(style, this.options.injectNonce)
|
|
167
198
|
}
|
|
168
199
|
}
|
|
@@ -178,7 +209,7 @@ export class Editor extends EventEmitter<EditorEvents> {
|
|
|
178
209
|
...options,
|
|
179
210
|
}
|
|
180
211
|
|
|
181
|
-
if (!this.
|
|
212
|
+
if (!this.editorView || !this.state || this.isDestroyed) {
|
|
182
213
|
return
|
|
183
214
|
}
|
|
184
215
|
|
|
@@ -196,7 +227,7 @@ export class Editor extends EventEmitter<EditorEvents> {
|
|
|
196
227
|
this.setOptions({ editable })
|
|
197
228
|
|
|
198
229
|
if (emitUpdate) {
|
|
199
|
-
this.emit('update', { editor: this, transaction: this.state.tr })
|
|
230
|
+
this.emit('update', { editor: this, transaction: this.state.tr, appendedTransactions: [] })
|
|
200
231
|
}
|
|
201
232
|
}
|
|
202
233
|
|
|
@@ -210,11 +241,57 @@ export class Editor extends EventEmitter<EditorEvents> {
|
|
|
210
241
|
return this.options.editable && this.view && this.view.editable
|
|
211
242
|
}
|
|
212
243
|
|
|
244
|
+
/**
|
|
245
|
+
* Returns the editor state.
|
|
246
|
+
*/
|
|
247
|
+
public get view(): EditorView {
|
|
248
|
+
if (this.editorView) {
|
|
249
|
+
return this.editorView
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return new Proxy(
|
|
253
|
+
{
|
|
254
|
+
state: this.editorState,
|
|
255
|
+
updateState: (state: EditorState): ReturnType<EditorView['updateState']> => {
|
|
256
|
+
this.editorState = state
|
|
257
|
+
},
|
|
258
|
+
dispatch: (tr: Transaction): ReturnType<EditorView['dispatch']> => {
|
|
259
|
+
this.editorState = this.state.apply(tr)
|
|
260
|
+
},
|
|
261
|
+
|
|
262
|
+
// Stub some commonly accessed properties to prevent errors
|
|
263
|
+
composing: false,
|
|
264
|
+
dragging: null,
|
|
265
|
+
editable: true,
|
|
266
|
+
} as EditorView,
|
|
267
|
+
{
|
|
268
|
+
get: (obj, key) => {
|
|
269
|
+
// Specifically always return the most recent editorState
|
|
270
|
+
if (key === 'state') {
|
|
271
|
+
return this.editorState
|
|
272
|
+
}
|
|
273
|
+
if (key in obj) {
|
|
274
|
+
return Reflect.get(obj, key)
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// We throw an error here, because we know the view is not available
|
|
278
|
+
throw new Error(
|
|
279
|
+
`[tiptap error]: The editor view is not available. Cannot access view['${key as string}']. The editor may not be mounted yet.`,
|
|
280
|
+
)
|
|
281
|
+
},
|
|
282
|
+
},
|
|
283
|
+
) as EditorView
|
|
284
|
+
}
|
|
285
|
+
|
|
213
286
|
/**
|
|
214
287
|
* Returns the editor state.
|
|
215
288
|
*/
|
|
216
289
|
public get state(): EditorState {
|
|
217
|
-
|
|
290
|
+
if (this.editorView) {
|
|
291
|
+
this.editorState = this.view.state
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
return this.editorState
|
|
218
295
|
}
|
|
219
296
|
|
|
220
297
|
/**
|
|
@@ -245,15 +322,17 @@ export class Editor extends EventEmitter<EditorEvents> {
|
|
|
245
322
|
* @param nameOrPluginKeyToRemove The plugins name
|
|
246
323
|
* @returns The new editor state or undefined if the editor is destroyed
|
|
247
324
|
*/
|
|
248
|
-
public unregisterPlugin(
|
|
325
|
+
public unregisterPlugin(
|
|
326
|
+
nameOrPluginKeyToRemove: string | PluginKey | (string | PluginKey)[],
|
|
327
|
+
): EditorState | undefined {
|
|
249
328
|
if (this.isDestroyed) {
|
|
250
329
|
return undefined
|
|
251
330
|
}
|
|
252
331
|
|
|
253
332
|
const prevPlugins = this.state.plugins
|
|
254
|
-
let plugins = prevPlugins
|
|
333
|
+
let plugins = prevPlugins
|
|
255
334
|
|
|
256
|
-
([] as (string | PluginKey)[]).concat(nameOrPluginKeyToRemove).forEach(nameOrPluginKey => {
|
|
335
|
+
;([] as (string | PluginKey)[]).concat(nameOrPluginKeyToRemove).forEach(nameOrPluginKey => {
|
|
257
336
|
// @ts-ignore
|
|
258
337
|
const name = typeof nameOrPluginKey === 'string' ? `${nameOrPluginKey}$` : nameOrPluginKey.key
|
|
259
338
|
|
|
@@ -279,24 +358,28 @@ export class Editor extends EventEmitter<EditorEvents> {
|
|
|
279
358
|
* Creates an extension manager.
|
|
280
359
|
*/
|
|
281
360
|
private createExtensionManager(): void {
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
361
|
+
const coreExtensions = this.options.enableCoreExtensions
|
|
362
|
+
? [
|
|
363
|
+
Editable,
|
|
364
|
+
ClipboardTextSerializer.configure({
|
|
365
|
+
blockSeparator: this.options.coreExtensionOptions?.clipboardTextSerializer?.blockSeparator,
|
|
366
|
+
}),
|
|
367
|
+
Commands,
|
|
368
|
+
FocusEvents,
|
|
369
|
+
Keymap,
|
|
370
|
+
Tabindex,
|
|
371
|
+
Drop,
|
|
372
|
+
Paste,
|
|
373
|
+
Delete,
|
|
374
|
+
].filter(ext => {
|
|
375
|
+
if (typeof this.options.enableCoreExtensions === 'object') {
|
|
376
|
+
return (
|
|
377
|
+
this.options.enableCoreExtensions[ext.name as keyof typeof this.options.enableCoreExtensions] !== false
|
|
378
|
+
)
|
|
379
|
+
}
|
|
380
|
+
return true
|
|
381
|
+
})
|
|
382
|
+
: []
|
|
300
383
|
const allExtensions = [...coreExtensions, ...this.options.extensions].filter(extension => {
|
|
301
384
|
return ['extension', 'node', 'mark'].includes(extension?.type)
|
|
302
385
|
})
|
|
@@ -321,20 +404,20 @@ export class Editor extends EventEmitter<EditorEvents> {
|
|
|
321
404
|
}
|
|
322
405
|
|
|
323
406
|
/**
|
|
324
|
-
* Creates
|
|
407
|
+
* Creates the initial document.
|
|
325
408
|
*/
|
|
326
|
-
private
|
|
409
|
+
private createDoc(): ProseMirrorNode {
|
|
327
410
|
let doc: ProseMirrorNode
|
|
328
411
|
|
|
329
412
|
try {
|
|
330
|
-
doc = createDocument(
|
|
331
|
-
this.options.
|
|
332
|
-
|
|
333
|
-
this.options.parseOptions,
|
|
334
|
-
{ errorOnInvalidContent: this.options.enableContentCheck },
|
|
335
|
-
)
|
|
413
|
+
doc = createDocument(this.options.content, this.schema, this.options.parseOptions, {
|
|
414
|
+
errorOnInvalidContent: this.options.enableContentCheck,
|
|
415
|
+
})
|
|
336
416
|
} catch (e) {
|
|
337
|
-
if (
|
|
417
|
+
if (
|
|
418
|
+
!(e instanceof Error) ||
|
|
419
|
+
!['[tiptap error]: Invalid JSON content', '[tiptap error]: Invalid HTML content'].includes(e.message)
|
|
420
|
+
) {
|
|
338
421
|
// Not the content error we were expecting
|
|
339
422
|
throw e
|
|
340
423
|
}
|
|
@@ -342,8 +425,12 @@ export class Editor extends EventEmitter<EditorEvents> {
|
|
|
342
425
|
editor: this,
|
|
343
426
|
error: e as Error,
|
|
344
427
|
disableCollaboration: () => {
|
|
345
|
-
if (
|
|
346
|
-
this.storage
|
|
428
|
+
if (
|
|
429
|
+
'collaboration' in this.storage &&
|
|
430
|
+
typeof this.storage.collaboration === 'object' &&
|
|
431
|
+
this.storage.collaboration
|
|
432
|
+
) {
|
|
433
|
+
;(this.storage.collaboration as any).isDisabled = true
|
|
347
434
|
}
|
|
348
435
|
// To avoid syncing back invalid content, reinitialize the extensions without the collaboration extension
|
|
349
436
|
this.options.extensions = this.options.extensions.filter(extension => extension.name !== 'collaboration')
|
|
@@ -354,16 +441,18 @@ export class Editor extends EventEmitter<EditorEvents> {
|
|
|
354
441
|
})
|
|
355
442
|
|
|
356
443
|
// Content is invalid, but attempt to create it anyway, stripping out the invalid parts
|
|
357
|
-
doc = createDocument(
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
this.options.parseOptions,
|
|
361
|
-
{ errorOnInvalidContent: false },
|
|
362
|
-
)
|
|
444
|
+
doc = createDocument(this.options.content, this.schema, this.options.parseOptions, {
|
|
445
|
+
errorOnInvalidContent: false,
|
|
446
|
+
})
|
|
363
447
|
}
|
|
364
|
-
|
|
448
|
+
return doc
|
|
449
|
+
}
|
|
365
450
|
|
|
366
|
-
|
|
451
|
+
/**
|
|
452
|
+
* Creates a ProseMirror view.
|
|
453
|
+
*/
|
|
454
|
+
private createView(element: NonNullable<EditorOptions['element']> & {}): void {
|
|
455
|
+
this.editorView = new EditorView(element, {
|
|
367
456
|
...this.options.editorProps,
|
|
368
457
|
attributes: {
|
|
369
458
|
// add `role="textbox"` to the editor element
|
|
@@ -371,10 +460,7 @@ export class Editor extends EventEmitter<EditorEvents> {
|
|
|
371
460
|
...this.options.editorProps?.attributes,
|
|
372
461
|
},
|
|
373
462
|
dispatchTransaction: this.dispatchTransaction.bind(this),
|
|
374
|
-
state:
|
|
375
|
-
doc,
|
|
376
|
-
selection: selection || undefined,
|
|
377
|
-
}),
|
|
463
|
+
state: this.editorState,
|
|
378
464
|
})
|
|
379
465
|
|
|
380
466
|
// `editor.view` is not yet available at this time.
|
|
@@ -387,6 +473,7 @@ export class Editor extends EventEmitter<EditorEvents> {
|
|
|
387
473
|
|
|
388
474
|
this.createNodeViews()
|
|
389
475
|
this.prependClass()
|
|
476
|
+
this.injectCSS()
|
|
390
477
|
|
|
391
478
|
// Let’s store the editor instance in the DOM element.
|
|
392
479
|
// So we’ll have access to it for tests.
|
|
@@ -405,6 +492,7 @@ export class Editor extends EventEmitter<EditorEvents> {
|
|
|
405
492
|
}
|
|
406
493
|
|
|
407
494
|
this.view.setProps({
|
|
495
|
+
markViews: this.extensionManager.markViews,
|
|
408
496
|
nodeViews: this.extensionManager.nodeViews,
|
|
409
497
|
})
|
|
410
498
|
}
|
|
@@ -420,7 +508,7 @@ export class Editor extends EventEmitter<EditorEvents> {
|
|
|
420
508
|
|
|
421
509
|
private capturedTransaction: Transaction | null = null
|
|
422
510
|
|
|
423
|
-
public captureTransaction(fn:
|
|
511
|
+
public captureTransaction(fn: () => void) {
|
|
424
512
|
this.isCapturingTransaction = true
|
|
425
513
|
fn()
|
|
426
514
|
this.isCapturingTransaction = false
|
|
@@ -456,18 +544,30 @@ export class Editor extends EventEmitter<EditorEvents> {
|
|
|
456
544
|
return
|
|
457
545
|
}
|
|
458
546
|
|
|
459
|
-
|
|
547
|
+
// Apply transaction and get resulting state and transactions
|
|
548
|
+
const { state, transactions } = this.state.applyTransaction(transaction)
|
|
460
549
|
const selectionHasChanged = !this.state.selection.eq(state.selection)
|
|
550
|
+
const rootTrWasApplied = transactions.includes(transaction)
|
|
551
|
+
const prevState = this.state
|
|
461
552
|
|
|
462
553
|
this.emit('beforeTransaction', {
|
|
463
554
|
editor: this,
|
|
464
555
|
transaction,
|
|
465
556
|
nextState: state,
|
|
466
557
|
})
|
|
558
|
+
|
|
559
|
+
// If transaction was filtered out, we can return early
|
|
560
|
+
if (!rootTrWasApplied) {
|
|
561
|
+
return
|
|
562
|
+
}
|
|
563
|
+
|
|
467
564
|
this.view.updateState(state)
|
|
565
|
+
|
|
566
|
+
// Emit transaction event with appended transactions info
|
|
468
567
|
this.emit('transaction', {
|
|
469
568
|
editor: this,
|
|
470
569
|
transaction,
|
|
570
|
+
appendedTransactions: transactions.slice(1),
|
|
471
571
|
})
|
|
472
572
|
|
|
473
573
|
if (selectionHasChanged) {
|
|
@@ -477,14 +577,17 @@ export class Editor extends EventEmitter<EditorEvents> {
|
|
|
477
577
|
})
|
|
478
578
|
}
|
|
479
579
|
|
|
480
|
-
|
|
481
|
-
const
|
|
580
|
+
// Only emit the latest between focus and blur events
|
|
581
|
+
const mostRecentFocusTr = transactions.findLast(tr => tr.getMeta('focus') || tr.getMeta('blur'))
|
|
582
|
+
const focus = mostRecentFocusTr?.getMeta('focus')
|
|
583
|
+
const blur = mostRecentFocusTr?.getMeta('blur')
|
|
482
584
|
|
|
483
585
|
if (focus) {
|
|
484
586
|
this.emit('focus', {
|
|
485
587
|
editor: this,
|
|
486
588
|
event: focus.event,
|
|
487
|
-
|
|
589
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
590
|
+
transaction: mostRecentFocusTr!,
|
|
488
591
|
})
|
|
489
592
|
}
|
|
490
593
|
|
|
@@ -492,17 +595,24 @@ export class Editor extends EventEmitter<EditorEvents> {
|
|
|
492
595
|
this.emit('blur', {
|
|
493
596
|
editor: this,
|
|
494
597
|
event: blur.event,
|
|
495
|
-
|
|
598
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
599
|
+
transaction: mostRecentFocusTr!,
|
|
496
600
|
})
|
|
497
601
|
}
|
|
498
602
|
|
|
499
|
-
|
|
603
|
+
// Compare states for update event
|
|
604
|
+
if (
|
|
605
|
+
transaction.getMeta('preventUpdate') ||
|
|
606
|
+
!transactions.some(tr => tr.docChanged) ||
|
|
607
|
+
prevState.doc.eq(state.doc)
|
|
608
|
+
) {
|
|
500
609
|
return
|
|
501
610
|
}
|
|
502
611
|
|
|
503
612
|
this.emit('update', {
|
|
504
613
|
editor: this,
|
|
505
614
|
transaction,
|
|
615
|
+
appendedTransactions: transactions.slice(1),
|
|
506
616
|
})
|
|
507
617
|
}
|
|
508
618
|
|
|
@@ -532,7 +642,10 @@ export class Editor extends EventEmitter<EditorEvents> {
|
|
|
532
642
|
/**
|
|
533
643
|
* Get the document as JSON.
|
|
534
644
|
*/
|
|
535
|
-
public getJSON():
|
|
645
|
+
public getJSON(): DocumentType<
|
|
646
|
+
Record<string, any> | undefined,
|
|
647
|
+
TNodeType<string, undefined | Record<string, any>, any, (TNodeType | TTextType)[]>[]
|
|
648
|
+
> {
|
|
536
649
|
return this.state.doc.toJSON()
|
|
537
650
|
}
|
|
538
651
|
|
|
@@ -546,10 +659,7 @@ export class Editor extends EventEmitter<EditorEvents> {
|
|
|
546
659
|
/**
|
|
547
660
|
* Get the document as text.
|
|
548
661
|
*/
|
|
549
|
-
public getText(options?: {
|
|
550
|
-
blockSeparator?: string
|
|
551
|
-
textSerializers?: Record<string, TextSerializer>
|
|
552
|
-
}): string {
|
|
662
|
+
public getText(options?: { blockSeparator?: string; textSerializers?: Record<string, TextSerializer> }): string {
|
|
553
663
|
const { blockSeparator = '\n\n', textSerializers = {} } = options || {}
|
|
554
664
|
|
|
555
665
|
return getText(this.state.doc, {
|
|
@@ -568,34 +678,21 @@ export class Editor extends EventEmitter<EditorEvents> {
|
|
|
568
678
|
return isNodeEmpty(this.state.doc)
|
|
569
679
|
}
|
|
570
680
|
|
|
571
|
-
/**
|
|
572
|
-
* Get the number of characters for the current document.
|
|
573
|
-
*
|
|
574
|
-
* @deprecated
|
|
575
|
-
*/
|
|
576
|
-
public getCharacterCount(): number {
|
|
577
|
-
console.warn(
|
|
578
|
-
'[tiptap warn]: "editor.getCharacterCount()" is deprecated. Please use "editor.storage.characterCount.characters()" instead.',
|
|
579
|
-
)
|
|
580
|
-
|
|
581
|
-
return this.state.doc.content.size - 2
|
|
582
|
-
}
|
|
583
|
-
|
|
584
681
|
/**
|
|
585
682
|
* Destroy the editor.
|
|
586
683
|
*/
|
|
587
684
|
public destroy(): void {
|
|
588
685
|
this.emit('destroy')
|
|
589
686
|
|
|
590
|
-
if (this.
|
|
687
|
+
if (this.editorView) {
|
|
591
688
|
// Cleanup our reference to prevent circular references which caused memory leaks
|
|
592
689
|
// @ts-ignore
|
|
593
|
-
const dom = this.
|
|
690
|
+
const dom = this.editorView.dom as TiptapEditorHTMLElement
|
|
594
691
|
|
|
595
692
|
if (dom && dom.editor) {
|
|
596
693
|
delete dom.editor
|
|
597
694
|
}
|
|
598
|
-
this.
|
|
695
|
+
this.editorView.destroy()
|
|
599
696
|
}
|
|
600
697
|
|
|
601
698
|
this.removeAllListeners()
|
package/src/EventEmitter.ts
CHANGED
|
@@ -1,16 +1,13 @@
|
|
|
1
1
|
type StringKeyOf<T> = Extract<keyof T, string>
|
|
2
|
-
type CallbackType<
|
|
3
|
-
T
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
EventName extends StringKeyOf<T>,
|
|
9
|
-
> = (...props: CallbackType<T, EventName>) => any
|
|
2
|
+
type CallbackType<T extends Record<string, any>, EventName extends StringKeyOf<T>> = T[EventName] extends any[]
|
|
3
|
+
? T[EventName]
|
|
4
|
+
: [T[EventName]]
|
|
5
|
+
type CallbackFunction<T extends Record<string, any>, EventName extends StringKeyOf<T>> = (
|
|
6
|
+
...props: CallbackType<T, EventName>
|
|
7
|
+
) => any
|
|
10
8
|
|
|
11
9
|
export class EventEmitter<T extends Record<string, any>> {
|
|
12
|
-
|
|
13
|
-
private callbacks: { [key: string]: Function[] } = {}
|
|
10
|
+
private callbacks: { [key: string]: Array<(...args: any[]) => void> } = {}
|
|
14
11
|
|
|
15
12
|
public on<EventName extends StringKeyOf<T>>(event: EventName, fn: CallbackFunction<T, EventName>): this {
|
|
16
13
|
if (!this.callbacks[event]) {
|