@uiw/react-json-view 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +95 -1
- package/dark.d.ts +19 -0
- package/light.d.ts +19 -0
- package/package.json +6 -3
- package/triangle-arrow.d.ts +5 -0
- package/triangle-solid-arrow.d.ts +5 -0
- package/src/arrow/TriangleArrow.tsx +0 -17
- package/src/arrow/TriangleSolidArrow.tsx +0 -17
- package/src/index.tsx +0 -43
- package/src/node.tsx +0 -201
- package/src/store.tsx +0 -37
- package/src/theme/dark.ts +0 -21
- package/src/theme/light.ts +0 -21
- package/src/value.tsx +0 -172
package/README.md
CHANGED
|
@@ -6,10 +6,20 @@ react-json-view
|
|
|
6
6
|
|
|
7
7
|
A React component for displaying and editing javascript arrays and JSON objects.
|
|
8
8
|
|
|
9
|
+
<!--rehype:ignore:start-->
|
|
10
|
+
<a href="https://uiwjs.github.io/react-json-view/" target="_blank">
|
|
11
|
+
<img width="360" alt="react-json-view" src="https://github.com/uiwjs/react-json-view/assets/1680273/b8b8300b-2a43-4173-878b-73e50cfac19c" />
|
|
12
|
+
</a>
|
|
13
|
+
<a href="https://uiwjs.github.io/react-json-view/" target="_blank">
|
|
14
|
+
<img width="360" alt="react-json-view" src="https://github.com/uiwjs/react-code-preview-layout/assets/1680273/da8e8499-6de3-4d4f-8316-8b2a3b616170" />
|
|
15
|
+
</a>
|
|
16
|
+
|
|
17
|
+
<!--rehype:ignore:end-->
|
|
18
|
+
|
|
9
19
|
## Features
|
|
10
20
|
|
|
11
21
|
📚 Use Typescript to write, better code hints.
|
|
12
|
-
🎨 Support theme customization
|
|
22
|
+
🎨 Support theme customization & [`online editing`](https://uiwjs.github.io/react-json-view/#online-editing-theme) theme
|
|
13
23
|
🌒 Support dark/light mode
|
|
14
24
|
|
|
15
25
|
## Quick Start
|
|
@@ -121,6 +131,90 @@ export default function Demo() {
|
|
|
121
131
|
}
|
|
122
132
|
```
|
|
123
133
|
|
|
134
|
+
## Online Editing Theme
|
|
135
|
+
|
|
136
|
+
Online custom style example, please check in the [documentation website](https://uiwjs.github.io/react-json-view/)
|
|
137
|
+
|
|
138
|
+
```tsx mdx:preview
|
|
139
|
+
import React, { useState, useRef } from 'react';
|
|
140
|
+
import Colorful from '@uiw/react-color-colorful';
|
|
141
|
+
import JsonView from '@uiw/react-json-view';
|
|
142
|
+
|
|
143
|
+
const object = {
|
|
144
|
+
avatar: 'https://i.imgur.com/MK3eW3As.jpg',
|
|
145
|
+
string: 'Lorem ipsum dolor sit amet',
|
|
146
|
+
integer: 42,
|
|
147
|
+
float: 114.514,
|
|
148
|
+
bigint: 10086n,
|
|
149
|
+
null: null,
|
|
150
|
+
undefined,
|
|
151
|
+
timer: 0,
|
|
152
|
+
date: new Date('Tue Sep 13 2022 14:07:44 GMT-0500 (Central Daylight Time)'),
|
|
153
|
+
array: [19, 100.86, 'test', NaN, Infinity],
|
|
154
|
+
nestedArray: [
|
|
155
|
+
[1, 2],
|
|
156
|
+
[3, 4],
|
|
157
|
+
],
|
|
158
|
+
object: {
|
|
159
|
+
'first-child': true,
|
|
160
|
+
'second-child': false,
|
|
161
|
+
'last-child': null,
|
|
162
|
+
},
|
|
163
|
+
string_number: '1234',
|
|
164
|
+
}
|
|
165
|
+
const customTheme = {
|
|
166
|
+
'--w-rjv-color': '#9cdcfe',
|
|
167
|
+
'--w-rjv-background-color': '#1e1e1e',
|
|
168
|
+
'--w-rjv-border-left': '1px solid #323232',
|
|
169
|
+
'--w-rjv-arrow-color': 'var(--w-rjv-color)',
|
|
170
|
+
'--w-rjv-info-color': '#656565',
|
|
171
|
+
|
|
172
|
+
'--w-rjv-curlybraces-color': '#d4d4d4',
|
|
173
|
+
'--w-rjv-brackets-color': '#d4d4d4',
|
|
174
|
+
|
|
175
|
+
'--w-rjv-type-string-color': '#ce9178',
|
|
176
|
+
'--w-rjv-type-int-color': '#268bd2',
|
|
177
|
+
'--w-rjv-type-float-color': '#859900',
|
|
178
|
+
'--w-rjv-type-bigint-color': '#268bd2',
|
|
179
|
+
'--w-rjv-type-boolean-color': '#559bd4',
|
|
180
|
+
'--w-rjv-type-date-color': '#586e75',
|
|
181
|
+
'--w-rjv-type-null-color': '#d33682',
|
|
182
|
+
'--w-rjv-type-nan-color': '#859900',
|
|
183
|
+
'--w-rjv-type-undefined-color': '#586e75',
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
export default function Demo() {
|
|
187
|
+
const [cssvar, setCssvar] = useState('--w-rjv-background-color');
|
|
188
|
+
const [hex, setHex] = useState("#1e1e1e");
|
|
189
|
+
const [theme, setTheme] = useState(customTheme);
|
|
190
|
+
const onChange = ({ hexa }) => {
|
|
191
|
+
const value = cssvar === '--w-rjv-border-left' ? `1px solid ${hexa}` : hexa;
|
|
192
|
+
setHex(hexa);
|
|
193
|
+
setTheme({ ...theme, [cssvar]: value });
|
|
194
|
+
};
|
|
195
|
+
return (
|
|
196
|
+
<React.Fragment>
|
|
197
|
+
<div style={{ display: 'flex', gap: '1rem' }}>
|
|
198
|
+
<JsonView value={object} keyName="root" style={{ flex: 1, ...theme }} />
|
|
199
|
+
<div>
|
|
200
|
+
<Colorful color={hex} onChange={onChange} />
|
|
201
|
+
<div style={{ display: 'flex', gap: '0.4rem', flexWrap: 'wrap' }}>
|
|
202
|
+
{Object.keys(customTheme).map((varname, idx) => {
|
|
203
|
+
const click = () => setCssvar(varname);
|
|
204
|
+
const active = cssvar === varname ? '#a8a8a8' : '';
|
|
205
|
+
return <button key={idx} style={{ background: active }} onClick={click}>{varname}</button>
|
|
206
|
+
})}
|
|
207
|
+
</div>
|
|
208
|
+
</div>
|
|
209
|
+
</div>
|
|
210
|
+
Copy the theme configuration below into your project.
|
|
211
|
+
<pre>
|
|
212
|
+
{JSON.stringify(theme, null, 2)}
|
|
213
|
+
</pre>
|
|
214
|
+
</React.Fragment>
|
|
215
|
+
)
|
|
216
|
+
}
|
|
217
|
+
```
|
|
124
218
|
## Render
|
|
125
219
|
|
|
126
220
|
```tsx mdx:preview
|
package/dark.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
declare module '@uiw/react-json-view/dark' {
|
|
2
|
+
export const darkTheme: {
|
|
3
|
+
'--w-rjv-font-family': string;
|
|
4
|
+
'--w-rjv-color': string;
|
|
5
|
+
'--w-rjv-background-color': string;
|
|
6
|
+
'--w-rjv-border-left': string;
|
|
7
|
+
'--w-rjv-arrow-color': string;
|
|
8
|
+
'--w-rjv-info-color': string;
|
|
9
|
+
'--w-rjv-type-string-color': string;
|
|
10
|
+
'--w-rjv-type-int-color': string;
|
|
11
|
+
'--w-rjv-type-float-color': string;
|
|
12
|
+
'--w-rjv-type-bigint-color': string;
|
|
13
|
+
'--w-rjv-type-boolean-color': string;
|
|
14
|
+
'--w-rjv-type-date-color': string;
|
|
15
|
+
'--w-rjv-type-null-color': string;
|
|
16
|
+
'--w-rjv-type-nan-color': string;
|
|
17
|
+
'--w-rjv-type-undefined-color': string;
|
|
18
|
+
};
|
|
19
|
+
}
|
package/light.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
declare module '@uiw/react-json-view/light' {
|
|
2
|
+
export const lightTheme: {
|
|
3
|
+
'--w-rjv-font-family': string;
|
|
4
|
+
'--w-rjv-color': string;
|
|
5
|
+
'--w-rjv-background-color': string;
|
|
6
|
+
'--w-rjv-border-left': string;
|
|
7
|
+
'--w-rjv-arrow-color': string;
|
|
8
|
+
'--w-rjv-info-color': string;
|
|
9
|
+
'--w-rjv-type-string-color': string;
|
|
10
|
+
'--w-rjv-type-int-color': string;
|
|
11
|
+
'--w-rjv-type-float-color': string;
|
|
12
|
+
'--w-rjv-type-bigint-color': string;
|
|
13
|
+
'--w-rjv-type-boolean-color': string;
|
|
14
|
+
'--w-rjv-type-date-color': string;
|
|
15
|
+
'--w-rjv-type-null-color': string;
|
|
16
|
+
'--w-rjv-type-nan-color': string;
|
|
17
|
+
'--w-rjv-type-undefined-color': string;
|
|
18
|
+
};
|
|
19
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uiw/react-json-view",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"description": "JSON viewer for react.",
|
|
5
5
|
"main": "cjs/index.js",
|
|
6
6
|
"module": "esm/index.js",
|
|
@@ -45,9 +45,12 @@
|
|
|
45
45
|
"react-dom": ">=18.0.0"
|
|
46
46
|
},
|
|
47
47
|
"files": [
|
|
48
|
+
"dark.d.ts",
|
|
49
|
+
"light.d.ts",
|
|
50
|
+
"triangle-arrow.d.ts",
|
|
51
|
+
"triangle-solid-arrow.d.ts",
|
|
48
52
|
"cjs",
|
|
49
|
-
"esm"
|
|
50
|
-
"src"
|
|
53
|
+
"esm"
|
|
51
54
|
],
|
|
52
55
|
"keywords": [
|
|
53
56
|
"monaco-editor",
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
declare module '@uiw/react-json-view/triangle-solid-arrow' {
|
|
2
|
+
import React from 'react';
|
|
3
|
+
export interface TriangleSolidArrowProps extends React.SVGProps<SVGSVGElement> { }
|
|
4
|
+
export function TriangleSolidArrow(props: TriangleSolidArrowProps): import("react/jsx-runtime").JSX.Element;
|
|
5
|
+
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
|
|
3
|
-
export interface TriangleArrowProps extends React.SVGProps<SVGSVGElement> {}
|
|
4
|
-
export function TriangleArrow(props: TriangleArrowProps) {
|
|
5
|
-
const { style, ...reset } = props;
|
|
6
|
-
const defaultStyle: React.CSSProperties = {
|
|
7
|
-
cursor: 'pointer',
|
|
8
|
-
height: '1em',
|
|
9
|
-
width: '1em',
|
|
10
|
-
...style,
|
|
11
|
-
};
|
|
12
|
-
return (
|
|
13
|
-
<svg viewBox="0 0 24 24" fill="var(--w-rjv-arrow-color, currentColor)" style={defaultStyle} {...reset}>
|
|
14
|
-
<path d="M16.59 8.59 12 13.17 7.41 8.59 6 10l6 6 6-6z"></path>
|
|
15
|
-
</svg>
|
|
16
|
-
);
|
|
17
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
|
|
3
|
-
export interface TriangleSolidArrowProps extends React.SVGProps<SVGSVGElement> {}
|
|
4
|
-
export function TriangleSolidArrow(props: TriangleSolidArrowProps) {
|
|
5
|
-
const { style, ...reset } = props;
|
|
6
|
-
const defaultStyle: React.CSSProperties = {
|
|
7
|
-
cursor: 'pointer',
|
|
8
|
-
height: '1em',
|
|
9
|
-
width: '1em',
|
|
10
|
-
...style,
|
|
11
|
-
};
|
|
12
|
-
return (
|
|
13
|
-
<svg viewBox="0 0 15 15" fill="var(--w-rjv-arrow-color, currentColor)" style={defaultStyle} {...reset}>
|
|
14
|
-
<path d="M0 5l6 6 6-6z"></path>
|
|
15
|
-
</svg>
|
|
16
|
-
);
|
|
17
|
-
}
|
package/src/index.tsx
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import React, { useId } from 'react';
|
|
2
|
-
import { forwardRef } from 'react';
|
|
3
|
-
import { RooNode, MetaProps, SemicolonProps, EllipsisProps } from './node';
|
|
4
|
-
import { ValueViewProps } from './value';
|
|
5
|
-
|
|
6
|
-
export * from './node';
|
|
7
|
-
export * from './value';
|
|
8
|
-
export interface JsonViewProps<T>
|
|
9
|
-
extends React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement> {
|
|
10
|
-
/** This property contains your input JSON */
|
|
11
|
-
value?: T;
|
|
12
|
-
/** Set the indent-width for nested objects @default `15`*/
|
|
13
|
-
indentWidth?: number;
|
|
14
|
-
/** When set to `true`, data type labels prefix values @default `true` */
|
|
15
|
-
displayDataTypes?: boolean;
|
|
16
|
-
/** When set to `true`, `objects` and `arrays` are labeled with size @default `true` */
|
|
17
|
-
displayObjectSize?: boolean;
|
|
18
|
-
/** Define the root node name. @default `undefined` */
|
|
19
|
-
keyName?: string | number;
|
|
20
|
-
components?: {
|
|
21
|
-
braces?: MetaProps['render'];
|
|
22
|
-
ellipsis?: EllipsisProps['render'];
|
|
23
|
-
arrow?: JSX.Element;
|
|
24
|
-
objectKey?: SemicolonProps['render'];
|
|
25
|
-
value?: ValueViewProps<T>['renderValue'];
|
|
26
|
-
};
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const JsonView = forwardRef<HTMLDivElement, JsonViewProps<object>>((props, ref) => {
|
|
30
|
-
const { value, style, className, ...reset } = props;
|
|
31
|
-
const defaultStyle = {
|
|
32
|
-
lineHeight: 1.4,
|
|
33
|
-
fontFamily: 'monospace',
|
|
34
|
-
color: 'var(--w-rjv-color, #002b36)',
|
|
35
|
-
backgroundColor: 'var(--w-rjv-background-color, #00000000)',
|
|
36
|
-
...style,
|
|
37
|
-
} as React.CSSProperties;
|
|
38
|
-
const cls = `w-json-view-container w-rjv ${className || ''}`;
|
|
39
|
-
const keyid = useId();
|
|
40
|
-
return <RooNode className={cls} value={value} {...reset} ref={ref} keyid={keyid} style={defaultStyle} />;
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
export default JsonView;
|
package/src/node.tsx
DELETED
|
@@ -1,201 +0,0 @@
|
|
|
1
|
-
import { FC, Fragment, PropsWithChildren, useId, cloneElement } from 'react';
|
|
2
|
-
import { ValueView, ValueViewProps, Colon, Label, LabelProps, typeMap } from './value';
|
|
3
|
-
import { TriangleArrow } from './arrow/TriangleArrow';
|
|
4
|
-
import { useExpandsStatus, store } from './store';
|
|
5
|
-
import { JsonViewProps } from './';
|
|
6
|
-
|
|
7
|
-
export interface MetaProps extends LabelProps {
|
|
8
|
-
isArray?: boolean;
|
|
9
|
-
start?: boolean;
|
|
10
|
-
render?: (props: Pick<MetaProps, 'start' | 'isArray' | 'className' | 'children'>) => JSX.Element;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export function Meta(props: MetaProps) {
|
|
14
|
-
const { isArray = false, start = false, className, render, ...reset } = props;
|
|
15
|
-
const mark = isArray ? '[]' : '{}';
|
|
16
|
-
const cls = `w-rjv-${isArray ? 'brackets' : 'curlybraces'}-${start ? 'start' : 'end'} ${className || ''}`;
|
|
17
|
-
const color = `var(--w-rjv-${isArray ? 'brackets' : 'curlybraces'}-color, #236a7c)`;
|
|
18
|
-
if (render)
|
|
19
|
-
return render({
|
|
20
|
-
isArray,
|
|
21
|
-
className: cls,
|
|
22
|
-
style: { color },
|
|
23
|
-
children: start ? mark.charAt(0) : mark.charAt(1),
|
|
24
|
-
...reset,
|
|
25
|
-
});
|
|
26
|
-
return (
|
|
27
|
-
<Label color={color} className={cls} {...reset}>
|
|
28
|
-
{start ? mark.charAt(0) : mark.charAt(1)}
|
|
29
|
-
</Label>
|
|
30
|
-
);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export const Line: FC<PropsWithChildren<React.HTMLAttributes<HTMLDivElement>>> = (props) => <div {...props} />;
|
|
34
|
-
|
|
35
|
-
export interface EllipsisProps extends React.HTMLAttributes<HTMLSpanElement> {
|
|
36
|
-
render?: (props: EllipsisProps) => JSX.Element;
|
|
37
|
-
}
|
|
38
|
-
export const Ellipsis: FC<PropsWithChildren<EllipsisProps>> = ({ style, render, ...props }) => {
|
|
39
|
-
const styl = { cursor: 'pointer', ...style };
|
|
40
|
-
const className = `w-rjv-ellipsis ${props.className || ''}`;
|
|
41
|
-
if (render) return render({ style: styl, ...props, className });
|
|
42
|
-
return (
|
|
43
|
-
<span className={className} style={styl} {...props}>
|
|
44
|
-
...
|
|
45
|
-
</span>
|
|
46
|
-
);
|
|
47
|
-
};
|
|
48
|
-
export interface SemicolonProps extends LabelProps {
|
|
49
|
-
show?: boolean;
|
|
50
|
-
value?: object;
|
|
51
|
-
render?: (props: Omit<SemicolonProps, 'show'> & {}) => JSX.Element;
|
|
52
|
-
}
|
|
53
|
-
export const Semicolon: FC<PropsWithChildren<SemicolonProps>> = ({
|
|
54
|
-
children,
|
|
55
|
-
render,
|
|
56
|
-
color,
|
|
57
|
-
value,
|
|
58
|
-
className = 'w-rjv-object-key',
|
|
59
|
-
show,
|
|
60
|
-
...props
|
|
61
|
-
}) => {
|
|
62
|
-
const content = show ? `"${children}"` : children;
|
|
63
|
-
if (render) return render({ className, ...props, value, style: { color }, children: content });
|
|
64
|
-
return (
|
|
65
|
-
<Label className={className} color={color} {...props}>
|
|
66
|
-
{content}
|
|
67
|
-
</Label>
|
|
68
|
-
);
|
|
69
|
-
};
|
|
70
|
-
export const CountInfo: FC<PropsWithChildren<LabelProps>> = ({ children }) => (
|
|
71
|
-
<Label
|
|
72
|
-
style={{ paddingLeft: 4, fontStyle: 'italic' }}
|
|
73
|
-
color="var(--w-rjv-info-color, #0000004d)"
|
|
74
|
-
className="object-size"
|
|
75
|
-
>
|
|
76
|
-
{children}
|
|
77
|
-
</Label>
|
|
78
|
-
);
|
|
79
|
-
|
|
80
|
-
export interface RooNodeProps<T> extends JsonViewProps<T> {
|
|
81
|
-
keyName?: string | number;
|
|
82
|
-
keyid?: string;
|
|
83
|
-
}
|
|
84
|
-
export function RooNode<T extends object>(props: RooNodeProps<T>) {
|
|
85
|
-
const {
|
|
86
|
-
value = {},
|
|
87
|
-
keyName,
|
|
88
|
-
displayDataTypes = true,
|
|
89
|
-
components = {},
|
|
90
|
-
displayObjectSize = true,
|
|
91
|
-
indentWidth = 15,
|
|
92
|
-
keyid = 'root',
|
|
93
|
-
...reset
|
|
94
|
-
} = props;
|
|
95
|
-
const isArray = Array.isArray(value);
|
|
96
|
-
const nameKeys = (isArray ? Object.keys(value).map(m => Number(m)) : Object.keys(value)) as (keyof typeof value)[];
|
|
97
|
-
const subkeyid = useId();
|
|
98
|
-
const expands = useExpandsStatus();
|
|
99
|
-
const expand = expands[keyid] ?? true;
|
|
100
|
-
const arrowStyle = { transform: `rotate(${expand ? '0' : '-90'}deg)`, transition: 'all 0.3s' };
|
|
101
|
-
const handle = () => {
|
|
102
|
-
!expand ? store.expand(keyid) : store.collapse(keyid);
|
|
103
|
-
};
|
|
104
|
-
const subNodeProps: RooNodeProps<T> = {
|
|
105
|
-
components,
|
|
106
|
-
indentWidth,
|
|
107
|
-
displayDataTypes,
|
|
108
|
-
displayObjectSize,
|
|
109
|
-
style: { paddingLeft: indentWidth },
|
|
110
|
-
};
|
|
111
|
-
const valueViewProps = {
|
|
112
|
-
displayDataTypes,
|
|
113
|
-
renderValue: components.value,
|
|
114
|
-
} as ValueViewProps<T>;
|
|
115
|
-
|
|
116
|
-
const arrowView = components.arrow ? (
|
|
117
|
-
cloneElement(components.arrow, { style: arrowStyle, 'data-expand': expand })
|
|
118
|
-
) : (
|
|
119
|
-
<TriangleArrow style={arrowStyle} />
|
|
120
|
-
);
|
|
121
|
-
return (
|
|
122
|
-
<div {...reset}>
|
|
123
|
-
<Line style={{ display: 'inline-flex', alignItems: 'center' }} onClick={handle}>
|
|
124
|
-
{arrowView}
|
|
125
|
-
{(typeof keyName === 'string' || typeof keyName === 'number') && (
|
|
126
|
-
<Fragment>
|
|
127
|
-
<Semicolon
|
|
128
|
-
value={value}
|
|
129
|
-
render={components.objectKey}
|
|
130
|
-
color={typeof keyName === 'number' ? typeMap['number'].color : ''}
|
|
131
|
-
show={typeof keyName === 'string'}
|
|
132
|
-
>
|
|
133
|
-
{keyName}
|
|
134
|
-
</Semicolon>
|
|
135
|
-
<Colon />
|
|
136
|
-
</Fragment>
|
|
137
|
-
)}
|
|
138
|
-
<Meta start isArray={isArray} render={components.braces} />
|
|
139
|
-
{!expand && <Ellipsis render={components.ellipsis} />}
|
|
140
|
-
{!expand && <Meta isArray={isArray} render={components.braces} />}
|
|
141
|
-
{displayObjectSize && <CountInfo>{nameKeys.length} items</CountInfo>}
|
|
142
|
-
</Line>
|
|
143
|
-
{expand && (
|
|
144
|
-
<Line style={{ borderLeft: 'var(--w-rjv-border-left, 1px solid #ebebeb)', marginLeft: 6 }}>
|
|
145
|
-
{nameKeys.length > 0 &&
|
|
146
|
-
nameKeys.map((key, idx) => {
|
|
147
|
-
const item = value[key];
|
|
148
|
-
if (Array.isArray(item)) {
|
|
149
|
-
const label = isArray ? idx : key;
|
|
150
|
-
return (
|
|
151
|
-
<Line key={label + idx}>
|
|
152
|
-
<RooNode value={item} keyid={keyid + subkeyid + label} keyName={label} {...subNodeProps} />
|
|
153
|
-
</Line>
|
|
154
|
-
);
|
|
155
|
-
}
|
|
156
|
-
const renderKey = (
|
|
157
|
-
<Semicolon
|
|
158
|
-
value={item}
|
|
159
|
-
render={components.objectKey}
|
|
160
|
-
color={typeof key === 'number' ? typeMap['number'].color : ''}
|
|
161
|
-
show={typeof key === 'string'}
|
|
162
|
-
>
|
|
163
|
-
{key}
|
|
164
|
-
</Semicolon>
|
|
165
|
-
);
|
|
166
|
-
if (typeof item === 'object' && item && !((item as any) instanceof Date)) {
|
|
167
|
-
if (Object.keys(item).length === 0) {
|
|
168
|
-
return (
|
|
169
|
-
<Line key={key + idx} style={{ paddingLeft: indentWidth }}>
|
|
170
|
-
<ValueView {...valueViewProps} renderKey={renderKey} keyName={key} value={item} />
|
|
171
|
-
<Meta render={components.braces} start isArray={isArray} />
|
|
172
|
-
<Meta render={components.braces} isArray={isArray} />
|
|
173
|
-
{displayObjectSize && <CountInfo>{Object.keys(item).length} items</CountInfo>}
|
|
174
|
-
</Line>
|
|
175
|
-
);
|
|
176
|
-
}
|
|
177
|
-
return (
|
|
178
|
-
<Line key={key + idx}>
|
|
179
|
-
<RooNode keyid={keyid + subkeyid + key} value={item} keyName={key} {...subNodeProps} />
|
|
180
|
-
</Line>
|
|
181
|
-
);
|
|
182
|
-
}
|
|
183
|
-
if (typeof item === 'function') {
|
|
184
|
-
return;
|
|
185
|
-
}
|
|
186
|
-
return (
|
|
187
|
-
<Line key={idx} style={{ paddingLeft: indentWidth }}>
|
|
188
|
-
<ValueView {...valueViewProps} renderKey={renderKey} keyName={key} value={item} />
|
|
189
|
-
</Line>
|
|
190
|
-
);
|
|
191
|
-
})}
|
|
192
|
-
</Line>
|
|
193
|
-
)}
|
|
194
|
-
{expand && (
|
|
195
|
-
<Line style={{ paddingLeft: 2 }}>
|
|
196
|
-
<Meta render={components.braces} isArray={isArray} style={{ paddingLeft: 2, display: 'inline-block' }} />
|
|
197
|
-
</Line>
|
|
198
|
-
)}
|
|
199
|
-
</div>
|
|
200
|
-
);
|
|
201
|
-
}
|
package/src/store.tsx
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { useSyncExternalStore } from 'react';
|
|
2
|
-
|
|
3
|
-
let expands: Record<string, boolean> = {};
|
|
4
|
-
let listeners: Function[] = [];
|
|
5
|
-
|
|
6
|
-
export const store = {
|
|
7
|
-
collapse(id: string) {
|
|
8
|
-
expands = { ...expands, [id]: false };
|
|
9
|
-
emitChange();
|
|
10
|
-
},
|
|
11
|
-
expand(id: string) {
|
|
12
|
-
expands = { ...expands, [id]: true };
|
|
13
|
-
emitChange();
|
|
14
|
-
},
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
function getSnapshot() {
|
|
18
|
-
return expands;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function subscribe(listener: Function) {
|
|
22
|
-
listeners = [...listeners, listener];
|
|
23
|
-
return () => {
|
|
24
|
-
listeners = listeners.filter((l) => l !== listener);
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function emitChange() {
|
|
29
|
-
for (let listener of listeners) {
|
|
30
|
-
listener();
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export function useExpandsStatus() {
|
|
35
|
-
const expands = useSyncExternalStore(subscribe, getSnapshot);
|
|
36
|
-
return expands;
|
|
37
|
-
}
|
package/src/theme/dark.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
export const darkTheme = {
|
|
2
|
-
'--w-rjv-font-family': 'monospace',
|
|
3
|
-
'--w-rjv-color': '#0184a6',
|
|
4
|
-
'--w-rjv-background-color': '#202020',
|
|
5
|
-
'--w-rjv-border-left': '1px solid #323232',
|
|
6
|
-
'--w-rjv-arrow-color': 'var(--w-rjv-color)',
|
|
7
|
-
'--w-rjv-info-color': '#656565',
|
|
8
|
-
|
|
9
|
-
'--w-rjv-curlybraces-color': '#1896b6',
|
|
10
|
-
'--w-rjv-brackets-color': '#1896b6',
|
|
11
|
-
|
|
12
|
-
'--w-rjv-type-string-color': '#cb4b16',
|
|
13
|
-
'--w-rjv-type-int-color': '#268bd2',
|
|
14
|
-
'--w-rjv-type-float-color': '#859900',
|
|
15
|
-
'--w-rjv-type-bigint-color': '#268bd2',
|
|
16
|
-
'--w-rjv-type-boolean-color': '#2aa198',
|
|
17
|
-
'--w-rjv-type-date-color': '#586e75',
|
|
18
|
-
'--w-rjv-type-null-color': '#d33682',
|
|
19
|
-
'--w-rjv-type-nan-color': '#859900',
|
|
20
|
-
'--w-rjv-type-undefined-color': '#586e75',
|
|
21
|
-
};
|
package/src/theme/light.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
export const lightTheme = {
|
|
2
|
-
'--w-rjv-font-family': 'monospace',
|
|
3
|
-
'--w-rjv-color': '#002b36',
|
|
4
|
-
'--w-rjv-background-color': '#ffffff',
|
|
5
|
-
'--w-rjv-border-left': '1px solid #ebebeb',
|
|
6
|
-
'--w-rjv-arrow-color': 'var(--w-rjv-color)',
|
|
7
|
-
'--w-rjv-info-color': '#0000004d',
|
|
8
|
-
|
|
9
|
-
'--w-rjv-curlybraces-color': '#236a7c',
|
|
10
|
-
'--w-rjv-brackets-color': '#236a7c',
|
|
11
|
-
|
|
12
|
-
'--w-rjv-type-string-color': '#cb4b16',
|
|
13
|
-
'--w-rjv-type-int-color': '#268bd2',
|
|
14
|
-
'--w-rjv-type-float-color': '#859900',
|
|
15
|
-
'--w-rjv-type-bigint-color': '#268bd2',
|
|
16
|
-
'--w-rjv-type-boolean-color': '#2aa198',
|
|
17
|
-
'--w-rjv-type-date-color': '#586e75',
|
|
18
|
-
'--w-rjv-type-null-color': '#d33682',
|
|
19
|
-
'--w-rjv-type-nan-color': '#859900',
|
|
20
|
-
'--w-rjv-type-undefined-color': '#586e75',
|
|
21
|
-
};
|
package/src/value.tsx
DELETED
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
import { FC, Fragment, PropsWithChildren } from 'react';
|
|
2
|
-
|
|
3
|
-
const isFloat = (n: number) => (Number(n) === n && n % 1 !== 0) || isNaN(n);
|
|
4
|
-
export const typeMap = {
|
|
5
|
-
string: {
|
|
6
|
-
color: 'var(--w-rjv-type-string-color, #cb4b16)',
|
|
7
|
-
label: 'string',
|
|
8
|
-
},
|
|
9
|
-
number: {
|
|
10
|
-
color: 'var(--w-rjv-type-int-color, #268bd2)',
|
|
11
|
-
label: 'int',
|
|
12
|
-
},
|
|
13
|
-
float: {
|
|
14
|
-
color: 'var(--w-rjv-type-float-color, #859900)',
|
|
15
|
-
label: 'float',
|
|
16
|
-
},
|
|
17
|
-
bigint: {
|
|
18
|
-
color: 'var(--w-rjv-type-bigint-color, #268bd2)',
|
|
19
|
-
label: 'bigint',
|
|
20
|
-
},
|
|
21
|
-
boolean: {
|
|
22
|
-
color: 'var(--w-rjv-type-boolean-color, #2aa198)',
|
|
23
|
-
label: 'bool',
|
|
24
|
-
},
|
|
25
|
-
date: {
|
|
26
|
-
color: 'var(--w-rjv-type-date-color, #586e75)',
|
|
27
|
-
label: 'date',
|
|
28
|
-
},
|
|
29
|
-
null: {
|
|
30
|
-
color: 'var(--w-rjv-type-null-color, #d33682)',
|
|
31
|
-
label: 'null',
|
|
32
|
-
},
|
|
33
|
-
NaN: {
|
|
34
|
-
color: 'var(--w-rjv-type-nan-color, #859900)',
|
|
35
|
-
label: 'NaN',
|
|
36
|
-
},
|
|
37
|
-
undefined: {
|
|
38
|
-
color: 'var(--w-rjv-type-undefined-color, #586e75)',
|
|
39
|
-
label: 'undefined',
|
|
40
|
-
},
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
export const Colon: FC<PropsWithChildren<React.HTMLAttributes<HTMLSpanElement>>> = ({
|
|
44
|
-
children = ':',
|
|
45
|
-
style,
|
|
46
|
-
...props
|
|
47
|
-
}) => (
|
|
48
|
-
<span className="w-rjv-colon" style={{ paddingRight: 3, ...style }} {...props}>
|
|
49
|
-
{children}
|
|
50
|
-
</span>
|
|
51
|
-
);
|
|
52
|
-
|
|
53
|
-
export interface ValueViewProps<T>
|
|
54
|
-
extends React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement> {
|
|
55
|
-
keyName?: string;
|
|
56
|
-
value: T;
|
|
57
|
-
displayDataTypes: boolean;
|
|
58
|
-
renderKey?: JSX.Element;
|
|
59
|
-
renderValue?: (props: React.HTMLAttributes<HTMLSpanElement> & { type: TypeProps['type'] }) => JSX.Element;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export function ValueView<T>(props: ValueViewProps<T>) {
|
|
63
|
-
const { value, keyName, renderKey, renderValue, displayDataTypes, ...reset } = props;
|
|
64
|
-
|
|
65
|
-
let type = typeof value as TypeProps['type'];
|
|
66
|
-
let content = '';
|
|
67
|
-
let color = '';
|
|
68
|
-
let style = {} as React.CSSProperties;
|
|
69
|
-
if (typeof value === 'number') {
|
|
70
|
-
type = isFloat(value) ? 'float' : 'number';
|
|
71
|
-
content = value.toString();
|
|
72
|
-
}
|
|
73
|
-
if (typeof value === 'boolean') {
|
|
74
|
-
type = 'boolean';
|
|
75
|
-
content = value.toString();
|
|
76
|
-
}
|
|
77
|
-
if (typeof value === 'object' && value instanceof Date) {
|
|
78
|
-
type = 'date';
|
|
79
|
-
content = value.toString();
|
|
80
|
-
}
|
|
81
|
-
let typeView = <Type type={type} />;
|
|
82
|
-
if (value === null) {
|
|
83
|
-
type = 'null';
|
|
84
|
-
content = `${value}`.toLocaleUpperCase();
|
|
85
|
-
typeView = <Fragment />;
|
|
86
|
-
style = { fontWeight: 'bold' };
|
|
87
|
-
}
|
|
88
|
-
if (value === undefined) {
|
|
89
|
-
type = 'undefined';
|
|
90
|
-
content = String(value);
|
|
91
|
-
typeView = <Fragment />;
|
|
92
|
-
}
|
|
93
|
-
if (typeof value === 'bigint') {
|
|
94
|
-
type = 'bigint';
|
|
95
|
-
content = `${value}n`;
|
|
96
|
-
}
|
|
97
|
-
if (typeof value === 'string') {
|
|
98
|
-
content = `"${value}"`;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
if (!displayDataTypes) {
|
|
102
|
-
typeView = <Fragment />;
|
|
103
|
-
}
|
|
104
|
-
color = typeMap[type]?.color || '';
|
|
105
|
-
if (content && typeof content === 'string') {
|
|
106
|
-
const valueView = renderValue ? (
|
|
107
|
-
renderValue({
|
|
108
|
-
className: 'w-rjv-value',
|
|
109
|
-
style: { color, ...style },
|
|
110
|
-
type,
|
|
111
|
-
children: content,
|
|
112
|
-
})
|
|
113
|
-
) : (
|
|
114
|
-
<Label color={color} style={style} className="w-rjv-value">
|
|
115
|
-
{content}
|
|
116
|
-
</Label>
|
|
117
|
-
);
|
|
118
|
-
return (
|
|
119
|
-
<Label {...reset}>
|
|
120
|
-
{renderKey}
|
|
121
|
-
<Colon />
|
|
122
|
-
{typeView}
|
|
123
|
-
{valueView}
|
|
124
|
-
</Label>
|
|
125
|
-
);
|
|
126
|
-
}
|
|
127
|
-
return (
|
|
128
|
-
<Label {...reset}>
|
|
129
|
-
{renderKey}
|
|
130
|
-
<Colon />
|
|
131
|
-
{typeView}
|
|
132
|
-
</Label>
|
|
133
|
-
);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
export interface LabelProps extends React.HTMLAttributes<HTMLSpanElement> {
|
|
137
|
-
color?: string;
|
|
138
|
-
fontSize?: number;
|
|
139
|
-
opacity?: number;
|
|
140
|
-
paddingRight?: number;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
export const Label: FC<PropsWithChildren<LabelProps>> = ({
|
|
144
|
-
children,
|
|
145
|
-
color,
|
|
146
|
-
fontSize,
|
|
147
|
-
opacity,
|
|
148
|
-
paddingRight,
|
|
149
|
-
style,
|
|
150
|
-
...reset
|
|
151
|
-
}) => (
|
|
152
|
-
<span style={{ color, fontSize, opacity, paddingRight, ...style }} {...reset}>
|
|
153
|
-
{children}
|
|
154
|
-
</span>
|
|
155
|
-
);
|
|
156
|
-
|
|
157
|
-
export interface TypeProps extends React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement> {
|
|
158
|
-
type: keyof typeof typeMap;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
export const Type: FC<PropsWithChildren<TypeProps>> = (props) => {
|
|
162
|
-
const { type } = props;
|
|
163
|
-
const color = typeMap[type]?.color;
|
|
164
|
-
const label = typeMap[type]?.label;
|
|
165
|
-
if (color && label) {
|
|
166
|
-
return (
|
|
167
|
-
<Label color={color} fontSize={11} opacity={0.8} paddingRight={4} className="w-rjv-type" data-type={type}>
|
|
168
|
-
{label}
|
|
169
|
-
</Label>
|
|
170
|
-
);
|
|
171
|
-
}
|
|
172
|
-
};
|