@portabletext/editor 2.10.0 → 2.12.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.
@@ -1,5 +1,5 @@
1
1
  import { Behavior, Editor, EditorEmittedEvent, EditorSchema } from "../_chunks-dts/behavior.types.action.cjs";
2
- import * as react12 from "react";
2
+ import * as react22 from "react";
3
3
  import React from "react";
4
4
  /**
5
5
  * @beta
@@ -181,7 +181,7 @@ type MarkdownPluginConfig = MarkdownBehaviorsConfig & {
181
181
  */
182
182
  declare function MarkdownPlugin(props: {
183
183
  config: MarkdownPluginConfig;
184
- }): react12.JSX.Element;
184
+ }): react22.JSX.Element;
185
185
  /**
186
186
  * @beta
187
187
  * Restrict the editor to one line. The plugin takes care of blocking
@@ -192,5 +192,5 @@ declare function MarkdownPlugin(props: {
192
192
  *
193
193
  * @deprecated Install the plugin from `@portabletext/plugin-one-line`
194
194
  */
195
- declare function OneLinePlugin(): react12.JSX.Element;
195
+ declare function OneLinePlugin(): react22.JSX.Element;
196
196
  export { BehaviorPlugin, DecoratorShortcutPlugin, EditorRefPlugin, EventListenerPlugin, MarkdownPlugin, type MarkdownPluginConfig, OneLinePlugin };
@@ -1,5 +1,5 @@
1
1
  import { BlockOffset, BlockPath, ChildPath, EditorContext, EditorSelection, EditorSelectionPoint } from "../_chunks-dts/behavior.types.action.js";
2
- import * as _sanity_types8 from "@sanity/types";
2
+ import * as _sanity_types9 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_types8.PortableTextObject | _sanity_types8.PortableTextSpan>;
146
+ }): PortableTextTextBlock<_sanity_types9.PortableTextObject | _sanity_types9.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.10.0",
3
+ "version": "2.12.0",
4
4
  "description": "Portable Text Editor made in React",
5
5
  "keywords": [
6
6
  "sanity",
@@ -81,8 +81,8 @@
81
81
  "xstate": "^5.21.0",
82
82
  "@portabletext/block-tools": "^3.5.6",
83
83
  "@portabletext/keyboard-shortcuts": "^1.1.1",
84
- "@portabletext/patches": "^1.1.8",
85
- "@portabletext/schema": "^1.2.0"
84
+ "@portabletext/schema": "^1.2.0",
85
+ "@portabletext/patches": "^1.1.8"
86
86
  },
87
87
  "devDependencies": {
88
88
  "@sanity/diff-match-patch": "^3.2.0",
@@ -0,0 +1,52 @@
1
+ import {getActiveAnnotationsMarks} from '../selectors/selector.get-active-annotation-marks'
2
+ import {getActiveDecorators} from '../selectors/selector.get-active-decorators'
3
+ import {getFocusSpan} from '../selectors/selector.get-focus-span'
4
+ import {getMarkState} from '../selectors/selector.get-mark-state'
5
+ import {raise} from './behavior.types.action'
6
+ import {defineBehavior} from './behavior.types.behavior'
7
+
8
+ export const coreInsertBehaviors = [
9
+ defineBehavior({
10
+ on: 'insert.text',
11
+ guard: ({snapshot}) => {
12
+ const focusSpan = getFocusSpan(snapshot)
13
+
14
+ if (!focusSpan) {
15
+ return false
16
+ }
17
+
18
+ const markState = getMarkState(snapshot)
19
+ const activeDecorators = getActiveDecorators(snapshot)
20
+ const activeAnnotations = getActiveAnnotationsMarks(snapshot)
21
+
22
+ if (markState && markState.state === 'unchanged') {
23
+ const markStateDecorators = (markState.marks ?? []).filter((mark) =>
24
+ snapshot.context.schema.decorators
25
+ .map((decorator) => decorator.name)
26
+ .includes(mark),
27
+ )
28
+
29
+ if (
30
+ markStateDecorators.length === activeDecorators.length &&
31
+ markStateDecorators.every((mark) => activeDecorators.includes(mark))
32
+ ) {
33
+ return false
34
+ }
35
+ }
36
+
37
+ return {activeDecorators, activeAnnotations}
38
+ },
39
+ actions: [
40
+ ({snapshot, event}, {activeDecorators, activeAnnotations}) => [
41
+ raise({
42
+ type: 'insert.child',
43
+ child: {
44
+ _type: snapshot.context.schema.span.name,
45
+ text: event.text,
46
+ marks: [...activeDecorators, ...activeAnnotations],
47
+ },
48
+ }),
49
+ ],
50
+ ],
51
+ }),
52
+ ]
@@ -3,6 +3,7 @@ import {coreAnnotationBehaviors} from './behavior.core.annotations'
3
3
  import {coreBlockObjectBehaviors} from './behavior.core.block-objects'
4
4
  import {coreDecoratorBehaviors} from './behavior.core.decorators'
5
5
  import {coreDndBehaviors} from './behavior.core.dnd'
6
+ import {coreInsertBehaviors} from './behavior.core.insert'
6
7
  import {coreInsertBreakBehaviors} from './behavior.core.insert-break'
7
8
  import {coreListBehaviors} from './behavior.core.lists'
8
9
 
@@ -20,6 +21,7 @@ export const coreBehaviorsConfig = [
20
21
  coreBlockObjectBehaviors.breakingBlockObject,
21
22
  coreBlockObjectBehaviors.deletingEmptyTextBlockAfterBlockObject,
22
23
  coreBlockObjectBehaviors.deletingEmptyTextBlockBeforeBlockObject,
24
+ ...coreInsertBehaviors,
23
25
  coreListBehaviors.clearListOnBackspace,
24
26
  coreListBehaviors.unindentListOnBackspace,
25
27
  coreListBehaviors.mergeTextIntoListOnDelete,
@@ -55,8 +55,7 @@ export type ExternalBehaviorEvent =
55
55
  value?: {[prop: string]: unknown}
56
56
  }
57
57
  }
58
- | SyntheticBehaviorEvent
59
- | CustomBehaviorEvent
58
+ | BehaviorEvent
60
59
 
61
60
  /**************************************
62
61
  * Synthetic events
@@ -6,18 +6,13 @@ import React from 'react'
6
6
  import {expect, vi} from 'vitest'
7
7
  import {render} from 'vitest-browser-react'
8
8
  import type {Context} from '../../gherkin-tests-v2/step-context'
9
- import type {NativeBehaviorEvent} from '../behaviors'
10
9
  import type {Editor} from '../editor'
11
10
  import {
12
11
  PortableTextEditable,
13
12
  type PortableTextEditableProps,
14
13
  } from '../editor/Editable'
15
- import type {EditorActor} from '../editor/editor-machine'
16
14
  import {EditorProvider} from '../editor/editor-provider'
17
15
  import {EditorRefPlugin} from '../plugins/plugin.editor-ref'
18
- import {InternalEditorAfterRefPlugin} from '../plugins/plugin.internal.editor-actor-ref'
19
- import {InternalSlateEditorRefPlugin} from '../plugins/plugin.internal.slate-editor-ref'
20
- import type {PortableTextSlateEditor} from '../types/editor'
21
16
 
22
17
  type CreateTestEditorOptions = {
23
18
  initialValue?: Array<PortableTextBlock>
@@ -35,8 +30,6 @@ export async function createTestEditor(
35
30
  }
36
31
  > {
37
32
  const editorRef = React.createRef<Editor>()
38
- const editorActorRef = React.createRef<EditorActor>()
39
- const slateRef = React.createRef<PortableTextSlateEditor>()
40
33
  const keyGenerator = options.keyGenerator ?? createTestKeyGenerator()
41
34
 
42
35
  const renderResult = render(
@@ -48,8 +41,6 @@ export async function createTestEditor(
48
41
  }}
49
42
  >
50
43
  <EditorRefPlugin ref={editorRef} />
51
- <InternalEditorAfterRefPlugin ref={editorActorRef} />
52
- <InternalSlateEditorRefPlugin ref={slateRef} />
53
44
  <PortableTextEditable {...options.editableProps} />
54
45
  {options.children}
55
46
  </EditorProvider>,
@@ -66,8 +57,6 @@ export async function createTestEditor(
66
57
  }}
67
58
  >
68
59
  <EditorRefPlugin ref={editorRef} />
69
- <InternalEditorAfterRefPlugin ref={editorActorRef} />
70
- <InternalSlateEditorRefPlugin ref={slateRef} />
71
60
  <PortableTextEditable {...newOptions.editableProps} />
72
61
  {newOptions.children}
73
62
  </EditorProvider>,
@@ -81,8 +70,6 @@ export async function createTestEditor(
81
70
  }}
82
71
  >
83
72
  <EditorRefPlugin ref={editorRef} />
84
- <InternalEditorAfterRefPlugin ref={editorActorRef} />
85
- <InternalSlateEditorRefPlugin ref={slateRef} />
86
73
  <PortableTextEditable {...options.editableProps} />
87
74
  {options.children}
88
75
  </EditorProvider>,
@@ -93,19 +80,8 @@ export async function createTestEditor(
93
80
 
94
81
  await vi.waitFor(() => expect.element(locator).toBeInTheDocument())
95
82
 
96
- function sendNativeEvent(event: NativeBehaviorEvent) {
97
- editorActorRef.current?.send({
98
- type: 'behavior event',
99
- behaviorEvent: event,
100
- editor: slateRef.current!,
101
- })
102
- }
103
-
104
83
  return {
105
- editor: {
106
- ...editorRef.current!,
107
- sendNativeEvent,
108
- },
84
+ editor: editorRef.current!,
109
85
  locator,
110
86
  rerender,
111
87
  }
@@ -1,5 +1,6 @@
1
1
  import {isTextBlock} from '@portabletext/schema'
2
2
  import {Transforms} from 'slate'
3
+ import {EDITOR_TO_PENDING_SELECTION} from 'slate-dom'
3
4
  import {parseInlineObject, parseSpan} from '../internal-utils/parse-blocks'
4
5
  import {getFocusBlock, getFocusSpan} from '../internal-utils/slate-utils'
5
6
  import {VOID_CHILD_KEY} from '../internal-utils/values'
@@ -54,6 +55,13 @@ export const insertChildOperationImplementation: BehaviorOperationImplementation
54
55
  })
55
56
  }
56
57
 
58
+ // This makes sure the selection is set correctly when event handling is run
59
+ // through Slate's Android input handling
60
+ EDITOR_TO_PENDING_SELECTION.set(
61
+ operation.editor,
62
+ operation.editor.selection,
63
+ )
64
+
57
65
  return
58
66
  }
59
67
 
@@ -1,76 +1,8 @@
1
1
  import {Transforms} from 'slate'
2
- import {EDITOR_TO_PENDING_SELECTION} from 'slate-dom'
3
- import type {EditorSnapshot} from '../editor/editor-snapshot'
4
- import {
5
- getFocusSpan,
6
- slateRangeToSelection,
7
- } from '../internal-utils/slate-utils'
8
- import {getActiveAnnotationsMarks} from '../selectors/selector.get-active-annotation-marks'
9
- import {getActiveDecorators} from '../selectors/selector.get-active-decorators'
10
- import {getMarkState} from '../selectors/selector.get-mark-state'
11
2
  import type {BehaviorOperationImplementation} from './behavior.operations'
12
3
 
13
4
  export const insertTextOperationImplementation: BehaviorOperationImplementation<
14
5
  'insert.text'
15
- > = ({context, operation}) => {
16
- const snapshot: EditorSnapshot = {
17
- blockIndexMap: operation.editor.blockIndexMap,
18
- context: {
19
- value: operation.editor.value,
20
- selection: operation.editor.selection
21
- ? slateRangeToSelection({
22
- schema: context.schema,
23
- editor: operation.editor,
24
- range: operation.editor.selection,
25
- })
26
- : null,
27
- schema: context.schema,
28
- keyGenerator: context.keyGenerator,
29
- converters: [],
30
- readOnly: false,
31
- },
32
- decoratorState: operation.editor.decoratorState,
33
- }
34
-
35
- const markState = getMarkState(snapshot)
36
- const activeDecorators = getActiveDecorators(snapshot)
37
- const activeAnnotations = getActiveAnnotationsMarks(snapshot)
38
-
39
- const [focusSpan] = getFocusSpan({
40
- editor: operation.editor,
41
- })
42
-
43
- if (!focusSpan) {
44
- Transforms.insertText(operation.editor, operation.text)
45
- return
46
- }
47
-
48
- if (markState && markState.state === 'unchanged') {
49
- const markStateDecorators = (markState.marks ?? []).filter((mark) =>
50
- context.schema.decorators
51
- .map((decorator) => decorator.name)
52
- .includes(mark),
53
- )
54
-
55
- if (
56
- markStateDecorators.length === activeDecorators.length &&
57
- markStateDecorators.every((mark) => activeDecorators.includes(mark))
58
- ) {
59
- Transforms.insertText(operation.editor, operation.text)
60
- return
61
- }
62
- }
63
-
64
- Transforms.insertNodes(operation.editor, {
65
- _type: focusSpan._type,
66
- _key: context.keyGenerator(),
67
- text: operation.text,
68
- marks: [...activeDecorators, ...activeAnnotations],
69
- })
70
-
71
- // This makes sure the selection is set correctly when event handling is run
72
- // through Slate's Android input handling
73
- EDITOR_TO_PENDING_SELECTION.set(operation.editor, operation.editor.selection)
74
-
75
- operation.editor.decoratorState = {}
6
+ > = ({operation}) => {
7
+ Transforms.insertText(operation.editor, operation.text)
76
8
  }
@@ -1,15 +0,0 @@
1
- import React, {useContext} from 'react'
2
- import {EditorActorContext} from '../editor/editor-actor-context'
3
- import type {EditorActor} from '../editor/editor-machine'
4
-
5
- export const InternalEditorAfterRefPlugin =
6
- React.forwardRef<EditorActor | null>((_, ref) => {
7
- const editorActor = useContext(EditorActorContext)
8
-
9
- const editorActorRef = React.useRef(editorActor)
10
-
11
- React.useImperativeHandle(ref, () => editorActorRef.current, [])
12
-
13
- return null
14
- })
15
- InternalEditorAfterRefPlugin.displayName = 'InternalEditorAfterRefPlugin'