@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 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.1",
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-arrow' {
2
+ import React from 'react';
3
+ export interface TriangleArrowProps extends React.SVGProps<SVGSVGElement> { }
4
+ export function TriangleArrow(props: TriangleArrowProps): import("react/jsx-runtime").JSX.Element;
5
+ }
@@ -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
- };
@@ -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
- };