@tanstack/react-query-devtools 5.0.0-alpha.7 → 5.0.0-alpha.70

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 (127) hide show
  1. package/build/lib/__tests__/devtools.test.d.ts +1 -1
  2. package/build/lib/__tests__/devtools.test.d.ts.map +1 -0
  3. package/build/lib/__tests__/utils.d.ts +2 -1
  4. package/build/lib/__tests__/utils.d.ts.map +1 -0
  5. package/build/lib/devtools.cjs +65 -0
  6. package/build/lib/devtools.cjs.map +1 -0
  7. package/build/lib/devtools.d.ts +11 -91
  8. package/build/lib/devtools.d.ts.map +1 -0
  9. package/build/lib/devtools.js +56 -913
  10. package/build/lib/devtools.js.map +1 -1
  11. package/build/lib/devtools.legacy.cjs +65 -0
  12. package/build/lib/devtools.legacy.cjs.map +1 -0
  13. package/build/lib/devtools.legacy.js +63 -0
  14. package/build/lib/devtools.legacy.js.map +1 -0
  15. package/build/lib/index.cjs +18 -0
  16. package/build/lib/index.cjs.map +1 -0
  17. package/build/lib/index.d.ts +1 -1
  18. package/build/lib/index.d.ts.map +1 -0
  19. package/build/lib/index.js +11 -9
  20. package/build/lib/index.js.map +1 -1
  21. package/build/lib/index.legacy.cjs +18 -0
  22. package/build/lib/index.legacy.cjs.map +1 -0
  23. package/build/lib/index.legacy.js +16 -0
  24. package/build/lib/index.legacy.js.map +1 -0
  25. package/build/lib/index.prod.cjs +70 -0
  26. package/build/lib/index.prod.cjs.map +1 -0
  27. package/build/lib/{index.prod.d.ts → index.prod.d.ts/index.d.ts} +1 -1
  28. package/build/lib/index.prod.js +59 -1571
  29. package/build/lib/index.prod.js.map +1 -1
  30. package/build/lib/index.prod.legacy.cjs +70 -0
  31. package/build/lib/index.prod.legacy.cjs.map +1 -0
  32. package/build/lib/index.prod.legacy.js +68 -0
  33. package/build/lib/index.prod.legacy.js.map +1 -0
  34. package/package.json +23 -24
  35. package/src/__tests__/devtools.test.tsx +1 -1009
  36. package/src/devtools.tsx +67 -1306
  37. package/src/index.ts +6 -6
  38. package/build/lib/Explorer.d.ts +0 -50
  39. package/build/lib/Explorer.esm.js +0 -253
  40. package/build/lib/Explorer.esm.js.map +0 -1
  41. package/build/lib/Explorer.js +0 -287
  42. package/build/lib/Explorer.js.map +0 -1
  43. package/build/lib/Explorer.mjs +0 -253
  44. package/build/lib/Explorer.mjs.map +0 -1
  45. package/build/lib/Logo.d.ts +0 -1
  46. package/build/lib/Logo.esm.js +0 -32
  47. package/build/lib/Logo.esm.js.map +0 -1
  48. package/build/lib/Logo.js +0 -55
  49. package/build/lib/Logo.js.map +0 -1
  50. package/build/lib/Logo.mjs +0 -32
  51. package/build/lib/Logo.mjs.map +0 -1
  52. package/build/lib/__tests__/Explorer.test.d.ts +0 -1
  53. package/build/lib/_virtual/_rollupPluginBabelHelpers.esm.js +0 -17
  54. package/build/lib/_virtual/_rollupPluginBabelHelpers.esm.js.map +0 -1
  55. package/build/lib/_virtual/_rollupPluginBabelHelpers.js +0 -19
  56. package/build/lib/_virtual/_rollupPluginBabelHelpers.js.map +0 -1
  57. package/build/lib/_virtual/_rollupPluginBabelHelpers.mjs +0 -17
  58. package/build/lib/_virtual/_rollupPluginBabelHelpers.mjs.map +0 -1
  59. package/build/lib/devtools.esm.js +0 -899
  60. package/build/lib/devtools.esm.js.map +0 -1
  61. package/build/lib/devtools.mjs +0 -868
  62. package/build/lib/devtools.mjs.map +0 -1
  63. package/build/lib/index.esm.js +0 -11
  64. package/build/lib/index.esm.js.map +0 -1
  65. package/build/lib/index.mjs +0 -11
  66. package/build/lib/index.mjs.map +0 -1
  67. package/build/lib/index.prod.esm.js +0 -1559
  68. package/build/lib/index.prod.esm.js.map +0 -1
  69. package/build/lib/index.prod.mjs +0 -1528
  70. package/build/lib/index.prod.mjs.map +0 -1
  71. package/build/lib/noop.d.ts +0 -2
  72. package/build/lib/screenreader.d.ts +0 -3
  73. package/build/lib/screenreader.esm.js +0 -17
  74. package/build/lib/screenreader.esm.js.map +0 -1
  75. package/build/lib/screenreader.js +0 -40
  76. package/build/lib/screenreader.js.map +0 -1
  77. package/build/lib/screenreader.mjs +0 -17
  78. package/build/lib/screenreader.mjs.map +0 -1
  79. package/build/lib/styledComponents.d.ts +0 -9
  80. package/build/lib/styledComponents.esm.js +0 -95
  81. package/build/lib/styledComponents.esm.js.map +0 -1
  82. package/build/lib/styledComponents.js +0 -104
  83. package/build/lib/styledComponents.js.map +0 -1
  84. package/build/lib/styledComponents.mjs +0 -95
  85. package/build/lib/styledComponents.mjs.map +0 -1
  86. package/build/lib/theme.d.ts +0 -36
  87. package/build/lib/theme.esm.js +0 -32
  88. package/build/lib/theme.esm.js.map +0 -1
  89. package/build/lib/theme.js +0 -55
  90. package/build/lib/theme.js.map +0 -1
  91. package/build/lib/theme.mjs +0 -32
  92. package/build/lib/theme.mjs.map +0 -1
  93. package/build/lib/useLocalStorage.d.ts +0 -1
  94. package/build/lib/useLocalStorage.esm.js +0 -40
  95. package/build/lib/useLocalStorage.esm.js.map +0 -1
  96. package/build/lib/useLocalStorage.js +0 -63
  97. package/build/lib/useLocalStorage.js.map +0 -1
  98. package/build/lib/useLocalStorage.mjs +0 -40
  99. package/build/lib/useLocalStorage.mjs.map +0 -1
  100. package/build/lib/useMediaQuery.d.ts +0 -1
  101. package/build/lib/useMediaQuery.esm.js +0 -36
  102. package/build/lib/useMediaQuery.esm.js.map +0 -1
  103. package/build/lib/useMediaQuery.js +0 -59
  104. package/build/lib/useMediaQuery.js.map +0 -1
  105. package/build/lib/useMediaQuery.mjs +0 -36
  106. package/build/lib/useMediaQuery.mjs.map +0 -1
  107. package/build/lib/utils.d.ts +0 -79
  108. package/build/lib/utils.esm.js +0 -184
  109. package/build/lib/utils.esm.js.map +0 -1
  110. package/build/lib/utils.js +0 -218
  111. package/build/lib/utils.js.map +0 -1
  112. package/build/lib/utils.mjs +0 -184
  113. package/build/lib/utils.mjs.map +0 -1
  114. package/build/umd/index.development.js +0 -3195
  115. package/build/umd/index.development.js.map +0 -1
  116. package/build/umd/index.production.js +0 -2
  117. package/build/umd/index.production.js.map +0 -1
  118. package/src/Explorer.tsx +0 -385
  119. package/src/Logo.tsx +0 -32
  120. package/src/__tests__/Explorer.test.tsx +0 -150
  121. package/src/noop.ts +0 -7
  122. package/src/screenreader.tsx +0 -16
  123. package/src/styledComponents.ts +0 -110
  124. package/src/theme.tsx +0 -33
  125. package/src/useLocalStorage.ts +0 -52
  126. package/src/useMediaQuery.ts +0 -34
  127. package/src/utils.ts +0 -319
package/src/devtools.tsx CHANGED
@@ -1,1345 +1,106 @@
1
1
  'use client'
2
- import * as React from 'react'
2
+ import { useRef, useState, useEffect } from 'react'
3
+ import type { QueryClient } from '@tanstack/react-query'
4
+ import { useQueryClient, onlineManager } from '@tanstack/react-query'
3
5
  import type {
4
- QueryCache,
5
- QueryClient,
6
- QueryKey as QueryKeyType,
7
- Query,
8
- DefaultError,
9
- } from '@tanstack/react-query'
10
- import {
11
- useQueryClient,
12
- onlineManager,
13
- notifyManager,
14
- } from '@tanstack/react-query'
15
- import { rankItem } from '@tanstack/match-sorter-utils'
16
- import useLocalStorage from './useLocalStorage'
17
- import {
18
- isVerticalSide,
19
- sortFns,
20
- useIsMounted,
21
- getSidePanelStyle,
22
- minPanelSize,
23
- getResizeHandleStyle,
24
- getSidedProp,
25
- defaultPanelSize,
26
- displayValue,
27
- } from './utils'
28
- import type { Corner, Side } from './utils'
29
- import {
30
- Panel,
31
- QueryKeys,
32
- QueryKey,
33
- Button,
34
- Code,
35
- Input,
36
- Select,
37
- ActiveQueryPanel,
38
- } from './styledComponents'
39
- import ScreenReader from './screenreader'
40
- import { ThemeProvider, defaultTheme as theme } from './theme'
41
- import { getQueryStatusLabel, getQueryStatusColor } from './utils'
42
- import Explorer from './Explorer'
43
- import Logo from './Logo'
44
- import { useMemo } from 'react'
45
-
46
- export interface DevToolsErrorType {
47
- /**
48
- * The name of the error.
49
- */
50
- name: string
51
- /**
52
- * How the error is initialized.
53
- */
54
- initializer: (query: Query) => DefaultError
55
- }
6
+ DevtoolsButtonPosition,
7
+ DevtoolsPosition,
8
+ DevToolsErrorType,
9
+ } from '@tanstack/query-devtools'
10
+ import { TanstackQueryDevtools } from '@tanstack/query-devtools'
11
+ import React from 'react'
56
12
 
57
13
  export interface DevtoolsOptions {
58
14
  /**
59
15
  * Set this true if you want the dev tools to default to being open
60
16
  */
61
17
  initialIsOpen?: boolean
62
- /**
63
- * Use this to add props to the panel. For example, you can add className, style (merge and override default style), etc.
64
- */
65
- panelProps?: React.ComponentPropsWithoutRef<'div'>
66
- /**
67
- * 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.
68
- */
69
- closeButtonProps?: React.ComponentPropsWithoutRef<'button'>
70
- /**
71
- * Use this to add props to the toggle button. For example, you can add className, style (merge and override default style), onClick (extend default handler), etc.
72
- */
73
- toggleButtonProps?: React.ComponentPropsWithoutRef<'button'>
74
18
  /**
75
19
  * The position of the React Query logo to open and close the devtools panel.
20
+ * 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'
76
21
  * Defaults to 'bottom-left'.
77
22
  */
78
- position?: Corner
23
+ buttonPosition?: DevtoolsButtonPosition
79
24
  /**
80
25
  * The position of the React Query devtools panel.
26
+ * 'top' | 'bottom' | 'left' | 'right'
81
27
  * Defaults to 'bottom'.
82
28
  */
83
- panelPosition?: Side
84
- /**
85
- * Use this to render the devtools inside a different type of container element for a11y purposes.
86
- * Any string which corresponds to a valid intrinsic JSX element is allowed.
87
- * Defaults to 'aside'.
88
- */
89
- containerElement?: string | any
90
- /**
91
- * nonce for style element for CSP
92
- */
93
- styleNonce?: string
29
+ position?: DevtoolsPosition
94
30
  /**
95
31
  * Custom instance of QueryClient
96
32
  */
97
- queryClient?: QueryClient
33
+ client?: QueryClient
98
34
  /**
99
35
  * Use this so you can define custom errors that can be shown in the devtools.
100
36
  */
101
37
  errorTypes?: DevToolsErrorType[]
102
38
  }
103
39
 
104
- interface DevtoolsPanelOptions {
105
- /**
106
- * The standard React style object used to style a component with inline styles
107
- */
108
- style?: React.CSSProperties
109
- /**
110
- * The standard React className property used to style a component with classes
111
- */
112
- className?: string
113
- /**
114
- * A boolean variable indicating whether the panel is open or closed
115
- */
116
- isOpen?: boolean
117
- /**
118
- * nonce for style element for CSP
119
- */
120
- styleNonce?: string
121
- /**
122
- * A function that toggles the open and close state of the panel
123
- */
124
- setIsOpen: (isOpen: boolean) => void
125
- /**
126
- * Handles the opening and closing the devtools panel
127
- */
128
- onDragStart: (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => void
129
- /**
130
- * The position of the React Query devtools panel.
131
- * Defaults to 'bottom'.
132
- */
133
- position?: Side
134
- /**
135
- * Handles the panel position select change
136
- */
137
- onPositionChange?: (side: Side) => void
138
- /**
139
- * Show a close button inside the panel
140
- */
141
- showCloseButton?: boolean
142
- /**
143
- * 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.
144
- */
145
- closeButtonProps?: React.ComponentPropsWithoutRef<'button'>
146
- /**
147
- * Custom instance of QueryClient
148
- */
149
- queryClient?: QueryClient
150
- /**
151
- * Use this so you can define custom errors that can be shown in the devtools.
152
- */
153
- errorTypes?: DevToolsErrorType[]
154
- }
155
-
156
- export function ReactQueryDevtools({
157
- initialIsOpen,
158
- panelProps = {},
159
- closeButtonProps = {},
160
- toggleButtonProps = {},
161
- position = 'bottom-left',
162
- containerElement: Container = 'aside',
163
- queryClient,
164
- styleNonce,
165
- panelPosition: initialPanelPosition = 'bottom',
166
- errorTypes = [],
167
- }: DevtoolsOptions): React.ReactElement | null {
168
- const rootRef = React.useRef<HTMLDivElement>(null)
169
- const panelRef = React.useRef<HTMLDivElement>(null)
170
- const [isOpen, setIsOpen] = useLocalStorage(
171
- 'reactQueryDevtoolsOpen',
172
- initialIsOpen,
173
- )
174
- const [devtoolsHeight, setDevtoolsHeight] = useLocalStorage<number>(
175
- 'reactQueryDevtoolsHeight',
176
- defaultPanelSize,
177
- )
178
- const [devtoolsWidth, setDevtoolsWidth] = useLocalStorage<number>(
179
- 'reactQueryDevtoolsWidth',
180
- defaultPanelSize,
181
- )
182
-
183
- const [panelPosition = 'bottom', setPanelPosition] = useLocalStorage<Side>(
184
- 'reactQueryDevtoolsPanelPosition',
185
- initialPanelPosition,
186
- )
187
-
188
- const [isResolvedOpen, setIsResolvedOpen] = React.useState(false)
189
- const [isResizing, setIsResizing] = React.useState(false)
190
- const isMounted = useIsMounted()
191
-
192
- const handleDragStart = (
193
- panelElement: HTMLDivElement | null,
194
- startEvent: React.MouseEvent<HTMLDivElement, MouseEvent>,
195
- ) => {
196
- if (!panelElement) return
197
- if (startEvent.button !== 0) return // Only allow left click for drag
198
- const isVertical = isVerticalSide(panelPosition)
199
- setIsResizing(true)
200
-
201
- const { height, width } = panelElement.getBoundingClientRect()
202
- const startX = startEvent.clientX
203
- const startY = startEvent.clientY
204
- let newSize = 0
205
-
206
- const run = (moveEvent: MouseEvent) => {
207
- // prevent mouse selecting stuff with mouse drag
208
- moveEvent.preventDefault()
209
-
210
- // calculate the correct size based on mouse position and current panel position
211
- // hint: it is different formula for the opposite sides
212
- if (isVertical) {
213
- newSize =
214
- width +
215
- (panelPosition === 'right'
216
- ? startX - moveEvent.clientX
217
- : moveEvent.clientX - startX)
218
- setDevtoolsWidth(newSize)
219
- } else {
220
- newSize =
221
- height +
222
- (panelPosition === 'bottom'
223
- ? startY - moveEvent.clientY
224
- : moveEvent.clientY - startY)
225
- setDevtoolsHeight(newSize)
226
- }
227
-
228
- if (newSize < minPanelSize) {
229
- setIsOpen(false)
230
- } else {
231
- setIsOpen(true)
232
- }
233
- }
234
-
235
- const unsub = () => {
236
- if (isResizing) {
237
- setIsResizing(false)
238
- }
239
-
240
- document.removeEventListener('mousemove', run, false)
241
- document.removeEventListener('mouseUp', unsub, false)
40
+ function ReactQueryDevtoolsDev(
41
+ props: DevtoolsOptions,
42
+ ): React.ReactElement | null {
43
+ const queryClient = useQueryClient()
44
+ const client = props.client || queryClient
45
+ const ref = useRef<HTMLDivElement>(null)
46
+ const { buttonPosition, position, initialIsOpen, errorTypes } = props
47
+ const [devtools] = useState(
48
+ new TanstackQueryDevtools({
49
+ client: client,
50
+ queryFlavor: 'React Query',
51
+ version: '5',
52
+ onlineManager,
53
+ buttonPosition,
54
+ position,
55
+ initialIsOpen,
56
+ errorTypes,
57
+ }),
58
+ )
59
+
60
+ useEffect(() => {
61
+ devtools.setClient(client)
62
+ }, [client, devtools])
63
+
64
+ useEffect(() => {
65
+ if (buttonPosition) {
66
+ devtools.setButtonPosition(buttonPosition)
242
67
  }
68
+ }, [buttonPosition, devtools])
243
69
 
244
- document.addEventListener('mousemove', run, false)
245
- document.addEventListener('mouseup', unsub, false)
246
- }
247
-
248
- React.useEffect(() => {
249
- setIsResolvedOpen(isOpen ?? false)
250
- }, [isOpen, isResolvedOpen, setIsResolvedOpen])
251
-
252
- // Toggle panel visibility before/after transition (depending on direction).
253
- // Prevents focusing in a closed panel.
254
- React.useEffect(() => {
255
- const ref = panelRef.current
256
- if (ref) {
257
- const handlePanelTransitionStart = () => {
258
- if (isResolvedOpen) {
259
- ref.style.visibility = 'visible'
260
- }
261
- }
262
-
263
- const handlePanelTransitionEnd = () => {
264
- if (!isResolvedOpen) {
265
- ref.style.visibility = 'hidden'
266
- }
267
- }
268
-
269
- ref.addEventListener('transitionstart', handlePanelTransitionStart)
270
- ref.addEventListener('transitionend', handlePanelTransitionEnd)
271
-
272
- return () => {
273
- ref.removeEventListener('transitionstart', handlePanelTransitionStart)
274
- ref.removeEventListener('transitionend', handlePanelTransitionEnd)
275
- }
276
- }
277
- return
278
- }, [isResolvedOpen])
279
-
280
- React.useEffect(() => {
281
- if (isResolvedOpen && rootRef.current?.parentElement) {
282
- const { parentElement } = rootRef.current
283
- const styleProp = getSidedProp('padding', panelPosition)
284
- const isVertical = isVerticalSide(panelPosition)
285
-
286
- const previousPaddings = (({
287
- padding,
288
- paddingTop,
289
- paddingBottom,
290
- paddingLeft,
291
- paddingRight,
292
- }) => ({
293
- padding,
294
- paddingTop,
295
- paddingBottom,
296
- paddingLeft,
297
- paddingRight,
298
- }))(parentElement.style)
299
-
300
- const run = () => {
301
- // reset the padding
302
- parentElement.style.padding = '0px'
303
- parentElement.style.paddingTop = '0px'
304
- parentElement.style.paddingBottom = '0px'
305
- parentElement.style.paddingLeft = '0px'
306
- parentElement.style.paddingRight = '0px'
307
- // set the new padding based on the new panel position
308
-
309
- parentElement.style[styleProp] = `${
310
- isVertical ? devtoolsWidth : devtoolsHeight
311
- }px`
312
- }
313
-
314
- run()
315
-
316
- if (typeof window !== 'undefined') {
317
- window.addEventListener('resize', run)
318
-
319
- return () => {
320
- window.removeEventListener('resize', run)
321
- Object.entries(previousPaddings).forEach(
322
- ([property, previousValue]) => {
323
- parentElement.style[property as keyof typeof previousPaddings] =
324
- previousValue
325
- },
326
- )
327
- }
328
- }
70
+ useEffect(() => {
71
+ if (position) {
72
+ devtools.setPosition(position)
329
73
  }
330
- return
331
- }, [isResolvedOpen, panelPosition, devtoolsHeight, devtoolsWidth])
332
-
333
- const { style: panelStyle = {}, ...otherPanelProps } = panelProps
334
-
335
- const {
336
- style: toggleButtonStyle = {},
337
- onClick: onToggleClick,
338
- ...otherToggleButtonProps
339
- } = toggleButtonProps
340
-
341
- // get computed style based on panel position
342
- const style = getSidePanelStyle({
343
- position: panelPosition,
344
- devtoolsTheme: theme,
345
- isOpen: isResolvedOpen,
346
- height: devtoolsHeight,
347
- width: devtoolsWidth,
348
- isResizing,
349
- panelStyle,
350
- })
351
-
352
- // Do not render on the server
353
- if (!isMounted()) return null
74
+ }, [position, devtools])
354
75
 
355
- return (
356
- <Container
357
- ref={rootRef}
358
- className="ReactQueryDevtools"
359
- aria-label="React Query Devtools"
360
- >
361
- <ThemeProvider theme={theme}>
362
- <ReactQueryDevtoolsPanel
363
- ref={panelRef as any}
364
- queryClient={queryClient}
365
- styleNonce={styleNonce}
366
- position={panelPosition}
367
- onPositionChange={setPanelPosition}
368
- showCloseButton
369
- closeButtonProps={closeButtonProps}
370
- {...otherPanelProps}
371
- style={style}
372
- isOpen={isResolvedOpen}
373
- setIsOpen={setIsOpen}
374
- onDragStart={(e) => handleDragStart(panelRef.current, e)}
375
- errorTypes={errorTypes}
376
- />
377
- </ThemeProvider>
378
- {!isResolvedOpen ? (
379
- <button
380
- type="button"
381
- {...otherToggleButtonProps}
382
- aria-label="Open React Query Devtools"
383
- aria-controls="ReactQueryDevtoolsPanel"
384
- aria-haspopup="true"
385
- aria-expanded="false"
386
- onClick={(e) => {
387
- setIsOpen(true)
388
- onToggleClick?.(e)
389
- }}
390
- style={{
391
- background: 'none',
392
- border: 0,
393
- padding: 0,
394
- position: 'fixed',
395
- zIndex: 99999,
396
- display: 'inline-flex',
397
- fontSize: '1.5em',
398
- margin: '.5em',
399
- cursor: 'pointer',
400
- width: 'fit-content',
401
- ...(position === 'top-right'
402
- ? {
403
- top: '0',
404
- right: '0',
405
- }
406
- : position === 'top-left'
407
- ? {
408
- top: '0',
409
- left: '0',
410
- }
411
- : position === 'bottom-right'
412
- ? {
413
- bottom: '0',
414
- right: '0',
415
- }
416
- : {
417
- bottom: '0',
418
- left: '0',
419
- }),
420
- ...toggleButtonStyle,
421
- }}
422
- >
423
- <Logo aria-hidden />
424
- <ScreenReader text="Open React Query Devtools" />
425
- </button>
426
- ) : null}
427
- </Container>
428
- )
429
- }
430
-
431
- const useSubscribeToQueryCache = <T,>(
432
- queryCache: QueryCache,
433
- getSnapshot: () => T,
434
- skip: boolean = false,
435
- ): T => {
436
- return React.useSyncExternalStore(
437
- React.useCallback(
438
- (onStoreChange) => {
439
- if (!skip)
440
- return queryCache.subscribe(notifyManager.batchCalls(onStoreChange))
441
- return () => {
442
- return
443
- }
444
- },
445
- [queryCache, skip],
446
- ),
447
- getSnapshot,
448
- getSnapshot,
449
- )
450
- }
76
+ useEffect(() => {
77
+ devtools.setInitialIsOpen(initialIsOpen || false)
78
+ }, [initialIsOpen, devtools])
451
79
 
452
- export const ReactQueryDevtoolsPanel = React.forwardRef<
453
- HTMLDivElement,
454
- DevtoolsPanelOptions
455
- >(function ReactQueryDevtoolsPanel(props, ref): React.ReactElement {
456
- const {
457
- isOpen = true,
458
- styleNonce,
459
- setIsOpen,
460
- queryClient,
461
- onDragStart,
462
- onPositionChange,
463
- showCloseButton,
464
- position,
465
- closeButtonProps = {},
466
- errorTypes = [],
467
- ...panelProps
468
- } = props
469
-
470
- const { onClick: onCloseClick, ...otherCloseButtonProps } = closeButtonProps
471
-
472
- const client = useQueryClient(queryClient)
473
- const queryCache = client.getQueryCache()
474
-
475
- const [sort, setSort] = useLocalStorage(
476
- 'reactQueryDevtoolsSortFn',
477
- Object.keys(sortFns)[0],
478
- )
80
+ useEffect(() => {
81
+ devtools.setErrorTypes(errorTypes || [])
82
+ }, [errorTypes, devtools])
479
83
 
480
- const [filter, setFilter] = useLocalStorage('reactQueryDevtoolsFilter', '')
481
-
482
- const [baseSort, setBaseSort] = useLocalStorage(
483
- 'reactQueryDevtoolsBaseSort',
484
- 1,
485
- )
486
-
487
- const sortFn = React.useMemo(() => sortFns[sort as string], [sort])
488
-
489
- const queriesCount = useSubscribeToQueryCache(
490
- queryCache,
491
- () => queryCache.getAll().length,
492
- !isOpen,
493
- )
494
-
495
- const [activeQueryHash, setActiveQueryHash] = useLocalStorage(
496
- 'reactQueryDevtoolsActiveQueryHash',
497
- '',
498
- )
499
-
500
- const queries = React.useMemo(() => {
501
- const unsortedQueries = queryCache.getAll()
502
-
503
- if (queriesCount === 0) {
504
- return []
84
+ useEffect(() => {
85
+ if (ref.current) {
86
+ devtools.mount(ref.current)
505
87
  }
506
88
 
507
- const filtered = filter
508
- ? unsortedQueries.filter(
509
- (item) => rankItem(item.queryHash, filter).passed,
510
- )
511
- : [...unsortedQueries]
512
-
513
- const sorted = sortFn
514
- ? filtered.sort((a, b) => sortFn(a, b) * (baseSort as number))
515
- : filtered
516
-
517
- return sorted
518
- }, [baseSort, sortFn, filter, queriesCount, queryCache])
519
-
520
- const [isMockOffline, setMockOffline] = React.useState(false)
521
-
522
- return (
523
- <ThemeProvider theme={theme}>
524
- <Panel
525
- ref={ref}
526
- className="ReactQueryDevtoolsPanel"
527
- aria-label="React Query Devtools Panel"
528
- id="ReactQueryDevtoolsPanel"
529
- {...panelProps}
530
- style={{
531
- height: defaultPanelSize,
532
- position: 'relative',
533
- ...panelProps.style,
534
- }}
535
- >
536
- <style
537
- nonce={styleNonce}
538
- dangerouslySetInnerHTML={{
539
- __html: `
540
- .ReactQueryDevtoolsPanel * {
541
- scrollbar-color: ${theme.backgroundAlt} ${theme.gray};
542
- }
543
-
544
- .ReactQueryDevtoolsPanel *::-webkit-scrollbar, .ReactQueryDevtoolsPanel scrollbar {
545
- width: 1em;
546
- height: 1em;
547
- }
548
-
549
- .ReactQueryDevtoolsPanel *::-webkit-scrollbar-track, .ReactQueryDevtoolsPanel scrollbar-track {
550
- background: ${theme.backgroundAlt};
551
- }
552
-
553
- .ReactQueryDevtoolsPanel *::-webkit-scrollbar-thumb, .ReactQueryDevtoolsPanel scrollbar-thumb {
554
- background: ${theme.gray};
555
- border-radius: .5em;
556
- border: 3px solid ${theme.backgroundAlt};
557
- }
558
- `,
559
- }}
560
- />
561
- <div
562
- style={getResizeHandleStyle(position)}
563
- onMouseDown={onDragStart}
564
- ></div>
565
-
566
- {isOpen && (
567
- <div
568
- style={{
569
- flex: '1 1 500px',
570
- minHeight: '40%',
571
- maxHeight: '100%',
572
- overflow: 'auto',
573
- borderRight: `1px solid ${theme.grayAlt}`,
574
- display: 'flex',
575
- flexDirection: 'column',
576
- }}
577
- >
578
- <div
579
- style={{
580
- padding: '.5em',
581
- background: theme.backgroundAlt,
582
- display: 'flex',
583
- justifyContent: 'space-between',
584
- alignItems: 'center',
585
- }}
586
- >
587
- <button
588
- type="button"
589
- aria-label="Close React Query Devtools"
590
- aria-controls="ReactQueryDevtoolsPanel"
591
- aria-haspopup="true"
592
- aria-expanded="true"
593
- onClick={() => setIsOpen(false)}
594
- style={{
595
- display: 'inline-flex',
596
- background: 'none',
597
- border: 0,
598
- padding: 0,
599
- marginRight: '.5em',
600
- cursor: 'pointer',
601
- }}
602
- >
603
- <Logo aria-hidden />
604
- <ScreenReader text="Close React Query Devtools" />
605
- </button>
606
- <div
607
- style={{
608
- display: 'flex',
609
- flexDirection: 'column',
610
- }}
611
- >
612
- <div
613
- style={{
614
- display: 'flex',
615
- justifyContent: 'space-between',
616
- alignItems: 'center',
617
- marginBottom: '.5em',
618
- }}
619
- >
620
- <QueryStatusCount queryCache={queryCache} />
621
- {position && onPositionChange ? (
622
- <Select
623
- aria-label="Panel position"
624
- value={position}
625
- style={{ marginInlineStart: '.5em' }}
626
- onChange={(e) => onPositionChange(e.target.value as Side)}
627
- >
628
- <option value="left">Left</option>
629
- <option value="right">Right</option>
630
- <option value="top">Top</option>
631
- <option value="bottom">Bottom</option>
632
- </Select>
633
- ) : null}
634
- </div>
635
- <div
636
- style={{
637
- display: 'flex',
638
- alignItems: 'center',
639
- flexWrap: 'wrap',
640
- gap: '0.5em',
641
- }}
642
- >
643
- <Input
644
- placeholder="Filter"
645
- aria-label="Filter by queryhash"
646
- value={filter ?? ''}
647
- onChange={(e) => setFilter(e.target.value)}
648
- onKeyDown={(e) => {
649
- if (e.key === 'Escape') setFilter('')
650
- }}
651
- style={{
652
- flex: '1',
653
- width: '100%',
654
- }}
655
- />
656
- <Select
657
- aria-label="Sort queries"
658
- value={sort}
659
- onChange={(e) => setSort(e.target.value)}
660
- style={{
661
- flex: '1',
662
- minWidth: 75,
663
- marginRight: '.5em',
664
- }}
665
- >
666
- {Object.keys(sortFns).map((key) => (
667
- <option key={key} value={key}>
668
- Sort by {key}
669
- </option>
670
- ))}
671
- </Select>
672
- <Button
673
- type="button"
674
- onClick={() => setBaseSort((old) => old * -1)}
675
- style={{
676
- padding: '.3em .4em',
677
- marginRight: '.5em',
678
- }}
679
- >
680
- {baseSort === 1 ? '⬆ Asc' : '⬇ Desc'}
681
- </Button>
682
- <Button
683
- type="button"
684
- onClick={() => {
685
- if (isMockOffline) {
686
- onlineManager.setOnline(undefined)
687
- setMockOffline(false)
688
- window.dispatchEvent(new Event('online'))
689
- } else {
690
- onlineManager.setOnline(false)
691
- setMockOffline(true)
692
- }
693
- }}
694
- aria-label={
695
- isMockOffline
696
- ? 'Restore offline mock'
697
- : 'Mock offline behavior'
698
- }
699
- title={
700
- isMockOffline
701
- ? 'Restore offline mock'
702
- : 'Mock offline behavior'
703
- }
704
- style={{
705
- padding: '0',
706
- height: '2em',
707
- }}
708
- >
709
- <svg
710
- xmlns="http://www.w3.org/2000/svg"
711
- width="2em"
712
- height="2em"
713
- viewBox="0 0 24 24"
714
- stroke={isMockOffline ? theme.danger : 'currentColor'}
715
- fill="none"
716
- >
717
- {isMockOffline ? (
718
- <>
719
- <path stroke="none" d="M0 0h24v24H0z" fill="none" />
720
- <line x1="12" y1="18" x2="12.01" y2="18" />
721
- <path d="M9.172 15.172a4 4 0 0 1 5.656 0" />
722
- <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" />
723
- <path d="M3.515 9.515a12 12 0 0 1 3.544 -2.455m3.101 -.92a12 12 0 0 1 10.325 3.374" />
724
- <line x1="3" y1="3" x2="21" y2="21" />
725
- </>
726
- ) : (
727
- <>
728
- <path stroke="none" d="M0 0h24v24H0z" fill="none" />
729
- <line x1="12" y1="18" x2="12.01" y2="18" />
730
- <path d="M9.172 15.172a4 4 0 0 1 5.656 0" />
731
- <path d="M6.343 12.343a8 8 0 0 1 11.314 0" />
732
- <path d="M3.515 9.515c4.686 -4.687 12.284 -4.687 17 0" />
733
- </>
734
- )}
735
- </svg>
736
- <ScreenReader
737
- text={
738
- isMockOffline
739
- ? 'Restore offline mock'
740
- : 'Mock offline behavior'
741
- }
742
- />
743
- </Button>
744
- </div>
745
- </div>
746
- </div>
747
- <div
748
- style={{
749
- overflowY: 'auto',
750
- flex: '1',
751
- }}
752
- >
753
- {queries.map((query) => {
754
- return (
755
- <QueryRow
756
- queryKey={query.queryKey}
757
- activeQueryHash={activeQueryHash}
758
- setActiveQueryHash={setActiveQueryHash}
759
- key={query.queryHash}
760
- queryCache={queryCache}
761
- />
762
- )
763
- })}
764
- </div>
765
- </div>
766
- )}
767
-
768
- {activeQueryHash && isOpen ? (
769
- <ActiveQuery
770
- activeQueryHash={activeQueryHash}
771
- queryCache={queryCache}
772
- queryClient={client}
773
- errorTypes={errorTypes}
774
- />
775
- ) : null}
776
-
777
- {showCloseButton ? (
778
- <Button
779
- type="button"
780
- aria-controls="ReactQueryDevtoolsPanel"
781
- aria-haspopup="true"
782
- aria-expanded="true"
783
- {...(otherCloseButtonProps as Record<string, unknown>)}
784
- style={{
785
- position: 'absolute',
786
- zIndex: 99999,
787
- margin: '.5em',
788
- bottom: 0,
789
- left: 0,
790
- ...otherCloseButtonProps.style,
791
- }}
792
- onClick={(e) => {
793
- setIsOpen(false)
794
- onCloseClick?.(e)
795
- }}
796
- >
797
- Close
798
- </Button>
799
- ) : null}
800
- </Panel>
801
- </ThemeProvider>
802
- )
803
- })
804
-
805
- const ActiveQuery = ({
806
- queryCache,
807
- activeQueryHash,
808
- queryClient,
809
- errorTypes,
810
- }: {
811
- queryCache: QueryCache
812
- activeQueryHash: string
813
- queryClient: QueryClient
814
- errorTypes: DevToolsErrorType[]
815
- }) => {
816
- const activeQuery = useSubscribeToQueryCache(queryCache, () =>
817
- queryCache.getAll().find((query) => query.queryHash === activeQueryHash),
818
- )
819
-
820
- const activeQueryState = useSubscribeToQueryCache(
821
- queryCache,
822
- () =>
823
- queryCache.getAll().find((query) => query.queryHash === activeQueryHash)
824
- ?.state,
825
- )
826
-
827
- const isStale =
828
- useSubscribeToQueryCache(queryCache, () =>
829
- queryCache
830
- .getAll()
831
- .find((query) => query.queryHash === activeQueryHash)
832
- ?.isStale(),
833
- ) ?? false
834
-
835
- const observerCount =
836
- useSubscribeToQueryCache(queryCache, () =>
837
- queryCache
838
- .getAll()
839
- .find((query) => query.queryHash === activeQueryHash)
840
- ?.getObserversCount(),
841
- ) ?? 0
842
-
843
- const handleRefetch = () => {
844
- const promise = activeQuery?.fetch()
845
- promise?.catch(noop)
846
- }
847
-
848
- const currentErrorTypeName = useMemo(() => {
849
- if (activeQuery && activeQueryState?.error) {
850
- const errorType = errorTypes.find(
851
- (type) =>
852
- type.initializer(activeQuery).toString() ===
853
- activeQueryState.error?.toString(),
854
- )
855
- return errorType?.name
89
+ return () => {
90
+ devtools.unmount()
856
91
  }
857
- return undefined
858
- }, [activeQuery, activeQueryState?.error, errorTypes])
859
-
860
- if (!activeQuery || !activeQueryState) {
861
- return null
862
- }
863
-
864
- const triggerError = (errorType?: DevToolsErrorType) => {
865
- const error =
866
- errorType?.initializer(activeQuery) ??
867
- new Error('Unknown error from devtools')
868
-
869
- const __previousQueryOptions = activeQuery.options
870
-
871
- activeQuery.setState({
872
- status: 'error',
873
- error,
874
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
875
- fetchMeta: {
876
- ...activeQuery.state.fetchMeta,
877
- __previousQueryOptions,
878
- } as any,
879
- })
880
- }
881
-
882
- const restoreQueryAfterLoadingOrError = () => {
883
- activeQuery.fetch(
884
- (activeQuery.state.fetchMeta as any).__previousQueryOptions,
885
- {
886
- // Make sure this fetch will cancel the previous one
887
- cancelRefetch: true,
888
- },
889
- )
890
- }
891
-
892
- return (
893
- <ActiveQueryPanel>
894
- <div
895
- style={{
896
- padding: '.5em',
897
- background: theme.backgroundAlt,
898
- position: 'sticky',
899
- top: 0,
900
- zIndex: 1,
901
- }}
902
- >
903
- Query Details
904
- </div>
905
- <div
906
- style={{
907
- padding: '.5em',
908
- }}
909
- >
910
- <div
911
- style={{
912
- marginBottom: '.5em',
913
- display: 'flex',
914
- alignItems: 'flex-start',
915
- justifyContent: 'space-between',
916
- }}
917
- >
918
- <Code
919
- style={{
920
- lineHeight: '1.8em',
921
- }}
922
- >
923
- <pre
924
- style={{
925
- margin: 0,
926
- padding: 0,
927
- overflow: 'auto',
928
- }}
929
- >
930
- {displayValue(activeQuery.queryKey, true)}
931
- </pre>
932
- </Code>
933
- <span
934
- style={{
935
- padding: '0.3em .6em',
936
- borderRadius: '0.4em',
937
- fontWeight: 'bold',
938
- textShadow: '0 2px 10px black',
939
- background: getQueryStatusColor({
940
- queryState: activeQueryState,
941
- isStale: isStale,
942
- observerCount: observerCount,
943
- theme,
944
- }),
945
- flexShrink: 0,
946
- }}
947
- >
948
- {getQueryStatusLabel(activeQuery)}
949
- </span>
950
- </div>
951
- <div
952
- style={{
953
- marginBottom: '.5em',
954
- display: 'flex',
955
- alignItems: 'center',
956
- justifyContent: 'space-between',
957
- }}
958
- >
959
- Observers: <Code>{observerCount}</Code>
960
- </div>
961
- <div
962
- style={{
963
- display: 'flex',
964
- alignItems: 'center',
965
- justifyContent: 'space-between',
966
- }}
967
- >
968
- Last Updated:{' '}
969
- <Code>
970
- {new Date(activeQueryState.dataUpdatedAt).toLocaleTimeString()}
971
- </Code>
972
- </div>
973
- </div>
974
- <div
975
- style={{
976
- background: theme.backgroundAlt,
977
- padding: '.5em',
978
- position: 'sticky',
979
- top: 0,
980
- zIndex: 1,
981
- }}
982
- >
983
- Actions
984
- </div>
985
- <div
986
- style={{
987
- padding: '0.5em',
988
- display: 'flex',
989
- flexWrap: 'wrap',
990
- gap: '0.5em',
991
- alignItems: 'flex-end',
992
- }}
993
- >
994
- <Button
995
- type="button"
996
- onClick={handleRefetch}
997
- disabled={activeQueryState.fetchStatus === 'fetching'}
998
- style={{
999
- background: theme.active,
1000
- }}
1001
- >
1002
- Refetch
1003
- </Button>{' '}
1004
- <Button
1005
- type="button"
1006
- onClick={() => queryClient.invalidateQueries(activeQuery)}
1007
- style={{
1008
- background: theme.warning,
1009
- color: theme.inputTextColor,
1010
- }}
1011
- >
1012
- Invalidate
1013
- </Button>{' '}
1014
- <Button
1015
- type="button"
1016
- onClick={() => queryClient.resetQueries(activeQuery)}
1017
- style={{
1018
- background: theme.gray,
1019
- }}
1020
- >
1021
- Reset
1022
- </Button>{' '}
1023
- <Button
1024
- type="button"
1025
- onClick={() => queryClient.removeQueries(activeQuery)}
1026
- style={{
1027
- background: theme.danger,
1028
- }}
1029
- >
1030
- Remove
1031
- </Button>{' '}
1032
- <Button
1033
- type="button"
1034
- onClick={() => {
1035
- if (activeQuery.state.data === undefined) {
1036
- restoreQueryAfterLoadingOrError()
1037
- } else {
1038
- const __previousQueryOptions = activeQuery.options
1039
- // Trigger a fetch in order to trigger suspense as well.
1040
- activeQuery.fetch({
1041
- ...__previousQueryOptions,
1042
- queryFn: () => {
1043
- return new Promise(() => {
1044
- // Never resolve
1045
- })
1046
- },
1047
- gcTime: -1,
1048
- })
1049
- activeQuery.setState({
1050
- data: undefined,
1051
- status: 'pending',
1052
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
1053
- fetchMeta: {
1054
- ...activeQuery.state.fetchMeta,
1055
- __previousQueryOptions,
1056
- } as any,
1057
- })
1058
- }
1059
- }}
1060
- style={{
1061
- background: theme.paused,
1062
- }}
1063
- >
1064
- {activeQuery.state.status === 'pending' ? 'Restore' : 'Trigger'}{' '}
1065
- loading
1066
- </Button>{' '}
1067
- {errorTypes.length === 0 || activeQuery.state.status === 'error' ? (
1068
- <Button
1069
- type="button"
1070
- onClick={() => {
1071
- if (!activeQuery.state.error) {
1072
- triggerError()
1073
- } else {
1074
- queryClient.resetQueries(activeQuery)
1075
- }
1076
- }}
1077
- style={{
1078
- background: theme.danger,
1079
- }}
1080
- >
1081
- {activeQuery.state.status === 'error' ? 'Restore' : 'Trigger'} error
1082
- </Button>
1083
- ) : (
1084
- <label>
1085
- Trigger error:
1086
- <Select
1087
- value={currentErrorTypeName ?? ''}
1088
- style={{ marginInlineStart: '.5em' }}
1089
- onChange={(e) => {
1090
- const errorType = errorTypes.find(
1091
- (t) => t.name === e.target.value,
1092
- )
92
+ }, [devtools])
1093
93
 
1094
- triggerError(errorType)
1095
- }}
1096
- >
1097
- <option key="" value="" />
1098
- {errorTypes.map((errorType) => (
1099
- <option key={errorType.name} value={errorType.name}>
1100
- {errorType.name}
1101
- </option>
1102
- ))}
1103
- </Select>
1104
- </label>
1105
- )}
1106
- </div>
1107
- <div
1108
- style={{
1109
- background: theme.backgroundAlt,
1110
- padding: '.5em',
1111
- position: 'sticky',
1112
- top: 0,
1113
- zIndex: 1,
1114
- }}
1115
- >
1116
- Data Explorer
1117
- </div>
1118
- <div
1119
- style={{
1120
- padding: '.5em',
1121
- }}
1122
- >
1123
- <Explorer
1124
- label="Data"
1125
- value={activeQueryState.data}
1126
- defaultExpanded={{}}
1127
- copyable
1128
- />
1129
- </div>
1130
- <div
1131
- style={{
1132
- background: theme.backgroundAlt,
1133
- padding: '.5em',
1134
- position: 'sticky',
1135
- top: 0,
1136
- zIndex: 1,
1137
- }}
1138
- >
1139
- Query Explorer
1140
- </div>
1141
- <div
1142
- style={{
1143
- padding: '.5em',
1144
- }}
1145
- >
1146
- <Explorer
1147
- label="Query"
1148
- value={activeQuery}
1149
- defaultExpanded={{
1150
- queryKey: true,
1151
- }}
1152
- />
1153
- </div>
1154
- </ActiveQueryPanel>
1155
- )
1156
- }
1157
-
1158
- const QueryStatusCount = ({ queryCache }: { queryCache: QueryCache }) => {
1159
- const hasFresh = useSubscribeToQueryCache(
1160
- queryCache,
1161
- () =>
1162
- queryCache.getAll().filter((q) => getQueryStatusLabel(q) === 'fresh')
1163
- .length,
1164
- )
1165
- const hasFetching = useSubscribeToQueryCache(
1166
- queryCache,
1167
- () =>
1168
- queryCache.getAll().filter((q) => getQueryStatusLabel(q) === 'fetching')
1169
- .length,
1170
- )
1171
- const hasPaused = useSubscribeToQueryCache(
1172
- queryCache,
1173
- () =>
1174
- queryCache.getAll().filter((q) => getQueryStatusLabel(q) === 'paused')
1175
- .length,
1176
- )
1177
- const hasStale = useSubscribeToQueryCache(
1178
- queryCache,
1179
- () =>
1180
- queryCache.getAll().filter((q) => getQueryStatusLabel(q) === 'stale')
1181
- .length,
1182
- )
1183
- const hasInactive = useSubscribeToQueryCache(
1184
- queryCache,
1185
- () =>
1186
- queryCache.getAll().filter((q) => getQueryStatusLabel(q) === 'inactive')
1187
- .length,
1188
- )
1189
- return (
1190
- <QueryKeys>
1191
- <QueryKey
1192
- style={{
1193
- background: theme.success,
1194
- opacity: hasFresh ? 1 : 0.3,
1195
- }}
1196
- >
1197
- fresh <Code>({hasFresh})</Code>
1198
- </QueryKey>{' '}
1199
- <QueryKey
1200
- style={{
1201
- background: theme.active,
1202
- opacity: hasFetching ? 1 : 0.3,
1203
- }}
1204
- >
1205
- fetching <Code>({hasFetching})</Code>
1206
- </QueryKey>{' '}
1207
- <QueryKey
1208
- style={{
1209
- background: theme.paused,
1210
- opacity: hasPaused ? 1 : 0.3,
1211
- }}
1212
- >
1213
- paused <Code>({hasPaused})</Code>
1214
- </QueryKey>{' '}
1215
- <QueryKey
1216
- style={{
1217
- background: theme.warning,
1218
- color: 'black',
1219
- textShadow: '0',
1220
- opacity: hasStale ? 1 : 0.3,
1221
- }}
1222
- >
1223
- stale <Code>({hasStale})</Code>
1224
- </QueryKey>{' '}
1225
- <QueryKey
1226
- style={{
1227
- background: theme.gray,
1228
- opacity: hasInactive ? 1 : 0.3,
1229
- }}
1230
- >
1231
- inactive <Code>({hasInactive})</Code>
1232
- </QueryKey>
1233
- </QueryKeys>
1234
- )
94
+ return <div ref={ref}></div>
1235
95
  }
1236
96
 
1237
- interface QueryRowProps {
1238
- queryKey: QueryKeyType
1239
- setActiveQueryHash: (hash: string) => void
1240
- activeQueryHash?: string
1241
- queryCache: QueryCache
97
+ function ReactQueryDevtoolsProd(
98
+ _props: DevtoolsOptions,
99
+ ): React.ReactElement | null {
100
+ return null
1242
101
  }
1243
102
 
1244
- const QueryRow = React.memo(
1245
- ({
1246
- queryKey,
1247
- setActiveQueryHash,
1248
- activeQueryHash,
1249
- queryCache,
1250
- }: QueryRowProps) => {
1251
- const queryHash =
1252
- useSubscribeToQueryCache(
1253
- queryCache,
1254
- () => queryCache.find({ queryKey })?.queryHash,
1255
- ) ?? ''
1256
-
1257
- const queryState = useSubscribeToQueryCache(
1258
- queryCache,
1259
- () => queryCache.find({ queryKey })?.state,
1260
- )
1261
-
1262
- const isStale =
1263
- useSubscribeToQueryCache(queryCache, () =>
1264
- queryCache.find({ queryKey })?.isStale(),
1265
- ) ?? false
1266
-
1267
- const isDisabled =
1268
- useSubscribeToQueryCache(queryCache, () =>
1269
- queryCache.find({ queryKey })?.isDisabled(),
1270
- ) ?? false
1271
-
1272
- const observerCount =
1273
- useSubscribeToQueryCache(queryCache, () =>
1274
- queryCache.find({ queryKey })?.getObserversCount(),
1275
- ) ?? 0
1276
-
1277
- if (!queryState) {
1278
- return null
1279
- }
1280
-
1281
- return (
1282
- <div
1283
- role="button"
1284
- aria-label={`Open query details for ${queryHash}`}
1285
- onClick={() =>
1286
- setActiveQueryHash(activeQueryHash === queryHash ? '' : queryHash)
1287
- }
1288
- style={{
1289
- display: 'flex',
1290
- borderBottom: `solid 1px ${theme.grayAlt}`,
1291
- cursor: 'pointer',
1292
- background:
1293
- queryHash === activeQueryHash ? 'rgba(255,255,255,.1)' : undefined,
1294
- }}
1295
- >
1296
- <div
1297
- style={{
1298
- flex: '0 0 auto',
1299
- width: '2em',
1300
- height: '2em',
1301
- background: getQueryStatusColor({
1302
- queryState,
1303
- isStale,
1304
- observerCount,
1305
- theme,
1306
- }),
1307
- display: 'flex',
1308
- alignItems: 'center',
1309
- justifyContent: 'center',
1310
- fontWeight: 'bold',
1311
- textShadow: isStale ? '0' : '0 0 10px black',
1312
- color: isStale ? 'black' : 'white',
1313
- }}
1314
- >
1315
- {observerCount}
1316
- </div>
1317
- {isDisabled ? (
1318
- <div
1319
- style={{
1320
- flex: '0 0 auto',
1321
- height: '2em',
1322
- background: theme.gray,
1323
- display: 'flex',
1324
- alignItems: 'center',
1325
- fontWeight: 'bold',
1326
- padding: '0 0.5em',
1327
- }}
1328
- >
1329
- disabled
1330
- </div>
1331
- ) : null}
1332
- <Code
1333
- style={{
1334
- padding: '.5em',
1335
- }}
1336
- >
1337
- {`${queryHash}`}
1338
- </Code>
1339
- </div>
1340
- )
1341
- },
1342
- )
1343
-
1344
- // eslint-disable-next-line @typescript-eslint/no-empty-function
1345
- function noop() {}
103
+ export const ReactQueryDevtools: typeof ReactQueryDevtoolsDev =
104
+ process.env.NODE_ENV !== 'development'
105
+ ? ReactQueryDevtoolsProd
106
+ : ReactQueryDevtoolsDev