@portabletext/editor 1.9.0 → 1.10.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.
- package/README.md +21 -6
- package/lib/index.d.mts +302 -28
- package/lib/index.d.ts +302 -28
- package/lib/index.esm.js +84 -50
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js +84 -50
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +84 -50
- package/lib/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/editor/PortableTextEditor.tsx +25 -15
- package/src/editor/behavior/behavior.links.ts +3 -3
- package/src/editor/behavior/behavior.markdown.ts +53 -41
- package/src/editor/components/Synchronizer.tsx +2 -2
- package/src/editor/editor-machine.ts +16 -4
- package/src/editor/use-editor.ts +3 -0
package/package.json
CHANGED
|
@@ -94,17 +94,17 @@ export type PortableTextEditorProps<
|
|
|
94
94
|
* Whether or not the editor should be in read-only mode
|
|
95
95
|
*/
|
|
96
96
|
readOnly?: boolean
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* The current value of the portable text field
|
|
100
|
+
*/
|
|
101
|
+
value?: PortableTextBlock[]
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* A ref to the editor instance
|
|
105
|
+
*/
|
|
106
|
+
editorRef?: MutableRefObject<PortableTextEditor | null>
|
|
107
|
+
}) & {}
|
|
108
108
|
>
|
|
109
109
|
|
|
110
110
|
/**
|
|
@@ -160,6 +160,7 @@ export class PortableTextEditor extends Component<
|
|
|
160
160
|
input: {
|
|
161
161
|
keyGenerator: props.keyGenerator || defaultKeyGenerator,
|
|
162
162
|
schema: this.schemaTypes,
|
|
163
|
+
value: props.value,
|
|
163
164
|
},
|
|
164
165
|
})
|
|
165
166
|
this.editorActor.start()
|
|
@@ -225,10 +226,20 @@ export class PortableTextEditor extends Component<
|
|
|
225
226
|
: Number.parseInt(this.props.maxBlocks.toString(), 10),
|
|
226
227
|
})
|
|
227
228
|
}
|
|
228
|
-
}
|
|
229
229
|
|
|
230
|
-
|
|
231
|
-
|
|
230
|
+
if (this.props.value !== prevProps.value) {
|
|
231
|
+
this.editorActor.send({
|
|
232
|
+
type: 'update value',
|
|
233
|
+
value: this.props.value,
|
|
234
|
+
})
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (
|
|
238
|
+
this.props.editorRef !== prevProps.editorRef &&
|
|
239
|
+
this.props.editorRef
|
|
240
|
+
) {
|
|
241
|
+
this.props.editorRef.current = this
|
|
242
|
+
}
|
|
232
243
|
}
|
|
233
244
|
}
|
|
234
245
|
|
|
@@ -279,7 +290,6 @@ export class PortableTextEditor extends Component<
|
|
|
279
290
|
*/
|
|
280
291
|
this.change$.next(change)
|
|
281
292
|
}}
|
|
282
|
-
value={this.props.value}
|
|
283
293
|
/>
|
|
284
294
|
{this.props.children}
|
|
285
295
|
</PortableTextEditorSelectionProvider>
|
|
@@ -6,7 +6,7 @@ import {getFocusSpan, selectionIsCollapsed} from './behavior.utils'
|
|
|
6
6
|
* @alpha
|
|
7
7
|
*/
|
|
8
8
|
export type LinkBehaviorsConfig = {
|
|
9
|
-
|
|
9
|
+
linkAnnotation?: (context: {
|
|
10
10
|
schema: PortableTextMemberSchemaTypes
|
|
11
11
|
url: string
|
|
12
12
|
}) => {name: string; value: {[prop: string]: unknown}} | undefined
|
|
@@ -24,7 +24,7 @@ export function createLinkBehaviors(config: LinkBehaviorsConfig) {
|
|
|
24
24
|
const url = looksLikeUrl(text) ? text : undefined
|
|
25
25
|
const annotation =
|
|
26
26
|
url !== undefined
|
|
27
|
-
? config.
|
|
27
|
+
? config.linkAnnotation?.({url, schema: context.schema})
|
|
28
28
|
: undefined
|
|
29
29
|
|
|
30
30
|
if (annotation && !selectionCollapsed) {
|
|
@@ -56,7 +56,7 @@ export function createLinkBehaviors(config: LinkBehaviorsConfig) {
|
|
|
56
56
|
const url = looksLikeUrl(text) ? text : undefined
|
|
57
57
|
const annotation =
|
|
58
58
|
url !== undefined
|
|
59
|
-
? config.
|
|
59
|
+
? config.linkAnnotation?.({url, schema: context.schema})
|
|
60
60
|
: undefined
|
|
61
61
|
|
|
62
62
|
if (url && annotation && selectionCollapsed) {
|
|
@@ -11,25 +11,25 @@ import {
|
|
|
11
11
|
* @alpha
|
|
12
12
|
*/
|
|
13
13
|
export type MarkdownBehaviorsConfig = {
|
|
14
|
-
|
|
15
|
-
schema: PortableTextMemberSchemaTypes
|
|
16
|
-
) => {name: string; value?: {[prop: string]: unknown}} | undefined
|
|
17
|
-
|
|
18
|
-
schema: PortableTextMemberSchemaTypes
|
|
19
|
-
) => string | undefined
|
|
20
|
-
|
|
21
|
-
schema: PortableTextMemberSchemaTypes
|
|
22
|
-
level: number
|
|
23
|
-
) => string | undefined
|
|
24
|
-
|
|
25
|
-
schema: PortableTextMemberSchemaTypes
|
|
26
|
-
) => string | undefined
|
|
27
|
-
|
|
28
|
-
schema: PortableTextMemberSchemaTypes
|
|
29
|
-
) => string | undefined
|
|
30
|
-
|
|
31
|
-
schema: PortableTextMemberSchemaTypes
|
|
32
|
-
) => string | undefined
|
|
14
|
+
horizontalRuleObject?: (context: {
|
|
15
|
+
schema: PortableTextMemberSchemaTypes
|
|
16
|
+
}) => {name: string; value?: {[prop: string]: unknown}} | undefined
|
|
17
|
+
defaultStyle?: (context: {
|
|
18
|
+
schema: PortableTextMemberSchemaTypes
|
|
19
|
+
}) => string | undefined
|
|
20
|
+
headingStyle?: (context: {
|
|
21
|
+
schema: PortableTextMemberSchemaTypes
|
|
22
|
+
level: number
|
|
23
|
+
}) => string | undefined
|
|
24
|
+
blockquoteStyle?: (context: {
|
|
25
|
+
schema: PortableTextMemberSchemaTypes
|
|
26
|
+
}) => string | undefined
|
|
27
|
+
unorderedListStyle?: (context: {
|
|
28
|
+
schema: PortableTextMemberSchemaTypes
|
|
29
|
+
}) => string | undefined
|
|
30
|
+
orderedListStyle?: (context: {
|
|
31
|
+
schema: PortableTextMemberSchemaTypes
|
|
32
|
+
}) => string | undefined
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
/**
|
|
@@ -55,7 +55,7 @@ export function createMarkdownBehaviors(config: MarkdownBehaviorsConfig) {
|
|
|
55
55
|
|
|
56
56
|
const caretAtTheEndOfQuote = context.selection.focus.offset === 1
|
|
57
57
|
const looksLikeMarkdownQuote = /^>/.test(focusSpan.node.text)
|
|
58
|
-
const blockquoteStyle = config.
|
|
58
|
+
const blockquoteStyle = config.blockquoteStyle?.({schema: context.schema})
|
|
59
59
|
|
|
60
60
|
if (
|
|
61
61
|
caretAtTheEndOfQuote &&
|
|
@@ -104,13 +104,22 @@ export function createMarkdownBehaviors(config: MarkdownBehaviorsConfig) {
|
|
|
104
104
|
const automaticBreak = defineBehavior({
|
|
105
105
|
on: 'insert text',
|
|
106
106
|
guard: ({context, event}) => {
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
|
|
107
|
+
const hrCharacter =
|
|
108
|
+
event.text === '-'
|
|
109
|
+
? '-'
|
|
110
|
+
: event.text === '*'
|
|
111
|
+
? '*'
|
|
112
|
+
: event.text === '_'
|
|
113
|
+
? '_'
|
|
114
|
+
: undefined
|
|
115
|
+
|
|
116
|
+
if (hrCharacter === undefined) {
|
|
110
117
|
return false
|
|
111
118
|
}
|
|
112
119
|
|
|
113
|
-
const breakObject = config.
|
|
120
|
+
const breakObject = config.horizontalRuleObject?.({
|
|
121
|
+
schema: context.schema,
|
|
122
|
+
})
|
|
114
123
|
const focusBlock = getFocusTextBlock(context)
|
|
115
124
|
const selectionCollapsed = selectionIsCollapsed(context)
|
|
116
125
|
|
|
@@ -123,17 +132,17 @@ export function createMarkdownBehaviors(config: MarkdownBehaviorsConfig) {
|
|
|
123
132
|
.map((child) => child.text ?? '')
|
|
124
133
|
.join('')
|
|
125
134
|
|
|
126
|
-
if (onlyText && blockText ===
|
|
127
|
-
return {breakObject, focusBlock}
|
|
135
|
+
if (onlyText && blockText === `${hrCharacter}${hrCharacter}`) {
|
|
136
|
+
return {breakObject, focusBlock, hrCharacter}
|
|
128
137
|
}
|
|
129
138
|
|
|
130
139
|
return false
|
|
131
140
|
},
|
|
132
141
|
actions: [
|
|
133
|
-
() => [
|
|
142
|
+
(_, {hrCharacter}) => [
|
|
134
143
|
{
|
|
135
144
|
type: 'insert text',
|
|
136
|
-
text:
|
|
145
|
+
text: hrCharacter,
|
|
137
146
|
},
|
|
138
147
|
],
|
|
139
148
|
(_, {breakObject, focusBlock}) => [
|
|
@@ -179,27 +188,26 @@ export function createMarkdownBehaviors(config: MarkdownBehaviorsConfig) {
|
|
|
179
188
|
}
|
|
180
189
|
|
|
181
190
|
const markdownHeadingSearch = /^#+/.exec(focusSpan.node.text)
|
|
182
|
-
const
|
|
191
|
+
const level = markdownHeadingSearch
|
|
183
192
|
? markdownHeadingSearch[0].length
|
|
184
193
|
: undefined
|
|
185
|
-
const caretAtTheEndOfHeading =
|
|
186
|
-
context.selection.focus.offset === headingLevel
|
|
194
|
+
const caretAtTheEndOfHeading = context.selection.focus.offset === level
|
|
187
195
|
|
|
188
196
|
if (!caretAtTheEndOfHeading) {
|
|
189
197
|
return false
|
|
190
198
|
}
|
|
191
199
|
|
|
192
|
-
const
|
|
193
|
-
|
|
194
|
-
? config.
|
|
200
|
+
const style =
|
|
201
|
+
level !== undefined
|
|
202
|
+
? config.headingStyle?.({schema: context.schema, level})
|
|
195
203
|
: undefined
|
|
196
204
|
|
|
197
|
-
if (
|
|
205
|
+
if (level !== undefined && style !== undefined) {
|
|
198
206
|
return {
|
|
199
207
|
focusTextBlock,
|
|
200
208
|
focusSpan,
|
|
201
|
-
style:
|
|
202
|
-
level
|
|
209
|
+
style: style,
|
|
210
|
+
level,
|
|
203
211
|
}
|
|
204
212
|
}
|
|
205
213
|
|
|
@@ -254,7 +262,7 @@ export function createMarkdownBehaviors(config: MarkdownBehaviorsConfig) {
|
|
|
254
262
|
focusTextBlock.node.children[0]._key === focusSpan.node._key &&
|
|
255
263
|
context.selection.focus.offset === 0
|
|
256
264
|
|
|
257
|
-
const defaultStyle = config.
|
|
265
|
+
const defaultStyle = config.defaultStyle?.({schema: context.schema})
|
|
258
266
|
|
|
259
267
|
if (
|
|
260
268
|
atTheBeginningOfBLock &&
|
|
@@ -293,9 +301,11 @@ export function createMarkdownBehaviors(config: MarkdownBehaviorsConfig) {
|
|
|
293
301
|
return false
|
|
294
302
|
}
|
|
295
303
|
|
|
296
|
-
const defaultStyle = config.
|
|
304
|
+
const defaultStyle = config.defaultStyle?.({schema: context.schema})
|
|
297
305
|
const looksLikeUnorderedList = /^(-|\*)/.test(focusSpan.node.text)
|
|
298
|
-
const unorderedListStyle = config.
|
|
306
|
+
const unorderedListStyle = config.unorderedListStyle?.({
|
|
307
|
+
schema: context.schema,
|
|
308
|
+
})
|
|
299
309
|
const caretAtTheEndOfUnorderedList = context.selection.focus.offset === 1
|
|
300
310
|
|
|
301
311
|
if (
|
|
@@ -314,7 +324,9 @@ export function createMarkdownBehaviors(config: MarkdownBehaviorsConfig) {
|
|
|
314
324
|
}
|
|
315
325
|
|
|
316
326
|
const looksLikeOrderedList = /^1./.test(focusSpan.node.text)
|
|
317
|
-
const orderedListStyle = config.
|
|
327
|
+
const orderedListStyle = config.orderedListStyle?.({
|
|
328
|
+
schema: context.schema,
|
|
329
|
+
})
|
|
318
330
|
const caretAtTheEndOfOrderedList = context.selection.focus.offset === 2
|
|
319
331
|
|
|
320
332
|
if (
|
|
@@ -27,7 +27,6 @@ export interface SynchronizerProps {
|
|
|
27
27
|
editorActor: EditorActor
|
|
28
28
|
getValue: () => Array<PortableTextBlock> | undefined
|
|
29
29
|
onChange: (change: EditorChange) => void
|
|
30
|
-
value: Array<PortableTextBlock> | undefined
|
|
31
30
|
}
|
|
32
31
|
|
|
33
32
|
/**
|
|
@@ -37,7 +36,8 @@ export interface SynchronizerProps {
|
|
|
37
36
|
export function Synchronizer(props: SynchronizerProps) {
|
|
38
37
|
const portableTextEditor = usePortableTextEditor()
|
|
39
38
|
const readOnly = useSelector(props.editorActor, (s) => s.context.readOnly)
|
|
40
|
-
const
|
|
39
|
+
const value = useSelector(props.editorActor, (s) => s.context.value)
|
|
40
|
+
const {editorActor, getValue, onChange} = props
|
|
41
41
|
const pendingPatches = useRef<Patch[]>([])
|
|
42
42
|
|
|
43
43
|
const syncValue = useSyncValue({
|
|
@@ -46,12 +46,16 @@ const networkLogic = fromCallback(({sendBack}) => {
|
|
|
46
46
|
sendBack({type: 'offline'})
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
window
|
|
50
|
-
|
|
49
|
+
if (window) {
|
|
50
|
+
window.addEventListener('online', onlineHandler)
|
|
51
|
+
window.addEventListener('offline', offlineHandler)
|
|
52
|
+
}
|
|
51
53
|
|
|
52
54
|
return () => {
|
|
53
|
-
window
|
|
54
|
-
|
|
55
|
+
if (window) {
|
|
56
|
+
window.removeEventListener('online', onlineHandler)
|
|
57
|
+
window.removeEventListener('offline', offlineHandler)
|
|
58
|
+
}
|
|
55
59
|
}
|
|
56
60
|
})
|
|
57
61
|
|
|
@@ -102,6 +106,10 @@ export type InternalEditorEvent =
|
|
|
102
106
|
type: 'update behaviors'
|
|
103
107
|
behaviors: Array<Behavior>
|
|
104
108
|
}
|
|
109
|
+
| {
|
|
110
|
+
type: 'update value'
|
|
111
|
+
value: Array<PortableTextBlock> | undefined
|
|
112
|
+
}
|
|
105
113
|
| {
|
|
106
114
|
type: 'toggle readOnly'
|
|
107
115
|
}
|
|
@@ -163,6 +171,7 @@ export const editorMachine = setup({
|
|
|
163
171
|
schema: PortableTextMemberSchemaTypes
|
|
164
172
|
readOnly: boolean
|
|
165
173
|
maxBlocks: number | undefined
|
|
174
|
+
value: Array<PortableTextBlock> | undefined
|
|
166
175
|
},
|
|
167
176
|
events: {} as InternalEditorEvent,
|
|
168
177
|
emitted: {} as InternalEditorEmittedEvent,
|
|
@@ -170,6 +179,7 @@ export const editorMachine = setup({
|
|
|
170
179
|
behaviors?: Array<Behavior>
|
|
171
180
|
keyGenerator: () => string
|
|
172
181
|
schema: PortableTextMemberSchemaTypes
|
|
182
|
+
value?: Array<PortableTextBlock>
|
|
173
183
|
},
|
|
174
184
|
},
|
|
175
185
|
actions: {
|
|
@@ -315,6 +325,7 @@ export const editorMachine = setup({
|
|
|
315
325
|
schema: input.schema,
|
|
316
326
|
readOnly: false,
|
|
317
327
|
maxBlocks: undefined,
|
|
328
|
+
value: input.value,
|
|
318
329
|
}),
|
|
319
330
|
invoke: {
|
|
320
331
|
id: 'networkLogic',
|
|
@@ -352,6 +363,7 @@ export const editorMachine = setup({
|
|
|
352
363
|
'done loading': {actions: emit({type: 'done loading'})},
|
|
353
364
|
'update behaviors': {actions: 'assign behaviors'},
|
|
354
365
|
'update schema': {actions: 'assign schema'},
|
|
366
|
+
'update value': {actions: assign({value: ({event}) => event.value})},
|
|
355
367
|
'toggle readOnly': {
|
|
356
368
|
actions: assign({readOnly: ({context}) => !context.readOnly}),
|
|
357
369
|
},
|
package/src/editor/use-editor.ts
CHANGED
|
@@ -22,6 +22,7 @@ import {defaultKeyGenerator} from './key-generator'
|
|
|
22
22
|
export type EditorConfig = {
|
|
23
23
|
behaviors?: Array<Behavior>
|
|
24
24
|
keyGenerator?: () => string
|
|
25
|
+
initialValue?: Array<PortableTextBlock>
|
|
25
26
|
} & (
|
|
26
27
|
| {
|
|
27
28
|
schemaDefinition: SchemaDefinition
|
|
@@ -44,6 +45,7 @@ export type EditorEvent = PickFromUnion<
|
|
|
44
45
|
| 'patches'
|
|
45
46
|
| 'toggle readOnly'
|
|
46
47
|
| 'update behaviors'
|
|
48
|
+
| 'update value'
|
|
47
49
|
>
|
|
48
50
|
|
|
49
51
|
/**
|
|
@@ -74,6 +76,7 @@ export function useEditor(config: EditorConfig): Editor {
|
|
|
74
76
|
? config.schema
|
|
75
77
|
: compileType(config.schema),
|
|
76
78
|
),
|
|
79
|
+
value: config.initialValue,
|
|
77
80
|
},
|
|
78
81
|
})
|
|
79
82
|
const slateEditor = createSlateEditor({editorActor})
|