@uiw/react-json-view 0.0.1 → 1.0.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/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
- };
@@ -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
- };