Skip to content
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

feat: support props.maxDisplayLength #30

Merged
merged 3 commits into from
Sep 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 56 additions & 9 deletions src/components/DataTypes/Object.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { Box } from '@mui/material'
import React, { useMemo } from 'react'
import React, { useMemo, useState } from 'react'

import { useTextColor } from '../../hooks/useColor'
import { useIsCycleReference } from '../../hooks/useIsCycleReference'
import { useJsonViewerStore } from '../../stores/JsonViewerStore'
import type { DataItemProps } from '../../type'
import { DataKeyPair } from '../DataKeyPair'
import { CircularArrowsIcon } from '../icons/CircularArrowsIcon'
import { DataBox } from '../mui/DataBox'

const objectLb = '{'
const arrayLb = '['
Expand Down Expand Up @@ -85,20 +86,44 @@ export const ObjectType: React.FC<DataItemProps<object>> = (props) => {
const groupArraysAfterLength = useJsonViewerStore(
store => store.groupArraysAfterLength)
const isTrap = useIsCycleReference(props.path, props.value)
const [displayLength, setDisplayLength] = useState(
useJsonViewerStore(store => store.maxDisplayLength)
)
const elements = useMemo(() => {
if (!props.inspect) {
return null
}
if (Array.isArray(props.value)) {
if (props.value.length <= groupArraysAfterLength) {
return props.value.map((value, index) => {
const value: unknown[] | object = props.value
if (Array.isArray(value)) {
// unknown[]
if (value.length <= groupArraysAfterLength) {
const elements = value.slice(0, displayLength).map((value, index) => {
const path = [...props.path, index]
return (
<DataKeyPair key={index} path={path} value={value}/>
)
})
if (value.length > displayLength) {
const rest = value.length - displayLength
elements.push(
<DataBox
sx={{
cursor: 'pointer',
lineHeight: 1.5,
color: keyColor,
letterSpacing: 0.5,
opacity: 0.8
}}
key='last'
onClick={() => setDisplayLength(length => length * 2)}
>
hidden {rest} items...
</DataBox>
)
}
return elements
}
const value = props.value.reduce<unknown[][]>((array, value, index) => {
const elements = value.reduce<unknown[][]>((array, value, index) => {
const target = Math.floor(index / groupArraysAfterLength)
if (array[target]) {
array[target].push(value)
Expand All @@ -108,21 +133,43 @@ export const ObjectType: React.FC<DataItemProps<object>> = (props) => {
return array
}, [])

return value.map((list, index) => {
return elements.map((list, index) => {
const path = [...props.path]
return (
<DataKeyPair key={index} path={path} value={list} nestedIndex={index}/>
<DataKeyPair key={index} path={path} value={list}
nestedIndex={index}/>
)
})
} else {
return Object.entries(props.value).map(([key, value]) => {
// object
const entries = Object.entries(value)
const elements = entries.slice(0, displayLength).map(([key, value]) => {
const path = [...props.path, key]
return (
<DataKeyPair key={key} path={path} value={value}/>
)
})
if (entries.length > displayLength) {
const rest = entries.length - displayLength
elements.push(
<DataBox
sx={{
cursor: 'pointer',
lineHeight: 1.5,
color: keyColor,
letterSpacing: 0.5,
opacity: 0.8
}}
key='last'
onClick={() => setDisplayLength(length => length * 2)}
>
hidden {rest} items...
</DataBox>
)
}
return elements
}
}, [props.inspect, props.value, props.path, groupArraysAfterLength])
}, [props.inspect, props.value, props.path, groupArraysAfterLength, displayLength, keyColor])
return (
<Box
className='data-object'
Expand Down
26 changes: 7 additions & 19 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,6 @@ import type React from 'react'
import { useCallback, useEffect, useMemo, useRef } from 'react'

import { DataKeyPair } from './components/DataKeyPair'
import {
ObjectType,
PostObjectType,
PreObjectType
} from './components/DataTypes/Object'
import { useThemeDetector } from './hooks/useThemeDetector'
import {
createJsonViewerStore,
Expand Down Expand Up @@ -46,18 +41,19 @@ const JsonViewerInner: React.FC<JsonViewerProps> = (props) => {
}, [props.value, setIfNotUndefined])
useEffect(() => {
// setIfNotUndefined('indentWidth', props.indentWidth)
setIfNotUndefined('defaultInspectDepth', props.defaultInspectDepth)
setIfNotUndefined('onChange', props.onChange)
setIfNotUndefined('groupArraysAfterLength', props.groupArraysAfterLength)
setIfNotUndefined('keyRenderer', props.keyRenderer)
setIfNotUndefined('maxDisplayLength', props.maxDisplayLength)
}, [
api,
props.defaultInspectDepth,
props.groupArraysAfterLength,
props.keyRenderer,
props.onChange,
props.value,
setIfNotUndefined])
props.maxDisplayLength,
setIfNotUndefined
])

useEffect(() => {
if (props.theme === 'light') {
Expand All @@ -79,7 +75,8 @@ const JsonViewerInner: React.FC<JsonViewerProps> = (props) => {
style={props.style}
sx={{
fontFamily: 'monospace',
userSelect: 'none'
userSelect: 'none',
contentVisibility: 'auto'
}}
onMouseLeave={
useCallback(() => {
Expand Down Expand Up @@ -116,21 +113,12 @@ export const JsonViewer = function JsonViewer<Value> (props: JsonViewerProps<Val
props.valueTypes?.forEach(type => {
registerType(type)
})
// Always last one, fallback for all data like 'object'
registerType<object>(
{
is: (value): value is object => typeof value === 'object',
Component: ObjectType,
PreComponent: PreObjectType,
PostComponent: PostObjectType
}
)
onceRef.current = false
}
const mixedProps = { ...props, theme: themeType }
return (
<ThemeProvider theme={theme}>
<JsonViewerProvider createStore={createJsonViewerStore}>
<JsonViewerProvider createStore={() => createJsonViewerStore(props)}>
<JsonViewerInner {...mixedProps}/>
</JsonViewerProvider>
</ThemeProvider>
Expand Down
22 changes: 12 additions & 10 deletions src/stores/JsonViewerStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,24 @@ import create from 'zustand'
import createContext from 'zustand/context'
import { combine } from 'zustand/middleware'

import type { JsonViewerOnChange, Path } from '..'
import type { JsonViewerOnChange, JsonViewerProps, Path } from '..'
import type { ColorNamespace } from '../theme/base16'
import { lightColorNamespace } from '../theme/base16'
import type { JsonViewerKeyRenderer } from '../type'

const DefaultKeyRenderer: JsonViewerKeyRenderer = () => null
DefaultKeyRenderer.when = () => false

export type JsonViewerState = {
export type JsonViewerState<T = unknown> = {
inspectCache: Record<string, boolean>
hoverPath: { path: Path; nestedIndex?: number } | null
groupArraysAfterLength: number
maxDisplayLength: number
defaultInspectDepth: number
colorNamespace: ColorNamespace
expanded: string[]
rootName: string
value: unknown
value: T
onChange: JsonViewerOnChange
keyRenderer: JsonViewerKeyRenderer
}
Expand All @@ -31,20 +32,21 @@ export type JsonViewerActions = {
setHover: (path: Path | null, nestedIndex?: number) => void
}

export const createJsonViewerStore = () =>
export const createJsonViewerStore = <T = unknown>(props: JsonViewerProps<T>) =>
create(
combine<JsonViewerState, JsonViewerActions>(
combine<JsonViewerState<T>, JsonViewerActions>(
{
inspectCache: {},
hoverPath: null,
groupArraysAfterLength: 100,
groupArraysAfterLength: props.groupArraysAfterLength ?? 100,
maxDisplayLength: props.maxDisplayLength ?? 30,
rootName: 'root',
defaultInspectDepth: 10,
defaultInspectDepth: 5,
colorNamespace: lightColorNamespace,
expanded: ['data-viewer-root'],
value: {},
onChange: () => {},
keyRenderer: DefaultKeyRenderer
value: props.value,
onChange: props.onChange ?? (() => {}),
keyRenderer: props.keyRenderer ?? DefaultKeyRenderer
},
(set, get) => ({
getInspectCache: (path, nestedIndex) => {
Expand Down
25 changes: 21 additions & 4 deletions src/stores/typeRegistry.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,15 @@ import React, { useMemo } from 'react'

import { createEasyType } from '../components/DataTypes/createEasyType'
import {
FunctionType, PostFunctionType,
FunctionType,
PostFunctionType,
PreFunctionType
} from '../components/DataTypes/Function'
import {
ObjectType,
PostObjectType,
PreObjectType
} from '../components/DataTypes/Object'
import type { DataType } from '../type'
import { useJsonViewerStore } from './JsonViewerStore'

Expand All @@ -16,6 +22,13 @@ export function registerType<Value> (type: DataType<Value>) {
typeRegistry.push(type)
}

const objectType: DataType<object> = {
is: (value): value is object => typeof value === 'object',
Component: ObjectType,
PreComponent: PreObjectType,
PostComponent: PostObjectType
}

export function matchTypeComponents<Value> (value: Value): DataType<Value> {
let potential: DataType<Value> | undefined
for (const T of typeRegistry) {
Expand All @@ -27,10 +40,14 @@ export function matchTypeComponents<Value> (value: Value): DataType<Value> {
}
}
}
if (potential === undefined) {
throw new DevelopmentError('this is not possible')
if (potential === undefined && typeof value === 'object') {
return objectType as unknown as DataType<Value>
} else {
if (potential === undefined) {
throw new DevelopmentError('this is not possible')
}
return potential
}
return potential
}

export function useTypeComponents (value: unknown) {
Expand Down
10 changes: 9 additions & 1 deletion src/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,17 @@ export type JsonViewerProps<T = unknown> = {
/**
* Inspect depth by default.
* Do not set the number too large, otherwise there will have performance issue
* @default 50
* @default 5
*/
defaultInspectDepth?: number
/**
* Hide items after reaching the count.
* Array and object will be affected.
*
* If `Object.keys(value).length` is large also, this will cause a performance issue.
* @default 30
*/
maxDisplayLength?: number
/**
* When an integer value is assigned, arrays will be displayed in groups by count of the value.
* Groups are displayed with bracket notation and can be expanded and collapsed by clicking on the brackets.
Expand Down