@tanstack/react-query-devtools 5.0.0-alpha.2 → 5.0.0-alpha.23

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 (88) hide show
  1. package/build/lib/CachePanel/ActiveQuery.d.ts +18 -0
  2. package/build/lib/CachePanel/ActiveQuery.esm.js +271 -0
  3. package/build/lib/CachePanel/ActiveQuery.esm.js.map +1 -0
  4. package/build/lib/CachePanel/ActiveQuery.js +275 -0
  5. package/build/lib/CachePanel/ActiveQuery.js.map +1 -0
  6. package/build/lib/CachePanel/ActiveQuery.mjs +257 -0
  7. package/build/lib/CachePanel/ActiveQuery.mjs.map +1 -0
  8. package/build/lib/CachePanel/CachePanel.d.ts +57 -0
  9. package/build/lib/CachePanel/CachePanel.esm.js +310 -0
  10. package/build/lib/CachePanel/CachePanel.esm.js.map +1 -0
  11. package/build/lib/CachePanel/CachePanel.js +315 -0
  12. package/build/lib/CachePanel/CachePanel.js.map +1 -0
  13. package/build/lib/CachePanel/CachePanel.mjs +310 -0
  14. package/build/lib/CachePanel/CachePanel.mjs.map +1 -0
  15. package/build/lib/CachePanel/Header/QueryStatusCount.d.ts +7 -0
  16. package/build/lib/CachePanel/Header/QueryStatusCount.esm.js +49 -0
  17. package/build/lib/CachePanel/Header/QueryStatusCount.esm.js.map +1 -0
  18. package/build/lib/CachePanel/Header/QueryStatusCount.js +53 -0
  19. package/build/lib/CachePanel/Header/QueryStatusCount.js.map +1 -0
  20. package/build/lib/CachePanel/Header/QueryStatusCount.mjs +49 -0
  21. package/build/lib/CachePanel/Header/QueryStatusCount.mjs.map +1 -0
  22. package/build/lib/CachePanel/QueryRow.d.ts +13 -0
  23. package/build/lib/CachePanel/QueryRow.esm.js +98 -0
  24. package/build/lib/CachePanel/QueryRow.esm.js.map +1 -0
  25. package/build/lib/CachePanel/QueryRow.js +102 -0
  26. package/build/lib/CachePanel/QueryRow.js.map +1 -0
  27. package/build/lib/CachePanel/QueryRow.mjs +82 -0
  28. package/build/lib/CachePanel/QueryRow.mjs.map +1 -0
  29. package/build/lib/Explorer.esm.js +2 -1
  30. package/build/lib/Explorer.esm.js.map +1 -1
  31. package/build/lib/Explorer.js +2 -1
  32. package/build/lib/Explorer.js.map +1 -1
  33. package/build/lib/Explorer.mjs +1 -0
  34. package/build/lib/Explorer.mjs.map +1 -1
  35. package/build/lib/devtools.d.ts +5 -48
  36. package/build/lib/devtools.esm.js +11 -550
  37. package/build/lib/devtools.esm.js.map +1 -1
  38. package/build/lib/devtools.js +12 -551
  39. package/build/lib/devtools.js.map +1 -1
  40. package/build/lib/devtools.mjs +7 -547
  41. package/build/lib/devtools.mjs.map +1 -1
  42. package/build/lib/index.esm.js +3 -1
  43. package/build/lib/index.esm.js.map +1 -1
  44. package/build/lib/index.js +3 -1
  45. package/build/lib/index.js.map +1 -1
  46. package/build/lib/index.mjs +3 -1
  47. package/build/lib/index.mjs.map +1 -1
  48. package/build/lib/index.prod.esm.js +821 -651
  49. package/build/lib/index.prod.esm.js.map +1 -1
  50. package/build/lib/index.prod.js +819 -650
  51. package/build/lib/index.prod.js.map +1 -1
  52. package/build/lib/index.prod.mjs +764 -625
  53. package/build/lib/index.prod.mjs.map +1 -1
  54. package/build/lib/styledComponents.d.ts +0 -1
  55. package/build/lib/styledComponents.esm.js +11 -1
  56. package/build/lib/styledComponents.esm.js.map +1 -1
  57. package/build/lib/styledComponents.js +11 -1
  58. package/build/lib/styledComponents.js.map +1 -1
  59. package/build/lib/styledComponents.mjs +11 -1
  60. package/build/lib/styledComponents.mjs.map +1 -1
  61. package/build/lib/theme.esm.js +1 -0
  62. package/build/lib/theme.esm.js.map +1 -1
  63. package/build/lib/theme.js +1 -0
  64. package/build/lib/theme.js.map +1 -1
  65. package/build/lib/theme.mjs +1 -0
  66. package/build/lib/theme.mjs.map +1 -1
  67. package/build/lib/types.d.ts +11 -0
  68. package/build/lib/useSubscribeToQueryCache.d.ts +3 -0
  69. package/build/lib/useSubscribeToQueryCache.esm.js +15 -0
  70. package/build/lib/useSubscribeToQueryCache.esm.js.map +1 -0
  71. package/build/lib/useSubscribeToQueryCache.js +19 -0
  72. package/build/lib/useSubscribeToQueryCache.js.map +1 -0
  73. package/build/lib/useSubscribeToQueryCache.mjs +15 -0
  74. package/build/lib/useSubscribeToQueryCache.mjs.map +1 -0
  75. package/build/umd/index.development.js +1667 -1529
  76. package/build/umd/index.development.js.map +1 -1
  77. package/build/umd/index.production.js.map +1 -1
  78. package/package.json +4 -4
  79. package/src/CachePanel/ActiveQuery.tsx +380 -0
  80. package/src/CachePanel/CachePanel.tsx +439 -0
  81. package/src/CachePanel/Header/QueryStatusCount.tsx +93 -0
  82. package/src/CachePanel/QueryRow.tsx +125 -0
  83. package/src/__tests__/Explorer.test.tsx +3 -2
  84. package/src/__tests__/devtools.test.tsx +229 -9
  85. package/src/devtools.tsx +10 -865
  86. package/src/styledComponents.ts +19 -1
  87. package/src/types.ts +12 -0
  88. package/src/useSubscribeToQueryCache.ts +26 -0
package/src/devtools.tsx CHANGED
@@ -1,45 +1,25 @@
1
1
  'use client'
2
2
  import * as React from 'react'
3
- import type {
4
- QueryCache,
5
- QueryClient,
6
- QueryKey as QueryKeyType,
7
- } from '@tanstack/react-query'
8
- import {
9
- useQueryClient,
10
- onlineManager,
11
- notifyManager,
12
- } from '@tanstack/react-query'
13
- import { rankItem } from '@tanstack/match-sorter-utils'
3
+ import type { QueryClient } from '@tanstack/react-query'
14
4
  import useLocalStorage from './useLocalStorage'
15
5
  import {
16
6
  isVerticalSide,
17
- sortFns,
18
7
  useIsMounted,
19
8
  getSidePanelStyle,
20
9
  minPanelSize,
21
- getResizeHandleStyle,
22
10
  getSidedProp,
23
11
  defaultPanelSize,
24
- displayValue,
25
12
  } from './utils'
26
13
  import type { Corner, Side } from './utils'
27
- import {
28
- Panel,
29
- QueryKeys,
30
- QueryKey,
31
- Button,
32
- Code,
33
- Input,
34
- Select,
35
- ActiveQueryPanel,
36
- } from './styledComponents'
37
14
  import ScreenReader from './screenreader'
38
15
  import { ThemeProvider, defaultTheme as theme } from './theme'
39
- import { getQueryStatusLabel, getQueryStatusColor } from './utils'
40
- import Explorer from './Explorer'
41
16
  import Logo from './Logo'
42
17
 
18
+ import type { DevToolsErrorType } from './types'
19
+ import ReactQueryDevtoolsPanel from './CachePanel/CachePanel'
20
+
21
+ export { default as ReactQueryDevtoolsPanel } from './CachePanel/CachePanel'
22
+
43
23
  export interface DevtoolsOptions {
44
24
  /**
45
25
  * Set this true if you want the dev tools to default to being open
@@ -81,54 +61,10 @@ export interface DevtoolsOptions {
81
61
  * Custom instance of QueryClient
82
62
  */
83
63
  queryClient?: QueryClient
84
- }
85
-
86
- interface DevtoolsPanelOptions {
87
- /**
88
- * The standard React style object used to style a component with inline styles
89
- */
90
- style?: React.CSSProperties
91
- /**
92
- * The standard React className property used to style a component with classes
93
- */
94
- className?: string
95
- /**
96
- * A boolean variable indicating whether the panel is open or closed
97
- */
98
- isOpen?: boolean
99
- /**
100
- * nonce for style element for CSP
101
- */
102
- styleNonce?: string
103
- /**
104
- * A function that toggles the open and close state of the panel
105
- */
106
- setIsOpen: (isOpen: boolean) => void
107
64
  /**
108
- * Handles the opening and closing the devtools panel
65
+ * Use this so you can define custom errors that can be shown in the devtools.
109
66
  */
110
- onDragStart: (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => void
111
- /**
112
- * The position of the React Query devtools panel.
113
- * Defaults to 'bottom'.
114
- */
115
- position?: Side
116
- /**
117
- * Handles the panel position select change
118
- */
119
- onPositionChange?: (side: Side) => void
120
- /**
121
- * Show a close button inside the panel
122
- */
123
- showCloseButton?: boolean
124
- /**
125
- * Use this to add props to the close button. For example, you can add className, style (merge and override default style), onClick (extend default handler), etc.
126
- */
127
- closeButtonProps?: React.ComponentPropsWithoutRef<'button'>
128
- /**
129
- * Custom instance of QueryClient
130
- */
131
- queryClient?: QueryClient
67
+ errorTypes?: DevToolsErrorType[]
132
68
  }
133
69
 
134
70
  export function ReactQueryDevtools({
@@ -141,6 +77,7 @@ export function ReactQueryDevtools({
141
77
  queryClient,
142
78
  styleNonce,
143
79
  panelPosition: initialPanelPosition = 'bottom',
80
+ errorTypes = [],
144
81
  }: DevtoolsOptions): React.ReactElement | null {
145
82
  const rootRef = React.useRef<HTMLDivElement>(null)
146
83
  const panelRef = React.useRef<HTMLDivElement>(null)
@@ -349,6 +286,7 @@ export function ReactQueryDevtools({
349
286
  isOpen={isResolvedOpen}
350
287
  setIsOpen={setIsOpen}
351
288
  onDragStart={(e) => handleDragStart(panelRef.current, e)}
289
+ errorTypes={errorTypes}
352
290
  />
353
291
  </ThemeProvider>
354
292
  {!isResolvedOpen ? (
@@ -403,796 +341,3 @@ export function ReactQueryDevtools({
403
341
  </Container>
404
342
  )
405
343
  }
406
-
407
- const useSubscribeToQueryCache = <T,>(
408
- queryCache: QueryCache,
409
- getSnapshot: () => T,
410
- skip: boolean = false,
411
- ): T => {
412
- return React.useSyncExternalStore(
413
- React.useCallback(
414
- (onStoreChange) => {
415
- if (!skip)
416
- return queryCache.subscribe(notifyManager.batchCalls(onStoreChange))
417
- return () => {
418
- return
419
- }
420
- },
421
- [queryCache, skip],
422
- ),
423
- getSnapshot,
424
- getSnapshot,
425
- )
426
- }
427
-
428
- export const ReactQueryDevtoolsPanel = React.forwardRef<
429
- HTMLDivElement,
430
- DevtoolsPanelOptions
431
- >(function ReactQueryDevtoolsPanel(props, ref): React.ReactElement {
432
- const {
433
- isOpen = true,
434
- styleNonce,
435
- setIsOpen,
436
- queryClient,
437
- onDragStart,
438
- onPositionChange,
439
- showCloseButton,
440
- position,
441
- closeButtonProps = {},
442
- ...panelProps
443
- } = props
444
-
445
- const { onClick: onCloseClick, ...otherCloseButtonProps } = closeButtonProps
446
-
447
- const client = useQueryClient(queryClient)
448
- const queryCache = client.getQueryCache()
449
-
450
- const [sort, setSort] = useLocalStorage(
451
- 'reactQueryDevtoolsSortFn',
452
- Object.keys(sortFns)[0],
453
- )
454
-
455
- const [filter, setFilter] = useLocalStorage('reactQueryDevtoolsFilter', '')
456
-
457
- const [baseSort, setBaseSort] = useLocalStorage(
458
- 'reactQueryDevtoolsBaseSort',
459
- 1,
460
- )
461
-
462
- const sortFn = React.useMemo(() => sortFns[sort as string], [sort])
463
-
464
- const queriesCount = useSubscribeToQueryCache(
465
- queryCache,
466
- () => queryCache.getAll().length,
467
- !isOpen,
468
- )
469
-
470
- const [activeQueryHash, setActiveQueryHash] = useLocalStorage(
471
- 'reactQueryDevtoolsActiveQueryHash',
472
- '',
473
- )
474
-
475
- const queries = React.useMemo(() => {
476
- const unsortedQueries = queryCache.getAll()
477
-
478
- if (queriesCount === 0) {
479
- return []
480
- }
481
-
482
- const filtered = filter
483
- ? unsortedQueries.filter(
484
- (item) => rankItem(item.queryHash, filter).passed,
485
- )
486
- : [...unsortedQueries]
487
-
488
- const sorted = sortFn
489
- ? filtered.sort((a, b) => sortFn(a, b) * (baseSort as number))
490
- : filtered
491
-
492
- return sorted
493
- }, [baseSort, sortFn, filter, queriesCount, queryCache])
494
-
495
- const [isMockOffline, setMockOffline] = React.useState(false)
496
-
497
- return (
498
- <ThemeProvider theme={theme}>
499
- <Panel
500
- ref={ref}
501
- className="ReactQueryDevtoolsPanel"
502
- aria-label="React Query Devtools Panel"
503
- id="ReactQueryDevtoolsPanel"
504
- {...panelProps}
505
- style={{
506
- height: defaultPanelSize,
507
- position: 'relative',
508
- ...panelProps.style,
509
- }}
510
- >
511
- <style
512
- nonce={styleNonce}
513
- dangerouslySetInnerHTML={{
514
- __html: `
515
- .ReactQueryDevtoolsPanel * {
516
- scrollbar-color: ${theme.backgroundAlt} ${theme.gray};
517
- }
518
-
519
- .ReactQueryDevtoolsPanel *::-webkit-scrollbar, .ReactQueryDevtoolsPanel scrollbar {
520
- width: 1em;
521
- height: 1em;
522
- }
523
-
524
- .ReactQueryDevtoolsPanel *::-webkit-scrollbar-track, .ReactQueryDevtoolsPanel scrollbar-track {
525
- background: ${theme.backgroundAlt};
526
- }
527
-
528
- .ReactQueryDevtoolsPanel *::-webkit-scrollbar-thumb, .ReactQueryDevtoolsPanel scrollbar-thumb {
529
- background: ${theme.gray};
530
- border-radius: .5em;
531
- border: 3px solid ${theme.backgroundAlt};
532
- }
533
- `,
534
- }}
535
- />
536
- <div
537
- style={getResizeHandleStyle(position)}
538
- onMouseDown={onDragStart}
539
- ></div>
540
-
541
- {isOpen && (
542
- <div
543
- style={{
544
- flex: '1 1 500px',
545
- minHeight: '40%',
546
- maxHeight: '100%',
547
- overflow: 'auto',
548
- borderRight: `1px solid ${theme.grayAlt}`,
549
- display: 'flex',
550
- flexDirection: 'column',
551
- }}
552
- >
553
- <div
554
- style={{
555
- padding: '.5em',
556
- background: theme.backgroundAlt,
557
- display: 'flex',
558
- justifyContent: 'space-between',
559
- alignItems: 'center',
560
- }}
561
- >
562
- <button
563
- type="button"
564
- aria-label="Close React Query Devtools"
565
- aria-controls="ReactQueryDevtoolsPanel"
566
- aria-haspopup="true"
567
- aria-expanded="true"
568
- onClick={() => setIsOpen(false)}
569
- style={{
570
- display: 'inline-flex',
571
- background: 'none',
572
- border: 0,
573
- padding: 0,
574
- marginRight: '.5em',
575
- cursor: 'pointer',
576
- }}
577
- >
578
- <Logo aria-hidden />
579
- <ScreenReader text="Close React Query Devtools" />
580
- </button>
581
- <div
582
- style={{
583
- display: 'flex',
584
- flexDirection: 'column',
585
- }}
586
- >
587
- <div
588
- style={{
589
- display: 'flex',
590
- justifyContent: 'space-between',
591
- alignItems: 'center',
592
- marginBottom: '.5em',
593
- }}
594
- >
595
- <QueryStatusCount queryCache={queryCache} />
596
- {position && onPositionChange ? (
597
- <Select
598
- aria-label="Panel position"
599
- value={position}
600
- style={{ marginInlineStart: '.5em' }}
601
- onChange={(e) => onPositionChange(e.target.value as Side)}
602
- >
603
- <option value="left">Left</option>
604
- <option value="right">Right</option>
605
- <option value="top">Top</option>
606
- <option value="bottom">Bottom</option>
607
- </Select>
608
- ) : null}
609
- </div>
610
- <div
611
- style={{
612
- display: 'flex',
613
- alignItems: 'center',
614
- }}
615
- >
616
- <Input
617
- placeholder="Filter"
618
- aria-label="Filter by queryhash"
619
- value={filter ?? ''}
620
- onChange={(e) => setFilter(e.target.value)}
621
- onKeyDown={(e) => {
622
- if (e.key === 'Escape') setFilter('')
623
- }}
624
- style={{
625
- flex: '1',
626
- marginRight: '.5em',
627
- width: '100%',
628
- }}
629
- />
630
- <Select
631
- aria-label="Sort queries"
632
- value={sort}
633
- onChange={(e) => setSort(e.target.value)}
634
- style={{
635
- flex: '1',
636
- minWidth: 75,
637
- marginRight: '.5em',
638
- }}
639
- >
640
- {Object.keys(sortFns).map((key) => (
641
- <option key={key} value={key}>
642
- Sort by {key}
643
- </option>
644
- ))}
645
- </Select>
646
- <Button
647
- type="button"
648
- onClick={() => setBaseSort((old) => old * -1)}
649
- style={{
650
- padding: '.3em .4em',
651
- marginRight: '.5em',
652
- }}
653
- >
654
- {baseSort === 1 ? '⬆ Asc' : '⬇ Desc'}
655
- </Button>
656
- <Button
657
- type="button"
658
- onClick={() => {
659
- if (isMockOffline) {
660
- onlineManager.setOnline(undefined)
661
- setMockOffline(false)
662
- window.dispatchEvent(new Event('online'))
663
- } else {
664
- onlineManager.setOnline(false)
665
- setMockOffline(true)
666
- }
667
- }}
668
- aria-label={
669
- isMockOffline
670
- ? 'Restore offline mock'
671
- : 'Mock offline behavior'
672
- }
673
- title={
674
- isMockOffline
675
- ? 'Restore offline mock'
676
- : 'Mock offline behavior'
677
- }
678
- style={{
679
- padding: '0',
680
- height: '2em',
681
- }}
682
- >
683
- <svg
684
- xmlns="http://www.w3.org/2000/svg"
685
- width="2em"
686
- height="2em"
687
- viewBox="0 0 24 24"
688
- stroke={isMockOffline ? theme.danger : 'currentColor'}
689
- fill="none"
690
- >
691
- {isMockOffline ? (
692
- <>
693
- <path stroke="none" d="M0 0h24v24H0z" fill="none" />
694
- <line x1="12" y1="18" x2="12.01" y2="18" />
695
- <path d="M9.172 15.172a4 4 0 0 1 5.656 0" />
696
- <path d="M6.343 12.343a7.963 7.963 0 0 1 3.864 -2.14m4.163 .155a7.965 7.965 0 0 1 3.287 2" />
697
- <path d="M3.515 9.515a12 12 0 0 1 3.544 -2.455m3.101 -.92a12 12 0 0 1 10.325 3.374" />
698
- <line x1="3" y1="3" x2="21" y2="21" />
699
- </>
700
- ) : (
701
- <>
702
- <path stroke="none" d="M0 0h24v24H0z" fill="none" />
703
- <line x1="12" y1="18" x2="12.01" y2="18" />
704
- <path d="M9.172 15.172a4 4 0 0 1 5.656 0" />
705
- <path d="M6.343 12.343a8 8 0 0 1 11.314 0" />
706
- <path d="M3.515 9.515c4.686 -4.687 12.284 -4.687 17 0" />
707
- </>
708
- )}
709
- </svg>
710
- <ScreenReader
711
- text={
712
- isMockOffline
713
- ? 'Restore offline mock'
714
- : 'Mock offline behavior'
715
- }
716
- />
717
- </Button>
718
- </div>
719
- </div>
720
- </div>
721
- <div
722
- style={{
723
- overflowY: 'auto',
724
- flex: '1',
725
- }}
726
- >
727
- {queries.map((query) => {
728
- return (
729
- <QueryRow
730
- queryKey={query.queryKey}
731
- activeQueryHash={activeQueryHash}
732
- setActiveQueryHash={setActiveQueryHash}
733
- key={query.queryHash}
734
- queryCache={queryCache}
735
- />
736
- )
737
- })}
738
- </div>
739
- </div>
740
- )}
741
-
742
- {activeQueryHash && isOpen ? (
743
- <ActiveQuery
744
- activeQueryHash={activeQueryHash}
745
- queryCache={queryCache}
746
- queryClient={client}
747
- />
748
- ) : null}
749
-
750
- {showCloseButton ? (
751
- <Button
752
- type="button"
753
- aria-controls="ReactQueryDevtoolsPanel"
754
- aria-haspopup="true"
755
- aria-expanded="true"
756
- {...(otherCloseButtonProps as Record<string, unknown>)}
757
- style={{
758
- position: 'absolute',
759
- zIndex: 99999,
760
- margin: '.5em',
761
- bottom: 0,
762
- left: 0,
763
- ...otherCloseButtonProps.style,
764
- }}
765
- onClick={(e) => {
766
- setIsOpen(false)
767
- onCloseClick?.(e)
768
- }}
769
- >
770
- Close
771
- </Button>
772
- ) : null}
773
- </Panel>
774
- </ThemeProvider>
775
- )
776
- })
777
-
778
- const ActiveQuery = ({
779
- queryCache,
780
- activeQueryHash,
781
- queryClient,
782
- }: {
783
- queryCache: QueryCache
784
- activeQueryHash: string
785
- queryClient: QueryClient
786
- }) => {
787
- const activeQuery = useSubscribeToQueryCache(queryCache, () =>
788
- queryCache.getAll().find((query) => query.queryHash === activeQueryHash),
789
- )
790
-
791
- const activeQueryState = useSubscribeToQueryCache(
792
- queryCache,
793
- () =>
794
- queryCache.getAll().find((query) => query.queryHash === activeQueryHash)
795
- ?.state,
796
- )
797
-
798
- const isStale =
799
- useSubscribeToQueryCache(queryCache, () =>
800
- queryCache
801
- .getAll()
802
- .find((query) => query.queryHash === activeQueryHash)
803
- ?.isStale(),
804
- ) ?? false
805
-
806
- const observerCount =
807
- useSubscribeToQueryCache(queryCache, () =>
808
- queryCache
809
- .getAll()
810
- .find((query) => query.queryHash === activeQueryHash)
811
- ?.getObserversCount(),
812
- ) ?? 0
813
-
814
- const handleRefetch = () => {
815
- const promise = activeQuery?.fetch()
816
- promise?.catch(noop)
817
- }
818
-
819
- if (!activeQuery || !activeQueryState) {
820
- return null
821
- }
822
-
823
- return (
824
- <ActiveQueryPanel>
825
- <div
826
- style={{
827
- padding: '.5em',
828
- background: theme.backgroundAlt,
829
- position: 'sticky',
830
- top: 0,
831
- zIndex: 1,
832
- }}
833
- >
834
- Query Details
835
- </div>
836
- <div
837
- style={{
838
- padding: '.5em',
839
- }}
840
- >
841
- <div
842
- style={{
843
- marginBottom: '.5em',
844
- display: 'flex',
845
- alignItems: 'flex-start',
846
- justifyContent: 'space-between',
847
- }}
848
- >
849
- <Code
850
- style={{
851
- lineHeight: '1.8em',
852
- }}
853
- >
854
- <pre
855
- style={{
856
- margin: 0,
857
- padding: 0,
858
- overflow: 'auto',
859
- }}
860
- >
861
- {displayValue(activeQuery.queryKey, true)}
862
- </pre>
863
- </Code>
864
- <span
865
- style={{
866
- padding: '0.3em .6em',
867
- borderRadius: '0.4em',
868
- fontWeight: 'bold',
869
- textShadow: '0 2px 10px black',
870
- background: getQueryStatusColor({
871
- queryState: activeQueryState,
872
- isStale: isStale,
873
- observerCount: observerCount,
874
- theme,
875
- }),
876
- flexShrink: 0,
877
- }}
878
- >
879
- {getQueryStatusLabel(activeQuery)}
880
- </span>
881
- </div>
882
- <div
883
- style={{
884
- marginBottom: '.5em',
885
- display: 'flex',
886
- alignItems: 'center',
887
- justifyContent: 'space-between',
888
- }}
889
- >
890
- Observers: <Code>{observerCount}</Code>
891
- </div>
892
- <div
893
- style={{
894
- display: 'flex',
895
- alignItems: 'center',
896
- justifyContent: 'space-between',
897
- }}
898
- >
899
- Last Updated:{' '}
900
- <Code>
901
- {new Date(activeQueryState.dataUpdatedAt).toLocaleTimeString()}
902
- </Code>
903
- </div>
904
- </div>
905
- <div
906
- style={{
907
- background: theme.backgroundAlt,
908
- padding: '.5em',
909
- position: 'sticky',
910
- top: 0,
911
- zIndex: 1,
912
- }}
913
- >
914
- Actions
915
- </div>
916
- <div
917
- style={{
918
- padding: '0.5em',
919
- }}
920
- >
921
- <Button
922
- type="button"
923
- onClick={handleRefetch}
924
- disabled={activeQueryState.fetchStatus === 'fetching'}
925
- style={{
926
- background: theme.active,
927
- }}
928
- >
929
- Refetch
930
- </Button>{' '}
931
- <Button
932
- type="button"
933
- onClick={() => queryClient.invalidateQueries(activeQuery)}
934
- style={{
935
- background: theme.warning,
936
- color: theme.inputTextColor,
937
- }}
938
- >
939
- Invalidate
940
- </Button>{' '}
941
- <Button
942
- type="button"
943
- onClick={() => queryClient.resetQueries(activeQuery)}
944
- style={{
945
- background: theme.gray,
946
- }}
947
- >
948
- Reset
949
- </Button>{' '}
950
- <Button
951
- type="button"
952
- onClick={() => queryClient.removeQueries(activeQuery)}
953
- style={{
954
- background: theme.danger,
955
- }}
956
- >
957
- Remove
958
- </Button>
959
- </div>
960
- <div
961
- style={{
962
- background: theme.backgroundAlt,
963
- padding: '.5em',
964
- position: 'sticky',
965
- top: 0,
966
- zIndex: 1,
967
- }}
968
- >
969
- Data Explorer
970
- </div>
971
- <div
972
- style={{
973
- padding: '.5em',
974
- }}
975
- >
976
- <Explorer
977
- label="Data"
978
- value={activeQueryState.data}
979
- defaultExpanded={{}}
980
- copyable
981
- />
982
- </div>
983
- <div
984
- style={{
985
- background: theme.backgroundAlt,
986
- padding: '.5em',
987
- position: 'sticky',
988
- top: 0,
989
- zIndex: 1,
990
- }}
991
- >
992
- Query Explorer
993
- </div>
994
- <div
995
- style={{
996
- padding: '.5em',
997
- }}
998
- >
999
- <Explorer
1000
- label="Query"
1001
- value={activeQuery}
1002
- defaultExpanded={{
1003
- queryKey: true,
1004
- }}
1005
- />
1006
- </div>
1007
- </ActiveQueryPanel>
1008
- )
1009
- }
1010
-
1011
- const QueryStatusCount = ({ queryCache }: { queryCache: QueryCache }) => {
1012
- const hasFresh = useSubscribeToQueryCache(
1013
- queryCache,
1014
- () =>
1015
- queryCache.getAll().filter((q) => getQueryStatusLabel(q) === 'fresh')
1016
- .length,
1017
- )
1018
- const hasFetching = useSubscribeToQueryCache(
1019
- queryCache,
1020
- () =>
1021
- queryCache.getAll().filter((q) => getQueryStatusLabel(q) === 'fetching')
1022
- .length,
1023
- )
1024
- const hasPaused = useSubscribeToQueryCache(
1025
- queryCache,
1026
- () =>
1027
- queryCache.getAll().filter((q) => getQueryStatusLabel(q) === 'paused')
1028
- .length,
1029
- )
1030
- const hasStale = useSubscribeToQueryCache(
1031
- queryCache,
1032
- () =>
1033
- queryCache.getAll().filter((q) => getQueryStatusLabel(q) === 'stale')
1034
- .length,
1035
- )
1036
- const hasInactive = useSubscribeToQueryCache(
1037
- queryCache,
1038
- () =>
1039
- queryCache.getAll().filter((q) => getQueryStatusLabel(q) === 'inactive')
1040
- .length,
1041
- )
1042
- return (
1043
- <QueryKeys>
1044
- <QueryKey
1045
- style={{
1046
- background: theme.success,
1047
- opacity: hasFresh ? 1 : 0.3,
1048
- }}
1049
- >
1050
- fresh <Code>({hasFresh})</Code>
1051
- </QueryKey>{' '}
1052
- <QueryKey
1053
- style={{
1054
- background: theme.active,
1055
- opacity: hasFetching ? 1 : 0.3,
1056
- }}
1057
- >
1058
- fetching <Code>({hasFetching})</Code>
1059
- </QueryKey>{' '}
1060
- <QueryKey
1061
- style={{
1062
- background: theme.paused,
1063
- opacity: hasPaused ? 1 : 0.3,
1064
- }}
1065
- >
1066
- paused <Code>({hasPaused})</Code>
1067
- </QueryKey>{' '}
1068
- <QueryKey
1069
- style={{
1070
- background: theme.warning,
1071
- color: 'black',
1072
- textShadow: '0',
1073
- opacity: hasStale ? 1 : 0.3,
1074
- }}
1075
- >
1076
- stale <Code>({hasStale})</Code>
1077
- </QueryKey>{' '}
1078
- <QueryKey
1079
- style={{
1080
- background: theme.gray,
1081
- opacity: hasInactive ? 1 : 0.3,
1082
- }}
1083
- >
1084
- inactive <Code>({hasInactive})</Code>
1085
- </QueryKey>
1086
- </QueryKeys>
1087
- )
1088
- }
1089
-
1090
- interface QueryRowProps {
1091
- queryKey: QueryKeyType
1092
- setActiveQueryHash: (hash: string) => void
1093
- activeQueryHash?: string
1094
- queryCache: QueryCache
1095
- }
1096
-
1097
- const QueryRow = React.memo(
1098
- ({
1099
- queryKey,
1100
- setActiveQueryHash,
1101
- activeQueryHash,
1102
- queryCache,
1103
- }: QueryRowProps) => {
1104
- const queryHash =
1105
- useSubscribeToQueryCache(
1106
- queryCache,
1107
- () => queryCache.find({ queryKey })?.queryHash,
1108
- ) ?? ''
1109
-
1110
- const queryState = useSubscribeToQueryCache(
1111
- queryCache,
1112
- () => queryCache.find({ queryKey })?.state,
1113
- )
1114
-
1115
- const isStale =
1116
- useSubscribeToQueryCache(queryCache, () =>
1117
- queryCache.find({ queryKey })?.isStale(),
1118
- ) ?? false
1119
-
1120
- const isDisabled =
1121
- useSubscribeToQueryCache(queryCache, () =>
1122
- queryCache.find({ queryKey })?.isDisabled(),
1123
- ) ?? false
1124
-
1125
- const observerCount =
1126
- useSubscribeToQueryCache(queryCache, () =>
1127
- queryCache.find({ queryKey })?.getObserversCount(),
1128
- ) ?? 0
1129
-
1130
- if (!queryState) {
1131
- return null
1132
- }
1133
-
1134
- return (
1135
- <div
1136
- role="button"
1137
- aria-label={`Open query details for ${queryHash}`}
1138
- onClick={() =>
1139
- setActiveQueryHash(activeQueryHash === queryHash ? '' : queryHash)
1140
- }
1141
- style={{
1142
- display: 'flex',
1143
- borderBottom: `solid 1px ${theme.grayAlt}`,
1144
- cursor: 'pointer',
1145
- background:
1146
- queryHash === activeQueryHash ? 'rgba(255,255,255,.1)' : undefined,
1147
- }}
1148
- >
1149
- <div
1150
- style={{
1151
- flex: '0 0 auto',
1152
- width: '2em',
1153
- height: '2em',
1154
- background: getQueryStatusColor({
1155
- queryState,
1156
- isStale,
1157
- observerCount,
1158
- theme,
1159
- }),
1160
- display: 'flex',
1161
- alignItems: 'center',
1162
- justifyContent: 'center',
1163
- fontWeight: 'bold',
1164
- textShadow: isStale ? '0' : '0 0 10px black',
1165
- color: isStale ? 'black' : 'white',
1166
- }}
1167
- >
1168
- {observerCount}
1169
- </div>
1170
- {isDisabled ? (
1171
- <div
1172
- style={{
1173
- flex: '0 0 auto',
1174
- height: '2em',
1175
- background: theme.gray,
1176
- display: 'flex',
1177
- alignItems: 'center',
1178
- fontWeight: 'bold',
1179
- padding: '0 0.5em',
1180
- }}
1181
- >
1182
- disabled
1183
- </div>
1184
- ) : null}
1185
- <Code
1186
- style={{
1187
- padding: '.5em',
1188
- }}
1189
- >
1190
- {`${queryHash}`}
1191
- </Code>
1192
- </div>
1193
- )
1194
- },
1195
- )
1196
-
1197
- // eslint-disable-next-line @typescript-eslint/no-empty-function
1198
- function noop() {}