Skip to content

Commit 2c85bdb

Browse files
committed
feat: support displayComma for showing comma
1 parent 1700637 commit 2c85bdb

File tree

7 files changed

+86
-14
lines changed

7 files changed

+86
-14
lines changed

docs/pages/apis.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
| `quotesOnKeys` | `boolean` | `true` | Whether add quotes on keys. |
3030
| `displayDataTypes` | `boolean` | `true` | Whether display data type labels. |
3131
| `displaySize` | `boolean` \|<br />`(path, currentValue) => boolean` | `true` | Whether display the size of `Object`, `Array`, `Map` and `Set`. Provide a function to customize this behavior by returning a boolean based on the value and path. |
32+
| `displayComma` | `boolean` | `true` | Whether display commas at the end of the line. |
3233
| `highlightUpdates` | `boolean` | `true` | Whether to highlight updates. |
3334

3435
### Mapping from [`mac-s-g/react-json-view`](https://github.com/mac-s-g/react-json-view)

docs/pages/full/index.tsx

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ const IndexPage: FC = () => {
206206
const [src, setSrc] = useState(() => example)
207207
const [displayDataTypes, setDisplayDataTypes] = useState(true)
208208
const [displaySize, setDisplaySize] = useState(true)
209+
const [displayComma, setDisplayComma] = useState(false)
209210
const [editable, setEditable] = useState(true)
210211
const [highlightUpdates, setHighlightUpdates] = useState(true)
211212
useEffect(() => {
@@ -281,6 +282,15 @@ const IndexPage: FC = () => {
281282
)}
282283
label='DisplayObjectSize'
283284
/>
285+
<FormControlLabel
286+
control={(
287+
<Switch
288+
checked={displayComma}
289+
onChange={event => setDisplayComma(event.target.checked)}
290+
/>
291+
)}
292+
label='DisplayComma'
293+
/>
284294
<TextField
285295
label='indentWidth'
286296
value={indent}
@@ -343,6 +353,7 @@ const IndexPage: FC = () => {
343353
enableDelete={true}
344354
displayDataTypes={displayDataTypes}
345355
displaySize={displaySize}
356+
displayComma={displayComma}
346357
groupArraysAfterLength={groupArraysAfterLength}
347358
keyRenderer={KeyRenderer}
348359
valueTypes={[
@@ -376,7 +387,7 @@ const IndexPage: FC = () => {
376387
)
377388
}
378389
sx={{
379-
paddingLeft: 2
390+
paddingX: 2
380391
}}
381392
/>
382393
</div>

src/components/DataKeyPair.tsx

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export type DataKeyPairProps = {
2727
nestedIndex?: number
2828
editable?: boolean
2929
path: (string | number)[]
30+
last: boolean
3031
}
3132

3233
type IconBoxProps = ComponentProps<typeof Box>
@@ -44,7 +45,7 @@ const IconBox: FC<IconBoxProps> = (props) => (
4445
)
4546

4647
export const DataKeyPair: FC<DataKeyPairProps> = (props) => {
47-
const { value, prevValue, path, nestedIndex } = props
48+
const { value, prevValue, path, nestedIndex, last } = props
4849
const { Component, PreComponent, PostComponent, Editor, serialize, deserialize } = useTypeComponents(value, path)
4950

5051
const propsEditable = props.editable ?? undefined
@@ -79,6 +80,7 @@ export const DataKeyPair: FC<DataKeyPairProps> = (props) => {
7980
const keyColor = useTextColor()
8081
const numberKeyColor = useJsonViewerStore(store => store.colorspace.base0C)
8182
const highlightColor = useJsonViewerStore(store => store.colorspace.base0A)
83+
const displayComma = useJsonViewerStore(store => store.displayComma)
8284
const quotesOnKeys = useJsonViewerStore(store => store.quotesOnKeys)
8385
const rootName = useJsonViewerStore(store => store.rootName)
8486
const isRoot = root === value
@@ -373,7 +375,17 @@ export const DataKeyPair: FC<DataKeyPairProps> = (props) => {
373375
? <KeyRenderer {...downstreamProps} />
374376
: nestedIndex === undefined && (
375377
isNumberKey
376-
? <Box component='span' style={{ color: numberKeyColor }}>{key}</Box>
378+
? (
379+
<Box
380+
component='span'
381+
style={{
382+
color: numberKeyColor,
383+
userSelect: isNumberKey ? 'none' : 'auto'
384+
}}
385+
>
386+
{key}
387+
</Box>
388+
)
377389
: quotesOnKeys ? <>&quot;{key}&quot;</> : <>{key}</>
378390
)
379391
)
@@ -382,13 +394,19 @@ export const DataKeyPair: FC<DataKeyPairProps> = (props) => {
382394
{
383395
(
384396
isRoot
385-
? (rootName !== false && <DataBox sx={{ mr: 0.5 }}>:</DataBox>)
397+
? (rootName !== false && (
398+
<DataBox
399+
className='data-key-colon'
400+
sx={{ mr: 0.5 }}
401+
>:</DataBox>
402+
))
386403
: nestedIndex === undefined && (
387404
<DataBox
388405
className='data-key-colon'
389406
sx={{
390407
mr: 0.5,
391-
'.data-key-key:empty + &': { display: 'none' }
408+
'.data-key-key:empty + &': { display: 'none' },
409+
userSelect: isNumberKey ? 'none' : 'auto'
392410
}}
393411
>:</DataBox>
394412
)
@@ -417,6 +435,7 @@ export const DataKeyPair: FC<DataKeyPairProps> = (props) => {
417435
)
418436
}
419437
{PostComponent && <PostComponent {...downstreamProps} />}
438+
{!last && displayComma && <DataBox>,</DataBox>}
420439
{(isHover && expandable && !inspect) && actionIcons}
421440
{(isHover && !expandable) && actionIcons}
422441
{(!isHover && editing) && actionIcons}

src/components/DataTypes/Object.tsx

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ function inspectMetadata (value: object) {
3232
const PreObjectType: FC<DataItemProps<object>> = (props) => {
3333
const metadataColor = useJsonViewerStore(store => store.colorspace.base04)
3434
const textColor = useTextColor()
35-
const isArray = useMemo(() => Array.isArray(props.value), [props.value])
35+
const isArrayLike = useMemo(() => Array.isArray(props.value) || (props.value instanceof Set), [props.value])
3636
const isEmptyValue = useMemo(() => getValueSize(props.value) === 0, [props.value])
3737
const sizeOfValue = useMemo(() => inspectMetadata(props.value), [props.value])
3838
const displaySize = useJsonViewerStore(store => store.displaySize)
@@ -46,7 +46,7 @@ const PreObjectType: FC<DataItemProps<object>> = (props) => {
4646
letterSpacing: 0.5
4747
}}
4848
>
49-
{isArray ? arrayLb : objectLb}
49+
{isArrayLike ? arrayLb : objectLb}
5050
{shouldDisplaySize && props.inspect && !isEmptyValue && (
5151
<Box
5252
component='span'
@@ -70,7 +70,14 @@ const PreObjectType: FC<DataItemProps<object>> = (props) => {
7070
mx: 0.5
7171
}}
7272
/>
73-
{isTrap}
73+
<DataBox
74+
sx={{
75+
cursor: 'pointer',
76+
userSelect: 'none'
77+
}}
78+
>
79+
{isTrap}
80+
</DataBox>
7481
</>
7582
)}
7683
</Box>
@@ -80,7 +87,7 @@ const PreObjectType: FC<DataItemProps<object>> = (props) => {
8087
const PostObjectType: FC<DataItemProps<object>> = (props) => {
8188
const metadataColor = useJsonViewerStore(store => store.colorspace.base04)
8289
const textColor = useTextColor()
83-
const isArray = useMemo(() => Array.isArray(props.value), [props.value])
90+
const isArrayLike = useMemo(() => Array.isArray(props.value) || (props.value instanceof Set), [props.value])
8491
const isEmptyValue = useMemo(() => getValueSize(props.value) === 0, [props.value])
8592
const sizeOfValue = useMemo(() => inspectMetadata(props.value), [props.value])
8693
const displaySize = useJsonViewerStore(store => store.displaySize)
@@ -97,7 +104,7 @@ const PostObjectType: FC<DataItemProps<object>> = (props) => {
97104
opacity: 0.8
98105
}}
99106
>
100-
{isArray ? arrayRb : objectRb}
107+
{isArrayLike ? arrayRb : objectRb}
101108
{shouldDisplaySize && (isEmptyValue || !props.inspect)
102109
? (
103110
<Box
@@ -138,6 +145,8 @@ const ObjectType: FC<DataItemProps<object>> = (props) => {
138145
if (iterator && !Array.isArray(value)) {
139146
const elements = []
140147
if (value instanceof Map) {
148+
const lastIndex = value.size - 1
149+
let index = 0
141150
value.forEach((value, k) => {
142151
// fixme: key might be a object, array, or any value for the `Map<any, any>`
143152
const key = k.toString()
@@ -149,31 +158,41 @@ const ObjectType: FC<DataItemProps<object>> = (props) => {
149158
value={value}
150159
prevValue={props.prevValue instanceof Map ? props.prevValue.get(k) : undefined}
151160
editable={false}
161+
last={index === lastIndex}
152162
/>
153163
)
164+
index++
154165
})
155166
} else {
156167
// iterate with iterator func
157168
const iterator = value[Symbol.iterator]()
158169
let result = iterator.next()
159170
let count = 0
160-
while (!result.done) {
171+
while (true) {
172+
const nextResult = iterator.next()
161173
elements.push(
162174
<DataKeyPair
163175
key={count}
164176
path={[...props.path, `iterator:${count}`]}
165177
value={result.value}
166178
nestedIndex={count}
167179
editable={false}
180+
last={nextResult.done ?? false}
168181
/>
169182
)
183+
184+
if (nextResult.done) {
185+
break
186+
}
187+
170188
count++
171-
result = iterator.next()
189+
result = nextResult
172190
}
173191
}
174192
return elements
175193
}
176194
if (Array.isArray(value)) {
195+
const lastIndex = value.length - 1
177196
// unknown[]
178197
if (value.length <= groupArraysAfterLength) {
179198
const elements = value.slice(0, displayLength).map((value, _index) => {
@@ -185,6 +204,7 @@ const ObjectType: FC<DataItemProps<object>> = (props) => {
185204
path={path}
186205
value={value}
187206
prevValue={Array.isArray(props.prevValue) ? props.prevValue[index] : undefined}
207+
last={_index === lastIndex}
188208
/>
189209
)
190210
})
@@ -213,6 +233,7 @@ const ObjectType: FC<DataItemProps<object>> = (props) => {
213233
const elements: unknown[][] = segmentArray(value, groupArraysAfterLength)
214234
const prevElements = Array.isArray(props.prevValue) ? segmentArray(props.prevValue, groupArraysAfterLength) : undefined
215235

236+
const elementsLastIndex = elements.length - 1
216237
return elements.map((list, index) => {
217238
return (
218239
<DataKeyPair
@@ -221,6 +242,7 @@ const ObjectType: FC<DataItemProps<object>> = (props) => {
221242
value={list}
222243
nestedIndex={index}
223244
prevValue={prevElements?.[index]}
245+
last={index === elementsLastIndex}
224246
/>
225247
)
226248
})
@@ -232,10 +254,17 @@ const ObjectType: FC<DataItemProps<object>> = (props) => {
232254
? entries.sort(([a], [b]) => a.localeCompare(b))
233255
: entries.sort(([a], [b]) => objectSortKeys(a, b))
234256
}
235-
const elements = entries.slice(0, displayLength).map(([key, value]) => {
257+
const lastIndex = entries.length - 1
258+
const elements = entries.slice(0, displayLength).map(([key, value], index) => {
236259
const path = [...props.path, key]
237260
return (
238-
<DataKeyPair key={key} path={path} value={value} prevValue={(props.prevValue as any)?.[key]} />
261+
<DataKeyPair
262+
key={key}
263+
path={path}
264+
value={value}
265+
prevValue={(props.prevValue as any)?.[key]}
266+
last={index === lastIndex}
267+
/>
239268
)
240269
})
241270
if (entries.length > displayLength) {

src/index.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,10 @@ const JsonViewerInner: FC<JsonViewerProps> = (props) => {
6464
useSetIfNotUndefinedEffect('onDelete', props.onDelete)
6565
useSetIfNotUndefinedEffect('maxDisplayLength', props.maxDisplayLength)
6666
useSetIfNotUndefinedEffect('groupArraysAfterLength', props.groupArraysAfterLength)
67+
useSetIfNotUndefinedEffect('quotesOnKeys', props.quotesOnKeys)
6768
useSetIfNotUndefinedEffect('displayDataTypes', props.displayDataTypes)
6869
useSetIfNotUndefinedEffect('displaySize', props.displaySize)
70+
useSetIfNotUndefinedEffect('displayComma', props.displayComma)
6971
useSetIfNotUndefinedEffect('highlightUpdates', props.highlightUpdates)
7072
useEffect(() => {
7173
if (props.theme === 'light') {
@@ -124,6 +126,7 @@ const JsonViewerInner: FC<JsonViewerProps> = (props) => {
124126
value={value}
125127
prevValue={prevValue}
126128
path={emptyPath}
129+
last={true}
127130
/>
128131
</Paper>
129132
)

src/stores/JsonViewerStore.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ export type JsonViewerState<T = unknown> = {
3939
quotesOnKeys: boolean
4040
displayDataTypes: boolean
4141
displaySize: boolean | ((path: Path, value: unknown) => boolean)
42+
displayComma: boolean
4243
highlightUpdates: boolean
4344

4445
inspectCache: Record<string, boolean>
@@ -79,6 +80,7 @@ export const createJsonViewerStore = <T = unknown> (props: JsonViewerProps<T>) =
7980
quotesOnKeys: props.quotesOnKeys ?? true,
8081
displayDataTypes: props.displayDataTypes ?? true,
8182
displaySize: props.displaySize ?? true,
83+
displayComma: props.displayComma ?? false,
8284
highlightUpdates: props.highlightUpdates ?? false,
8385

8486
// internal state

src/type.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,13 @@ export type JsonViewerProps<T = unknown> = {
283283
*/
284284
displaySize?: boolean | ((path: Path, value: unknown) => boolean)
285285

286+
/**
287+
* Whether display comma at the end of items. Just like valid JSON.
288+
*
289+
* @default false
290+
*/
291+
displayComma?: boolean
292+
286293
/**
287294
* Whether to highlight updates.
288295
*

0 commit comments

Comments
 (0)