Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/chubby-clowns-walk.md
Original file line number Diff line number Diff line change
@@ -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.
53 changes: 49 additions & 4 deletions packages/devtools-ui/src/components/tree.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 { CollapsiblePaths } from '../utils/deep-keys'

export function JsonTree(props: {
value: any
export function JsonTree<TData, TName extends CollapsiblePaths<TData>>(props: {
value: TData
copyable?: boolean
defaultExpansionDepth?: number
collapsePaths?: Array<TName>
}) {
return (
<JsonValue
Expand All @@ -15,6 +17,8 @@ export function JsonTree(props: {
copyable={props.copyable}
depth={0}
defaultExpansionDepth={props.defaultExpansionDepth ?? 1}
path=""
collapsePaths={props.collapsePaths}
/>
)
}
Expand All @@ -25,8 +29,12 @@ function JsonValue(props: {
isRoot?: boolean
isLastKey?: boolean
copyable?: boolean

defaultExpansionDepth: number
depth: number

collapsePaths?: Array<string>
path: string
}) {
const {
value,
Expand All @@ -36,6 +44,8 @@ function JsonValue(props: {
copyable,
defaultExpansionDepth,
depth,
collapsePaths,
path,
} = props
const styles = useStyles()

Expand Down Expand Up @@ -75,6 +85,8 @@ function JsonValue(props: {
copyable={copyable}
keyName={keyName}
value={value}
collapsePaths={collapsePaths}
path={path}
/>
)
}
Expand All @@ -86,6 +98,8 @@ function JsonValue(props: {
copyable={copyable}
keyName={keyName}
value={value}
collapsePaths={collapsePaths}
path={path}
/>
)
}
Expand All @@ -107,15 +121,22 @@ const ArrayValue = ({
copyable,
defaultExpansionDepth,
depth,
collapsePaths,
path,
}: {
value: Array<any>
copyable?: boolean
keyName?: string
defaultExpansionDepth: number
depth: number
collapsePaths?: Array<string>
path: string
}) => {
const styles = useStyles()
const [expanded, setExpanded] = createSignal(depth <= defaultExpansionDepth)

const [expanded, setExpanded] = createSignal(
depth <= defaultExpansionDepth && !collapsePaths?.includes(path),
)

if (value.length === 0) {
return (
Expand All @@ -125,6 +146,7 @@ const ArrayValue = ({
&quot;{keyName}&quot;:{' '}
</span>
)}

<span class={styles().tree.valueBraces}>[]</span>
</span>
)
Expand All @@ -135,6 +157,7 @@ const ArrayValue = ({
onClick={() => setExpanded(!expanded())}
expanded={expanded()}
/>

{keyName && (
<span
onclick={(e) => {
Expand All @@ -148,7 +171,9 @@ const ArrayValue = ({
<span class={styles().tree.info}>{value.length} items</span>
</span>
)}

<span class={styles().tree.valueBraces}>[</span>

<Show when={expanded()}>
<span class={styles().tree.expandedLine(Boolean(keyName))}>
<For each={value}>
Expand All @@ -161,12 +186,15 @@ const ArrayValue = ({
isLastKey={isLastKey}
defaultExpansionDepth={defaultExpansionDepth}
depth={depth + 1}
collapsePaths={collapsePaths}
path={path ? `${path}[${i()}]` : `[${i()}]`}
/>
)
}}
</For>
</span>
</Show>

<Show when={!expanded()}>
<span
onClick={(e) => {
Expand All @@ -190,15 +218,23 @@ const ObjectValue = ({
copyable,
defaultExpansionDepth,
depth,
collapsePaths,
path,
}: {
value: Record<string, any>
keyName?: string
copyable?: boolean
defaultExpansionDepth: number
depth: number
collapsePaths?: Array<string>
path: string
}) => {
const styles = useStyles()
const [expanded, setExpanded] = createSignal(depth <= defaultExpansionDepth)

const [expanded, setExpanded] = createSignal(
depth <= defaultExpansionDepth && !collapsePaths?.includes(path),
)

const keys = Object.keys(value)
const lastKeyName = keys[keys.length - 1]

Expand All @@ -210,10 +246,12 @@ const ObjectValue = ({
&quot;{keyName}&quot;:{' '}
</span>
)}

<span class={styles().tree.valueBraces}>{'{}'}</span>
</span>
)
}

return (
<span class={styles().tree.expanderContainer}>
{keyName && (
Expand All @@ -222,6 +260,7 @@ const ObjectValue = ({
expanded={expanded()}
/>
)}

{keyName && (
<span
onClick={(e) => {
Expand All @@ -235,7 +274,9 @@ const ObjectValue = ({
<span class={styles().tree.info}>{keys.length} items</span>
</span>
)}

<span class={styles().tree.valueBraces}>{'{'}</span>

<Show when={expanded()}>
<span class={styles().tree.expandedLine(Boolean(keyName))}>
<For each={keys}>
Expand All @@ -248,12 +289,15 @@ const ObjectValue = ({
copyable={copyable}
defaultExpansionDepth={defaultExpansionDepth}
depth={depth + 1}
collapsePaths={collapsePaths}
path={`${path}${path ? '.' : ''}${k}`}
/>
</>
)}
</For>
</span>
</Show>

<Show when={!expanded()}>
<span
onClick={(e) => {
Expand All @@ -266,6 +310,7 @@ const ObjectValue = ({
{`...`}
</span>
</Show>

<span class={styles().tree.valueBraces}>{'}'}</span>
</span>
)
Expand Down
18 changes: 18 additions & 0 deletions packages/devtools-ui/src/utils/deep-keys.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
type CollapsibleKeys<T, TPrefix extends string = ''> =
T extends ReadonlyArray<infer U>
?
| (TPrefix extends '' ? '' : TPrefix)
| CollapsibleKeys<U, `${TPrefix}[${number}]`>
: T extends object
?
| (TPrefix extends '' ? '' : TPrefix)
| {
[K in Extract<keyof T, string>]: CollapsibleKeys<
T[K],
TPrefix extends '' ? `${K}` : `${TPrefix}.${K}`
>
}[Extract<keyof T, string>]
: never

export type CollapsiblePaths<T> =
CollapsibleKeys<T> extends string ? CollapsibleKeys<T> : never
Loading