@gfazioli/mantine-json-tree 2.0.1 → 2.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 +23 -2
- package/dist/cjs/JsonTree.cjs +298 -175
- package/dist/cjs/JsonTree.cjs.map +1 -1
- package/dist/cjs/JsonTree.module.css.cjs +1 -1
- package/dist/cjs/JsonTreeMediaVariables.cjs +38 -0
- package/dist/cjs/JsonTreeMediaVariables.cjs.map +1 -0
- package/dist/cjs/lib/utils.cjs +15 -0
- package/dist/cjs/lib/utils.cjs.map +1 -1
- package/dist/esm/JsonTree.mjs +301 -178
- package/dist/esm/JsonTree.mjs.map +1 -1
- package/dist/esm/JsonTree.module.css.mjs +1 -1
- package/dist/esm/JsonTreeMediaVariables.mjs +36 -0
- package/dist/esm/JsonTreeMediaVariables.mjs.map +1 -0
- package/dist/esm/lib/utils.mjs +15 -1
- package/dist/esm/lib/utils.mjs.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/styles.layer.css +1 -1
- package/dist/types/JsonTree.d.ts +22 -6
- package/dist/types/JsonTreeMediaVariables.d.ts +8 -0
- package/dist/types/index.d.mts +1 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/lib/utils.d.ts +4 -0
- package/package.json +12 -5
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Mantine Json Tree Component
|
|
2
2
|
|
|
3
|
-
<img alt="Mantine Json Tree" src="https://github.com/gfazioli/mantine-json-tree/blob/master/logo.
|
|
3
|
+
<img alt="Mantine Json Tree" src="https://github.com/gfazioli/mantine-json-tree/blob/master/logo.jpeg" />
|
|
4
4
|
|
|
5
5
|
<div align="center">
|
|
6
6
|
|
|
@@ -19,10 +19,31 @@
|
|
|
19
19
|
|
|
20
20
|
This component is created on top of the [Mantine](https://mantine.dev/) library.
|
|
21
21
|
|
|
22
|
-
[Mantine JsonTree](https://gfazioli.github.io/mantine-json-tree) provides a structured, interactive view of heterogeneous data—strings, numbers, booleans, nulls, objects, arrays, and even functions—organized as a collapsible tree. Developers can control initial expansion, show visual indent guides, and customize expand/collapse controls with arbitrary React nodes (e.g., emojis or styled icons) to match their design system. For function values, the component offers flexible rendering modes: show the function signature as text, hide functions entirely, or inspect them as objects when needed.
|
|
22
|
+
[Mantine JsonTree](https://gfazioli.github.io/mantine-json-tree) provides a structured, interactive view of heterogeneous data—strings, numbers, booleans, nulls, objects, arrays, and even functions—organized as a collapsible tree. Developers can control initial expansion, show visual indent guides, and customize expand/collapse controls with arbitrary React nodes (e.g., emojis or styled icons) to match their design system. For function values, the component offers flexible rendering modes: show the function signature as text, hide functions entirely, or inspect them as objects when needed.
|
|
23
23
|
|
|
24
24
|
Wrapped with Mantine layout primitives like Paper, Stack, and SimpleGrid, JsonTree integrates cleanly into dashboards, developer tools, and documentation pages where readable, navigable data visualization is essential.
|
|
25
25
|
|
|
26
|
+
## Features
|
|
27
|
+
|
|
28
|
+
- Interactive collapsible tree view for any JSON-serializable data
|
|
29
|
+
- Syntax highlighting with customizable colors for 16+ data types (strings, numbers, booleans, null, Date, RegExp, Map, Set, BigInt, Symbol, React elements, etc.)
|
|
30
|
+
- Dark mode support with automatic color adaptation
|
|
31
|
+
- Copy-to-clipboard on individual nodes
|
|
32
|
+
- Keyboard navigation (arrow keys, Space to expand, Ctrl+C to copy)
|
|
33
|
+
- Configurable expansion depth with expand/collapse all controls
|
|
34
|
+
- Controlled expand/collapse state with `expanded` and `onExpandedChange` props
|
|
35
|
+
- Line numbers display
|
|
36
|
+
- Visual indent guides with rotating color palette
|
|
37
|
+
- Path tooltip on hover showing full JSON path
|
|
38
|
+
- Max height with scrollable container
|
|
39
|
+
- Sticky header support with configurable offset
|
|
40
|
+
- Function display modes: as-string, hide, or as-object introspection
|
|
41
|
+
- Responsive font size via Mantine breakpoint objects (CSS-native, no re-renders)
|
|
42
|
+
- Full Mantine Styles API support with 13 style selectors and 25+ CSS variables
|
|
43
|
+
- Custom icons for expand/collapse and copy controls
|
|
44
|
+
- Item count badges for objects and arrays
|
|
45
|
+
- `onExpand`, `onCollapse`, `onNodeClick`, and `onCopy` callbacks
|
|
46
|
+
|
|
26
47
|
> [!note]
|
|
27
48
|
>
|
|
28
49
|
> → [Demo and Documentation](https://gfazioli.github.io/mantine-json-tree/) → [Youtube Video](https://www.youtube.com/playlist?list=PL85tTROKkZrWyqCcmNCdWajpx05-cTal4) → [More Mantine Components](https://mantine-extensions.vercel.app/)
|
package/dist/cjs/JsonTree.cjs
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
var React = require('react');
|
|
5
5
|
var iconsReact = require('@tabler/icons-react');
|
|
6
6
|
var core = require('@mantine/core');
|
|
7
|
+
var JsonTreeMediaVariables = require('./JsonTreeMediaVariables.cjs');
|
|
7
8
|
var utils = require('./lib/utils.cjs');
|
|
8
9
|
var JsonTree_module = require('./JsonTree.module.css.cjs');
|
|
9
10
|
|
|
@@ -14,18 +15,210 @@ const defaultProps = {
|
|
|
14
15
|
showItemsCount: false,
|
|
15
16
|
withCopyToClipboard: false,
|
|
16
17
|
showIndentGuides: false,
|
|
18
|
+
showLineNumbers: false,
|
|
19
|
+
showPathOnHover: false,
|
|
17
20
|
stickyHeader: false,
|
|
18
21
|
displayFunctions: "as-string",
|
|
19
22
|
expandAllControlIcon: /* @__PURE__ */ React.createElement(iconsReact.IconLibraryPlus, { size: 16 }),
|
|
20
23
|
collapseAllControlIcon: /* @__PURE__ */ React.createElement(iconsReact.IconLibraryMinus, { size: 16 }),
|
|
21
24
|
copyToClipboardIcon: /* @__PURE__ */ React.createElement(iconsReact.IconCopy, { size: 12 })
|
|
22
25
|
};
|
|
26
|
+
function renderJSONNode({ node, expanded, hasChildren, elementProps, tree }, props, ctx, onNodeClick) {
|
|
27
|
+
const {
|
|
28
|
+
getStyles,
|
|
29
|
+
copyToClipboardIcon,
|
|
30
|
+
expandControlIcon,
|
|
31
|
+
collapseControlIcon,
|
|
32
|
+
onExpand,
|
|
33
|
+
onCollapse,
|
|
34
|
+
onExpandedChange
|
|
35
|
+
} = ctx;
|
|
36
|
+
const jsonNode = node;
|
|
37
|
+
const {
|
|
38
|
+
type,
|
|
39
|
+
value,
|
|
40
|
+
key,
|
|
41
|
+
path,
|
|
42
|
+
itemCount,
|
|
43
|
+
depth = 0
|
|
44
|
+
} = jsonNode.nodeData || {
|
|
45
|
+
type: "null",
|
|
46
|
+
value: null,
|
|
47
|
+
path: "unknown",
|
|
48
|
+
depth: 0
|
|
49
|
+
};
|
|
50
|
+
const {
|
|
51
|
+
showItemsCount,
|
|
52
|
+
withCopyToClipboard,
|
|
53
|
+
onCopy,
|
|
54
|
+
showIndentGuides,
|
|
55
|
+
showLineNumbers,
|
|
56
|
+
showPathOnHover,
|
|
57
|
+
tooltipProps
|
|
58
|
+
} = props;
|
|
59
|
+
const handleCopy = async (e) => {
|
|
60
|
+
e.stopPropagation();
|
|
61
|
+
try {
|
|
62
|
+
const copy = JSON.stringify(value, null, 2);
|
|
63
|
+
await navigator.clipboard.writeText(copy);
|
|
64
|
+
onCopy?.(copy, value);
|
|
65
|
+
} catch (error) {
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
const handleClick = () => {
|
|
69
|
+
if (onNodeClick) {
|
|
70
|
+
onNodeClick(path, value);
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
const handleToggleExpanded = (e) => {
|
|
74
|
+
e.stopPropagation();
|
|
75
|
+
if (expanded) {
|
|
76
|
+
onCollapse?.(node.value);
|
|
77
|
+
} else {
|
|
78
|
+
onExpand?.(node.value);
|
|
79
|
+
}
|
|
80
|
+
if (onExpandedChange) {
|
|
81
|
+
const newState = { ...tree.expandedState, [node.value]: !expanded };
|
|
82
|
+
onExpandedChange(Object.keys(newState).filter((k) => newState[k]));
|
|
83
|
+
} else {
|
|
84
|
+
tree.toggleExpanded(node.value);
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
const renderIndentGuides = () => {
|
|
88
|
+
if (!showIndentGuides || depth === 0) {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
const guides = [];
|
|
92
|
+
for (let i = 0; i < depth; i++) {
|
|
93
|
+
const colorIndex = i % 5;
|
|
94
|
+
guides.push(
|
|
95
|
+
/* @__PURE__ */ React.createElement(
|
|
96
|
+
"div",
|
|
97
|
+
{
|
|
98
|
+
key: i,
|
|
99
|
+
...getStyles("indentGuide", {
|
|
100
|
+
style: {
|
|
101
|
+
left: `${i * 32 + 8}px`
|
|
102
|
+
}
|
|
103
|
+
}),
|
|
104
|
+
"data-color-index": colorIndex
|
|
105
|
+
}
|
|
106
|
+
)
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
return guides;
|
|
110
|
+
};
|
|
111
|
+
const lineNumber = showLineNumbers ? /* @__PURE__ */ React.createElement("span", { ...getStyles("lineNumber") }) : null;
|
|
112
|
+
const wrapWithTooltip = (content) => showPathOnHover ? /* @__PURE__ */ React.createElement(core.Tooltip, { label: path, position: "top-start", withArrow: true, openDelay: 300, ...tooltipProps }, content) : content;
|
|
113
|
+
if (!hasChildren) {
|
|
114
|
+
return wrapWithTooltip(
|
|
115
|
+
/* @__PURE__ */ React.createElement(
|
|
116
|
+
core.Group,
|
|
117
|
+
{
|
|
118
|
+
gap: 4,
|
|
119
|
+
wrap: "nowrap",
|
|
120
|
+
...elementProps,
|
|
121
|
+
onClick: handleClick,
|
|
122
|
+
style: { cursor: onNodeClick ? "pointer" : "default", position: "relative" }
|
|
123
|
+
},
|
|
124
|
+
lineNumber,
|
|
125
|
+
renderIndentGuides(),
|
|
126
|
+
key !== void 0 && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(core.Text, { component: "span", ...getStyles("key"), "data-key": key }, key), /* @__PURE__ */ React.createElement(core.Text, { component: "span", ...getStyles("keyValueSeparator") }, ":")),
|
|
127
|
+
(() => {
|
|
128
|
+
const formattedValue = utils.formatValue(value, type);
|
|
129
|
+
return /* @__PURE__ */ React.createElement(core.Code, { ...getStyles("value"), "data-type": type, "data-value": formattedValue }, formattedValue);
|
|
130
|
+
})(),
|
|
131
|
+
withCopyToClipboard && /* @__PURE__ */ React.createElement(
|
|
132
|
+
core.ActionIcon,
|
|
133
|
+
{
|
|
134
|
+
size: "xs",
|
|
135
|
+
variant: "subtle",
|
|
136
|
+
color: "gray",
|
|
137
|
+
onClick: handleCopy,
|
|
138
|
+
...getStyles("copyButton")
|
|
139
|
+
},
|
|
140
|
+
copyToClipboardIcon
|
|
141
|
+
)
|
|
142
|
+
)
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
const openBracket = type === "array" ? "[" : "{";
|
|
146
|
+
const closeBracket = type === "array" ? "]" : "}";
|
|
147
|
+
const expandCollapseIcon = (() => {
|
|
148
|
+
if (!expandControlIcon && !collapseControlIcon) {
|
|
149
|
+
return /* @__PURE__ */ React.createElement(
|
|
150
|
+
iconsReact.IconChevronRight,
|
|
151
|
+
{
|
|
152
|
+
size: 14,
|
|
153
|
+
style: {
|
|
154
|
+
transform: expanded ? "rotate(90deg)" : "rotate(0deg)",
|
|
155
|
+
transition: "transform 0.2s ease"
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
if (expandControlIcon && !collapseControlIcon) {
|
|
161
|
+
return React.cloneElement(expandControlIcon, {
|
|
162
|
+
style: {
|
|
163
|
+
...expandControlIcon.props?.style,
|
|
164
|
+
transform: expanded ? "rotate(90deg)" : "rotate(0deg)",
|
|
165
|
+
transition: "transform 0.2s ease"
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
if (!expandControlIcon && collapseControlIcon) {
|
|
170
|
+
return expanded ? collapseControlIcon : /* @__PURE__ */ React.createElement(iconsReact.IconChevronRight, { size: 14 });
|
|
171
|
+
}
|
|
172
|
+
return expanded ? collapseControlIcon : expandControlIcon;
|
|
173
|
+
})();
|
|
174
|
+
return wrapWithTooltip(
|
|
175
|
+
/* @__PURE__ */ React.createElement(
|
|
176
|
+
core.Group,
|
|
177
|
+
{
|
|
178
|
+
gap: 4,
|
|
179
|
+
wrap: "nowrap",
|
|
180
|
+
...elementProps,
|
|
181
|
+
onClick: handleClick,
|
|
182
|
+
"data-expanded": expanded,
|
|
183
|
+
"data-has-children": hasChildren,
|
|
184
|
+
"data-type": type,
|
|
185
|
+
style: { cursor: onNodeClick ? "pointer" : "default", position: "relative" }
|
|
186
|
+
},
|
|
187
|
+
lineNumber,
|
|
188
|
+
renderIndentGuides(),
|
|
189
|
+
/* @__PURE__ */ React.createElement(
|
|
190
|
+
core.ActionIcon,
|
|
191
|
+
{
|
|
192
|
+
size: "xs",
|
|
193
|
+
variant: "subtle",
|
|
194
|
+
onClick: handleToggleExpanded,
|
|
195
|
+
...getStyles("expandCollapse")
|
|
196
|
+
},
|
|
197
|
+
expandCollapseIcon
|
|
198
|
+
),
|
|
199
|
+
key !== void 0 && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(core.Text, { component: "span", ...getStyles("key") }, key), /* @__PURE__ */ React.createElement(core.Text, { component: "span", ...getStyles("keyValueSeparator") }, ":")),
|
|
200
|
+
/* @__PURE__ */ React.createElement(core.Text, { component: "span", ...getStyles("bracket") }, openBracket),
|
|
201
|
+
!expanded && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(core.Text, { component: "span", size: "xs", ...getStyles("ellipsis") }, "..."), /* @__PURE__ */ React.createElement(core.Text, { component: "span", ...getStyles("bracket") }, closeBracket), itemCount !== void 0 && showItemsCount && /* @__PURE__ */ React.createElement(core.Badge, { size: "xs", variant: "light", color: "gray", ...getStyles("itemsCount") }, itemCount)),
|
|
202
|
+
withCopyToClipboard && /* @__PURE__ */ React.createElement(
|
|
203
|
+
core.ActionIcon,
|
|
204
|
+
{
|
|
205
|
+
size: "xs",
|
|
206
|
+
variant: "subtle",
|
|
207
|
+
color: "gray",
|
|
208
|
+
onClick: handleCopy,
|
|
209
|
+
...getStyles("copyButton")
|
|
210
|
+
},
|
|
211
|
+
copyToClipboardIcon
|
|
212
|
+
)
|
|
213
|
+
)
|
|
214
|
+
);
|
|
215
|
+
}
|
|
23
216
|
const varsResolver = core.createVarsResolver(
|
|
24
|
-
(_, {
|
|
217
|
+
(_, { stickyHeader, stickyHeaderOffset }) => {
|
|
25
218
|
return {
|
|
26
219
|
root: {
|
|
27
220
|
"--json-tree-font-family": "var(--mantine-font-family-monospace)",
|
|
28
|
-
"--json-tree-font-size":
|
|
221
|
+
"--json-tree-font-size": void 0
|
|
29
222
|
},
|
|
30
223
|
header: {
|
|
31
224
|
"--json-tree-header-background-color": "inherit",
|
|
@@ -60,7 +253,8 @@ const varsResolver = core.createVarsResolver(
|
|
|
60
253
|
},
|
|
61
254
|
expandCollapse: {},
|
|
62
255
|
keyValueSeparator: {},
|
|
63
|
-
ellipsis: {},
|
|
256
|
+
ellipsis: { "--json-tree-color-ellipsis": "var(--mantine-color-dark-3)" },
|
|
257
|
+
lineNumber: { "--json-tree-color-line-number": "var(--mantine-color-gray-5)" },
|
|
64
258
|
itemsCount: {},
|
|
65
259
|
controls: {},
|
|
66
260
|
copyButton: {}
|
|
@@ -75,12 +269,19 @@ const JsonTree = core.factory((_props, ref) => {
|
|
|
75
269
|
maxDepth,
|
|
76
270
|
onNodeClick,
|
|
77
271
|
onCopy,
|
|
272
|
+
onExpand,
|
|
273
|
+
onCollapse,
|
|
78
274
|
withExpandAll,
|
|
79
|
-
variant,
|
|
80
275
|
title,
|
|
81
276
|
showItemsCount,
|
|
82
277
|
withCopyToClipboard,
|
|
83
278
|
showIndentGuides,
|
|
279
|
+
showLineNumbers,
|
|
280
|
+
showPathOnHover,
|
|
281
|
+
tooltipProps,
|
|
282
|
+
maxHeight,
|
|
283
|
+
expanded: controlledExpanded,
|
|
284
|
+
onExpandedChange,
|
|
84
285
|
stickyHeaderOffset,
|
|
85
286
|
stickyHeader,
|
|
86
287
|
displayFunctions,
|
|
@@ -89,6 +290,7 @@ const JsonTree = core.factory((_props, ref) => {
|
|
|
89
290
|
copyToClipboardIcon,
|
|
90
291
|
expandControlIcon,
|
|
91
292
|
collapseControlIcon,
|
|
293
|
+
size,
|
|
92
294
|
classNames,
|
|
93
295
|
style,
|
|
94
296
|
styles,
|
|
@@ -109,12 +311,19 @@ const JsonTree = core.factory((_props, ref) => {
|
|
|
109
311
|
vars,
|
|
110
312
|
varsResolver
|
|
111
313
|
});
|
|
112
|
-
const
|
|
314
|
+
const responsiveClassName = core.useRandomClassName();
|
|
113
315
|
const treeData = React.useMemo(
|
|
114
316
|
() => [utils.convertToTreeData(data, void 0, "root", 0, displayFunctions)],
|
|
115
317
|
[data, displayFunctions]
|
|
116
318
|
);
|
|
117
319
|
const initialExpandedState = React.useMemo(() => {
|
|
320
|
+
if (controlledExpanded) {
|
|
321
|
+
const state = {};
|
|
322
|
+
controlledExpanded.forEach((path) => {
|
|
323
|
+
state[path] = true;
|
|
324
|
+
});
|
|
325
|
+
return state;
|
|
326
|
+
}
|
|
118
327
|
if (defaultExpanded) {
|
|
119
328
|
if (maxDepth === -1) {
|
|
120
329
|
return core.getTreeExpandedState(treeData, "*");
|
|
@@ -132,190 +341,104 @@ const JsonTree = core.factory((_props, ref) => {
|
|
|
132
341
|
return core.getTreeExpandedState(treeData, expandedNodes);
|
|
133
342
|
}
|
|
134
343
|
return {};
|
|
135
|
-
}, [treeData, defaultExpanded, maxDepth]);
|
|
344
|
+
}, [treeData, defaultExpanded, maxDepth, controlledExpanded]);
|
|
136
345
|
const tree = core.useTree({
|
|
137
346
|
initialExpandedState
|
|
138
347
|
});
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
itemCount,
|
|
147
|
-
depth = 0
|
|
148
|
-
} = jsonNode.nodeData || {
|
|
149
|
-
type: "null",
|
|
150
|
-
value: null,
|
|
151
|
-
path: "unknown",
|
|
152
|
-
depth: 0
|
|
153
|
-
};
|
|
154
|
-
const { showItemsCount: showItemsCount2, withCopyToClipboard: withCopyToClipboard2, onCopy: onCopy2, showIndentGuides: showIndentGuides2 } = props2;
|
|
155
|
-
const handleCopy = async (e) => {
|
|
156
|
-
e.stopPropagation();
|
|
157
|
-
try {
|
|
158
|
-
const copy = JSON.stringify(value, null, 2);
|
|
159
|
-
await navigator.clipboard.writeText(copy);
|
|
160
|
-
onCopy2?.(copy, value);
|
|
161
|
-
} catch (error) {
|
|
162
|
-
}
|
|
163
|
-
};
|
|
164
|
-
const handleClick = () => {
|
|
165
|
-
if (onNodeClick2) {
|
|
166
|
-
onNodeClick2(path, value);
|
|
167
|
-
}
|
|
168
|
-
};
|
|
169
|
-
const renderIndentGuides = () => {
|
|
170
|
-
if (!showIndentGuides2 || depth === 0) {
|
|
171
|
-
return null;
|
|
172
|
-
}
|
|
173
|
-
const guides = [];
|
|
174
|
-
for (let i = 0; i < depth; i++) {
|
|
175
|
-
const colorIndex = i % 5;
|
|
176
|
-
guides.push(
|
|
177
|
-
/* @__PURE__ */ React.createElement(
|
|
178
|
-
"div",
|
|
179
|
-
{
|
|
180
|
-
key: i,
|
|
181
|
-
...getStyles("indentGuide", {
|
|
182
|
-
style: {
|
|
183
|
-
left: `${i * 32 + 8}px`
|
|
184
|
-
}
|
|
185
|
-
}),
|
|
186
|
-
"data-color-index": colorIndex
|
|
187
|
-
}
|
|
188
|
-
)
|
|
189
|
-
);
|
|
190
|
-
}
|
|
191
|
-
return guides;
|
|
192
|
-
};
|
|
193
|
-
if (!hasChildren) {
|
|
194
|
-
return /* @__PURE__ */ React.createElement(
|
|
195
|
-
core.Group,
|
|
196
|
-
{
|
|
197
|
-
gap: 4,
|
|
198
|
-
wrap: "nowrap",
|
|
199
|
-
...elementProps,
|
|
200
|
-
onClick: handleClick,
|
|
201
|
-
style: { cursor: onNodeClick2 ? "pointer" : "default", position: "relative" }
|
|
202
|
-
},
|
|
203
|
-
renderIndentGuides(),
|
|
204
|
-
key !== void 0 && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(core.Text, { component: "span", ...getStyles("key"), "data-key": key }, key), /* @__PURE__ */ React.createElement(core.Text, { component: "span", ...getStyles("keyValueSeparator") }, ":")),
|
|
205
|
-
(() => {
|
|
206
|
-
const formattedValue = utils.formatValue(value, type);
|
|
207
|
-
return /* @__PURE__ */ React.createElement(core.Code, { ...getStyles("value"), "data-type": type, "data-value": formattedValue }, formattedValue);
|
|
208
|
-
})(),
|
|
209
|
-
withCopyToClipboard2 && /* @__PURE__ */ React.createElement(
|
|
210
|
-
core.ActionIcon,
|
|
211
|
-
{
|
|
212
|
-
size: "xs",
|
|
213
|
-
variant: "subtle",
|
|
214
|
-
color: "gray",
|
|
215
|
-
onClick: handleCopy,
|
|
216
|
-
...getStyles("copyButton")
|
|
217
|
-
},
|
|
218
|
-
copyToClipboardIcon
|
|
219
|
-
)
|
|
220
|
-
);
|
|
348
|
+
React.useEffect(() => {
|
|
349
|
+
if (controlledExpanded) {
|
|
350
|
+
const state = {};
|
|
351
|
+
controlledExpanded.forEach((path) => {
|
|
352
|
+
state[path] = true;
|
|
353
|
+
});
|
|
354
|
+
tree.setExpandedState(state);
|
|
221
355
|
}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
if (
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
size: 14,
|
|
230
|
-
style: {
|
|
231
|
-
transform: expanded ? "rotate(90deg)" : "rotate(0deg)",
|
|
232
|
-
transition: "transform 0.2s ease"
|
|
233
|
-
}
|
|
234
|
-
}
|
|
356
|
+
}, [controlledExpanded, tree.setExpandedState]);
|
|
357
|
+
const handleKeyDown = React.useCallback(
|
|
358
|
+
async (e) => {
|
|
359
|
+
if ((e.metaKey || e.ctrlKey) && e.key === "c" && withCopyToClipboard) {
|
|
360
|
+
e.preventDefault();
|
|
361
|
+
const focused = e.currentTarget.querySelector(
|
|
362
|
+
'[data-value][tabindex="0"], [data-value]:focus'
|
|
235
363
|
);
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
364
|
+
if (focused) {
|
|
365
|
+
const nodePath = focused.getAttribute("data-value");
|
|
366
|
+
if (nodePath) {
|
|
367
|
+
const nodeData = utils.findNodeByPath(treeData, nodePath);
|
|
368
|
+
if (nodeData?.nodeData) {
|
|
369
|
+
try {
|
|
370
|
+
const copy = JSON.stringify(nodeData.nodeData.value, null, 2);
|
|
371
|
+
await navigator.clipboard.writeText(copy);
|
|
372
|
+
onCopy?.(copy, nodeData.nodeData.value);
|
|
373
|
+
} catch {
|
|
374
|
+
}
|
|
375
|
+
}
|
|
243
376
|
}
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
if (!expandControlIcon && collapseControlIcon) {
|
|
247
|
-
return expanded ? collapseControlIcon : /* @__PURE__ */ React.createElement(iconsReact.IconChevronRight, { size: 14 });
|
|
377
|
+
}
|
|
248
378
|
}
|
|
249
|
-
return expanded ? collapseControlIcon : expandControlIcon;
|
|
250
|
-
})();
|
|
251
|
-
return /* @__PURE__ */ React.createElement(
|
|
252
|
-
core.Group,
|
|
253
|
-
{
|
|
254
|
-
gap: 4,
|
|
255
|
-
wrap: "nowrap",
|
|
256
|
-
...elementProps,
|
|
257
|
-
onClick: handleClick,
|
|
258
|
-
"data-expanded": expanded,
|
|
259
|
-
"data-has-children": hasChildren,
|
|
260
|
-
"data-type": type,
|
|
261
|
-
style: { cursor: onNodeClick2 ? "pointer" : "default", position: "relative" }
|
|
262
|
-
},
|
|
263
|
-
renderIndentGuides(),
|
|
264
|
-
/* @__PURE__ */ React.createElement(
|
|
265
|
-
core.ActionIcon,
|
|
266
|
-
{
|
|
267
|
-
size: "xs",
|
|
268
|
-
variant: "subtle",
|
|
269
|
-
onClick: (e) => {
|
|
270
|
-
e.stopPropagation();
|
|
271
|
-
tree2.toggleExpanded(node.value);
|
|
272
|
-
},
|
|
273
|
-
...getStyles("expandCollapse")
|
|
274
|
-
},
|
|
275
|
-
expandCollapseIcon
|
|
276
|
-
),
|
|
277
|
-
key !== void 0 && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(core.Text, { component: "span", ...getStyles("key") }, key), /* @__PURE__ */ React.createElement(core.Text, { component: "span", ...getStyles("keyValueSeparator") }, ":")),
|
|
278
|
-
/* @__PURE__ */ React.createElement(core.Text, { component: "span", ...getStyles("bracket") }, openBracket),
|
|
279
|
-
!expanded && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(core.Text, { component: "span", size: "xs", ...getStyles("ellipsis") }, "..."), /* @__PURE__ */ React.createElement(core.Text, { component: "span", ...getStyles("bracket") }, closeBracket), itemCount !== void 0 && showItemsCount2 && /* @__PURE__ */ React.createElement(core.Badge, { size: "xs", variant: "light", color: "gray", ...getStyles("itemsCount") }, itemCount)),
|
|
280
|
-
withCopyToClipboard2 && /* @__PURE__ */ React.createElement(
|
|
281
|
-
core.ActionIcon,
|
|
282
|
-
{
|
|
283
|
-
size: "xs",
|
|
284
|
-
variant: "subtle",
|
|
285
|
-
color: "gray",
|
|
286
|
-
onClick: handleCopy,
|
|
287
|
-
...getStyles("copyButton")
|
|
288
|
-
},
|
|
289
|
-
copyToClipboardIcon
|
|
290
|
-
)
|
|
291
|
-
);
|
|
292
|
-
}
|
|
293
|
-
return /* @__PURE__ */ React.createElement(core.Box, { ref, ...getStyles("root"), ...others }, (title || withExpandAll) && /* @__PURE__ */ React.createElement(core.Group, { ...getStyles("header"), justify: "space-between", mod: { sticky: stickyHeader } }, title || /* @__PURE__ */ React.createElement("div", null), withExpandAll && utils.isExpandable(data) && /* @__PURE__ */ React.createElement(core.Group, { gap: "xs", style: { top: 10, zIndex: 1 } }, /* @__PURE__ */ React.createElement(
|
|
294
|
-
core.ActionIcon,
|
|
295
|
-
{
|
|
296
|
-
size: "xs",
|
|
297
|
-
variant: "transparent",
|
|
298
|
-
onClick: () => tree.expandAllNodes(),
|
|
299
|
-
...getStyles("controls")
|
|
300
379
|
},
|
|
301
|
-
|
|
302
|
-
)
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
380
|
+
[withCopyToClipboard, treeData, onCopy]
|
|
381
|
+
);
|
|
382
|
+
const renderCtx = {
|
|
383
|
+
getStyles,
|
|
384
|
+
copyToClipboardIcon,
|
|
385
|
+
expandControlIcon,
|
|
386
|
+
collapseControlIcon,
|
|
387
|
+
onExpand,
|
|
388
|
+
onCollapse,
|
|
389
|
+
onExpandedChange
|
|
390
|
+
};
|
|
391
|
+
const treeComponent = /* @__PURE__ */ React.createElement(
|
|
312
392
|
core.Tree,
|
|
313
393
|
{
|
|
314
394
|
data: treeData,
|
|
315
395
|
tree,
|
|
316
396
|
levelOffset: 32,
|
|
317
|
-
renderNode: (payload) => renderJSONNode(payload,
|
|
397
|
+
renderNode: (payload) => renderJSONNode(payload, props, renderCtx, onNodeClick)
|
|
318
398
|
}
|
|
399
|
+
);
|
|
400
|
+
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(JsonTreeMediaVariables.JsonTreeMediaVariables, { size, selector: `.${responsiveClassName}` }), /* @__PURE__ */ React.createElement(
|
|
401
|
+
core.Box,
|
|
402
|
+
{
|
|
403
|
+
ref,
|
|
404
|
+
...getStyles("root", { className: responsiveClassName }),
|
|
405
|
+
...others,
|
|
406
|
+
"data-line-numbers": showLineNumbers || void 0,
|
|
407
|
+
onKeyDown: handleKeyDown
|
|
408
|
+
},
|
|
409
|
+
(title || withExpandAll) && /* @__PURE__ */ React.createElement(core.Group, { ...getStyles("header"), justify: "space-between", mod: { sticky: stickyHeader } }, title || /* @__PURE__ */ React.createElement("div", null), withExpandAll && utils.isExpandable(data) && /* @__PURE__ */ React.createElement(core.Group, { gap: "xs", style: { top: 10, zIndex: 1 } }, /* @__PURE__ */ React.createElement(
|
|
410
|
+
core.ActionIcon,
|
|
411
|
+
{
|
|
412
|
+
size: "xs",
|
|
413
|
+
variant: "transparent",
|
|
414
|
+
onClick: () => {
|
|
415
|
+
const allState = core.getTreeExpandedState(treeData, "*");
|
|
416
|
+
if (onExpandedChange) {
|
|
417
|
+
onExpandedChange(Object.keys(allState).filter((k) => allState[k]));
|
|
418
|
+
} else {
|
|
419
|
+
tree.expandAllNodes();
|
|
420
|
+
}
|
|
421
|
+
},
|
|
422
|
+
...getStyles("controls")
|
|
423
|
+
},
|
|
424
|
+
expandAllControlIcon
|
|
425
|
+
), /* @__PURE__ */ React.createElement(
|
|
426
|
+
core.ActionIcon,
|
|
427
|
+
{
|
|
428
|
+
size: "xs",
|
|
429
|
+
variant: "transparent",
|
|
430
|
+
onClick: () => {
|
|
431
|
+
if (onExpandedChange) {
|
|
432
|
+
onExpandedChange([]);
|
|
433
|
+
} else {
|
|
434
|
+
tree.collapseAllNodes();
|
|
435
|
+
}
|
|
436
|
+
},
|
|
437
|
+
...getStyles("controls")
|
|
438
|
+
},
|
|
439
|
+
collapseAllControlIcon
|
|
440
|
+
))),
|
|
441
|
+
maxHeight ? /* @__PURE__ */ React.createElement(core.ScrollArea.Autosize, { mah: maxHeight }, treeComponent) : treeComponent
|
|
319
442
|
));
|
|
320
443
|
});
|
|
321
444
|
JsonTree.classes = JsonTree_module;
|