@tiptap/core 2.5.4 → 2.5.6

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.
@@ -6,10 +6,8 @@ import { ExtensionManager } from './ExtensionManager.js';
6
6
  import { NodePos } from './NodePos.js';
7
7
  import { CanCommands, ChainedCommands, EditorEvents, EditorOptions, JSONContent, SingleCommands, TextSerializer } from './types.js';
8
8
  export * as extensions from './extensions/index.js';
9
- declare global {
10
- interface HTMLElement {
11
- editor?: Editor;
12
- }
9
+ export interface TiptapEditorHTMLElement extends HTMLElement {
10
+ editor?: Editor;
13
11
  }
14
12
  export declare class Editor extends EventEmitter<EditorEvents> {
15
13
  private commandManager;
@@ -40,6 +40,11 @@ export interface EditorEvents {
40
40
  editor: Editor;
41
41
  transaction: Transaction;
42
42
  };
43
+ beforeTransaction: {
44
+ editor: Editor;
45
+ transaction: Transaction;
46
+ nextState: EditorState;
47
+ };
43
48
  transaction: {
44
49
  editor: Editor;
45
50
  transaction: Transaction;
@@ -149,9 +154,7 @@ export type GlobalAttributes = {
149
154
  /**
150
155
  * The attributes to add to the node or mark types.
151
156
  */
152
- attributes: {
153
- [key: string]: Attribute;
154
- };
157
+ attributes: Record<string, Attribute | undefined>;
155
158
  }[];
156
159
  export type PickValue<T, K extends keyof T> = T[K];
157
160
  export type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
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.5.4",
4
+ "version": "2.5.6",
5
5
  "homepage": "https://tiptap.dev",
6
6
  "keywords": [
7
7
  "tiptap",
@@ -32,10 +32,10 @@
32
32
  "dist"
33
33
  ],
34
34
  "devDependencies": {
35
- "@tiptap/pm": "^2.5.4"
35
+ "@tiptap/pm": "^2.5.6"
36
36
  },
37
37
  "peerDependencies": {
38
- "@tiptap/pm": "^2.5.4"
38
+ "@tiptap/pm": "^2.5.6"
39
39
  },
40
40
  "repository": {
41
41
  "type": "git",
package/src/Editor.ts CHANGED
@@ -39,10 +39,8 @@ import { isFunction } from './utilities/isFunction.js'
39
39
 
40
40
  export * as extensions from './extensions/index.js'
41
41
 
42
- declare global {
43
- interface HTMLElement {
44
- editor?: Editor;
45
- }
42
+ export interface TiptapEditorHTMLElement extends HTMLElement {
43
+ editor?: Editor
46
44
  }
47
45
 
48
46
  export class Editor extends EventEmitter<EditorEvents> {
@@ -342,7 +340,7 @@ export class Editor extends EventEmitter<EditorEvents> {
342
340
 
343
341
  // Let’s store the editor instance in the DOM element.
344
342
  // So we’ll have access to it for tests.
345
- const dom = this.view.dom as HTMLElement
343
+ const dom = this.view.dom as TiptapEditorHTMLElement
346
344
 
347
345
  dom.editor = this
348
346
  }
@@ -351,6 +349,10 @@ export class Editor extends EventEmitter<EditorEvents> {
351
349
  * Creates all node views.
352
350
  */
353
351
  public createNodeViews(): void {
352
+ if (this.view.isDestroyed) {
353
+ return
354
+ }
355
+
354
356
  this.view.setProps({
355
357
  nodeViews: this.extensionManager.nodeViews,
356
358
  })
@@ -406,6 +408,11 @@ export class Editor extends EventEmitter<EditorEvents> {
406
408
  const state = this.state.apply(transaction)
407
409
  const selectionHasChanged = !this.state.selection.eq(state.selection)
408
410
 
411
+ this.emit('beforeTransaction', {
412
+ editor: this,
413
+ transaction,
414
+ nextState: state,
415
+ })
409
416
  this.view.updateState(state)
410
417
  this.emit('transaction', {
411
418
  editor: this,
@@ -61,18 +61,30 @@ export const splitBlock: RawCommands['splitBlock'] = ({ keepMarks = true } = {})
61
61
  return false
62
62
  }
63
63
 
64
- if (dispatch) {
65
- const atEnd = $to.parentOffset === $to.parent.content.size
66
-
67
- if (selection instanceof TextSelection) {
68
- tr.deleteSelection()
69
- }
70
-
71
- const deflt = $from.depth === 0
72
- ? undefined
73
- : defaultBlockAt($from.node(-1).contentMatchAt($from.indexAfter(-1)))
74
-
75
- let types = atEnd && deflt
64
+ const atEnd = $to.parentOffset === $to.parent.content.size
65
+
66
+ const deflt = $from.depth === 0
67
+ ? undefined
68
+ : defaultBlockAt($from.node(-1).contentMatchAt($from.indexAfter(-1)))
69
+
70
+ let types = atEnd && deflt
71
+ ? [
72
+ {
73
+ type: deflt,
74
+ attrs: newAttributes,
75
+ },
76
+ ]
77
+ : undefined
78
+
79
+ let can = canSplit(tr.doc, tr.mapping.map($from.pos), 1, types)
80
+
81
+ if (
82
+ !types
83
+ && !can
84
+ && canSplit(tr.doc, tr.mapping.map($from.pos), 1, deflt ? [{ type: deflt }] : undefined)
85
+ ) {
86
+ can = true
87
+ types = deflt
76
88
  ? [
77
89
  {
78
90
  type: deflt,
@@ -80,26 +92,14 @@ export const splitBlock: RawCommands['splitBlock'] = ({ keepMarks = true } = {})
80
92
  },
81
93
  ]
82
94
  : undefined
95
+ }
83
96
 
84
- let can = canSplit(tr.doc, tr.mapping.map($from.pos), 1, types)
85
-
86
- if (
87
- !types
88
- && !can
89
- && canSplit(tr.doc, tr.mapping.map($from.pos), 1, deflt ? [{ type: deflt }] : undefined)
90
- ) {
91
- can = true
92
- types = deflt
93
- ? [
94
- {
95
- type: deflt,
96
- attrs: newAttributes,
97
- },
98
- ]
99
- : undefined
100
- }
101
-
97
+ if (dispatch) {
102
98
  if (can) {
99
+ if (selection instanceof TextSelection) {
100
+ tr.deleteSelection()
101
+ }
102
+
103
103
  tr.split(tr.mapping.map($from.pos), 1, types)
104
104
 
105
105
  if (deflt && !atEnd && !$from.parentOffset && $from.parent.type !== deflt) {
@@ -119,5 +119,5 @@ export const splitBlock: RawCommands['splitBlock'] = ({ keepMarks = true } = {})
119
119
  tr.scrollIntoView()
120
120
  }
121
121
 
122
- return true
122
+ return can
123
123
  }
@@ -24,7 +24,7 @@ export const getTextContentFromNodes = ($from: ResolvedPos, maxMatch = 500) => {
24
24
  || node.textContent
25
25
  || '%leaf%'
26
26
 
27
- textBefore += chunk.slice(0, Math.max(0, sliceEndPos - pos))
27
+ textBefore += node.isAtom ? chunk : chunk.slice(0, Math.max(0, sliceEndPos - pos))
28
28
  },
29
29
  )
30
30
 
@@ -1,7 +1,7 @@
1
1
  import { Node as ProseMirrorNode } from '@tiptap/pm/model'
2
2
 
3
3
  export function isNodeEmpty(node: ProseMirrorNode): boolean {
4
- const defaultContent = node.type.createAndFill()
4
+ const defaultContent = node.type.createAndFill(node.attrs)
5
5
 
6
6
  if (!defaultContent) {
7
7
  return false
package/src/types.ts CHANGED
@@ -1,5 +1,8 @@
1
1
  import {
2
- Mark as ProseMirrorMark, Node as ProseMirrorNode, NodeType, ParseOptions,
2
+ Mark as ProseMirrorMark,
3
+ Node as ProseMirrorNode,
4
+ NodeType,
5
+ ParseOptions,
3
6
  } from '@tiptap/pm/model'
4
7
  import { EditorState, Transaction } from '@tiptap/pm/state'
5
8
  import {
@@ -14,266 +17,265 @@ import {
14
17
  import { Mark } from './Mark.js'
15
18
  import { Node } from './Node.js'
16
19
 
17
- export type AnyConfig = ExtensionConfig | NodeConfig | MarkConfig
18
- export type AnyExtension = Extension | Node | Mark
19
- export type Extensions = AnyExtension[]
20
+ export type AnyConfig = ExtensionConfig | NodeConfig | MarkConfig;
21
+ export type AnyExtension = Extension | Node | Mark;
22
+ export type Extensions = AnyExtension[];
20
23
 
21
24
  export type ParentConfig<T> = Partial<{
22
25
  [P in keyof T]: Required<T>[P] extends (...args: any) => any
23
26
  ? (...args: Parameters<Required<T>[P]>) => ReturnType<Required<T>[P]>
24
- : T[P]
25
- }>
27
+ : T[P];
28
+ }>;
26
29
 
27
- export type Primitive = null | undefined | string | number | boolean | symbol | bigint
30
+ export type Primitive = null | undefined | string | number | boolean | symbol | bigint;
28
31
 
29
32
  export type RemoveThis<T> = T extends (...args: any) => any
30
33
  ? (...args: Parameters<T>) => ReturnType<T>
31
- : T
34
+ : T;
32
35
 
33
- export type MaybeReturnType<T> = T extends (...args: any) => any ? ReturnType<T> : T
36
+ export type MaybeReturnType<T> = T extends (...args: any) => any ? ReturnType<T> : T;
34
37
 
35
38
  export type MaybeThisParameterType<T> = Exclude<T, Primitive> extends (...args: any) => any
36
39
  ? ThisParameterType<Exclude<T, Primitive>>
37
- : any
40
+ : any;
38
41
 
39
42
  export interface EditorEvents {
40
- beforeCreate: { editor: Editor }
41
- create: { editor: Editor }
43
+ beforeCreate: { editor: Editor };
44
+ create: { editor: Editor };
42
45
  contentError: {
43
- editor: Editor,
44
- error: Error,
46
+ editor: Editor;
47
+ error: Error;
45
48
  /**
46
49
  * If called, will re-initialize the editor with the collaboration extension removed.
47
50
  * This will prevent syncing back deletions of content not present in the current schema.
48
51
  */
49
- disableCollaboration: () => void
50
- }
51
- update: { editor: Editor; transaction: Transaction }
52
- selectionUpdate: { editor: Editor; transaction: Transaction }
53
- transaction: { editor: Editor; transaction: Transaction }
54
- focus: { editor: Editor; event: FocusEvent; transaction: Transaction }
55
- blur: { editor: Editor; event: FocusEvent; transaction: Transaction }
56
- destroy: void
52
+ disableCollaboration: () => void;
53
+ };
54
+ update: { editor: Editor; transaction: Transaction };
55
+ selectionUpdate: { editor: Editor; transaction: Transaction };
56
+ beforeTransaction: { editor: Editor; transaction: Transaction; nextState: EditorState };
57
+ transaction: { editor: Editor; transaction: Transaction };
58
+ focus: { editor: Editor; event: FocusEvent; transaction: Transaction };
59
+ blur: { editor: Editor; event: FocusEvent; transaction: Transaction };
60
+ destroy: void;
57
61
  }
58
62
 
59
- export type EnableRules = (AnyExtension | string)[] | boolean
63
+ export type EnableRules = (AnyExtension | string)[] | boolean;
60
64
 
61
65
  export interface EditorOptions {
62
- element: Element
63
- content: Content
64
- extensions: Extensions
65
- injectCSS: boolean
66
- injectNonce: string | undefined
67
- autofocus: FocusPosition
68
- editable: boolean
69
- editorProps: EditorProps
70
- parseOptions: ParseOptions
66
+ element: Element;
67
+ content: Content;
68
+ extensions: Extensions;
69
+ injectCSS: boolean;
70
+ injectNonce: string | undefined;
71
+ autofocus: FocusPosition;
72
+ editable: boolean;
73
+ editorProps: EditorProps;
74
+ parseOptions: ParseOptions;
71
75
  coreExtensionOptions?: {
72
76
  clipboardTextSerializer?: {
73
- blockSeparator?: string
74
- }
75
- }
76
- enableInputRules: EnableRules
77
- enablePasteRules: EnableRules
78
- enableCoreExtensions: boolean
77
+ blockSeparator?: string;
78
+ };
79
+ };
80
+ enableInputRules: EnableRules;
81
+ enablePasteRules: EnableRules;
82
+ enableCoreExtensions: boolean;
79
83
  /**
80
84
  * If `true`, the editor will check the content for errors on initialization.
81
85
  * Emitting the `contentError` event if the content is invalid.
82
86
  * Which can be used to show a warning or error message to the user.
83
87
  * @default false
84
88
  */
85
- enableContentCheck: boolean
86
- onBeforeCreate: (props: EditorEvents['beforeCreate']) => void
87
- onCreate: (props: EditorEvents['create']) => void
89
+ enableContentCheck: boolean;
90
+ onBeforeCreate: (props: EditorEvents['beforeCreate']) => void;
91
+ onCreate: (props: EditorEvents['create']) => void;
88
92
  /**
89
93
  * Called when the editor encounters an error while parsing the content.
90
94
  * Only enabled if `enableContentCheck` is `true`.
91
95
  */
92
- onContentError: (props: EditorEvents['contentError']) => void
93
- onUpdate: (props: EditorEvents['update']) => void
94
- onSelectionUpdate: (props: EditorEvents['selectionUpdate']) => void
95
- onTransaction: (props: EditorEvents['transaction']) => void
96
- onFocus: (props: EditorEvents['focus']) => void
97
- onBlur: (props: EditorEvents['blur']) => void
98
- onDestroy: (props: EditorEvents['destroy']) => void
96
+ onContentError: (props: EditorEvents['contentError']) => void;
97
+ onUpdate: (props: EditorEvents['update']) => void;
98
+ onSelectionUpdate: (props: EditorEvents['selectionUpdate']) => void;
99
+ onTransaction: (props: EditorEvents['transaction']) => void;
100
+ onFocus: (props: EditorEvents['focus']) => void;
101
+ onBlur: (props: EditorEvents['blur']) => void;
102
+ onDestroy: (props: EditorEvents['destroy']) => void;
99
103
  }
100
104
 
101
- export type HTMLContent = string
105
+ export type HTMLContent = string;
102
106
 
103
107
  export type JSONContent = {
104
- type?: string
105
- attrs?: Record<string, any>
106
- content?: JSONContent[]
108
+ type?: string;
109
+ attrs?: Record<string, any>;
110
+ content?: JSONContent[];
107
111
  marks?: {
108
- type: string
109
- attrs?: Record<string, any>
110
- [key: string]: any
111
- }[]
112
- text?: string
113
- [key: string]: any
114
- }
112
+ type: string;
113
+ attrs?: Record<string, any>;
114
+ [key: string]: any;
115
+ }[];
116
+ text?: string;
117
+ [key: string]: any;
118
+ };
115
119
 
116
- export type Content = HTMLContent | JSONContent | JSONContent[] | null
120
+ export type Content = HTMLContent | JSONContent | JSONContent[] | null;
117
121
 
118
122
  export type CommandProps = {
119
- editor: Editor
120
- tr: Transaction
121
- commands: SingleCommands
122
- can: () => CanCommands
123
- chain: () => ChainedCommands
124
- state: EditorState
125
- view: EditorView
126
- dispatch: ((args?: any) => any) | undefined
127
- }
123
+ editor: Editor;
124
+ tr: Transaction;
125
+ commands: SingleCommands;
126
+ can: () => CanCommands;
127
+ chain: () => ChainedCommands;
128
+ state: EditorState;
129
+ view: EditorView;
130
+ dispatch: ((args?: any) => any) | undefined;
131
+ };
128
132
 
129
- export type Command = (props: CommandProps) => boolean
133
+ export type Command = (props: CommandProps) => boolean;
130
134
 
131
- export type CommandSpec = (...args: any[]) => Command
135
+ export type CommandSpec = (...args: any[]) => Command;
132
136
 
133
- export type KeyboardShortcutCommand = (props: { editor: Editor }) => boolean
137
+ export type KeyboardShortcutCommand = (props: { editor: Editor }) => boolean;
134
138
 
135
139
  export type Attribute = {
136
- default?: any
137
- rendered?: boolean
138
- renderHTML?: ((attributes: Record<string, any>) => Record<string, any> | null) | null
139
- parseHTML?: ((element: HTMLElement) => any | null) | null
140
- keepOnSplit?: boolean
141
- isRequired?: boolean
142
- }
140
+ default?: any;
141
+ rendered?: boolean;
142
+ renderHTML?: ((attributes: Record<string, any>) => Record<string, any> | null) | null;
143
+ parseHTML?: ((element: HTMLElement) => any | null) | null;
144
+ keepOnSplit?: boolean;
145
+ isRequired?: boolean;
146
+ };
143
147
 
144
148
  export type Attributes = {
145
- [key: string]: Attribute
146
- }
149
+ [key: string]: Attribute;
150
+ };
147
151
 
148
152
  export type ExtensionAttribute = {
149
- type: string
150
- name: string
151
- attribute: Required<Attribute>
152
- }
153
+ type: string;
154
+ name: string;
155
+ attribute: Required<Attribute>;
156
+ };
153
157
 
154
158
  export type GlobalAttributes = {
155
159
  /**
156
160
  * The node & mark types this attribute should be applied to.
157
161
  */
158
- types: string[]
162
+ types: string[];
159
163
  /**
160
164
  * The attributes to add to the node or mark types.
161
165
  */
162
- attributes: {
163
- [key: string]: Attribute
164
- }
165
- }[]
166
+ attributes: Record<string, Attribute | undefined>;
167
+ }[];
166
168
 
167
- export type PickValue<T, K extends keyof T> = T[K]
169
+ export type PickValue<T, K extends keyof T> = T[K];
168
170
 
169
171
  export type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
170
- k: infer I,
172
+ k: infer I
171
173
  ) => void
172
174
  ? I
173
- : never
175
+ : never;
174
176
 
175
177
  export type Diff<T extends keyof any, U extends keyof any> = ({ [P in T]: P } & {
176
- [P in U]: never
177
- } & { [x: string]: never })[T]
178
+ [P in U]: never;
179
+ } & { [x: string]: never })[T];
178
180
 
179
- export type Overwrite<T, U> = Pick<T, Diff<keyof T, keyof U>> & U
181
+ export type Overwrite<T, U> = Pick<T, Diff<keyof T, keyof U>> & U;
180
182
 
181
- export type ValuesOf<T> = T[keyof T]
183
+ export type ValuesOf<T> = T[keyof T];
182
184
 
183
- export type KeysWithTypeOf<T, Type> = { [P in keyof T]: T[P] extends Type ? P : never }[keyof T]
185
+ export type KeysWithTypeOf<T, Type> = { [P in keyof T]: T[P] extends Type ? P : never }[keyof T];
184
186
 
185
187
  export type DecorationWithType = Decoration & {
186
- type: NodeType
187
- }
188
+ type: NodeType;
189
+ };
188
190
 
189
191
  export type NodeViewProps = {
190
- editor: Editor
191
- node: ProseMirrorNode
192
- decorations: DecorationWithType[]
193
- selected: boolean
194
- extension: Node
195
- getPos: () => number
196
- updateAttributes: (attributes: Record<string, any>) => void
197
- deleteNode: () => void
198
- }
192
+ editor: Editor;
193
+ node: ProseMirrorNode;
194
+ decorations: DecorationWithType[];
195
+ selected: boolean;
196
+ extension: Node;
197
+ getPos: () => number;
198
+ updateAttributes: (attributes: Record<string, any>) => void;
199
+ deleteNode: () => void;
200
+ };
199
201
 
200
202
  export interface NodeViewRendererOptions {
201
- stopEvent: ((props: { event: Event }) => boolean) | null
203
+ stopEvent: ((props: { event: Event }) => boolean) | null;
202
204
  ignoreMutation:
203
205
  | ((props: { mutation: MutationRecord | { type: 'selection'; target: Element } }) => boolean)
204
- | null
205
- contentDOMElementTag: string
206
+ | null;
207
+ contentDOMElementTag: string;
206
208
  }
207
209
 
208
210
  export type NodeViewRendererProps = {
209
- editor: Editor
210
- node: ProseMirrorNode
211
- getPos: (() => number) | boolean
212
- HTMLAttributes: Record<string, any>
213
- decorations: Decoration[]
214
- extension: Node
215
- }
211
+ editor: Editor;
212
+ node: ProseMirrorNode;
213
+ getPos: (() => number) | boolean;
214
+ HTMLAttributes: Record<string, any>;
215
+ decorations: Decoration[];
216
+ extension: Node;
217
+ };
216
218
 
217
- export type NodeViewRenderer = (props: NodeViewRendererProps) => NodeView | {}
219
+ export type NodeViewRenderer = (props: NodeViewRendererProps) => NodeView | {};
218
220
 
219
- export type AnyCommands = Record<string, (...args: any[]) => Command>
221
+ export type AnyCommands = Record<string, (...args: any[]) => Command>;
220
222
 
221
223
  export type UnionCommands<T = Command> = UnionToIntersection<
222
224
  ValuesOf<Pick<Commands<T>, KeysWithTypeOf<Commands<T>, {}>>>
223
- >
225
+ >;
224
226
 
225
227
  export type RawCommands = {
226
- [Item in keyof UnionCommands]: UnionCommands<Command>[Item]
227
- }
228
+ [Item in keyof UnionCommands]: UnionCommands<Command>[Item];
229
+ };
228
230
 
229
231
  export type SingleCommands = {
230
- [Item in keyof UnionCommands]: UnionCommands<boolean>[Item]
231
- }
232
+ [Item in keyof UnionCommands]: UnionCommands<boolean>[Item];
233
+ };
232
234
 
233
235
  export type ChainedCommands = {
234
- [Item in keyof UnionCommands]: UnionCommands<ChainedCommands>[Item]
236
+ [Item in keyof UnionCommands]: UnionCommands<ChainedCommands>[Item];
235
237
  } & {
236
- run: () => boolean
237
- }
238
+ run: () => boolean;
239
+ };
238
240
 
239
- export type CanCommands = SingleCommands & { chain: () => ChainedCommands }
241
+ export type CanCommands = SingleCommands & { chain: () => ChainedCommands };
240
242
 
241
- export type FocusPosition = 'start' | 'end' | 'all' | number | boolean | null
243
+ export type FocusPosition = 'start' | 'end' | 'all' | number | boolean | null;
242
244
 
243
245
  export type Range = {
244
- from: number
245
- to: number
246
- }
246
+ from: number;
247
+ to: number;
248
+ };
247
249
 
248
250
  export type NodeRange = {
249
- node: ProseMirrorNode
250
- from: number
251
- to: number
252
- }
251
+ node: ProseMirrorNode;
252
+ from: number;
253
+ to: number;
254
+ };
253
255
 
254
256
  export type MarkRange = {
255
- mark: ProseMirrorMark
256
- from: number
257
- to: number
258
- }
257
+ mark: ProseMirrorMark;
258
+ from: number;
259
+ to: number;
260
+ };
259
261
 
260
- export type Predicate = (node: ProseMirrorNode) => boolean
262
+ export type Predicate = (node: ProseMirrorNode) => boolean;
261
263
 
262
264
  export type NodeWithPos = {
263
- node: ProseMirrorNode
264
- pos: number
265
- }
265
+ node: ProseMirrorNode;
266
+ pos: number;
267
+ };
266
268
 
267
269
  export type TextSerializer = (props: {
268
- node: ProseMirrorNode
269
- pos: number
270
- parent: ProseMirrorNode
271
- index: number
272
- range: Range
273
- }) => string
270
+ node: ProseMirrorNode;
271
+ pos: number;
272
+ parent: ProseMirrorNode;
273
+ index: number;
274
+ range: Range;
275
+ }) => string;
274
276
 
275
277
  export type ExtendedRegExpMatchArray = RegExpMatchArray & {
276
- data?: Record<string, any>
277
- }
278
+ data?: Record<string, any>;
279
+ };
278
280
 
279
- export type Dispatch = ((args?: any) => any) | undefined
281
+ export type Dispatch = ((args?: any) => any) | undefined;