Skip to content

Commit fdf9962

Browse files
committed
feat: support plugin system
Fixes: #9
1 parent 95f80c7 commit fdf9962

File tree

6 files changed

+69
-30
lines changed

6 files changed

+69
-30
lines changed

examples/basic/pages/index.tsx

+7
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { TextField } from '@mui/material'
22
import {
33
applyValue,
44
JsonViewer,
5+
JsonViewerKeyRenderer,
56
JsonViewerOnChange
67
} from '@textea/json-viewer'
78
import type React from 'react'
@@ -47,6 +48,11 @@ const example = {
4748
date: new Date('Tue Sep 13 2022 14:07:44 GMT-0500 (Central Daylight Time)')
4849
}
4950

51+
const KeyRenderer: JsonViewerKeyRenderer = ({ path }) => {
52+
return <del aria-label='I dont like this number'>&quot;{path.slice(-1)}&quot;</del>
53+
}
54+
KeyRenderer.when = (props) => props.value === 114.514
55+
5056
const IndexPage: React.FC = () => {
5157
const [indent, setIndent] = useState(2)
5258
const [groupArraysAfterLength, setGroupArraysAfterLength] = useState(100)
@@ -93,6 +99,7 @@ const IndexPage: React.FC = () => {
9399
value={src}
94100
indentWidth={indent}
95101
groupArraysAfterLength={groupArraysAfterLength}
102+
keyRenderer={KeyRenderer}
96103
onChange={
97104
useCallback<JsonViewerOnChange>(
98105
(path, oldValue, newValue) => {

src/components/DataKeyPair.tsx

+10-8
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ export const DataKeyPair: React.FC<DataKeyPairProps> = (props) => {
129129
}, [Editor, editing, onChange, path, tempValue, value])
130130

131131
const expandable = PreComponent && PostComponent
132-
132+
const KeyRenderer = useJsonViewerStore(store => store.keyRenderer)
133133
return (
134134
<Box className='data-key-pair'
135135
onMouseEnter={
@@ -155,12 +155,14 @@ export const DataKeyPair: React.FC<DataKeyPairProps> = (props) => {
155155
}
156156
>
157157
{
158-
!props.nested && (
159-
isNumberKey
160-
? <Box component='span'
161-
style={{ color: numberKeyColor }}>{displayKey}</Box>
162-
: <>&quot;{displayKey}&quot;</>
163-
)
158+
KeyRenderer.when(downstreamProps)
159+
? <KeyRenderer {...downstreamProps}/>
160+
: !props.nested && (
161+
isNumberKey
162+
? <Box component='span'
163+
style={{ color: numberKeyColor }}>{displayKey}</Box>
164+
: <>&quot;{displayKey}&quot;</>
165+
)
164166
}
165167
{
166168
!props.nested && (
@@ -176,7 +178,7 @@ export const DataKeyPair: React.FC<DataKeyPairProps> = (props) => {
176178
: (Component)
177179
? <Component {...downstreamProps}/>
178180
: <Box component='span'
179-
className='data-value-fallback'>{`fallback: ${value}`}</Box>
181+
className='data-value-fallback'>{`fallback: ${value}`}</Box>
180182
}
181183
{PostComponent && <PostComponent {...downstreamProps}/>}
182184
{(isHover && expandable && !inspect) && actionIcons}

src/index.tsx

+32-4
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,28 @@ import {
44
ThemeProvider
55
} from '@mui/material'
66
import type React from 'react'
7-
import { useCallback, useEffect, useMemo } from 'react'
7+
import { useCallback, useEffect, useMemo, useRef } from 'react'
88

99
import { DataKeyPair } from './components/DataKeyPair'
10+
import {
11+
ObjectType,
12+
PostObjectType,
13+
PreObjectType
14+
} from './components/DataTypes/Object'
1015
import {
1116
createJsonViewerStore,
12-
JsonViewerProvider, JsonViewerState, useJsonViewerStore,
17+
JsonViewerProvider,
18+
JsonViewerState,
19+
useJsonViewerStore,
1320
useJsonViewerStoreApi
1421
} from './stores/JsonViewerStore'
22+
import { registerType } from './stores/typeRegistry'
1523
import type { JsonViewerProps } from './type'
1624
import { applyValue } from './utils'
1725

1826
export { applyValue }
1927

20-
export type JsonViewerOnChange = <U = unknown>(path: (string | number)[], oldValue: U, newValue: U) => void
28+
export type JsonViewerOnChange = <U = unknown>(path: (string | number)[], oldValue: U, newValue: U /*, type: ChangeType */) => void
2129

2230
const JsonViewerInner: React.FC<JsonViewerProps> = (props) => {
2331
const api = useJsonViewerStoreApi()
@@ -36,7 +44,8 @@ const JsonViewerInner: React.FC<JsonViewerProps> = (props) => {
3644
setIfNotUndefined('defaultInspectDepth', props.defaultInspectDepth)
3745
setIfNotUndefined('onChange', props.onChange)
3846
setIfNotUndefined('groupArraysAfterLength', props.groupArraysAfterLength)
39-
}, [api, props.defaultInspectDepth, props.groupArraysAfterLength, props.onChange, props.value, setIfNotUndefined])
47+
setIfNotUndefined('keyRenderer', props.keyRenderer)
48+
}, [api, props.defaultInspectDepth, props.groupArraysAfterLength, props.keyRenderer, props.onChange, props.value, setIfNotUndefined])
4049

4150
const value = useJsonViewerStore(store => store.value)
4251
const setHover = useJsonViewerStore(store => store.setHover)
@@ -66,6 +75,23 @@ export const JsonViewer: React.FC<JsonViewerProps> = (props) => {
6675
const theme = useMemo(() => createTheme({
6776
// todo: inject theme based on base16
6877
}), [])
78+
const onceRef = useRef(true)
79+
// DO NOT try to dynamic add value types, that is costly. Trust me.
80+
if (onceRef.current) {
81+
props.valueTypes?.forEach(type => {
82+
registerType(type)
83+
})
84+
// Always last one, fallback for all data like 'object'
85+
registerType<object>(
86+
{
87+
is: (value): value is object => typeof value === 'object',
88+
Component: ObjectType,
89+
PreComponent: PreObjectType,
90+
PostComponent: PostObjectType
91+
}
92+
)
93+
onceRef.current = false
94+
}
6995
return (
7096
<ThemeProvider theme={theme}>
7197
<JsonViewerProvider createStore={createJsonViewerStore}>
@@ -74,3 +100,5 @@ export const JsonViewer: React.FC<JsonViewerProps> = (props) => {
74100
</ThemeProvider>
75101
)
76102
}
103+
104+
export * from './type'

src/stores/JsonViewerStore.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import createContext from 'zustand/context'
44
import { combine } from 'zustand/middleware'
55

66
import type { JsonViewerOnChange } from '..'
7+
import type { JsonViewerKeyRenderer } from '../type'
78

89
export type ColorNamespace = {
910
base00: string
@@ -62,6 +63,9 @@ export const darkNamespace: ColorNamespace = {
6263
base0F: 'rgba(1, 1, 1, 0.8)'
6364
}
6465

66+
const DefaultKeyRenderer: JsonViewerKeyRenderer = () => null
67+
DefaultKeyRenderer.when = () => false
68+
6569
export type JsonViewerState = {
6670
inspectCache: Record<string, boolean>
6771
hoverPath: (string | number)[] | null
@@ -72,6 +76,7 @@ export type JsonViewerState = {
7276
rootName: string
7377
value: unknown
7478
onChange: JsonViewerOnChange
79+
keyRenderer: JsonViewerKeyRenderer
7580
}
7681

7782
export type JsonViewerActions = {
@@ -92,7 +97,8 @@ export const createJsonViewerStore = () =>
9297
colorNamespace: defaultColorNamespace,
9398
expanded: ['data-viewer-root'],
9499
value: {},
95-
onChange: () => {}
100+
onChange: () => {},
101+
keyRenderer: DefaultKeyRenderer
96102
},
97103
(set, get) => ({
98104
getInspectCache: (path) => {

src/stores/typeRegistry.tsx

-15
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,6 @@ import {
77
FunctionType, PostFunctionType,
88
PreFunctionType
99
} from '../components/DataTypes/Function'
10-
import {
11-
ObjectType,
12-
PostObjectType,
13-
PreObjectType
14-
} from '../components/DataTypes/Object'
1510
import type { DataType } from '../type'
1611
import { useJsonViewerStore } from './JsonViewerStore'
1712

@@ -199,13 +194,3 @@ registerType<number>(
199194
)
200195
}
201196
)
202-
203-
// fallback for all data like 'object'
204-
registerType<object>(
205-
{
206-
is: (value): value is object => typeof value === 'object',
207-
Component: ObjectType,
208-
PreComponent: PreObjectType,
209-
PostComponent: PostObjectType
210-
}
211-
)

src/type.ts

+13-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import type { Dispatch, SetStateAction } from 'react'
22
import type React from 'react'
33

4+
type Path = (string | number)[]
5+
46
export interface DataItemProps<ValueType = unknown> {
57
inspect: boolean
68
setInspect: Dispatch<SetStateAction<boolean>>
79
value: ValueType
8-
path: (string | number)[]
10+
path: Path
911
}
1012

1113
export type EditorProps<ValueType = unknown> = {
@@ -21,6 +23,10 @@ export type DataType<ValueType = unknown> = {
2123
PostComponent?: React.ComponentType<DataItemProps<ValueType>>
2224
}
2325

26+
export interface JsonViewerKeyRenderer extends React.FC<DataItemProps> {
27+
when(props: DataItemProps): boolean
28+
}
29+
2430
export type JsonViewerProps<T = unknown> = {
2531
/**
2632
* any value
@@ -30,13 +36,18 @@ export type JsonViewerProps<T = unknown> = {
3036
* indent width for nested objects
3137
*/
3238
indentWidth?: number
39+
/**
40+
* Customize a key, if `keyRenderer.when` returns `true`.
41+
*/
42+
keyRenderer?: JsonViewerKeyRenderer
43+
valueTypes?: DataType<any>[]
3344
/**
3445
*
3546
* @param path path to the target value
3647
* @param oldValue
3748
* @param newValue
3849
*/
39-
onChange?: <U>(path: (string | number)[], oldValue: U, newValue: U) => void
50+
onChange?: <U>(path: Path, oldValue: U, newValue: U) => void
4051
/**
4152
* Inspect depth by default.
4253
* Do not set the number too large, otherwise there will have performance issue

0 commit comments

Comments
 (0)