@tanstack/react-router-devtools 0.0.1-alpha.6 → 0.0.1-alpha.7

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tanstack/react-router-devtools",
3
3
  "author": "Tanner Linsley",
4
- "version": "0.0.1-alpha.6",
4
+ "version": "0.0.1-alpha.7",
5
5
  "license": "MIT",
6
6
  "repository": "tanstack/router",
7
7
  "homepage": "https://react-router.tanstack.com/",
@@ -35,7 +35,7 @@
35
35
  "src"
36
36
  ],
37
37
  "peerDependencies": {
38
- "@tanstack/react-router": "0.0.1-alpha.6",
38
+ "@tanstack/react-router": "0.0.1-alpha.7",
39
39
  "react": ">=16",
40
40
  "react-dom": ">=16"
41
41
  },
package/src/devtools.tsx CHANGED
@@ -3,7 +3,12 @@ import { Router, last } from '@tanstack/react-router'
3
3
  import { formatDistanceStrict } from 'date-fns'
4
4
 
5
5
  import useLocalStorage from './useLocalStorage'
6
- import { getStatusColor, useIsMounted, useSafeState } from './utils'
6
+ import {
7
+ getStatusColor,
8
+ multiSortBy,
9
+ useIsMounted,
10
+ useSafeState,
11
+ } from './utils'
7
12
  import { Panel, Button, Code, ActivePanel } from './styledComponents'
8
13
  import { ThemeProvider, defaultTheme as theme } from './theme'
9
14
  // import { getQueryStatusLabel, getQueryStatusColor } from './utils'
@@ -363,37 +368,12 @@ export const TanStackRouterDevtoolsPanel = React.forwardRef<
363
368
  router,
364
369
  ...panelProps
365
370
  } = props
366
- const routerExplorerValue = React.useMemo(() => {
367
- const {
368
- listeners,
369
- buildLocation,
370
- mount,
371
- update,
372
- buildNext,
373
- navigate,
374
- cancelMatches,
375
- loadLocation,
376
- cleanPreloadCache,
377
- loadRoute,
378
- matchRoutes,
379
- loadMatches,
380
- invalidateRoute,
381
- resolvePath,
382
- matchRoute,
383
- buildLink,
384
- __experimental__createSnapshot,
385
- destroy,
386
- ...rest
387
- } = router
388
-
389
- return rest
390
- }, [router.state])
391
371
 
392
372
  const rerender = React.useReducer(() => ({}), {})[1]
393
373
 
394
374
  React.useEffect(() => {
395
375
  let interval = setInterval(() => {
396
- router.cleanPreloadCache()
376
+ router.cleanMatchCache()
397
377
  // router.notify()
398
378
  rerender()
399
379
  }, 250)
@@ -408,30 +388,29 @@ export const TanStackRouterDevtoolsPanel = React.forwardRef<
408
388
  '',
409
389
  )
410
390
 
411
- const activeMatch = router.state.matches?.find(
412
- (d) => d.routeId === activeRouteId,
391
+ const [activeMatchId, setActiveMatchId] = useLocalStorage(
392
+ 'tanstackRouterDevtoolsActiveMatchId',
393
+ '',
413
394
  )
414
395
 
415
- const activeMatchExplorerValue = React.useMemo(() => {
416
- if (!activeMatch) {
417
- return {}
418
- }
419
-
420
- const {
421
- cancel,
422
- load,
423
- router,
424
- Link,
425
- MatchRoute,
426
- buildLink,
427
- linkProps,
428
- matchRoute,
429
- navigate,
430
- ...rest
431
- } = activeMatch
432
-
433
- return rest
434
- }, [activeMatch])
396
+ React.useEffect(() => {
397
+ setActiveMatchId('')
398
+ }, [activeRouteId])
399
+
400
+ const activeMatch =
401
+ Object.values(router.matchCache)?.find(
402
+ (d) => d.match.matchId === activeMatchId,
403
+ )?.match ?? router.state.matches?.find((d) => d.routeId === activeRouteId)
404
+
405
+ const matchCacheValues = multiSortBy(
406
+ Object.keys(router.matchCache)
407
+ .filter((key) => {
408
+ const cacheEntry = router.matchCache[key]!
409
+ return cacheEntry.gc > Date.now()
410
+ })
411
+ .map((key) => router.matchCache[key]!),
412
+ [(d) => (d.match.isFetching ? -1 : 1), (d) => -d.match.updatedAt!],
413
+ )
435
414
 
436
415
  return (
437
416
  <ThemeProvider theme={theme}>
@@ -550,11 +529,7 @@ export const TanStackRouterDevtoolsPanel = React.forwardRef<
550
529
  padding: '.5em',
551
530
  }}
552
531
  >
553
- <Explorer
554
- label="Router"
555
- value={routerExplorerValue}
556
- defaultExpanded={{}}
557
- />
532
+ <Explorer label="Router" value={router} defaultExpanded={{}} />
558
533
  </div>
559
534
  </div>
560
535
  </div>
@@ -578,7 +553,7 @@ export const TanStackRouterDevtoolsPanel = React.forwardRef<
578
553
  zIndex: 1,
579
554
  }}
580
555
  >
581
- Current Matches
556
+ Active Matches
582
557
  </div>
583
558
  {router.state.matches.map((match, i) => {
584
559
  return (
@@ -625,153 +600,162 @@ export const TanStackRouterDevtoolsPanel = React.forwardRef<
625
600
  </div>
626
601
  )
627
602
  })}
628
- <div
629
- style={{
630
- marginTop: '2rem',
631
- padding: '.5em',
632
- background: theme.backgroundAlt,
633
- position: 'sticky',
634
- top: 0,
635
- zIndex: 1,
636
- }}
637
- >
638
- Pending Matches
639
- </div>
640
- {router.state.pending?.matches.map((match, i) => {
641
- return (
603
+ {router.state.pending?.matches.length ? (
604
+ <>
642
605
  <div
643
- key={match.routeId || i}
644
- role="button"
645
- aria-label={`Open match details for ${match.routeId}`}
646
- onClick={() =>
647
- setActiveRouteId(
648
- activeRouteId === match.routeId ? '' : match.routeId,
649
- )
650
- }
651
606
  style={{
652
- display: 'flex',
653
- borderBottom: `solid 1px ${theme.grayAlt}`,
654
- cursor: 'pointer',
655
- background:
656
- match === activeMatch ? 'rgba(255,255,255,.1)' : undefined,
607
+ marginTop: '2rem',
608
+ padding: '.5em',
609
+ background: theme.backgroundAlt,
610
+ position: 'sticky',
611
+ top: 0,
612
+ zIndex: 1,
657
613
  }}
658
614
  >
659
- <div
660
- style={{
661
- flex: '0 0 auto',
662
- width: '1.3rem',
663
- height: '1.3rem',
664
- marginLeft: '.25rem',
665
- background: getStatusColor(match, theme),
666
- alignItems: 'center',
667
- justifyContent: 'center',
668
- fontWeight: 'bold',
669
- borderRadius: '.25rem',
670
- transition: 'all .2s ease-out',
671
- }}
672
- />
615
+ Pending Matches
616
+ </div>
617
+ {router.state.pending?.matches.map((match, i) => {
618
+ return (
619
+ <div
620
+ key={match.routeId || i}
621
+ role="button"
622
+ aria-label={`Open match details for ${match.routeId}`}
623
+ onClick={() =>
624
+ setActiveRouteId(
625
+ activeRouteId === match.routeId ? '' : match.routeId,
626
+ )
627
+ }
628
+ style={{
629
+ display: 'flex',
630
+ borderBottom: `solid 1px ${theme.grayAlt}`,
631
+ cursor: 'pointer',
632
+ background:
633
+ match === activeMatch
634
+ ? 'rgba(255,255,255,.1)'
635
+ : undefined,
636
+ }}
637
+ >
638
+ <div
639
+ style={{
640
+ flex: '0 0 auto',
641
+ width: '1.3rem',
642
+ height: '1.3rem',
643
+ marginLeft: '.25rem',
644
+ background: getStatusColor(match, theme),
645
+ alignItems: 'center',
646
+ justifyContent: 'center',
647
+ fontWeight: 'bold',
648
+ borderRadius: '.25rem',
649
+ transition: 'all .2s ease-out',
650
+ }}
651
+ />
673
652
 
674
- <Code
675
- style={{
676
- padding: '.5em',
653
+ <Code
654
+ style={{
655
+ padding: '.5em',
656
+ }}
657
+ >
658
+ {`${match.matchId}`}
659
+ </Code>
660
+ </div>
661
+ )
662
+ })}
663
+ </>
664
+ ) : null}
665
+ {matchCacheValues.length ? (
666
+ <>
667
+ <div
668
+ style={{
669
+ marginTop: '2rem',
670
+ padding: '.5em',
671
+ background: theme.backgroundAlt,
672
+ position: 'sticky',
673
+ top: 0,
674
+ zIndex: 1,
675
+ display: 'flex',
676
+ alignItems: 'center',
677
+ justifyContent: 'space-between',
678
+ }}
679
+ >
680
+ <div>Match Cache</div>
681
+ <Button
682
+ onClick={() => {
683
+ router.matchCache = {}
684
+ router.notify()
677
685
  }}
678
686
  >
679
- {`${match.matchId}`}
680
- </Code>
687
+ Clear
688
+ </Button>
681
689
  </div>
682
- )
683
- })}
684
- <div
685
- style={{
686
- marginTop: '2rem',
687
- padding: '.5em',
688
- background: theme.backgroundAlt,
689
- position: 'sticky',
690
- top: 0,
691
- zIndex: 1,
692
- }}
693
- >
694
- Preloading Matches
695
- </div>
696
- {Object.keys(router.preloadCache)
697
- .filter((key) => {
698
- const cacheEntry = router.preloadCache[key]!
699
- return (
700
- (cacheEntry.match.updatedAt ?? Date.now()) + cacheEntry.maxAge >
701
- Date.now()
702
- )
703
- })
704
- .map((key, i) => {
705
- const { match, maxAge } = router.preloadCache[key]!
706
-
707
- return (
708
- <div
709
- key={match.matchId || i}
710
- role="button"
711
- aria-label={`Open match details for ${match.matchId}`}
712
- onClick={() =>
713
- setActiveRouteId(
714
- activeRouteId === match.routeId ? '' : match.routeId,
715
- )
716
- }
717
- style={{
718
- display: 'flex',
719
- borderBottom: `solid 1px ${theme.grayAlt}`,
720
- cursor: 'pointer',
721
- background:
722
- match === activeMatch
723
- ? 'rgba(255,255,255,.1)'
724
- : undefined,
725
- }}
726
- >
690
+ {matchCacheValues.map((d, i) => {
691
+ const { match, gc } = d
692
+
693
+ return (
727
694
  <div
695
+ key={match.matchId || i}
696
+ role="button"
697
+ aria-label={`Open match details for ${match.matchId}`}
698
+ onClick={() =>
699
+ setActiveMatchId(
700
+ activeMatchId === match.matchId ? '' : match.matchId,
701
+ )
702
+ }
728
703
  style={{
729
704
  display: 'flex',
730
- flexDirection: 'column',
731
- padding: '.5rem',
732
- gap: '.3rem',
705
+ borderBottom: `solid 1px ${theme.grayAlt}`,
706
+ cursor: 'pointer',
707
+ background:
708
+ match === activeMatch
709
+ ? 'rgba(255,255,255,.1)'
710
+ : undefined,
733
711
  }}
734
712
  >
735
713
  <div
736
714
  style={{
737
715
  display: 'flex',
738
- alignItems: 'center',
739
- gap: '.5rem',
716
+ flexDirection: 'column',
717
+ padding: '.5rem',
718
+ gap: '.3rem',
740
719
  }}
741
720
  >
742
721
  <div
743
722
  style={{
744
- flex: '0 0 auto',
745
- width: '1.3rem',
746
- height: '1.3rem',
747
- background: getStatusColor(match, theme),
723
+ display: 'flex',
748
724
  alignItems: 'center',
749
- justifyContent: 'center',
750
- fontWeight: 'bold',
751
- borderRadius: '.25rem',
752
- transition: 'all .2s ease-out',
725
+ gap: '.5rem',
753
726
  }}
754
- />
755
- <Code>{`${match.matchId}`}</Code>
756
- </div>
757
- <span
758
- style={{
759
- opacity: '.5',
760
- }}
761
- >
762
- Expires{' '}
763
- {formatDistanceStrict(
764
- new Date(),
765
- new Date((match.updatedAt ?? Date.now()) + maxAge),
766
- {
727
+ >
728
+ <div
729
+ style={{
730
+ flex: '0 0 auto',
731
+ width: '1.3rem',
732
+ height: '1.3rem',
733
+ background: getStatusColor(match, theme),
734
+ alignItems: 'center',
735
+ justifyContent: 'center',
736
+ fontWeight: 'bold',
737
+ borderRadius: '.25rem',
738
+ transition: 'all .2s ease-out',
739
+ }}
740
+ />
741
+ <Code>{`${match.matchId}`}</Code>
742
+ </div>
743
+ <span
744
+ style={{
745
+ opacity: '.5',
746
+ }}
747
+ >
748
+ Expires{' '}
749
+ {formatDistanceStrict(new Date(), new Date(gc), {
767
750
  addSuffix: true,
768
- },
769
- )}
770
- </span>
751
+ })}
752
+ </span>
753
+ </div>
771
754
  </div>
772
- </div>
773
- )
774
- })}
755
+ )
756
+ })}
757
+ </>
758
+ ) : null}
775
759
  </div>
776
760
 
777
761
  {activeMatch ? (
@@ -884,7 +868,7 @@ export const TanStackRouterDevtoolsPanel = React.forwardRef<
884
868
  >
885
869
  <Explorer
886
870
  label="Match"
887
- value={activeMatchExplorerValue}
871
+ value={activeMatch}
888
872
  defaultExpanded={{}}
889
873
  />
890
874
  </div>
package/src/utils.ts CHANGED
@@ -149,3 +149,33 @@ function scheduleMicrotask(callback: () => void) {
149
149
  }),
150
150
  )
151
151
  }
152
+
153
+ export function multiSortBy<T>(
154
+ arr: T[],
155
+ accessors: ((item: T) => any)[] = [(d) => d],
156
+ ): T[] {
157
+ return arr
158
+ .map((d, i) => [d, i] as const)
159
+ .sort(([a, ai], [b, bi]) => {
160
+ for (const accessor of accessors) {
161
+ const ao = accessor(a)
162
+ const bo = accessor(b)
163
+
164
+ if (typeof ao === 'undefined') {
165
+ if (typeof bo === 'undefined') {
166
+ continue
167
+ }
168
+ return 1
169
+ }
170
+
171
+ if (ao === bo) {
172
+ continue
173
+ }
174
+
175
+ return ao > bo ? 1 : -1
176
+ }
177
+
178
+ return ai - bi
179
+ })
180
+ .map(([d]) => d)
181
+ }