@tanstack/react-query-devtools 5.0.0-alpha.11 → 5.0.0-alpha.14

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 (72) 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 +301 -0
  10. package/build/lib/CachePanel/CachePanel.esm.js.map +1 -0
  11. package/build/lib/CachePanel/CachePanel.js +306 -0
  12. package/build/lib/CachePanel/CachePanel.js.map +1 -0
  13. package/build/lib/CachePanel/CachePanel.mjs +301 -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/devtools.d.ts +3 -64
  30. package/build/lib/devtools.esm.js +2 -669
  31. package/build/lib/devtools.esm.js.map +1 -1
  32. package/build/lib/devtools.js +3 -669
  33. package/build/lib/devtools.js.map +1 -1
  34. package/build/lib/devtools.mjs +2 -639
  35. package/build/lib/devtools.mjs.map +1 -1
  36. package/build/lib/index.esm.js +2 -1
  37. package/build/lib/index.esm.js.map +1 -1
  38. package/build/lib/index.js +2 -1
  39. package/build/lib/index.js.map +1 -1
  40. package/build/lib/index.mjs +2 -1
  41. package/build/lib/index.mjs.map +1 -1
  42. package/build/lib/index.prod.esm.js +745 -713
  43. package/build/lib/index.prod.esm.js.map +1 -1
  44. package/build/lib/index.prod.js +742 -710
  45. package/build/lib/index.prod.js.map +1 -1
  46. package/build/lib/index.prod.mjs +745 -713
  47. package/build/lib/index.prod.mjs.map +1 -1
  48. package/build/lib/styledComponents.esm.js +8 -0
  49. package/build/lib/styledComponents.esm.js.map +1 -1
  50. package/build/lib/styledComponents.js +8 -0
  51. package/build/lib/styledComponents.js.map +1 -1
  52. package/build/lib/styledComponents.mjs +8 -0
  53. package/build/lib/styledComponents.mjs.map +1 -1
  54. package/build/lib/types.d.ts +11 -0
  55. package/build/lib/useSubscribeToQueryCache.d.ts +3 -0
  56. package/build/lib/useSubscribeToQueryCache.esm.js +15 -0
  57. package/build/lib/useSubscribeToQueryCache.esm.js.map +1 -0
  58. package/build/lib/useSubscribeToQueryCache.js +19 -0
  59. package/build/lib/useSubscribeToQueryCache.js.map +1 -0
  60. package/build/lib/useSubscribeToQueryCache.mjs +15 -0
  61. package/build/lib/useSubscribeToQueryCache.mjs.map +1 -0
  62. package/build/umd/index.development.js +2385 -2353
  63. package/build/umd/index.development.js.map +1 -1
  64. package/package.json +3 -3
  65. package/src/CachePanel/ActiveQuery.tsx +380 -0
  66. package/src/CachePanel/CachePanel.tsx +427 -0
  67. package/src/CachePanel/Header/QueryStatusCount.tsx +93 -0
  68. package/src/CachePanel/QueryRow.tsx +125 -0
  69. package/src/devtools.tsx +5 -1008
  70. package/src/styledComponents.ts +16 -0
  71. package/src/types.ts +12 -0
  72. package/src/useSubscribeToQueryCache.ts +26 -0
@@ -0,0 +1,427 @@
1
+ import React from 'react'
2
+ import type { QueryClient } from '@tanstack/react-query'
3
+ import { useQueryClient, onlineManager } from '@tanstack/react-query'
4
+ import { rankItem } from '@tanstack/match-sorter-utils'
5
+
6
+ import { Panel, Button, Input, Select } from '../styledComponents'
7
+ import useSubscribeToQueryCache from '../useSubscribeToQueryCache'
8
+ import QueryStatusCount from './Header/QueryStatusCount'
9
+ import QueryRow from './QueryRow'
10
+ import ActiveQuery from './ActiveQuery'
11
+ import type { Side } from '../utils'
12
+ import { sortFns, getResizeHandleStyle, defaultPanelSize } from '../utils'
13
+ import { ThemeProvider, defaultTheme as theme } from '../theme'
14
+ import type { DevToolsErrorType } from '../types'
15
+ import useLocalStorage from '../useLocalStorage'
16
+ import Logo from '../Logo'
17
+ import ScreenReader from '../screenreader'
18
+
19
+ interface DevtoolsPanelOptions {
20
+ /**
21
+ * The standard React style object used to style a component with inline styles
22
+ */
23
+ style?: React.CSSProperties
24
+ /**
25
+ * The standard React className property used to style a component with classes
26
+ */
27
+ className?: string
28
+ /**
29
+ * A boolean variable indicating whether the panel is open or closed
30
+ */
31
+ isOpen?: boolean
32
+ /**
33
+ * nonce for style element for CSP
34
+ */
35
+ styleNonce?: string
36
+ /**
37
+ * A function that toggles the open and close state of the panel
38
+ */
39
+ setIsOpen: (isOpen: boolean) => void
40
+ /**
41
+ * Handles the opening and closing the devtools panel
42
+ */
43
+ onDragStart: (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => void
44
+ /**
45
+ * The position of the React Query devtools panel.
46
+ * Defaults to 'bottom'.
47
+ */
48
+ position?: Side
49
+ /**
50
+ * Handles the panel position select change
51
+ */
52
+ onPositionChange?: (side: Side) => void
53
+ /**
54
+ * Show a close button inside the panel
55
+ */
56
+ showCloseButton?: boolean
57
+ /**
58
+ * 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.
59
+ */
60
+ closeButtonProps?: React.ComponentPropsWithoutRef<'button'>
61
+ /**
62
+ * Custom instance of QueryClient
63
+ */
64
+ queryClient?: QueryClient
65
+ /**
66
+ * Use this so you can define custom errors that can be shown in the devtools.
67
+ */
68
+ errorTypes?: DevToolsErrorType[]
69
+ }
70
+
71
+ export const ReactQueryDevtoolsPanel = React.forwardRef<
72
+ HTMLDivElement,
73
+ DevtoolsPanelOptions
74
+ >(function ReactQueryDevtoolsPanel(props, ref): React.ReactElement {
75
+ const {
76
+ isOpen = true,
77
+ styleNonce,
78
+ setIsOpen,
79
+ queryClient,
80
+ onDragStart,
81
+ onPositionChange,
82
+ showCloseButton,
83
+ position,
84
+ closeButtonProps = {},
85
+ errorTypes = [],
86
+ ...panelProps
87
+ } = props
88
+
89
+ const { onClick: onCloseClick, ...otherCloseButtonProps } = closeButtonProps
90
+
91
+ const client = useQueryClient(queryClient)
92
+ const queryCache = client.getQueryCache()
93
+
94
+ const [sort, setSort] = useLocalStorage(
95
+ 'reactQueryDevtoolsSortFn',
96
+ Object.keys(sortFns)[0],
97
+ )
98
+
99
+ const [filter, setFilter] = useLocalStorage('reactQueryDevtoolsFilter', '')
100
+
101
+ const [baseSort, setBaseSort] = useLocalStorage(
102
+ 'reactQueryDevtoolsBaseSort',
103
+ 1,
104
+ )
105
+
106
+ const sortFn = React.useMemo(() => sortFns[sort as string], [sort])
107
+
108
+ const queriesCount = useSubscribeToQueryCache(
109
+ queryCache,
110
+ () => queryCache.getAll().length,
111
+ !isOpen,
112
+ )
113
+
114
+ const [activeQueryHash, setActiveQueryHash] = useLocalStorage(
115
+ 'reactQueryDevtoolsActiveQueryHash',
116
+ '',
117
+ )
118
+
119
+ const queries = React.useMemo(() => {
120
+ const unsortedQueries = queryCache.getAll()
121
+
122
+ if (queriesCount === 0) {
123
+ return []
124
+ }
125
+
126
+ const filtered = filter
127
+ ? unsortedQueries.filter(
128
+ (item) => rankItem(item.queryHash, filter).passed,
129
+ )
130
+ : [...unsortedQueries]
131
+
132
+ const sorted = sortFn
133
+ ? filtered.sort((a, b) => sortFn(a, b) * (baseSort as number))
134
+ : filtered
135
+
136
+ return sorted
137
+ }, [baseSort, sortFn, filter, queriesCount, queryCache])
138
+
139
+ const [isMockOffline, setMockOffline] = React.useState(false)
140
+
141
+ return (
142
+ <ThemeProvider theme={theme}>
143
+ <Panel
144
+ ref={ref}
145
+ className="ReactQueryDevtoolsPanel"
146
+ aria-label="React Query Devtools Panel"
147
+ id="ReactQueryDevtoolsPanel"
148
+ {...panelProps}
149
+ style={{
150
+ height: defaultPanelSize,
151
+ position: 'relative',
152
+ ...panelProps.style,
153
+ }}
154
+ >
155
+ <style
156
+ nonce={styleNonce}
157
+ dangerouslySetInnerHTML={{
158
+ __html: `
159
+ .ReactQueryDevtoolsPanel * {
160
+ scrollbar-color: ${theme.backgroundAlt} ${theme.gray};
161
+ }
162
+
163
+ .ReactQueryDevtoolsPanel *::-webkit-scrollbar, .ReactQueryDevtoolsPanel scrollbar {
164
+ width: 1em;
165
+ height: 1em;
166
+ }
167
+
168
+ .ReactQueryDevtoolsPanel *::-webkit-scrollbar-track, .ReactQueryDevtoolsPanel scrollbar-track {
169
+ background: ${theme.backgroundAlt};
170
+ }
171
+
172
+ .ReactQueryDevtoolsPanel *::-webkit-scrollbar-thumb, .ReactQueryDevtoolsPanel scrollbar-thumb {
173
+ background: ${theme.gray};
174
+ border-radius: .5em;
175
+ border: 3px solid ${theme.backgroundAlt};
176
+ }
177
+ `,
178
+ }}
179
+ />
180
+ <div
181
+ style={getResizeHandleStyle(position)}
182
+ onMouseDown={onDragStart}
183
+ ></div>
184
+
185
+ {isOpen && (
186
+ <div
187
+ style={{
188
+ flex: '1 1 500px',
189
+ minHeight: '40%',
190
+ maxHeight: '100%',
191
+ overflow: 'auto',
192
+ borderRight: `1px solid ${theme.grayAlt}`,
193
+ display: 'flex',
194
+ flexDirection: 'column',
195
+ }}
196
+ >
197
+ <div
198
+ style={{
199
+ padding: '.5em',
200
+ background: theme.backgroundAlt,
201
+ display: 'flex',
202
+ justifyContent: 'space-between',
203
+ alignItems: 'center',
204
+ }}
205
+ >
206
+ <button
207
+ type="button"
208
+ aria-label="Close React Query Devtools"
209
+ aria-controls="ReactQueryDevtoolsPanel"
210
+ aria-haspopup="true"
211
+ aria-expanded="true"
212
+ onClick={() => setIsOpen(false)}
213
+ style={{
214
+ display: 'inline-flex',
215
+ background: 'none',
216
+ border: 0,
217
+ padding: 0,
218
+ marginRight: '.5em',
219
+ cursor: 'pointer',
220
+ }}
221
+ >
222
+ <Logo aria-hidden />
223
+ <ScreenReader text="Close React Query Devtools" />
224
+ </button>
225
+ <div
226
+ style={{
227
+ display: 'flex',
228
+ flexDirection: 'column',
229
+ }}
230
+ >
231
+ <div
232
+ style={{
233
+ display: 'flex',
234
+ justifyContent: 'space-between',
235
+ alignItems: 'center',
236
+ marginBottom: '.5em',
237
+ }}
238
+ >
239
+ <QueryStatusCount queryCache={queryCache} />
240
+ {position && onPositionChange ? (
241
+ <Select
242
+ aria-label="Panel position"
243
+ value={position}
244
+ style={{ marginInlineStart: '.5em' }}
245
+ onChange={(e) => onPositionChange(e.target.value as Side)}
246
+ >
247
+ <option value="left">Left</option>
248
+ <option value="right">Right</option>
249
+ <option value="top">Top</option>
250
+ <option value="bottom">Bottom</option>
251
+ </Select>
252
+ ) : null}
253
+ </div>
254
+ <div
255
+ style={{
256
+ display: 'flex',
257
+ alignItems: 'center',
258
+ flexWrap: 'wrap',
259
+ gap: '0.5em',
260
+ }}
261
+ >
262
+ <Input
263
+ placeholder="Filter"
264
+ aria-label="Filter by queryhash"
265
+ value={filter ?? ''}
266
+ onChange={(e) => setFilter(e.target.value)}
267
+ onKeyDown={(e) => {
268
+ if (e.key === 'Escape') setFilter('')
269
+ }}
270
+ style={{
271
+ flex: '1',
272
+ width: '100%',
273
+ }}
274
+ />
275
+ <Select
276
+ aria-label="Sort queries"
277
+ value={sort}
278
+ onChange={(e) => setSort(e.target.value)}
279
+ style={{
280
+ flex: '1',
281
+ minWidth: 75,
282
+ marginRight: '.5em',
283
+ }}
284
+ >
285
+ {Object.keys(sortFns).map((key) => (
286
+ <option key={key} value={key}>
287
+ Sort by {key}
288
+ </option>
289
+ ))}
290
+ </Select>
291
+ <Button
292
+ type="button"
293
+ onClick={() => setBaseSort((old) => old * -1)}
294
+ style={{
295
+ padding: '.3em .4em',
296
+ marginRight: '.5em',
297
+ }}
298
+ >
299
+ {baseSort === 1 ? '⬆ Asc' : '⬇ Desc'}
300
+ </Button>
301
+ <Button
302
+ type="button"
303
+ onClick={() => {
304
+ if (isMockOffline) {
305
+ onlineManager.setOnline(undefined)
306
+ setMockOffline(false)
307
+ window.dispatchEvent(new Event('online'))
308
+ } else {
309
+ onlineManager.setOnline(false)
310
+ setMockOffline(true)
311
+ window.dispatchEvent(new Event('offline'))
312
+ }
313
+ }}
314
+ aria-label={
315
+ isMockOffline
316
+ ? 'Restore offline mock'
317
+ : 'Mock offline behavior'
318
+ }
319
+ title={
320
+ isMockOffline
321
+ ? 'Restore offline mock'
322
+ : 'Mock offline behavior'
323
+ }
324
+ style={{
325
+ padding: '0',
326
+ height: '2em',
327
+ }}
328
+ >
329
+ <svg
330
+ xmlns="http://www.w3.org/2000/svg"
331
+ width="2em"
332
+ height="2em"
333
+ viewBox="0 0 24 24"
334
+ stroke={isMockOffline ? theme.danger : 'currentColor'}
335
+ fill="none"
336
+ >
337
+ {isMockOffline ? (
338
+ <>
339
+ <path stroke="none" d="M0 0h24v24H0z" fill="none" />
340
+ <line x1="12" y1="18" x2="12.01" y2="18" />
341
+ <path d="M9.172 15.172a4 4 0 0 1 5.656 0" />
342
+ <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" />
343
+ <path d="M3.515 9.515a12 12 0 0 1 3.544 -2.455m3.101 -.92a12 12 0 0 1 10.325 3.374" />
344
+ <line x1="3" y1="3" x2="21" y2="21" />
345
+ </>
346
+ ) : (
347
+ <>
348
+ <path stroke="none" d="M0 0h24v24H0z" fill="none" />
349
+ <line x1="12" y1="18" x2="12.01" y2="18" />
350
+ <path d="M9.172 15.172a4 4 0 0 1 5.656 0" />
351
+ <path d="M6.343 12.343a8 8 0 0 1 11.314 0" />
352
+ <path d="M3.515 9.515c4.686 -4.687 12.284 -4.687 17 0" />
353
+ </>
354
+ )}
355
+ </svg>
356
+ <ScreenReader
357
+ text={
358
+ isMockOffline
359
+ ? 'Restore offline mock'
360
+ : 'Mock offline behavior'
361
+ }
362
+ />
363
+ </Button>
364
+ </div>
365
+ </div>
366
+ </div>
367
+ <div
368
+ style={{
369
+ overflowY: 'auto',
370
+ flex: '1',
371
+ }}
372
+ >
373
+ {queries.map((query) => {
374
+ return (
375
+ <QueryRow
376
+ queryKey={query.queryKey}
377
+ activeQueryHash={activeQueryHash}
378
+ setActiveQueryHash={setActiveQueryHash}
379
+ key={query.queryHash}
380
+ queryCache={queryCache}
381
+ />
382
+ )
383
+ })}
384
+ </div>
385
+ </div>
386
+ )}
387
+
388
+ {activeQueryHash && isOpen ? (
389
+ <ActiveQuery
390
+ activeQueryHash={activeQueryHash}
391
+ queryCache={queryCache}
392
+ queryClient={client}
393
+ errorTypes={errorTypes}
394
+ />
395
+ ) : null}
396
+
397
+ {showCloseButton ? (
398
+ <Button
399
+ type="button"
400
+ aria-controls="ReactQueryDevtoolsPanel"
401
+ aria-haspopup="true"
402
+ aria-expanded="true"
403
+ {...(otherCloseButtonProps as Record<string, unknown>)}
404
+ style={{
405
+ position: 'absolute',
406
+ zIndex: 99999,
407
+ margin: '.5em',
408
+ bottom: 0,
409
+ left: 0,
410
+ ...otherCloseButtonProps.style,
411
+ }}
412
+ onClick={(e) => {
413
+ setIsOpen(false)
414
+ onCloseClick?.(e)
415
+ }}
416
+ >
417
+ Close
418
+ </Button>
419
+ ) : null}
420
+ </Panel>
421
+ </ThemeProvider>
422
+ )
423
+ })
424
+
425
+ ReactQueryDevtoolsPanel.displayName = 'ReactQueryDevtoolsPanel'
426
+
427
+ export default ReactQueryDevtoolsPanel
@@ -0,0 +1,93 @@
1
+ import React from 'react'
2
+ import type { QueryCache } from '@tanstack/react-query'
3
+
4
+ import { Code, QueryKey, QueryKeys } from '../../styledComponents'
5
+ import { getQueryStatusLabel } from '../../utils'
6
+ import useSubscribeToQueryCache from '../../useSubscribeToQueryCache'
7
+ import { defaultTheme as theme } from '../../theme'
8
+
9
+ /**
10
+ * Displays the number of queries for each status
11
+ */
12
+ export default function QueryStatusCount({
13
+ queryCache,
14
+ }: {
15
+ queryCache: QueryCache
16
+ }) {
17
+ const hasFresh = useSubscribeToQueryCache(
18
+ queryCache,
19
+ () =>
20
+ queryCache.getAll().filter((q) => getQueryStatusLabel(q) === 'fresh')
21
+ .length,
22
+ )
23
+ const hasFetching = useSubscribeToQueryCache(
24
+ queryCache,
25
+ () =>
26
+ queryCache.getAll().filter((q) => getQueryStatusLabel(q) === 'fetching')
27
+ .length,
28
+ )
29
+ const hasPaused = useSubscribeToQueryCache(
30
+ queryCache,
31
+ () =>
32
+ queryCache.getAll().filter((q) => getQueryStatusLabel(q) === 'paused')
33
+ .length,
34
+ )
35
+ const hasStale = useSubscribeToQueryCache(
36
+ queryCache,
37
+ () =>
38
+ queryCache.getAll().filter((q) => getQueryStatusLabel(q) === 'stale')
39
+ .length,
40
+ )
41
+ const hasInactive = useSubscribeToQueryCache(
42
+ queryCache,
43
+ () =>
44
+ queryCache.getAll().filter((q) => getQueryStatusLabel(q) === 'inactive')
45
+ .length,
46
+ )
47
+ return (
48
+ <QueryKeys>
49
+ <QueryKey
50
+ style={{
51
+ background: theme.success,
52
+ opacity: hasFresh ? 1 : 0.3,
53
+ }}
54
+ >
55
+ fresh <Code>({hasFresh})</Code>
56
+ </QueryKey>{' '}
57
+ <QueryKey
58
+ style={{
59
+ background: theme.active,
60
+ opacity: hasFetching ? 1 : 0.3,
61
+ }}
62
+ >
63
+ fetching <Code>({hasFetching})</Code>
64
+ </QueryKey>{' '}
65
+ <QueryKey
66
+ style={{
67
+ background: theme.paused,
68
+ opacity: hasPaused ? 1 : 0.3,
69
+ }}
70
+ >
71
+ paused <Code>({hasPaused})</Code>
72
+ </QueryKey>{' '}
73
+ <QueryKey
74
+ style={{
75
+ background: theme.warning,
76
+ color: 'black',
77
+ textShadow: '0',
78
+ opacity: hasStale ? 1 : 0.3,
79
+ }}
80
+ >
81
+ stale <Code>({hasStale})</Code>
82
+ </QueryKey>{' '}
83
+ <QueryKey
84
+ style={{
85
+ background: theme.gray,
86
+ opacity: hasInactive ? 1 : 0.3,
87
+ }}
88
+ >
89
+ inactive <Code>({hasInactive})</Code>
90
+ </QueryKey>
91
+ </QueryKeys>
92
+ )
93
+ }
@@ -0,0 +1,125 @@
1
+ import React from 'react'
2
+ import type {
3
+ QueryCache,
4
+ QueryKey as QueryKeyType,
5
+ } from '@tanstack/react-query'
6
+
7
+ import { Code } from '../styledComponents'
8
+
9
+ import useSubscribeToQueryCache from '../useSubscribeToQueryCache'
10
+ import { getQueryStatusColor } from '../utils'
11
+ import { defaultTheme as theme } from '../theme'
12
+
13
+ interface QueryRowProps {
14
+ queryKey: QueryKeyType
15
+ setActiveQueryHash: (hash: string) => void
16
+ activeQueryHash?: string
17
+ queryCache: QueryCache
18
+ }
19
+
20
+ /**
21
+ * Row for a query in the query list
22
+ */
23
+ const QueryRow = React.memo(
24
+ ({
25
+ queryKey,
26
+ setActiveQueryHash,
27
+ activeQueryHash,
28
+ queryCache,
29
+ }: QueryRowProps) => {
30
+ const queryHash =
31
+ useSubscribeToQueryCache(
32
+ queryCache,
33
+ () => queryCache.find({ queryKey })?.queryHash,
34
+ ) ?? ''
35
+
36
+ const queryState = useSubscribeToQueryCache(
37
+ queryCache,
38
+ () => queryCache.find({ queryKey })?.state,
39
+ )
40
+
41
+ const isStale =
42
+ useSubscribeToQueryCache(queryCache, () =>
43
+ queryCache.find({ queryKey })?.isStale(),
44
+ ) ?? false
45
+
46
+ const isDisabled =
47
+ useSubscribeToQueryCache(queryCache, () =>
48
+ queryCache.find({ queryKey })?.isDisabled(),
49
+ ) ?? false
50
+
51
+ const observerCount =
52
+ useSubscribeToQueryCache(queryCache, () =>
53
+ queryCache.find({ queryKey })?.getObserversCount(),
54
+ ) ?? 0
55
+
56
+ if (!queryState) {
57
+ return null
58
+ }
59
+
60
+ return (
61
+ <div
62
+ role="button"
63
+ aria-label={`Open query details for ${queryHash}`}
64
+ onClick={() =>
65
+ setActiveQueryHash(activeQueryHash === queryHash ? '' : queryHash)
66
+ }
67
+ style={{
68
+ display: 'flex',
69
+ borderBottom: `solid 1px ${theme.grayAlt}`,
70
+ cursor: 'pointer',
71
+ background:
72
+ queryHash === activeQueryHash ? 'rgba(255,255,255,.1)' : undefined,
73
+ }}
74
+ >
75
+ <div
76
+ style={{
77
+ flex: '0 0 auto',
78
+ width: '2em',
79
+ height: '2em',
80
+ background: getQueryStatusColor({
81
+ queryState,
82
+ isStale,
83
+ observerCount,
84
+ theme,
85
+ }),
86
+ display: 'flex',
87
+ alignItems: 'center',
88
+ justifyContent: 'center',
89
+ fontWeight: 'bold',
90
+ textShadow: isStale ? '0' : '0 0 10px black',
91
+ color: isStale ? 'black' : 'white',
92
+ }}
93
+ >
94
+ {observerCount}
95
+ </div>
96
+ {isDisabled ? (
97
+ <div
98
+ style={{
99
+ flex: '0 0 auto',
100
+ height: '2em',
101
+ background: theme.gray,
102
+ display: 'flex',
103
+ alignItems: 'center',
104
+ fontWeight: 'bold',
105
+ padding: '0 0.5em',
106
+ }}
107
+ >
108
+ disabled
109
+ </div>
110
+ ) : null}
111
+ <Code
112
+ style={{
113
+ padding: '.5em',
114
+ }}
115
+ >
116
+ {`${queryHash}`}
117
+ </Code>
118
+ </div>
119
+ )
120
+ },
121
+ )
122
+
123
+ QueryRow.displayName = 'QueryRow'
124
+
125
+ export default QueryRow