Skip to content

#122 Handle cancel when "Type" changes #177

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Mar 8, 2025
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -854,6 +854,7 @@ This component is heavily inspired by [react-json-view](https://github.com/mac-s

- **1.24.0**:
- Option to access (and render) the original node (and its key) within a [Custom Node](#custom-nodes) ([#180](https://github.com/CarlosNZ/json-edit-react/issues/180))
- Cancelling edit after changing type correctly reverts to previous value ([#122](https://github.com/CarlosNZ/json-edit-react/issues/122))
- **1.23.1**: Fix bug where you could collapse a node by clicking inside a "new key" input field [#175](https://github.com/CarlosNZ/json-edit-react/issues/175)
- **1.23.0**:
- Add `viewOnly` prop as a shorthand for restricting all editing [#168](https://github.com/CarlosNZ/json-edit-react/issues/168)
Expand Down
11 changes: 10 additions & 1 deletion src/CollectionNode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ export const CollectionNode: React.FC<CollectionNodeProps> = (props) => {
currentlyEditingElement,
setCurrentlyEditingElement,
areChildrenBeingEdited,
previousValue,
setPreviousValue,
} = useTreeState()
const {
mainContainerRef,
Expand Down Expand Up @@ -99,7 +101,7 @@ export const CollectionNode: React.FC<CollectionNodeProps> = (props) => {

useEffect(() => {
setStringifiedValue(jsonStringify(data))
if (isEditing) setCurrentlyEditingElement(null)
// if (isEditing) setCurrentlyEditingElement(null)
}, [data])

useEffect(() => {
Expand Down Expand Up @@ -189,6 +191,7 @@ export const CollectionNode: React.FC<CollectionNodeProps> = (props) => {
try {
const value = jsonParse(stringifiedValue)
setCurrentlyEditingElement(null)
setPreviousValue(null)
setError(null)
if (JSON.stringify(value) === JSON.stringify(data)) return
onEdit(value, path).then((error) => {
Expand Down Expand Up @@ -236,8 +239,13 @@ export const CollectionNode: React.FC<CollectionNodeProps> = (props) => {

const handleCancel = () => {
setCurrentlyEditingElement(null)
if (previousValue !== null) {
onEdit(previousValue, path)
return
}
setError(null)
setStringifiedValue(jsonStringify(data))
setPreviousValue(null)
}

const showLabel = showArrayIndices || !isArray
Expand Down Expand Up @@ -366,6 +374,7 @@ export const CollectionNode: React.FC<CollectionNodeProps> = (props) => {
canEdit
? () => {
hasBeenOpened.current = true
setPreviousValue(null)
setCurrentlyEditingElement(path)
}
: undefined
Expand Down
15 changes: 14 additions & 1 deletion src/ValueNodeWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ export const ValueNodeWrapper: React.FC<ValueNodeProps> = (props) => {
setPreviouslyEditedElement,
tabDirection,
setTabDirection,
previousValue,
setPreviousValue,
} = useTreeState()
const [value, setValue] = useState<typeof data | CollectionData>(
// Bad things happen when you put a function into useState
Expand Down Expand Up @@ -178,6 +180,7 @@ export const ValueNodeWrapper: React.FC<ValueNodeProps> = (props) => {

const handleEdit = () => {
setCurrentlyEditingElement(null)
setPreviousValue(null)
let newValue: JsonData
switch (dataType) {
case 'object':
Expand All @@ -202,7 +205,12 @@ export const ValueNodeWrapper: React.FC<ValueNodeProps> = (props) => {

const handleCancel = () => {
setCurrentlyEditingElement(null)
if (previousValue !== null) {
onEdit(previousValue, path)
return
}
setValue(data)
setPreviousValue(null)
}

const handleDelete = () => {
Expand Down Expand Up @@ -322,7 +330,12 @@ export const ValueNodeWrapper: React.FC<ValueNodeProps> = (props) => {
showEditButtons && (
<EditButtons
startEdit={
canEdit ? () => setCurrentlyEditingElement(path, handleCancel) : undefined
canEdit
? () => {
setPreviousValue(value)
setCurrentlyEditingElement(path, handleCancel)
}
: undefined
}
handleDelete={canDelete ? handleDelete : undefined}
enableClipboard={enableClipboard}
Expand Down
13 changes: 12 additions & 1 deletion src/contexts/TreeStateProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
*/

import React, { createContext, useContext, useRef, useState } from 'react'
import { type TabDirection, type CollectionKey } from '../types'
import { type TabDirection, type CollectionKey, type JsonData } from '../types'
import { toPathString } from '../helpers'

interface CollapseAllState {
Expand Down Expand Up @@ -37,6 +37,8 @@ interface TreeStateContext {
setDragSource: (newState: DragSource) => void
tabDirection: TabDirection
setTabDirection: (dir: TabDirection) => void
previousValue: JsonData | null
setPreviousValue: (value: JsonData | null) => void
}
const initialContext: TreeStateContext = {
collapseState: null,
Expand All @@ -51,13 +53,20 @@ const initialContext: TreeStateContext = {
setDragSource: () => {},
tabDirection: 'next',
setTabDirection: () => {},
previousValue: null,
setPreviousValue: () => {},
}

const TreeStateProviderContext = createContext(initialContext)

export const TreeStateProvider = ({ children }: { children: React.ReactNode }) => {
const [collapseState, setCollapseState] = useState<CollapseAllState | null>(null)
const [currentlyEditingElement, setCurrentlyEditingElement] = useState<string | null>(null)

// This value holds the "previous" value when user changes type. Because
// changing data type causes a proper data update, cancelling afterwards
// doesn't revert to the previous type. This value allows us to do that.
const [previousValue, setPreviousValue] = useState<JsonData | null>(null)
const [dragSource, setDragSource] = useState<DragSource>({
path: null,
pathString: null,
Expand Down Expand Up @@ -130,6 +139,8 @@ export const TreeStateProvider = ({ children }: { children: React.ReactNode }) =
setTabDirection: (dir: TabDirection) => {
tabDirection.current = dir
},
previousValue,
setPreviousValue,
// Drag-n-drop
dragSource,
setDragSource,
Expand Down