From 4f3409a77b89ecfe8ee3e5522263aed0e1470b23 Mon Sep 17 00:00:00 2001 From: ooithianhooi Date: Mon, 16 Dec 2024 23:50:51 +0800 Subject: [PATCH] feat: return parent paths on edit --- src/components/json-node.tsx | 28 ++++++---- src/components/json-view.tsx | 85 +++++++++++++++++------------ src/components/large-array-node.tsx | 20 +++---- src/components/large-array.tsx | 36 ++++++++---- src/components/name-value.tsx | 17 ++++-- src/components/object-node.tsx | 38 ++++++++----- src/stories/editable.stories.tsx | 21 ++++--- website/src/contents/editable.tsx | 5 +- 8 files changed, 151 insertions(+), 99 deletions(-) diff --git a/src/components/json-node.tsx b/src/components/json-node.tsx index 0095ca9..92942f9 100644 --- a/src/components/json-node.tsx +++ b/src/components/json-node.tsx @@ -26,13 +26,14 @@ import type { CustomizeNode, CustomizeOptions } from '../types' interface Props { node: any depth: number - deleteHandle?: (indexOrName: string | number) => void - editHandle?: (indexOrName: string | number, newValue: any, oldValue: any) => void + deleteHandle?: (indexOrName: string | number, parentPath: string[]) => void + editHandle?: (indexOrName: string | number, newValue: any, oldValue: any, parentPath: string[]) => void indexOrName?: number | string parent?: Record | Array + parentPath: string[] } -export default function JsonNode({ node, depth, deleteHandle: _deleteHandle, indexOrName, parent, editHandle }: Props) { +export default function JsonNode({ node, depth, deleteHandle: _deleteHandle, indexOrName, parent, editHandle, parentPath }: Props) { // prettier-ignore const { collapseStringsAfterLength, enableClipboard, editable, src, onDelete, onChange, customizeNode, matchesURL, urlRegExp, EditComponent, DoneComponent, CancelComponent, CustomOperation } = useContext(JsonViewContext) @@ -54,6 +55,7 @@ export default function JsonNode({ node, depth, deleteHandle: _deleteHandle, ind depth={depth} indexOrName={indexOrName} deleteHandle={_deleteHandle} + parentPath={parentPath} customOptions={typeof customReturn === 'object' ? (customReturn as CustomizeOptions) : undefined} /> ) @@ -78,28 +80,29 @@ export default function JsonNode({ node, depth, deleteHandle: _deleteHandle, ind try { const parsedValue = JSON.parse(newValue) - if (editHandle) editHandle(indexOrName!, parsedValue, node) + if (editHandle) editHandle(indexOrName!, parsedValue, node, parentPath) } catch (e) { const trimmedStringValue = resolveEvalFailedNewValue(type, newValue) - if (editHandle) editHandle(indexOrName!, trimmedStringValue, node) + if (editHandle) editHandle(indexOrName!, trimmedStringValue, node, parentPath) } setEditing(false) - }, [editHandle]) + }, [editHandle, indexOrName, node, parentPath, type]) const cancel = () => { setEditing(false) setDeleting(false) } const deleteHandle = () => { setDeleting(false) - if (_deleteHandle) _deleteHandle(indexOrName!) + if (_deleteHandle) _deleteHandle(indexOrName!, parentPath) if (onDelete) onDelete({ value: node, depth, src, indexOrName: indexOrName!, - parentType: Array.isArray(parent) ? 'array' : 'object' + parentType: Array.isArray(parent) ? 'array' : 'object', + parentPath }) if (onChange) onChange({ @@ -107,7 +110,8 @@ export default function JsonNode({ node, depth, deleteHandle: _deleteHandle, ind src, indexOrName: indexOrName!, parentType: Array.isArray(parent) ? 'array' : 'object', - type: 'delete' + type: 'delete', + parentPath }) } @@ -164,12 +168,12 @@ export default function JsonNode({ node, depth, deleteHandle: _deleteHandle, ind {!isEditing && editableDelete(editable) && customDelete(customReturn as CustomizeOptions | undefined) && _deleteHandle && ( setDeleting(true)} /> )} - { typeof CustomOperation === 'function' ? : null } + {typeof CustomOperation === 'function' ? : null} ) let className = 'json-view--string' - + switch (type) { case 'number': case 'bigint': @@ -182,7 +186,7 @@ export default function JsonNode({ node, depth, deleteHandle: _deleteHandle, ind className = 'json-view--null' break } - + if (typeof (customReturn as CustomizeOptions)?.className === 'string') className += ' ' + (customReturn as CustomizeOptions).className if (deleting) className += ' json-view--deleting' diff --git a/src/components/json-view.tsx b/src/components/json-view.tsx index 32e8555..4e8ae69 100644 --- a/src/components/json-view.tsx +++ b/src/components/json-view.tsx @@ -3,15 +3,31 @@ import JsonNode from './json-node' import type { Collapsed, CustomizeCollapseStringUI, CustomizeNode, DisplaySize, Editable } from '../types' import { stringifyForCopying } from '../utils' -type OnEdit = (params: { newValue: any; oldValue: any; depth: number; src: any; indexOrName: string | number; parentType: 'object' | 'array' | null }) => void -type OnDelete = (params: { value: any; indexOrName: string | number; depth: number; src: any; parentType: 'object' | 'array' | null }) => void -type OnAdd = (params: { indexOrName: string | number; depth: number; src: any; parentType: 'object' | 'array' }) => void +type OnEdit = (params: { + newValue: any + oldValue: any + depth: number + src: any + indexOrName: string | number + parentType: 'object' | 'array' | null + parentPath: string[] +}) => void +type OnDelete = (params: { + value: any + indexOrName: string | number + depth: number + src: any + parentType: 'object' | 'array' | null + parentPath: string[] +}) => void +type OnAdd = (params: { indexOrName: string | number; depth: number; src: any; parentType: 'object' | 'array'; parentPath: string[] }) => void type OnChange = (params: { indexOrName: string | number depth: number src: any parentType: 'object' | 'array' | null type: 'add' | 'edit' | 'delete' + parentPath: string[] }) => void type OnCollapse = (params: { isCollapsing: boolean; node: Record | Array; indexOrName: string | number | undefined; depth: number }) => void @@ -57,21 +73,18 @@ export const JsonViewContext = createContext({ | React.Component<{ className: string; style: React.CSSProperties }> | undefined, EditComponent: undefined as - | React.FC<{ onClick: (event: React.MouseEvent) => void; className: string }> - | React.Component<{ onClick: (event: React.MouseEvent) => void; className: string }> - | undefined, + | React.FC<{ onClick: (event: React.MouseEvent) => void; className: string }> + | React.Component<{ onClick: (event: React.MouseEvent) => void; className: string }> + | undefined, CancelComponent: undefined as - | React.FC<{ onClick: (event: React.MouseEvent) => void; className: string; style: React.CSSProperties }> - | React.Component<{ onClick: (event: React.MouseEvent) => void; className: string; style: React.CSSProperties }> - | undefined, + | React.FC<{ onClick: (event: React.MouseEvent) => void; className: string; style: React.CSSProperties }> + | React.Component<{ onClick: (event: React.MouseEvent) => void; className: string; style: React.CSSProperties }> + | undefined, DoneComponent: undefined as - | React.FC<{ onClick: (event: React.MouseEvent) => void; className: string; style: React.CSSProperties }> - | React.Component<{ onClick: (event: React.MouseEvent) => void; className: string; style: React.CSSProperties }> - | undefined, - CustomOperation: undefined as - | React.FC<{ node: any }> - | React.Component<{ node: any }> - | undefined, + | React.FC<{ onClick: (event: React.MouseEvent) => void; className: string; style: React.CSSProperties }> + | React.Component<{ onClick: (event: React.MouseEvent) => void; className: string; style: React.CSSProperties }> + | undefined, + CustomOperation: undefined as React.FC<{ node: any }> | React.Component<{ node: any }> | undefined }) export interface JsonViewProps { @@ -116,20 +129,18 @@ export interface JsonViewProps { CopiedComponent?: React.FC<{ className: string; style: React.CSSProperties }> | React.Component<{ className: string; style: React.CSSProperties }> EditComponent?: - | React.FC<{ onClick: (event: React.MouseEvent) => void; className: string }> - | React.Component<{ onClick: (event: React.MouseEvent) => void; className: string }> + | React.FC<{ onClick: (event: React.MouseEvent) => void; className: string }> + | React.Component<{ onClick: (event: React.MouseEvent) => void; className: string }> CancelComponent?: - | React.FC<{ onClick: (event: React.MouseEvent) => void; className: string; style: React.CSSProperties }> - | React.Component<{ onClick: (event: React.MouseEvent) => void; className: string; style: React.CSSProperties }> + | React.FC<{ onClick: (event: React.MouseEvent) => void; className: string; style: React.CSSProperties }> + | React.Component<{ onClick: (event: React.MouseEvent) => void; className: string; style: React.CSSProperties }> DoneComponent?: - | React.FC<{ onClick: (event: React.MouseEvent) => void; className: string; style: React.CSSProperties }> - | React.Component<{ onClick: (event: React.MouseEvent) => void; className: string; style: React.CSSProperties }> + | React.FC<{ onClick: (event: React.MouseEvent) => void; className: string; style: React.CSSProperties }> + | React.Component<{ onClick: (event: React.MouseEvent) => void; className: string; style: React.CSSProperties }> - CustomOperation?: - | React.FC<{ node: any }> - | React.Component<{ node: any }> + CustomOperation?: React.FC<{ node: any }> | React.Component<{ node: any }> } export default function JsonView({ @@ -174,7 +185,7 @@ export default function JsonView({ EditComponent, CancelComponent, DoneComponent, - CustomOperation, + CustomOperation }: JsonViewProps) { const [_, update] = useState(0) const forceUpdate = useCallback(() => update(state => ++state), []) @@ -219,7 +230,7 @@ export default function JsonView({ EditComponent, CancelComponent, DoneComponent, - CustomOperation, + CustomOperation }}> { + editHandle={(indexOrName: number | string, newValue: any, oldValue: any, parentPath: string[]) => { setSrc(newValue) if (onEdit) onEdit({ @@ -236,29 +247,33 @@ export default function JsonView({ depth: 1, src, indexOrName: indexOrName, - parentType: null + parentType: null, + parentPath: parentPath }) - if (onChange) onChange({ type: 'edit', depth: 1, src, indexOrName: indexOrName, parentType: null }) + if (onChange) onChange({ type: 'edit', depth: 1, src, indexOrName: indexOrName, parentType: null, parentPath: parentPath }) }} - deleteHandle={() => { + deleteHandle={(indexOrName: number | string, parentPath: string[]) => { setSrc(undefined) if (onDelete) onDelete({ value: src, depth: 1, src, - indexOrName: '', - parentType: null + indexOrName: indexOrName, + parentType: null, + parentPath: parentPath }) if (onChange) onChange({ depth: 1, src, - indexOrName: '', + indexOrName: indexOrName, parentType: null, - type: 'delete' + type: 'delete', + parentPath: parentPath }) }} + parentPath={[]} /> diff --git a/src/components/large-array-node.tsx b/src/components/large-array-node.tsx index eedb8f3..b9fd7bf 100644 --- a/src/components/large-array-node.tsx +++ b/src/components/large-array-node.tsx @@ -11,12 +11,13 @@ interface Props { node: Array depth: number index: number - deleteHandle?: (_: string | number) => void + deleteHandle?: (_: string | number, currentPath: string[]) => void customOptions?: CustomizeOptions startIndex: number + parentPath: string[] } -export default function LargeArrayNode({ originNode, node, depth, index, deleteHandle: _deleteSelf, customOptions, startIndex }: Props) { +export default function LargeArrayNode({ originNode, node, depth, index, deleteHandle: _deleteSelf, customOptions, startIndex, parentPath }: Props) { const { enableClipboard, src, onEdit, onChange, forceUpdate, displaySize, CustomOperation } = useContext(JsonViewContext) const [fold, setFold] = useState(true) @@ -32,9 +33,10 @@ export default function LargeArrayNode({ originNode, node, depth, index, deleteH depth, src, indexOrName, - parentType: 'array' + parentType: 'array', + parentPath }) - if (onChange) onChange({ type: 'edit', depth, src, indexOrName, parentType: 'array' }) + if (onChange) onChange({ type: 'edit', depth, src, indexOrName, parentType: 'array', parentPath }) forceUpdate() }, [node, onEdit, onChange, forceUpdate] @@ -43,6 +45,7 @@ export default function LargeArrayNode({ originNode, node, depth, index, deleteH // Delete property const deleteHandle = (index: number | string) => { originNode.splice(index as number, 1) + if (_deleteSelf) _deleteSelf(index, parentPath) forceUpdate() } @@ -57,7 +60,7 @@ export default function LargeArrayNode({ originNode, node, depth, index, deleteH )} {!fold && enableClipboard && customCopy(customOptions) && } - { typeof CustomOperation === 'function' ? : null } + {typeof CustomOperation === 'function' ? : null} ) @@ -78,6 +81,7 @@ export default function LargeArrayNode({ originNode, node, depth, index, deleteH parent={node} deleteHandle={deleteHandle} editHandle={editHandle} + parentPath={parentPath} /> ))} @@ -88,12 +92,6 @@ export default function LargeArrayNode({ originNode, node, depth, index, deleteH )} {']'} - - {/* {fold && ifDisplay(displaySize, depth, fold) && ( - setFold(false)} className='jv-size'> - {objectSize(node)} Items - - )} */} ) } diff --git a/src/components/large-array.tsx b/src/components/large-array.tsx index cc409f7..ae31a95 100644 --- a/src/components/large-array.tsx +++ b/src/components/large-array.tsx @@ -1,24 +1,25 @@ import { useContext, useEffect, useState } from 'react' -import LargeArrayNode from './large-array-node' import { JsonViewContext } from './json-view' -import { CustomizeOptions } from '../types' -import { customAdd, customCopy, customDelete, editableAdd, editableDelete, ifDisplay, isCollapsed, isCollapsed_largeArray } from '../utils' +import { isCollapsed_largeArray, ifDisplay, editableAdd, editableDelete, customAdd, customCopy, customDelete } from '../utils' import { ReactComponent as AngleDownSVG } from '../svgs/angle-down.svg' import CopyButton from './copy-button' import { ReactComponent as DeleteSVG } from '../svgs/trash.svg' import { ReactComponent as AddSVG } from '../svgs/add-square.svg' import { ReactComponent as DoneSVG } from '../svgs/done.svg' import { ReactComponent as CancelSVG } from '../svgs/cancel.svg' +import type { CustomizeOptions } from '../types' +import LargeArrayNode from './large-array-node' interface Props { node: Array depth: number indexOrName?: number | string - deleteHandle?: (_: string | number) => void + deleteHandle?: (_: string | number, currentPath: string[]) => void customOptions?: CustomizeOptions + parentPath: string[] } -export default function LargeArray({ node, depth, deleteHandle: _deleteSelf, indexOrName, customOptions }: Props) { +export default function LargeArray({ node, depth, deleteHandle: _deleteSelf, indexOrName, customOptions, parentPath }: Props) { const nestCollapsedArray: any[] = [] for (let i = 0; i < node.length; i += 100) { nestCollapsedArray.push(node.slice(i, i + 100)) @@ -36,15 +37,16 @@ export default function LargeArray({ node, depth, deleteHandle: _deleteSelf, ind const [deleting, setDeleting] = useState(false) const deleteSelf = () => { setDeleting(false) - if (_deleteSelf) _deleteSelf(indexOrName!) - if (onDelete) onDelete({ value: node, depth, src, indexOrName: indexOrName!, parentType: 'array' }) + if (_deleteSelf) _deleteSelf(indexOrName!, parentPath) + if (onDelete) onDelete({ value: node, depth, src, indexOrName: indexOrName!, parentType: 'array', parentPath }) if (onChange) onChange({ type: 'delete', depth, src, indexOrName: indexOrName!, - parentType: 'array' + parentType: 'array', + parentPath }) } @@ -53,8 +55,8 @@ export default function LargeArray({ node, depth, deleteHandle: _deleteSelf, ind const add = () => { const arr = node as unknown as any[] arr.push(null) - if (onAdd) onAdd({ indexOrName: arr.length - 1, depth, src, parentType: 'array' }) - if (onChange) onChange({ type: 'add', indexOrName: arr.length - 1, depth, src, parentType: 'array' }) + if (onAdd) onAdd({ indexOrName: arr.length - 1, depth, src, parentType: 'array', parentPath }) + if (onChange) onChange({ type: 'add', indexOrName: arr.length - 1, depth, src, parentType: 'array', parentPath }) forceUpdate() } @@ -89,7 +91,7 @@ export default function LargeArray({ node, depth, deleteHandle: _deleteSelf, ind {!fold && !isEditing && editableDelete(editable) && customDelete(customOptions) && _deleteSelf && ( setDeleting(true)} /> )} - { typeof CustomOperation === 'function' ? : null } + {typeof CustomOperation === 'function' ? : null} ) @@ -102,7 +104,17 @@ export default function LargeArray({ node, depth, deleteHandle: _deleteSelf, ind {!fold ? (
{nestCollapsedArray.map((item, index) => ( - + ))}
) : ( diff --git a/src/components/name-value.tsx b/src/components/name-value.tsx index 1ee7c5d..50edb55 100644 --- a/src/components/name-value.tsx +++ b/src/components/name-value.tsx @@ -8,11 +8,12 @@ interface Props { value: any depth: number parent?: Record | Array - deleteHandle: (indexOrName: string | number) => void - editHandle: (indexOrName: string | number, newValue: any, oldValue: any) => void + parentPath: string[] + deleteHandle: (indexOrName: string | number, parentPath: string[]) => void + editHandle: (indexOrName: string | number, newValue: any, oldValue: any, parentPath: string[]) => void } -export default function NameValue({ indexOrName, value, depth, parent, deleteHandle, editHandle }: Props) { +export default function NameValue({ indexOrName, value, depth, parent, deleteHandle, editHandle, parentPath }: Props) { const { displayArrayIndex } = useContext(JsonViewContext) const isArray = Array.isArray(parent) @@ -25,7 +26,15 @@ export default function NameValue({ indexOrName, value, depth, parent, deleteHan ) : ( <> )} - + deleteHandle(indexOrName, parentPath)} + editHandle={(indexOrName, newValue, oldValue, parentPath) => editHandle(indexOrName, newValue, oldValue, parentPath)} + parent={parent} + indexOrName={indexOrName} + parentPath={parentPath} + /> ) } diff --git a/src/components/object-node.tsx b/src/components/object-node.tsx index 30c1d63..395cebe 100644 --- a/src/components/object-node.tsx +++ b/src/components/object-node.tsx @@ -15,11 +15,12 @@ interface Props { node: Record | Array depth: number indexOrName?: number | string - deleteHandle?: (_: string | number) => void + deleteHandle?: (_: string | number, currentPath: string[]) => void customOptions?: CustomizeOptions + parentPath: string[] } -export default function ObjectNode({ node, depth, indexOrName, deleteHandle: _deleteSelf, customOptions }: Props) { +export default function ObjectNode({ node, depth, indexOrName, deleteHandle: _deleteSelf, customOptions, parentPath }: Props) { const { collapsed, onCollapse, @@ -34,11 +35,13 @@ export default function ObjectNode({ node, depth, indexOrName, deleteHandle: _de onChange, forceUpdate, displaySize, - CustomOperation, + CustomOperation } = useContext(JsonViewContext) + const currentPath = [...parentPath, indexOrName ? String(indexOrName) : ''].filter(Boolean) + if (!ignoreLargeArray && Array.isArray(node) && node.length > 100) { - return + return } const isPlainObject = isObject(node) @@ -69,9 +72,10 @@ export default function ObjectNode({ node, depth, indexOrName, deleteHandle: _de depth, src, indexOrName: indexOrName, - parentType: isPlainObject ? 'object' : 'array' + parentType: isPlainObject ? 'object' : 'array', + parentPath: currentPath }) - if (onChange) onChange({ type: 'edit', depth, src, indexOrName: indexOrName, parentType: isPlainObject ? 'object' : 'array' }) + if (onChange) onChange({ type: 'edit', depth, src, indexOrName: indexOrName, parentType: isPlainObject ? 'object' : 'array', parentPath: currentPath }) forceUpdate() }, [node, onEdit, onChange, forceUpdate] @@ -91,15 +95,16 @@ export default function ObjectNode({ node, depth, indexOrName, deleteHandle: _de const [deleting, setDeleting] = useState(false) const deleteSelf = () => { setDeleting(false) - if (_deleteSelf) _deleteSelf(indexOrName!) - if (onDelete) onDelete({ value: node, depth, src, indexOrName: indexOrName!, parentType: isPlainObject ? 'object' : 'array' }) + if (_deleteSelf) _deleteSelf(indexOrName!, currentPath) + if (onDelete) onDelete({ value: node, depth, src, indexOrName: indexOrName!, parentType: isPlainObject ? 'object' : 'array', parentPath: currentPath }) if (onChange) onChange({ type: 'delete', depth, src, indexOrName: indexOrName!, - parentType: isPlainObject ? 'object' : 'array' + parentType: isPlainObject ? 'object' : 'array', + parentPath: currentPath }) } @@ -116,14 +121,14 @@ export default function ObjectNode({ node, depth, indexOrName, deleteHandle: _de if (inputRef.current) inputRef.current.value = '' setAdding(false) - if (onAdd) onAdd({ indexOrName: inputName, depth, src, parentType: 'object' }) - if (onChange) onChange({ type: 'add', indexOrName: inputName, depth, src, parentType: 'object' }) + if (onAdd) onAdd({ indexOrName: inputName, depth, src, parentType: 'object', parentPath: currentPath }) + if (onChange) onChange({ type: 'add', indexOrName: inputName, depth, src, parentType: 'object', parentPath: currentPath }) } } else if (Array.isArray(node)) { const arr = node as unknown as any[] arr.push(null) - if (onAdd) onAdd({ indexOrName: arr.length - 1, depth, src, parentType: 'array' }) - if (onChange) onChange({ type: 'add', indexOrName: arr.length - 1, depth, src, parentType: 'array' }) + if (onAdd) onAdd({ indexOrName: arr.length - 1, depth, src, parentType: 'array', parentPath: currentPath }) + if (onChange) onChange({ type: 'add', indexOrName: arr.length - 1, depth, src, parentType: 'array', parentPath: currentPath }) } forceUpdate() } @@ -174,7 +179,7 @@ export default function ObjectNode({ node, depth, indexOrName, deleteHandle: _de {!fold && !isEditing && editableDelete(editable) && customDelete(customOptions) && _deleteSelf && ( setDeleting(true)} /> )} - { typeof CustomOperation === 'function' ? : null } + {typeof CustomOperation === 'function' ? : null} ) @@ -196,6 +201,7 @@ export default function ObjectNode({ node, depth, indexOrName, deleteHandle: _de parent={node} deleteHandle={deleteHandle} editHandle={editHandle} + parentPath={currentPath} /> ))} @@ -232,6 +238,7 @@ export default function ObjectNode({ node, depth, indexOrName, deleteHandle: _de parent={node} deleteHandle={deleteHandle} editHandle={editHandle} + parentPath={currentPath} /> ))} @@ -250,6 +257,7 @@ export default function ObjectNode({ node, depth, indexOrName, deleteHandle: _de )} ) + } else { + return {String(node)} } - return null } diff --git a/src/stories/editable.stories.tsx b/src/stories/editable.stories.tsx index f42e7b7..3a438e2 100644 --- a/src/stories/editable.stories.tsx +++ b/src/stories/editable.stories.tsx @@ -27,21 +27,24 @@ export default { obj: { k1: 123, k2: '123', - k3: false + k3: false, + nested: { + k4: 'nested' + } }, arr: ['string', 123456, false, null] }, - onEdit: ({ newValue, src, oldValue, indexOrName }) => { - console.log('[onEdit]', indexOrName, newValue, oldValue, src) + onEdit: ({ newValue, src, oldValue, indexOrName, parentPath }) => { + console.log('[onEdit]', indexOrName, newValue, oldValue, src, parentPath) }, - onDelete: ({ value, src, indexOrName }) => { - console.log('[onDelete]', indexOrName, value, src) + onDelete: ({ value, src, indexOrName, parentPath }) => { + console.log('[onDelete]', indexOrName, value, src, parentPath) }, - onAdd: ({ src, indexOrName }) => { - console.log('[onAdd]', indexOrName, src) + onAdd: ({ src, indexOrName, parentPath }) => { + console.log('[onAdd]', indexOrName, src, parentPath) }, - onChange: ({ src, indexOrName }) => { - console.log('[onChange]', indexOrName, src) + onChange: ({ src, indexOrName, parentPath }) => { + console.log('[onChange]', indexOrName, src, parentPath) } }, decorators: [ diff --git a/website/src/contents/editable.tsx b/website/src/contents/editable.tsx index c7ba05c..b9e426e 100644 --- a/website/src/contents/editable.tsx +++ b/website/src/contents/editable.tsx @@ -69,7 +69,10 @@ export default function Editable() { obj: { k1: 123, k2: '123', - k3: false + k3: false, + nested: { + k4: 'nested' + } }, arr: ['string', 123456, false, null], largeArr: new Array(Math.trunc(Math.random() * 1000) + 800).fill((Math.random() * 10).toFixed(2))