@tiptap/core 2.2.0-rc.7 → 2.2.0
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/index.cjs +198 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +199 -13
- package/dist/index.js.map +1 -1
- package/dist/index.umd.js +198 -11
- package/dist/index.umd.js.map +1 -1
- package/dist/packages/core/src/Editor.d.ts +9 -0
- package/dist/packages/core/src/NodePos.d.ts +40 -0
- package/dist/packages/core/src/PasteRule.d.ts +1 -1
- package/dist/packages/core/src/commands/index.d.ts +2 -0
- package/dist/packages/core/src/commands/joinTextblockBackward.d.ts +12 -0
- package/dist/packages/core/src/commands/joinTextblockForward.d.ts +12 -0
- package/dist/packages/core/src/index.d.ts +1 -0
- package/package.json +2 -2
- package/src/Editor.ts +22 -1
- package/src/Extension.ts +2 -2
- package/src/Mark.ts +2 -2
- package/src/Node.ts +2 -2
- package/src/NodePos.ts +201 -0
- package/src/PasteRule.ts +4 -3
- package/src/commands/index.ts +2 -0
- package/src/commands/joinTextblockBackward.ts +18 -0
- package/src/commands/joinTextblockForward.ts +18 -0
- package/src/helpers/createChainableState.ts +0 -1
- package/src/helpers/getMarksBetween.ts +4 -0
- package/src/index.ts +1 -0
- package/src/utilities/elementFromString.ts +19 -1
|
@@ -4,6 +4,7 @@ import { EditorView } from '@tiptap/pm/view';
|
|
|
4
4
|
import { EventEmitter } from './EventEmitter.js';
|
|
5
5
|
import { ExtensionManager } from './ExtensionManager.js';
|
|
6
6
|
import * as extensions from './extensions/index.js';
|
|
7
|
+
import { NodePos } from './NodePos.js';
|
|
7
8
|
import { CanCommands, ChainedCommands, EditorEvents, EditorOptions, JSONContent, SingleCommands, TextSerializer } from './types.js';
|
|
8
9
|
export { extensions };
|
|
9
10
|
export interface HTMLElement {
|
|
@@ -148,4 +149,12 @@ export declare class Editor extends EventEmitter<EditorEvents> {
|
|
|
148
149
|
* Check if the editor is already destroyed.
|
|
149
150
|
*/
|
|
150
151
|
get isDestroyed(): boolean;
|
|
152
|
+
$node(selector: string, attributes?: {
|
|
153
|
+
[key: string]: any;
|
|
154
|
+
}): NodePos | null;
|
|
155
|
+
$nodes(selector: string, attributes?: {
|
|
156
|
+
[key: string]: any;
|
|
157
|
+
}): NodePos[] | null;
|
|
158
|
+
$pos(pos: number): NodePos;
|
|
159
|
+
get $doc(): NodePos;
|
|
151
160
|
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Fragment, Node, ResolvedPos } from '@tiptap/pm/model';
|
|
2
|
+
import { Editor } from './Editor.js';
|
|
3
|
+
import { Content, Range } from './types.js';
|
|
4
|
+
export declare class NodePos {
|
|
5
|
+
private resolvedPos;
|
|
6
|
+
private editor;
|
|
7
|
+
constructor(pos: ResolvedPos, editor: Editor);
|
|
8
|
+
get node(): Node;
|
|
9
|
+
get element(): HTMLElement;
|
|
10
|
+
get depth(): number;
|
|
11
|
+
get pos(): number;
|
|
12
|
+
get content(): Fragment;
|
|
13
|
+
set content(content: Content);
|
|
14
|
+
get attributes(): {
|
|
15
|
+
[key: string]: any;
|
|
16
|
+
};
|
|
17
|
+
get textContent(): string;
|
|
18
|
+
get size(): number;
|
|
19
|
+
get from(): number;
|
|
20
|
+
get range(): Range;
|
|
21
|
+
get to(): number;
|
|
22
|
+
get parent(): NodePos | null;
|
|
23
|
+
get before(): NodePos | null;
|
|
24
|
+
get after(): NodePos | null;
|
|
25
|
+
get children(): NodePos[];
|
|
26
|
+
get firstChild(): NodePos | null;
|
|
27
|
+
get lastChild(): NodePos | null;
|
|
28
|
+
closest(selector: string, attributes?: {
|
|
29
|
+
[key: string]: any;
|
|
30
|
+
}): NodePos | null;
|
|
31
|
+
querySelector(selector: string, attributes?: {
|
|
32
|
+
[key: string]: any;
|
|
33
|
+
}): NodePos | null;
|
|
34
|
+
querySelectorAll(selector: string, attributes?: {
|
|
35
|
+
[key: string]: any;
|
|
36
|
+
}, firstItemOnly?: boolean): NodePos[];
|
|
37
|
+
setAttribute(attributes: {
|
|
38
|
+
[key: string]: any;
|
|
39
|
+
}): void;
|
|
40
|
+
}
|
|
@@ -8,7 +8,7 @@ export declare type PasteRuleMatch = {
|
|
|
8
8
|
match?: RegExpMatchArray;
|
|
9
9
|
data?: Record<string, any>;
|
|
10
10
|
};
|
|
11
|
-
export declare type PasteRuleFinder = RegExp | ((text: string) => PasteRuleMatch[] | null | undefined);
|
|
11
|
+
export declare type PasteRuleFinder = RegExp | ((text: string, event?: ClipboardEvent) => PasteRuleMatch[] | null | undefined);
|
|
12
12
|
export declare class PasteRule {
|
|
13
13
|
find: PasteRuleFinder;
|
|
14
14
|
handler: (props: {
|
|
@@ -19,6 +19,8 @@ export * from './insertContentAt.js';
|
|
|
19
19
|
export * from './join.js';
|
|
20
20
|
export * from './joinItemBackward.js';
|
|
21
21
|
export * from './joinItemForward.js';
|
|
22
|
+
export * from './joinTextblockBackward.js';
|
|
23
|
+
export * from './joinTextblockForward.js';
|
|
22
24
|
export * from './keyboardShortcut.js';
|
|
23
25
|
export * from './lift.js';
|
|
24
26
|
export * from './liftEmptyBlock.js';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { RawCommands } from '../types.js';
|
|
2
|
+
declare module '@tiptap/core' {
|
|
3
|
+
interface Commands<ReturnType> {
|
|
4
|
+
joinTextblockBackward: {
|
|
5
|
+
/**
|
|
6
|
+
* A more limited form of joinBackward that only tries to join the current textblock to the one before it, if the cursor is at the start of a textblock.
|
|
7
|
+
*/
|
|
8
|
+
joinTextblockBackward: () => ReturnType;
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
export declare const joinTextblockBackward: RawCommands['joinTextblockBackward'];
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { RawCommands } from '../types.js';
|
|
2
|
+
declare module '@tiptap/core' {
|
|
3
|
+
interface Commands<ReturnType> {
|
|
4
|
+
joinTextblockForward: {
|
|
5
|
+
/**
|
|
6
|
+
* A more limited form of joinForward that only tries to join the current textblock to the one after it, if the cursor is at the end of a textblock.
|
|
7
|
+
*/
|
|
8
|
+
joinTextblockForward: () => ReturnType;
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
export declare const joinTextblockForward: RawCommands['joinTextblockForward'];
|
|
@@ -7,6 +7,7 @@ export * from './InputRule.js';
|
|
|
7
7
|
export * from './inputRules/index.js';
|
|
8
8
|
export * from './Mark.js';
|
|
9
9
|
export * from './Node.js';
|
|
10
|
+
export * from './NodePos.js';
|
|
10
11
|
export * from './NodeView.js';
|
|
11
12
|
export * from './PasteRule.js';
|
|
12
13
|
export * from './pasteRules/index.js';
|
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.2.0
|
|
4
|
+
"version": "2.2.0",
|
|
5
5
|
"homepage": "https://tiptap.dev",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"tiptap",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"dist"
|
|
33
33
|
],
|
|
34
34
|
"devDependencies": {
|
|
35
|
-
"@tiptap/pm": "^2.2.0
|
|
35
|
+
"@tiptap/pm": "^2.2.0"
|
|
36
36
|
},
|
|
37
37
|
"peerDependencies": {
|
|
38
38
|
"@tiptap/pm": "^2.0.0"
|
package/src/Editor.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
MarkType, NodeType, Schema,
|
|
3
|
+
} from '@tiptap/pm/model'
|
|
2
4
|
import {
|
|
3
5
|
EditorState, Plugin, PluginKey, Transaction,
|
|
4
6
|
} from '@tiptap/pm/state'
|
|
@@ -16,6 +18,7 @@ import { getTextSerializersFromSchema } from './helpers/getTextSerializersFromSc
|
|
|
16
18
|
import { isActive } from './helpers/isActive.js'
|
|
17
19
|
import { isNodeEmpty } from './helpers/isNodeEmpty.js'
|
|
18
20
|
import { resolveFocusPosition } from './helpers/resolveFocusPosition.js'
|
|
21
|
+
import { NodePos } from './NodePos.js'
|
|
19
22
|
import { style } from './style.js'
|
|
20
23
|
import {
|
|
21
24
|
CanCommands,
|
|
@@ -486,4 +489,22 @@ export class Editor extends EventEmitter<EditorEvents> {
|
|
|
486
489
|
// @ts-ignore
|
|
487
490
|
return !this.view?.docView
|
|
488
491
|
}
|
|
492
|
+
|
|
493
|
+
public $node(selector: string, attributes?: { [key: string]: any }): NodePos | null {
|
|
494
|
+
return this.$doc?.querySelector(selector, attributes) || null
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
public $nodes(selector: string, attributes?: { [key: string]: any }): NodePos[] | null {
|
|
498
|
+
return this.$doc?.querySelectorAll(selector, attributes) || null
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
public $pos(pos: number) {
|
|
502
|
+
const $pos = this.state.doc.resolve(pos)
|
|
503
|
+
|
|
504
|
+
return new NodePos($pos, this)
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
get $doc() {
|
|
508
|
+
return this.$pos(0)
|
|
509
|
+
}
|
|
489
510
|
}
|
package/src/Extension.ts
CHANGED
|
@@ -297,7 +297,7 @@ export class Extension<Options = any, Storage = any> {
|
|
|
297
297
|
|
|
298
298
|
config: ExtensionConfig = {
|
|
299
299
|
name: this.name,
|
|
300
|
-
defaultOptions:
|
|
300
|
+
defaultOptions: {},
|
|
301
301
|
}
|
|
302
302
|
|
|
303
303
|
constructor(config: Partial<ExtensionConfig<Options, Storage>> = {}) {
|
|
@@ -308,7 +308,7 @@ export class Extension<Options = any, Storage = any> {
|
|
|
308
308
|
|
|
309
309
|
this.name = this.config.name
|
|
310
310
|
|
|
311
|
-
if (config.defaultOptions) {
|
|
311
|
+
if (config.defaultOptions && Object.keys(config.defaultOptions).length > 0) {
|
|
312
312
|
console.warn(
|
|
313
313
|
`[tiptap warn]: BREAKING CHANGE: "defaultOptions" is deprecated. Please use "addOptions" instead. Found in extension: "${this.name}".`,
|
|
314
314
|
)
|
package/src/Mark.ts
CHANGED
|
@@ -429,7 +429,7 @@ export class Mark<Options = any, Storage = any> {
|
|
|
429
429
|
|
|
430
430
|
config: MarkConfig = {
|
|
431
431
|
name: this.name,
|
|
432
|
-
defaultOptions:
|
|
432
|
+
defaultOptions: {},
|
|
433
433
|
}
|
|
434
434
|
|
|
435
435
|
constructor(config: Partial<MarkConfig<Options, Storage>> = {}) {
|
|
@@ -440,7 +440,7 @@ export class Mark<Options = any, Storage = any> {
|
|
|
440
440
|
|
|
441
441
|
this.name = this.config.name
|
|
442
442
|
|
|
443
|
-
if (config.defaultOptions) {
|
|
443
|
+
if (config.defaultOptions && Object.keys(config.defaultOptions).length > 0) {
|
|
444
444
|
console.warn(
|
|
445
445
|
`[tiptap warn]: BREAKING CHANGE: "defaultOptions" is deprecated. Please use "addOptions" instead. Found in extension: "${this.name}".`,
|
|
446
446
|
)
|
package/src/Node.ts
CHANGED
|
@@ -538,7 +538,7 @@ export class Node<Options = any, Storage = any> {
|
|
|
538
538
|
|
|
539
539
|
config: NodeConfig = {
|
|
540
540
|
name: this.name,
|
|
541
|
-
defaultOptions:
|
|
541
|
+
defaultOptions: {},
|
|
542
542
|
}
|
|
543
543
|
|
|
544
544
|
constructor(config: Partial<NodeConfig<Options, Storage>> = {}) {
|
|
@@ -549,7 +549,7 @@ export class Node<Options = any, Storage = any> {
|
|
|
549
549
|
|
|
550
550
|
this.name = this.config.name
|
|
551
551
|
|
|
552
|
-
if (config.defaultOptions) {
|
|
552
|
+
if (config.defaultOptions && Object.keys(config.defaultOptions).length > 0) {
|
|
553
553
|
console.warn(
|
|
554
554
|
`[tiptap warn]: BREAKING CHANGE: "defaultOptions" is deprecated. Please use "addOptions" instead. Found in extension: "${this.name}".`,
|
|
555
555
|
)
|
package/src/NodePos.ts
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Fragment, Node, ResolvedPos,
|
|
3
|
+
} from '@tiptap/pm/model'
|
|
4
|
+
|
|
5
|
+
import { Editor } from './Editor.js'
|
|
6
|
+
import { Content, Range } from './types.js'
|
|
7
|
+
|
|
8
|
+
export class NodePos {
|
|
9
|
+
private resolvedPos: ResolvedPos
|
|
10
|
+
|
|
11
|
+
private editor: Editor
|
|
12
|
+
|
|
13
|
+
constructor(pos: ResolvedPos, editor: Editor) {
|
|
14
|
+
this.resolvedPos = pos
|
|
15
|
+
this.editor = editor
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
get node(): Node {
|
|
19
|
+
return this.resolvedPos.node()
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
get element(): HTMLElement {
|
|
23
|
+
return this.editor.view.domAtPos(this.pos).node as HTMLElement
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
get depth(): number {
|
|
27
|
+
return this.resolvedPos.depth
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
get pos(): number {
|
|
31
|
+
return this.resolvedPos.pos
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
get content(): Fragment {
|
|
35
|
+
return this.node.content
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
set content(content: Content) {
|
|
39
|
+
this.editor.commands.insertContentAt({ from: this.from, to: this.to }, content)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
get attributes() : { [key: string]: any } {
|
|
43
|
+
return this.node.attrs
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
get textContent(): string {
|
|
47
|
+
return this.node.textContent
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
get size(): number {
|
|
51
|
+
return this.node.nodeSize
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
get from(): number {
|
|
55
|
+
return this.resolvedPos.start(this.resolvedPos.depth)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
get range(): Range {
|
|
59
|
+
return {
|
|
60
|
+
from: this.from,
|
|
61
|
+
to: this.to,
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
get to(): number {
|
|
66
|
+
return this.resolvedPos.end(this.resolvedPos.depth) + (this.node.isText ? 0 : 1)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
get parent(): NodePos | null {
|
|
70
|
+
if (this.depth === 0) {
|
|
71
|
+
return null
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const parentPos = this.resolvedPos.start(this.resolvedPos.depth - 1)
|
|
75
|
+
const $pos = this.resolvedPos.doc.resolve(parentPos)
|
|
76
|
+
|
|
77
|
+
return new NodePos($pos, this.editor)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
get before(): NodePos | null {
|
|
81
|
+
let $pos = this.resolvedPos.doc.resolve(this.from - 2)
|
|
82
|
+
|
|
83
|
+
if ($pos.depth !== this.depth) {
|
|
84
|
+
$pos = this.resolvedPos.doc.resolve(this.from - 3)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return new NodePos($pos, this.editor)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
get after(): NodePos | null {
|
|
91
|
+
let $pos = this.resolvedPos.doc.resolve(this.to + 2)
|
|
92
|
+
|
|
93
|
+
if ($pos.depth !== this.depth) {
|
|
94
|
+
$pos = this.resolvedPos.doc.resolve(this.to + 3)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return new NodePos($pos, this.editor)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
get children(): NodePos[] {
|
|
101
|
+
const children: NodePos[] = []
|
|
102
|
+
|
|
103
|
+
this.node.content.forEach((node, offset) => {
|
|
104
|
+
const targetPos = this.pos + offset + 1
|
|
105
|
+
const $pos = this.resolvedPos.doc.resolve(targetPos)
|
|
106
|
+
|
|
107
|
+
if ($pos.depth === this.depth) {
|
|
108
|
+
return
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
children.push(new NodePos($pos, this.editor))
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
return children
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
get firstChild(): NodePos | null {
|
|
118
|
+
return this.children[0] || null
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
get lastChild(): NodePos | null {
|
|
122
|
+
const children = this.children
|
|
123
|
+
|
|
124
|
+
return children[children.length - 1] || null
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
closest(selector: string, attributes: { [key: string]: any } = {}): NodePos | null {
|
|
128
|
+
let node: NodePos | null = null
|
|
129
|
+
let currentNode = this.parent
|
|
130
|
+
|
|
131
|
+
while (currentNode && !node) {
|
|
132
|
+
if (currentNode.node.type.name === selector) {
|
|
133
|
+
if (Object.keys(attributes).length > 0) {
|
|
134
|
+
const nodeAttributes = currentNode.node.attrs
|
|
135
|
+
const attrKeys = Object.keys(attributes)
|
|
136
|
+
|
|
137
|
+
for (let index = 0; index < attrKeys.length; index += 1) {
|
|
138
|
+
const key = attrKeys[index]
|
|
139
|
+
|
|
140
|
+
if (nodeAttributes[key] !== attributes[key]) {
|
|
141
|
+
break
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
} else {
|
|
145
|
+
node = currentNode
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
currentNode = currentNode.parent
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return node
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
querySelector(selector: string, attributes: { [key: string]: any } = {}): NodePos | null {
|
|
156
|
+
return this.querySelectorAll(selector, attributes, true)[0] || null
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
querySelectorAll(selector: string, attributes: { [key: string]: any } = {}, firstItemOnly = false): NodePos[] {
|
|
160
|
+
let nodes: NodePos[] = []
|
|
161
|
+
|
|
162
|
+
// iterate through children recursively finding all nodes which match the selector with the node name
|
|
163
|
+
if (!this.children || this.children.length === 0) {
|
|
164
|
+
return nodes
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
this.children.forEach(node => {
|
|
168
|
+
if (node.node.type.name === selector) {
|
|
169
|
+
if (Object.keys(attributes).length > 0) {
|
|
170
|
+
const nodeAttributes = node.node.attrs
|
|
171
|
+
const attrKeys = Object.keys(attributes)
|
|
172
|
+
|
|
173
|
+
for (let index = 0; index < attrKeys.length; index += 1) {
|
|
174
|
+
const key = attrKeys[index]
|
|
175
|
+
|
|
176
|
+
if (nodeAttributes[key] !== attributes[key]) {
|
|
177
|
+
return
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
nodes.push(node)
|
|
183
|
+
|
|
184
|
+
if (firstItemOnly) {
|
|
185
|
+
return
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
nodes = nodes.concat(node.querySelectorAll(selector))
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
return nodes
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
setAttribute(attributes: { [key: string]: any }) {
|
|
196
|
+
const oldSelection = this.editor.state.selection
|
|
197
|
+
|
|
198
|
+
this.editor.chain().setTextSelection(this.from).updateAttributes(this.node.type.name, attributes).setTextSelection(oldSelection.from)
|
|
199
|
+
.run()
|
|
200
|
+
}
|
|
201
|
+
}
|
package/src/PasteRule.ts
CHANGED
|
@@ -21,7 +21,7 @@ export type PasteRuleMatch = {
|
|
|
21
21
|
data?: Record<string, any>
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
export type PasteRuleFinder = RegExp | ((text: string) => PasteRuleMatch[] | null | undefined)
|
|
24
|
+
export type PasteRuleFinder = RegExp | ((text: string, event?: ClipboardEvent) => PasteRuleMatch[] | null | undefined)
|
|
25
25
|
|
|
26
26
|
export class PasteRule {
|
|
27
27
|
find: PasteRuleFinder
|
|
@@ -58,12 +58,13 @@ export class PasteRule {
|
|
|
58
58
|
const pasteRuleMatcherHandler = (
|
|
59
59
|
text: string,
|
|
60
60
|
find: PasteRuleFinder,
|
|
61
|
+
event?: ClipboardEvent,
|
|
61
62
|
): ExtendedRegExpMatchArray[] => {
|
|
62
63
|
if (isRegExp(find)) {
|
|
63
64
|
return [...text.matchAll(find)]
|
|
64
65
|
}
|
|
65
66
|
|
|
66
|
-
const matches = find(text)
|
|
67
|
+
const matches = find(text, event)
|
|
67
68
|
|
|
68
69
|
if (!matches) {
|
|
69
70
|
return []
|
|
@@ -119,7 +120,7 @@ function run(config: {
|
|
|
119
120
|
const resolvedTo = Math.min(to, pos + node.content.size)
|
|
120
121
|
const textToMatch = node.textBetween(resolvedFrom - pos, resolvedTo - pos, undefined, '\ufffc')
|
|
121
122
|
|
|
122
|
-
const matches = pasteRuleMatcherHandler(textToMatch, rule.find)
|
|
123
|
+
const matches = pasteRuleMatcherHandler(textToMatch, rule.find, pasteEvent)
|
|
123
124
|
|
|
124
125
|
matches.forEach(match => {
|
|
125
126
|
if (match.index === undefined) {
|
package/src/commands/index.ts
CHANGED
|
@@ -19,6 +19,8 @@ export * from './insertContentAt.js'
|
|
|
19
19
|
export * from './join.js'
|
|
20
20
|
export * from './joinItemBackward.js'
|
|
21
21
|
export * from './joinItemForward.js'
|
|
22
|
+
export * from './joinTextblockBackward.js'
|
|
23
|
+
export * from './joinTextblockForward.js'
|
|
22
24
|
export * from './keyboardShortcut.js'
|
|
23
25
|
export * from './lift.js'
|
|
24
26
|
export * from './liftEmptyBlock.js'
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { joinTextblockBackward as originalCommand } from '@tiptap/pm/commands'
|
|
2
|
+
|
|
3
|
+
import { RawCommands } from '../types.js'
|
|
4
|
+
|
|
5
|
+
declare module '@tiptap/core' {
|
|
6
|
+
interface Commands<ReturnType> {
|
|
7
|
+
joinTextblockBackward: {
|
|
8
|
+
/**
|
|
9
|
+
* A more limited form of joinBackward that only tries to join the current textblock to the one before it, if the cursor is at the start of a textblock.
|
|
10
|
+
*/
|
|
11
|
+
joinTextblockBackward: () => ReturnType
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const joinTextblockBackward: RawCommands['joinTextblockBackward'] = () => ({ state, dispatch }) => {
|
|
17
|
+
return originalCommand(state, dispatch)
|
|
18
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { joinTextblockForward as originalCommand } from '@tiptap/pm/commands'
|
|
2
|
+
|
|
3
|
+
import { RawCommands } from '../types.js'
|
|
4
|
+
|
|
5
|
+
declare module '@tiptap/core' {
|
|
6
|
+
interface Commands<ReturnType> {
|
|
7
|
+
joinTextblockForward: {
|
|
8
|
+
/**
|
|
9
|
+
* A more limited form of joinForward that only tries to join the current textblock to the one after it, if the cursor is at the end of a textblock.
|
|
10
|
+
*/
|
|
11
|
+
joinTextblockForward: () => ReturnType
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const joinTextblockForward: RawCommands['joinTextblockForward'] = () => ({ state, dispatch }) => {
|
|
17
|
+
return originalCommand(state, dispatch)
|
|
18
|
+
}
|
|
@@ -13,7 +13,6 @@ export function createChainableState(config: {
|
|
|
13
13
|
...state,
|
|
14
14
|
apply: state.apply.bind(state),
|
|
15
15
|
applyTransaction: state.applyTransaction.bind(state),
|
|
16
|
-
filterTransaction: state.filterTransaction,
|
|
17
16
|
plugins: state.plugins,
|
|
18
17
|
schema: state.schema,
|
|
19
18
|
reconfigure: state.reconfigure.bind(state),
|
|
@@ -26,6 +26,10 @@ export function getMarksBetween(from: number, to: number, doc: ProseMirrorNode):
|
|
|
26
26
|
})
|
|
27
27
|
} else {
|
|
28
28
|
doc.nodesBetween(from, to, (node, pos) => {
|
|
29
|
+
if (!node || node?.nodeSize === undefined) {
|
|
30
|
+
return
|
|
31
|
+
}
|
|
32
|
+
|
|
29
33
|
marks.push(
|
|
30
34
|
...node.marks.map(mark => ({
|
|
31
35
|
from: pos,
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,24 @@
|
|
|
1
|
+
const removeWhitespaces = (node: HTMLElement) => {
|
|
2
|
+
const children = node.childNodes
|
|
3
|
+
|
|
4
|
+
for (let i = children.length - 1; i >= 0; i -= 1) {
|
|
5
|
+
const child = children[i]
|
|
6
|
+
|
|
7
|
+
if (child.nodeType === 3 && child.nodeValue && /^(\n\s\s|\n)$/.test(child.nodeValue)) {
|
|
8
|
+
node.removeChild(child)
|
|
9
|
+
} else if (child.nodeType === 1) {
|
|
10
|
+
removeWhitespaces(child as HTMLElement)
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return node
|
|
15
|
+
}
|
|
16
|
+
|
|
1
17
|
export function elementFromString(value: string): HTMLElement {
|
|
2
18
|
// add a wrapper to preserve leading and trailing whitespace
|
|
3
19
|
const wrappedValue = `<body>${value}</body>`
|
|
4
20
|
|
|
5
|
-
|
|
21
|
+
const html = new window.DOMParser().parseFromString(wrappedValue, 'text/html').body
|
|
22
|
+
|
|
23
|
+
return removeWhitespaces(html)
|
|
6
24
|
}
|