@tanstack/query-devtools 5.0.0-rc.1 → 5.0.0-rc.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build/index.js CHANGED
@@ -52,7 +52,7 @@ var TanstackQueryDevtools = class {
52
52
  if (this.Component) {
53
53
  Devtools = this.Component;
54
54
  } else {
55
- Devtools = lazy(() => import('./Devtools/66PXWIOF.js'));
55
+ Devtools = lazy(() => import('./Devtools/CIR3WCBJ.js'));
56
56
  this.Component = Devtools;
57
57
  }
58
58
  const _self$ = this;
package/build/index.jsx CHANGED
@@ -56,7 +56,7 @@ var TanstackQueryDevtools = class {
56
56
  if (this.Component) {
57
57
  Devtools = this.Component;
58
58
  } else {
59
- Devtools = lazy(() => import("./Devtools/ALVY75L5.jsx"));
59
+ Devtools = lazy(() => import("./Devtools/5OOWR6BQ.jsx"));
60
60
  this.Component = Devtools;
61
61
  }
62
62
  return <Devtools
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/query-devtools",
3
- "version": "5.0.0-rc.1",
3
+ "version": "5.0.0-rc.9",
4
4
  "description": "Developer tools to interact with and visualize the TanStack Query cache",
5
5
  "author": "tannerlinsley",
6
6
  "license": "MIT",
@@ -17,8 +17,8 @@
17
17
  "browser": {},
18
18
  "exports": {
19
19
  "solid": {
20
- "development": "./build/dev.jsx",
21
- "import": "./build/index.jsx"
20
+ "development": "./build/index.js",
21
+ "import": "./build/index.js"
22
22
  },
23
23
  "development": {
24
24
  "import": {
@@ -50,7 +50,7 @@
50
50
  "superjson": "^1.12.4",
51
51
  "tsup-preset-solid": "^2.0.1",
52
52
  "vite-plugin-solid": "^2.7.0",
53
- "@tanstack/query-core": "5.0.0-rc.1"
53
+ "@tanstack/query-core": "5.0.0-rc.6"
54
54
  },
55
55
  "scripts": {
56
56
  "clean": "rimraf ./build && rimraf ./coverage",
package/src/Devtools.tsx CHANGED
@@ -1273,7 +1273,8 @@ const QueryDetails = () => {
1273
1273
  label="Data"
1274
1274
  defaultExpanded={['Data']}
1275
1275
  value={activeQueryStateData()}
1276
- copyable={true}
1276
+ editable={true}
1277
+ activeQuery={activeQuery()}
1277
1278
  />
1278
1279
  </div>
1279
1280
  <div class={cx(styles.detailsHeader, 'tsqd-query-details-header')}>
package/src/Explorer.tsx CHANGED
@@ -4,8 +4,14 @@ import { clsx as cx } from 'clsx'
4
4
  import { Index, Match, Show, Switch, createMemo, createSignal } from 'solid-js'
5
5
  import { Key } from '@solid-primitives/keyed'
6
6
  import { tokens } from './theme'
7
- import { displayValue } from './utils'
8
- import { CopiedCopier, Copier, ErrorCopier } from './icons'
7
+ import {
8
+ deleteNestedDataByPath,
9
+ displayValue,
10
+ updateNestedDataByPath,
11
+ } from './utils'
12
+ import { CopiedCopier, Copier, ErrorCopier, List, Trash } from './icons'
13
+ import { useQueryDevtoolsContext } from './Context'
14
+ import type { Query } from '@tanstack/query-core'
9
15
 
10
16
  /**
11
17
  * Chunk elements in the array by size
@@ -73,7 +79,8 @@ const CopyButton = (props: { value: unknown }) => {
73
79
 
74
80
  return (
75
81
  <button
76
- class={styles.copyButton}
82
+ class={styles.actionButton}
83
+ title="Copy object to clipboard"
77
84
  aria-label={`${
78
85
  copyState() === 'NoCopy'
79
86
  ? 'Copy object to clipboard'
@@ -118,11 +125,60 @@ const CopyButton = (props: { value: unknown }) => {
118
125
  )
119
126
  }
120
127
 
128
+ const ClearArrayButton = (props: {
129
+ dataPath: Array<string>
130
+ activeQuery: Query
131
+ }) => {
132
+ const styles = getStyles()
133
+ const queryClient = useQueryDevtoolsContext().client
134
+
135
+ return (
136
+ <button
137
+ class={styles.actionButton}
138
+ title={'Remove all items'}
139
+ aria-label={'Remove all items'}
140
+ onClick={() => {
141
+ const oldData = props.activeQuery.state.data
142
+ const newData = updateNestedDataByPath(oldData, props.dataPath, [])
143
+ queryClient.setQueryData(props.activeQuery.queryKey, newData)
144
+ }}
145
+ >
146
+ <List />
147
+ </button>
148
+ )
149
+ }
150
+
151
+ const DeleteItemButton = (props: {
152
+ dataPath: Array<string>
153
+ activeQuery: Query
154
+ }) => {
155
+ const styles = getStyles()
156
+ const queryClient = useQueryDevtoolsContext().client
157
+
158
+ return (
159
+ <button
160
+ class={cx(styles.actionButton)}
161
+ title={'Delete item'}
162
+ aria-label={'Delete item'}
163
+ onClick={() => {
164
+ const oldData = props.activeQuery.state.data
165
+ const newData = deleteNestedDataByPath(oldData, props.dataPath)
166
+ queryClient.setQueryData(props.activeQuery.queryKey, newData)
167
+ }}
168
+ >
169
+ <Trash />
170
+ </button>
171
+ )
172
+ }
173
+
121
174
  type ExplorerProps = {
122
- copyable?: boolean
175
+ editable?: boolean
123
176
  label: string
124
177
  value: unknown
125
178
  defaultExpanded?: Array<string>
179
+ dataPath?: Array<string>
180
+ activeQuery?: Query
181
+ itemsDeletable?: boolean
126
182
  }
127
183
 
128
184
  function isIterable(x: any): x is Iterable<unknown> {
@@ -131,6 +187,7 @@ function isIterable(x: any): x is Iterable<unknown> {
131
187
 
132
188
  export default function Explorer(props: ExplorerProps) {
133
189
  const styles = getStyles()
190
+ const queryClient = useQueryDevtoolsContext().client
134
191
 
135
192
  const [expanded, setExpanded] = createSignal(
136
193
  (props.defaultExpanded || []).includes(props.label),
@@ -187,6 +244,8 @@ export default function Explorer(props: ExplorerProps) {
187
244
 
188
245
  const subEntryPages = createMemo(() => chunkArray(subEntries(), 100))
189
246
 
247
+ const currentDataPath = props.dataPath ?? []
248
+
190
249
  return (
191
250
  <div class={styles.entry}>
192
251
  <Show when={subEntryPages().length}>
@@ -201,8 +260,28 @@ export default function Explorer(props: ExplorerProps) {
201
260
  {subEntries().length} {subEntries().length > 1 ? `items` : `item`}
202
261
  </span>
203
262
  </button>
204
- <Show when={props.copyable}>
205
- <CopyButton value={props.value} />
263
+ <Show when={props.editable}>
264
+ <div class={styles.actions}>
265
+ <CopyButton value={props.value} />
266
+
267
+ <Show
268
+ when={props.itemsDeletable && props.activeQuery !== undefined}
269
+ >
270
+ <DeleteItemButton
271
+ activeQuery={props.activeQuery!}
272
+ dataPath={currentDataPath}
273
+ />
274
+ </Show>
275
+
276
+ <Show
277
+ when={type() === 'array' && props.activeQuery !== undefined}
278
+ >
279
+ <ClearArrayButton
280
+ activeQuery={props.activeQuery!}
281
+ dataPath={currentDataPath}
282
+ />
283
+ </Show>
284
+ </div>
206
285
  </Show>
207
286
  </div>
208
287
  <Show when={expanded()}>
@@ -215,7 +294,14 @@ export default function Explorer(props: ExplorerProps) {
215
294
  defaultExpanded={props.defaultExpanded}
216
295
  label={entry().label}
217
296
  value={entry().value}
218
- copyable={props.copyable}
297
+ editable={props.editable}
298
+ dataPath={[...currentDataPath, entry().label]}
299
+ activeQuery={props.activeQuery}
300
+ itemsDeletable={
301
+ type() === 'array' ||
302
+ type() === 'Iterable' ||
303
+ type() === 'object'
304
+ }
219
305
  />
220
306
  )
221
307
  }}
@@ -250,7 +336,9 @@ export default function Explorer(props: ExplorerProps) {
250
336
  defaultExpanded={props.defaultExpanded}
251
337
  label={entry().label}
252
338
  value={entry().value}
253
- copyable={props.copyable}
339
+ editable={props.editable}
340
+ dataPath={[...currentDataPath, entry().label]}
341
+ activeQuery={props.activeQuery}
254
342
  />
255
343
  )}
256
344
  </Key>
@@ -265,13 +353,50 @@ export default function Explorer(props: ExplorerProps) {
265
353
  </Show>
266
354
  </Show>
267
355
  <Show when={subEntryPages().length === 0}>
268
- <div
269
- style={{
270
- 'line-height': '1.125rem',
271
- }}
272
- >
273
- <span class={styles.label}>{props.label}:</span>{' '}
274
- <span class={styles.value}>{displayValue(props.value)}</span>
356
+ <div class={styles.row}>
357
+ <span class={styles.label}>{props.label}:</span>
358
+ <Show
359
+ when={
360
+ props.editable &&
361
+ props.activeQuery !== undefined &&
362
+ (type() === 'string' || type() === 'number')
363
+ }
364
+ fallback={
365
+ <span class={styles.value}>{displayValue(props.value)}</span>
366
+ }
367
+ >
368
+ <input
369
+ type={type() === 'number' ? 'number' : 'text'}
370
+ class={cx(styles.value, styles.editableInput)}
371
+ value={props.value as string | number}
372
+ onChange={(changeEvent) => {
373
+ const oldData = props.activeQuery!.state.data
374
+
375
+ const newData = updateNestedDataByPath(
376
+ oldData,
377
+ currentDataPath,
378
+ type() === 'number'
379
+ ? changeEvent.target.valueAsNumber
380
+ : changeEvent.target.value,
381
+ )
382
+
383
+ queryClient.setQueryData(props.activeQuery!.queryKey, newData)
384
+ }}
385
+ />
386
+ </Show>
387
+
388
+ <Show
389
+ when={
390
+ props.editable &&
391
+ props.itemsDeletable &&
392
+ props.activeQuery !== undefined
393
+ }
394
+ >
395
+ <DeleteItemButton
396
+ activeQuery={props.activeQuery!}
397
+ dataPath={currentDataPath}
398
+ />
399
+ </Show>
275
400
  </div>
276
401
  </Show>
277
402
  </div>
@@ -315,6 +440,7 @@ const getStyles = () => {
315
440
  align-items: center;
316
441
  line-height: 1.125rem;
317
442
  min-height: 1.125rem;
443
+ gap: ${size[2]};
318
444
  `,
319
445
  expanderButton: css`
320
446
  cursor: pointer;
@@ -352,8 +478,30 @@ const getStyles = () => {
352
478
  `,
353
479
  value: css`
354
480
  color: ${colors.purple[400]};
481
+ flex-grow: 1;
355
482
  `,
356
- copyButton: css`
483
+ actions: css`
484
+ display: inline-flex;
485
+ gap: ${size[2]};
486
+ `,
487
+ row: css`
488
+ display: inline-flex;
489
+ gap: ${size[2]};
490
+ width: 100%;
491
+ margin-bottom: ${size[0.5]};
492
+ line-height: 1.125rem;
493
+ `,
494
+ editableInput: css`
495
+ border: none;
496
+ padding: 0px ${size[1]};
497
+ flex-grow: 1;
498
+ background-color: ${colors.gray[900]};
499
+
500
+ &:hover {
501
+ background-color: ${colors.gray[800]};
502
+ }
503
+ `,
504
+ actionButton: css`
357
505
  background-color: transparent;
358
506
  border: none;
359
507
  display: inline-flex;
@@ -364,11 +512,17 @@ const getStyles = () => {
364
512
  width: ${size[3]};
365
513
  height: ${size[3]};
366
514
  position: relative;
367
- left: ${size[2]};
368
515
  z-index: 1;
369
516
 
370
- &:hover svg .copier {
371
- stroke: ${colors.gray[500]} !important;
517
+ &:hover svg {
518
+ .copier,
519
+ .list {
520
+ stroke: ${colors.gray[500]} !important;
521
+ }
522
+
523
+ .list-item {
524
+ stroke: ${colors.gray[700]};
525
+ }
372
526
  }
373
527
 
374
528
  &:focus-visible {