Skip to content

Commit ba6d873

Browse files
committed
all worts
1 parent 7faf734 commit ba6d873

27 files changed

+2611
-1094
lines changed

examples/react/todo/package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@
44
"version": "0.0.25",
55
"dependencies": {
66
"@tanstack/db-collections": "^0.0.23",
7+
"@tanstack/db-devtools": "workspace:*",
78
"@tanstack/query-core": "^5.81.5",
8-
"@tanstack/react-router": "^1.125.6",
9-
"@tanstack/react-start": "^1.126.1",
109
"@tanstack/react-db": "^0.0.20",
1110
"@tanstack/react-db-devtools": "workspace:*",
11+
"@tanstack/react-router": "^1.125.6",
12+
"@tanstack/react-router-devtools": "^1.127.3",
13+
"@tanstack/react-start": "^1.126.1",
1214
"cors": "^2.8.5",
1315
"drizzle-orm": "^0.40.1",
1416
"drizzle-zod": "^0.7.0",

examples/react/todo/src/App.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {
44
electricCollectionOptions,
55
queryCollectionOptions,
66
} from "@tanstack/db-collections"
7-
import { ReactDbDevtools } from "@tanstack/react-db-devtools"
7+
import { TanStackReactDbDevtools } from "@tanstack/react-db-devtools"
88
import { QueryClient } from "@tanstack/query-core"
99
import { selectConfigSchema, selectTodoSchema } from "./db/validation"
1010
import type { Collection } from "@tanstack/react-db"
@@ -641,7 +641,6 @@ export default function App() {
641641
</div>
642642
</div>
643643
</div>
644-
<ReactDbDevtools />
645644
</>
646645
)
647646
}

examples/react/todo/src/main.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,15 @@ import React from "react"
22
import { createRoot } from "react-dom/client"
33
import { RouterProvider } from "@tanstack/react-router"
44
import { createRouter } from "./router"
5+
import { initializeDbDevtools } from "@tanstack/react-db-devtools"
56
import "./index.css"
67

8+
// Initialize DB devtools BEFORE any collections are created
9+
if (typeof window !== 'undefined' && process.env.NODE_ENV === 'development') {
10+
console.log('Main: Initializing devtools early...')
11+
initializeDbDevtools()
12+
}
13+
714
const router = createRouter()
815

916
createRoot(document.getElementById(`root`)!).render(

examples/react/todo/src/routes/__root.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import {
44
Scripts,
55
createRootRoute,
66
} from "@tanstack/react-router"
7+
import { TanStackRouterDevtools } from '@tanstack/react-router-devtools'
8+
import { TanStackReactDbDevtools } from "@tanstack/react-db-devtools"
79

810
import appCss from "../styles.css?url"
911

@@ -32,6 +34,8 @@ export const Route = createRootRoute({
3234
component: () => (
3335
<RootDocument>
3436
<Outlet />
37+
<TanStackRouterDevtools />
38+
<TanStackReactDbDevtools position="bottom-right" />
3539
</RootDocument>
3640
),
3741
})
Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
import { clsx as cx } from 'clsx'
2+
import { Show, createMemo, createSignal, onMount } from 'solid-js'
3+
import { useDevtoolsOnClose } from './contexts'
4+
import { useStyles } from './useStyles'
5+
import { useLocalStorage } from './useLocalStorage'
6+
import { multiSortBy } from './utils'
7+
import type { Accessor, JSX } from 'solid-js'
8+
import type { DbDevtoolsRegistry, CollectionMetadata } from './types'
9+
10+
export interface BaseDbDevtoolsPanelOptions {
11+
/**
12+
* The standard React style object used to style a component with inline styles
13+
*/
14+
style?: Accessor<JSX.CSSProperties>
15+
/**
16+
* The standard React class property used to style a component with classes
17+
*/
18+
className?: Accessor<string>
19+
/**
20+
* A boolean variable indicating whether the panel is open or closed
21+
*/
22+
isOpen?: boolean
23+
/**
24+
* A function that toggles the open and close state of the panel
25+
*/
26+
setIsOpen?: (isOpen: boolean) => void
27+
/**
28+
* Handles the opening and closing the devtools panel
29+
*/
30+
handleDragStart?: (e: any) => void
31+
/**
32+
* The DB devtools registry instance
33+
*/
34+
registry: Accessor<DbDevtoolsRegistry>
35+
/**
36+
* Use this to attach the devtool's styles to specific element in the DOM.
37+
*/
38+
shadowDOMTarget?: ShadowRoot
39+
}
40+
41+
function Logo(props: any) {
42+
const { className, ...rest } = props
43+
const styles = useStyles()
44+
return (
45+
<button {...rest} class={cx(styles().logo, className ? className() : '')}>
46+
<div class={styles().tanstackLogo}>TANSTACK</div>
47+
<div class={styles().dbLogo}>TanStack DB v0</div>
48+
</button>
49+
)
50+
}
51+
52+
function CollectionItem({
53+
collection,
54+
isActive,
55+
onSelect,
56+
}: {
57+
collection: CollectionMetadata
58+
isActive: boolean
59+
onSelect: (collection: CollectionMetadata) => void
60+
}) {
61+
const styles = useStyles()
62+
63+
return (
64+
<div
65+
class={cx(
66+
styles().collectionItem,
67+
isActive ? styles().collectionItemActive : ''
68+
)}
69+
onClick={() => onSelect(collection)}
70+
>
71+
<div class={styles().collectionName}>{collection.id}</div>
72+
<div class={styles().collectionCount}>({collection.size})</div>
73+
<div class={cx(
74+
styles().collectionStatus,
75+
collection.status === 'error' ? styles().collectionStatusError : ''
76+
)}>
77+
{collection.status}
78+
</div>
79+
</div>
80+
)
81+
}
82+
83+
export const BaseTanStackDbDevtoolsPanel = function BaseTanStackDbDevtoolsPanel({
84+
...props
85+
}: BaseDbDevtoolsPanelOptions): JSX.Element {
86+
const {
87+
isOpen = true,
88+
setIsOpen,
89+
handleDragStart,
90+
registry,
91+
shadowDOMTarget,
92+
...panelProps
93+
} = props
94+
95+
const { onCloseClick } = useDevtoolsOnClose()
96+
const styles = useStyles()
97+
const { className, style, ...otherPanelProps } = panelProps
98+
99+
const [activeCollectionId, setActiveCollectionId] = useLocalStorage(
100+
'tanstackDbDevtoolsActiveCollectionId',
101+
'',
102+
)
103+
104+
const [collections, setCollections] = createSignal<CollectionMetadata[]>([])
105+
106+
// Poll for collections data
107+
onMount(() => {
108+
const updateCollections = () => {
109+
if (typeof window === 'undefined') return
110+
111+
try {
112+
const metadata = registry().getAllCollectionMetadata()
113+
setCollections(metadata)
114+
} catch (error) {
115+
console.warn('Error fetching collections metadata:', error)
116+
}
117+
}
118+
119+
updateCollections()
120+
const intervalId = setInterval(updateCollections, 1000)
121+
122+
return () => clearInterval(intervalId)
123+
})
124+
125+
const activeCollection = createMemo(() => {
126+
const active = collections().find(c => c.id === activeCollectionId())
127+
return active || collections()[0]
128+
})
129+
130+
const sortedCollections = createMemo(() => {
131+
return multiSortBy(
132+
collections(),
133+
[
134+
(c) => c.status === 'error' ? 0 : 1, // Errors first
135+
(c) => c.id.toLowerCase(), // Then alphabetically by ID
136+
]
137+
)
138+
})
139+
140+
const collectionDetails = createMemo(() => {
141+
const active = activeCollection()
142+
if (!active) return null
143+
144+
try {
145+
const collection = registry().getCollection(active.id)
146+
const metadata = registry().getCollectionMetadata(active.id)
147+
148+
return {
149+
collection,
150+
metadata,
151+
transactions: registry().getTransactions(active.id),
152+
}
153+
} catch (error) {
154+
console.warn('Error getting collection details:', error)
155+
return null
156+
}
157+
})
158+
159+
return (
160+
<div
161+
class={cx(
162+
styles().devtoolsPanel,
163+
'TanStackDbDevtoolsPanel',
164+
className ? className() : '',
165+
)}
166+
style={style ? style() : ''}
167+
{...otherPanelProps}
168+
>
169+
{handleDragStart ? (
170+
<div class={styles().dragHandle} onMouseDown={handleDragStart}></div>
171+
) : null}
172+
173+
<button
174+
class={styles().panelCloseBtn}
175+
onClick={(e: any) => {
176+
if (setIsOpen) {
177+
setIsOpen(false)
178+
}
179+
onCloseClick(e)
180+
}}
181+
>
182+
<svg
183+
xmlns="http://www.w3.org/2000/svg"
184+
width="10"
185+
height="6"
186+
fill="none"
187+
viewBox="0 0 10 6"
188+
class={styles().panelCloseBtnIcon}
189+
>
190+
<path
191+
stroke="currentColor"
192+
stroke-linecap="round"
193+
stroke-linejoin="round"
194+
stroke-width="1.667"
195+
d="M1 1l4 4 4-4"
196+
></path>
197+
</svg>
198+
</button>
199+
200+
<div class={styles().firstContainer}>
201+
<div class={styles().row}>
202+
<Logo />
203+
</div>
204+
<div class={styles().collectionsExplorerContainer}>
205+
<div class={styles().collectionsExplorer}>
206+
<div class={styles().collectionsHeader}>
207+
<div>Collections ({collections().length})</div>
208+
</div>
209+
210+
<div class={styles().collectionsList}>
211+
<Show
212+
when={sortedCollections().length > 0}
213+
fallback={
214+
<div style={{ padding: '16px', color: '#666' }}>
215+
No collections found
216+
</div>
217+
}
218+
>
219+
{sortedCollections().map((collection) => (
220+
<CollectionItem
221+
collection={collection}
222+
isActive={collection.id === activeCollectionId()}
223+
onSelect={(c) => setActiveCollectionId(c.id)}
224+
/>
225+
))}
226+
</Show>
227+
</div>
228+
</div>
229+
</div>
230+
</div>
231+
232+
<div class={styles().secondContainer}>
233+
<Show
234+
when={activeCollection()}
235+
fallback={
236+
<div class={styles().detailsPanel}>
237+
<div class={styles().detailsHeader}>
238+
Select a collection to view details
239+
</div>
240+
</div>
241+
}
242+
>
243+
{(collection) => (
244+
<div class={styles().detailsPanel}>
245+
<div class={styles().detailsHeader}>
246+
{collection().id}
247+
</div>
248+
<div class={styles().detailsContent}>
249+
<pre>{JSON.stringify(collection(), null, 2)}</pre>
250+
</div>
251+
</div>
252+
)}
253+
</Show>
254+
</div>
255+
</div>
256+
)
257+
}
258+
259+
export default BaseTanStackDbDevtoolsPanel

0 commit comments

Comments
 (0)