@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@portabletext/editor",
3
- "version": "1.9.0",
3
+ "version": "1.10.1",
4
4
  "description": "Portable Text Editor made in React",
5
5
  "keywords": [
6
6
  "sanity",
@@ -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
- * 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
- }
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
- if (this.props.editorRef !== prevProps.editorRef && this.props.editorRef) {
231
- this.props.editorRef.current = this
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
- mapLinkAnnotation?: (config: {
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.mapLinkAnnotation?.({url, schema: context.schema})
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.mapLinkAnnotation?.({url, schema: context.schema})
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
- mapBreakObject?: (
15
- schema: PortableTextMemberSchemaTypes,
16
- ) => {name: string; value?: {[prop: string]: unknown}} | undefined
17
- mapDefaultStyle?: (
18
- schema: PortableTextMemberSchemaTypes,
19
- ) => string | undefined
20
- mapHeadingStyle?: (
21
- schema: PortableTextMemberSchemaTypes,
22
- level: number,
23
- ) => string | undefined
24
- mapBlockquoteStyle?: (
25
- schema: PortableTextMemberSchemaTypes,
26
- ) => string | undefined
27
- mapUnorderedListStyle?: (
28
- schema: PortableTextMemberSchemaTypes,
29
- ) => string | undefined
30
- mapOrderedListStyle?: (
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.mapBlockquoteStyle?.(context.schema)
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 isDash = event.text === '-'
108
-
109
- if (!isDash) {
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.mapBreakObject?.(context.schema)
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 headingLevel = markdownHeadingSearch
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 headingStyle =
193
- headingLevel !== undefined
194
- ? config.mapHeadingStyle?.(context.schema, headingLevel)
200
+ const style =
201
+ level !== undefined
202
+ ? config.headingStyle?.({schema: context.schema, level})
195
203
  : undefined
196
204
 
197
- if (headingLevel !== undefined && headingStyle !== undefined) {
205
+ if (level !== undefined && style !== undefined) {
198
206
  return {
199
207
  focusTextBlock,
200
208
  focusSpan,
201
- style: headingStyle,
202
- level: headingLevel,
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.mapDefaultStyle?.(context.schema)
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.mapDefaultStyle?.(context.schema)
304
+ const defaultStyle = config.defaultStyle?.({schema: context.schema})
297
305
  const looksLikeUnorderedList = /^(-|\*)/.test(focusSpan.node.text)
298
- const unorderedListStyle = config.mapUnorderedListStyle?.(context.schema)
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.mapOrderedListStyle?.(context.schema)
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 {editorActor, getValue, onChange, value} = props
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.addEventListener('online', onlineHandler)
50
- window.addEventListener('offline', offlineHandler)
49
+ if (window) {
50
+ window.addEventListener('online', onlineHandler)
51
+ window.addEventListener('offline', offlineHandler)
52
+ }
51
53
 
52
54
  return () => {
53
- window.removeEventListener('online', onlineHandler)
54
- window.removeEventListener('offline', offlineHandler)
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
  },
@@ -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})