@tiptap/core 3.0.0-next.2 → 3.0.0-next.4
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 +2402 -2540
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1355 -1273
- package/dist/index.d.ts +1355 -1273
- package/dist/index.js +2408 -2562
- package/dist/index.js.map +1 -1
- package/package.json +9 -5
- package/src/CommandManager.ts +2 -9
- package/src/Editor.ts +87 -72
- package/src/EventEmitter.ts +7 -10
- package/src/Extension.ts +8 -14
- package/src/ExtensionManager.ts +26 -125
- package/src/InputRule.ts +35 -48
- package/src/Mark.ts +9 -9
- package/src/Node.ts +9 -9
- 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 +6 -4
- 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 +45 -48
- package/src/commands/forEach.ts +2 -2
- package/src/commands/insertContent.ts +12 -14
- package/src/commands/insertContentAt.ts +101 -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 +25 -25
- 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/focusEvents.ts +2 -6
- package/src/extensions/keymap.ts +54 -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 +1 -4
- 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/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/pasteRules/markPasteRule.ts +1 -3
- package/src/pasteRules/nodePasteRule.ts +2 -8
- package/src/pasteRules/textPasteRule.ts +1 -4
- package/src/types.ts +212 -172
- package/src/utilities/createStyleTag.ts +3 -1
- package/src/utilities/deleteProps.ts +7 -11
- package/src/utilities/findDuplicates.ts +4 -1
- 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 +16 -6
- package/src/utilities/removeDuplicates.ts +1 -3
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tiptap/core",
|
|
3
3
|
"description": "headless rich text editor",
|
|
4
|
-
"version": "3.0.0-next.
|
|
4
|
+
"version": "3.0.0-next.4",
|
|
5
5
|
"homepage": "https://tiptap.dev",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"tiptap",
|
|
@@ -18,7 +18,10 @@
|
|
|
18
18
|
"type": "module",
|
|
19
19
|
"exports": {
|
|
20
20
|
".": {
|
|
21
|
-
"types":
|
|
21
|
+
"types": {
|
|
22
|
+
"import": "./dist/index.d.ts",
|
|
23
|
+
"require": "./dist/index.d.cts"
|
|
24
|
+
},
|
|
22
25
|
"import": "./dist/index.js",
|
|
23
26
|
"require": "./dist/index.cjs"
|
|
24
27
|
}
|
|
@@ -31,7 +34,7 @@
|
|
|
31
34
|
"dist"
|
|
32
35
|
],
|
|
33
36
|
"devDependencies": {
|
|
34
|
-
"@tiptap/pm": "^3.0.0-next.
|
|
37
|
+
"@tiptap/pm": "^3.0.0-next.4"
|
|
35
38
|
},
|
|
36
39
|
"peerDependencies": {
|
|
37
40
|
"@tiptap/pm": "^3.0.0-next.1"
|
|
@@ -43,6 +46,7 @@
|
|
|
43
46
|
},
|
|
44
47
|
"sideEffects": false,
|
|
45
48
|
"scripts": {
|
|
46
|
-
"build": "tsup"
|
|
49
|
+
"build": "tsup",
|
|
50
|
+
"lint": "prettier ./src/ --check && eslint --cache --quiet --no-error-on-unmatched-pattern ./src/"
|
|
47
51
|
}
|
|
48
|
-
}
|
|
52
|
+
}
|
package/src/CommandManager.ts
CHANGED
|
@@ -2,9 +2,7 @@ import { EditorState, Transaction } from '@tiptap/pm/state'
|
|
|
2
2
|
|
|
3
3
|
import { Editor } from './Editor.js'
|
|
4
4
|
import { createChainableState } from './helpers/createChainableState.js'
|
|
5
|
-
import {
|
|
6
|
-
AnyCommands, CanCommands, ChainedCommands, CommandProps, SingleCommands,
|
|
7
|
-
} from './types.js'
|
|
5
|
+
import { AnyCommands, CanCommands, ChainedCommands, CommandProps, SingleCommands } from './types.js'
|
|
8
6
|
|
|
9
7
|
export class CommandManager {
|
|
10
8
|
editor: Editor
|
|
@@ -66,12 +64,7 @@ export class CommandManager {
|
|
|
66
64
|
const tr = startTr || state.tr
|
|
67
65
|
|
|
68
66
|
const run = () => {
|
|
69
|
-
if (
|
|
70
|
-
!hasStartTransaction
|
|
71
|
-
&& shouldDispatch
|
|
72
|
-
&& !tr.getMeta('preventDispatch')
|
|
73
|
-
&& !this.hasCustomState
|
|
74
|
-
) {
|
|
67
|
+
if (!hasStartTransaction && shouldDispatch && !tr.getMeta('preventDispatch') && !this.hasCustomState) {
|
|
75
68
|
view.dispatch(tr)
|
|
76
69
|
}
|
|
77
70
|
|
package/src/Editor.ts
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
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
|
+
Drop,
|
|
13
|
+
Editable,
|
|
14
|
+
FocusEvents,
|
|
15
|
+
Keymap,
|
|
16
|
+
Paste,
|
|
17
17
|
Tabindex,
|
|
18
18
|
} from './extensions/index.js'
|
|
19
19
|
import { createDocument } from './helpers/createDocument.js'
|
|
@@ -29,11 +29,13 @@ import { style } from './style.js'
|
|
|
29
29
|
import {
|
|
30
30
|
CanCommands,
|
|
31
31
|
ChainedCommands,
|
|
32
|
+
DocumentType,
|
|
32
33
|
EditorEvents,
|
|
33
34
|
EditorOptions,
|
|
34
|
-
|
|
35
|
+
NodeType as TNodeType,
|
|
35
36
|
SingleCommands,
|
|
36
37
|
TextSerializer,
|
|
38
|
+
TextType as TTextType,
|
|
37
39
|
} from './types.js'
|
|
38
40
|
import { createStyleTag } from './utilities/createStyleTag.js'
|
|
39
41
|
import { isFunction } from './utilities/isFunction.js'
|
|
@@ -93,7 +95,9 @@ export class Editor extends EventEmitter<EditorEvents> {
|
|
|
93
95
|
onFocus: () => null,
|
|
94
96
|
onBlur: () => null,
|
|
95
97
|
onDestroy: () => null,
|
|
96
|
-
onContentError: ({ error }) => {
|
|
98
|
+
onContentError: ({ error }) => {
|
|
99
|
+
throw error
|
|
100
|
+
},
|
|
97
101
|
onPaste: () => null,
|
|
98
102
|
onDrop: () => null,
|
|
99
103
|
}
|
|
@@ -196,7 +200,7 @@ export class Editor extends EventEmitter<EditorEvents> {
|
|
|
196
200
|
this.setOptions({ editable })
|
|
197
201
|
|
|
198
202
|
if (emitUpdate) {
|
|
199
|
-
this.emit('update', { editor: this, transaction: this.state.tr })
|
|
203
|
+
this.emit('update', { editor: this, transaction: this.state.tr, appendedTransactions: [] })
|
|
200
204
|
}
|
|
201
205
|
}
|
|
202
206
|
|
|
@@ -245,15 +249,17 @@ export class Editor extends EventEmitter<EditorEvents> {
|
|
|
245
249
|
* @param nameOrPluginKeyToRemove The plugins name
|
|
246
250
|
* @returns The new editor state or undefined if the editor is destroyed
|
|
247
251
|
*/
|
|
248
|
-
public unregisterPlugin(
|
|
252
|
+
public unregisterPlugin(
|
|
253
|
+
nameOrPluginKeyToRemove: string | PluginKey | (string | PluginKey)[],
|
|
254
|
+
): EditorState | undefined {
|
|
249
255
|
if (this.isDestroyed) {
|
|
250
256
|
return undefined
|
|
251
257
|
}
|
|
252
258
|
|
|
253
259
|
const prevPlugins = this.state.plugins
|
|
254
|
-
let plugins = prevPlugins
|
|
260
|
+
let plugins = prevPlugins
|
|
255
261
|
|
|
256
|
-
([] as (string | PluginKey)[]).concat(nameOrPluginKeyToRemove).forEach(nameOrPluginKey => {
|
|
262
|
+
;([] as (string | PluginKey)[]).concat(nameOrPluginKeyToRemove).forEach(nameOrPluginKey => {
|
|
257
263
|
// @ts-ignore
|
|
258
264
|
const name = typeof nameOrPluginKey === 'string' ? `${nameOrPluginKey}$` : nameOrPluginKey.key
|
|
259
265
|
|
|
@@ -279,24 +285,27 @@ export class Editor extends EventEmitter<EditorEvents> {
|
|
|
279
285
|
* Creates an extension manager.
|
|
280
286
|
*/
|
|
281
287
|
private createExtensionManager(): void {
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
288
|
+
const coreExtensions = this.options.enableCoreExtensions
|
|
289
|
+
? [
|
|
290
|
+
Editable,
|
|
291
|
+
ClipboardTextSerializer.configure({
|
|
292
|
+
blockSeparator: this.options.coreExtensionOptions?.clipboardTextSerializer?.blockSeparator,
|
|
293
|
+
}),
|
|
294
|
+
Commands,
|
|
295
|
+
FocusEvents,
|
|
296
|
+
Keymap,
|
|
297
|
+
Tabindex,
|
|
298
|
+
Drop,
|
|
299
|
+
Paste,
|
|
300
|
+
].filter(ext => {
|
|
301
|
+
if (typeof this.options.enableCoreExtensions === 'object') {
|
|
302
|
+
return (
|
|
303
|
+
this.options.enableCoreExtensions[ext.name as keyof typeof this.options.enableCoreExtensions] !== false
|
|
304
|
+
)
|
|
305
|
+
}
|
|
306
|
+
return true
|
|
307
|
+
})
|
|
308
|
+
: []
|
|
300
309
|
const allExtensions = [...coreExtensions, ...this.options.extensions].filter(extension => {
|
|
301
310
|
return ['extension', 'node', 'mark'].includes(extension?.type)
|
|
302
311
|
})
|
|
@@ -327,14 +336,14 @@ export class Editor extends EventEmitter<EditorEvents> {
|
|
|
327
336
|
let doc: ProseMirrorNode
|
|
328
337
|
|
|
329
338
|
try {
|
|
330
|
-
doc = createDocument(
|
|
331
|
-
this.options.
|
|
332
|
-
|
|
333
|
-
this.options.parseOptions,
|
|
334
|
-
{ errorOnInvalidContent: this.options.enableContentCheck },
|
|
335
|
-
)
|
|
339
|
+
doc = createDocument(this.options.content, this.schema, this.options.parseOptions, {
|
|
340
|
+
errorOnInvalidContent: this.options.enableContentCheck,
|
|
341
|
+
})
|
|
336
342
|
} catch (e) {
|
|
337
|
-
if (
|
|
343
|
+
if (
|
|
344
|
+
!(e instanceof Error) ||
|
|
345
|
+
!['[tiptap error]: Invalid JSON content', '[tiptap error]: Invalid HTML content'].includes(e.message)
|
|
346
|
+
) {
|
|
338
347
|
// Not the content error we were expecting
|
|
339
348
|
throw e
|
|
340
349
|
}
|
|
@@ -354,12 +363,9 @@ export class Editor extends EventEmitter<EditorEvents> {
|
|
|
354
363
|
})
|
|
355
364
|
|
|
356
365
|
// 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
|
-
)
|
|
366
|
+
doc = createDocument(this.options.content, this.schema, this.options.parseOptions, {
|
|
367
|
+
errorOnInvalidContent: false,
|
|
368
|
+
})
|
|
363
369
|
}
|
|
364
370
|
const selection = resolveFocusPosition(doc, this.options.autofocus)
|
|
365
371
|
|
|
@@ -420,7 +426,7 @@ export class Editor extends EventEmitter<EditorEvents> {
|
|
|
420
426
|
|
|
421
427
|
private capturedTransaction: Transaction | null = null
|
|
422
428
|
|
|
423
|
-
public captureTransaction(fn:
|
|
429
|
+
public captureTransaction(fn: () => void) {
|
|
424
430
|
this.isCapturingTransaction = true
|
|
425
431
|
fn()
|
|
426
432
|
this.isCapturingTransaction = false
|
|
@@ -456,18 +462,30 @@ export class Editor extends EventEmitter<EditorEvents> {
|
|
|
456
462
|
return
|
|
457
463
|
}
|
|
458
464
|
|
|
459
|
-
|
|
465
|
+
// Apply transaction and get resulting state and transactions
|
|
466
|
+
const { state, transactions } = this.state.applyTransaction(transaction)
|
|
460
467
|
const selectionHasChanged = !this.state.selection.eq(state.selection)
|
|
468
|
+
const rootTrWasApplied = transactions.includes(transaction)
|
|
469
|
+
const prevState = this.state
|
|
461
470
|
|
|
462
471
|
this.emit('beforeTransaction', {
|
|
463
472
|
editor: this,
|
|
464
473
|
transaction,
|
|
465
474
|
nextState: state,
|
|
466
475
|
})
|
|
476
|
+
|
|
477
|
+
// If transaction was filtered out, we can return early
|
|
478
|
+
if (!rootTrWasApplied) {
|
|
479
|
+
return
|
|
480
|
+
}
|
|
481
|
+
|
|
467
482
|
this.view.updateState(state)
|
|
483
|
+
|
|
484
|
+
// Emit transaction event with appended transactions info
|
|
468
485
|
this.emit('transaction', {
|
|
469
486
|
editor: this,
|
|
470
487
|
transaction,
|
|
488
|
+
appendedTransactions: transactions.slice(1),
|
|
471
489
|
})
|
|
472
490
|
|
|
473
491
|
if (selectionHasChanged) {
|
|
@@ -477,14 +495,17 @@ export class Editor extends EventEmitter<EditorEvents> {
|
|
|
477
495
|
})
|
|
478
496
|
}
|
|
479
497
|
|
|
480
|
-
|
|
481
|
-
const
|
|
498
|
+
// Only emit the latest between focus and blur events
|
|
499
|
+
const mostRecentFocusTr = transactions.findLast(tr => tr.getMeta('focus') || tr.getMeta('blur'))
|
|
500
|
+
const focus = mostRecentFocusTr?.getMeta('focus')
|
|
501
|
+
const blur = mostRecentFocusTr?.getMeta('blur')
|
|
482
502
|
|
|
483
503
|
if (focus) {
|
|
484
504
|
this.emit('focus', {
|
|
485
505
|
editor: this,
|
|
486
506
|
event: focus.event,
|
|
487
|
-
|
|
507
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
508
|
+
transaction: mostRecentFocusTr!,
|
|
488
509
|
})
|
|
489
510
|
}
|
|
490
511
|
|
|
@@ -492,17 +513,24 @@ export class Editor extends EventEmitter<EditorEvents> {
|
|
|
492
513
|
this.emit('blur', {
|
|
493
514
|
editor: this,
|
|
494
515
|
event: blur.event,
|
|
495
|
-
|
|
516
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
517
|
+
transaction: mostRecentFocusTr!,
|
|
496
518
|
})
|
|
497
519
|
}
|
|
498
520
|
|
|
499
|
-
|
|
521
|
+
// Compare states for update event
|
|
522
|
+
if (
|
|
523
|
+
transaction.getMeta('preventUpdate') ||
|
|
524
|
+
!transactions.some(tr => tr.docChanged) ||
|
|
525
|
+
prevState.doc.eq(state.doc)
|
|
526
|
+
) {
|
|
500
527
|
return
|
|
501
528
|
}
|
|
502
529
|
|
|
503
530
|
this.emit('update', {
|
|
504
531
|
editor: this,
|
|
505
532
|
transaction,
|
|
533
|
+
appendedTransactions: transactions.slice(1),
|
|
506
534
|
})
|
|
507
535
|
}
|
|
508
536
|
|
|
@@ -532,7 +560,10 @@ export class Editor extends EventEmitter<EditorEvents> {
|
|
|
532
560
|
/**
|
|
533
561
|
* Get the document as JSON.
|
|
534
562
|
*/
|
|
535
|
-
public getJSON():
|
|
563
|
+
public getJSON(): DocumentType<
|
|
564
|
+
Record<string, any> | undefined,
|
|
565
|
+
TNodeType<string, undefined | Record<string, any>, any, (TNodeType | TTextType)[]>[]
|
|
566
|
+
> {
|
|
536
567
|
return this.state.doc.toJSON()
|
|
537
568
|
}
|
|
538
569
|
|
|
@@ -546,10 +577,7 @@ export class Editor extends EventEmitter<EditorEvents> {
|
|
|
546
577
|
/**
|
|
547
578
|
* Get the document as text.
|
|
548
579
|
*/
|
|
549
|
-
public getText(options?: {
|
|
550
|
-
blockSeparator?: string
|
|
551
|
-
textSerializers?: Record<string, TextSerializer>
|
|
552
|
-
}): string {
|
|
580
|
+
public getText(options?: { blockSeparator?: string; textSerializers?: Record<string, TextSerializer> }): string {
|
|
553
581
|
const { blockSeparator = '\n\n', textSerializers = {} } = options || {}
|
|
554
582
|
|
|
555
583
|
return getText(this.state.doc, {
|
|
@@ -568,19 +596,6 @@ export class Editor extends EventEmitter<EditorEvents> {
|
|
|
568
596
|
return isNodeEmpty(this.state.doc)
|
|
569
597
|
}
|
|
570
598
|
|
|
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
599
|
/**
|
|
585
600
|
* Destroy the editor.
|
|
586
601
|
*/
|
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]) {
|
package/src/Extension.ts
CHANGED
|
@@ -7,14 +7,7 @@ import { InputRule } from './InputRule.js'
|
|
|
7
7
|
import { Mark } from './Mark.js'
|
|
8
8
|
import { Node } from './Node.js'
|
|
9
9
|
import { PasteRule } from './PasteRule.js'
|
|
10
|
-
import {
|
|
11
|
-
AnyConfig,
|
|
12
|
-
Extensions,
|
|
13
|
-
GlobalAttributes,
|
|
14
|
-
KeyboardShortcutCommand,
|
|
15
|
-
ParentConfig,
|
|
16
|
-
RawCommands,
|
|
17
|
-
} from './types.js'
|
|
10
|
+
import { AnyConfig, Extensions, GlobalAttributes, KeyboardShortcutCommand, ParentConfig, RawCommands } from './types.js'
|
|
18
11
|
import { callOrReturn } from './utilities/callOrReturn.js'
|
|
19
12
|
import { mergeDeep } from './utilities/mergeDeep.js'
|
|
20
13
|
|
|
@@ -443,12 +436,13 @@ export class Extension<Options = any, Storage = any> {
|
|
|
443
436
|
)
|
|
444
437
|
}
|
|
445
438
|
|
|
446
|
-
this.storage =
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
439
|
+
this.storage =
|
|
440
|
+
callOrReturn(
|
|
441
|
+
getExtensionField<AnyConfig['addStorage']>(this, 'addStorage', {
|
|
442
|
+
name: this.name,
|
|
443
|
+
options: this.options,
|
|
444
|
+
}),
|
|
445
|
+
) || {}
|
|
452
446
|
}
|
|
453
447
|
|
|
454
448
|
static create<O = any, S = any>(config: Partial<ExtensionConfig<O, S>> = {}) {
|
package/src/ExtensionManager.ts
CHANGED
|
@@ -4,21 +4,25 @@ import { Plugin } from '@tiptap/pm/state'
|
|
|
4
4
|
import { NodeViewConstructor } from '@tiptap/pm/view'
|
|
5
5
|
|
|
6
6
|
import type { Editor } from './Editor.js'
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
7
|
+
import {
|
|
8
|
+
flattenExtensions,
|
|
9
|
+
getAttributesFromExtensions,
|
|
10
|
+
getExtensionField,
|
|
11
|
+
getNodeType,
|
|
12
|
+
getRenderedAttributes,
|
|
13
|
+
getSchemaByResolvedExtensions,
|
|
14
|
+
getSchemaTypeByName,
|
|
15
|
+
isExtensionRulesEnabled,
|
|
16
|
+
resolveExtensions,
|
|
17
|
+
sortExtensions,
|
|
18
|
+
splitExtensions,
|
|
19
|
+
} from './helpers/index.js'
|
|
15
20
|
import type { NodeConfig } from './index.js'
|
|
16
21
|
import { InputRule, inputRulesPlugin } from './InputRule.js'
|
|
17
22
|
import { Mark } from './Mark.js'
|
|
18
23
|
import { PasteRule, pasteRulesPlugin } from './PasteRule.js'
|
|
19
24
|
import { AnyConfig, Extensions, RawCommands } from './types.js'
|
|
20
25
|
import { callOrReturn } from './utilities/callOrReturn.js'
|
|
21
|
-
import { findDuplicates } from './utilities/findDuplicates.js'
|
|
22
26
|
|
|
23
27
|
export class ExtensionManager {
|
|
24
28
|
editor: Editor
|
|
@@ -31,87 +35,16 @@ export class ExtensionManager {
|
|
|
31
35
|
|
|
32
36
|
constructor(extensions: Extensions, editor: Editor) {
|
|
33
37
|
this.editor = editor
|
|
34
|
-
this.extensions =
|
|
38
|
+
this.extensions = resolveExtensions(extensions)
|
|
35
39
|
this.schema = getSchemaByResolvedExtensions(this.extensions, editor)
|
|
36
40
|
this.setupExtensions()
|
|
37
41
|
}
|
|
38
42
|
|
|
39
|
-
|
|
40
|
-
* Returns a flattened and sorted extension list while
|
|
41
|
-
* also checking for duplicated extensions and warns the user.
|
|
42
|
-
* @param extensions An array of Tiptap extensions
|
|
43
|
-
* @returns An flattened and sorted array of Tiptap extensions
|
|
44
|
-
*/
|
|
45
|
-
static resolve(extensions: Extensions): Extensions {
|
|
46
|
-
const resolvedExtensions = ExtensionManager.sort(ExtensionManager.flatten(extensions))
|
|
47
|
-
const duplicatedNames = findDuplicates(resolvedExtensions.map(extension => extension.name))
|
|
48
|
-
|
|
49
|
-
if (duplicatedNames.length) {
|
|
50
|
-
console.warn(
|
|
51
|
-
`[tiptap warn]: Duplicate extension names found: [${duplicatedNames
|
|
52
|
-
.map(item => `'${item}'`)
|
|
53
|
-
.join(', ')}]. This can lead to issues.`,
|
|
54
|
-
)
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
return resolvedExtensions
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Create a flattened array of extensions by traversing the `addExtensions` field.
|
|
62
|
-
* @param extensions An array of Tiptap extensions
|
|
63
|
-
* @returns A flattened array of Tiptap extensions
|
|
64
|
-
*/
|
|
65
|
-
static flatten(extensions: Extensions): Extensions {
|
|
66
|
-
return (
|
|
67
|
-
extensions
|
|
68
|
-
.map(extension => {
|
|
69
|
-
const context = {
|
|
70
|
-
name: extension.name,
|
|
71
|
-
options: extension.options,
|
|
72
|
-
storage: extension.storage,
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const addExtensions = getExtensionField<AnyConfig['addExtensions']>(
|
|
76
|
-
extension,
|
|
77
|
-
'addExtensions',
|
|
78
|
-
context,
|
|
79
|
-
)
|
|
43
|
+
static resolve = resolveExtensions
|
|
80
44
|
|
|
81
|
-
|
|
82
|
-
return [extension, ...this.flatten(addExtensions())]
|
|
83
|
-
}
|
|
45
|
+
static sort = sortExtensions
|
|
84
46
|
|
|
85
|
-
|
|
86
|
-
})
|
|
87
|
-
// `Infinity` will break TypeScript so we set a number that is probably high enough
|
|
88
|
-
.flat(10)
|
|
89
|
-
)
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Sort extensions by priority.
|
|
94
|
-
* @param extensions An array of Tiptap extensions
|
|
95
|
-
* @returns A sorted array of Tiptap extensions by priority
|
|
96
|
-
*/
|
|
97
|
-
static sort(extensions: Extensions): Extensions {
|
|
98
|
-
const defaultPriority = 100
|
|
99
|
-
|
|
100
|
-
return extensions.sort((a, b) => {
|
|
101
|
-
const priorityA = getExtensionField<AnyConfig['priority']>(a, 'priority') || defaultPriority
|
|
102
|
-
const priorityB = getExtensionField<AnyConfig['priority']>(b, 'priority') || defaultPriority
|
|
103
|
-
|
|
104
|
-
if (priorityA > priorityB) {
|
|
105
|
-
return -1
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
if (priorityA < priorityB) {
|
|
109
|
-
return 1
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
return 0
|
|
113
|
-
})
|
|
114
|
-
}
|
|
47
|
+
static flatten = flattenExtensions
|
|
115
48
|
|
|
116
49
|
/**
|
|
117
50
|
* Get all commands from the extensions.
|
|
@@ -127,11 +60,7 @@ export class ExtensionManager {
|
|
|
127
60
|
type: getSchemaTypeByName(extension.name, this.schema),
|
|
128
61
|
}
|
|
129
62
|
|
|
130
|
-
const addCommands = getExtensionField<AnyConfig['addCommands']>(
|
|
131
|
-
extension,
|
|
132
|
-
'addCommands',
|
|
133
|
-
context,
|
|
134
|
-
)
|
|
63
|
+
const addCommands = getExtensionField<AnyConfig['addCommands']>(extension, 'addCommands', context)
|
|
135
64
|
|
|
136
65
|
if (!addCommands) {
|
|
137
66
|
return commands
|
|
@@ -156,7 +85,7 @@ export class ExtensionManager {
|
|
|
156
85
|
// so it feels more natural to run plugins at the end of an array first.
|
|
157
86
|
// That’s why we have to reverse the `extensions` array and sort again
|
|
158
87
|
// based on the `priority` option.
|
|
159
|
-
const extensions =
|
|
88
|
+
const extensions = sortExtensions([...this.extensions].reverse())
|
|
160
89
|
|
|
161
90
|
const inputRules: InputRule[] = []
|
|
162
91
|
const pasteRules: PasteRule[] = []
|
|
@@ -200,21 +129,13 @@ export class ExtensionManager {
|
|
|
200
129
|
|
|
201
130
|
plugins.push(keyMapPlugin)
|
|
202
131
|
|
|
203
|
-
const addInputRules = getExtensionField<AnyConfig['addInputRules']>(
|
|
204
|
-
extension,
|
|
205
|
-
'addInputRules',
|
|
206
|
-
context,
|
|
207
|
-
)
|
|
132
|
+
const addInputRules = getExtensionField<AnyConfig['addInputRules']>(extension, 'addInputRules', context)
|
|
208
133
|
|
|
209
134
|
if (isExtensionRulesEnabled(extension, editor.options.enableInputRules) && addInputRules) {
|
|
210
135
|
inputRules.push(...addInputRules())
|
|
211
136
|
}
|
|
212
137
|
|
|
213
|
-
const addPasteRules = getExtensionField<AnyConfig['addPasteRules']>(
|
|
214
|
-
extension,
|
|
215
|
-
'addPasteRules',
|
|
216
|
-
context,
|
|
217
|
-
)
|
|
138
|
+
const addPasteRules = getExtensionField<AnyConfig['addPasteRules']>(extension, 'addPasteRules', context)
|
|
218
139
|
|
|
219
140
|
if (isExtensionRulesEnabled(extension, editor.options.enablePasteRules) && addPasteRules) {
|
|
220
141
|
pasteRules.push(...addPasteRules())
|
|
@@ -269,9 +190,7 @@ export class ExtensionManager {
|
|
|
269
190
|
nodeExtensions
|
|
270
191
|
.filter(extension => !!getExtensionField(extension, 'addNodeView'))
|
|
271
192
|
.map(extension => {
|
|
272
|
-
const extensionAttributes = this.attributes.filter(
|
|
273
|
-
attribute => attribute.type === extension.name,
|
|
274
|
-
)
|
|
193
|
+
const extensionAttributes = this.attributes.filter(attribute => attribute.type === extension.name)
|
|
275
194
|
const context = {
|
|
276
195
|
name: extension.name,
|
|
277
196
|
options: extension.options,
|
|
@@ -279,23 +198,13 @@ export class ExtensionManager {
|
|
|
279
198
|
editor,
|
|
280
199
|
type: getNodeType(extension.name, this.schema),
|
|
281
200
|
}
|
|
282
|
-
const addNodeView = getExtensionField<NodeConfig['addNodeView']>(
|
|
283
|
-
extension,
|
|
284
|
-
'addNodeView',
|
|
285
|
-
context,
|
|
286
|
-
)
|
|
201
|
+
const addNodeView = getExtensionField<NodeConfig['addNodeView']>(extension, 'addNodeView', context)
|
|
287
202
|
|
|
288
203
|
if (!addNodeView) {
|
|
289
204
|
return []
|
|
290
205
|
}
|
|
291
206
|
|
|
292
|
-
const nodeview: NodeViewConstructor = (
|
|
293
|
-
node,
|
|
294
|
-
view,
|
|
295
|
-
getPos,
|
|
296
|
-
decorations,
|
|
297
|
-
innerDecorations,
|
|
298
|
-
) => {
|
|
207
|
+
const nodeview: NodeViewConstructor = (node, view, getPos, decorations, innerDecorations) => {
|
|
299
208
|
const HTMLAttributes = getRenderedAttributes(node, extensionAttributes)
|
|
300
209
|
|
|
301
210
|
return addNodeView()({
|
|
@@ -342,11 +251,7 @@ export class ExtensionManager {
|
|
|
342
251
|
}
|
|
343
252
|
}
|
|
344
253
|
|
|
345
|
-
const onBeforeCreate = getExtensionField<AnyConfig['onBeforeCreate']>(
|
|
346
|
-
extension,
|
|
347
|
-
'onBeforeCreate',
|
|
348
|
-
context,
|
|
349
|
-
)
|
|
254
|
+
const onBeforeCreate = getExtensionField<AnyConfig['onBeforeCreate']>(extension, 'onBeforeCreate', context)
|
|
350
255
|
const onCreate = getExtensionField<AnyConfig['onCreate']>(extension, 'onCreate', context)
|
|
351
256
|
const onUpdate = getExtensionField<AnyConfig['onUpdate']>(extension, 'onUpdate', context)
|
|
352
257
|
const onSelectionUpdate = getExtensionField<AnyConfig['onSelectionUpdate']>(
|
|
@@ -354,11 +259,7 @@ export class ExtensionManager {
|
|
|
354
259
|
'onSelectionUpdate',
|
|
355
260
|
context,
|
|
356
261
|
)
|
|
357
|
-
const onTransaction = getExtensionField<AnyConfig['onTransaction']>(
|
|
358
|
-
extension,
|
|
359
|
-
'onTransaction',
|
|
360
|
-
context,
|
|
361
|
-
)
|
|
262
|
+
const onTransaction = getExtensionField<AnyConfig['onTransaction']>(extension, 'onTransaction', context)
|
|
362
263
|
const onFocus = getExtensionField<AnyConfig['onFocus']>(extension, 'onFocus', context)
|
|
363
264
|
const onBlur = getExtensionField<AnyConfig['onBlur']>(extension, 'onBlur', context)
|
|
364
265
|
const onDestroy = getExtensionField<AnyConfig['onDestroy']>(extension, 'onDestroy', context)
|