Skip to content

Commit 470069a

Browse files
committed
feat: add web components to devtools
1 parent 4c00d9b commit 470069a

13 files changed

Lines changed: 139 additions & 10 deletions

File tree

examples/react/basic/src/index.tsx

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { createRoot } from 'react-dom/client'
2+
import { useEffect, useState } from 'react'
23
import Devtools from './setup'
34
import { queryPlugin } from './plugin'
45

@@ -15,13 +16,31 @@ queryPlugin.on('test', (event) => {
1516
})
1617

1718
function App() {
19+
const [value, setValue] = useState<any>(({
20+
initial: "value",
21+
should: "change",
22+
in: 2,
23+
array: [
24+
1, 2, 3
25+
]
26+
}))
27+
useEffect(() => {
28+
setTimeout(() => {
29+
setValue(({ title: 'Test Event', description: 'This is a test event.' }))
30+
}, 2000)
31+
}, [])
32+
// console.log('Current value:', value)
1833
return (
1934
<div>
2035
<h1>TanStack Devtools React Basic Example</h1>
36+
<tsd-json-tree
37+
value={JSON.stringify(value)}
38+
/>
2139
<Devtools />
2240
</div>
2341
)
2442
}
2543

44+
2645
const root = createRoot(document.getElementById('root')!)
2746
root.render(<App />)

examples/react/start/src/components/client-plugin.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { queryPlugin } from '@/plugin'
21
import { useEffect, useState } from 'react'
2+
import { queryPlugin } from '@/plugin'
33

44
export default function ClientPlugin() {
55
const [events, setEvents] = useState<
@@ -17,6 +17,7 @@ export default function ClientPlugin() {
1717
<div>
1818
<h1>Client Plugin Initialized</h1>
1919
<p>Devtools Client is connected.</p>
20+
2021
<button
2122
className="bg-blue-500 text-white px-4 py-2 rounded"
2223
onClick={() => {

examples/solid/basic/src/setup.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ const rootRoute = createRootRoute({
2222
About
2323
</Link>
2424
</div>
25+
<tsd-json-tree value={{ title: 'Test Event', description: 'This is a test event.' }} />
2526
<hr />
2627
<Outlet />
2728
</>
@@ -43,6 +44,7 @@ function About() {
4344
return (
4445
<div class="p-2">
4546
<h3>Hello from About!</h3>
47+
4648
</div>
4749
)
4850
}

packages/devtools-ui/package.json

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,16 @@
3232
"default": "./dist/cjs/index.cjs"
3333
}
3434
},
35+
"./types": {
36+
"import": {
37+
"types": "./dist/esm/types.d.ts",
38+
"default": "./dist/esm/types.js"
39+
},
40+
"require": {
41+
"types": "./dist/cjs/types.d.cts",
42+
"default": "./dist/cjs/types.cjs"
43+
}
44+
},
3545
"./package.json": "./package.json"
3646
},
3747
"sideEffects": false,
@@ -55,12 +65,15 @@
5565
"dependencies": {
5666
"clsx": "^2.1.1",
5767
"goober": "^2.1.16",
68+
"solid-element": "^1.9.1",
5869
"solid-js": "^1.9.7"
5970
},
6071
"peerDependencies": {
72+
"@types/react": ">=17.0.0",
6173
"solid-js": ">=1.9.7"
6274
},
6375
"devDependencies": {
64-
"vite-plugin-solid": "^2.11.6"
76+
"vite-plugin-solid": "^2.11.6",
77+
"@types/react": "19.1.10"
6578
}
66-
}
79+
}

packages/devtools-ui/src/components/tree.tsx

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
import { For, Show, createSignal } from 'solid-js'
1+
import { For, Show, createEffect, createSignal } from 'solid-js'
22
import clsx from 'clsx'
3+
import { customElement, noShadowDOM } from "solid-element"
34
import { useStyles } from '../styles/use-styles'
45

56
export function JsonTree(props: { value: any }) {
7+
68
return <JsonValue isRoot value={props.value} />
79
}
810

@@ -165,3 +167,33 @@ const ObjectValue = ({
165167
</span>
166168
)
167169
}
170+
171+
172+
export const registerJsonTreeComponent = (elName: string = "tsd-json-tree") => customElement(elName, { value: {}, }, (props, { element }) => {
173+
noShadowDOM()
174+
function getValue(value: any) {
175+
176+
if (typeof value === 'string') {
177+
try {
178+
const parsedValue = JSON.parse(value)
179+
return parsedValue
180+
} catch (e) {
181+
return value
182+
}
183+
}
184+
return value
185+
}
186+
const [value, setValue] = createSignal(getValue(props.value))
187+
188+
createEffect(() => {
189+
element.addPropertyChangedCallback((name, value) => {
190+
if (name === "value") {
191+
const finalValue = getValue(value)
192+
setValue(finalValue)
193+
}
194+
})
195+
})
196+
return <Show keyed when={value()}><JsonTree value={value()} /></Show>
197+
})
198+
199+

packages/devtools-ui/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@ export { Checkbox } from './components/checkbox'
22
export { Input } from './components/input'
33
export { Select } from './components/select'
44
export { TanStackLogo } from './components/logo'
5-
export { JsonTree } from './components/tree'
5+
export { JsonTree, registerJsonTreeComponent } from './components/tree'
66
export { Button } from './components/button'
77
export { Tag } from './components/tag'

packages/devtools-ui/src/types.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import type { } from "react";
2+
import type { } from "solid-js";
3+
4+
declare module 'react' {
5+
// eslint-disable-next-line @typescript-eslint/no-namespace
6+
namespace JSX {
7+
interface IntrinsicElements {
8+
'tsd-json-tree': { value: any, }
9+
}
10+
}
11+
}
12+
13+
declare module 'solid-js' {
14+
// eslint-disable-next-line @typescript-eslint/no-namespace
15+
namespace JSX {
16+
interface IntrinsicElements {
17+
'tsd-json-tree': { value: any }
18+
}
19+
}
20+
}
21+
22+
type _ = string
23+
export type { _ }

packages/devtools-ui/vite.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ const config = defineConfig({
1919
export default mergeConfig(
2020
config,
2121
tanstackViteConfig({
22-
entry: ['./src/index.ts'],
22+
entry: ['./src/index.ts', "./src/types.ts"],
2323
srcDir: './src',
2424
}),
2525
)

packages/devtools/src/core.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { lazy } from 'solid-js'
22
import { Portal, render } from 'solid-js/web'
33
import { ClientEventBus } from '@tanstack/devtools-event-bus/client'
4+
import { registerJsonTreeComponent } from '@tanstack/devtools-ui'
45
import { DevtoolsProvider } from './context/devtools-context'
56
import { initialState } from './context/devtools-store'
67
import type { ClientEventBusConfig } from '@tanstack/devtools-event-bus/client'
@@ -9,6 +10,7 @@ import type {
910
TanStackDevtoolsPlugin,
1011
} from './context/devtools-context'
1112

13+
1214
export interface TanStackDevtoolsInit {
1315
/**
1416
* Configuration for the devtools shell. These configuration options are used to set the
@@ -69,6 +71,7 @@ export class TanStackDevtoolsCore {
6971
const Devtools = this.#Component
7072
this.#eventBus = new ClientEventBus(this.#eventBusConfig)
7173
this.#eventBus.start()
74+
registerJsonTreeComponent();
7275
return (
7376
<DevtoolsProvider plugins={this.#plugins} config={this.#config}>
7477
<Portal mount={mountTo}>
@@ -87,6 +90,7 @@ export class TanStackDevtoolsCore {
8790
throw new Error('Devtools is not mounted')
8891
}
8992
this.#eventBus?.stop()
93+
9094
this.#dispose?.()
9195
this.#isMounted = false
9296
}

packages/devtools/src/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
1+
import type { _ } from "@tanstack/devtools-ui/types"
2+
13
export { PLUGIN_CONTAINER_ID, PLUGIN_TITLE_CONTAINER_ID } from './constants'
24
export { TanStackDevtoolsCore } from './core'
35
export type { TanStackDevtoolsInit, ClientEventBusConfig } from './core'
46
export type {
57
TanStackDevtoolsPlugin,
68
TanStackDevtoolsConfig,
79
} from './context/devtools-context'
10+
11+
export type { _ }

0 commit comments

Comments
 (0)