@portabletext/editor 2.12.0 → 2.12.1

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.
@@ -1,5 +1,5 @@
1
1
  import { BlockOffset, BlockPath, ChildPath, EditorContext, EditorSelection, EditorSelectionPoint } from "../_chunks-dts/behavior.types.action.cjs";
2
- import * as _sanity_types9 from "@sanity/types";
2
+ import * as _sanity_types8 from "@sanity/types";
3
3
  import { KeyedSegment, PortableTextBlock, PortableTextTextBlock } from "@sanity/types";
4
4
  import { isSpan, isTextBlock } from "@portabletext/schema";
5
5
  /**
@@ -143,7 +143,7 @@ declare function mergeTextBlocks({
143
143
  context: Pick<EditorContext, 'keyGenerator' | 'schema'>;
144
144
  targetBlock: PortableTextTextBlock;
145
145
  incomingBlock: PortableTextTextBlock;
146
- }): PortableTextTextBlock<_sanity_types9.PortableTextObject | _sanity_types9.PortableTextSpan>;
146
+ }): PortableTextTextBlock<_sanity_types8.PortableTextObject | _sanity_types8.PortableTextSpan>;
147
147
  /**
148
148
  * @public
149
149
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@portabletext/editor",
3
- "version": "2.12.0",
3
+ "version": "2.12.1",
4
4
  "description": "Portable Text Editor made in React",
5
5
  "keywords": [
6
6
  "sanity",
@@ -51,6 +51,12 @@
51
51
  "require": "./lib/selectors/index.cjs",
52
52
  "default": "./lib/selectors/index.js"
53
53
  },
54
+ "./test": {
55
+ "default": "./src/test/_exports/index.ts"
56
+ },
57
+ "./test/vitest": {
58
+ "default": "./src/test/vitest/_exports/index.ts"
59
+ },
54
60
  "./utils": {
55
61
  "source": "./src/utils/_exports/index.ts",
56
62
  "import": "./lib/utils/index.js",
@@ -81,8 +87,8 @@
81
87
  "xstate": "^5.21.0",
82
88
  "@portabletext/block-tools": "^3.5.6",
83
89
  "@portabletext/keyboard-shortcuts": "^1.1.1",
84
- "@portabletext/schema": "^1.2.0",
85
- "@portabletext/patches": "^1.1.8"
90
+ "@portabletext/patches": "^1.1.8",
91
+ "@portabletext/schema": "^1.2.0"
86
92
  },
87
93
  "devDependencies": {
88
94
  "@sanity/diff-match-patch": "^3.2.0",
@@ -1,8 +1,28 @@
1
+ import {createKeyboardShortcut} from '@portabletext/keyboard-shortcuts'
2
+ import {isTextBlock} from '@portabletext/schema'
1
3
  import {defaultKeyboardShortcuts} from '../keyboard-shortcuts/default-keyboard-shortcuts'
2
- import * as selectors from '../selectors'
4
+ import {
5
+ getFocusBlock,
6
+ getFocusInlineObject,
7
+ getPreviousBlock,
8
+ isSelectionCollapsed,
9
+ } from '../selectors'
10
+ import {getBlockEndPoint, isEmptyTextBlock} from '../utils'
3
11
  import {raise} from './behavior.types.action'
4
12
  import {defineBehavior} from './behavior.types.behavior'
5
13
 
14
+ const shiftLeft = createKeyboardShortcut({
15
+ default: [
16
+ {
17
+ key: 'ArrowLeft',
18
+ shift: true,
19
+ meta: false,
20
+ ctrl: false,
21
+ alt: false,
22
+ },
23
+ ],
24
+ })
25
+
6
26
  export const abstractKeyboardBehaviors = [
7
27
  /**
8
28
  * Allow raising an `insert.break` event when pressing Enter on an inline
@@ -12,8 +32,8 @@ export const abstractKeyboardBehaviors = [
12
32
  on: 'keyboard.keydown',
13
33
  guard: ({snapshot, event}) =>
14
34
  defaultKeyboardShortcuts.break.guard(event.originEvent) &&
15
- selectors.isSelectionCollapsed(snapshot) &&
16
- selectors.getFocusInlineObject(snapshot),
35
+ isSelectionCollapsed(snapshot) &&
36
+ getFocusInlineObject(snapshot),
17
37
  actions: [() => [raise({type: 'insert.break'})]],
18
38
  }),
19
39
 
@@ -48,4 +68,68 @@ export const abstractKeyboardBehaviors = [
48
68
  defaultKeyboardShortcuts.history.redo.guard(event.originEvent),
49
69
  actions: [() => [raise({type: 'history.redo'})]],
50
70
  }),
71
+
72
+ /**
73
+ * Fix edge case where Shift+ArrowLeft didn't reduce a selection hanging
74
+ * onto an empty text block.
75
+ */
76
+ defineBehavior({
77
+ on: 'keyboard.keydown',
78
+ guard: ({snapshot, event}) => {
79
+ if (!snapshot.context.selection || !shiftLeft.guard(event.originEvent)) {
80
+ return false
81
+ }
82
+
83
+ const focusBlock = getFocusBlock(snapshot)
84
+
85
+ if (!focusBlock) {
86
+ return false
87
+ }
88
+
89
+ const previousBlock = getPreviousBlock({
90
+ ...snapshot,
91
+ context: {
92
+ ...snapshot.context,
93
+ selection: {
94
+ anchor: {
95
+ path: focusBlock.path,
96
+ offset: 0,
97
+ },
98
+ focus: {
99
+ path: focusBlock.path,
100
+ offset: 0,
101
+ },
102
+ },
103
+ },
104
+ })
105
+
106
+ if (!previousBlock) {
107
+ return false
108
+ }
109
+
110
+ const hanging =
111
+ isTextBlock(snapshot.context, focusBlock.node) &&
112
+ snapshot.context.selection.focus.offset === 0
113
+
114
+ if (hanging && isEmptyTextBlock(snapshot.context, focusBlock.node)) {
115
+ return {previousBlock, selection: snapshot.context.selection}
116
+ }
117
+
118
+ return false
119
+ },
120
+ actions: [
121
+ ({snapshot}, {previousBlock, selection}) => [
122
+ raise({
123
+ type: 'select',
124
+ at: {
125
+ anchor: selection.anchor,
126
+ focus: getBlockEndPoint({
127
+ context: snapshot.context,
128
+ block: previousBlock,
129
+ }),
130
+ },
131
+ }),
132
+ ],
133
+ ],
134
+ }),
51
135
  ]
@@ -168,6 +168,10 @@ export function performEvent({
168
168
  // action prevented
169
169
  defaultBehaviorOverwritten = true
170
170
 
171
+ if (eventBehavior.actions.length === 0) {
172
+ nativeEventPrevented = true
173
+ }
174
+
171
175
  for (const actionSet of eventBehavior.actions) {
172
176
  const actionsSnapshot = getSnapshot()
173
177
 
@@ -3,9 +3,9 @@ import {createTestKeyGenerator} from '@portabletext/test'
3
3
  import React from 'react'
4
4
  import {describe, expect, test} from 'vitest'
5
5
  import {InternalSlateEditorRefPlugin} from '../plugins/plugin.internal.slate-editor-ref'
6
+ import {createTestEditor} from '../test/vitest'
6
7
  import type {PortableTextSlateEditor} from '../types/editor'
7
8
  import {getFocusSpan} from './slate-utils'
8
- import {createTestEditor} from './test-editor'
9
9
 
10
10
  describe(getFocusSpan.name, () => {
11
11
  const keyGenerator = createTestKeyGenerator()
@@ -1,7 +1,7 @@
1
1
  import {getTersePt} from '@portabletext/test'
2
2
  import {userEvent} from '@vitest/browser/context'
3
3
  import {describe, expect, test, vi} from 'vitest'
4
- import {createTestEditor} from '../internal-utils/test-editor'
4
+ import {createTestEditor} from '../test/vitest'
5
5
  import {AutoCloseBracketsPlugin} from './plugin.internal.auto-close-brackets'
6
6
 
7
7
  describe(AutoCloseBracketsPlugin.name, () => {
@@ -2,8 +2,8 @@ import {defineSchema} from '@portabletext/schema'
2
2
  import {getTersePt} from '@portabletext/test'
3
3
  import {userEvent} from '@vitest/browser/context'
4
4
  import {describe, expect, test, vi} from 'vitest'
5
- import {createTestEditor} from '../internal-utils/test-editor'
6
5
  import {getTextMarks} from '../internal-utils/text-marks'
6
+ import {createTestEditor} from '../test/vitest'
7
7
  import {MarkdownPlugin} from './plugin.markdown'
8
8
 
9
9
  describe(MarkdownPlugin.name, () => {
@@ -0,0 +1 @@
1
+ export * from '../index'
@@ -0,0 +1,102 @@
1
+ import {parseTersePtString} from '@portabletext/test'
2
+ import {createParameterType, type ParameterType} from 'racejar'
3
+
4
+ /**
5
+ * @internal
6
+ */
7
+ export type Parameter = {
8
+ [K in keyof typeof parameterType]: (typeof parameterType)[K] extends ParameterType<
9
+ infer TParameterType
10
+ >
11
+ ? TParameterType
12
+ : never
13
+ }
14
+
15
+ const parameterType = {
16
+ annotation: createParameterType<'comment' | 'link'>({
17
+ name: 'annotation',
18
+ matcher: /"(comment|link)"/,
19
+ }),
20
+ blockObject: createParameterType<'image'>({
21
+ name: 'block-object',
22
+ matcher: /"(image)"/,
23
+ }),
24
+ button: createParameterType<string>({
25
+ name: 'button',
26
+ matcher: /"(([A-Z]|[a-z]|{|}|>|\/|\+)+)"/,
27
+ type: String,
28
+ }),
29
+ decorator: createParameterType<'em' | 'strong'>({
30
+ name: 'decorator',
31
+ matcher: /"(em|strong)"/,
32
+ }),
33
+ index: createParameterType({
34
+ name: 'index',
35
+ matcher: /"(\d)"/,
36
+ type: Number,
37
+ transform: (input) => Number.parseInt(input, 10),
38
+ }),
39
+ inlineObject: createParameterType<'stock-ticker'>({
40
+ name: 'inline-object',
41
+ matcher: /"(stock-ticker)"/,
42
+ }),
43
+ key: createParameterType<'key'>({
44
+ name: 'key',
45
+ matcher: /"([a-z]\d)"/,
46
+ }),
47
+ keyKeys: createParameterType<Array<string>>({
48
+ name: 'keyKeys',
49
+ matcher: /"(([a-z]\d)(,([a-z]\d))*)"/,
50
+ type: Array,
51
+ transform: (input) => input.split(','),
52
+ }),
53
+ marks: createParameterType<Array<string>>({
54
+ name: 'marks',
55
+ matcher: /"((strong|em|[a-z]\d)(,(strong|em|[a-z]\d))*)"/,
56
+ type: Array,
57
+ transform: (input) => input.split(','),
58
+ }),
59
+ placement: createParameterType<'auto' | 'after' | 'before'>({
60
+ name: 'placement',
61
+ matcher: /"(auto|after|before)"/,
62
+ }),
63
+ selectPosition: createParameterType<'start' | 'end' | 'none'>({
64
+ name: 'select-position',
65
+ matcher: /"(start|end|none)"/,
66
+ }),
67
+ style: createParameterType({
68
+ name: 'style',
69
+ matcher: /"(normal|blockquote|h\d)"/,
70
+ }),
71
+ tersePt: createParameterType<Array<string>>({
72
+ name: 'terse-pt',
73
+ matcher: /"([a-z-,#>:\\n \d|{}'"‘’“”?]*)"/u,
74
+ type: Array,
75
+ transform: parseTersePtString,
76
+ }),
77
+ text: createParameterType<string>({
78
+ name: 'text',
79
+ matcher: /"([a-z]*)"/u,
80
+ type: String,
81
+ }),
82
+ }
83
+
84
+ /**
85
+ * @internal
86
+ */
87
+ export const parameterTypes = [
88
+ parameterType.annotation,
89
+ parameterType.blockObject,
90
+ parameterType.button,
91
+ parameterType.decorator,
92
+ parameterType.index,
93
+ parameterType.inlineObject,
94
+ parameterType.key,
95
+ parameterType.keyKeys,
96
+ parameterType.marks,
97
+ parameterType.placement,
98
+ parameterType.selectPosition,
99
+ parameterType.style,
100
+ parameterType.tersePt,
101
+ parameterType.text,
102
+ ]
@@ -0,0 +1 @@
1
+ export * from './gherkin-parameter-types'
@@ -0,0 +1 @@
1
+ export * from '../index'
@@ -0,0 +1,3 @@
1
+ export * from './step-context'
2
+ export * from './step-definitions'
3
+ export * from './test-editor'
@@ -0,0 +1,11 @@
1
+ import type {Locator} from '@vitest/browser/context'
2
+ import type {Editor} from '../../editor'
3
+
4
+ /**
5
+ * @internal
6
+ */
7
+ export type Context = {
8
+ editor: Editor
9
+ locator: Locator
10
+ keyMap?: Map<string, string>
11
+ }