@portabletext/editor 1.0.8 → 1.0.10

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.0.8",
3
+ "version": "1.0.10",
4
4
  "description": "Portable Text Editor made in React",
5
5
  "keywords": [
6
6
  "sanity",
@@ -42,7 +42,7 @@
42
42
  "src"
43
43
  ],
44
44
  "dependencies": {
45
- "@portabletext/patches": "1.0.2",
45
+ "@portabletext/patches": "1.1.0",
46
46
  "debug": "^4.3.4",
47
47
  "is-hotkey-esm": "^1.0.0",
48
48
  "lodash": "^4.17.21",
@@ -50,32 +50,31 @@
50
50
  "slate-react": "0.101.0"
51
51
  },
52
52
  "devDependencies": {
53
- "@babel/plugin-proposal-class-properties": "^7.18.6",
54
53
  "@jest/globals": "^29.7.0",
55
- "@playwright/test": "1.45.0",
54
+ "@playwright/test": "1.45.3",
56
55
  "@portabletext/toolkit": "^2.0.15",
57
- "@sanity/block-tools": "^3.48.1",
56
+ "@sanity/block-tools": "^3.52.2",
58
57
  "@sanity/diff-match-patch": "^3.1.1",
59
58
  "@sanity/eslint-config-i18n": "^1.1.0",
60
59
  "@sanity/eslint-config-studio": "^4.0.0",
61
- "@sanity/pkg-utils": "^6.10.0",
62
- "@sanity/schema": "^3.48.1",
60
+ "@sanity/pkg-utils": "^6.10.6",
61
+ "@sanity/schema": "^3.52.2",
63
62
  "@sanity/test": "0.0.1-alpha.1",
64
- "@sanity/types": "^3.48.1",
65
- "@sanity/ui": "^2.5.0",
66
- "@sanity/util": "^3.48.1",
63
+ "@sanity/types": "^3.52.2",
64
+ "@sanity/ui": "^2.8.8",
65
+ "@sanity/util": "^3.52.2",
67
66
  "@testing-library/react": "^13.4.0",
68
67
  "@types/debug": "^4.1.5",
69
68
  "@types/express": "^4.17.21",
70
69
  "@types/express-ws": "^3.0.4",
71
- "@types/lodash": "^4.17.5",
70
+ "@types/lodash": "^4.17.7",
72
71
  "@types/node": "^18.19.8",
73
72
  "@types/node-ipc": "^9.2.3",
74
73
  "@types/react": "^18.3.3",
75
74
  "@types/react-dom": "^18.3.0",
76
- "@types/ws": "~8.5.10",
77
- "@typescript-eslint/eslint-plugin": "^7.14.1",
78
- "@typescript-eslint/parser": "^7.14.1",
75
+ "@types/ws": "~8.5.11",
76
+ "@typescript-eslint/eslint-plugin": "^7.17.0",
77
+ "@typescript-eslint/parser": "^7.17.0",
79
78
  "@vitejs/plugin-react": "^4.3.1",
80
79
  "dotenv": "^16.4.5",
81
80
  "eslint": "^8.57.0",
@@ -83,11 +82,11 @@
83
82
  "eslint-config-sanity": "^7.1.2",
84
83
  "eslint-import-resolver-typescript": "^3.6.1",
85
84
  "eslint-plugin-import": "^2.29.1",
86
- "eslint-plugin-prettier": "^5.1.3",
87
- "eslint-plugin-react-compiler": "0.0.0-experimental-51a85ea-20240601",
85
+ "eslint-plugin-prettier": "^5.2.1",
86
+ "eslint-plugin-react-compiler": "0.0.0-experimental-9ed098e-20240725",
88
87
  "eslint-plugin-tsdoc": "^0.3.0",
89
88
  "eslint-plugin-unicorn": "^54.0.0",
90
- "eslint-plugin-unused-imports": "^4.0.0",
89
+ "eslint-plugin-unused-imports": "^4.0.1",
91
90
  "express": "^4.19.2",
92
91
  "express-ws": "^5.0.2",
93
92
  "jest": "^29.7.0",
@@ -97,11 +96,10 @@
97
96
  "node-ipc": "npm:@node-ipc/compat@9.2.5",
98
97
  "react": "^18.3.1",
99
98
  "react-dom": "^18.3.1",
100
- "rimraf": "^3.0.2",
101
99
  "rxjs": "^7.8.1",
102
- "styled-components": "^6.1.11",
103
- "tsx": "^4.15.7",
104
- "typescript": "^5.4.5",
100
+ "styled-components": "^6.1.12",
101
+ "tsx": "^4.16.2",
102
+ "typescript": "^5.5.3",
105
103
  "vite": "^4.5.3"
106
104
  },
107
105
  "peerDependencies": {
@@ -123,7 +121,7 @@
123
121
  "build": "pkg-utils build --strict --check --clean",
124
122
  "check:lint": "eslint .",
125
123
  "check:types": "tsc --project tsconfig.lib.json",
126
- "clean": "rimraf lib",
124
+ "clean": "del .turbo && del lib && del node_modules",
127
125
  "dev": "pkg-utils watch",
128
126
  "dev:e2e-server": "cd ./e2e-tests/ && tsx serve",
129
127
  "lint:fix": "eslint . --fix",
@@ -79,8 +79,6 @@ interface BaseRangeWithDecoration extends BaseRange {
79
79
  rangeDecoration: RangeDecoration
80
80
  }
81
81
 
82
- const EMPTY_DECORATIONS_STATE: BaseRangeWithDecoration[] = []
83
-
84
82
  /**
85
83
  * @public
86
84
  */
@@ -142,8 +140,7 @@ export const PortableTextEditable = forwardRef(function PortableTextEditable(
142
140
  const ref = useRef<HTMLDivElement | null>(null)
143
141
  const [editableElement, setEditableElement] = useState<HTMLDivElement | null>(null)
144
142
  const [hasInvalidValue, setHasInvalidValue] = useState(false)
145
- const [rangeDecorationState, setRangeDecorationsState] =
146
- useState<BaseRangeWithDecoration[]>(EMPTY_DECORATIONS_STATE)
143
+ const [rangeDecorationState, setRangeDecorationsState] = useState<BaseRangeWithDecoration[]>([])
147
144
 
148
145
  // Forward ref to parent component
149
146
  useImperativeHandle<HTMLDivElement | null, HTMLDivElement | null>(forwardedRef, () => ref.current)
@@ -296,7 +293,7 @@ export const PortableTextEditable = forwardRef(function PortableTextEditable(
296
293
  return
297
294
  }
298
295
  }
299
- setRangeDecorationsState(EMPTY_DECORATIONS_STATE)
296
+ setRangeDecorationsState([])
300
297
  },
301
298
  [portableTextEditor, rangeDecorations, schemaTypes, slateEditor],
302
299
  )
@@ -619,7 +616,7 @@ export const PortableTextEditable = forwardRef(function PortableTextEditable(
619
616
  }
620
617
  // Editor node has a path length of 0 (should never be decorated)
621
618
  if (path.length === 0) {
622
- return EMPTY_DECORATIONS_STATE
619
+ return []
623
620
  }
624
621
  const result = rangeDecorationState.filter((item) => {
625
622
  // Special case in order to only return one decoration for collapsed ranges
@@ -639,7 +636,7 @@ export const PortableTextEditable = forwardRef(function PortableTextEditable(
639
636
  if (result.length > 0) {
640
637
  return result
641
638
  }
642
- return EMPTY_DECORATIONS_STATE
639
+ return []
643
640
  },
644
641
  [slateEditor, schemaTypes, rangeDecorationState],
645
642
  )
@@ -26,15 +26,16 @@ import {getPortableTextMemberSchemaTypes} from '../utils/getPortableTextMemberSc
26
26
  import {compileType} from '../utils/schema'
27
27
  import {SlateContainer} from './components/SlateContainer'
28
28
  import {Synchronizer} from './components/Synchronizer'
29
- import {defaultKeyGenerator} from './hooks/usePortableTextEditorKeyGenerator'
29
+ import {PortableTextEditorContext} from './hooks/usePortableTextEditor'
30
+ import {
31
+ defaultKeyGenerator,
32
+ PortableTextEditorKeyGeneratorContext,
33
+ } from './hooks/usePortableTextEditorKeyGenerator'
34
+ import {PortableTextEditorSelectionProvider} from './hooks/usePortableTextEditorSelection'
35
+ import {PortableTextEditorReadOnlyContext} from './hooks/usePortableTextReadOnly'
30
36
 
31
37
  const debug = debugWithName('component:PortableTextEditor')
32
38
 
33
- /**
34
- * Props for the PortableTextEditor component
35
- *
36
- * @public
37
- */
38
39
  /**
39
40
  * Props for the PortableTextEditor component
40
41
  *
@@ -109,7 +110,7 @@ export class PortableTextEditor extends Component<PortableTextEditorProps> {
109
110
  super(props)
110
111
 
111
112
  if (!props.schemaType) {
112
- throw new Error('PortableTextEditor: missing "type" property')
113
+ throw new Error('PortableTextEditor: missing "schemaType" property')
113
114
  }
114
115
 
115
116
  if (props.incomingPatches$) {
@@ -143,6 +144,14 @@ export class PortableTextEditor extends Component<PortableTextEditorProps> {
143
144
  this.editable = {...this.editable, ...editable}
144
145
  }
145
146
 
147
+ private getValue = () => {
148
+ if (this.editable) {
149
+ return this.editable.getValue()
150
+ }
151
+
152
+ return undefined
153
+ }
154
+
146
155
  render() {
147
156
  const {onChange, value, children, patches$, incomingPatches$} = this.props
148
157
  const {change$} = this
@@ -163,16 +172,21 @@ export class PortableTextEditor extends Component<PortableTextEditorProps> {
163
172
  portableTextEditor={this}
164
173
  readOnly={readOnly}
165
174
  >
166
- <Synchronizer
167
- change$={change$}
168
- keyGenerator={keyGenerator}
169
- onChange={onChange}
170
- portableTextEditor={this}
171
- readOnly={readOnly}
172
- value={value}
173
- >
174
- {children}
175
- </Synchronizer>
175
+ <PortableTextEditorKeyGeneratorContext.Provider value={keyGenerator}>
176
+ <PortableTextEditorContext.Provider value={this}>
177
+ <PortableTextEditorReadOnlyContext.Provider value={readOnly}>
178
+ <PortableTextEditorSelectionProvider change$={change$}>
179
+ <Synchronizer
180
+ change$={change$}
181
+ getValue={this.getValue}
182
+ onChange={onChange}
183
+ value={value}
184
+ />
185
+ {children}
186
+ </PortableTextEditorSelectionProvider>
187
+ </PortableTextEditorReadOnlyContext.Provider>
188
+ </PortableTextEditorContext.Provider>
189
+ </PortableTextEditorKeyGeneratorContext.Provider>
176
190
  </SlateContainer>
177
191
  )
178
192
  }
@@ -46,7 +46,7 @@ describe('RangeDecorations', () => {
46
46
  />,
47
47
  )
48
48
  await waitFor(() => {
49
- expect([rangeDecorationIteration, 'initial']).toEqual([0, 'initial'])
49
+ expect([rangeDecorationIteration, 'initial']).toEqual([1, 'initial'])
50
50
  })
51
51
  // Re-render with the same range decorations
52
52
  rerender(
@@ -59,7 +59,7 @@ describe('RangeDecorations', () => {
59
59
  />,
60
60
  )
61
61
  await waitFor(() => {
62
- expect([rangeDecorationIteration, 'initial']).toEqual([0, 'initial'])
62
+ expect([rangeDecorationIteration, 'initial']).toEqual([1, 'initial'])
63
63
  })
64
64
  // Update the range decorations, a new object with identical values
65
65
  rangeDecorations = [
@@ -82,7 +82,7 @@ describe('RangeDecorations', () => {
82
82
  )
83
83
  await waitFor(() => {
84
84
  expect([rangeDecorationIteration, 'updated-with-equal-values']).toEqual([
85
- 0,
85
+ 1,
86
86
  'updated-with-equal-values',
87
87
  ])
88
88
  })
@@ -107,7 +107,7 @@ describe('RangeDecorations', () => {
107
107
  )
108
108
  await waitFor(() => {
109
109
  expect([rangeDecorationIteration, 'updated-with-different']).toEqual([
110
- 1,
110
+ 2,
111
111
  'updated-with-different',
112
112
  ])
113
113
  })
@@ -1,28 +1,17 @@
1
1
  import {type Patch} from '@portabletext/patches'
2
2
  import {type PortableTextBlock} from '@sanity/types'
3
3
  import {throttle} from 'lodash'
4
- import {
5
- type PropsWithChildren,
6
- startTransition,
7
- useCallback,
8
- useEffect,
9
- useMemo,
10
- useRef,
11
- useState,
12
- } from 'react'
4
+ import {useCallback, useEffect, useMemo, useRef} from 'react'
13
5
  import {Editor} from 'slate'
14
6
  import {useSlate} from 'slate-react'
15
7
 
16
- import {type EditorChange, type EditorChanges, type EditorSelection} from '../../types/editor'
8
+ import {type EditorChange, type EditorChanges} from '../../types/editor'
17
9
  import {debugWithName} from '../../utils/debug'
18
10
  import {IS_PROCESSING_LOCAL_CHANGES} from '../../utils/weakMaps'
19
- import {PortableTextEditorContext} from '../hooks/usePortableTextEditor'
20
- import {PortableTextEditorKeyGeneratorContext} from '../hooks/usePortableTextEditorKeyGenerator'
21
- import {PortableTextEditorSelectionContext} from '../hooks/usePortableTextEditorSelection'
22
- import {PortableTextEditorValueContext} from '../hooks/usePortableTextEditorValue'
23
- import {PortableTextEditorReadOnlyContext} from '../hooks/usePortableTextReadOnly'
11
+ import {usePortableTextEditor} from '../hooks/usePortableTextEditor'
12
+ import {usePortableTextEditorKeyGenerator} from '../hooks/usePortableTextEditorKeyGenerator'
13
+ import {usePortableTextEditorReadOnlyStatus} from '../hooks/usePortableTextReadOnly'
24
14
  import {useSyncValue} from '../hooks/useSyncValue'
25
- import {PortableTextEditor} from '../PortableTextEditor'
26
15
 
27
16
  const debug = debugWithName('component:PortableTextEditor:Synchronizer')
28
17
  const debugVerbose = debug.enabled && false
@@ -34,13 +23,11 @@ const FLUSH_PATCHES_THROTTLED_MS = process.env.NODE_ENV === 'test' ? 500 : 1000
34
23
  /**
35
24
  * @internal
36
25
  */
37
- export interface SynchronizerProps extends PropsWithChildren {
26
+ export interface SynchronizerProps {
38
27
  change$: EditorChanges
39
- portableTextEditor: PortableTextEditor
40
- keyGenerator: () => string
28
+ getValue: () => Array<PortableTextBlock> | undefined
41
29
  onChange: (change: EditorChange) => void
42
- readOnly: boolean
43
- value: PortableTextBlock[] | undefined
30
+ value: Array<PortableTextBlock> | undefined
44
31
  }
45
32
 
46
33
  /**
@@ -48,8 +35,10 @@ export interface SynchronizerProps extends PropsWithChildren {
48
35
  * @internal
49
36
  */
50
37
  export function Synchronizer(props: SynchronizerProps) {
51
- const {change$, portableTextEditor, onChange, keyGenerator, readOnly, value} = props
52
- const [selection, setSelection] = useState<EditorSelection>(null)
38
+ const portableTextEditor = usePortableTextEditor()
39
+ const keyGenerator = usePortableTextEditorKeyGenerator()
40
+ const readOnly = usePortableTextEditorReadOnlyStatus()
41
+ const {change$, getValue, onChange, value} = props
53
42
  const pendingPatches = useRef<Patch[]>([])
54
43
 
55
44
  const syncValue = useSyncValue({
@@ -71,12 +60,12 @@ export function Synchronizer(props: SynchronizerProps) {
71
60
  if (debugVerbose) {
72
61
  debug(`Patches:\n${JSON.stringify(pendingPatches.current, null, 2)}`)
73
62
  }
74
- const snapshot = PortableTextEditor.getValue(portableTextEditor)
63
+ const snapshot = getValue()
75
64
  change$.next({type: 'mutation', patches: pendingPatches.current, snapshot})
76
65
  pendingPatches.current = []
77
66
  }
78
67
  IS_PROCESSING_LOCAL_CHANGES.set(slateEditor, false)
79
- }, [slateEditor, portableTextEditor, change$])
68
+ }, [slateEditor, getValue, change$])
80
69
 
81
70
  const onFlushPendingPatchesThrottled = useMemo(() => {
82
71
  return throttle(
@@ -116,14 +105,6 @@ export function Synchronizer(props: SynchronizerProps) {
116
105
  onFlushPendingPatchesThrottled()
117
106
  onChange(next)
118
107
  break
119
- case 'selection':
120
- // Set the selection state in a transition, we don't need the state immediately.
121
- startTransition(() => {
122
- if (debugVerbose) debug('Setting selection')
123
- setSelection(next.selection)
124
- })
125
- onChange(next) // Keep this out of the startTransition!
126
- break
127
108
  default:
128
109
  onChange(next)
129
110
  }
@@ -174,17 +155,5 @@ export function Synchronizer(props: SynchronizerProps) {
174
155
  }
175
156
  }, [change$, syncValue, value])
176
157
 
177
- return (
178
- <PortableTextEditorKeyGeneratorContext.Provider value={keyGenerator}>
179
- <PortableTextEditorContext.Provider value={portableTextEditor}>
180
- <PortableTextEditorValueContext.Provider value={value}>
181
- <PortableTextEditorReadOnlyContext.Provider value={readOnly}>
182
- <PortableTextEditorSelectionContext.Provider value={selection}>
183
- {props.children}
184
- </PortableTextEditorSelectionContext.Provider>
185
- </PortableTextEditorReadOnlyContext.Provider>
186
- </PortableTextEditorValueContext.Provider>
187
- </PortableTextEditorContext.Provider>
188
- </PortableTextEditorKeyGeneratorContext.Provider>
189
- )
158
+ return null
190
159
  }
@@ -8,6 +8,7 @@ import {type PortableTextEditor} from '../PortableTextEditor'
8
8
  export const PortableTextEditorContext = createContext<PortableTextEditor | null>(null)
9
9
 
10
10
  /**
11
+ * @public
11
12
  * Get the current editor object from the React context.
12
13
  */
13
14
  export const usePortableTextEditor = (): PortableTextEditor => {
@@ -1,6 +1,9 @@
1
1
  import {randomKey} from '@sanity/util/content'
2
2
  import {createContext, useContext} from 'react'
3
3
 
4
+ /**
5
+ * @public
6
+ */
4
7
  export const defaultKeyGenerator = (): string => randomKey(12)
5
8
 
6
9
  /**
@@ -0,0 +1,63 @@
1
+ import {createContext, startTransition, useContext, useEffect, useState} from 'react'
2
+
3
+ import {type EditorChanges, type EditorSelection} from '../../types/editor'
4
+ import {debugWithName} from '../../utils/debug'
5
+
6
+ /**
7
+ * A React context for sharing the editor selection.
8
+ */
9
+ const PortableTextEditorSelectionContext = createContext<EditorSelection | null>(null)
10
+
11
+ /**
12
+ * @public
13
+ * Get the current editor selection from the React context.
14
+ */
15
+ export const usePortableTextEditorSelection = (): EditorSelection => {
16
+ const selection = useContext(PortableTextEditorSelectionContext)
17
+
18
+ if (selection === undefined) {
19
+ throw new Error(
20
+ `The \`usePortableTextEditorSelection\` hook must be used inside the <PortableTextEditor> component's context.`,
21
+ )
22
+ }
23
+ return selection
24
+ }
25
+ const debug = debugWithName('component:PortableTextEditor:SelectionProvider')
26
+ const debugVerbose = debug.enabled && false
27
+
28
+ /**
29
+ * @internal
30
+ */
31
+ export function PortableTextEditorSelectionProvider(
32
+ props: React.PropsWithChildren<{
33
+ change$: EditorChanges
34
+ }>,
35
+ ) {
36
+ const {change$} = props
37
+ const [selection, setSelection] = useState<EditorSelection>(null)
38
+
39
+ // Subscribe to, and handle changes from the editor
40
+ useEffect(() => {
41
+ debug('Subscribing to selection changes$')
42
+ const subscription = change$.subscribe((next): void => {
43
+ if (next.type === 'selection') {
44
+ // Set the selection state in a transition, we don't need the state immediately.
45
+ startTransition(() => {
46
+ if (debugVerbose) debug('Setting selection')
47
+ setSelection(next.selection)
48
+ })
49
+ }
50
+ })
51
+
52
+ return () => {
53
+ debug('Unsubscribing to selection changes$')
54
+ subscription.unsubscribe()
55
+ }
56
+ }, [change$])
57
+
58
+ return (
59
+ <PortableTextEditorSelectionContext.Provider value={selection}>
60
+ {props.children}
61
+ </PortableTextEditorSelectionContext.Provider>
62
+ )
63
+ }
@@ -195,12 +195,16 @@ export function createWithHotkeys(
195
195
  ) as SlateTextBlock | VoidElement
196
196
  const focusBlockPath = editor.selection.focus.path.slice(0, 1)
197
197
  const focusBlock = Node.descendant(editor, focusBlockPath) as SlateTextBlock | VoidElement
198
+ const isTextBlock = isPortableTextTextBlock(focusBlock)
199
+ const isEmptyFocusBlock =
200
+ isTextBlock && focusBlock.children.length === 1 && focusBlock.children?.[0]?.text === ''
198
201
 
199
202
  if (
200
203
  nextBlock &&
201
204
  focusBlock &&
202
205
  !Editor.isVoid(editor, focusBlock) &&
203
- Editor.isVoid(editor, nextBlock)
206
+ Editor.isVoid(editor, nextBlock) &&
207
+ isEmptyFocusBlock
204
208
  ) {
205
209
  debug('Preventing deleting void block below')
206
210
  event.preventDefault()
@@ -375,6 +375,9 @@ export type EditorChange =
375
375
  | UnsetChange
376
376
  | ValueChange
377
377
 
378
+ /**
379
+ * @beta
380
+ */
378
381
  export type EditorChanges = Subject<EditorChange>
379
382
 
380
383
  /** @beta */
@@ -384,6 +387,10 @@ export type OnPasteResult =
384
387
  path?: Path
385
388
  }
386
389
  | undefined
390
+
391
+ /**
392
+ * @beta
393
+ */
387
394
  export type OnPasteResultOrPromise = OnPasteResult | Promise<OnPasteResult>
388
395
 
389
396
  /** @beta */
@@ -3,6 +3,9 @@ import {type BaseSyntheticEvent} from 'react'
3
3
  import {type PortableTextEditor} from '../editor/PortableTextEditor'
4
4
  import {type PatchObservable} from './editor'
5
5
 
6
+ /**
7
+ * @internal
8
+ */
6
9
  export type createEditorOptions = {
7
10
  keyGenerator: () => string
8
11
  patches$?: PatchObservable
@@ -11,6 +14,9 @@ export type createEditorOptions = {
11
14
  maxBlocks?: number
12
15
  }
13
16
 
17
+ /**
18
+ * @beta
19
+ */
14
20
  export type HotkeyOptions = {
15
21
  marks?: Record<string, string>
16
22
  custom?: Record<string, (event: BaseSyntheticEvent, editor: PortableTextEditor) => void>
@@ -13,13 +13,6 @@ export function isChangingRemotely(editor: Editor): boolean | undefined {
13
13
  return IS_PROCESSING_REMOTE_CHANGES.get(editor)
14
14
  }
15
15
 
16
- export function withLocalChanges(editor: Editor, fn: () => void): void {
17
- const prev = isChangingLocally(editor) || false
18
- IS_PROCESSING_LOCAL_CHANGES.set(editor, true)
19
- fn()
20
- IS_PROCESSING_LOCAL_CHANGES.set(editor, prev)
21
- }
22
-
23
16
  export function isChangingLocally(editor: Editor): boolean | undefined {
24
17
  return IS_PROCESSING_LOCAL_CHANGES.get(editor)
25
18
  }
@@ -1,22 +0,0 @@
1
- import {createContext, useContext} from 'react'
2
-
3
- import {type EditorSelection} from '../../types/editor'
4
-
5
- /**
6
- * A React context for sharing the editor selection.
7
- */
8
- export const PortableTextEditorSelectionContext = createContext<EditorSelection | null>(null)
9
-
10
- /**
11
- * Get the current editor selection from the React context.
12
- */
13
- export const usePortableTextEditorSelection = (): EditorSelection => {
14
- const selection = useContext(PortableTextEditorSelectionContext)
15
-
16
- if (selection === undefined) {
17
- throw new Error(
18
- `The \`usePortableTextEditorSelection\` hook must be used inside the <PortableTextEditor> component's context.`,
19
- )
20
- }
21
- return selection
22
- }
@@ -1,16 +0,0 @@
1
- import {type PortableTextBlock} from '@sanity/types'
2
- import {createContext, useContext} from 'react'
3
-
4
- /**
5
- * A React context for sharing the editor value.
6
- */
7
- export const PortableTextEditorValueContext = createContext<PortableTextBlock[] | undefined>(
8
- undefined,
9
- )
10
-
11
- /**
12
- * Get the current editor value from the React context.
13
- */
14
- export const usePortableTextEditorValue = () => {
15
- return useContext(PortableTextEditorValueContext)
16
- }
@@ -1,15 +0,0 @@
1
- import {defer, EMPTY, type Observable, of, type OperatorFunction, switchMap, tap} from 'rxjs'
2
-
3
- export function bufferUntil<T>(
4
- emitWhen: (currentBuffer: T[]) => boolean,
5
- ): OperatorFunction<T, T[]> {
6
- return (source: Observable<T>) =>
7
- defer(() => {
8
- let buffer: T[] = [] // custom buffer
9
- return source.pipe(
10
- tap((v) => buffer.push(v)), // add values to buffer
11
- switchMap(() => (emitWhen(buffer) ? of(buffer) : EMPTY)), // emit the buffer when the condition is met
12
- tap(() => (buffer = [])), // clear the buffer
13
- )
14
- })
15
- }