Skip to content

Commit

Permalink
fix: state on nested array (#28)
Browse files Browse the repository at this point in the history
  • Loading branch information
himself65 authored Sep 19, 2022
1 parent 24f9167 commit 661151a
Show file tree
Hide file tree
Showing 7 changed files with 84 additions and 62 deletions.
3 changes: 3 additions & 0 deletions examples/basic/pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,12 @@ const loopArray = [

loopArray[1] = loopArray

const longArray = Array.from({ length: 1000 }).map((_, i) => i)

const example = {
loopObject,
loopArray,
longArray,
string: 'this is a string',
integer: 42,
array: [19, 19, 810, 'test', NaN],
Expand Down
58 changes: 30 additions & 28 deletions src/components/DataKeyPair.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,27 +18,29 @@ import { DataBox } from './mui/DataBox'

export type DataKeyPairProps = {
value: unknown
nested?: boolean
nestedIndex?: number
path: (string | number)[]
}

const IconBox = styled(props => <Box {...props} component='span' />)`
const IconBox = styled(props => <Box {...props} component='span'/>)`
cursor: pointer;
padding-left: 0.7rem;
` as typeof Box

export const DataKeyPair: React.FC<DataKeyPairProps> = (props) => {
const { value, path } = props
const { value, path, nestedIndex } = props
const [tempValue, setTempValue] = useState(value)
const depth = path.length
const key = path[depth - 1]
const hoverPath = useJsonViewerStore(store => store.hoverPath)
const isHover = useMemo(() => {
return hoverPath && path.every((value, index) => value === hoverPath[index])
}, [hoverPath, path])
return hoverPath && path.every(
(value, index) => value === hoverPath.path[index] && nestedIndex ===
hoverPath.nestedIndex)
}, [hoverPath, path, nestedIndex])
const setHover = useJsonViewerStore(store => store.setHover)
const root = useJsonViewerStore(store => store.value)
const [inspect, setInspect] = useInspect(path, value)
const [inspect, setInspect] = useInspect(path, value, nestedIndex)
const [editing, setEditing] = useState(false)
const onChange = useJsonViewerStore(store => store.onChange)
const keyColor = useTextColor()
Expand Down Expand Up @@ -106,18 +108,18 @@ export const DataKeyPair: React.FC<DataKeyPairProps> = (props) => {
{
copied
? (
<CheckIcon
sx={{
fontSize: '.8rem'
}}
/>
<CheckIcon
sx={{
fontSize: '.8rem'
}}
/>
)
: (
<ContentCopyIcon
sx={{
fontSize: '.8rem'
}}
/>
<ContentCopyIcon
sx={{
fontSize: '.8rem'
}}
/>
)
}
</IconBox>
Expand Down Expand Up @@ -146,9 +148,9 @@ export const DataKeyPair: React.FC<DataKeyPairProps> = (props) => {
const KeyRenderer = useJsonViewerStore(store => store.keyRenderer)
return (
<Box className='data-key-pair'
onMouseEnter={
useCallback(() => setHover(path), [setHover, path])
}
onMouseEnter={
useCallback(() => setHover(path, nestedIndex), [setHover, path, nestedIndex])
}
>
<DataBox
component='span'
Expand All @@ -171,15 +173,15 @@ export const DataKeyPair: React.FC<DataKeyPairProps> = (props) => {
{
KeyRenderer.when(downstreamProps)
? <KeyRenderer {...downstreamProps} />
: !props.nested && (
isNumberKey
? <Box component='span'
style={{ color: numberKeyColor }}>{displayKey}</Box>
: <>&quot;{displayKey}&quot;</>
)
: nestedIndex === undefined && (
isNumberKey
? <Box component='span'
style={{ color: numberKeyColor }}>{displayKey}</Box>
: <>&quot;{displayKey}&quot;</>
)
}
{
!props.nested && (
nestedIndex === undefined && (
<DataBox sx={{ mx: 0.5 }}>:</DataBox>
)
}
Expand All @@ -188,11 +190,11 @@ export const DataKeyPair: React.FC<DataKeyPairProps> = (props) => {
</DataBox>
{
editing
? (Editor && <Editor value={tempValue} setValue={setTempValue} />)
? (Editor && <Editor value={tempValue} setValue={setTempValue}/>)
: (Component)
? <Component {...downstreamProps} />
: <Box component='span'
className='data-value-fallback'>{`fallback: ${value}`}</Box>
className='data-value-fallback'>{`fallback: ${value}`}</Box>
}
{PostComponent && <PostComponent {...downstreamProps} />}
{(isHover && expandable && !inspect) && actionIcons}
Expand Down
2 changes: 1 addition & 1 deletion src/components/DataTypes/Object.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ export const ObjectType: React.FC<DataItemProps<object>> = (props) => {
return value.map((list, index) => {
const path = [...props.path]
return (
<DataKeyPair key={index} path={path} value={list} nested/>
<DataKeyPair key={index} path={path} value={list} nestedIndex={index}/>
)
})
} else {
Expand Down
36 changes: 18 additions & 18 deletions src/hooks/useInspect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,33 +11,33 @@ import {
} from '../stores/JsonViewerStore'
import { useIsCycleReference } from './useIsCycleReference'

export function useInspect (path: (string | number)[], value: any) {
export function useInspect (path: (string | number)[], value: any, nestedIndex?: number) {
const depth = path.length
const isTrap = useIsCycleReference(path, value)
const getInspectCache = useJsonViewerStore(store => store.getInspectCache)
const setInspectCache = useJsonViewerStore(store => store.setInspectCache)
const defaultInspectDepth = useJsonViewerStore(
store => store.defaultInspectDepth)
useEffect(() => {
const inspect = getInspectCache(path)
const inspect = getInspectCache(path, nestedIndex)
if (inspect === undefined) {
// do not inspect when it is a cycle reference, otherwise there will have a loop
const inspect = isTrap
? false
: depth < defaultInspectDepth
setInspectCache(path, inspect)
if (nestedIndex !== undefined) {
setInspectCache(path, false, nestedIndex)
} else {
// do not inspect when it is a cycle reference, otherwise there will have a loop
const inspect = isTrap
? false
: depth < defaultInspectDepth
setInspectCache(path, inspect)
}
}
}, [
defaultInspectDepth,
depth,
getInspectCache,
isTrap,
path,
setInspectCache
])
}, [defaultInspectDepth, depth, getInspectCache, isTrap, nestedIndex, path, setInspectCache])
const [inspect, set] = useState<boolean>(() => {
const shouldInspect = getInspectCache(path)
const shouldInspect = getInspectCache(path, nestedIndex)
if (shouldInspect === undefined) {
if (nestedIndex !== undefined) {
return false
}
return isTrap
? false
: depth < defaultInspectDepth
Expand All @@ -47,9 +47,9 @@ export function useInspect (path: (string | number)[], value: any) {
const setInspect = useCallback<Dispatch<SetStateAction<boolean>>>((apply) => {
set((oldState) => {
const newState = typeof apply === 'boolean' ? apply : apply(oldState)
setInspectCache(path, newState)
setInspectCache(path, newState, nestedIndex)
return newState
})
}, [path, setInspectCache])
}, [nestedIndex, path, setInspectCache])
return [inspect, setInspect] as const
}
38 changes: 27 additions & 11 deletions src/stores/JsonViewerStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import create from 'zustand'
import createContext from 'zustand/context'
import { combine } from 'zustand/middleware'

import type { JsonViewerOnChange } from '..'
import type { JsonViewerOnChange, Path } from '..'
import type { ColorNamespace } from '../theme/base16'
import { lightColorNamespace } from '../theme/base16'
import type { JsonViewerKeyRenderer } from '../type'
Expand All @@ -13,7 +13,7 @@ DefaultKeyRenderer.when = () => false

export type JsonViewerState = {
inspectCache: Record<string, boolean>
hoverPath: (string | number)[] | null
hoverPath: { path: Path; nestedIndex?: number } | null
groupArraysAfterLength: number
defaultInspectDepth: number
colorNamespace: ColorNamespace
Expand All @@ -25,9 +25,10 @@ export type JsonViewerState = {
}

export type JsonViewerActions = {
getInspectCache: (path: (string | number)[]) => boolean
setInspectCache: (path: (string | number)[], action: SetStateAction<boolean>) => void
setHover: (path: (string | number)[] | null) => void
getInspectCache: (path: Path, nestedIndex?: number) => boolean
setInspectCache: (
path: Path, action: SetStateAction<boolean>, nestedIndex?: number) => void
setHover: (path: Path | null, nestedIndex?: number) => void
}

export const createJsonViewerStore = () =>
Expand All @@ -46,21 +47,36 @@ export const createJsonViewerStore = () =>
keyRenderer: DefaultKeyRenderer
},
(set, get) => ({
getInspectCache: (path) => {
return get().inspectCache[path.join('.')]
getInspectCache: (path, nestedIndex) => {
const target = nestedIndex !== undefined
? path.join('.') +
`[${nestedIndex}]nt`
: path.join('.')
return get().inspectCache[target]
},
setInspectCache: (path, action) => {
const target = path.join('.')
setInspectCache: (path, action, nestedIndex) => {
const target = nestedIndex !== undefined
? path.join('.') +
`[${nestedIndex}]nt`
: path.join('.')
set(state => ({
inspectCache: {
...state.inspectCache,
[target]: typeof action === 'function' ? action(state.inspectCache[target]) : action
[target]: typeof action === 'function'
? action(
state.inspectCache[target])
: action
}
}))
},
setHover: (path) => {
setHover: (path, nestedIndex) => {
set({
hoverPath: path
? ({
path,
nestedIndex
})
: null
})
}
})
Expand Down
7 changes: 4 additions & 3 deletions src/stores/typeRegistry.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,10 @@ export function matchTypeComponents<Value> (value: Value): DataType<Value> {
for (const T of typeRegistry) {
if (T.is(value)) {
potential = T
}
if (typeof value === 'object' && value === null) {
return T
if (typeof value === 'object') {
// early return for case like `null`
return T
}
}
}
if (potential === undefined) {
Expand Down
2 changes: 1 addition & 1 deletion src/type.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { Dispatch, SetStateAction } from 'react'
import type React from 'react'

type Path = (string | number)[]
export type Path = (string | number)[]

export interface DataItemProps<ValueType = unknown> {
inspect: boolean
Expand Down

0 comments on commit 661151a

Please sign in to comment.