@gfazioli/mantine-json-tree 2.1.0 → 3.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 +8 -3
- package/dist/cjs/JsonTree.cjs +251 -46
- package/dist/cjs/JsonTree.cjs.map +1 -1
- package/dist/cjs/JsonTree.module.css.cjs +1 -1
- package/dist/cjs/lib/utils.cjs +70 -5
- package/dist/cjs/lib/utils.cjs.map +1 -1
- package/dist/esm/JsonTree.mjs +255 -50
- package/dist/esm/JsonTree.mjs.map +1 -1
- package/dist/esm/JsonTree.module.css.mjs +1 -1
- package/dist/esm/lib/utils.mjs +69 -6
- 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 +38 -2
- package/dist/types/lib/utils.d.ts +20 -0
- package/package.json +10 -10
package/dist/esm/JsonTree.mjs
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import React, { useMemo, useEffect, useCallback } from 'react';
|
|
3
|
-
import { IconCopy,
|
|
4
|
-
import { createVarsResolver, rem, factory, useProps, useStyles, useRandomClassName, getTreeExpandedState, useTree, Box, Group, ActionIcon, ScrollArea, Tree, Text, Code,
|
|
2
|
+
import React, { useMemo, useEffect, useCallback, useState, useRef } from 'react';
|
|
3
|
+
import { IconCheck, IconSearch, IconCopy, IconArrowBarToUp, IconArrowBarToDown, IconChevronRight } from '@tabler/icons-react';
|
|
4
|
+
import { createVarsResolver, rem, factory, useProps, useStyles, useRandomClassName, getTreeExpandedState, useTree, Box, Group, Badge, ActionIcon, Divider, TextInput, CloseButton, ScrollArea, Paper, Tree, Text, Code, Tooltip } from '@mantine/core';
|
|
5
|
+
import { useDebouncedValue } from '@mantine/hooks';
|
|
5
6
|
import { JsonTreeMediaVariables } from './JsonTreeMediaVariables.mjs';
|
|
6
|
-
import { convertToTreeData, findNodeByPath, isExpandable, formatValue } from './lib/utils.mjs';
|
|
7
|
+
import { convertToTreeData, findNodeByPath, getItemCount, searchTree, filterTreeBySearch, isExpandable, formatValue } from './lib/utils.mjs';
|
|
7
8
|
import classes from './JsonTree.module.css.mjs';
|
|
8
9
|
|
|
9
10
|
const defaultProps = {
|
|
11
|
+
rootName: "root",
|
|
10
12
|
defaultExpanded: false,
|
|
11
13
|
maxDepth: 2,
|
|
12
14
|
withExpandAll: false,
|
|
@@ -17,10 +19,71 @@ const defaultProps = {
|
|
|
17
19
|
showPathOnHover: false,
|
|
18
20
|
stickyHeader: false,
|
|
19
21
|
displayFunctions: "as-string",
|
|
20
|
-
expandAllControlIcon: /* @__PURE__ */ React.createElement(
|
|
21
|
-
collapseAllControlIcon: /* @__PURE__ */ React.createElement(
|
|
22
|
-
copyToClipboardIcon: /* @__PURE__ */ React.createElement(IconCopy, { size: 12 })
|
|
22
|
+
expandAllControlIcon: /* @__PURE__ */ React.createElement(IconArrowBarToDown, { size: 16 }),
|
|
23
|
+
collapseAllControlIcon: /* @__PURE__ */ React.createElement(IconArrowBarToUp, { size: 16 }),
|
|
24
|
+
copyToClipboardIcon: /* @__PURE__ */ React.createElement(IconCopy, { size: 12 }),
|
|
25
|
+
withBorder: false,
|
|
26
|
+
borderRadius: "sm",
|
|
27
|
+
withKeyCountBadge: false,
|
|
28
|
+
withCopyAll: false,
|
|
29
|
+
withSearch: false,
|
|
30
|
+
copyAllIcon: /* @__PURE__ */ React.createElement(IconCopy, { size: 16 }),
|
|
31
|
+
searchIcon: /* @__PURE__ */ React.createElement(IconSearch, { size: 16 }),
|
|
32
|
+
searchPlaceholder: "Filter keys and values...",
|
|
33
|
+
searchDebounce: 300
|
|
23
34
|
};
|
|
35
|
+
function highlightText(text, query, getStyles) {
|
|
36
|
+
if (!query) {
|
|
37
|
+
return text;
|
|
38
|
+
}
|
|
39
|
+
const lowerText = text.toLowerCase();
|
|
40
|
+
const lowerQuery = query.toLowerCase();
|
|
41
|
+
const idx = lowerText.indexOf(lowerQuery);
|
|
42
|
+
if (idx === -1) {
|
|
43
|
+
return text;
|
|
44
|
+
}
|
|
45
|
+
return /* @__PURE__ */ React.createElement(React.Fragment, null, text.substring(0, idx), /* @__PURE__ */ React.createElement("span", { ...getStyles("searchHighlight") }, text.substring(idx, idx + query.length)), text.substring(idx + query.length));
|
|
46
|
+
}
|
|
47
|
+
function CopyNodeButton({
|
|
48
|
+
icon,
|
|
49
|
+
getStyles,
|
|
50
|
+
onCopy
|
|
51
|
+
}) {
|
|
52
|
+
const [copied, setCopied] = useState(false);
|
|
53
|
+
const timeoutRef = useRef(null);
|
|
54
|
+
useEffect(() => {
|
|
55
|
+
return () => {
|
|
56
|
+
if (timeoutRef.current) {
|
|
57
|
+
clearTimeout(timeoutRef.current);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
}, []);
|
|
61
|
+
const handleClick = async (e) => {
|
|
62
|
+
const success = await onCopy(e);
|
|
63
|
+
if (!success) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
setCopied(true);
|
|
67
|
+
if (timeoutRef.current) {
|
|
68
|
+
clearTimeout(timeoutRef.current);
|
|
69
|
+
}
|
|
70
|
+
timeoutRef.current = setTimeout(() => {
|
|
71
|
+
setCopied(false);
|
|
72
|
+
timeoutRef.current = null;
|
|
73
|
+
}, 1500);
|
|
74
|
+
};
|
|
75
|
+
return /* @__PURE__ */ React.createElement(
|
|
76
|
+
ActionIcon,
|
|
77
|
+
{
|
|
78
|
+
size: "xs",
|
|
79
|
+
variant: "subtle",
|
|
80
|
+
color: copied ? "green" : "gray",
|
|
81
|
+
onClick: handleClick,
|
|
82
|
+
...getStyles("copyButton")
|
|
83
|
+
},
|
|
84
|
+
copied ? /* @__PURE__ */ React.createElement(IconCheck, { size: 12 }) : icon
|
|
85
|
+
);
|
|
86
|
+
}
|
|
24
87
|
function renderJSONNode({ node, expanded, hasChildren, elementProps, tree }, props, ctx, onNodeClick) {
|
|
25
88
|
const {
|
|
26
89
|
getStyles,
|
|
@@ -60,7 +123,9 @@ function renderJSONNode({ node, expanded, hasChildren, elementProps, tree }, pro
|
|
|
60
123
|
const copy = JSON.stringify(value, null, 2);
|
|
61
124
|
await navigator.clipboard.writeText(copy);
|
|
62
125
|
onCopy?.(copy, value);
|
|
63
|
-
|
|
126
|
+
return true;
|
|
127
|
+
} catch {
|
|
128
|
+
return false;
|
|
64
129
|
}
|
|
65
130
|
};
|
|
66
131
|
const handleClick = () => {
|
|
@@ -117,26 +182,21 @@ function renderJSONNode({ node, expanded, hasChildren, elementProps, tree }, pro
|
|
|
117
182
|
wrap: "nowrap",
|
|
118
183
|
...elementProps,
|
|
119
184
|
onClick: handleClick,
|
|
120
|
-
style: {
|
|
185
|
+
style: {
|
|
186
|
+
cursor: onNodeClick ? "pointer" : "default",
|
|
187
|
+
position: "relative",
|
|
188
|
+
backgroundColor: ctx.directMatches?.has(node.value) ? "rgba(251, 191, 36, 0.15)" : void 0,
|
|
189
|
+
borderRadius: ctx.directMatches?.has(node.value) ? "4px" : void 0
|
|
190
|
+
}
|
|
121
191
|
},
|
|
122
192
|
lineNumber,
|
|
123
193
|
renderIndentGuides(),
|
|
124
|
-
key !== void 0 && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Text, { component: "span", ...getStyles("key"), "data-key": key }, key), /* @__PURE__ */ React.createElement(Text, { component: "span", ...getStyles("keyValueSeparator") }, ":")),
|
|
194
|
+
key !== void 0 && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Text, { component: "span", ...getStyles("key"), "data-key": key }, ctx.searchQuery ? highlightText(String(key), ctx.searchQuery, getStyles) : key), /* @__PURE__ */ React.createElement(Text, { component: "span", ...getStyles("keyValueSeparator") }, ":")),
|
|
125
195
|
(() => {
|
|
126
196
|
const formattedValue = formatValue(value, type);
|
|
127
|
-
return /* @__PURE__ */ React.createElement(Code, { ...getStyles("value"), "data-type": type, "data-value": formattedValue }, formattedValue);
|
|
197
|
+
return /* @__PURE__ */ React.createElement(Code, { ...getStyles("value"), "data-type": type, "data-value": formattedValue }, ctx.searchQuery ? highlightText(formattedValue, ctx.searchQuery, getStyles) : formattedValue);
|
|
128
198
|
})(),
|
|
129
|
-
withCopyToClipboard && /* @__PURE__ */ React.createElement(
|
|
130
|
-
ActionIcon,
|
|
131
|
-
{
|
|
132
|
-
size: "xs",
|
|
133
|
-
variant: "subtle",
|
|
134
|
-
color: "gray",
|
|
135
|
-
onClick: handleCopy,
|
|
136
|
-
...getStyles("copyButton")
|
|
137
|
-
},
|
|
138
|
-
copyToClipboardIcon
|
|
139
|
-
)
|
|
199
|
+
withCopyToClipboard && /* @__PURE__ */ React.createElement(CopyNodeButton, { icon: copyToClipboardIcon, getStyles, onCopy: handleCopy })
|
|
140
200
|
)
|
|
141
201
|
);
|
|
142
202
|
}
|
|
@@ -180,7 +240,12 @@ function renderJSONNode({ node, expanded, hasChildren, elementProps, tree }, pro
|
|
|
180
240
|
"data-expanded": expanded,
|
|
181
241
|
"data-has-children": hasChildren,
|
|
182
242
|
"data-type": type,
|
|
183
|
-
style: {
|
|
243
|
+
style: {
|
|
244
|
+
cursor: onNodeClick ? "pointer" : "default",
|
|
245
|
+
position: "relative",
|
|
246
|
+
backgroundColor: ctx.directMatches?.has(node.value) ? "rgba(251, 191, 36, 0.15)" : void 0,
|
|
247
|
+
borderRadius: ctx.directMatches?.has(node.value) ? "4px" : void 0
|
|
248
|
+
}
|
|
184
249
|
},
|
|
185
250
|
lineNumber,
|
|
186
251
|
renderIndentGuides(),
|
|
@@ -194,7 +259,7 @@ function renderJSONNode({ node, expanded, hasChildren, elementProps, tree }, pro
|
|
|
194
259
|
},
|
|
195
260
|
expandCollapseIcon
|
|
196
261
|
),
|
|
197
|
-
key !== void 0 && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Text, { component: "span", ...getStyles("key") }, key), /* @__PURE__ */ React.createElement(Text, { component: "span", ...getStyles("keyValueSeparator") }, ":")),
|
|
262
|
+
key !== void 0 && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Text, { component: "span", ...getStyles("key") }, ctx.searchQuery ? highlightText(String(key), ctx.searchQuery, getStyles) : key), /* @__PURE__ */ React.createElement(Text, { component: "span", ...getStyles("keyValueSeparator") }, ":")),
|
|
198
263
|
/* @__PURE__ */ React.createElement(Text, { component: "span", ...getStyles("bracket") }, openBracket),
|
|
199
264
|
!expanded && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Text, { component: "span", size: "xs", ...getStyles("ellipsis") }, "..."), /* @__PURE__ */ React.createElement(Text, { component: "span", ...getStyles("bracket") }, closeBracket), itemCount !== void 0 && showItemsCount && /* @__PURE__ */ React.createElement(Badge, { size: "xs", variant: "light", color: "gray", ...getStyles("itemsCount") }, itemCount)),
|
|
200
265
|
withCopyToClipboard && /* @__PURE__ */ React.createElement(
|
|
@@ -255,14 +320,25 @@ const varsResolver = createVarsResolver(
|
|
|
255
320
|
lineNumber: { "--json-tree-color-line-number": "var(--mantine-color-gray-5)" },
|
|
256
321
|
itemsCount: {},
|
|
257
322
|
controls: {},
|
|
258
|
-
copyButton: {}
|
|
323
|
+
copyButton: {},
|
|
324
|
+
paper: {},
|
|
325
|
+
toolbar: {},
|
|
326
|
+
keyCountBadge: {},
|
|
327
|
+
copyAllButton: {},
|
|
328
|
+
searchToggle: {},
|
|
329
|
+
searchBar: {},
|
|
330
|
+
searchInput: {},
|
|
331
|
+
searchHighlight: {
|
|
332
|
+
"--json-tree-search-highlight-color": "var(--mantine-color-yellow-3)"
|
|
333
|
+
}
|
|
259
334
|
};
|
|
260
335
|
}
|
|
261
336
|
);
|
|
262
|
-
const JsonTree = factory((_props
|
|
337
|
+
const JsonTree = factory((_props) => {
|
|
263
338
|
const props = useProps("JsonTree", defaultProps, _props);
|
|
264
339
|
const {
|
|
265
340
|
data,
|
|
341
|
+
rootName,
|
|
266
342
|
defaultExpanded,
|
|
267
343
|
maxDepth,
|
|
268
344
|
onNodeClick,
|
|
@@ -289,6 +365,19 @@ const JsonTree = factory((_props, ref) => {
|
|
|
289
365
|
expandControlIcon,
|
|
290
366
|
collapseControlIcon,
|
|
291
367
|
size,
|
|
368
|
+
withBorder,
|
|
369
|
+
borderRadius,
|
|
370
|
+
withKeyCountBadge,
|
|
371
|
+
keyCountBadgeLabel,
|
|
372
|
+
withCopyAll,
|
|
373
|
+
copyAllIcon,
|
|
374
|
+
onCopyAll,
|
|
375
|
+
withSearch,
|
|
376
|
+
searchIcon,
|
|
377
|
+
searchPlaceholder,
|
|
378
|
+
searchQuery: controlledSearchQuery,
|
|
379
|
+
onSearchChange,
|
|
380
|
+
searchDebounce,
|
|
292
381
|
classNames,
|
|
293
382
|
style,
|
|
294
383
|
styles,
|
|
@@ -311,8 +400,8 @@ const JsonTree = factory((_props, ref) => {
|
|
|
311
400
|
});
|
|
312
401
|
const responsiveClassName = useRandomClassName();
|
|
313
402
|
const treeData = useMemo(
|
|
314
|
-
() => [convertToTreeData(data,
|
|
315
|
-
[data, displayFunctions]
|
|
403
|
+
() => [convertToTreeData(data, rootName ?? "root", rootName ?? "root", 0, displayFunctions)],
|
|
404
|
+
[data, rootName, displayFunctions]
|
|
316
405
|
);
|
|
317
406
|
const initialExpandedState = useMemo(() => {
|
|
318
407
|
if (controlledExpanded) {
|
|
@@ -329,7 +418,7 @@ const JsonTree = factory((_props, ref) => {
|
|
|
329
418
|
const expandedNodes = [];
|
|
330
419
|
const traverse = (nodes, depth) => {
|
|
331
420
|
nodes.forEach((node) => {
|
|
332
|
-
if (depth < maxDepth && node.children) {
|
|
421
|
+
if (depth < (maxDepth ?? Infinity) && node.children) {
|
|
333
422
|
expandedNodes.push(node.value);
|
|
334
423
|
traverse(node.children, depth + 1);
|
|
335
424
|
}
|
|
@@ -351,7 +440,7 @@ const JsonTree = factory((_props, ref) => {
|
|
|
351
440
|
});
|
|
352
441
|
tree.setExpandedState(state);
|
|
353
442
|
}
|
|
354
|
-
}, [controlledExpanded
|
|
443
|
+
}, [controlledExpanded]);
|
|
355
444
|
const handleKeyDown = useCallback(
|
|
356
445
|
async (e) => {
|
|
357
446
|
if ((e.metaKey || e.ctrlKey) && e.key === "c" && withCopyToClipboard) {
|
|
@@ -377,6 +466,83 @@ const JsonTree = factory((_props, ref) => {
|
|
|
377
466
|
},
|
|
378
467
|
[withCopyToClipboard, treeData, onCopy]
|
|
379
468
|
);
|
|
469
|
+
const totalKeyCount = useMemo(() => getItemCount(data), [data]);
|
|
470
|
+
const [searchOpen, setSearchOpen] = useState(false);
|
|
471
|
+
const [searchQueryInternal, setSearchQueryInternal] = useState("");
|
|
472
|
+
const activeSearchQuery = controlledSearchQuery ?? searchQueryInternal ?? "";
|
|
473
|
+
const [debouncedQuery] = useDebouncedValue(activeSearchQuery, searchDebounce ?? 300);
|
|
474
|
+
const preSearchExpandedRef = useRef(null);
|
|
475
|
+
const searchResults = useMemo(
|
|
476
|
+
() => searchTree(treeData, debouncedQuery),
|
|
477
|
+
[treeData, debouncedQuery]
|
|
478
|
+
);
|
|
479
|
+
const filteredTreeData = useMemo(() => {
|
|
480
|
+
if (!debouncedQuery || searchResults.matchedPaths.size === 0) {
|
|
481
|
+
return treeData;
|
|
482
|
+
}
|
|
483
|
+
return filterTreeBySearch(treeData, searchResults.matchedPaths);
|
|
484
|
+
}, [treeData, debouncedQuery, searchResults]);
|
|
485
|
+
useEffect(() => {
|
|
486
|
+
if (debouncedQuery && searchResults.expandedPaths.length > 0) {
|
|
487
|
+
if (!preSearchExpandedRef.current) {
|
|
488
|
+
preSearchExpandedRef.current = { ...tree.expandedState };
|
|
489
|
+
}
|
|
490
|
+
const newState = {};
|
|
491
|
+
searchResults.expandedPaths.forEach((p) => {
|
|
492
|
+
newState[p] = true;
|
|
493
|
+
});
|
|
494
|
+
if (onExpandedChange) {
|
|
495
|
+
onExpandedChange(Object.keys(newState).filter((k) => newState[k]));
|
|
496
|
+
} else {
|
|
497
|
+
tree.setExpandedState(newState);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
}, [debouncedQuery, searchResults]);
|
|
501
|
+
const handleClearSearch = useCallback(() => {
|
|
502
|
+
setSearchQueryInternal("");
|
|
503
|
+
onSearchChange?.("");
|
|
504
|
+
if (preSearchExpandedRef.current) {
|
|
505
|
+
if (onExpandedChange) {
|
|
506
|
+
onExpandedChange(
|
|
507
|
+
Object.keys(preSearchExpandedRef.current).filter((k) => preSearchExpandedRef.current[k])
|
|
508
|
+
);
|
|
509
|
+
} else {
|
|
510
|
+
tree.setExpandedState(preSearchExpandedRef.current);
|
|
511
|
+
}
|
|
512
|
+
preSearchExpandedRef.current = null;
|
|
513
|
+
}
|
|
514
|
+
}, [onExpandedChange, onSearchChange, tree]);
|
|
515
|
+
const handleCloseSearch = useCallback(() => {
|
|
516
|
+
setSearchOpen(false);
|
|
517
|
+
handleClearSearch();
|
|
518
|
+
}, [handleClearSearch]);
|
|
519
|
+
const handleExpandAll = useCallback(() => {
|
|
520
|
+
const allState = getTreeExpandedState(treeData, "*");
|
|
521
|
+
if (onExpandedChange) {
|
|
522
|
+
onExpandedChange(Object.keys(allState).filter((k) => allState[k]));
|
|
523
|
+
} else {
|
|
524
|
+
tree.expandAllNodes();
|
|
525
|
+
}
|
|
526
|
+
}, [treeData, onExpandedChange, tree]);
|
|
527
|
+
const handleCollapseAll = useCallback(() => {
|
|
528
|
+
if (onExpandedChange) {
|
|
529
|
+
onExpandedChange([]);
|
|
530
|
+
} else {
|
|
531
|
+
tree.collapseAllNodes();
|
|
532
|
+
}
|
|
533
|
+
}, [onExpandedChange, tree]);
|
|
534
|
+
const [copiedAll, setCopiedAll] = useState(false);
|
|
535
|
+
const handleCopyAll = useCallback(async () => {
|
|
536
|
+
try {
|
|
537
|
+
const json = JSON.stringify(data, null, 2);
|
|
538
|
+
await navigator.clipboard.writeText(json);
|
|
539
|
+
onCopyAll?.(json);
|
|
540
|
+
onCopy?.(json, data);
|
|
541
|
+
setCopiedAll(true);
|
|
542
|
+
setTimeout(() => setCopiedAll(false), 1500);
|
|
543
|
+
} catch {
|
|
544
|
+
}
|
|
545
|
+
}, [data, onCopyAll, onCopy]);
|
|
380
546
|
const renderCtx = {
|
|
381
547
|
getStyles,
|
|
382
548
|
copyToClipboardIcon,
|
|
@@ -384,60 +550,99 @@ const JsonTree = factory((_props, ref) => {
|
|
|
384
550
|
collapseControlIcon,
|
|
385
551
|
onExpand,
|
|
386
552
|
onCollapse,
|
|
387
|
-
onExpandedChange
|
|
553
|
+
onExpandedChange,
|
|
554
|
+
searchQuery: debouncedQuery || void 0,
|
|
555
|
+
matchedPaths: debouncedQuery ? searchResults.matchedPaths : void 0,
|
|
556
|
+
directMatches: debouncedQuery ? searchResults.directMatches : void 0
|
|
388
557
|
};
|
|
389
558
|
const treeComponent = /* @__PURE__ */ React.createElement(
|
|
390
559
|
Tree,
|
|
391
560
|
{
|
|
392
|
-
data:
|
|
561
|
+
data: filteredTreeData,
|
|
393
562
|
tree,
|
|
394
563
|
levelOffset: 32,
|
|
395
564
|
renderNode: (payload) => renderJSONNode(payload, props, renderCtx, onNodeClick)
|
|
396
565
|
}
|
|
397
566
|
);
|
|
398
|
-
|
|
567
|
+
const showHeader = title || withExpandAll || withKeyCountBadge || withCopyAll || withSearch;
|
|
568
|
+
const content = /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(JsonTreeMediaVariables, { size, selector: `.${responsiveClassName}` }), /* @__PURE__ */ React.createElement(
|
|
399
569
|
Box,
|
|
400
570
|
{
|
|
401
|
-
ref,
|
|
402
571
|
...getStyles("root", { className: responsiveClassName }),
|
|
403
572
|
...others,
|
|
404
573
|
"data-line-numbers": showLineNumbers || void 0,
|
|
574
|
+
"data-searching": debouncedQuery ? true : void 0,
|
|
405
575
|
onKeyDown: handleKeyDown
|
|
406
576
|
},
|
|
407
|
-
|
|
577
|
+
showHeader && /* @__PURE__ */ React.createElement(Group, { ...getStyles("header"), justify: "space-between", mod: { sticky: stickyHeader } }, /* @__PURE__ */ React.createElement(Group, { gap: "xs" }, title || /* @__PURE__ */ React.createElement("div", null), withKeyCountBadge && isExpandable(data) && /* @__PURE__ */ React.createElement(Badge, { size: "sm", variant: "light", color: "gray", ...getStyles("keyCountBadge") }, keyCountBadgeLabel ? keyCountBadgeLabel(totalKeyCount) : `${totalKeyCount} ${Array.isArray(data) ? "items" : "keys"}`)), /* @__PURE__ */ React.createElement(Group, { gap: 4, ...getStyles("toolbar") }, withSearch && /* @__PURE__ */ React.createElement(
|
|
408
578
|
ActionIcon,
|
|
409
579
|
{
|
|
410
|
-
size: "
|
|
411
|
-
variant: "
|
|
580
|
+
size: "sm",
|
|
581
|
+
variant: searchOpen ? "light" : "subtle",
|
|
582
|
+
color: "gray",
|
|
412
583
|
onClick: () => {
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
onExpandedChange(Object.keys(allState).filter((k) => allState[k]));
|
|
584
|
+
if (searchOpen) {
|
|
585
|
+
handleCloseSearch();
|
|
416
586
|
} else {
|
|
417
|
-
|
|
587
|
+
setSearchOpen(true);
|
|
418
588
|
}
|
|
419
589
|
},
|
|
590
|
+
...getStyles("searchToggle")
|
|
591
|
+
},
|
|
592
|
+
searchIcon
|
|
593
|
+
), withExpandAll && isExpandable(data) && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
|
|
594
|
+
ActionIcon,
|
|
595
|
+
{
|
|
596
|
+
size: "sm",
|
|
597
|
+
variant: "subtle",
|
|
598
|
+
color: "gray",
|
|
599
|
+
onClick: handleExpandAll,
|
|
420
600
|
...getStyles("controls")
|
|
421
601
|
},
|
|
422
602
|
expandAllControlIcon
|
|
423
603
|
), /* @__PURE__ */ React.createElement(
|
|
424
604
|
ActionIcon,
|
|
425
605
|
{
|
|
426
|
-
size: "
|
|
427
|
-
variant: "
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
onExpandedChange([]);
|
|
431
|
-
} else {
|
|
432
|
-
tree.collapseAllNodes();
|
|
433
|
-
}
|
|
434
|
-
},
|
|
606
|
+
size: "sm",
|
|
607
|
+
variant: "subtle",
|
|
608
|
+
color: "gray",
|
|
609
|
+
onClick: handleCollapseAll,
|
|
435
610
|
...getStyles("controls")
|
|
436
611
|
},
|
|
437
612
|
collapseAllControlIcon
|
|
613
|
+
)), withCopyAll && /* @__PURE__ */ React.createElement(
|
|
614
|
+
ActionIcon,
|
|
615
|
+
{
|
|
616
|
+
size: "sm",
|
|
617
|
+
variant: "subtle",
|
|
618
|
+
color: copiedAll ? "green" : "gray",
|
|
619
|
+
onClick: handleCopyAll,
|
|
620
|
+
...getStyles("copyAllButton")
|
|
621
|
+
},
|
|
622
|
+
copiedAll ? /* @__PURE__ */ React.createElement(IconCheck, { size: 16 }) : copyAllIcon
|
|
623
|
+
))),
|
|
624
|
+
searchOpen && withSearch && /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Divider, null), /* @__PURE__ */ React.createElement(Box, { ...getStyles("searchBar"), p: "xs" }, /* @__PURE__ */ React.createElement(
|
|
625
|
+
TextInput,
|
|
626
|
+
{
|
|
627
|
+
placeholder: searchPlaceholder,
|
|
628
|
+
value: activeSearchQuery,
|
|
629
|
+
onChange: (e) => {
|
|
630
|
+
const val = e.currentTarget.value;
|
|
631
|
+
setSearchQueryInternal(val);
|
|
632
|
+
onSearchChange?.(val);
|
|
633
|
+
},
|
|
634
|
+
leftSection: /* @__PURE__ */ React.createElement(IconSearch, { size: 14 }),
|
|
635
|
+
rightSection: activeSearchQuery ? /* @__PURE__ */ React.createElement(CloseButton, { size: "sm", onClick: handleClearSearch }) : null,
|
|
636
|
+
size: "sm",
|
|
637
|
+
...getStyles("searchInput")
|
|
638
|
+
}
|
|
438
639
|
))),
|
|
439
640
|
maxHeight ? /* @__PURE__ */ React.createElement(ScrollArea.Autosize, { mah: maxHeight }, treeComponent) : treeComponent
|
|
440
641
|
));
|
|
642
|
+
if (withBorder) {
|
|
643
|
+
return /* @__PURE__ */ React.createElement(Paper, { withBorder: true, radius: borderRadius, ...getStyles("paper") }, content);
|
|
644
|
+
}
|
|
645
|
+
return content;
|
|
441
646
|
});
|
|
442
647
|
JsonTree.classes = classes;
|
|
443
648
|
JsonTree.displayName = "JsonTree";
|