@tanstack/router-devtools-core 1.20.3-alpha.1

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.
Files changed (107) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +5 -0
  3. package/dist/cjs/AgeTicker.cjs +47 -0
  4. package/dist/cjs/AgeTicker.cjs.map +1 -0
  5. package/dist/cjs/AgeTicker.d.cts +6 -0
  6. package/dist/cjs/BaseTanStackRouterDevtoolsPanel.cjs +602 -0
  7. package/dist/cjs/BaseTanStackRouterDevtoolsPanel.cjs.map +1 -0
  8. package/dist/cjs/BaseTanStackRouterDevtoolsPanel.d.cts +35 -0
  9. package/dist/cjs/Explorer.cjs +307 -0
  10. package/dist/cjs/Explorer.cjs.map +1 -0
  11. package/dist/cjs/Explorer.d.cts +43 -0
  12. package/dist/cjs/FloatingTanStackRouterDevtools.cjs +204 -0
  13. package/dist/cjs/FloatingTanStackRouterDevtools.cjs.map +1 -0
  14. package/dist/cjs/FloatingTanStackRouterDevtools.d.cts +48 -0
  15. package/dist/cjs/NavigateButton.cjs +30 -0
  16. package/dist/cjs/NavigateButton.cjs.map +1 -0
  17. package/dist/cjs/NavigateButton.d.cts +7 -0
  18. package/dist/cjs/TanStackRouterDevtoolsCore.cjs +107 -0
  19. package/dist/cjs/TanStackRouterDevtoolsCore.cjs.map +1 -0
  20. package/dist/cjs/TanStackRouterDevtoolsCore.d.cts +55 -0
  21. package/dist/cjs/TanStackRouterDevtoolsPanelCore.cjs +107 -0
  22. package/dist/cjs/TanStackRouterDevtoolsPanelCore.cjs.map +1 -0
  23. package/dist/cjs/TanStackRouterDevtoolsPanelCore.d.cts +43 -0
  24. package/dist/cjs/context.cjs +20 -0
  25. package/dist/cjs/context.cjs.map +1 -0
  26. package/dist/cjs/context.d.cts +13 -0
  27. package/dist/cjs/index.cjs +7 -0
  28. package/dist/cjs/index.cjs.map +1 -0
  29. package/dist/cjs/index.d.cts +2 -0
  30. package/dist/cjs/logo.cjs +92 -0
  31. package/dist/cjs/logo.cjs.map +1 -0
  32. package/dist/cjs/logo.d.cts +1 -0
  33. package/dist/cjs/theme.d.cts +34 -0
  34. package/dist/cjs/tokens.cjs +201 -0
  35. package/dist/cjs/tokens.cjs.map +1 -0
  36. package/dist/cjs/tokens.d.cts +298 -0
  37. package/dist/cjs/useLocalStorage.cjs +42 -0
  38. package/dist/cjs/useLocalStorage.cjs.map +1 -0
  39. package/dist/cjs/useLocalStorage.d.cts +2 -0
  40. package/dist/cjs/useMediaQuery.d.cts +2 -0
  41. package/dist/cjs/useStyles.cjs +607 -0
  42. package/dist/cjs/useStyles.cjs.map +1 -0
  43. package/dist/cjs/useStyles.d.cts +55 -0
  44. package/dist/cjs/utils.cjs +63 -0
  45. package/dist/cjs/utils.cjs.map +1 -0
  46. package/dist/cjs/utils.d.cts +25 -0
  47. package/dist/esm/AgeTicker.d.ts +6 -0
  48. package/dist/esm/AgeTicker.js +47 -0
  49. package/dist/esm/AgeTicker.js.map +1 -0
  50. package/dist/esm/BaseTanStackRouterDevtoolsPanel.d.ts +35 -0
  51. package/dist/esm/BaseTanStackRouterDevtoolsPanel.js +602 -0
  52. package/dist/esm/BaseTanStackRouterDevtoolsPanel.js.map +1 -0
  53. package/dist/esm/Explorer.d.ts +43 -0
  54. package/dist/esm/Explorer.js +290 -0
  55. package/dist/esm/Explorer.js.map +1 -0
  56. package/dist/esm/FloatingTanStackRouterDevtools.d.ts +48 -0
  57. package/dist/esm/FloatingTanStackRouterDevtools.js +204 -0
  58. package/dist/esm/FloatingTanStackRouterDevtools.js.map +1 -0
  59. package/dist/esm/NavigateButton.d.ts +7 -0
  60. package/dist/esm/NavigateButton.js +30 -0
  61. package/dist/esm/NavigateButton.js.map +1 -0
  62. package/dist/esm/TanStackRouterDevtoolsCore.d.ts +55 -0
  63. package/dist/esm/TanStackRouterDevtoolsCore.js +107 -0
  64. package/dist/esm/TanStackRouterDevtoolsCore.js.map +1 -0
  65. package/dist/esm/TanStackRouterDevtoolsPanelCore.d.ts +43 -0
  66. package/dist/esm/TanStackRouterDevtoolsPanelCore.js +107 -0
  67. package/dist/esm/TanStackRouterDevtoolsPanelCore.js.map +1 -0
  68. package/dist/esm/context.d.ts +13 -0
  69. package/dist/esm/context.js +20 -0
  70. package/dist/esm/context.js.map +1 -0
  71. package/dist/esm/index.d.ts +2 -0
  72. package/dist/esm/index.js +7 -0
  73. package/dist/esm/index.js.map +1 -0
  74. package/dist/esm/logo.d.ts +1 -0
  75. package/dist/esm/logo.js +92 -0
  76. package/dist/esm/logo.js.map +1 -0
  77. package/dist/esm/theme.d.ts +34 -0
  78. package/dist/esm/tokens.d.ts +298 -0
  79. package/dist/esm/tokens.js +201 -0
  80. package/dist/esm/tokens.js.map +1 -0
  81. package/dist/esm/useLocalStorage.d.ts +2 -0
  82. package/dist/esm/useLocalStorage.js +43 -0
  83. package/dist/esm/useLocalStorage.js.map +1 -0
  84. package/dist/esm/useMediaQuery.d.ts +2 -0
  85. package/dist/esm/useStyles.d.ts +55 -0
  86. package/dist/esm/useStyles.js +590 -0
  87. package/dist/esm/useStyles.js.map +1 -0
  88. package/dist/esm/utils.d.ts +25 -0
  89. package/dist/esm/utils.js +63 -0
  90. package/dist/esm/utils.js.map +1 -0
  91. package/package.json +71 -0
  92. package/src/AgeTicker.tsx +59 -0
  93. package/src/BaseTanStackRouterDevtoolsPanel.tsx +632 -0
  94. package/src/Explorer.tsx +339 -0
  95. package/src/FloatingTanStackRouterDevtools.tsx +290 -0
  96. package/src/NavigateButton.tsx +25 -0
  97. package/src/TanStackRouterDevtoolsCore.tsx +152 -0
  98. package/src/TanStackRouterDevtoolsPanelCore.tsx +131 -0
  99. package/src/context.ts +24 -0
  100. package/src/index.tsx +2 -0
  101. package/src/logo.tsx +817 -0
  102. package/src/theme.tsx +36 -0
  103. package/src/tokens.ts +305 -0
  104. package/src/useLocalStorage.ts +52 -0
  105. package/src/useMediaQuery.ts +44 -0
  106. package/src/useStyles.tsx +614 -0
  107. package/src/utils.tsx +185 -0
@@ -0,0 +1,632 @@
1
+ import { clsx as cx } from 'clsx'
2
+ import { default as invariant } from 'tiny-invariant'
3
+ import { interpolatePath, rootRouteId, trimPath } from '@tanstack/router-core'
4
+ import { Show, createMemo } from 'solid-js'
5
+ import { useDevtoolsOnClose } from './context'
6
+ import { useStyles } from './useStyles'
7
+ import useLocalStorage from './useLocalStorage'
8
+ import { Explorer } from './Explorer'
9
+ import { getRouteStatusColor, getStatusColor, multiSortBy } from './utils'
10
+ import { AgeTicker } from './AgeTicker'
11
+ // import type { DevtoolsPanelOptions } from './TanStackRouterDevtoolsPanel'
12
+
13
+ import { NavigateButton } from './NavigateButton'
14
+ import type {
15
+ AnyContext,
16
+ AnyRoute,
17
+ AnyRouter,
18
+ FileRouteTypes,
19
+ MakeRouteMatchUnion,
20
+ Route,
21
+ RouterState,
22
+ } from '@tanstack/router-core'
23
+ import type { Accessor, JSX } from 'solid-js'
24
+
25
+ export interface BaseDevtoolsPanelOptions {
26
+ /**
27
+ * The standard React style object used to style a component with inline styles
28
+ */
29
+ style?: Accessor<JSX.CSSProperties>
30
+ /**
31
+ * The standard React class property used to style a component with classes
32
+ */
33
+ className?: Accessor<string>
34
+ /**
35
+ * A boolean variable indicating whether the panel is open or closed
36
+ */
37
+ isOpen?: boolean
38
+ /**
39
+ * A function that toggles the open and close state of the panel
40
+ */
41
+ setIsOpen?: (isOpen: boolean) => void
42
+ /**
43
+ * Handles the opening and closing the devtools panel
44
+ */
45
+ handleDragStart?: (e: any) => void
46
+ /**
47
+ * A boolean variable indicating if the "lite" version of the library is being used
48
+ */
49
+ router: Accessor<AnyRouter>
50
+ routerState: Accessor<any>
51
+ /**
52
+ * Use this to attach the devtool's styles to specific element in the DOM.
53
+ */
54
+ shadowDOMTarget?: ShadowRoot
55
+ }
56
+
57
+ function Logo(props: any) {
58
+ const { className, ...rest } = props
59
+ const styles = useStyles()
60
+ return (
61
+ <button {...rest} class={cx(styles().logo, className ? className() : '')}>
62
+ <div class={styles().tanstackLogo}>TANSTACK</div>
63
+ <div class={styles().routerLogo}>TanStack Router v1</div>
64
+ </button>
65
+ )
66
+ }
67
+
68
+ function NavigateLink(props: {
69
+ class?: string
70
+ left?: JSX.Element
71
+ children?: JSX.Element
72
+ right?: JSX.Element
73
+ }) {
74
+ return (
75
+ <div
76
+ class={props.class}
77
+ style={{
78
+ display: 'flex',
79
+ 'align-items': 'center',
80
+ width: '100%',
81
+ }}
82
+ >
83
+ {props.left}
84
+ <div style={{ 'flex-grow': 1, 'min-width': 0 }}>{props.children}</div>
85
+ {props.right}
86
+ </div>
87
+ )
88
+ }
89
+
90
+ function RouteComp({
91
+ routerState,
92
+ router,
93
+ route,
94
+ isRoot,
95
+ activeId,
96
+ setActiveId,
97
+ }: {
98
+ routerState: Accessor<
99
+ RouterState<
100
+ Route<
101
+ any,
102
+ '/',
103
+ '/',
104
+ string,
105
+ '__root__',
106
+ undefined,
107
+ {},
108
+ {},
109
+ AnyContext,
110
+ AnyContext,
111
+ {},
112
+ undefined,
113
+ any,
114
+ FileRouteTypes
115
+ >,
116
+ MakeRouteMatchUnion
117
+ >
118
+ >
119
+ router: Accessor<AnyRouter>
120
+ route: AnyRoute
121
+ isRoot?: boolean
122
+ activeId: Accessor<string | undefined>
123
+ setActiveId: (id: string) => void
124
+ }) {
125
+ const styles = useStyles()
126
+ const matches = createMemo(
127
+ () => routerState().pendingMatches || routerState().matches,
128
+ )
129
+ const match = createMemo(() =>
130
+ routerState().matches.find((d) => d.routeId === route.id),
131
+ )
132
+
133
+ const param = createMemo(() => {
134
+ try {
135
+ if (match()?.params) {
136
+ const p = match()?.params
137
+ const r: string = route.path || trimPath(route.id)
138
+ if (r.startsWith('$')) {
139
+ const trimmed = r.slice(1)
140
+
141
+ if (p[trimmed]) {
142
+ return `(${p[trimmed]})`
143
+ }
144
+ }
145
+ }
146
+ return ''
147
+ } catch (error) {
148
+ return ''
149
+ }
150
+ })
151
+
152
+ const navigationTarget = createMemo<string | undefined>(() => {
153
+ if (isRoot) return undefined // rootRouteId has no path
154
+ if (!route.path) return undefined // no path to navigate to
155
+
156
+ // flatten all params in the router state, into a single object
157
+ const allParams = Object.assign({}, ...matches().map((m) => m.params))
158
+
159
+ // interpolatePath is used by router-core to generate the `to`
160
+ // path for the navigate function in the router
161
+ const interpolated = interpolatePath({
162
+ path: route.fullPath,
163
+ params: allParams,
164
+ leaveWildcards: false,
165
+ leaveParams: false,
166
+ decodeCharMap: router().pathParamsDecodeCharMap,
167
+ })
168
+
169
+ // only if `interpolated` is not missing params, return the path since this
170
+ // means that all the params are present for a successful navigation
171
+ return !interpolated.isMissingParams
172
+ ? interpolated.interpolatedPath
173
+ : undefined
174
+ })
175
+
176
+ return (
177
+ <div>
178
+ <div
179
+ role="button"
180
+ aria-label={`Open match details for ${route.id}`}
181
+ onClick={() => {
182
+ if (match()) {
183
+ setActiveId(activeId() === route.id ? '' : route.id)
184
+ }
185
+ }}
186
+ class={cx(
187
+ styles().routesRowContainer(route.id === activeId(), !!match()),
188
+ )}
189
+ >
190
+ <div
191
+ class={cx(
192
+ styles().matchIndicator(getRouteStatusColor(matches(), route)),
193
+ )}
194
+ />
195
+ <NavigateLink
196
+ class={cx(styles().routesRow(!!match()))}
197
+ left={
198
+ <Show when={navigationTarget()}>
199
+ {(navigate) => <NavigateButton to={navigate()} router={router} />}
200
+ </Show>
201
+ }
202
+ right={<AgeTicker match={match()} router={router} />}
203
+ >
204
+ <code class={styles().code}>
205
+ {isRoot ? rootRouteId : route.path || trimPath(route.id)}{' '}
206
+ </code>
207
+ <code class={styles().routeParamInfo}>{param()}</code>
208
+ </NavigateLink>
209
+ </div>
210
+ {route.children?.length ? (
211
+ <div class={styles().nestedRouteRow(!!isRoot)}>
212
+ {[...(route.children as Array<AnyRoute>)]
213
+ .sort((a, b) => {
214
+ return a.rank - b.rank
215
+ })
216
+ .map((r) => (
217
+ <RouteComp
218
+ routerState={routerState}
219
+ router={router}
220
+ route={r}
221
+ activeId={activeId}
222
+ setActiveId={setActiveId}
223
+ />
224
+ ))}
225
+ </div>
226
+ ) : null}
227
+ </div>
228
+ )
229
+ }
230
+
231
+ export const BaseTanStackRouterDevtoolsPanel =
232
+ function BaseTanStackRouterDevtoolsPanel({
233
+ ...props
234
+ }: BaseDevtoolsPanelOptions): JSX.Element {
235
+ const {
236
+ isOpen = true,
237
+ setIsOpen,
238
+ handleDragStart,
239
+ router,
240
+ routerState,
241
+ shadowDOMTarget,
242
+ ...panelProps
243
+ } = props
244
+
245
+ const { onCloseClick } = useDevtoolsOnClose()
246
+ const styles = useStyles()
247
+ const { className, style, ...otherPanelProps } = panelProps
248
+
249
+ invariant(
250
+ router,
251
+ 'No router was found for the TanStack Router Devtools. Please place the devtools in the <RouterProvider> component tree or pass the router instance to the devtools manually.',
252
+ )
253
+
254
+ // useStore(router.__store)
255
+
256
+ const [showMatches, setShowMatches] = useLocalStorage(
257
+ 'tanstackRouterDevtoolsShowMatches',
258
+ true,
259
+ )
260
+
261
+ const [activeId, setActiveId] = useLocalStorage(
262
+ 'tanstackRouterDevtoolsActiveRouteId',
263
+ '',
264
+ )
265
+
266
+ const activeMatch = createMemo(() => {
267
+ const matches = [
268
+ ...(routerState().pendingMatches ?? []),
269
+ ...routerState().matches,
270
+ ...routerState().cachedMatches,
271
+ ]
272
+ return matches.find(
273
+ (d) => d.routeId === activeId() || d.id === activeId(),
274
+ )
275
+ })
276
+
277
+ const hasSearch = createMemo(
278
+ () => Object.keys(routerState().location.search).length,
279
+ )
280
+
281
+ const explorerState = createMemo(() => {
282
+ return {
283
+ ...router(),
284
+ state: routerState(),
285
+ }
286
+ })
287
+
288
+ const routerExplorerValue = createMemo(() =>
289
+ Object.fromEntries(
290
+ multiSortBy(
291
+ Object.keys(explorerState()),
292
+ (
293
+ [
294
+ 'state',
295
+ 'routesById',
296
+ 'routesByPath',
297
+ 'flatRoutes',
298
+ 'options',
299
+ 'manifest',
300
+ ] as const
301
+ ).map((d) => (dd) => dd !== d),
302
+ )
303
+ .map((key) => [key, (explorerState() as any)[key]])
304
+ .filter(
305
+ (d) =>
306
+ typeof d[1] !== 'function' &&
307
+ ![
308
+ '__store',
309
+ 'basepath',
310
+ 'injectedHtml',
311
+ 'subscribers',
312
+ 'latestLoadPromise',
313
+ 'navigateTimeout',
314
+ 'resetNextScroll',
315
+ 'tempLocationKey',
316
+ 'latestLocation',
317
+ 'routeTree',
318
+ 'history',
319
+ ].includes(d[0]),
320
+ ),
321
+ ),
322
+ )
323
+ const activeMatchLoaderData = createMemo(() => activeMatch()?.loaderData)
324
+ const activeMatchValue = createMemo(() => activeMatch())
325
+ const locationSearchValue = createMemo(() => routerState().location.search)
326
+
327
+ return (
328
+ <div
329
+ class={cx(
330
+ styles().devtoolsPanel,
331
+ 'TanStackRouterDevtoolsPanel',
332
+ className ? className() : '',
333
+ )}
334
+ style={style ? style() : ''}
335
+ {...otherPanelProps}
336
+ >
337
+ {handleDragStart ? (
338
+ <div class={styles().dragHandle} onMouseDown={handleDragStart}></div>
339
+ ) : null}
340
+ <button
341
+ class={styles().panelCloseBtn}
342
+ onClick={(e: any) => {
343
+ if (setIsOpen) {
344
+ setIsOpen(false)
345
+ }
346
+ onCloseClick(e)
347
+ }}
348
+ >
349
+ <svg
350
+ xmlns="http://www.w3.org/2000/svg"
351
+ width="10"
352
+ height="6"
353
+ fill="none"
354
+ viewBox="0 0 10 6"
355
+ class={styles().panelCloseBtnIcon}
356
+ >
357
+ <path
358
+ stroke="currentColor"
359
+ stroke-linecap="round"
360
+ stroke-linejoin="round"
361
+ stroke-width="1.667"
362
+ d="M1 1l4 4 4-4"
363
+ ></path>
364
+ </svg>
365
+ </button>
366
+ <div class={styles().firstContainer}>
367
+ <div class={styles().row}>
368
+ <Logo
369
+ aria-hidden
370
+ onClick={(e: any) => {
371
+ if (setIsOpen) {
372
+ setIsOpen(false)
373
+ }
374
+ onCloseClick(e)
375
+ }}
376
+ />
377
+ </div>
378
+ <div class={styles().routerExplorerContainer}>
379
+ <div class={styles().routerExplorer}>
380
+ <Explorer
381
+ label="Router"
382
+ value={routerExplorerValue}
383
+ defaultExpanded={{
384
+ state: {} as any,
385
+ context: {} as any,
386
+ options: {} as any,
387
+ }}
388
+ filterSubEntries={(subEntries) => {
389
+ return subEntries.filter(
390
+ (d: any) => typeof d.value() !== 'function',
391
+ )
392
+ }}
393
+ />
394
+ </div>
395
+ </div>
396
+ </div>
397
+ <div class={styles().secondContainer}>
398
+ <div class={styles().matchesContainer}>
399
+ <div class={styles().detailsHeader}>
400
+ <span>Pathname</span>
401
+ {routerState().location.maskedLocation ? (
402
+ <div class={styles().maskedBadgeContainer}>
403
+ <span class={styles().maskedBadge}>masked</span>
404
+ </div>
405
+ ) : null}
406
+ </div>
407
+ <div class={styles().detailsContent}>
408
+ <code>{routerState().location.pathname}</code>
409
+ {routerState().location.maskedLocation ? (
410
+ <code class={styles().maskedLocation}>
411
+ {routerState().location.maskedLocation?.pathname}
412
+ </code>
413
+ ) : null}
414
+ </div>
415
+ <div class={styles().detailsHeader}>
416
+ <div class={styles().routeMatchesToggle}>
417
+ <button
418
+ type="button"
419
+ onClick={() => {
420
+ setShowMatches(false)
421
+ }}
422
+ disabled={!showMatches()}
423
+ class={cx(
424
+ styles().routeMatchesToggleBtn(!showMatches(), true),
425
+ )}
426
+ >
427
+ Routes
428
+ </button>
429
+ <button
430
+ type="button"
431
+ onClick={() => {
432
+ setShowMatches(true)
433
+ }}
434
+ disabled={showMatches()}
435
+ class={cx(
436
+ styles().routeMatchesToggleBtn(!!showMatches(), false),
437
+ )}
438
+ >
439
+ Matches
440
+ </button>
441
+ </div>
442
+ <div class={styles().detailsHeaderInfo}>
443
+ <div>age / staleTime / gcTime</div>
444
+ </div>
445
+ </div>
446
+ <div class={cx(styles().routesContainer)}>
447
+ {!showMatches() ? (
448
+ <RouteComp
449
+ routerState={routerState}
450
+ router={router}
451
+ route={router().routeTree}
452
+ isRoot
453
+ activeId={activeId}
454
+ setActiveId={setActiveId}
455
+ />
456
+ ) : (
457
+ <div>
458
+ {(routerState().pendingMatches?.length
459
+ ? routerState().pendingMatches
460
+ : routerState().matches
461
+ )?.map((match: any, _i: any) => {
462
+ return (
463
+ <div
464
+ role="button"
465
+ aria-label={`Open match details for ${match.id}`}
466
+ onClick={() =>
467
+ setActiveId(activeId() === match.id ? '' : match.id)
468
+ }
469
+ class={cx(styles().matchRow(match === activeMatch()))}
470
+ >
471
+ <div
472
+ class={cx(
473
+ styles().matchIndicator(getStatusColor(match)),
474
+ )}
475
+ />
476
+ <NavigateLink
477
+ left={
478
+ <NavigateButton
479
+ to={match.pathname}
480
+ params={match.params}
481
+ search={match.search}
482
+ router={router}
483
+ />
484
+ }
485
+ right={<AgeTicker match={match} router={router} />}
486
+ >
487
+ <code class={styles().matchID}>
488
+ {`${match.routeId === rootRouteId ? rootRouteId : match.pathname}`}
489
+ </code>
490
+ </NavigateLink>
491
+ </div>
492
+ )
493
+ })}
494
+ </div>
495
+ )}
496
+ </div>
497
+ </div>
498
+ {routerState().cachedMatches.length ? (
499
+ <div class={styles().cachedMatchesContainer}>
500
+ <div class={styles().detailsHeader}>
501
+ <div>Cached Matches</div>
502
+ <div class={styles().detailsHeaderInfo}>
503
+ age / staleTime / gcTime
504
+ </div>
505
+ </div>
506
+ <div>
507
+ {routerState().cachedMatches.map((match: any) => {
508
+ return (
509
+ <div
510
+ role="button"
511
+ aria-label={`Open match details for ${match.id}`}
512
+ onClick={() =>
513
+ setActiveId(activeId() === match.id ? '' : match.id)
514
+ }
515
+ class={cx(styles().matchRow(match === activeMatch()))}
516
+ >
517
+ <div
518
+ class={cx(
519
+ styles().matchIndicator(getStatusColor(match)),
520
+ )}
521
+ />
522
+ <NavigateLink
523
+ left={
524
+ <NavigateButton
525
+ to={match.pathname}
526
+ params={match.params}
527
+ search={match.search}
528
+ router={router}
529
+ />
530
+ }
531
+ right={<AgeTicker match={match} router={router} />}
532
+ >
533
+ <code class={styles().matchID}>{`${match.id}`}</code>
534
+ </NavigateLink>
535
+ </div>
536
+ )
537
+ })}
538
+ </div>
539
+ </div>
540
+ ) : null}
541
+ </div>
542
+ {activeMatch() && activeMatch()?.status ? (
543
+ <div class={styles().thirdContainer}>
544
+ <div class={styles().detailsHeader}>Match Details</div>
545
+ <div>
546
+ <div class={styles().matchDetails}>
547
+ <div
548
+ class={styles().matchStatus(
549
+ activeMatch()?.status,
550
+ activeMatch()?.isFetching,
551
+ )}
552
+ >
553
+ <div>
554
+ {activeMatch()?.status === 'success' &&
555
+ activeMatch()?.isFetching
556
+ ? 'fetching'
557
+ : activeMatch()?.status}
558
+ </div>
559
+ </div>
560
+ <div class={styles().matchDetailsInfoLabel}>
561
+ <div>ID:</div>
562
+ <div class={styles().matchDetailsInfo}>
563
+ <code>{activeMatch()?.id}</code>
564
+ </div>
565
+ </div>
566
+ <div class={styles().matchDetailsInfoLabel}>
567
+ <div>State:</div>
568
+ <div class={styles().matchDetailsInfo}>
569
+ {routerState().pendingMatches?.find(
570
+ (d: any) => d.id === activeMatch()?.id,
571
+ )
572
+ ? 'Pending'
573
+ : routerState().matches.find(
574
+ (d: any) => d.id === activeMatch()?.id,
575
+ )
576
+ ? 'Active'
577
+ : 'Cached'}
578
+ </div>
579
+ </div>
580
+ <div class={styles().matchDetailsInfoLabel}>
581
+ <div>Last Updated:</div>
582
+ <div class={styles().matchDetailsInfo}>
583
+ {activeMatch()?.updatedAt
584
+ ? new Date(activeMatch()?.updatedAt).toLocaleTimeString()
585
+ : 'N/A'}
586
+ </div>
587
+ </div>
588
+ </div>
589
+ </div>
590
+ {activeMatchLoaderData() ? (
591
+ <>
592
+ <div class={styles().detailsHeader}>Loader Data</div>
593
+ <div class={styles().detailsContent}>
594
+ <Explorer
595
+ label="loaderData"
596
+ value={activeMatchLoaderData}
597
+ defaultExpanded={{}}
598
+ />
599
+ </div>
600
+ </>
601
+ ) : null}
602
+ <div class={styles().detailsHeader}>Explorer</div>
603
+ <div class={styles().detailsContent}>
604
+ <Explorer
605
+ label="Match"
606
+ value={activeMatchValue}
607
+ defaultExpanded={{}}
608
+ />
609
+ </div>
610
+ </div>
611
+ ) : null}
612
+ {hasSearch() ? (
613
+ <div class={styles().fourthContainer}>
614
+ <div class={styles().detailsHeader}>Search Params</div>
615
+ <div class={styles().detailsContent}>
616
+ <Explorer
617
+ value={locationSearchValue}
618
+ defaultExpanded={Object.keys(
619
+ routerState().location.search,
620
+ ).reduce((obj: any, next) => {
621
+ obj[next] = {}
622
+ return obj
623
+ }, {})}
624
+ />
625
+ </div>
626
+ </div>
627
+ ) : null}
628
+ </div>
629
+ )
630
+ }
631
+
632
+ export default BaseTanStackRouterDevtoolsPanel