@portabletext/editor 1.12.3 → 1.13.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.
@@ -71,6 +71,7 @@ export type InternalEditorEvent =
71
71
  type: 'behavior event'
72
72
  behaviorEvent: BehaviorEvent
73
73
  editor: PortableTextSlateEditor
74
+ nativeEvent?: {preventDefault: () => void}
74
75
  }
75
76
  | {
76
77
  type: 'behavior action intends'
@@ -271,10 +272,11 @@ export const editorMachine = setup({
271
272
 
272
273
  for (const eventBehavior of eventBehaviors) {
273
274
  const shouldRun =
274
- eventBehavior.guard?.({
275
+ eventBehavior.guard === undefined ||
276
+ eventBehavior.guard({
275
277
  context: behaviorContext,
276
278
  event: event.behaviorEvent,
277
- }) ?? true
279
+ })
278
280
 
279
281
  if (!shouldRun) {
280
282
  continue
@@ -303,6 +305,7 @@ export const editorMachine = setup({
303
305
  }
304
306
 
305
307
  if (behaviorOverwritten) {
308
+ event.nativeEvent?.preventDefault()
306
309
  break
307
310
  }
308
311
  }
@@ -1,6 +1,6 @@
1
1
  import {isPortableTextSpan, isPortableTextTextBlock} from '@sanity/types'
2
2
  import type {KeyboardEvent} from 'react'
3
- import {Editor, Node, Path, Range, Transforms} from 'slate'
3
+ import {Editor, Node, Range} from 'slate'
4
4
  import type {ReactEditor} from 'slate-react'
5
5
  import type {PortableTextSlateEditor} from '../../types/editor'
6
6
  import type {HotkeyOptions} from '../../types/options'
@@ -80,53 +80,6 @@ export function createWithHotkeys(
80
80
  const isTab = isHotkey('tab', event.nativeEvent)
81
81
  const isShiftEnter = isHotkey('shift+enter', event.nativeEvent)
82
82
  const isShiftTab = isHotkey('shift+tab', event.nativeEvent)
83
- const isArrowDown = isHotkey('down', event.nativeEvent)
84
- const isArrowUp = isHotkey('up', event.nativeEvent)
85
-
86
- // Check if the user is in a void block, in that case, add an empty text block below if there is no next block
87
- if (isArrowDown && editor.selection) {
88
- const focusBlock = Node.descendant(
89
- editor,
90
- editor.selection.focus.path.slice(0, 1),
91
- ) as SlateTextBlock | VoidElement
92
-
93
- if (focusBlock && Editor.isVoid(editor, focusBlock)) {
94
- const nextPath = Path.next(editor.selection.focus.path.slice(0, 1))
95
- const nextBlock = Node.has(editor, nextPath)
96
- if (!nextBlock) {
97
- Transforms.insertNodes(
98
- editor,
99
- editor.pteCreateTextBlock({decorators: []}),
100
- {
101
- at: nextPath,
102
- },
103
- )
104
- Transforms.select(editor, {path: [...nextPath, 0], offset: 0})
105
- editor.onChange()
106
- return
107
- }
108
- }
109
- }
110
- if (isArrowUp && editor.selection) {
111
- const isFirstBlock = editor.selection.focus.path[0] === 0
112
- const focusBlock = Node.descendant(
113
- editor,
114
- editor.selection.focus.path.slice(0, 1),
115
- ) as SlateTextBlock | VoidElement
116
-
117
- if (isFirstBlock && focusBlock && Editor.isVoid(editor, focusBlock)) {
118
- Transforms.insertNodes(
119
- editor,
120
- editor.pteCreateTextBlock({decorators: []}),
121
- {
122
- at: [0],
123
- },
124
- )
125
- Transforms.select(editor, {path: [0, 0], offset: 0})
126
- editor.onChange()
127
- return
128
- }
129
- }
130
83
 
131
84
  // Tab for lists
132
85
  // Only steal tab when we are on a plain text span or we are at the start of the line (fallback if the whole block is annotated or contains a single inline object)
package/src/index.ts CHANGED
@@ -9,6 +9,10 @@ export {
9
9
  createMarkdownBehaviors,
10
10
  type MarkdownBehaviorsConfig,
11
11
  } from './editor/behavior/behavior.markdown'
12
+ export {
13
+ createCodeEditorBehaviors,
14
+ type CodeEditorBehaviorsConfig,
15
+ } from './editor/behavior/behavior.code-editor'
12
16
  export {
13
17
  defineBehavior,
14
18
  type Behavior,
@@ -1,61 +1,112 @@
1
1
  import {expect, test} from 'vitest'
2
2
  import {isHotkey, type KeyboardEventLike} from './is-hotkey'
3
3
 
4
- function e(value: string | number, ...modifiers: string[]) {
4
+ function e(
5
+ value: string | number,
6
+ modifiers: Array<'altKey' | 'ctrlKey' | 'metaKey' | 'shiftKey'> = [],
7
+ ) {
5
8
  return {
6
9
  ...(typeof value === 'string' ? {key: value} : {keyCode: value}),
7
- altKey: modifiers.includes('alt'),
8
- ctrlKey: modifiers.includes('ctrl'),
9
- metaKey: modifiers.includes('meta'),
10
- shiftKey: modifiers.includes('shift'),
10
+ altKey: modifiers.includes('altKey'),
11
+ ctrlKey: modifiers.includes('ctrlKey'),
12
+ metaKey: modifiers.includes('metaKey'),
13
+ shiftKey: modifiers.includes('shiftKey'),
11
14
  } as KeyboardEventLike
12
15
  }
13
16
 
14
- type TestCase = [KeyboardEventLike, string, boolean]
15
-
16
- const testCases = [
17
- [e(83, 'meta'), 'Meta+S', true],
18
- [e(83, 'alt', 'meta'), 'Meta+Alt+s', true],
19
- [e(83, 'meta'), 'meta+s', true],
20
- [e(83, 'meta'), 'cmd+s', true],
21
- [e(32, 'meta'), 'cmd+space', true],
22
- [e(187, 'meta'), 'cmd+=', true],
23
- [e(83, 'ctrl'), 'mod+s', true],
24
- [e(16, 'shift'), 'shift', true],
25
- [e(93, 'meta'), 'meta', true],
26
- [e(65), 'a', true],
27
- [e(83, 'alt', 'meta'), 'cmd+s', false],
28
- [e('a', 'ctrl'), 'a', false],
29
- [e(83, 'alt', 'meta'), 'cmd+alt?+s', true],
30
- [e(83, 'meta'), 'cmd+alt?+s', true],
31
- [e('?'), '?', true],
32
- [e(13), 'enter', true],
33
- [e(65, 'meta'), 'cmd+a', true],
34
- [e(83, 'meta'), 'cmd+s', true],
35
- [e('s', 'meta'), 'Meta+S', true],
36
- [e('ß', 'alt', 'meta'), 'Meta+Alt+ß', true],
37
- [e('s', 'meta'), 'meta+s', true],
38
- [e('s', 'meta'), 'cmd+s', true],
39
- [e(' ', 'meta'), 'cmd+space', true],
40
- [e('+', 'meta'), 'cmd++', true],
41
- [e('s', 'ctrl'), 'mod+s', true],
42
- [e('Shift', 'shift'), 'shift', true],
43
- [e('a'), 'a', true],
44
- [e('s', 'alt', 'meta'), 'cmd+s', false],
45
- [e('a', 'ctrl'), 'a', false],
46
- [e('s', 'alt', 'meta'), 'cmd+alt?+s', true],
47
- [e('s', 'meta'), 'cmd+alt?+s', true],
48
- [e('Enter'), 'enter', true],
49
- [e('a', 'meta'), 'meta+a', true],
50
- [e('s', 'meta'), 'meta+s', true],
17
+ type TestCase = [string, KeyboardEventLike, boolean]
18
+
19
+ const testCases: TestCase[] = [
20
+ ['meta', e('Meta', ['metaKey']), true],
21
+ ['Meta', e('Meta', ['metaKey']), true],
22
+ ['meta', e(93, ['metaKey']), true],
23
+ ['Meta', e(93, ['metaKey']), true],
24
+
25
+ ['meta+s', e('s', ['metaKey']), true],
26
+ ['Meta+S', e('s', ['metaKey']), true],
27
+ ['meta+s', e(83, ['metaKey']), true],
28
+ ['Meta+S', e(83, ['metaKey']), true],
29
+
30
+ ['cmd+space', e(' ', ['metaKey']), true],
31
+ ['Cmd+Space', e(' ', ['metaKey']), true],
32
+ ['cmd+space', e(32, ['metaKey']), true],
33
+ ['Cmd+Space', e(32, ['metaKey']), true],
34
+
35
+ ['cmd+alt?+s', e('s', ['metaKey']), true],
36
+ ['cmd+alt?+s', e('s', ['metaKey', 'altKey']), true],
37
+ ['cmd+alt?+s', e(83, ['metaKey']), true],
38
+ ['cmd+alt?+s', e(83, ['metaKey', 'altKey']), true],
39
+
40
+ ['Cmd+Alt?+S', e('s', ['metaKey']), true],
41
+ ['Cmd+Alt?+S', e('s', ['metaKey', 'altKey']), true],
42
+ ['Cmd+Alt?+S', e(83, ['metaKey']), true],
43
+ ['Cmd+Alt?+S', e(83, ['metaKey', 'altKey']), true],
44
+
45
+ ['cmd+s', e('s', ['metaKey', 'altKey']), false],
46
+ ['Cmd+S', e('s', ['metaKey', 'altKey']), false],
47
+ ['cmd+s', e(83, ['metaKey', 'altKey']), false],
48
+ ['Cmd+S', e(83, ['metaKey', 'altKey']), false],
49
+
50
+ ['cmd+s', e('s', ['metaKey']), true],
51
+ ['Cmd+s', e('s', ['metaKey']), true],
52
+ ['cmd+s', e(83, ['metaKey']), true],
53
+ ['Cmd+s', e(83, ['metaKey']), true],
54
+
55
+ ['mod+s', e('s', ['ctrlKey']), true],
56
+ ['Mod+S', e('s', ['ctrlKey']), true],
57
+ ['mod+s', e(83, ['ctrlKey']), true],
58
+ ['Mod+S', e(83, ['ctrlKey']), true],
59
+
60
+ ['meta+alt+s', e('s', ['metaKey', 'altKey']), true],
61
+ ['Meta+Alt+S', e('s', ['metaKey', 'altKey']), true],
62
+ ['meta+alt+s', e(83, ['metaKey', 'altKey']), true],
63
+ ['Meta+Alt+S', e(83, ['metaKey', 'altKey']), true],
64
+
65
+ ['?', e('?'), true],
66
+ ['?', e('?', ['altKey']), false],
67
+
68
+ ['a', e('a'), true],
69
+ ['a', e('A'), true],
70
+ ['A', e('a'), true],
71
+ ['A', e('A'), true],
72
+ ['a', e(65), true],
73
+ ['A', e(65), true],
74
+
75
+ ['a', e('a', ['ctrlKey']), false],
76
+ ['A', e('a', ['ctrlKey']), false],
77
+ ['a', e(65, ['ctrlKey']), false],
78
+ ['A', e(65, ['ctrlKey']), false],
79
+
80
+ ['shift', e('Shift', ['shiftKey']), true],
81
+ ['Shift', e('Shift', ['shiftKey']), true],
82
+ ['shift', e(16, ['shiftKey']), true],
83
+ ['Shift', e(16, ['shiftKey']), true],
84
+
85
+ ['meta+a', e('a', ['metaKey']), true],
86
+ ['Meta+A', e('a', ['metaKey']), true],
87
+ ['cmd+a', e(65, ['metaKey']), true],
88
+ ['Cmd+A', e(65, ['metaKey']), true],
89
+
90
+ ['enter', e('Enter'), true],
91
+ ['Enter', e('Enter'), true],
92
+ ['enter', e(13), true],
93
+ ['Enter', e(13), true],
94
+
95
+ ['cmd+=', e(187, ['metaKey']), true],
96
+ ['Cmd+=', e(187, ['metaKey']), true],
97
+ ['cmd++', e('+', ['metaKey']), true],
98
+ ['Cmd++', e('+', ['metaKey']), true],
99
+
100
+ ['meta+alt+ß', e('ß', ['metaKey', 'altKey']), true],
101
+ ['Meta+Alt+ß', e('ß', ['metaKey', 'altKey']), true],
51
102
  ] satisfies Array<TestCase>
52
103
 
53
104
  test(isHotkey.name, () => {
54
105
  for (const testCase of testCases) {
55
- expect(isHotkey(testCase[1], testCase[0])).toBe(testCase[2])
106
+ expect(isHotkey(testCase[0], testCase[1])).toBe(testCase[2])
56
107
  }
57
108
 
58
- expect(() => isHotkey('ctrlalt+k', e('k', 'ctrl', 'alt'))).toThrowError(
59
- 'Unknown modifier: "ctrlalt"',
60
- )
109
+ expect(() =>
110
+ isHotkey('ctrlalt+k', e('k', ['ctrlKey', 'altKey'])),
111
+ ).toThrowError('Unknown modifier: "ctrlalt"')
61
112
  })
@@ -1,6 +1,6 @@
1
1
  export interface KeyboardEventLike {
2
2
  key: string
3
- keyCode: number
3
+ keyCode?: number
4
4
  altKey: boolean
5
5
  ctrlKey: boolean
6
6
  metaKey: boolean