@tanstack/devtools-utils 0.3.3 → 0.4.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/bin/intent.js +20 -0
- package/dist/preact/esm/index.js +1 -5
- package/dist/preact/esm/panel.d.ts +4 -5
- package/dist/preact/esm/panel.js +48 -28
- package/dist/preact/esm/panel.js.map +1 -1
- package/dist/preact/esm/plugin.d.ts +3 -2
- package/dist/preact/esm/plugin.js +20 -22
- package/dist/preact/esm/plugin.js.map +1 -1
- package/dist/react/esm/index.js +1 -5
- package/dist/react/esm/panel.d.ts +4 -4
- package/dist/react/esm/panel.js +47 -28
- package/dist/react/esm/panel.js.map +1 -1
- package/dist/react/esm/plugin.d.ts +3 -2
- package/dist/react/esm/plugin.js +20 -22
- package/dist/react/esm/plugin.js.map +1 -1
- package/dist/solid/esm/chunk/{ZXPCWXRH.js → 7TSS377A.js} +2 -10
- package/dist/solid/esm/chunk/{UX5ZZ4MG.js → WUD4VD3N.js} +2 -10
- package/dist/solid/esm/class-mount-impl/5TF6RAHJ.js +1 -0
- package/dist/solid/esm/class-mount-impl/VN5QTPB3.js +1 -0
- package/dist/solid/esm/dev.js +9 -11
- package/dist/solid/esm/index.d.ts +10 -10
- package/dist/solid/esm/index.js +9 -11
- package/dist/solid/esm/server.js +8 -10
- package/dist/solid-class/esm/class-mount-impl.d.ts +3 -2
- package/dist/solid-class/esm/class-mount-impl.js +17 -23
- package/dist/solid-class/esm/class-mount-impl.js.map +1 -1
- package/dist/solid-class/esm/class.d.ts +4 -3
- package/dist/solid-class/esm/class.js +57 -54
- package/dist/solid-class/esm/class.js.map +1 -1
- package/dist/solid-class/esm/panel.d.ts +3 -3
- package/dist/solid-class/esm/plugin.d.ts +3 -2
- package/dist/vue/esm/index.js +1 -5
- package/dist/vue/esm/panel.d.ts +3 -3
- package/dist/vue/esm/panel.js +36 -45
- package/dist/vue/esm/panel.js.map +1 -1
- package/dist/vue/esm/plugin.js +20 -19
- package/dist/vue/esm/plugin.js.map +1 -1
- package/package.json +12 -9
- package/skills/devtools-framework-adapters/SKILL.md +263 -0
- package/skills/devtools-framework-adapters/references/preact.md +262 -0
- package/skills/devtools-framework-adapters/references/react.md +241 -0
- package/skills/devtools-framework-adapters/references/solid.md +274 -0
- package/skills/devtools-framework-adapters/references/vue.md +270 -0
- package/src/preact/panel.tsx +6 -7
- package/src/preact/plugin.tsx +4 -3
- package/src/react/panel.tsx +6 -7
- package/src/react/plugin.tsx +4 -3
- package/src/solid/class-mount-impl.tsx +7 -10
- package/src/solid/class.test.tsx +43 -12
- package/src/solid/class.ts +15 -4
- package/src/solid/panel.tsx +6 -7
- package/src/solid/plugin.tsx +4 -3
- package/src/vue/panel.ts +6 -7
- package/dist/preact/esm/index.js.map +0 -1
- package/dist/react/esm/index.js.map +0 -1
- package/dist/solid/esm/class-mount-impl/LK7V47IP.js +0 -1
- package/dist/solid/esm/class-mount-impl/ZAIAXV4M.js +0 -1
- package/dist/vue/esm/index.js.map +0 -1
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
# React Framework Adapter Reference
|
|
2
|
+
|
|
3
|
+
## Import
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
import {
|
|
7
|
+
createReactPlugin,
|
|
8
|
+
createReactPanel,
|
|
9
|
+
} from '@tanstack/devtools-utils/react'
|
|
10
|
+
import type { DevtoolsPanelProps } from '@tanstack/devtools-utils/react'
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## DevtoolsPanelProps
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
interface DevtoolsPanelProps {
|
|
17
|
+
theme?: 'light' | 'dark'
|
|
18
|
+
}
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## createReactPlugin
|
|
22
|
+
|
|
23
|
+
Creates a `[Plugin, NoOpPlugin]` tuple from a React component and plugin metadata.
|
|
24
|
+
|
|
25
|
+
### Signature
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
function createReactPlugin(options: {
|
|
29
|
+
name: string
|
|
30
|
+
id?: string
|
|
31
|
+
defaultOpen?: boolean
|
|
32
|
+
Component: (props: DevtoolsPanelProps) => JSX.Element
|
|
33
|
+
}): readonly [Plugin, NoOpPlugin]
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Parameters
|
|
37
|
+
|
|
38
|
+
| Parameter | Type | Required | Description |
|
|
39
|
+
| ------------- | -------------------------------------------- | -------- | -------------------------------------- |
|
|
40
|
+
| `name` | `string` | Yes | Display name shown in the devtools tab |
|
|
41
|
+
| `id` | `string` | No | Unique identifier for the plugin |
|
|
42
|
+
| `defaultOpen` | `boolean` | No | Whether the plugin panel starts open |
|
|
43
|
+
| `Component` | `(props: DevtoolsPanelProps) => JSX.Element` | Yes | React component to render in the panel |
|
|
44
|
+
|
|
45
|
+
### Return Value
|
|
46
|
+
|
|
47
|
+
A `readonly [Plugin, NoOpPlugin]` tuple:
|
|
48
|
+
|
|
49
|
+
- **`Plugin()`** -- returns `{ name, id?, defaultOpen?, render(el: HTMLElement, theme: 'light' | 'dark') => JSX.Element }`. The `render` function renders `<Component theme={theme} />`.
|
|
50
|
+
- **`NoOpPlugin()`** -- returns the same shape but `render` returns `<></>`.
|
|
51
|
+
|
|
52
|
+
### Source
|
|
53
|
+
|
|
54
|
+
```tsx
|
|
55
|
+
// packages/devtools-utils/src/react/plugin.tsx
|
|
56
|
+
export function createReactPlugin({
|
|
57
|
+
Component,
|
|
58
|
+
...config
|
|
59
|
+
}: {
|
|
60
|
+
name: string
|
|
61
|
+
id?: string
|
|
62
|
+
defaultOpen?: boolean
|
|
63
|
+
Component: (props: DevtoolsPanelProps) => JSX.Element
|
|
64
|
+
}) {
|
|
65
|
+
function Plugin() {
|
|
66
|
+
return {
|
|
67
|
+
...config,
|
|
68
|
+
render: (_el: HTMLElement, theme: 'light' | 'dark') => (
|
|
69
|
+
<Component theme={theme} />
|
|
70
|
+
),
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function NoOpPlugin() {
|
|
74
|
+
return {
|
|
75
|
+
...config,
|
|
76
|
+
render: (_el: HTMLElement, _theme: 'light' | 'dark') => <></>,
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return [Plugin, NoOpPlugin] as const
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Usage
|
|
84
|
+
|
|
85
|
+
#### Basic
|
|
86
|
+
|
|
87
|
+
```tsx
|
|
88
|
+
import { createReactPlugin } from '@tanstack/devtools-utils/react'
|
|
89
|
+
|
|
90
|
+
function MyStorePanel({ theme }: { theme?: 'light' | 'dark' }) {
|
|
91
|
+
return (
|
|
92
|
+
<div className={theme === 'dark' ? 'dark' : 'light'}>
|
|
93
|
+
<h2>My Store Devtools</h2>
|
|
94
|
+
</div>
|
|
95
|
+
)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const [MyPlugin, NoOpPlugin] = createReactPlugin({
|
|
99
|
+
name: 'My Store',
|
|
100
|
+
id: 'my-store',
|
|
101
|
+
defaultOpen: false,
|
|
102
|
+
Component: MyStorePanel,
|
|
103
|
+
})
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
#### Inline Component
|
|
107
|
+
|
|
108
|
+
```tsx
|
|
109
|
+
const [MyPlugin, NoOpPlugin] = createReactPlugin({
|
|
110
|
+
name: 'My Store',
|
|
111
|
+
Component: ({ theme }) => <MyStorePanel theme={theme} />,
|
|
112
|
+
})
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
#### Production Tree-Shaking
|
|
116
|
+
|
|
117
|
+
```tsx
|
|
118
|
+
const [MyPlugin, NoOpPlugin] = createReactPlugin({
|
|
119
|
+
name: 'My Store',
|
|
120
|
+
Component: MyStorePanel,
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
const ActivePlugin =
|
|
124
|
+
process.env.NODE_ENV === 'development' ? MyPlugin : NoOpPlugin
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## createReactPanel
|
|
128
|
+
|
|
129
|
+
Wraps a class-based devtools core (with `mount` and `unmount` methods) in a React component that handles lifecycle.
|
|
130
|
+
|
|
131
|
+
### Signature
|
|
132
|
+
|
|
133
|
+
```ts
|
|
134
|
+
function createReactPanel<
|
|
135
|
+
TComponentProps extends DevtoolsPanelProps | undefined,
|
|
136
|
+
TCoreDevtoolsClass extends {
|
|
137
|
+
mount: (el: HTMLElement, theme: 'light' | 'dark') => void
|
|
138
|
+
unmount: () => void
|
|
139
|
+
},
|
|
140
|
+
>(CoreClass: new () => TCoreDevtoolsClass): readonly [Panel, NoOpPanel]
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Parameters
|
|
144
|
+
|
|
145
|
+
| Parameter | Type | Required | Description |
|
|
146
|
+
| ----------- | ------------------------------------------------------- | -------- | --------------------------------------- |
|
|
147
|
+
| `CoreClass` | `new () => { mount(el, theme): void; unmount(): void }` | Yes | Class constructor for the devtools core |
|
|
148
|
+
|
|
149
|
+
### Return Value
|
|
150
|
+
|
|
151
|
+
A `readonly [Panel, NoOpPanel]` tuple:
|
|
152
|
+
|
|
153
|
+
- **`Panel`** -- A React component that:
|
|
154
|
+
- Creates a `<div style={{ height: '100%' }}>` with a ref.
|
|
155
|
+
- Instantiates `CoreClass` on mount via `useEffect`.
|
|
156
|
+
- Calls `core.mount(el, props.theme ?? 'dark')`.
|
|
157
|
+
- Calls `core.unmount()` on cleanup.
|
|
158
|
+
- Re-runs the effect when `theme` prop changes.
|
|
159
|
+
- **`NoOpPanel`** -- Renders `<></>`.
|
|
160
|
+
|
|
161
|
+
### Source
|
|
162
|
+
|
|
163
|
+
```tsx
|
|
164
|
+
// packages/devtools-utils/src/react/panel.tsx
|
|
165
|
+
export function createReactPanel<
|
|
166
|
+
TComponentProps extends DevtoolsPanelProps | undefined,
|
|
167
|
+
TCoreDevtoolsClass extends {
|
|
168
|
+
mount: (el: HTMLElement, theme: 'light' | 'dark') => void
|
|
169
|
+
unmount: () => void
|
|
170
|
+
},
|
|
171
|
+
>(CoreClass: new () => TCoreDevtoolsClass) {
|
|
172
|
+
function Panel(props: TComponentProps) {
|
|
173
|
+
const devToolRef = useRef<HTMLDivElement>(null)
|
|
174
|
+
const devtools = useRef<TCoreDevtoolsClass | null>(null)
|
|
175
|
+
useEffect(() => {
|
|
176
|
+
if (devtools.current) return
|
|
177
|
+
devtools.current = new CoreClass()
|
|
178
|
+
if (devToolRef.current) {
|
|
179
|
+
devtools.current.mount(devToolRef.current, props?.theme ?? 'dark')
|
|
180
|
+
}
|
|
181
|
+
return () => {
|
|
182
|
+
if (devToolRef.current) {
|
|
183
|
+
devtools.current?.unmount()
|
|
184
|
+
devtools.current = null
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}, [props?.theme])
|
|
188
|
+
return <div style={{ height: '100%' }} ref={devToolRef} />
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function NoOpPanel(_props: TComponentProps) {
|
|
192
|
+
return <></>
|
|
193
|
+
}
|
|
194
|
+
return [Panel, NoOpPanel] as const
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Usage
|
|
199
|
+
|
|
200
|
+
#### Composing Panel + Plugin
|
|
201
|
+
|
|
202
|
+
```tsx
|
|
203
|
+
import {
|
|
204
|
+
createReactPanel,
|
|
205
|
+
createReactPlugin,
|
|
206
|
+
} from '@tanstack/devtools-utils/react'
|
|
207
|
+
|
|
208
|
+
class MyDevtoolsCore {
|
|
209
|
+
mount(el: HTMLElement, theme: 'light' | 'dark') {
|
|
210
|
+
// Use DOM APIs to render your devtools UI into the provided element
|
|
211
|
+
const container = document.createElement('div')
|
|
212
|
+
container.className = theme
|
|
213
|
+
container.textContent = 'Devtools loaded'
|
|
214
|
+
el.appendChild(container)
|
|
215
|
+
}
|
|
216
|
+
unmount() {
|
|
217
|
+
// Clean up event listeners, subscriptions, etc.
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Step 1: Create the panel component from the class
|
|
222
|
+
const [MyPanel, NoOpPanel] = createReactPanel(MyDevtoolsCore)
|
|
223
|
+
|
|
224
|
+
// Step 2: Create the plugin from the panel component
|
|
225
|
+
const [MyPlugin, NoOpPlugin] = createReactPlugin({
|
|
226
|
+
name: 'My Store',
|
|
227
|
+
Component: MyPanel,
|
|
228
|
+
})
|
|
229
|
+
|
|
230
|
+
// Step 3: Use conditionally for production
|
|
231
|
+
const ActivePlugin =
|
|
232
|
+
process.env.NODE_ENV === 'development' ? MyPlugin : NoOpPlugin
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
## React-Specific Gotchas
|
|
236
|
+
|
|
237
|
+
1. **`useEffect` dependency on `theme`**: The panel re-runs the mount effect when `theme` changes. This means the core class is unmounted and re-mounted on theme change. Design your core class to handle this gracefully.
|
|
238
|
+
|
|
239
|
+
2. **Ref guard**: `createReactPanel` uses `if (devtools.current) return` to prevent double-mounting in React Strict Mode. Do not remove this guard.
|
|
240
|
+
|
|
241
|
+
3. **Default theme is `'dark'`**: If `props.theme` is undefined, the panel defaults to `'dark'`.
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
# Solid Framework Adapter Reference
|
|
2
|
+
|
|
3
|
+
## Import
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
import {
|
|
7
|
+
createSolidPlugin,
|
|
8
|
+
createSolidPanel,
|
|
9
|
+
} from '@tanstack/devtools-utils/solid'
|
|
10
|
+
import type { DevtoolsPanelProps } from '@tanstack/devtools-utils/solid'
|
|
11
|
+
|
|
12
|
+
// For class-based lazy loading (separate subpath)
|
|
13
|
+
import { constructCoreClass } from '@tanstack/devtools-utils/solid/class'
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## DevtoolsPanelProps
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
interface DevtoolsPanelProps {
|
|
20
|
+
theme?: 'light' | 'dark'
|
|
21
|
+
}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## createSolidPlugin
|
|
25
|
+
|
|
26
|
+
Creates a `[Plugin, NoOpPlugin]` tuple from a Solid component and plugin metadata. Same options-object API as React.
|
|
27
|
+
|
|
28
|
+
### Signature
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
function createSolidPlugin(options: {
|
|
32
|
+
name: string
|
|
33
|
+
id?: string
|
|
34
|
+
defaultOpen?: boolean
|
|
35
|
+
Component: (props: DevtoolsPanelProps) => JSX.Element
|
|
36
|
+
}): readonly [Plugin, NoOpPlugin]
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Parameters
|
|
40
|
+
|
|
41
|
+
| Parameter | Type | Required | Description |
|
|
42
|
+
| ------------- | -------------------------------------------- | -------- | -------------------------------------- |
|
|
43
|
+
| `name` | `string` | Yes | Display name shown in the devtools tab |
|
|
44
|
+
| `id` | `string` | No | Unique identifier for the plugin |
|
|
45
|
+
| `defaultOpen` | `boolean` | No | Whether the plugin panel starts open |
|
|
46
|
+
| `Component` | `(props: DevtoolsPanelProps) => JSX.Element` | Yes | Solid component function |
|
|
47
|
+
|
|
48
|
+
### Return Value
|
|
49
|
+
|
|
50
|
+
A `readonly [Plugin, NoOpPlugin]` tuple:
|
|
51
|
+
|
|
52
|
+
- **`Plugin()`** -- returns `{ name, id?, defaultOpen?, render(el, theme) }`. The `render` function returns `<Component theme={theme} />`.
|
|
53
|
+
- **`NoOpPlugin()`** -- returns the same shape but `render` returns `<></>`.
|
|
54
|
+
|
|
55
|
+
### Source
|
|
56
|
+
|
|
57
|
+
```tsx
|
|
58
|
+
// packages/devtools-utils/src/solid/plugin.tsx
|
|
59
|
+
/** @jsxImportSource solid-js */
|
|
60
|
+
import type { JSX } from 'solid-js'
|
|
61
|
+
import type { DevtoolsPanelProps } from './panel'
|
|
62
|
+
|
|
63
|
+
export function createSolidPlugin({
|
|
64
|
+
Component,
|
|
65
|
+
...config
|
|
66
|
+
}: {
|
|
67
|
+
name: string
|
|
68
|
+
id?: string
|
|
69
|
+
defaultOpen?: boolean
|
|
70
|
+
Component: (props: DevtoolsPanelProps) => JSX.Element
|
|
71
|
+
}) {
|
|
72
|
+
function Plugin() {
|
|
73
|
+
return {
|
|
74
|
+
...config,
|
|
75
|
+
render: (_el: HTMLElement, theme: 'light' | 'dark') => {
|
|
76
|
+
return <Component theme={theme} />
|
|
77
|
+
},
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
function NoOpPlugin() {
|
|
81
|
+
return {
|
|
82
|
+
...config,
|
|
83
|
+
render: (_el: HTMLElement, _theme: 'light' | 'dark') => <></>,
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return [Plugin, NoOpPlugin] as const
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Usage
|
|
91
|
+
|
|
92
|
+
#### Basic
|
|
93
|
+
|
|
94
|
+
```tsx
|
|
95
|
+
import { createSolidPlugin } from '@tanstack/devtools-utils/solid'
|
|
96
|
+
|
|
97
|
+
function MyStorePanel(props: { theme?: 'light' | 'dark' }) {
|
|
98
|
+
return (
|
|
99
|
+
<div class={props.theme === 'dark' ? 'dark' : 'light'}>
|
|
100
|
+
<h2>My Store Devtools</h2>
|
|
101
|
+
</div>
|
|
102
|
+
)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const [MyPlugin, NoOpPlugin] = createSolidPlugin({
|
|
106
|
+
name: 'My Store',
|
|
107
|
+
id: 'my-store',
|
|
108
|
+
defaultOpen: false,
|
|
109
|
+
Component: MyStorePanel,
|
|
110
|
+
})
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
#### Inline Component
|
|
114
|
+
|
|
115
|
+
```tsx
|
|
116
|
+
const [MyPlugin, NoOpPlugin] = createSolidPlugin({
|
|
117
|
+
name: 'My Store',
|
|
118
|
+
Component: (props) => <MyStorePanel theme={props.theme} />,
|
|
119
|
+
})
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
#### Production Tree-Shaking
|
|
123
|
+
|
|
124
|
+
```tsx
|
|
125
|
+
const [MyPlugin, NoOpPlugin] = createSolidPlugin({
|
|
126
|
+
name: 'My Store',
|
|
127
|
+
Component: MyStorePanel,
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
const ActivePlugin = import.meta.env.DEV ? MyPlugin : NoOpPlugin
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## createSolidPanel
|
|
134
|
+
|
|
135
|
+
Wraps a class-based devtools core in a Solid component.
|
|
136
|
+
|
|
137
|
+
### Signature
|
|
138
|
+
|
|
139
|
+
```ts
|
|
140
|
+
function createSolidPanel<
|
|
141
|
+
TComponentProps extends DevtoolsPanelProps | undefined,
|
|
142
|
+
>(CoreClass: ClassType): readonly [Panel, NoOpPanel]
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Where `ClassType` is `ReturnType<typeof constructCoreClass>[0]` -- a class with `mount(el, theme)` and `unmount()`.
|
|
146
|
+
|
|
147
|
+
### Return Value
|
|
148
|
+
|
|
149
|
+
- **`Panel`** -- A Solid component that:
|
|
150
|
+
- Creates a `<div style={{ height: '100%' }}>` with a ref.
|
|
151
|
+
- Instantiates `CoreClass` immediately via `createSignal`.
|
|
152
|
+
- Calls `core.mount(el, props.theme ?? 'dark')` inside `onMount`.
|
|
153
|
+
- Calls `core.unmount()` via `onCleanup` (nested inside `onMount`).
|
|
154
|
+
- **`NoOpPanel`** -- Renders `<></>`.
|
|
155
|
+
|
|
156
|
+
### Source
|
|
157
|
+
|
|
158
|
+
```tsx
|
|
159
|
+
// packages/devtools-utils/src/solid/panel.tsx
|
|
160
|
+
/** @jsxImportSource solid-js */
|
|
161
|
+
import { createSignal, onCleanup, onMount } from 'solid-js'
|
|
162
|
+
import type { ClassType } from './class'
|
|
163
|
+
|
|
164
|
+
export function createSolidPanel<
|
|
165
|
+
TComponentProps extends DevtoolsPanelProps | undefined,
|
|
166
|
+
>(CoreClass: ClassType) {
|
|
167
|
+
function Panel(props: TComponentProps) {
|
|
168
|
+
let devToolRef: HTMLDivElement | undefined
|
|
169
|
+
const [devtools] = createSignal(new CoreClass())
|
|
170
|
+
onMount(() => {
|
|
171
|
+
if (devToolRef) {
|
|
172
|
+
devtools().mount(devToolRef, props?.theme ?? 'dark')
|
|
173
|
+
}
|
|
174
|
+
onCleanup(() => {
|
|
175
|
+
devtools().unmount()
|
|
176
|
+
})
|
|
177
|
+
})
|
|
178
|
+
return <div style={{ height: '100%' }} ref={devToolRef} />
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function NoOpPanel(_props: TComponentProps) {
|
|
182
|
+
return <></>
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return [Panel, NoOpPanel] as const
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Usage
|
|
190
|
+
|
|
191
|
+
```tsx
|
|
192
|
+
import {
|
|
193
|
+
createSolidPanel,
|
|
194
|
+
createSolidPlugin,
|
|
195
|
+
} from '@tanstack/devtools-utils/solid'
|
|
196
|
+
import { constructCoreClass } from '@tanstack/devtools-utils/solid/class'
|
|
197
|
+
|
|
198
|
+
// Step 1: Build a core class with lazy loading
|
|
199
|
+
const [MyDevtoolsCore, NoOpCore] = constructCoreClass(
|
|
200
|
+
() => import('./MyDevtoolsUI'),
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
// Step 2: Create panel from core class
|
|
204
|
+
const [MyPanel, NoOpPanel] = createSolidPanel(MyDevtoolsCore)
|
|
205
|
+
|
|
206
|
+
// Step 3: Create plugin from panel
|
|
207
|
+
const [MyPlugin, NoOpPlugin] = createSolidPlugin({
|
|
208
|
+
name: 'My Store',
|
|
209
|
+
Component: MyPanel,
|
|
210
|
+
})
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
## constructCoreClass
|
|
214
|
+
|
|
215
|
+
Solid has an additional utility for building lazy-loaded devtools cores. Import from the separate subpath `@tanstack/devtools-utils/solid/class`.
|
|
216
|
+
|
|
217
|
+
### Signature
|
|
218
|
+
|
|
219
|
+
```ts
|
|
220
|
+
function constructCoreClass(
|
|
221
|
+
importFn: () => Promise<{ default: () => JSX.Element }>,
|
|
222
|
+
): readonly [DevtoolsCore, NoOpDevtoolsCore]
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### Behavior
|
|
226
|
+
|
|
227
|
+
- **`DevtoolsCore`** -- Has an async `mount(el, theme)` that dynamically imports the component, then mounts it into `el`. Tracks mounting state to prevent double-mounting. Supports abort if `unmount()` is called during the async import.
|
|
228
|
+
- **`NoOpDevtoolsCore`** -- Extends `DevtoolsCore` but `mount` and `unmount` are no-ops.
|
|
229
|
+
|
|
230
|
+
### Usage
|
|
231
|
+
|
|
232
|
+
```ts
|
|
233
|
+
import { constructCoreClass } from '@tanstack/devtools-utils/solid/class'
|
|
234
|
+
|
|
235
|
+
const [DevtoolsCore, NoOpDevtoolsCore] = constructCoreClass(
|
|
236
|
+
() => import('./MyDevtoolsPanel'),
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
// Use DevtoolsCore with createSolidPanel
|
|
240
|
+
// Use NoOpDevtoolsCore for production
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
## Solid-Specific Gotchas
|
|
244
|
+
|
|
245
|
+
1. **Component must be a function, not JSX.** The `Component` field expects a function `(props) => JSX.Element`, not evaluated JSX.
|
|
246
|
+
|
|
247
|
+
```tsx
|
|
248
|
+
// WRONG -- <MyPanel /> is JSX.Element, not a component function
|
|
249
|
+
Component: <MyPanel />
|
|
250
|
+
|
|
251
|
+
// CORRECT -- pass the component function
|
|
252
|
+
Component: MyStorePanel
|
|
253
|
+
|
|
254
|
+
// ALSO CORRECT -- wrap in arrow function
|
|
255
|
+
Component: (props) => <MyStorePanel theme={props.theme} />
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
2. **Solid props are accessed via `props.theme`, not destructured.** Solid's reactivity requires accessing props through the props object. Destructuring breaks reactivity.
|
|
259
|
+
|
|
260
|
+
```tsx
|
|
261
|
+
// CAUTION -- destructuring may break reactivity tracking
|
|
262
|
+
const Component = ({ theme }: DevtoolsPanelProps) => <div>{theme}</div>
|
|
263
|
+
|
|
264
|
+
// PREFERRED -- access via props object
|
|
265
|
+
const Component = (props: DevtoolsPanelProps) => <div>{props.theme}</div>
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
3. **Default theme is `'dark'`.** If `props.theme` is undefined, the panel defaults to `'dark'`.
|
|
269
|
+
|
|
270
|
+
4. **`onCleanup` is nested inside `onMount`.** In `createSolidPanel`, cleanup is registered inside `onMount`, which is the Solid idiom for pairing mount/unmount lifecycle.
|
|
271
|
+
|
|
272
|
+
5. **Core class instantiation is eager.** `createSignal(new CoreClass())` runs immediately when the Panel component is created, not lazily. The actual `mount` call happens in `onMount`.
|
|
273
|
+
|
|
274
|
+
6. **`constructCoreClass` handles async import abort.** If `unmount()` is called while the dynamic import is still in flight, the mount is aborted cleanly. No need to handle this manually.
|