Skip to content
This repository was archived by the owner on Feb 3, 2026. It is now read-only.

Commit f9b14cf

Browse files
committed
fix: enhance onChange handler for paste operations in InternationalizedInput
- Ensure wrapped onChange handler correctly processes both single and multiple patch operations. - Add checks for empty or uninitialized Portable Text fields to prevent errors during paste operations. - Improve patch transformation logic to handle nested structures more robustly. This update addresses issues related to applying insert patches to non-existent structures, ensuring a smoother user experience when interacting with internationalized fields.
1 parent c59dba0 commit f9b14cf

File tree

1 file changed

+77
-38
lines changed

1 file changed

+77
-38
lines changed

src/components/InternationalizedInput.tsx

Lines changed: 77 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -38,49 +38,88 @@ export default function InternationalizedInput(
3838
const originalOnChange = props.inputProps.onChange
3939

4040
// Create a wrapped onChange handler to intercept patches for paste operations
41-
const wrappedOnChange = useCallback((patches: any) => {
42-
// Check if this is a paste operation into an empty or uninitialized Portable Text field
43-
const valueField = props.value?.value
44-
const isEmptyOrUndefined = valueField === undefined ||
45-
(Array.isArray(valueField) && valueField.length === 0)
46-
47-
if (isEmptyOrUndefined) {
48-
// Check for insert patches that are trying to operate on a non-existent structure
49-
const hasProblematicInsert = patches.some((patch: any) => {
50-
// Look for insert patches targeting the value field or direct array index
51-
if (patch.type === 'insert' && patch.path && patch.path.length > 0) {
52-
// The path might be ['value', index] or just [index] depending on context
53-
const isTargetingValue = patch.path[0] === 'value' || typeof patch.path[0] === 'number'
54-
return isTargetingValue
55-
}
56-
return false
57-
})
58-
59-
if (hasProblematicInsert) {
60-
// First, ensure the value field exists as an empty array if it doesn't
61-
const initPatch = valueField === undefined ? { type: 'setIfMissing', path: ['value'], value: [] } : null
62-
63-
// Transform the patches to ensure they work with the nested structure
64-
const fixedPatches = patches.map((patch: any) => {
65-
if (patch.type === 'insert' && patch.path) {
66-
// Ensure the path is correct for the nested structure
67-
const fixedPath = patch.path[0] === 'value' ? patch.path : ['value', ...patch.path]
68-
const fixedPatch = { ...patch, path: fixedPath }
69-
return fixedPatch
41+
const wrappedOnChange = useCallback(
42+
(patches: any) => {
43+
// Ensure patches is an array before proceeding with paste logic
44+
// For single patch operations (like unset), pass through directly
45+
if (!Array.isArray(patches)) {
46+
return originalOnChange(patches)
47+
}
48+
49+
// Check if this is a paste operation into an empty or uninitialized Portable Text field
50+
const valueField = props.value?.value
51+
const isEmptyOrUndefined =
52+
valueField === undefined ||
53+
valueField === null ||
54+
(Array.isArray(valueField) && valueField.length === 0)
55+
56+
if (isEmptyOrUndefined) {
57+
// Check for insert patches that are trying to operate on a non-existent structure
58+
const hasProblematicInsert = patches.some((patch: any) => {
59+
// Ensure patch exists and has required properties
60+
if (!patch || typeof patch !== 'object') {
61+
return false
7062
}
71-
return patch
72-
})
7363

74-
// If we need to initialize the field, include that patch first
75-
const allPatches = initPatch ? [initPatch, ...fixedPatches] : fixedPatches
64+
// Look for insert patches targeting the value field or direct array index
65+
if (
66+
patch.type === 'insert' &&
67+
patch.path &&
68+
Array.isArray(patch.path) &&
69+
patch.path.length > 0
70+
) {
71+
// The path might be ['value', index] or just [index] depending on context
72+
const isTargetingValue =
73+
patch.path[0] === 'value' || typeof patch.path[0] === 'number'
74+
return isTargetingValue
75+
}
76+
return false
77+
})
7678

77-
return originalOnChange(allPatches)
79+
if (hasProblematicInsert) {
80+
// First, ensure the value field exists as an empty array if it doesn't
81+
const initPatch =
82+
valueField === undefined
83+
? {type: 'setIfMissing', path: ['value'], value: []}
84+
: null
85+
86+
// Transform the patches to ensure they work with the nested structure
87+
const fixedPatches = patches.map((patch: any) => {
88+
// Ensure patch exists and has required properties
89+
if (!patch || typeof patch !== 'object') {
90+
return patch
91+
}
92+
93+
if (
94+
patch.type === 'insert' &&
95+
patch.path &&
96+
Array.isArray(patch.path)
97+
) {
98+
// Ensure the path is correct for the nested structure
99+
const fixedPath =
100+
patch.path[0] === 'value'
101+
? patch.path
102+
: ['value', ...patch.path]
103+
const fixedPatch = {...patch, path: fixedPath}
104+
return fixedPatch
105+
}
106+
return patch
107+
})
108+
109+
// If we need to initialize the field, include that patch first
110+
const allPatches = initPatch
111+
? [initPatch, ...fixedPatches]
112+
: fixedPatches
113+
114+
return originalOnChange(allPatches)
115+
}
78116
}
79-
}
80117

81-
// For all other cases, pass through unchanged
82-
return originalOnChange(patches)
83-
}, [props.value, originalOnChange])
118+
// For all other cases, pass through unchanged
119+
return originalOnChange(patches)
120+
},
121+
[props.value, originalOnChange]
122+
)
84123

85124
const inlineProps = {
86125
...props.inputProps,

0 commit comments

Comments
 (0)