@portabletext/editor 2.3.5 → 2.3.7

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,20 +1,16 @@
1
- import {isPortableTextBlock, isPortableTextSpan} from '@portabletext/toolkit'
2
- import type {PortableTextBlock} from '@sanity/types'
1
+ import type {EditorContext} from '../editor/editor-snapshot'
2
+ import {isSpan, isTextBlock} from './parse-blocks'
3
3
 
4
4
  export function getTextMarks(
5
- value: Array<PortableTextBlock> | undefined,
5
+ context: Pick<EditorContext, 'schema' | 'value'>,
6
6
  text: string,
7
7
  ) {
8
- if (!value) {
9
- return undefined
10
- }
11
-
12
8
  let marks: Array<string> | undefined
13
9
 
14
- for (const block of value) {
15
- if (isPortableTextBlock(block)) {
10
+ for (const block of context.value) {
11
+ if (isTextBlock(context, block)) {
16
12
  for (const child of block.children) {
17
- if (isPortableTextSpan(child) && child.text === text) {
13
+ if (isSpan(context, child) && child.text === text) {
18
14
  marks = child.marks ?? []
19
15
  break
20
16
  }
@@ -1,3 +1,4 @@
1
+ import {compileSchema, defineSchema} from '@portabletext/schema'
1
2
  import {expect, test} from 'vitest'
2
3
  import {
3
4
  getSelectionAfterText,
@@ -6,13 +7,14 @@ import {
6
7
  } from './text-selection'
7
8
 
8
9
  test(getTextSelection.name, () => {
10
+ const schema = compileSchema(defineSchema({}))
9
11
  const simpleBlock = {
10
12
  _key: 'b1',
11
13
  _type: 'block',
12
14
  children: [{_key: 's1', _type: 'span', text: 'foo'}],
13
15
  }
14
16
 
15
- expect(getTextSelection([simpleBlock], 'foo')).toEqual({
17
+ expect(getTextSelection({schema, value: [simpleBlock]}, 'foo')).toEqual({
16
18
  anchor: {path: [{_key: 'b1'}, 'children', {_key: 's1'}], offset: 0},
17
19
  focus: {path: [{_key: 'b1'}, 'children', {_key: 's1'}], offset: 3},
18
20
  })
@@ -23,19 +25,19 @@ test(getTextSelection.name, () => {
23
25
  children: [{_key: 's1', _type: 'span', text: 'foo bar baz'}],
24
26
  }
25
27
 
26
- expect(getTextSelection([joinedBlock], 'foo ')).toEqual({
28
+ expect(getTextSelection({schema, value: [joinedBlock]}, 'foo ')).toEqual({
27
29
  anchor: {path: [{_key: 'b1'}, 'children', {_key: 's1'}], offset: 0},
28
30
  focus: {path: [{_key: 'b1'}, 'children', {_key: 's1'}], offset: 4},
29
31
  })
30
- expect(getTextSelection([joinedBlock], 'o')).toEqual({
32
+ expect(getTextSelection({schema, value: [joinedBlock]}, 'o')).toEqual({
31
33
  anchor: {path: [{_key: 'b1'}, 'children', {_key: 's1'}], offset: 1},
32
34
  focus: {path: [{_key: 'b1'}, 'children', {_key: 's1'}], offset: 2},
33
35
  })
34
- expect(getTextSelection([joinedBlock], 'bar')).toEqual({
36
+ expect(getTextSelection({schema, value: [joinedBlock]}, 'bar')).toEqual({
35
37
  anchor: {path: [{_key: 'b1'}, 'children', {_key: 's1'}], offset: 4},
36
38
  focus: {path: [{_key: 'b1'}, 'children', {_key: 's1'}], offset: 7},
37
39
  })
38
- expect(getTextSelection([joinedBlock], ' baz')).toEqual({
40
+ expect(getTextSelection({schema, value: [joinedBlock]}, ' baz')).toEqual({
39
41
  anchor: {path: [{_key: 'b1'}, 'children', {_key: 's1'}], offset: 7},
40
42
  focus: {path: [{_key: 'b1'}, 'children', {_key: 's1'}], offset: 11},
41
43
  })
@@ -49,7 +51,7 @@ test(getTextSelection.name, () => {
49
51
  ],
50
52
  }
51
53
 
52
- expect(getTextSelection([noSpaceBlock], 'obar')).toEqual({
54
+ expect(getTextSelection({schema, value: [noSpaceBlock]}, 'obar')).toEqual({
53
55
  anchor: {path: [{_key: 'b1'}, 'children', {_key: 's1'}], offset: 2},
54
56
  focus: {path: [{_key: 'b1'}, 'children', {_key: 's2'}], offset: 3},
55
57
  })
@@ -64,10 +66,12 @@ test(getTextSelection.name, () => {
64
66
  ],
65
67
  }
66
68
 
67
- expect(getTextSelection([emptyLineBlock], 'foobar')).toEqual({
68
- anchor: {path: [{_key: 'b1'}, 'children', {_key: 's1'}], offset: 0},
69
- focus: {path: [{_key: 'b1'}, 'children', {_key: 's3'}], offset: 3},
70
- })
69
+ expect(getTextSelection({schema, value: [emptyLineBlock]}, 'foobar')).toEqual(
70
+ {
71
+ anchor: {path: [{_key: 'b1'}, 'children', {_key: 's1'}], offset: 0},
72
+ focus: {path: [{_key: 'b1'}, 'children', {_key: 's3'}], offset: 3},
73
+ },
74
+ )
71
75
 
72
76
  const splitBlock = {
73
77
  _key: 'b1',
@@ -79,23 +83,25 @@ test(getTextSelection.name, () => {
79
83
  ],
80
84
  }
81
85
 
82
- expect(getTextSelection([splitBlock], 'foo')).toEqual({
86
+ expect(getTextSelection({schema, value: [splitBlock]}, 'foo')).toEqual({
83
87
  anchor: {path: [{_key: 'b1'}, 'children', {_key: 's1'}], offset: 0},
84
88
  focus: {path: [{_key: 'b1'}, 'children', {_key: 's1'}], offset: 3},
85
89
  })
86
- expect(getTextSelection([splitBlock], 'bar')).toEqual({
90
+ expect(getTextSelection({schema, value: [splitBlock]}, 'bar')).toEqual({
87
91
  anchor: {path: [{_key: 'b1'}, 'children', {_key: 's2'}], offset: 0},
88
92
  focus: {path: [{_key: 'b1'}, 'children', {_key: 's2'}], offset: 3},
89
93
  })
90
- expect(getTextSelection([splitBlock], 'baz')).toEqual({
94
+ expect(getTextSelection({schema, value: [splitBlock]}, 'baz')).toEqual({
91
95
  anchor: {path: [{_key: 'b1'}, 'children', {_key: 's3'}], offset: 1},
92
96
  focus: {path: [{_key: 'b1'}, 'children', {_key: 's3'}], offset: 4},
93
97
  })
94
- expect(getTextSelection([splitBlock], 'foo bar baz')).toEqual({
98
+ expect(
99
+ getTextSelection({schema, value: [splitBlock]}, 'foo bar baz'),
100
+ ).toEqual({
95
101
  anchor: {path: [{_key: 'b1'}, 'children', {_key: 's1'}], offset: 0},
96
102
  focus: {path: [{_key: 'b1'}, 'children', {_key: 's3'}], offset: 4},
97
103
  })
98
- expect(getTextSelection([splitBlock], 'o bar b')).toEqual({
104
+ expect(getTextSelection({schema, value: [splitBlock]}, 'o bar b')).toEqual({
99
105
  anchor: {path: [{_key: 'b1'}, 'children', {_key: 's1'}], offset: 2},
100
106
  focus: {path: [{_key: 'b1'}, 'children', {_key: 's3'}], offset: 2},
101
107
  })
@@ -113,13 +119,14 @@ test(getTextSelection.name, () => {
113
119
  },
114
120
  ]
115
121
 
116
- expect(getTextSelection(twoBlocks, 'ooba')).toEqual({
122
+ expect(getTextSelection({schema, value: twoBlocks}, 'ooba')).toEqual({
117
123
  anchor: {path: [{_key: 'b1'}, 'children', {_key: 's1'}], offset: 1},
118
124
  focus: {path: [{_key: 'b2'}, 'children', {_key: 's2'}], offset: 2},
119
125
  })
120
126
  })
121
127
 
122
128
  test(getSelectionBeforeText.name, () => {
129
+ const schema = compileSchema(defineSchema({}))
123
130
  const splitBlock = {
124
131
  _type: 'block',
125
132
  _key: 'b1',
@@ -130,34 +137,39 @@ test(getSelectionBeforeText.name, () => {
130
137
  ],
131
138
  }
132
139
 
133
- expect(getSelectionBeforeText([splitBlock], 'foo ')).toEqual({
134
- anchor: {path: [{_key: 'b1'}, 'children', {_key: 's1'}], offset: 0},
135
- focus: {path: [{_key: 'b1'}, 'children', {_key: 's1'}], offset: 0},
136
- backward: false,
137
- })
138
- expect(getSelectionBeforeText([splitBlock], 'f')).toEqual({
140
+ expect(getSelectionBeforeText({schema, value: [splitBlock]}, 'foo ')).toEqual(
141
+ {
142
+ anchor: {path: [{_key: 'b1'}, 'children', {_key: 's1'}], offset: 0},
143
+ focus: {path: [{_key: 'b1'}, 'children', {_key: 's1'}], offset: 0},
144
+ backward: false,
145
+ },
146
+ )
147
+ expect(getSelectionBeforeText({schema, value: [splitBlock]}, 'f')).toEqual({
139
148
  anchor: {path: [{_key: 'b1'}, 'children', {_key: 's1'}], offset: 0},
140
149
  focus: {path: [{_key: 'b1'}, 'children', {_key: 's1'}], offset: 0},
141
150
  backward: false,
142
151
  })
143
- expect(getSelectionBeforeText([splitBlock], 'o')).toEqual({
152
+ expect(getSelectionBeforeText({schema, value: [splitBlock]}, 'o')).toEqual({
144
153
  anchor: {path: [{_key: 'b1'}, 'children', {_key: 's1'}], offset: 1},
145
154
  focus: {path: [{_key: 'b1'}, 'children', {_key: 's1'}], offset: 1},
146
155
  backward: false,
147
156
  })
148
- expect(getSelectionBeforeText([splitBlock], 'bar')).toEqual({
157
+ expect(getSelectionBeforeText({schema, value: [splitBlock]}, 'bar')).toEqual({
149
158
  anchor: {path: [{_key: 'b1'}, 'children', {_key: 's2'}], offset: 0},
150
159
  focus: {path: [{_key: 'b1'}, 'children', {_key: 's2'}], offset: 0},
151
160
  backward: false,
152
161
  })
153
- expect(getSelectionBeforeText([splitBlock], ' baz')).toEqual({
154
- anchor: {path: [{_key: 'b1'}, 'children', {_key: 's3'}], offset: 0},
155
- focus: {path: [{_key: 'b1'}, 'children', {_key: 's3'}], offset: 0},
156
- backward: false,
157
- })
162
+ expect(getSelectionBeforeText({schema, value: [splitBlock]}, ' baz')).toEqual(
163
+ {
164
+ anchor: {path: [{_key: 'b1'}, 'children', {_key: 's3'}], offset: 0},
165
+ focus: {path: [{_key: 'b1'}, 'children', {_key: 's3'}], offset: 0},
166
+ backward: false,
167
+ },
168
+ )
158
169
  })
159
170
 
160
171
  test(getSelectionAfterText.name, () => {
172
+ const schema = compileSchema(defineSchema({}))
161
173
  const splitBlock = {
162
174
  _type: 'block',
163
175
  _key: 'b1',
@@ -168,17 +180,17 @@ test(getSelectionAfterText.name, () => {
168
180
  ],
169
181
  }
170
182
 
171
- expect(getSelectionAfterText([splitBlock], 'foo ')).toEqual({
183
+ expect(getSelectionAfterText({schema, value: [splitBlock]}, 'foo ')).toEqual({
172
184
  anchor: {path: [{_key: 'b1'}, 'children', {_key: 's1'}], offset: 4},
173
185
  focus: {path: [{_key: 'b1'}, 'children', {_key: 's1'}], offset: 4},
174
186
  backward: false,
175
187
  })
176
- expect(getSelectionAfterText([splitBlock], 'bar')).toEqual({
188
+ expect(getSelectionAfterText({schema, value: [splitBlock]}, 'bar')).toEqual({
177
189
  anchor: {path: [{_key: 'b1'}, 'children', {_key: 's2'}], offset: 3},
178
190
  focus: {path: [{_key: 'b1'}, 'children', {_key: 's2'}], offset: 3},
179
191
  backward: false,
180
192
  })
181
- expect(getSelectionAfterText([splitBlock], ' baz')).toEqual({
193
+ expect(getSelectionAfterText({schema, value: [splitBlock]}, ' baz')).toEqual({
182
194
  anchor: {path: [{_key: 'b1'}, 'children', {_key: 's3'}], offset: 4},
183
195
  focus: {path: [{_key: 'b1'}, 'children', {_key: 's3'}], offset: 4},
184
196
  backward: false,
@@ -1,25 +1,21 @@
1
- import {isPortableTextBlock, isPortableTextSpan} from '@portabletext/toolkit'
2
- import type {PortableTextBlock} from '@sanity/types'
1
+ import type {EditorContext} from '../editor/editor-snapshot'
3
2
  import type {EditorSelection, EditorSelectionPoint} from '../types/editor'
4
3
  import {collapseSelection} from './collapse-selection'
4
+ import {isSpan, isTextBlock} from './parse-blocks'
5
5
  import {splitString} from './split-string'
6
6
  import {stringOverlap} from './string-overlap'
7
7
 
8
8
  export function getTextSelection(
9
- value: Array<PortableTextBlock> | undefined,
9
+ context: Pick<EditorContext, 'schema' | 'value'>,
10
10
  text: string,
11
11
  ): EditorSelection {
12
- if (!value) {
13
- throw new Error(`Unable to find selection for value ${value}`)
14
- }
15
-
16
12
  let anchor: EditorSelectionPoint | undefined
17
13
  let focus: EditorSelectionPoint | undefined
18
14
 
19
- for (const block of value) {
20
- if (isPortableTextBlock(block)) {
15
+ for (const block of context.value) {
16
+ if (isTextBlock(context, block)) {
21
17
  for (const child of block.children) {
22
- if (isPortableTextSpan(child)) {
18
+ if (isSpan(context, child)) {
23
19
  if (child.text === text) {
24
20
  anchor = {
25
21
  path: [{_key: block._key}, 'children', {_key: child._key}],
@@ -99,7 +95,7 @@ export function getTextSelection(
99
95
 
100
96
  if (!anchor || !focus) {
101
97
  throw new Error(
102
- `Unable to find selection for text "${text}" in value "${JSON.stringify(value)}"`,
98
+ `Unable to find selection for text "${text}" in value "${JSON.stringify(context.value)}"`,
103
99
  )
104
100
  }
105
101
 
@@ -110,15 +106,15 @@ export function getTextSelection(
110
106
  }
111
107
 
112
108
  export function getSelectionBeforeText(
113
- value: Array<PortableTextBlock> | undefined,
109
+ context: Pick<EditorContext, 'schema' | 'value'>,
114
110
  text: string,
115
111
  ): EditorSelection {
116
- return collapseSelection(getTextSelection(value, text), 'start')
112
+ return collapseSelection(getTextSelection(context, text), 'start')
117
113
  }
118
114
 
119
115
  export function getSelectionAfterText(
120
- value: Array<PortableTextBlock> | undefined,
116
+ context: Pick<EditorContext, 'schema' | 'value'>,
121
117
  text: string,
122
118
  ): EditorSelection {
123
- return collapseSelection(getTextSelection(value, text), 'end')
119
+ return collapseSelection(getTextSelection(context, text), 'end')
124
120
  }
@@ -1,7 +1,9 @@
1
- import {isPortableTextBlock, isPortableTextSpan} from '@portabletext/toolkit'
2
1
  import type {PortableTextBlock} from '@sanity/types'
2
+ import type {EditorSchema} from '../editor/editor-schema'
3
+ import {isSpan, isTextBlock} from './parse-blocks'
3
4
 
4
5
  export function getValueAnnotations(
6
+ schema: EditorSchema,
5
7
  value: Array<PortableTextBlock> | undefined,
6
8
  ): Array<string> {
7
9
  if (!value) {
@@ -11,9 +13,9 @@ export function getValueAnnotations(
11
13
  const annotations: Array<string> = []
12
14
 
13
15
  for (const block of value) {
14
- if (isPortableTextBlock(block)) {
16
+ if (isTextBlock({schema}, block)) {
15
17
  for (const child of block.children) {
16
- if (isPortableTextSpan(child) && child.marks) {
18
+ if (isSpan({schema}, child) && child.marks) {
17
19
  for (const mark of child.marks) {
18
20
  if (
19
21
  block.markDefs?.some((markDef) => markDef._key === mark) &&
@@ -33,33 +33,33 @@ describe(AutoCloseBracketsPlugin.name, () => {
33
33
  editorRef.current?.send({type: 'insert.text', text: '('})
34
34
 
35
35
  await vi.waitFor(() => {
36
- expect(
37
- getTersePt(editorRef.current?.getSnapshot().context.value),
38
- ).toEqual(['()'])
36
+ expect(getTersePt(editorRef.current!.getSnapshot().context)).toEqual([
37
+ '()',
38
+ ])
39
39
  })
40
40
 
41
41
  await userEvent.type(locator, 'foo')
42
42
 
43
43
  await vi.waitFor(() => {
44
- expect(
45
- getTersePt(editorRef.current?.getSnapshot().context.value),
46
- ).toEqual(['(foo)'])
44
+ expect(getTersePt(editorRef.current!.getSnapshot().context)).toEqual([
45
+ '(foo)',
46
+ ])
47
47
  })
48
48
 
49
49
  editorRef.current?.send({type: 'history.undo'})
50
50
 
51
51
  await vi.waitFor(() => {
52
- expect(
53
- getTersePt(editorRef.current?.getSnapshot().context.value),
54
- ).toEqual(['()'])
52
+ expect(getTersePt(editorRef.current!.getSnapshot().context)).toEqual([
53
+ '()',
54
+ ])
55
55
  })
56
56
 
57
57
  editorRef.current?.send({type: 'history.undo'})
58
58
 
59
59
  await vi.waitFor(() => {
60
- expect(
61
- getTersePt(editorRef.current?.getSnapshot().context.value),
62
- ).toEqual(['('])
60
+ expect(getTersePt(editorRef.current!.getSnapshot().context)).toEqual([
61
+ '(',
62
+ ])
63
63
  })
64
64
  })
65
65
 
@@ -86,33 +86,33 @@ describe(AutoCloseBracketsPlugin.name, () => {
86
86
  editorRef.current?.send({type: 'insert.text', text: '(f'})
87
87
 
88
88
  await vi.waitFor(() => {
89
- expect(
90
- getTersePt(editorRef.current?.getSnapshot().context.value),
91
- ).toEqual(['(f)'])
89
+ expect(getTersePt(editorRef.current!.getSnapshot().context)).toEqual([
90
+ '(f)',
91
+ ])
92
92
  })
93
93
 
94
94
  await userEvent.type(locator, 'oo')
95
95
 
96
96
  await vi.waitFor(() => {
97
- expect(
98
- getTersePt(editorRef.current?.getSnapshot().context.value),
99
- ).toEqual(['(foo)'])
97
+ expect(getTersePt(editorRef.current!.getSnapshot().context)).toEqual([
98
+ '(foo)',
99
+ ])
100
100
  })
101
101
 
102
102
  editorRef.current?.send({type: 'history.undo'})
103
103
 
104
104
  await vi.waitFor(() => {
105
- expect(
106
- getTersePt(editorRef.current?.getSnapshot().context.value),
107
- ).toEqual(['(f)'])
105
+ expect(getTersePt(editorRef.current!.getSnapshot().context)).toEqual([
106
+ '(f)',
107
+ ])
108
108
  })
109
109
 
110
110
  editorRef.current?.send({type: 'history.undo'})
111
111
 
112
112
  await vi.waitFor(() => {
113
- expect(
114
- getTersePt(editorRef.current?.getSnapshot().context.value),
115
- ).toEqual(['(f'])
113
+ expect(getTersePt(editorRef.current!.getSnapshot().context)).toEqual([
114
+ '(f',
115
+ ])
116
116
  })
117
117
  })
118
118
  })
@@ -39,26 +39,23 @@ describe(MarkdownPlugin.name, () => {
39
39
  await userEvent.type(locator, '**Hello world!**')
40
40
 
41
41
  await vi.waitFor(() => {
42
- expect(
43
- getTersePt(editorRef.current?.getSnapshot().context.value),
44
- ).toEqual(['Hello world!'])
42
+ expect(getTersePt(editorRef.current!.getSnapshot().context)).toEqual([
43
+ 'Hello world!',
44
+ ])
45
45
  })
46
46
 
47
47
  await vi.waitFor(() => {
48
48
  expect(
49
- getTextMarks(
50
- editorRef.current!.getSnapshot().context.value,
51
- 'Hello world!',
52
- ),
49
+ getTextMarks(editorRef.current!.getSnapshot().context, 'Hello world!'),
53
50
  ).toEqual(['strong'])
54
51
  })
55
52
 
56
53
  editorRef.current?.send({type: 'history.undo'})
57
54
 
58
55
  await vi.waitFor(() => {
59
- expect(
60
- getTersePt(editorRef.current?.getSnapshot().context.value),
61
- ).toEqual(['**Hello world!**'])
56
+ expect(getTersePt(editorRef.current!.getSnapshot().context)).toEqual([
57
+ '**Hello world!**',
58
+ ])
62
59
  })
63
60
  })
64
61
  })