@tiptap/core 2.0.0-beta.182 → 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/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.182",
4
+ "version": "2.0.0-beta.183",
5
5
  "homepage": "https://tiptap.dev",
6
6
  "keywords": [
7
7
  "tiptap",
@@ -38,5 +38,5 @@
38
38
  "directory": "packages/core"
39
39
  },
40
40
  "sideEffects": false,
41
- "gitHead": "3e05093f00dd4f52ddefeb2451622d3661a25123"
41
+ "gitHead": "5bcfb322ceb30575430bd8797f2787d65de80f2c"
42
42
  }
@@ -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 = undefined
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
@@ -169,6 +169,7 @@ export class Editor extends EventEmitter<EditorEvents> {
169
169
  */
170
170
  public setEditable(editable: boolean): void {
171
171
  this.setOptions({ editable })
172
+ this.emit('update', { editor: this, transaction: this.state.tr })
172
173
  }
173
174
 
174
175
  /**
@@ -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
- const keyMapPlugin = keymap(bindings)
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
@@ -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
 
@@ -1,4 +1,4 @@
1
- import { NodeSelection, Selection } from 'prosemirror-state'
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 minPos = Selection.atStart(doc).from
21
- const maxPos = Selection.atEnd(doc).to
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())
@@ -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 isObject(value) && value instanceof NodeSelection
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 isObject(value) && value instanceof TextSelection
4
+ return value instanceof TextSelection
7
5
  }
@@ -1 +0,0 @@
1
- export declare function isClass(value: any): boolean;
@@ -1 +0,0 @@
1
- export declare function isObject(value: any): boolean;
@@ -1,7 +0,0 @@
1
- export function isClass(value: any): boolean {
2
- if (value.constructor?.toString().substring(0, 5) !== 'class') {
3
- return false
4
- }
5
-
6
- return true
7
- }
@@ -1,10 +0,0 @@
1
- import { isClass } from './isClass'
2
-
3
- export function isObject(value: any): boolean {
4
- return (
5
- value
6
- && typeof value === 'object'
7
- && !Array.isArray(value)
8
- && !isClass(value)
9
- )
10
- }