@portabletext/editor 0.0.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.
Files changed (97) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +3 -0
  3. package/lib/index.d.mts +911 -0
  4. package/lib/index.d.ts +911 -0
  5. package/lib/index.esm.js +4896 -0
  6. package/lib/index.esm.js.map +1 -0
  7. package/lib/index.js +4874 -0
  8. package/lib/index.js.map +1 -0
  9. package/lib/index.mjs +4896 -0
  10. package/lib/index.mjs.map +1 -0
  11. package/package.json +119 -0
  12. package/src/editor/Editable.tsx +683 -0
  13. package/src/editor/PortableTextEditor.tsx +308 -0
  14. package/src/editor/__tests__/PortableTextEditor.test.tsx +386 -0
  15. package/src/editor/__tests__/PortableTextEditorTester.tsx +116 -0
  16. package/src/editor/__tests__/RangeDecorations.test.tsx +115 -0
  17. package/src/editor/__tests__/handleClick.test.tsx +218 -0
  18. package/src/editor/__tests__/pteWarningsSelfSolving.test.tsx +389 -0
  19. package/src/editor/__tests__/utils.ts +39 -0
  20. package/src/editor/components/DraggableBlock.tsx +287 -0
  21. package/src/editor/components/Element.tsx +279 -0
  22. package/src/editor/components/Leaf.tsx +288 -0
  23. package/src/editor/components/SlateContainer.tsx +81 -0
  24. package/src/editor/components/Synchronizer.tsx +190 -0
  25. package/src/editor/hooks/usePortableTextEditor.ts +23 -0
  26. package/src/editor/hooks/usePortableTextEditorKeyGenerator.ts +24 -0
  27. package/src/editor/hooks/usePortableTextEditorSelection.ts +22 -0
  28. package/src/editor/hooks/usePortableTextEditorValue.ts +16 -0
  29. package/src/editor/hooks/usePortableTextReadOnly.ts +20 -0
  30. package/src/editor/hooks/useSyncValue.test.tsx +125 -0
  31. package/src/editor/hooks/useSyncValue.ts +372 -0
  32. package/src/editor/nodes/DefaultAnnotation.tsx +16 -0
  33. package/src/editor/nodes/DefaultObject.tsx +15 -0
  34. package/src/editor/nodes/index.ts +189 -0
  35. package/src/editor/plugins/__tests__/withEditableAPIDelete.test.tsx +244 -0
  36. package/src/editor/plugins/__tests__/withEditableAPIGetFragment.test.tsx +142 -0
  37. package/src/editor/plugins/__tests__/withEditableAPIInsert.test.tsx +346 -0
  38. package/src/editor/plugins/__tests__/withEditableAPISelectionsOverlapping.test.tsx +162 -0
  39. package/src/editor/plugins/__tests__/withHotkeys.test.tsx +212 -0
  40. package/src/editor/plugins/__tests__/withInsertBreak.test.tsx +204 -0
  41. package/src/editor/plugins/__tests__/withPlaceholderBlock.test.tsx +133 -0
  42. package/src/editor/plugins/__tests__/withPortableTextLists.test.tsx +65 -0
  43. package/src/editor/plugins/__tests__/withPortableTextMarkModel.test.tsx +1377 -0
  44. package/src/editor/plugins/__tests__/withPortableTextSelections.test.tsx +91 -0
  45. package/src/editor/plugins/__tests__/withUndoRedo.test.tsx +115 -0
  46. package/src/editor/plugins/createWithEditableAPI.ts +573 -0
  47. package/src/editor/plugins/createWithHotKeys.ts +304 -0
  48. package/src/editor/plugins/createWithInsertBreak.ts +45 -0
  49. package/src/editor/plugins/createWithInsertData.ts +359 -0
  50. package/src/editor/plugins/createWithMaxBlocks.ts +24 -0
  51. package/src/editor/plugins/createWithObjectKeys.ts +63 -0
  52. package/src/editor/plugins/createWithPatches.ts +274 -0
  53. package/src/editor/plugins/createWithPlaceholderBlock.ts +36 -0
  54. package/src/editor/plugins/createWithPortableTextBlockStyle.ts +91 -0
  55. package/src/editor/plugins/createWithPortableTextLists.ts +160 -0
  56. package/src/editor/plugins/createWithPortableTextMarkModel.ts +441 -0
  57. package/src/editor/plugins/createWithPortableTextSelections.ts +65 -0
  58. package/src/editor/plugins/createWithSchemaTypes.ts +76 -0
  59. package/src/editor/plugins/createWithUndoRedo.ts +494 -0
  60. package/src/editor/plugins/createWithUtils.ts +81 -0
  61. package/src/editor/plugins/index.ts +155 -0
  62. package/src/index.ts +11 -0
  63. package/src/patch/PatchEvent.ts +33 -0
  64. package/src/patch/applyPatch.ts +29 -0
  65. package/src/patch/array.ts +89 -0
  66. package/src/patch/arrayInsert.ts +27 -0
  67. package/src/patch/object.ts +39 -0
  68. package/src/patch/patches.ts +53 -0
  69. package/src/patch/primitive.ts +43 -0
  70. package/src/patch/string.ts +51 -0
  71. package/src/types/editor.ts +576 -0
  72. package/src/types/options.ts +17 -0
  73. package/src/types/patch.ts +65 -0
  74. package/src/types/slate.ts +25 -0
  75. package/src/utils/__tests__/dmpToOperations.test.ts +181 -0
  76. package/src/utils/__tests__/operationToPatches.test.ts +421 -0
  77. package/src/utils/__tests__/patchToOperations.test.ts +293 -0
  78. package/src/utils/__tests__/ranges.test.ts +18 -0
  79. package/src/utils/__tests__/valueNormalization.test.tsx +62 -0
  80. package/src/utils/__tests__/values.test.ts +253 -0
  81. package/src/utils/applyPatch.ts +407 -0
  82. package/src/utils/bufferUntil.ts +15 -0
  83. package/src/utils/debug.ts +12 -0
  84. package/src/utils/getPortableTextMemberSchemaTypes.ts +100 -0
  85. package/src/utils/operationToPatches.ts +357 -0
  86. package/src/utils/patches.ts +36 -0
  87. package/src/utils/paths.ts +60 -0
  88. package/src/utils/ranges.ts +77 -0
  89. package/src/utils/schema.ts +8 -0
  90. package/src/utils/selection.ts +65 -0
  91. package/src/utils/ucs2Indices.ts +67 -0
  92. package/src/utils/validateValue.ts +394 -0
  93. package/src/utils/values.ts +208 -0
  94. package/src/utils/weakMaps.ts +24 -0
  95. package/src/utils/withChanges.ts +25 -0
  96. package/src/utils/withPreserveKeys.ts +14 -0
  97. package/src/utils/withoutPatching.ts +14 -0
@@ -0,0 +1,155 @@
1
+ import {noop} from 'lodash'
2
+ import {type BaseOperation, type Editor, type Node, type NodeEntry} from 'slate'
3
+
4
+ import {type PortableTextSlateEditor} from '../../types/editor'
5
+ import {type createEditorOptions} from '../../types/options'
6
+ import {createOperationToPatches} from '../../utils/operationToPatches'
7
+ import {createWithEditableAPI} from './createWithEditableAPI'
8
+ import {createWithInsertBreak} from './createWithInsertBreak'
9
+ import {createWithMaxBlocks} from './createWithMaxBlocks'
10
+ import {createWithObjectKeys} from './createWithObjectKeys'
11
+ import {createWithPatches} from './createWithPatches'
12
+ import {createWithPlaceholderBlock} from './createWithPlaceholderBlock'
13
+ import {createWithPortableTextBlockStyle} from './createWithPortableTextBlockStyle'
14
+ import {createWithPortableTextLists} from './createWithPortableTextLists'
15
+ import {createWithPortableTextMarkModel} from './createWithPortableTextMarkModel'
16
+ import {createWithPortableTextSelections} from './createWithPortableTextSelections'
17
+ import {createWithSchemaTypes} from './createWithSchemaTypes'
18
+ import {createWithUndoRedo} from './createWithUndoRedo'
19
+ import {createWithUtils} from './createWithUtils'
20
+
21
+ export {createWithEditableAPI} from './createWithEditableAPI'
22
+ export {createWithHotkeys} from './createWithHotKeys'
23
+ export {createWithInsertData} from './createWithInsertData'
24
+ export {createWithMaxBlocks} from './createWithMaxBlocks'
25
+ export {createWithObjectKeys} from './createWithObjectKeys'
26
+ export {createWithPatches} from './createWithPatches'
27
+ export {createWithPortableTextBlockStyle} from './createWithPortableTextBlockStyle'
28
+ export {createWithPortableTextLists} from './createWithPortableTextLists'
29
+ export {createWithPortableTextMarkModel} from './createWithPortableTextMarkModel'
30
+ export {createWithPortableTextSelections} from './createWithPortableTextSelections'
31
+ export {createWithSchemaTypes} from './createWithSchemaTypes'
32
+ export {createWithUndoRedo} from './createWithUndoRedo'
33
+ export {createWithUtils} from './createWithUtils'
34
+
35
+ export interface OriginalEditorFunctions {
36
+ apply: (operation: BaseOperation) => void
37
+ onChange: () => void
38
+ normalizeNode: (entry: NodeEntry<Node>) => void
39
+ }
40
+
41
+ const originalFnMap = new WeakMap<PortableTextSlateEditor, OriginalEditorFunctions>()
42
+
43
+ export const withPlugins = <T extends Editor>(
44
+ editor: T,
45
+ options: createEditorOptions,
46
+ ): {editor: PortableTextSlateEditor; subscribe: () => () => void} => {
47
+ const e = editor as T & PortableTextSlateEditor
48
+ const {keyGenerator, portableTextEditor, patches$, readOnly, maxBlocks} = options
49
+ const {schemaTypes, change$} = portableTextEditor
50
+ e.subscriptions = []
51
+ if (e.destroy) {
52
+ e.destroy()
53
+ } else {
54
+ // Save a copy of the original editor functions here before they were changed by plugins.
55
+ // We will put them back when .destroy is called (see below).
56
+ originalFnMap.set(e, {
57
+ apply: e.apply,
58
+ onChange: e.onChange,
59
+ normalizeNode: e.normalizeNode,
60
+ })
61
+ }
62
+ const operationToPatches = createOperationToPatches(schemaTypes)
63
+ const withObjectKeys = createWithObjectKeys(schemaTypes, keyGenerator)
64
+ const withSchemaTypes = createWithSchemaTypes({schemaTypes, keyGenerator})
65
+ const withEditableAPI = createWithEditableAPI(portableTextEditor, schemaTypes, keyGenerator)
66
+ const withPatches = createWithPatches({
67
+ change$,
68
+ keyGenerator,
69
+ patches$,
70
+ patchFunctions: operationToPatches,
71
+ readOnly,
72
+ schemaTypes,
73
+ })
74
+ const withMaxBlocks = createWithMaxBlocks(maxBlocks || -1)
75
+ const withPortableTextLists = createWithPortableTextLists(schemaTypes)
76
+ const withUndoRedo = createWithUndoRedo({
77
+ readOnly,
78
+ patches$,
79
+ blockSchemaType: schemaTypes.block,
80
+ })
81
+ const withPortableTextMarkModel = createWithPortableTextMarkModel(schemaTypes, change$)
82
+ const withPortableTextBlockStyle = createWithPortableTextBlockStyle(schemaTypes)
83
+
84
+ const withPlaceholderBlock = createWithPlaceholderBlock()
85
+
86
+ const withInsertBreak = createWithInsertBreak(schemaTypes)
87
+
88
+ const withUtils = createWithUtils({keyGenerator, schemaTypes, portableTextEditor})
89
+ const withPortableTextSelections = createWithPortableTextSelections(change$, schemaTypes)
90
+
91
+ e.destroy = () => {
92
+ const originalFunctions = originalFnMap.get(e)
93
+ if (!originalFunctions) {
94
+ throw new Error('Could not find pristine versions of editor functions')
95
+ }
96
+ e.apply = originalFunctions.apply
97
+ e.history = {undos: [], redos: []}
98
+ e.normalizeNode = originalFunctions.normalizeNode
99
+ e.onChange = originalFunctions.onChange
100
+ }
101
+ if (readOnly) {
102
+ return {
103
+ editor: withSchemaTypes(
104
+ withObjectKeys(
105
+ withPortableTextMarkModel(
106
+ withPortableTextBlockStyle(
107
+ withUtils(
108
+ withPlaceholderBlock(
109
+ withPortableTextLists(
110
+ withPortableTextSelections(withEditableAPI(withInsertBreak(e))),
111
+ ),
112
+ ),
113
+ ),
114
+ ),
115
+ ),
116
+ ),
117
+ ),
118
+ subscribe: () => noop,
119
+ }
120
+ }
121
+
122
+ // Ordering is important here, selection dealing last, data manipulation in the middle and core model stuff first.
123
+ return {
124
+ editor: withSchemaTypes(
125
+ withObjectKeys(
126
+ withPortableTextMarkModel(
127
+ withPortableTextBlockStyle(
128
+ withPortableTextLists(
129
+ withPlaceholderBlock(
130
+ withUtils(
131
+ withMaxBlocks(
132
+ withUndoRedo(
133
+ withPatches(withPortableTextSelections(withEditableAPI(withInsertBreak(e)))),
134
+ ),
135
+ ),
136
+ ),
137
+ ),
138
+ ),
139
+ ),
140
+ ),
141
+ ),
142
+ ),
143
+ subscribe: () => {
144
+ const unsubscribes: (() => void)[] = []
145
+ editor.subscriptions.forEach((subscribeFn) => {
146
+ unsubscribes.push(subscribeFn())
147
+ })
148
+ return () => {
149
+ unsubscribes.forEach((unsubscribeFn) => {
150
+ unsubscribeFn()
151
+ })
152
+ }
153
+ },
154
+ }
155
+ }
package/src/index.ts ADDED
@@ -0,0 +1,11 @@
1
+ export type {PortableTextEditableProps} from './editor/Editable'
2
+ export {PortableTextEditable} from './editor/Editable'
3
+ export {usePortableTextEditor} from './editor/hooks/usePortableTextEditor'
4
+ export {defaultKeyGenerator as keyGenerator} from './editor/hooks/usePortableTextEditorKeyGenerator'
5
+ export {usePortableTextEditorSelection} from './editor/hooks/usePortableTextEditorSelection'
6
+ export type {PortableTextEditorProps} from './editor/PortableTextEditor'
7
+ export {PortableTextEditor} from './editor/PortableTextEditor'
8
+ export * from './types/editor'
9
+ export * from './types/options'
10
+ export * from './types/patch'
11
+ export {compactPatches} from './utils/patches'
@@ -0,0 +1,33 @@
1
+ import {type PathSegment} from '@sanity/types'
2
+ import {flatten} from 'lodash'
3
+
4
+ import {type Patch} from '../types/patch'
5
+ import {diffMatchPatch, insert, prefixPath, set, setIfMissing, unset} from './patches'
6
+
7
+ type PatchArg = Patch | Array<Patch>
8
+
9
+ export default class PatchEvent {
10
+ static from(...patches: Array<PatchArg>) {
11
+ return new PatchEvent(flatten(patches))
12
+ }
13
+
14
+ patches: Array<Patch>
15
+
16
+ constructor(patches: Array<Patch>) {
17
+ this.patches = patches
18
+ }
19
+
20
+ prepend(...patches: Array<PatchArg>): PatchEvent {
21
+ return PatchEvent.from([...flatten(patches), ...this.patches])
22
+ }
23
+
24
+ append(...patches: Array<PatchArg>): PatchEvent {
25
+ return PatchEvent.from([...this.patches, ...flatten(patches)])
26
+ }
27
+
28
+ prefixAll(segment: PathSegment): PatchEvent {
29
+ return PatchEvent.from(this.patches.map((patch) => prefixPath(patch, segment)))
30
+ }
31
+ }
32
+
33
+ export {diffMatchPatch, insert, PatchEvent, set, setIfMissing, unset}
@@ -0,0 +1,29 @@
1
+ import {isObject, isString} from 'lodash'
2
+
3
+ import applyArrayPatch from './array'
4
+ import applyObjectPatch from './object'
5
+ import applyPrimitivePatch from './primitive'
6
+ import applyStringPatch from './string'
7
+
8
+ export function applyAll(value: any, patches: any[]) {
9
+ return patches.reduce(_apply, value)
10
+ }
11
+
12
+ function applyPatch(value: string, patch: {type: string; path: any[]; value: any}) {
13
+ if (Array.isArray(value)) {
14
+ return applyArrayPatch(value, patch as any)
15
+ }
16
+ if (isString(value)) {
17
+ return applyStringPatch(value, patch)
18
+ }
19
+ if (isObject(value)) {
20
+ return applyObjectPatch(value, patch)
21
+ }
22
+ return applyPrimitivePatch(value, patch)
23
+ }
24
+
25
+ export default function _apply(value: string, patch: {type: string; path: any[]; value: any}) {
26
+ const res = applyPatch(value, patch)
27
+ // console.log('applyPatch(%o, %o) : %o (noop? %o)', value, patch, res, value === res)
28
+ return res
29
+ }
@@ -0,0 +1,89 @@
1
+ import {type PathSegment} from '@sanity/types'
2
+ import {findIndex} from 'lodash'
3
+
4
+ import applyPatch from './applyPatch'
5
+ import insert from './arrayInsert'
6
+
7
+ const hasOwn = Object.prototype.hasOwnProperty.call.bind(Object.prototype.hasOwnProperty)
8
+
9
+ function move(arr: any[], from: number, to: any) {
10
+ const nextValue = arr.slice()
11
+ const val = nextValue[from]
12
+ nextValue.splice(from, 1)
13
+ nextValue.splice(to, 0, val)
14
+ return nextValue
15
+ }
16
+
17
+ function findTargetIndex(array: any[], pathSegment: PathSegment) {
18
+ if (typeof pathSegment === 'number') {
19
+ return pathSegment
20
+ }
21
+ const index = findIndex(array, pathSegment)
22
+ return index === -1 ? false : index
23
+ }
24
+
25
+ export default function apply(
26
+ value: any,
27
+ patch: {type: any; path: any; value: any; position: any; items: any},
28
+ ) {
29
+ const nextValue = value.slice() // make a copy for internal mutation
30
+
31
+ if (patch.path.length === 0) {
32
+ // its directed to me
33
+ if (patch.type === 'setIfMissing') {
34
+ if (!Array.isArray(patch.value)) {
35
+ // eslint-disable-line max-depth
36
+ throw new Error('Cannot set value of an array to a non-array')
37
+ }
38
+ return value === undefined ? patch.value : value
39
+ } else if (patch.type === 'set') {
40
+ if (!Array.isArray(patch.value)) {
41
+ // eslint-disable-line max-depth
42
+ throw new Error('Cannot set value of an array to a non-array')
43
+ }
44
+ return patch.value
45
+ } else if (patch.type === 'unset') {
46
+ return undefined
47
+ } else if (patch.type === 'move') {
48
+ if (!patch.value || !hasOwn(patch.value, 'from') || !hasOwn(patch.value, 'to')) {
49
+ // eslint-disable-line max-depth
50
+ throw new Error(
51
+ `Invalid value of 'move' patch. Expected a value with "from" and "to" indexes, instead got: ${JSON.stringify(
52
+ patch.value,
53
+ )}`,
54
+ )
55
+ }
56
+ return move(nextValue, patch.value.from, patch.value.to)
57
+ }
58
+ throw new Error(`Invalid array operation: ${patch.type}`)
59
+ }
60
+
61
+ const [head, ...tail] = patch.path
62
+
63
+ const index = findTargetIndex(value, head)
64
+
65
+ // If the given selector could not be found, return as-is
66
+ if (index === false) {
67
+ return nextValue
68
+ }
69
+
70
+ if (tail.length === 0) {
71
+ if (patch.type === 'insert') {
72
+ const {position, items} = patch
73
+ return insert(value, position, index, items)
74
+ } else if (patch.type === 'unset') {
75
+ if (typeof index !== 'number') {
76
+ throw new Error(`Expected array index to be a number, instead got "${index}"`)
77
+ }
78
+ nextValue.splice(index, 1)
79
+ return nextValue
80
+ }
81
+ }
82
+
83
+ // The patch is not directed to me
84
+ nextValue[index] = applyPatch(nextValue[index], {
85
+ ...patch,
86
+ path: tail,
87
+ })
88
+ return nextValue
89
+ }
@@ -0,0 +1,27 @@
1
+ export const BEFORE = 'before'
2
+ export const AFTER = 'after'
3
+
4
+ export default function insert(array: any[], position: string, index: number, ...args: any[]) {
5
+ if (position !== BEFORE && position !== AFTER) {
6
+ throw new Error(`Invalid position "${position}", must be either ${BEFORE} or ${AFTER}`)
7
+ }
8
+
9
+ const items = flatten(...args)
10
+
11
+ if (array.length === 0) {
12
+ return items
13
+ }
14
+
15
+ const len = array.length
16
+ const idx = Math.abs((len + index) % len) % len
17
+
18
+ const normalizedIdx = position === 'after' ? idx + 1 : idx
19
+
20
+ const copy = array.slice()
21
+ copy.splice(normalizedIdx, 0, ...flatten(items))
22
+ return copy
23
+ }
24
+
25
+ function flatten(...values: any[]) {
26
+ return values.reduce((prev, item) => prev.concat(item), [])
27
+ }
@@ -0,0 +1,39 @@
1
+ import {clone, isObject, omit} from 'lodash'
2
+
3
+ import applyPatch from './applyPatch'
4
+
5
+ export default function apply(value: any, patch: {type: any; path: any; value: any}) {
6
+ const nextValue = clone(value)
7
+ if (patch.path.length === 0) {
8
+ // its directed to me
9
+ if (patch.type === 'set') {
10
+ if (!isObject(patch.value)) {
11
+ // eslint-disable-line max-depth
12
+ throw new Error('Cannot set value of an object to a non-object')
13
+ }
14
+ return patch.value
15
+ } else if (patch.type === 'unset') {
16
+ return undefined
17
+ } else if (patch.type === 'setIfMissing') {
18
+ // console.log('IS IT missing?', value)
19
+ return value === undefined ? patch.value : value
20
+ }
21
+ throw new Error(`Invalid object operation: ${patch.type}`)
22
+ }
23
+
24
+ // The patch is not directed to me
25
+ const [head, ...tail] = patch.path
26
+ if (typeof head !== 'string') {
27
+ throw new Error(`Expected field name to be a string, instad got: ${head}`)
28
+ }
29
+
30
+ if (tail.length === 0 && patch.type === 'unset') {
31
+ return omit(nextValue, head)
32
+ }
33
+
34
+ nextValue[head] = applyPatch(nextValue[head], {
35
+ ...patch,
36
+ path: tail,
37
+ })
38
+ return nextValue
39
+ }
@@ -0,0 +1,53 @@
1
+ import {makePatches, stringifyPatches} from '@sanity/diff-match-patch'
2
+ import {type Path, type PathSegment} from '@sanity/types'
3
+
4
+ import {
5
+ type DiffMatchPatch,
6
+ type InsertPatch,
7
+ type InsertPosition,
8
+ type SetIfMissingPatch,
9
+ type SetPatch,
10
+ type UnsetPatch,
11
+ } from '../types/patch'
12
+
13
+ export function setIfMissing(value: any, path: Path = []): SetIfMissingPatch {
14
+ return {
15
+ type: 'setIfMissing',
16
+ path,
17
+ value,
18
+ }
19
+ }
20
+
21
+ export function diffMatchPatch(
22
+ currentValue: string,
23
+ nextValue: string,
24
+ path: Path = [],
25
+ ): DiffMatchPatch {
26
+ const patches = makePatches(currentValue, nextValue)
27
+ const patch = stringifyPatches(patches)
28
+ return {type: 'diffMatchPatch', path, value: patch}
29
+ }
30
+
31
+ export function insert(items: any[], position: InsertPosition, path: Path = []): InsertPatch {
32
+ return {
33
+ type: 'insert',
34
+ path,
35
+ position,
36
+ items,
37
+ }
38
+ }
39
+
40
+ export function set(value: any, path: Path = []): SetPatch {
41
+ return {type: 'set', path, value}
42
+ }
43
+
44
+ export function unset(path: Path = []): UnsetPatch {
45
+ return {type: 'unset', path}
46
+ }
47
+
48
+ export function prefixPath<T extends {path: Path}>(patch: T, segment: PathSegment): T {
49
+ return {
50
+ ...patch,
51
+ path: [segment, ...patch.path],
52
+ }
53
+ }
@@ -0,0 +1,43 @@
1
+ const OPERATIONS: Record<string, any> = {
2
+ replace(_currentValue: any, nextValue: any) {
3
+ return nextValue
4
+ },
5
+ set(_currentValue: any, nextValue: any) {
6
+ return nextValue
7
+ },
8
+ setIfMissing(currentValue: any, nextValue: any) {
9
+ return currentValue === undefined ? nextValue : currentValue
10
+ },
11
+ unset(_currentValue: any, _nextValue: any) {
12
+ return undefined
13
+ },
14
+ inc(currentValue: any, nextValue: any) {
15
+ return currentValue + nextValue
16
+ },
17
+ dec(currentValue: any, nextValue: any) {
18
+ return currentValue - nextValue
19
+ },
20
+ }
21
+
22
+ const SUPPORTED_PATCH_TYPES = Object.keys(OPERATIONS)
23
+
24
+ export default function apply(value: any, patch: any) {
25
+ if (!SUPPORTED_PATCH_TYPES.includes(patch.type)) {
26
+ throw new Error(
27
+ `Received patch of unsupported type: "${JSON.stringify(
28
+ patch.type,
29
+ )}" for primitives. This is most likely a bug.`,
30
+ )
31
+ }
32
+
33
+ if (patch.path.length > 0) {
34
+ throw new Error(
35
+ `Cannot apply deep operations on primitive values. Received patch with type "${
36
+ patch.type
37
+ }" and path "${patch.path
38
+ .map((path: any) => JSON.stringify(path))
39
+ .join('.')} that targeted the value "${JSON.stringify(value)}"`,
40
+ )
41
+ }
42
+ return OPERATIONS[patch.type](value, patch.value)
43
+ }
@@ -0,0 +1,51 @@
1
+ import {applyPatches, parsePatch} from '@sanity/diff-match-patch'
2
+
3
+ type fn = (oldVal: any, newVal: any) => any
4
+ const OPERATIONS: Record<string, fn> = {
5
+ replace(currentValue: any, nextValue: any) {
6
+ return nextValue
7
+ },
8
+ set(currentValue: any, nextValue: any) {
9
+ return nextValue
10
+ },
11
+ setIfMissing(currentValue: undefined, nextValue: any) {
12
+ return currentValue === undefined ? nextValue : currentValue
13
+ },
14
+ unset(currentValue: any, nextValue: any) {
15
+ return undefined
16
+ },
17
+ diffMatchPatch(currentValue: string, nextValue: string): string {
18
+ const [result] = applyPatches(parsePatch(nextValue), currentValue, {
19
+ allowExceedingIndices: true,
20
+ })
21
+ return result
22
+ },
23
+ }
24
+
25
+ const SUPPORTED_PATCH_TYPES = Object.keys(OPERATIONS)
26
+
27
+ export default function apply(
28
+ value: string,
29
+ patch: {type: string; path: any[]; value: any},
30
+ ): string {
31
+ if (!SUPPORTED_PATCH_TYPES.includes(patch.type)) {
32
+ throw new Error(
33
+ `Received patch of unsupported type: "${JSON.stringify(
34
+ patch.type,
35
+ )}" for string. This is most likely a bug.`,
36
+ )
37
+ }
38
+
39
+ if (patch.path.length > 0) {
40
+ throw new Error(
41
+ `Cannot apply deep operations on string values. Received patch with type "${
42
+ patch.type
43
+ }" and path "${patch.path.join('.')} that targeted the value "${JSON.stringify(value)}"`,
44
+ )
45
+ }
46
+ const func = OPERATIONS[patch.type]
47
+ if (func) {
48
+ return func(value, patch.value)
49
+ }
50
+ throw new Error('Unknown patch type')
51
+ }