@tiptap/core 2.0.0-beta.180 → 2.0.0-beta.183
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/packages/core/src/Mark.d.ts +8 -0
- package/dist/packages/core/src/pasteRules/index.d.ts +1 -0
- package/dist/packages/core/src/pasteRules/nodePasteRule.d.ts +12 -0
- package/dist/tiptap-core.cjs.js +83 -57
- package/dist/tiptap-core.cjs.js.map +1 -1
- package/dist/tiptap-core.esm.js +83 -58
- package/dist/tiptap-core.esm.js.map +1 -1
- package/dist/tiptap-core.umd.js +83 -57
- package/dist/tiptap-core.umd.js.map +1 -1
- package/package.json +9 -9
- package/src/CommandManager.ts +2 -2
- package/src/Editor.ts +1 -0
- package/src/ExtensionManager.ts +13 -4
- package/src/Mark.ts +39 -0
- package/src/PasteRule.ts +2 -2
- package/src/commands/focus.ts +3 -1
- package/src/commands/setContent.ts +1 -4
- package/src/commands/setNodeSelection.ts +3 -5
- package/src/commands/splitBlock.ts +1 -12
- package/src/extensions/focusEvents.ts +2 -2
- package/src/helpers/getTextContentFromNodes.ts +1 -1
- package/src/helpers/isNodeSelection.ts +1 -3
- package/src/helpers/isTextSelection.ts +1 -3
- package/src/pasteRules/index.ts +1 -0
- package/src/pasteRules/nodePasteRule.ts +39 -0
- package/dist/packages/core/src/utilities/isClass.d.ts +0 -1
- package/dist/packages/core/src/utilities/isObject.d.ts +0 -1
- package/src/utilities/isClass.ts +0 -7
- package/src/utilities/isObject.ts +0 -10
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.
|
|
4
|
+
"version": "2.0.0-beta.183",
|
|
5
5
|
"homepage": "https://tiptap.dev",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"tiptap",
|
|
@@ -24,13 +24,13 @@
|
|
|
24
24
|
"dist"
|
|
25
25
|
],
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"prosemirror-commands": "
|
|
28
|
-
"prosemirror-keymap": "
|
|
29
|
-
"prosemirror-model": "
|
|
30
|
-
"prosemirror-schema-list": "
|
|
31
|
-
"prosemirror-state": "
|
|
32
|
-
"prosemirror-transform": "
|
|
33
|
-
"prosemirror-view": "
|
|
27
|
+
"prosemirror-commands": "1.3.0",
|
|
28
|
+
"prosemirror-keymap": "1.2.0",
|
|
29
|
+
"prosemirror-model": "1.18.1",
|
|
30
|
+
"prosemirror-schema-list": "1.2.0",
|
|
31
|
+
"prosemirror-state": "1.4.1",
|
|
32
|
+
"prosemirror-transform": "1.6.0",
|
|
33
|
+
"prosemirror-view": "1.26.2"
|
|
34
34
|
},
|
|
35
35
|
"repository": {
|
|
36
36
|
"type": "git",
|
|
@@ -38,5 +38,5 @@
|
|
|
38
38
|
"directory": "packages/core"
|
|
39
39
|
},
|
|
40
40
|
"sideEffects": false,
|
|
41
|
-
"gitHead": "
|
|
41
|
+
"gitHead": "5bcfb322ceb30575430bd8797f2787d65de80f2c"
|
|
42
42
|
}
|
package/src/CommandManager.ts
CHANGED
|
@@ -107,13 +107,13 @@ export class CommandManager {
|
|
|
107
107
|
|
|
108
108
|
public createCan(startTr?: Transaction): CanCommands {
|
|
109
109
|
const { rawCommands, state } = this
|
|
110
|
-
const dispatch =
|
|
110
|
+
const dispatch = false
|
|
111
111
|
const tr = startTr || state.tr
|
|
112
112
|
const props = this.buildProps(tr, dispatch)
|
|
113
113
|
const formattedCommands = Object.fromEntries(Object
|
|
114
114
|
.entries(rawCommands)
|
|
115
115
|
.map(([name, command]) => {
|
|
116
|
-
return [name, (...args: never[]) => command(...args)({ ...props, dispatch })]
|
|
116
|
+
return [name, (...args: never[]) => command(...args)({ ...props, dispatch: undefined })]
|
|
117
117
|
})) as unknown as SingleCommands
|
|
118
118
|
|
|
119
119
|
return {
|
package/src/Editor.ts
CHANGED
package/src/ExtensionManager.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { Node as ProsemirrorNode, Schema } from 'prosemirror-model'
|
|
|
3
3
|
import { Plugin } from 'prosemirror-state'
|
|
4
4
|
import { Decoration, EditorView } from 'prosemirror-view'
|
|
5
5
|
|
|
6
|
-
import { NodeConfig } from '.'
|
|
6
|
+
import { Mark, NodeConfig } from '.'
|
|
7
7
|
import { Editor } from './Editor'
|
|
8
8
|
import { getAttributesFromExtensions } from './helpers/getAttributesFromExtensions'
|
|
9
9
|
import { getExtensionField } from './helpers/getExtensionField'
|
|
@@ -252,6 +252,13 @@ export class ExtensionManager {
|
|
|
252
252
|
context,
|
|
253
253
|
)
|
|
254
254
|
|
|
255
|
+
let defaultBindings: Record<string, () => boolean> = {}
|
|
256
|
+
|
|
257
|
+
// bind exit handling
|
|
258
|
+
if (extension.type === 'mark' && extension.config.exitable) {
|
|
259
|
+
defaultBindings.ArrowRight = () => Mark.handleExit({ editor, mark: (extension as Mark) })
|
|
260
|
+
}
|
|
261
|
+
|
|
255
262
|
if (addKeyboardShortcuts) {
|
|
256
263
|
const bindings = Object.fromEntries(
|
|
257
264
|
Object
|
|
@@ -261,11 +268,13 @@ export class ExtensionManager {
|
|
|
261
268
|
}),
|
|
262
269
|
)
|
|
263
270
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
plugins.push(keyMapPlugin)
|
|
271
|
+
defaultBindings = { ...defaultBindings, ...bindings }
|
|
267
272
|
}
|
|
268
273
|
|
|
274
|
+
const keyMapPlugin = keymap(defaultBindings)
|
|
275
|
+
|
|
276
|
+
plugins.push(keyMapPlugin)
|
|
277
|
+
|
|
269
278
|
const addInputRules = getExtensionField<AnyConfig['addInputRules']>(
|
|
270
279
|
extension,
|
|
271
280
|
'addInputRules',
|
package/src/Mark.ts
CHANGED
|
@@ -304,6 +304,11 @@ declare module '@tiptap/core' {
|
|
|
304
304
|
parent: ParentConfig<MarkConfig<Options, Storage>>['excludes'],
|
|
305
305
|
}) => MarkSpec['excludes']),
|
|
306
306
|
|
|
307
|
+
/**
|
|
308
|
+
* Marks this Mark as exitable
|
|
309
|
+
*/
|
|
310
|
+
exitable?: boolean | (() => boolean),
|
|
311
|
+
|
|
307
312
|
/**
|
|
308
313
|
* Group
|
|
309
314
|
*/
|
|
@@ -486,4 +491,38 @@ export class Mark<Options = any, Storage = any> {
|
|
|
486
491
|
|
|
487
492
|
return extension
|
|
488
493
|
}
|
|
494
|
+
|
|
495
|
+
static handleExit({
|
|
496
|
+
editor,
|
|
497
|
+
mark,
|
|
498
|
+
}: {
|
|
499
|
+
editor: Editor
|
|
500
|
+
mark: Mark
|
|
501
|
+
}) {
|
|
502
|
+
const { tr } = editor.state
|
|
503
|
+
const currentPos = editor.state.selection.$from
|
|
504
|
+
const isAtEnd = currentPos.pos === currentPos.end()
|
|
505
|
+
|
|
506
|
+
if (isAtEnd) {
|
|
507
|
+
const currentMarks = currentPos.marks()
|
|
508
|
+
const isInMark = !!currentMarks.find(m => m?.type.name === mark.name)
|
|
509
|
+
|
|
510
|
+
if (!isInMark) {
|
|
511
|
+
return false
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
const removeMark = currentMarks.find(m => m?.type.name === mark.name)
|
|
515
|
+
|
|
516
|
+
if (removeMark) {
|
|
517
|
+
tr.removeStoredMark(removeMark)
|
|
518
|
+
}
|
|
519
|
+
tr.insertText(' ', currentPos.pos)
|
|
520
|
+
|
|
521
|
+
editor.view.dispatch(tr)
|
|
522
|
+
|
|
523
|
+
return true
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
return false
|
|
527
|
+
}
|
|
489
528
|
}
|
package/src/PasteRule.ts
CHANGED
|
@@ -190,7 +190,7 @@ export function pasteRulesPlugin(props: { editor: Editor, rules: PasteRule[] }):
|
|
|
190
190
|
return false
|
|
191
191
|
},
|
|
192
192
|
|
|
193
|
-
paste: (view, event) => {
|
|
193
|
+
paste: (view, event: Event) => {
|
|
194
194
|
const html = (event as ClipboardEvent).clipboardData?.getData('text/html')
|
|
195
195
|
|
|
196
196
|
isPastedFromProseMirror = !!html?.includes('data-pm-slice')
|
|
@@ -229,7 +229,7 @@ export function pasteRulesPlugin(props: { editor: Editor, rules: PasteRule[] }):
|
|
|
229
229
|
editor,
|
|
230
230
|
state: chainableState,
|
|
231
231
|
from: Math.max(from - 1, 0),
|
|
232
|
-
to: to.b,
|
|
232
|
+
to: to.b - 1,
|
|
233
233
|
rule,
|
|
234
234
|
})
|
|
235
235
|
|
package/src/commands/focus.ts
CHANGED
|
@@ -60,7 +60,9 @@ export const focus: RawCommands['focus'] = (position = null, options = {}) => ({
|
|
|
60
60
|
return true
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
|
|
63
|
+
// pass through tr.doc instead of editor.state.doc
|
|
64
|
+
// since transactions could change the editors state before this command has been run
|
|
65
|
+
const selection = resolveFocusPosition(tr.doc, position) || editor.state.selection
|
|
64
66
|
const isSameSelection = editor.state.selection.eq(selection)
|
|
65
67
|
|
|
66
68
|
if (dispatch) {
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { ParseOptions } from 'prosemirror-model'
|
|
2
|
-
import { TextSelection } from 'prosemirror-state'
|
|
3
2
|
|
|
4
3
|
import { createDocument } from '../helpers/createDocument'
|
|
5
4
|
import { Content, RawCommands } from '../types'
|
|
@@ -22,11 +21,9 @@ declare module '@tiptap/core' {
|
|
|
22
21
|
export const setContent: RawCommands['setContent'] = (content, emitUpdate = false, parseOptions = {}) => ({ tr, editor, dispatch }) => {
|
|
23
22
|
const { doc } = tr
|
|
24
23
|
const document = createDocument(content, editor.schema, parseOptions)
|
|
25
|
-
const selection = TextSelection.create(doc, 0, doc.content.size)
|
|
26
24
|
|
|
27
25
|
if (dispatch) {
|
|
28
|
-
tr.
|
|
29
|
-
.replaceSelectionWith(document, false)
|
|
26
|
+
tr.replaceWith(0, doc.content.size, document)
|
|
30
27
|
.setMeta('preventUpdate', !emitUpdate)
|
|
31
28
|
}
|
|
32
29
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { NodeSelection
|
|
1
|
+
import { NodeSelection } from 'prosemirror-state'
|
|
2
2
|
|
|
3
3
|
import { RawCommands } from '../types'
|
|
4
4
|
import { minMax } from '../utilities/minMax'
|
|
@@ -17,10 +17,8 @@ declare module '@tiptap/core' {
|
|
|
17
17
|
export const setNodeSelection: RawCommands['setNodeSelection'] = position => ({ tr, dispatch }) => {
|
|
18
18
|
if (dispatch) {
|
|
19
19
|
const { doc } = tr
|
|
20
|
-
const
|
|
21
|
-
const
|
|
22
|
-
const resolvedPos = minMax(position, minPos, maxPos)
|
|
23
|
-
const selection = NodeSelection.create(doc, resolvedPos)
|
|
20
|
+
const from = minMax(position, 0, doc.content.size)
|
|
21
|
+
const selection = NodeSelection.create(doc, from)
|
|
24
22
|
|
|
25
23
|
tr.setSelection(selection)
|
|
26
24
|
}
|
|
@@ -1,21 +1,10 @@
|
|
|
1
|
-
import { ContentMatch } from 'prosemirror-model'
|
|
2
1
|
import { EditorState, NodeSelection, TextSelection } from 'prosemirror-state'
|
|
3
2
|
import { canSplit } from 'prosemirror-transform'
|
|
4
3
|
|
|
4
|
+
import { defaultBlockAt } from '../helpers/defaultBlockAt'
|
|
5
5
|
import { getSplittedAttributes } from '../helpers/getSplittedAttributes'
|
|
6
6
|
import { RawCommands } from '../types'
|
|
7
7
|
|
|
8
|
-
function defaultBlockAt(match: ContentMatch) {
|
|
9
|
-
for (let i = 0; i < match.edgeCount; i += 1) {
|
|
10
|
-
const { type } = match.edge(i)
|
|
11
|
-
|
|
12
|
-
if (type.isTextblock && !type.hasRequiredAttrs()) {
|
|
13
|
-
return type
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
return null
|
|
17
|
-
}
|
|
18
|
-
|
|
19
8
|
function ensureMarks(state: EditorState, splittableMarks?: string[]) {
|
|
20
9
|
const marks = state.storedMarks
|
|
21
10
|
|| (state.selection.$to.parentOffset && state.selection.$from.marks())
|
|
@@ -13,7 +13,7 @@ export const FocusEvents = Extension.create({
|
|
|
13
13
|
key: new PluginKey('focusEvents'),
|
|
14
14
|
props: {
|
|
15
15
|
handleDOMEvents: {
|
|
16
|
-
focus: (view, event) => {
|
|
16
|
+
focus: (view, event: Event) => {
|
|
17
17
|
editor.isFocused = true
|
|
18
18
|
|
|
19
19
|
const transaction = editor.state.tr
|
|
@@ -24,7 +24,7 @@ export const FocusEvents = Extension.create({
|
|
|
24
24
|
|
|
25
25
|
return false
|
|
26
26
|
},
|
|
27
|
-
blur: (view, event) => {
|
|
27
|
+
blur: (view, event: Event) => {
|
|
28
28
|
editor.isFocused = false
|
|
29
29
|
|
|
30
30
|
const transaction = editor.state.tr
|
|
@@ -9,7 +9,7 @@ export const getTextContentFromNodes = ($from: ResolvedPos, maxMatch = 500) => {
|
|
|
9
9
|
(node, pos, parent, index) => {
|
|
10
10
|
textBefore += node.type.spec.toText?.({
|
|
11
11
|
node, pos, parent, index,
|
|
12
|
-
}) ||
|
|
12
|
+
}) || $from.nodeBefore?.text || '%leaf%'
|
|
13
13
|
},
|
|
14
14
|
)
|
|
15
15
|
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { NodeSelection } from 'prosemirror-state'
|
|
2
2
|
|
|
3
|
-
import { isObject } from '../utilities/isObject'
|
|
4
|
-
|
|
5
3
|
export function isNodeSelection(value: unknown): value is NodeSelection {
|
|
6
|
-
return
|
|
4
|
+
return value instanceof NodeSelection
|
|
7
5
|
}
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { TextSelection } from 'prosemirror-state'
|
|
2
2
|
|
|
3
|
-
import { isObject } from '../utilities/isObject'
|
|
4
|
-
|
|
5
3
|
export function isTextSelection(value: unknown): value is TextSelection {
|
|
6
|
-
return
|
|
4
|
+
return value instanceof TextSelection
|
|
7
5
|
}
|
package/src/pasteRules/index.ts
CHANGED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { NodeType } from 'prosemirror-model'
|
|
2
|
+
|
|
3
|
+
import { PasteRule } from '../PasteRule'
|
|
4
|
+
import { ExtendedRegExpMatchArray } from '../types'
|
|
5
|
+
import { callOrReturn } from '../utilities'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Build an paste rule that adds a node when the
|
|
9
|
+
* matched text is pasted into it.
|
|
10
|
+
*/
|
|
11
|
+
export function nodePasteRule(config: {
|
|
12
|
+
find: RegExp,
|
|
13
|
+
type: NodeType,
|
|
14
|
+
getAttributes?:
|
|
15
|
+
| Record<string, any>
|
|
16
|
+
| ((match: ExtendedRegExpMatchArray) => Record<string, any>)
|
|
17
|
+
| false
|
|
18
|
+
| null,
|
|
19
|
+
}) {
|
|
20
|
+
return new PasteRule({
|
|
21
|
+
find: config.find,
|
|
22
|
+
handler({ match, chain, range }) {
|
|
23
|
+
const attributes = callOrReturn(config.getAttributes, undefined, match)
|
|
24
|
+
|
|
25
|
+
if (attributes === false || attributes === null) {
|
|
26
|
+
return null
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (match.input) {
|
|
30
|
+
chain()
|
|
31
|
+
.deleteRange(range)
|
|
32
|
+
.insertContent({
|
|
33
|
+
type: config.type.name,
|
|
34
|
+
attrs: attributes,
|
|
35
|
+
})
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
})
|
|
39
|
+
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function isClass(value: any): boolean;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function isObject(value: any): boolean;
|
package/src/utilities/isClass.ts
DELETED