Line data Source code
1 : import * as React from 'react';
2 : import * as styles from './json_view.module.css';
3 :
4 : import { JsonViewerState, JsonViewerStateProvider } from './json_view_state.js';
5 : import { JsonValue } from './json_value.js';
6 :
7 : export * from './json_view_state.js';
8 : export * from './json_nested_state.js';
9 : export * from './json_tool_state.js';
10 : export * from './json_arrow_symbol.js';
11 :
12 : export type ShouldExpandNodeInitially = (
13 : isExpanded: boolean,
14 : props: { keyName?: string | number; value?: object; parentValue?: object; keyPath: (number | string)[]; level: number },
15 : ) => boolean;
16 :
17 : /// How the viewer renders values.
18 : /// `json` shows the JSON syntax, `explorer` shows an object explorer without brackets, quotes or colons.
19 : export type JsonViewVariant = 'json' | 'explorer';
20 :
21 : export interface JsonViewProps
22 : extends React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement> {
23 : /// This property contains your input JSON
24 : value: object;
25 : /// Render the JSON syntax or an object explorer @default 'json'
26 : variant?: JsonViewVariant;
27 : /// Define the root node name. @default undefined
28 : keyName?: string | number;
29 : /// Whether sort keys through `String.prototype.localeCompare()` @default false
30 : objectSortKeys?: boolean | ((keyA: string, keyB: string, valueA: object, valueB: object) => number);
31 : /// Set the indent-width for nested objects @default 15
32 : indentWidth?: number;
33 : /// Render arrays incrementally in pages of this size. Set to `0` to render all items. @default 0
34 : arrayPageSize?: number;
35 : /// When set to `true`, `objects` and `arrays` are labeled with size @default true
36 : displayObjectSize?: boolean;
37 : /// The user can copy objects and arrays to clipboard by clicking on the clipboard icon. @default true
38 : enableClipboard?: boolean;
39 : ///
40 : /// When set to true, all nodes will be collapsed by default. Use an integer value to collapse at a specific depth. @default false
41 : /// collapsed takes precedence over shouldExpandNodeInitially.
42 : /// @see {@link shouldExpandNodeInitially} for more details on how the initial expansion works.
43 : ///
44 : collapsed?: boolean | number;
45 : ///
46 : /// Determines whether the node should be expanded on the first render, or you can use collapsed to control the level of expansion (by default, the root is expanded).
47 : /// If both collapsed and shouldExpandNodeInitially are set, the value of collapsed takes precedence.
48 : /// @see {@link collapsed} for more details on how this works.
49 : ///
50 : shouldExpandNodeInitially?: ShouldExpandNodeInitially;
51 : /// Whether to highlight updates. @default true
52 : highlightUpdates?: boolean;
53 : /// Shorten long JSON strings, Set to `0` to disable this feature @default 30
54 : shortenTextAfterLength?: number;
55 : /// When the text exceeds the length, `...` will be displayed. Currently, this `...` can be customized. @default "..."
56 : stringEllipsis?: number;
57 : /// Callback function for when a treeNode is expanded or collapsed
58 : onExpand?: (props: { expand: boolean; value?: object; keyName?: string | number }) => void;
59 : /// Fires event when you copy
60 : onCopied?: (text: string, value?: object) => void;
61 : /// Transform the text before copying to clipboard
62 : beforeCopy?: (
63 : copyText: string,
64 : keyName?: string | number,
65 : value?: object,
66 : parentValue?: object,
67 : expandKey?: string,
68 : keyPath?: (number | string)[],
69 : ) => string;
70 : }
71 :
72 : type JsonViewComponent = React.FC<React.PropsWithRef<JsonViewProps>>;
73 :
74 1 : export const AlwaysExpand: ShouldExpandNodeInitially = () => true;
75 1 : export const AlwaysCollapse = () => false;
76 :
77 1 : export const JsonView: JsonViewComponent = React.memo(React.forwardRef<HTMLDivElement, JsonViewProps>((props, ref) => {
78 1 : const {
79 1 : value,
80 1 : variant,
81 1 : keyName: _keyName,
82 1 : objectSortKeys,
83 1 : indentWidth,
84 1 : arrayPageSize,
85 1 : displayObjectSize,
86 1 : enableClipboard,
87 1 : collapsed,
88 1 : shouldExpandNodeInitially,
89 1 : highlightUpdates: _highlightUpdates,
90 1 : shortenTextAfterLength,
91 1 : stringEllipsis,
92 1 : onExpand,
93 1 : onCopied,
94 1 : beforeCopy,
95 1 : children,
96 1 : className,
97 1 : ...containerProps
98 1 : } = props;
99 : // Collapse nested values below a numeric depth, unless the caller decides explicitly
100 1 : const expandNodeInitially = React.useMemo<ShouldExpandNodeInitially>(() => {
101 1 : if (collapsed === true) {
102 0 : return AlwaysCollapse;
103 1 : } else if (shouldExpandNodeInitially) {
104 0 : return shouldExpandNodeInitially;
105 1 : } else if (typeof collapsed === 'number') {
106 1 : return (_isExpanded, { level }) => level <= collapsed;
107 : } else {
108 1 : return AlwaysExpand;
109 : }
110 1 : }, [collapsed, shouldExpandNodeInitially]);
111 : // Memoize the state to not re-render the whole tree when the parent re-renders
112 1 : const initialState: JsonViewerState = React.useMemo(() => ({
113 1 : value,
114 1 : variant: variant ?? 'json',
115 1 : objectSortKeys,
116 1 : indentWidth: indentWidth ?? 15,
117 1 : arrayPageSize: arrayPageSize ?? 0,
118 1 : shouldExpandNodeInitially: expandNodeInitially,
119 1 : displayObjectSize: displayObjectSize ?? true,
120 1 : collapsed: collapsed ?? false,
121 1 : enableClipboard,
122 1 : shortenTextAfterLength: shortenTextAfterLength ?? 30,
123 1 : stringEllipsis,
124 1 : onCopied,
125 1 : onExpand,
126 1 : beforeCopy,
127 1 : }), [
128 1 : value,
129 1 : variant,
130 1 : objectSortKeys,
131 1 : indentWidth,
132 1 : arrayPageSize,
133 1 : expandNodeInitially,
134 1 : displayObjectSize,
135 1 : collapsed,
136 1 : enableClipboard,
137 1 : shortenTextAfterLength,
138 1 : stringEllipsis,
139 1 : onCopied,
140 1 : onExpand,
141 1 : beforeCopy,
142 1 : ]);
143 1 : return (
144 1 : <JsonViewerStateProvider initialState={initialState}>
145 1 : <div
146 1 : {...containerProps}
147 1 : ref={ref}
148 1 : className={`${styles.viewer}${variant === 'explorer' ? ` ${styles.viewer_explorer}` : ''}${className ? ` ${className}` : ''}`}
149 1 : data-electron-drag-region="false"
150 : >
151 1 : <JsonValue
152 1 : value={value}
153 1 : initialValue={value}
154 1 : level={0}
155 1 : keyName={undefined}
156 1 : keyPath={[]}
157 1 : />
158 1 : {children}
159 1 : </div>
160 1 : </JsonViewerStateProvider>
161 : );
162 1 : })) as unknown as JsonViewComponent;
163 :
164 : export default JsonView;
|