@uiw/react-json-view 1.2.0 → 1.3.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 +66 -3
- package/cjs/copied.d.ts +1 -1
- package/cjs/copied.js +4 -2
- package/cjs/index.d.ts +2 -0
- package/cjs/node.d.ts +2 -0
- package/cjs/node.js +60 -4
- package/cjs/theme/dark.d.ts +1 -0
- package/cjs/theme/dark.js +1 -0
- package/cjs/theme/light.d.ts +1 -0
- package/cjs/theme/light.js +1 -0
- package/cjs/value.d.ts +1 -1
- package/cjs/value.js +5 -2
- package/esm/copied.d.ts +1 -1
- package/esm/copied.js +4 -2
- package/esm/index.d.ts +2 -0
- package/esm/node.d.ts +2 -0
- package/esm/node.js +58 -4
- package/esm/theme/dark.d.ts +1 -0
- package/esm/theme/dark.js +1 -0
- package/esm/theme/light.d.ts +1 -0
- package/esm/theme/light.js +1 -0
- package/esm/value.d.ts +1 -1
- package/esm/value.js +6 -3
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -4,6 +4,7 @@ react-json-view
|
|
|
4
4
|
[](https://github.com/uiwjs/react-json-view/actions/workflows/ci.yml)
|
|
5
5
|
[](https://www.npmjs.com/package/@uiw/react-json-view)
|
|
6
6
|
[](https://github.com/facebook/react/releases)
|
|
7
|
+
[](https://uiwjs.github.io/react-json-view/lcov-report/)
|
|
7
8
|
|
|
8
9
|
A React component for displaying and editing javascript arrays and JSON objects.
|
|
9
10
|
|
|
@@ -22,7 +23,8 @@ A React component for displaying and editing javascript arrays and JSON objects.
|
|
|
22
23
|
📚 Use Typescript to write, better code hints.
|
|
23
24
|
🎨 Support theme customization & [`online editing`](https://uiwjs.github.io/react-json-view/#online-editing-theme) theme
|
|
24
25
|
🌒 Support dark/light mode
|
|
25
|
-
📦 Zero dependencies
|
|
26
|
+
📦 Zero dependencies
|
|
27
|
+
♻️ Whether to highlight updates.
|
|
26
28
|
|
|
27
29
|
## Quick Start
|
|
28
30
|
|
|
@@ -111,6 +113,7 @@ const customTheme = {
|
|
|
111
113
|
'--w-rjv-line-color': '#323232',
|
|
112
114
|
'--w-rjv-arrow-color': 'var(--w-rjv-color)',
|
|
113
115
|
'--w-rjv-info-color': '#656565',
|
|
116
|
+
'--w-rjv-update-color': '#ebcb8b',
|
|
114
117
|
'--w-rjv-copied-color': '#9cdcfe',
|
|
115
118
|
'--w-rjv-copied-success-color': '#28a745',
|
|
116
119
|
|
|
@@ -140,7 +143,7 @@ export default function Demo() {
|
|
|
140
143
|
Online custom style example, please check in the [documentation website](https://uiwjs.github.io/react-json-view/)
|
|
141
144
|
|
|
142
145
|
```tsx mdx:preview:&title=Online Editing Theme
|
|
143
|
-
import React, { useState, useRef } from 'react';
|
|
146
|
+
import React, { useState, useEffect, useRef } from 'react';
|
|
144
147
|
import Colorful from '@uiw/react-color-colorful';
|
|
145
148
|
import JsonView from '@uiw/react-json-view';
|
|
146
149
|
|
|
@@ -172,6 +175,7 @@ const customTheme = {
|
|
|
172
175
|
'--w-rjv-line-color': '#323232',
|
|
173
176
|
'--w-rjv-arrow-color': '#9cdcfe',
|
|
174
177
|
'--w-rjv-info-color': '#656565',
|
|
178
|
+
'--w-rjv-update-color': '#ebcb8b',
|
|
175
179
|
'--w-rjv-copied-color': '#0184a6',
|
|
176
180
|
'--w-rjv-copied-success-color': '#28a745',
|
|
177
181
|
|
|
@@ -197,10 +201,23 @@ export default function Demo() {
|
|
|
197
201
|
setHex(hexa);
|
|
198
202
|
setTheme({ ...theme, [cssvar]: hexa });
|
|
199
203
|
};
|
|
204
|
+
|
|
205
|
+
const [src, setSrc] = useState({ ...object })
|
|
206
|
+
useEffect(() => {
|
|
207
|
+
const loop = () => {
|
|
208
|
+
setSrc(src => ({
|
|
209
|
+
...src,
|
|
210
|
+
timer: src.timer + 1
|
|
211
|
+
}))
|
|
212
|
+
}
|
|
213
|
+
const id = setInterval(loop, 1000)
|
|
214
|
+
return () => clearInterval(id)
|
|
215
|
+
}, []);
|
|
216
|
+
|
|
200
217
|
return (
|
|
201
218
|
<React.Fragment>
|
|
202
219
|
<div style={{ display: 'flex', gap: '1rem' }}>
|
|
203
|
-
<JsonView value={
|
|
220
|
+
<JsonView value={src} keyName="root" style={{ flex: 1, ...theme }} />
|
|
204
221
|
<div>
|
|
205
222
|
<Colorful color={hex} onChange={onChange} />
|
|
206
223
|
<div style={{ display: 'flex', gap: '0.4rem', flexWrap: 'wrap' }}>
|
|
@@ -305,6 +322,50 @@ export default function Demo() {
|
|
|
305
322
|
}
|
|
306
323
|
```
|
|
307
324
|
|
|
325
|
+
## Highlight Updates
|
|
326
|
+
|
|
327
|
+
```tsx mdx:preview
|
|
328
|
+
import React, { useState, useEffect } from 'react';
|
|
329
|
+
import JsonView from '@uiw/react-json-view';
|
|
330
|
+
import { TriangleArrow } from '@uiw/react-json-view/triangle-arrow';
|
|
331
|
+
import { TriangleSolidArrow } from '@uiw/react-json-view/triangle-solid-arrow';
|
|
332
|
+
|
|
333
|
+
const object = {
|
|
334
|
+
string: 'Lorem ipsum dolor sit amet',
|
|
335
|
+
integer: 42,
|
|
336
|
+
timer: 0,
|
|
337
|
+
object: { 'first-child': true, 'second-child': false, 'last-child': null },
|
|
338
|
+
}
|
|
339
|
+
export default function Demo() {
|
|
340
|
+
const [src, setSrc] = useState({ ...object })
|
|
341
|
+
useEffect(() => {
|
|
342
|
+
const loop = () => {
|
|
343
|
+
setSrc(src => ({
|
|
344
|
+
...src,
|
|
345
|
+
timer: src.timer + 1
|
|
346
|
+
}))
|
|
347
|
+
}
|
|
348
|
+
const id = setInterval(loop, 1000)
|
|
349
|
+
return () => clearInterval(id)
|
|
350
|
+
}, []);
|
|
351
|
+
|
|
352
|
+
return (
|
|
353
|
+
<JsonView
|
|
354
|
+
value={src}
|
|
355
|
+
keyName="root"
|
|
356
|
+
style={{
|
|
357
|
+
'--w-rjv-background-color': '#ffffff',
|
|
358
|
+
'--w-rjv-border-left': '1px dashed #ebebeb',
|
|
359
|
+
// ✅ Change default update background color ✅
|
|
360
|
+
'--w-rjv-update-color': '#ff6ffd',
|
|
361
|
+
}}
|
|
362
|
+
/>
|
|
363
|
+
)
|
|
364
|
+
}
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
This feature can be disabled with `highlightUpdates={false}`, and the default color can be changed with `--w-rjv-update-color`.
|
|
368
|
+
|
|
308
369
|
## Modify Icon Style
|
|
309
370
|
|
|
310
371
|
Use built-in default icons.
|
|
@@ -424,6 +485,8 @@ export interface JsonViewProps<T> extends React.DetailedHTMLProps<React.HTMLAttr
|
|
|
424
485
|
keyName?: string | number;
|
|
425
486
|
/** The user can copy objects and arrays to clipboard by clicking on the clipboard icon. @default true */
|
|
426
487
|
enableClipboard?: boolean;
|
|
488
|
+
/** Whether to highlight updates. @default true */
|
|
489
|
+
highlightUpdates?: boolean;
|
|
427
490
|
/** Display for quotes in object-key @default " */
|
|
428
491
|
quotes?: "'" | '"' | '';
|
|
429
492
|
/** When set to true, all nodes will be collapsed by default. Use an integer value to collapse at a particular depth. @default false */
|
package/cjs/copied.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
export interface CopiedProps<T = object> extends React.SVGProps<SVGSVGElement> {
|
|
3
3
|
show?: boolean;
|
|
4
4
|
text?: T;
|
|
5
|
-
onCopied?: (text: string, obj: T) => void;
|
|
5
|
+
onCopied?: (text: string, obj: T | '') => void;
|
|
6
6
|
render?: (props: Omit<CopiedProps<T>, 'render'>) => JSX.Element;
|
|
7
7
|
}
|
|
8
8
|
export declare function Copied<T>(props: CopiedProps<T>): JSX.Element | null;
|
package/cjs/copied.js
CHANGED
|
@@ -10,12 +10,13 @@ var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/sli
|
|
|
10
10
|
var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties"));
|
|
11
11
|
var _react = require("react");
|
|
12
12
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
13
|
-
var _excluded = ["children", "style", "text", "render", "show"];
|
|
13
|
+
var _excluded = ["children", "style", "text", "onCopied", "render", "show"];
|
|
14
14
|
function Copied(props) {
|
|
15
15
|
var children = props.children,
|
|
16
16
|
style = props.style,
|
|
17
17
|
_props$text = props.text,
|
|
18
18
|
text = _props$text === void 0 ? '' : _props$text,
|
|
19
|
+
onCopied = props.onCopied,
|
|
19
20
|
render = props.render,
|
|
20
21
|
show = props.show,
|
|
21
22
|
reset = (0, _objectWithoutProperties2["default"])(props, _excluded);
|
|
@@ -26,7 +27,7 @@ function Copied(props) {
|
|
|
26
27
|
setCopied = _useState2[1];
|
|
27
28
|
var click = function click(event) {
|
|
28
29
|
event.stopPropagation();
|
|
29
|
-
var copyText = JSON.stringify(text, function (key, value) {
|
|
30
|
+
var copyText = JSON.stringify(text || '', function (key, value) {
|
|
30
31
|
if (typeof value === 'bigint') {
|
|
31
32
|
return value.toString();
|
|
32
33
|
}
|
|
@@ -34,6 +35,7 @@ function Copied(props) {
|
|
|
34
35
|
}, 2);
|
|
35
36
|
if (text === Infinity) copyText = Infinity;
|
|
36
37
|
navigator.clipboard.writeText(copyText).then(function () {
|
|
38
|
+
onCopied && onCopied(copyText, text);
|
|
37
39
|
setCopied(true);
|
|
38
40
|
var timer = setTimeout(function () {
|
|
39
41
|
setCopied(false);
|
package/cjs/index.d.ts
CHANGED
|
@@ -17,6 +17,8 @@ export interface JsonViewProps<T extends object> extends React.DetailedHTMLProps
|
|
|
17
17
|
keyName?: string | number;
|
|
18
18
|
/** The user can copy objects and arrays to clipboard by clicking on the clipboard icon. @default true */
|
|
19
19
|
enableClipboard?: boolean;
|
|
20
|
+
/** Whether to highlight updates. @default true */
|
|
21
|
+
highlightUpdates?: boolean;
|
|
20
22
|
/** Display for quotes in object-key @default " */
|
|
21
23
|
quotes?: "'" | '"' | '';
|
|
22
24
|
/** When set to true, all nodes will be collapsed by default. Use an integer value to collapse at a particular depth. @default false */
|
package/cjs/node.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ export interface MetaProps extends LabelProps {
|
|
|
6
6
|
start?: boolean;
|
|
7
7
|
render?: (props: Pick<MetaProps, 'start' | 'isArray' | 'className' | 'children'>) => JSX.Element;
|
|
8
8
|
}
|
|
9
|
+
export declare function usePrevious<T>(value: T): T | undefined;
|
|
9
10
|
export declare function Meta(props: MetaProps): JSX.Element;
|
|
10
11
|
export interface EllipsisProps extends React.HTMLAttributes<HTMLSpanElement> {
|
|
11
12
|
render?: (props: EllipsisProps) => JSX.Element;
|
|
@@ -13,6 +14,7 @@ export interface EllipsisProps extends React.HTMLAttributes<HTMLSpanElement> {
|
|
|
13
14
|
export declare const Ellipsis: FC<PropsWithChildren<EllipsisProps>>;
|
|
14
15
|
export interface SemicolonProps extends LabelProps {
|
|
15
16
|
show?: boolean;
|
|
17
|
+
highlightUpdates?: boolean;
|
|
16
18
|
quotes?: JsonViewProps<object>['quotes'];
|
|
17
19
|
value?: object;
|
|
18
20
|
render?: (props: Omit<SemicolonProps, 'show'> & {}) => JSX.Element;
|
package/cjs/node.js
CHANGED
|
@@ -8,8 +8,9 @@ exports.Ellipsis = exports.CountInfo = void 0;
|
|
|
8
8
|
exports.Meta = Meta;
|
|
9
9
|
exports.RooNode = RooNode;
|
|
10
10
|
exports.Semicolon = void 0;
|
|
11
|
-
|
|
11
|
+
exports.usePrevious = usePrevious;
|
|
12
12
|
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
|
|
13
|
+
var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));
|
|
13
14
|
var _objectSpread2 = _interopRequireDefault(require("@babel/runtime/helpers/objectSpread2"));
|
|
14
15
|
var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties"));
|
|
15
16
|
var _react = require("react");
|
|
@@ -20,8 +21,15 @@ var _copied = require("./copied");
|
|
|
20
21
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
21
22
|
var _excluded = ["isArray", "start", "className", "render"],
|
|
22
23
|
_excluded2 = ["style", "render"],
|
|
23
|
-
_excluded3 = ["children", "render", "color", "value", "className", "show", "quotes"],
|
|
24
|
-
_excluded4 = ["value", "keyName", "displayDataTypes", "components", "displayObjectSize", "enableClipboard", "indentWidth", "collapsed", "level", "keyid", "quotes", "onCopied", "onExpand"];
|
|
24
|
+
_excluded3 = ["children", "render", "color", "value", "className", "show", "highlightUpdates", "quotes"],
|
|
25
|
+
_excluded4 = ["value", "keyName", "className", "displayDataTypes", "components", "displayObjectSize", "enableClipboard", "highlightUpdates", "indentWidth", "collapsed", "level", "keyid", "quotes", "onCopied", "onExpand"];
|
|
26
|
+
function usePrevious(value) {
|
|
27
|
+
var ref = (0, _react.useRef)();
|
|
28
|
+
(0, _react.useEffect)(function () {
|
|
29
|
+
ref.current = value;
|
|
30
|
+
});
|
|
31
|
+
return ref.current;
|
|
32
|
+
}
|
|
25
33
|
function Meta(props) {
|
|
26
34
|
var _props$isArray = props.isArray,
|
|
27
35
|
isArray = _props$isArray === void 0 ? false : _props$isArray,
|
|
@@ -77,8 +85,50 @@ var Semicolon = function Semicolon(_ref2) {
|
|
|
77
85
|
_ref2$className = _ref2.className,
|
|
78
86
|
className = _ref2$className === void 0 ? 'w-rjv-object-key' : _ref2$className,
|
|
79
87
|
show = _ref2.show,
|
|
88
|
+
highlightUpdates = _ref2.highlightUpdates,
|
|
80
89
|
quotes = _ref2.quotes,
|
|
81
90
|
props = (0, _objectWithoutProperties2["default"])(_ref2, _excluded3);
|
|
91
|
+
var prevValue = usePrevious(value);
|
|
92
|
+
var highlightContainer = (0, _react.useRef)(null);
|
|
93
|
+
var isHighlight = (0, _react.useMemo)(function () {
|
|
94
|
+
if (!highlightUpdates || prevValue === undefined) return false;
|
|
95
|
+
// highlight if value type changed
|
|
96
|
+
if ((0, _typeof2["default"])(value) !== (0, _typeof2["default"])(prevValue)) {
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
if (typeof value === 'number') {
|
|
100
|
+
// notice: NaN !== NaN
|
|
101
|
+
if (isNaN(value) && isNaN(prevValue)) return false;
|
|
102
|
+
return value !== prevValue;
|
|
103
|
+
}
|
|
104
|
+
// highlight if isArray changed
|
|
105
|
+
if (Array.isArray(value) !== Array.isArray(prevValue)) {
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
// not highlight object/function
|
|
109
|
+
// deep compare they will be slow
|
|
110
|
+
if ((0, _typeof2["default"])(value) === 'object' || typeof value === 'function') {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// highlight if not equal
|
|
115
|
+
if (value !== prevValue) {
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
return false;
|
|
119
|
+
}, [highlightUpdates, value]);
|
|
120
|
+
(0, _react.useEffect)(function () {
|
|
121
|
+
if (highlightContainer.current && isHighlight && 'animate' in highlightContainer.current) {
|
|
122
|
+
highlightContainer.current.animate([{
|
|
123
|
+
backgroundColor: 'var(--w-rjv-update-color, #ebcb8b)'
|
|
124
|
+
}, {
|
|
125
|
+
backgroundColor: ''
|
|
126
|
+
}], {
|
|
127
|
+
duration: 1000,
|
|
128
|
+
easing: 'ease-in'
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
}, [isHighlight, value]);
|
|
82
132
|
var content = show ? "".concat(quotes).concat(children).concat(quotes) : children;
|
|
83
133
|
if (render) return render((0, _objectSpread2["default"])((0, _objectSpread2["default"])({
|
|
84
134
|
className: className
|
|
@@ -93,6 +143,7 @@ var Semicolon = function Semicolon(_ref2) {
|
|
|
93
143
|
className: className,
|
|
94
144
|
color: color
|
|
95
145
|
}, props), {}, {
|
|
146
|
+
ref: highlightContainer,
|
|
96
147
|
children: content
|
|
97
148
|
}));
|
|
98
149
|
};
|
|
@@ -115,6 +166,7 @@ function RooNode(props) {
|
|
|
115
166
|
var _props$value = props.value,
|
|
116
167
|
value = _props$value === void 0 ? {} : _props$value,
|
|
117
168
|
keyName = props.keyName,
|
|
169
|
+
className = props.className,
|
|
118
170
|
_props$displayDataTyp = props.displayDataTypes,
|
|
119
171
|
displayDataTypes = _props$displayDataTyp === void 0 ? true : _props$displayDataTyp,
|
|
120
172
|
_props$components = props.components,
|
|
@@ -123,6 +175,8 @@ function RooNode(props) {
|
|
|
123
175
|
displayObjectSize = _props$displayObjectS === void 0 ? true : _props$displayObjectS,
|
|
124
176
|
_props$enableClipboar = props.enableClipboard,
|
|
125
177
|
enableClipboard = _props$enableClipboar === void 0 ? true : _props$enableClipboar,
|
|
178
|
+
_props$highlightUpdat = props.highlightUpdates,
|
|
179
|
+
highlightUpdates = _props$highlightUpdat === void 0 ? true : _props$highlightUpdat,
|
|
126
180
|
_props$indentWidth = props.indentWidth,
|
|
127
181
|
indentWidth = _props$indentWidth === void 0 ? 15 : _props$indentWidth,
|
|
128
182
|
collapsed = props.collapsed,
|
|
@@ -161,6 +215,7 @@ function RooNode(props) {
|
|
|
161
215
|
displayDataTypes: displayDataTypes,
|
|
162
216
|
displayObjectSize: displayObjectSize,
|
|
163
217
|
enableClipboard: enableClipboard,
|
|
218
|
+
highlightUpdates: highlightUpdates,
|
|
164
219
|
onCopied: onCopied,
|
|
165
220
|
onExpand: onExpand,
|
|
166
221
|
collapsed: collapsed,
|
|
@@ -206,7 +261,7 @@ function RooNode(props) {
|
|
|
206
261
|
};
|
|
207
262
|
}
|
|
208
263
|
return /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", (0, _objectSpread2["default"])((0, _objectSpread2["default"])((0, _objectSpread2["default"])({}, reset), {}, {
|
|
209
|
-
className: "w-rjv-inner"
|
|
264
|
+
className: "".concat(className, " w-rjv-inner")
|
|
210
265
|
}, eventProps), {}, {
|
|
211
266
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_value.Line, {
|
|
212
267
|
style: {
|
|
@@ -246,6 +301,7 @@ function RooNode(props) {
|
|
|
246
301
|
var renderKey = /*#__PURE__*/(0, _jsxRuntime.jsx)(Semicolon, {
|
|
247
302
|
value: item,
|
|
248
303
|
quotes: quotes,
|
|
304
|
+
highlightUpdates: highlightUpdates,
|
|
249
305
|
render: components.objectKey,
|
|
250
306
|
color: typeof key === 'number' ? _value.typeMap['number'].color : '',
|
|
251
307
|
show: typeof key === 'string',
|
package/cjs/theme/dark.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export declare const darkTheme: {
|
|
|
5
5
|
'--w-rjv-line-color': string;
|
|
6
6
|
'--w-rjv-arrow-color': string;
|
|
7
7
|
'--w-rjv-info-color': string;
|
|
8
|
+
'--w-rjv-update-color': string;
|
|
8
9
|
'--w-rjv-copied-color': string;
|
|
9
10
|
'--w-rjv-copied-success-color': string;
|
|
10
11
|
'--w-rjv-curlybraces-color': string;
|
package/cjs/theme/dark.js
CHANGED
|
@@ -11,6 +11,7 @@ var darkTheme = {
|
|
|
11
11
|
'--w-rjv-line-color': '#323232',
|
|
12
12
|
'--w-rjv-arrow-color': 'var(--w-rjv-color)',
|
|
13
13
|
'--w-rjv-info-color': '#656565',
|
|
14
|
+
'--w-rjv-update-color': '#ebcb8b',
|
|
14
15
|
'--w-rjv-copied-color': '#0184a6',
|
|
15
16
|
'--w-rjv-copied-success-color': '#28a745',
|
|
16
17
|
'--w-rjv-curlybraces-color': '#1896b6',
|
package/cjs/theme/light.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export declare const lightTheme: {
|
|
|
5
5
|
'--w-rjv-line-color': string;
|
|
6
6
|
'--w-rjv-arrow-color': string;
|
|
7
7
|
'--w-rjv-info-color': string;
|
|
8
|
+
'--w-rjv-update-color': string;
|
|
8
9
|
'--w-rjv-copied-color': string;
|
|
9
10
|
'--w-rjv-copied-success-color': string;
|
|
10
11
|
'--w-rjv-curlybraces-color': string;
|
package/cjs/theme/light.js
CHANGED
|
@@ -11,6 +11,7 @@ var lightTheme = {
|
|
|
11
11
|
'--w-rjv-line-color': '#ebebeb',
|
|
12
12
|
'--w-rjv-arrow-color': 'var(--w-rjv-color)',
|
|
13
13
|
'--w-rjv-info-color': '#0000004d',
|
|
14
|
+
'--w-rjv-update-color': '#ebcb8b',
|
|
14
15
|
'--w-rjv-copied-color': '#002b36',
|
|
15
16
|
'--w-rjv-copied-success-color': '#28a745',
|
|
16
17
|
'--w-rjv-curlybraces-color': '#236a7c',
|
package/cjs/value.d.ts
CHANGED
|
@@ -60,7 +60,7 @@ export interface LabelProps extends React.HTMLAttributes<HTMLSpanElement> {
|
|
|
60
60
|
opacity?: number;
|
|
61
61
|
paddingRight?: number;
|
|
62
62
|
}
|
|
63
|
-
export declare const Label:
|
|
63
|
+
export declare const Label: import("react").ForwardRefExoticComponent<LabelProps & import("react").RefAttributes<HTMLSpanElement>>;
|
|
64
64
|
export interface TypeProps extends React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement> {
|
|
65
65
|
type: keyof typeof typeMap;
|
|
66
66
|
}
|
package/cjs/value.js
CHANGED
|
@@ -174,6 +174,7 @@ function ValueView(props) {
|
|
|
174
174
|
});
|
|
175
175
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(Line, (0, _objectSpread2["default"])((0, _objectSpread2["default"])({}, eventProps), {}, {
|
|
176
176
|
children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(Label, (0, _objectSpread2["default"])((0, _objectSpread2["default"])({}, reset), {}, {
|
|
177
|
+
ref: null,
|
|
177
178
|
children: [renderKey, /*#__PURE__*/(0, _jsxRuntime.jsx)(Colon, {}), typeView, valueView, tools]
|
|
178
179
|
}))
|
|
179
180
|
}));
|
|
@@ -193,11 +194,12 @@ function ValueView(props) {
|
|
|
193
194
|
});
|
|
194
195
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)(Line, (0, _objectSpread2["default"])((0, _objectSpread2["default"])({}, eventProps), {}, {
|
|
195
196
|
children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(Label, (0, _objectSpread2["default"])((0, _objectSpread2["default"])({}, reset), {}, {
|
|
197
|
+
ref: null,
|
|
196
198
|
children: [renderKey, /*#__PURE__*/(0, _jsxRuntime.jsx)(Colon, {}), typeView, empty, tools]
|
|
197
199
|
}))
|
|
198
200
|
}));
|
|
199
201
|
}
|
|
200
|
-
var Label = function
|
|
202
|
+
var Label = /*#__PURE__*/(0, _react.forwardRef)(function (_ref2, ref) {
|
|
201
203
|
var children = _ref2.children,
|
|
202
204
|
color = _ref2.color,
|
|
203
205
|
fontSize = _ref2.fontSize,
|
|
@@ -213,9 +215,10 @@ var Label = function Label(_ref2) {
|
|
|
213
215
|
paddingRight: paddingRight
|
|
214
216
|
}, style)
|
|
215
217
|
}, reset), {}, {
|
|
218
|
+
ref: ref,
|
|
216
219
|
children: children
|
|
217
220
|
}));
|
|
218
|
-
};
|
|
221
|
+
});
|
|
219
222
|
exports.Label = Label;
|
|
220
223
|
var Type = function Type(props) {
|
|
221
224
|
var _typeMap$type2, _typeMap$type3;
|
package/esm/copied.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
export interface CopiedProps<T = object> extends React.SVGProps<SVGSVGElement> {
|
|
3
3
|
show?: boolean;
|
|
4
4
|
text?: T;
|
|
5
|
-
onCopied?: (text: string, obj: T) => void;
|
|
5
|
+
onCopied?: (text: string, obj: T | '') => void;
|
|
6
6
|
render?: (props: Omit<CopiedProps<T>, 'render'>) => JSX.Element;
|
|
7
7
|
}
|
|
8
8
|
export declare function Copied<T>(props: CopiedProps<T>): JSX.Element | null;
|
package/esm/copied.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import _extends from "@babel/runtime/helpers/extends";
|
|
2
2
|
import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/objectWithoutPropertiesLoose";
|
|
3
|
-
var _excluded = ["children", "style", "text", "render", "show"];
|
|
3
|
+
var _excluded = ["children", "style", "text", "onCopied", "render", "show"];
|
|
4
4
|
import { useState } from 'react';
|
|
5
5
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
6
6
|
export function Copied(props) {
|
|
7
7
|
var {
|
|
8
8
|
style,
|
|
9
9
|
text = '',
|
|
10
|
+
onCopied,
|
|
10
11
|
render,
|
|
11
12
|
show
|
|
12
13
|
} = props,
|
|
@@ -15,7 +16,7 @@ export function Copied(props) {
|
|
|
15
16
|
var [copied, setCopied] = useState(false);
|
|
16
17
|
var click = event => {
|
|
17
18
|
event.stopPropagation();
|
|
18
|
-
var copyText = JSON.stringify(text, (key, value) => {
|
|
19
|
+
var copyText = JSON.stringify(text || '', (key, value) => {
|
|
19
20
|
if (typeof value === 'bigint') {
|
|
20
21
|
return value.toString();
|
|
21
22
|
}
|
|
@@ -23,6 +24,7 @@ export function Copied(props) {
|
|
|
23
24
|
}, 2);
|
|
24
25
|
if (text === Infinity) copyText = Infinity;
|
|
25
26
|
navigator.clipboard.writeText(copyText).then(() => {
|
|
27
|
+
onCopied && onCopied(copyText, text);
|
|
26
28
|
setCopied(true);
|
|
27
29
|
var timer = setTimeout(() => {
|
|
28
30
|
setCopied(false);
|
package/esm/index.d.ts
CHANGED
|
@@ -17,6 +17,8 @@ export interface JsonViewProps<T extends object> extends React.DetailedHTMLProps
|
|
|
17
17
|
keyName?: string | number;
|
|
18
18
|
/** The user can copy objects and arrays to clipboard by clicking on the clipboard icon. @default true */
|
|
19
19
|
enableClipboard?: boolean;
|
|
20
|
+
/** Whether to highlight updates. @default true */
|
|
21
|
+
highlightUpdates?: boolean;
|
|
20
22
|
/** Display for quotes in object-key @default " */
|
|
21
23
|
quotes?: "'" | '"' | '';
|
|
22
24
|
/** When set to true, all nodes will be collapsed by default. Use an integer value to collapse at a particular depth. @default false */
|
package/esm/node.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ export interface MetaProps extends LabelProps {
|
|
|
6
6
|
start?: boolean;
|
|
7
7
|
render?: (props: Pick<MetaProps, 'start' | 'isArray' | 'className' | 'children'>) => JSX.Element;
|
|
8
8
|
}
|
|
9
|
+
export declare function usePrevious<T>(value: T): T | undefined;
|
|
9
10
|
export declare function Meta(props: MetaProps): JSX.Element;
|
|
10
11
|
export interface EllipsisProps extends React.HTMLAttributes<HTMLSpanElement> {
|
|
11
12
|
render?: (props: EllipsisProps) => JSX.Element;
|
|
@@ -13,6 +14,7 @@ export interface EllipsisProps extends React.HTMLAttributes<HTMLSpanElement> {
|
|
|
13
14
|
export declare const Ellipsis: FC<PropsWithChildren<EllipsisProps>>;
|
|
14
15
|
export interface SemicolonProps extends LabelProps {
|
|
15
16
|
show?: boolean;
|
|
17
|
+
highlightUpdates?: boolean;
|
|
16
18
|
quotes?: JsonViewProps<object>['quotes'];
|
|
17
19
|
value?: object;
|
|
18
20
|
render?: (props: Omit<SemicolonProps, 'show'> & {}) => JSX.Element;
|
package/esm/node.js
CHANGED
|
@@ -2,15 +2,22 @@ import _extends from "@babel/runtime/helpers/extends";
|
|
|
2
2
|
import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/objectWithoutPropertiesLoose";
|
|
3
3
|
var _excluded = ["isArray", "start", "className", "render"],
|
|
4
4
|
_excluded2 = ["style", "render"],
|
|
5
|
-
_excluded3 = ["children", "render", "color", "value", "className", "show", "quotes"],
|
|
6
|
-
_excluded4 = ["value", "keyName", "displayDataTypes", "components", "displayObjectSize", "enableClipboard", "indentWidth", "collapsed", "level", "keyid", "quotes", "onCopied", "onExpand"];
|
|
7
|
-
import { Fragment, useId, cloneElement, useState } from 'react';
|
|
5
|
+
_excluded3 = ["children", "render", "color", "value", "className", "show", "highlightUpdates", "quotes"],
|
|
6
|
+
_excluded4 = ["value", "keyName", "className", "displayDataTypes", "components", "displayObjectSize", "enableClipboard", "highlightUpdates", "indentWidth", "collapsed", "level", "keyid", "quotes", "onCopied", "onExpand"];
|
|
7
|
+
import { Fragment, useId, cloneElement, useState, useMemo, useRef, useEffect } from 'react';
|
|
8
8
|
import { ValueView, Colon, Label, Line, typeMap } from './value';
|
|
9
9
|
import { TriangleArrow } from './arrow/TriangleArrow';
|
|
10
10
|
import { useExpandsStatus, store } from './store';
|
|
11
11
|
import { Copied } from './copied';
|
|
12
12
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
13
13
|
import { jsxs as _jsxs } from "react/jsx-runtime";
|
|
14
|
+
export function usePrevious(value) {
|
|
15
|
+
var ref = useRef();
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
ref.current = value;
|
|
18
|
+
});
|
|
19
|
+
return ref.current;
|
|
20
|
+
}
|
|
14
21
|
export function Meta(props) {
|
|
15
22
|
var {
|
|
16
23
|
isArray = false,
|
|
@@ -67,9 +74,51 @@ export var Semicolon = _ref2 => {
|
|
|
67
74
|
value,
|
|
68
75
|
className = 'w-rjv-object-key',
|
|
69
76
|
show,
|
|
77
|
+
highlightUpdates,
|
|
70
78
|
quotes
|
|
71
79
|
} = _ref2,
|
|
72
80
|
props = _objectWithoutPropertiesLoose(_ref2, _excluded3);
|
|
81
|
+
var prevValue = usePrevious(value);
|
|
82
|
+
var highlightContainer = useRef(null);
|
|
83
|
+
var isHighlight = useMemo(() => {
|
|
84
|
+
if (!highlightUpdates || prevValue === undefined) return false;
|
|
85
|
+
// highlight if value type changed
|
|
86
|
+
if (typeof value !== typeof prevValue) {
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
if (typeof value === 'number') {
|
|
90
|
+
// notice: NaN !== NaN
|
|
91
|
+
if (isNaN(value) && isNaN(prevValue)) return false;
|
|
92
|
+
return value !== prevValue;
|
|
93
|
+
}
|
|
94
|
+
// highlight if isArray changed
|
|
95
|
+
if (Array.isArray(value) !== Array.isArray(prevValue)) {
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
// not highlight object/function
|
|
99
|
+
// deep compare they will be slow
|
|
100
|
+
if (typeof value === 'object' || typeof value === 'function') {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// highlight if not equal
|
|
105
|
+
if (value !== prevValue) {
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
return false;
|
|
109
|
+
}, [highlightUpdates, value]);
|
|
110
|
+
useEffect(() => {
|
|
111
|
+
if (highlightContainer.current && isHighlight && 'animate' in highlightContainer.current) {
|
|
112
|
+
highlightContainer.current.animate([{
|
|
113
|
+
backgroundColor: 'var(--w-rjv-update-color, #ebcb8b)'
|
|
114
|
+
}, {
|
|
115
|
+
backgroundColor: ''
|
|
116
|
+
}], {
|
|
117
|
+
duration: 1000,
|
|
118
|
+
easing: 'ease-in'
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
}, [isHighlight, value]);
|
|
73
122
|
var content = show ? "" + quotes + children + quotes : children;
|
|
74
123
|
if (render) return render(_extends({
|
|
75
124
|
className
|
|
@@ -84,6 +133,7 @@ export var Semicolon = _ref2 => {
|
|
|
84
133
|
className: className,
|
|
85
134
|
color: color
|
|
86
135
|
}, props, {
|
|
136
|
+
ref: highlightContainer,
|
|
87
137
|
children: content
|
|
88
138
|
}));
|
|
89
139
|
};
|
|
@@ -106,10 +156,12 @@ export function RooNode(props) {
|
|
|
106
156
|
var {
|
|
107
157
|
value = {},
|
|
108
158
|
keyName,
|
|
159
|
+
className,
|
|
109
160
|
displayDataTypes = true,
|
|
110
161
|
components = {},
|
|
111
162
|
displayObjectSize = true,
|
|
112
163
|
enableClipboard = true,
|
|
164
|
+
highlightUpdates = true,
|
|
113
165
|
indentWidth = 15,
|
|
114
166
|
collapsed,
|
|
115
167
|
level = 1,
|
|
@@ -143,6 +195,7 @@ export function RooNode(props) {
|
|
|
143
195
|
displayDataTypes,
|
|
144
196
|
displayObjectSize,
|
|
145
197
|
enableClipboard,
|
|
198
|
+
highlightUpdates,
|
|
146
199
|
onCopied,
|
|
147
200
|
onExpand,
|
|
148
201
|
collapsed,
|
|
@@ -181,7 +234,7 @@ export function RooNode(props) {
|
|
|
181
234
|
eventProps.onMouseLeave = () => setShowTools(false);
|
|
182
235
|
}
|
|
183
236
|
return /*#__PURE__*/_jsxs("div", _extends({}, reset, {
|
|
184
|
-
className: "w-rjv-inner"
|
|
237
|
+
className: className + " w-rjv-inner"
|
|
185
238
|
}, eventProps, {
|
|
186
239
|
children: [/*#__PURE__*/_jsxs(Line, {
|
|
187
240
|
style: {
|
|
@@ -221,6 +274,7 @@ export function RooNode(props) {
|
|
|
221
274
|
var renderKey = /*#__PURE__*/_jsx(Semicolon, {
|
|
222
275
|
value: item,
|
|
223
276
|
quotes: quotes,
|
|
277
|
+
highlightUpdates: highlightUpdates,
|
|
224
278
|
render: components.objectKey,
|
|
225
279
|
color: typeof key === 'number' ? typeMap['number'].color : '',
|
|
226
280
|
show: typeof key === 'string',
|
package/esm/theme/dark.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export declare const darkTheme: {
|
|
|
5
5
|
'--w-rjv-line-color': string;
|
|
6
6
|
'--w-rjv-arrow-color': string;
|
|
7
7
|
'--w-rjv-info-color': string;
|
|
8
|
+
'--w-rjv-update-color': string;
|
|
8
9
|
'--w-rjv-copied-color': string;
|
|
9
10
|
'--w-rjv-copied-success-color': string;
|
|
10
11
|
'--w-rjv-curlybraces-color': string;
|
package/esm/theme/dark.js
CHANGED
|
@@ -5,6 +5,7 @@ export var darkTheme = {
|
|
|
5
5
|
'--w-rjv-line-color': '#323232',
|
|
6
6
|
'--w-rjv-arrow-color': 'var(--w-rjv-color)',
|
|
7
7
|
'--w-rjv-info-color': '#656565',
|
|
8
|
+
'--w-rjv-update-color': '#ebcb8b',
|
|
8
9
|
'--w-rjv-copied-color': '#0184a6',
|
|
9
10
|
'--w-rjv-copied-success-color': '#28a745',
|
|
10
11
|
'--w-rjv-curlybraces-color': '#1896b6',
|
package/esm/theme/light.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export declare const lightTheme: {
|
|
|
5
5
|
'--w-rjv-line-color': string;
|
|
6
6
|
'--w-rjv-arrow-color': string;
|
|
7
7
|
'--w-rjv-info-color': string;
|
|
8
|
+
'--w-rjv-update-color': string;
|
|
8
9
|
'--w-rjv-copied-color': string;
|
|
9
10
|
'--w-rjv-copied-success-color': string;
|
|
10
11
|
'--w-rjv-curlybraces-color': string;
|
package/esm/theme/light.js
CHANGED
|
@@ -5,6 +5,7 @@ export var lightTheme = {
|
|
|
5
5
|
'--w-rjv-line-color': '#ebebeb',
|
|
6
6
|
'--w-rjv-arrow-color': 'var(--w-rjv-color)',
|
|
7
7
|
'--w-rjv-info-color': '#0000004d',
|
|
8
|
+
'--w-rjv-update-color': '#ebcb8b',
|
|
8
9
|
'--w-rjv-copied-color': '#002b36',
|
|
9
10
|
'--w-rjv-copied-success-color': '#28a745',
|
|
10
11
|
'--w-rjv-curlybraces-color': '#236a7c',
|
package/esm/value.d.ts
CHANGED
|
@@ -60,7 +60,7 @@ export interface LabelProps extends React.HTMLAttributes<HTMLSpanElement> {
|
|
|
60
60
|
opacity?: number;
|
|
61
61
|
paddingRight?: number;
|
|
62
62
|
}
|
|
63
|
-
export declare const Label:
|
|
63
|
+
export declare const Label: import("react").ForwardRefExoticComponent<LabelProps & import("react").RefAttributes<HTMLSpanElement>>;
|
|
64
64
|
export interface TypeProps extends React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement> {
|
|
65
65
|
type: keyof typeof typeMap;
|
|
66
66
|
}
|
package/esm/value.js
CHANGED
|
@@ -3,7 +3,7 @@ import _extends from "@babel/runtime/helpers/extends";
|
|
|
3
3
|
var _excluded = ["children", "style"],
|
|
4
4
|
_excluded2 = ["value", "keyName", "indentWidth", "renderKey", "renderValue", "renderBraces", "enableClipboard", "displayObjectSize", "displayDataTypes"],
|
|
5
5
|
_excluded3 = ["children", "color", "fontSize", "opacity", "paddingRight", "style"];
|
|
6
|
-
import { Fragment, useState } from 'react';
|
|
6
|
+
import { Fragment, forwardRef, useState } from 'react';
|
|
7
7
|
import { Meta, CountInfo } from './node';
|
|
8
8
|
import { Copied } from './copied';
|
|
9
9
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
@@ -152,6 +152,7 @@ export function ValueView(props) {
|
|
|
152
152
|
});
|
|
153
153
|
return /*#__PURE__*/_jsx(Line, _extends({}, eventProps, {
|
|
154
154
|
children: /*#__PURE__*/_jsxs(Label, _extends({}, reset, {
|
|
155
|
+
ref: null,
|
|
155
156
|
children: [renderKey, /*#__PURE__*/_jsx(Colon, {}), typeView, valueView, tools]
|
|
156
157
|
}))
|
|
157
158
|
}));
|
|
@@ -171,11 +172,12 @@ export function ValueView(props) {
|
|
|
171
172
|
});
|
|
172
173
|
return /*#__PURE__*/_jsx(Line, _extends({}, eventProps, {
|
|
173
174
|
children: /*#__PURE__*/_jsxs(Label, _extends({}, reset, {
|
|
175
|
+
ref: null,
|
|
174
176
|
children: [renderKey, /*#__PURE__*/_jsx(Colon, {}), typeView, empty, tools]
|
|
175
177
|
}))
|
|
176
178
|
}));
|
|
177
179
|
}
|
|
178
|
-
export var Label = _ref2 => {
|
|
180
|
+
export var Label = /*#__PURE__*/forwardRef((_ref2, ref) => {
|
|
179
181
|
var {
|
|
180
182
|
children,
|
|
181
183
|
color,
|
|
@@ -193,9 +195,10 @@ export var Label = _ref2 => {
|
|
|
193
195
|
paddingRight
|
|
194
196
|
}, style)
|
|
195
197
|
}, reset, {
|
|
198
|
+
ref: ref,
|
|
196
199
|
children: children
|
|
197
200
|
}));
|
|
198
|
-
};
|
|
201
|
+
});
|
|
199
202
|
export var Type = props => {
|
|
200
203
|
var _typeMap$type2, _typeMap$type3;
|
|
201
204
|
var {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uiw/react-json-view",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "JSON viewer for react.",
|
|
5
5
|
"main": "cjs/index.js",
|
|
6
6
|
"module": "esm/index.js",
|
|
@@ -74,7 +74,10 @@
|
|
|
74
74
|
"treeview"
|
|
75
75
|
],
|
|
76
76
|
"devDependencies": {
|
|
77
|
+
"@testing-library/react": "^14.0.0",
|
|
78
|
+
"@types/react-test-renderer": "^18.0.0",
|
|
77
79
|
"react": "^18.2.0",
|
|
78
|
-
"react-dom": "^18.2.0"
|
|
80
|
+
"react-dom": "^18.2.0",
|
|
81
|
+
"react-test-renderer": "^18.2.0"
|
|
79
82
|
}
|
|
80
83
|
}
|