From 175813fae6c1b301dc03232bdf2eeb9bfcec4f56 Mon Sep 17 00:00:00 2001 From: Harry Whorlow Date: Fri, 3 Oct 2025 12:04:54 +0200 Subject: [PATCH 1/5] feat(devtools-ui): jsonTree collapsible path --- packages/devtools-ui/src/components/tree.tsx | 46 ++++++++++++++++++-- packages/devtools-ui/src/utils/deep-keys.ts | 18 ++++++++ 2 files changed, 60 insertions(+), 4 deletions(-) create mode 100644 packages/devtools-ui/src/utils/deep-keys.ts diff --git a/packages/devtools-ui/src/components/tree.tsx b/packages/devtools-ui/src/components/tree.tsx index 546d8842..74530964 100644 --- a/packages/devtools-ui/src/components/tree.tsx +++ b/packages/devtools-ui/src/components/tree.tsx @@ -2,11 +2,13 @@ import { For, Match, Show, Switch, createSignal } from 'solid-js' import clsx from 'clsx' import { css, useStyles } from '../styles/use-styles' import { CopiedCopier, Copier, ErrorCopier } from './icons' +import type { DeepKeys } from '../utils/deep-keys' -export function JsonTree(props: { - value: any +export function JsonTree>(props: { + value: TData copyable?: boolean defaultExpansionDepth?: number + collapsePath?: TName }) { return ( ) } @@ -25,8 +29,12 @@ function JsonValue(props: { isRoot?: boolean isLastKey?: boolean copyable?: boolean + defaultExpansionDepth: number depth: number + + collapsePath?: string + path: string }) { const { value, @@ -36,9 +44,13 @@ function JsonValue(props: { copyable, defaultExpansionDepth, depth, + collapsePath, + path, } = props const styles = useStyles() + console.log('path jsonval', path, collapsePath) + return ( {keyName && typeof value !== 'object' && !Array.isArray(value) && ( @@ -75,6 +87,8 @@ function JsonValue(props: { copyable={copyable} keyName={keyName} value={value} + collapsePath={collapsePath} + path={path} /> ) } @@ -86,6 +100,8 @@ function JsonValue(props: { copyable={copyable} keyName={keyName} value={value} + collapsePath={collapsePath} + path={path} /> ) } @@ -107,15 +123,22 @@ const ArrayValue = ({ copyable, defaultExpansionDepth, depth, + collapsePath, + path, }: { value: Array copyable?: boolean keyName?: string defaultExpansionDepth: number depth: number + collapsePath?: string + path: string }) => { const styles = useStyles() - const [expanded, setExpanded] = createSignal(depth <= defaultExpansionDepth) + + const [expanded, setExpanded] = createSignal( + depth <= defaultExpansionDepth && collapsePath !== path, + ) if (value.length === 0) { return ( @@ -161,12 +184,15 @@ const ArrayValue = ({ isLastKey={isLastKey} defaultExpansionDepth={defaultExpansionDepth} depth={depth + 1} + collapsePath={collapsePath} + path={path ? `${path}[${i()}]` : `[${i()}]`} /> ) }} + { @@ -190,15 +216,23 @@ const ObjectValue = ({ copyable, defaultExpansionDepth, depth, + collapsePath, + path, }: { value: Record keyName?: string copyable?: boolean defaultExpansionDepth: number depth: number + collapsePath?: string + path: string }) => { const styles = useStyles() - const [expanded, setExpanded] = createSignal(depth <= defaultExpansionDepth) + + const [expanded, setExpanded] = createSignal( + depth <= defaultExpansionDepth && collapsePath !== path, + ) + const keys = Object.keys(value) const lastKeyName = keys[keys.length - 1] @@ -214,6 +248,7 @@ const ObjectValue = ({ ) } + return ( {keyName && ( @@ -248,12 +283,15 @@ const ObjectValue = ({ copyable={copyable} defaultExpansionDepth={defaultExpansionDepth} depth={depth + 1} + collapsePath={collapsePath} + path={`${path}${path ? '.' : ''}${k}`} /> )} + { diff --git a/packages/devtools-ui/src/utils/deep-keys.ts b/packages/devtools-ui/src/utils/deep-keys.ts new file mode 100644 index 00000000..4bae5c28 --- /dev/null +++ b/packages/devtools-ui/src/utils/deep-keys.ts @@ -0,0 +1,18 @@ +export type CollapsibleKeys = + T extends ReadonlyArray + ? + | (TPrefix extends '' ? '' : TPrefix) + | CollapsibleKeys + : T extends object + ? + | (TPrefix extends '' ? '' : TPrefix) + | { + [K in Extract]: CollapsibleKeys< + T[K], + TPrefix extends '' ? `${K}` : `${TPrefix}.${K}` + > + }[Extract] + : never + +export type CollapsiblePaths = + CollapsibleKeys extends string ? CollapsibleKeys : never From f0157008cbe10c1c2f2b03a3c19b6ebbf4b04d9d Mon Sep 17 00:00:00 2001 From: Harry Whorlow Date: Fri, 3 Oct 2025 12:08:20 +0200 Subject: [PATCH 2/5] chore: changeset --- .changeset/chubby-clowns-walk.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/chubby-clowns-walk.md diff --git a/.changeset/chubby-clowns-walk.md b/.changeset/chubby-clowns-walk.md new file mode 100644 index 00000000..8201e17f --- /dev/null +++ b/.changeset/chubby-clowns-walk.md @@ -0,0 +1,5 @@ +--- +'@tanstack/devtools-ui': minor +--- + +Adds collapsible path prop to devtools-ui, allowing an array of object paths to collapse by default. From 36237b7cf67e30294528501e2ddad340ab3feef6 Mon Sep 17 00:00:00 2001 From: Harry Whorlow Date: Fri, 3 Oct 2025 12:12:27 +0200 Subject: [PATCH 3/5] chore: fix knip test --- packages/devtools-ui/src/utils/deep-keys.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/devtools-ui/src/utils/deep-keys.ts b/packages/devtools-ui/src/utils/deep-keys.ts index 4bae5c28..4d76f258 100644 --- a/packages/devtools-ui/src/utils/deep-keys.ts +++ b/packages/devtools-ui/src/utils/deep-keys.ts @@ -1,4 +1,4 @@ -export type CollapsibleKeys = +type CollapsibleKeys = T extends ReadonlyArray ? | (TPrefix extends '' ? '' : TPrefix) From ce10af69cfa829fbdb9abe6168d5cbd35c8533e7 Mon Sep 17 00:00:00 2001 From: Harry Whorlow Date: Fri, 3 Oct 2025 12:24:16 +0200 Subject: [PATCH 4/5] fix: merge error --- packages/devtools-ui/src/components/tree.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/devtools-ui/src/components/tree.tsx b/packages/devtools-ui/src/components/tree.tsx index 74530964..0587b9c1 100644 --- a/packages/devtools-ui/src/components/tree.tsx +++ b/packages/devtools-ui/src/components/tree.tsx @@ -2,9 +2,9 @@ import { For, Match, Show, Switch, createSignal } from 'solid-js' import clsx from 'clsx' import { css, useStyles } from '../styles/use-styles' import { CopiedCopier, Copier, ErrorCopier } from './icons' -import type { DeepKeys } from '../utils/deep-keys' +import type { CollapsiblePaths } from '../utils/deep-keys' -export function JsonTree>(props: { +export function JsonTree>(props: { value: TData copyable?: boolean defaultExpansionDepth?: number From 4c3282a8f9541c7e8b83105c7f7dd48356b42b52 Mon Sep 17 00:00:00 2001 From: Harry Whorlow Date: Fri, 3 Oct 2025 13:09:35 +0200 Subject: [PATCH 5/5] fix: incorrect merge --- packages/devtools-ui/src/components/tree.tsx | 39 ++++++++++++-------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/packages/devtools-ui/src/components/tree.tsx b/packages/devtools-ui/src/components/tree.tsx index 0587b9c1..a1291fd3 100644 --- a/packages/devtools-ui/src/components/tree.tsx +++ b/packages/devtools-ui/src/components/tree.tsx @@ -8,7 +8,7 @@ export function JsonTree>(props: { value: TData copyable?: boolean defaultExpansionDepth?: number - collapsePath?: TName + collapsePaths?: Array }) { return ( >(props: { depth={0} defaultExpansionDepth={props.defaultExpansionDepth ?? 1} path="" - collapsePath={props.collapsePath} + collapsePaths={props.collapsePaths} /> ) } @@ -33,7 +33,7 @@ function JsonValue(props: { defaultExpansionDepth: number depth: number - collapsePath?: string + collapsePaths?: Array path: string }) { const { @@ -44,13 +44,11 @@ function JsonValue(props: { copyable, defaultExpansionDepth, depth, - collapsePath, + collapsePaths, path, } = props const styles = useStyles() - console.log('path jsonval', path, collapsePath) - return ( {keyName && typeof value !== 'object' && !Array.isArray(value) && ( @@ -87,7 +85,7 @@ function JsonValue(props: { copyable={copyable} keyName={keyName} value={value} - collapsePath={collapsePath} + collapsePaths={collapsePaths} path={path} /> ) @@ -100,7 +98,7 @@ function JsonValue(props: { copyable={copyable} keyName={keyName} value={value} - collapsePath={collapsePath} + collapsePaths={collapsePaths} path={path} /> ) @@ -123,7 +121,7 @@ const ArrayValue = ({ copyable, defaultExpansionDepth, depth, - collapsePath, + collapsePaths, path, }: { value: Array @@ -131,13 +129,13 @@ const ArrayValue = ({ keyName?: string defaultExpansionDepth: number depth: number - collapsePath?: string + collapsePaths?: Array path: string }) => { const styles = useStyles() const [expanded, setExpanded] = createSignal( - depth <= defaultExpansionDepth && collapsePath !== path, + depth <= defaultExpansionDepth && !collapsePaths?.includes(path), ) if (value.length === 0) { @@ -148,6 +146,7 @@ const ArrayValue = ({ "{keyName}":{' '} )} + [] ) @@ -158,6 +157,7 @@ const ArrayValue = ({ onClick={() => setExpanded(!expanded())} expanded={expanded()} /> + {keyName && ( { @@ -171,7 +171,9 @@ const ArrayValue = ({ {value.length} items )} + [ + @@ -184,7 +186,7 @@ const ArrayValue = ({ isLastKey={isLastKey} defaultExpansionDepth={defaultExpansionDepth} depth={depth + 1} - collapsePath={collapsePath} + collapsePaths={collapsePaths} path={path ? `${path}[${i()}]` : `[${i()}]`} /> ) @@ -216,7 +218,7 @@ const ObjectValue = ({ copyable, defaultExpansionDepth, depth, - collapsePath, + collapsePaths, path, }: { value: Record @@ -224,13 +226,13 @@ const ObjectValue = ({ copyable?: boolean defaultExpansionDepth: number depth: number - collapsePath?: string + collapsePaths?: Array path: string }) => { const styles = useStyles() const [expanded, setExpanded] = createSignal( - depth <= defaultExpansionDepth && collapsePath !== path, + depth <= defaultExpansionDepth && !collapsePaths?.includes(path), ) const keys = Object.keys(value) @@ -244,6 +246,7 @@ const ObjectValue = ({ "{keyName}":{' '} )} + {'{}'} ) @@ -257,6 +260,7 @@ const ObjectValue = ({ expanded={expanded()} /> )} + {keyName && ( { @@ -270,7 +274,9 @@ const ObjectValue = ({ {keys.length} items )} + {'{'} + @@ -283,7 +289,7 @@ const ObjectValue = ({ copyable={copyable} defaultExpansionDepth={defaultExpansionDepth} depth={depth + 1} - collapsePath={collapsePath} + collapsePaths={collapsePaths} path={`${path}${path ? '.' : ''}${k}`} /> @@ -304,6 +310,7 @@ const ObjectValue = ({ {`...`} + {'}'} )