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

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/src/Explorer.tsx CHANGED
@@ -9,8 +9,8 @@ import {
9
9
  displayValue,
10
10
  updateNestedDataByPath,
11
11
  } from './utils'
12
- import { CopiedCopier, Copier, ErrorCopier, List, Trash } from './icons'
13
- import { useQueryDevtoolsContext } from './Context'
12
+ import { Check, CopiedCopier, Copier, ErrorCopier, List, Trash } from './icons'
13
+ import { useQueryDevtoolsContext, useTheme } from './Context'
14
14
  import type { Query } from '@tanstack/query-core'
15
15
 
16
16
  /**
@@ -37,12 +37,15 @@ export function chunkArray<T extends { label: string; value: unknown }>(
37
37
  }
38
38
 
39
39
  const Expander = (props: { expanded: boolean }) => {
40
- const styles = getStyles()
40
+ const theme = useTheme()
41
+ const styles = createMemo(() => {
42
+ return theme() === 'dark' ? darkStyles : lightStyles
43
+ })
41
44
 
42
45
  return (
43
46
  <span
44
47
  class={cx(
45
- styles.expander,
48
+ styles().expander,
46
49
  css`
47
50
  transform: rotate(${props.expanded ? 90 : 0}deg);
48
51
  `,
@@ -74,12 +77,15 @@ const Expander = (props: { expanded: boolean }) => {
74
77
 
75
78
  type CopyState = 'NoCopy' | 'SuccessCopy' | 'ErrorCopy'
76
79
  const CopyButton = (props: { value: unknown }) => {
77
- const styles = getStyles()
80
+ const theme = useTheme()
81
+ const styles = createMemo(() => {
82
+ return theme() === 'dark' ? darkStyles : lightStyles
83
+ })
78
84
  const [copyState, setCopyState] = createSignal<CopyState>('NoCopy')
79
85
 
80
86
  return (
81
87
  <button
82
- class={styles.actionButton}
88
+ class={styles().actionButton}
83
89
  title="Copy object to clipboard"
84
90
  aria-label={`${
85
91
  copyState() === 'NoCopy'
@@ -115,7 +121,7 @@ const CopyButton = (props: { value: unknown }) => {
115
121
  <Copier />
116
122
  </Match>
117
123
  <Match when={copyState() === 'SuccessCopy'}>
118
- <CopiedCopier />
124
+ <CopiedCopier theme={theme()} />
119
125
  </Match>
120
126
  <Match when={copyState() === 'ErrorCopy'}>
121
127
  <ErrorCopier />
@@ -129,12 +135,15 @@ const ClearArrayButton = (props: {
129
135
  dataPath: Array<string>
130
136
  activeQuery: Query
131
137
  }) => {
132
- const styles = getStyles()
138
+ const theme = useTheme()
139
+ const styles = createMemo(() => {
140
+ return theme() === 'dark' ? darkStyles : lightStyles
141
+ })
133
142
  const queryClient = useQueryDevtoolsContext().client
134
143
 
135
144
  return (
136
145
  <button
137
- class={styles.actionButton}
146
+ class={styles().actionButton}
138
147
  title={'Remove all items'}
139
148
  aria-label={'Remove all items'}
140
149
  onClick={() => {
@@ -152,12 +161,15 @@ const DeleteItemButton = (props: {
152
161
  dataPath: Array<string>
153
162
  activeQuery: Query
154
163
  }) => {
155
- const styles = getStyles()
164
+ const theme = useTheme()
165
+ const styles = createMemo(() => {
166
+ return theme() === 'dark' ? darkStyles : lightStyles
167
+ })
156
168
  const queryClient = useQueryDevtoolsContext().client
157
169
 
158
170
  return (
159
171
  <button
160
- class={cx(styles.actionButton)}
172
+ class={cx(styles().actionButton)}
161
173
  title={'Delete item'}
162
174
  aria-label={'Delete item'}
163
175
  onClick={() => {
@@ -171,6 +183,43 @@ const DeleteItemButton = (props: {
171
183
  )
172
184
  }
173
185
 
186
+ const ToggleValueButton = (props: {
187
+ dataPath: Array<string>
188
+ activeQuery: Query
189
+ value: boolean
190
+ }) => {
191
+ const theme = useTheme()
192
+ const styles = createMemo(() => {
193
+ return theme() === 'dark' ? darkStyles : lightStyles
194
+ })
195
+ const queryClient = useQueryDevtoolsContext().client
196
+
197
+ return (
198
+ <button
199
+ class={cx(
200
+ styles().actionButton,
201
+ css`
202
+ width: ${tokens.size[3.5]};
203
+ height: ${tokens.size[3.5]};
204
+ `,
205
+ )}
206
+ title={'Toggle value'}
207
+ aria-label={'Toggle value'}
208
+ onClick={() => {
209
+ const oldData = props.activeQuery.state.data
210
+ const newData = updateNestedDataByPath(
211
+ oldData,
212
+ props.dataPath,
213
+ !props.value,
214
+ )
215
+ queryClient.setQueryData(props.activeQuery.queryKey, newData)
216
+ }}
217
+ >
218
+ <Check theme={theme()} checked={props.value} />
219
+ </button>
220
+ )
221
+ }
222
+
174
223
  type ExplorerProps = {
175
224
  editable?: boolean
176
225
  label: string
@@ -186,7 +235,10 @@ function isIterable(x: any): x is Iterable<unknown> {
186
235
  }
187
236
 
188
237
  export default function Explorer(props: ExplorerProps) {
189
- const styles = getStyles()
238
+ const theme = useTheme()
239
+ const styles = createMemo(() => {
240
+ return theme() === 'dark' ? darkStyles : lightStyles
241
+ })
190
242
  const queryClient = useQueryDevtoolsContext().client
191
243
 
192
244
  const [expanded, setExpanded] = createSignal(
@@ -247,21 +299,21 @@ export default function Explorer(props: ExplorerProps) {
247
299
  const currentDataPath = props.dataPath ?? []
248
300
 
249
301
  return (
250
- <div class={styles.entry}>
302
+ <div class={styles().entry}>
251
303
  <Show when={subEntryPages().length}>
252
- <div class={styles.expanderButtonContainer}>
304
+ <div class={styles().expanderButtonContainer}>
253
305
  <button
254
- class={styles.expanderButton}
306
+ class={styles().expanderButton}
255
307
  onClick={() => toggleExpanded()}
256
308
  >
257
309
  <Expander expanded={expanded()} /> <span>{props.label}</span>{' '}
258
- <span class={styles.info}>
310
+ <span class={styles().info}>
259
311
  {String(type()).toLowerCase() === 'iterable' ? '(Iterable) ' : ''}
260
312
  {subEntries().length} {subEntries().length > 1 ? `items` : `item`}
261
313
  </span>
262
314
  </button>
263
315
  <Show when={props.editable}>
264
- <div class={styles.actions}>
316
+ <div class={styles().actions}>
265
317
  <CopyButton value={props.value} />
266
318
 
267
319
  <Show
@@ -286,7 +338,7 @@ export default function Explorer(props: ExplorerProps) {
286
338
  </div>
287
339
  <Show when={expanded()}>
288
340
  <Show when={subEntryPages().length === 1}>
289
- <div class={styles.subEntry}>
341
+ <div class={styles().subEntry}>
290
342
  <Key each={subEntries()} by={(item) => item.label}>
291
343
  {(entry) => {
292
344
  return (
@@ -309,11 +361,11 @@ export default function Explorer(props: ExplorerProps) {
309
361
  </div>
310
362
  </Show>
311
363
  <Show when={subEntryPages().length > 1}>
312
- <div class={styles.subEntry}>
364
+ <div class={styles().subEntry}>
313
365
  <Index each={subEntryPages()}>
314
366
  {(entries, index) => (
315
367
  <div>
316
- <div class={styles.entry}>
368
+ <div class={styles().entry}>
317
369
  <button
318
370
  onClick={() =>
319
371
  setExpandedPages((old) =>
@@ -322,14 +374,14 @@ export default function Explorer(props: ExplorerProps) {
322
374
  : [...old, index],
323
375
  )
324
376
  }
325
- class={styles.expanderButton}
377
+ class={styles().expanderButton}
326
378
  >
327
379
  <Expander expanded={expandedPages().includes(index)} />{' '}
328
380
  [{index * 100}...
329
381
  {index * 100 + 100 - 1}]
330
382
  </button>
331
383
  <Show when={expandedPages().includes(index)}>
332
- <div class={styles.subEntry}>
384
+ <div class={styles().subEntry}>
333
385
  <Key each={entries()} by={(entry) => entry.label}>
334
386
  {(entry) => (
335
387
  <Explorer
@@ -353,36 +405,63 @@ export default function Explorer(props: ExplorerProps) {
353
405
  </Show>
354
406
  </Show>
355
407
  <Show when={subEntryPages().length === 0}>
356
- <div class={styles.row}>
357
- <span class={styles.label}>{props.label}:</span>
408
+ <div class={styles().row}>
409
+ <span class={styles().label}>{props.label}:</span>
358
410
  <Show
359
411
  when={
360
412
  props.editable &&
361
413
  props.activeQuery !== undefined &&
362
- (type() === 'string' || type() === 'number')
414
+ (type() === 'string' ||
415
+ type() === 'number' ||
416
+ type() === 'boolean')
363
417
  }
364
418
  fallback={
365
- <span class={styles.value}>{displayValue(props.value)}</span>
419
+ <span class={styles().value}>{displayValue(props.value)}</span>
366
420
  }
367
421
  >
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
- />
422
+ <Show
423
+ when={
424
+ props.editable &&
425
+ props.activeQuery !== undefined &&
426
+ (type() === 'string' || type() === 'number')
427
+ }
428
+ >
429
+ <input
430
+ type={type() === 'number' ? 'number' : 'text'}
431
+ class={cx(styles().value, styles().editableInput)}
432
+ value={props.value as string | number}
433
+ onChange={(changeEvent) => {
434
+ const oldData = props.activeQuery!.state.data
435
+
436
+ const newData = updateNestedDataByPath(
437
+ oldData,
438
+ currentDataPath,
439
+ type() === 'number'
440
+ ? changeEvent.target.valueAsNumber
441
+ : changeEvent.target.value,
442
+ )
443
+
444
+ queryClient.setQueryData(props.activeQuery!.queryKey, newData)
445
+ }}
446
+ />
447
+ </Show>
448
+
449
+ <Show when={type() === 'boolean'}>
450
+ <span
451
+ class={cx(
452
+ styles().value,
453
+ styles().actions,
454
+ styles().editableInput,
455
+ )}
456
+ >
457
+ <ToggleValueButton
458
+ activeQuery={props.activeQuery!}
459
+ dataPath={currentDataPath}
460
+ value={props.value as boolean}
461
+ />
462
+ {displayValue(props.value)}
463
+ </span>
464
+ </Show>
386
465
  </Show>
387
466
 
388
467
  <Show
@@ -403,9 +482,9 @@ export default function Explorer(props: ExplorerProps) {
403
482
  )
404
483
  }
405
484
 
406
- const getStyles = () => {
485
+ const stylesFactory = (theme: 'light' | 'dark') => {
407
486
  const { colors, font, size, border } = tokens
408
-
487
+ const t = (light: string, dark: string) => (theme === 'light' ? light : dark)
409
488
  return {
410
489
  entry: css`
411
490
  & * {
@@ -419,7 +498,7 @@ const getStyles = () => {
419
498
  subEntry: css`
420
499
  margin: 0 0 0 0.5em;
421
500
  padding-left: 0.75em;
422
- border-left: 2px solid ${colors.darkGray[400]};
501
+ border-left: 2px solid ${t(colors.gray[300], colors.darkGray[400])};
423
502
  /* outline: 1px solid ${colors.teal[400]}; */
424
503
  `,
425
504
  expander: css`
@@ -468,21 +547,22 @@ const getStyles = () => {
468
547
  }
469
548
  `,
470
549
  info: css`
471
- color: ${colors.gray[500]};
550
+ color: ${t(colors.gray[500], colors.gray[500])};
472
551
  font-size: ${font.size.xs};
473
552
  margin-left: ${size[1]};
474
553
  /* outline: 1px solid ${colors.yellow[400]}; */
475
554
  `,
476
555
  label: css`
477
- color: ${colors.gray[300]};
556
+ color: ${t(colors.gray[700], colors.gray[300])};
478
557
  `,
479
558
  value: css`
480
- color: ${colors.purple[400]};
559
+ color: ${t(colors.purple[600], colors.purple[400])};
481
560
  flex-grow: 1;
482
561
  `,
483
562
  actions: css`
484
563
  display: inline-flex;
485
564
  gap: ${size[2]};
565
+ align-items: center;
486
566
  `,
487
567
  row: css`
488
568
  display: inline-flex;
@@ -490,19 +570,22 @@ const getStyles = () => {
490
570
  width: 100%;
491
571
  margin-bottom: ${size[0.5]};
492
572
  line-height: 1.125rem;
573
+ align-items: center;
493
574
  `,
494
575
  editableInput: css`
495
576
  border: none;
496
- padding: 0px ${size[1]};
577
+ padding: ${size[0.5]} ${size[1]} ${size[0.5]} ${size[1.5]};
497
578
  flex-grow: 1;
498
- background-color: ${colors.gray[900]};
579
+ border-radius: ${border.radius.xs};
580
+ background-color: ${t(colors.gray[200], colors.darkGray[500])};
499
581
 
500
582
  &:hover {
501
- background-color: ${colors.gray[800]};
583
+ background-color: ${t(colors.gray[300], colors.darkGray[600])};
502
584
  }
503
585
  `,
504
586
  actionButton: css`
505
587
  background-color: transparent;
588
+ color: ${t(colors.gray[500], colors.gray[500])};
506
589
  border: none;
507
590
  display: inline-flex;
508
591
  padding: 0px;
@@ -515,14 +598,7 @@ const getStyles = () => {
515
598
  z-index: 1;
516
599
 
517
600
  &:hover svg {
518
- .copier,
519
- .list {
520
- stroke: ${colors.gray[500]} !important;
521
- }
522
-
523
- .list-item {
524
- stroke: ${colors.gray[700]};
525
- }
601
+ color: ${t(colors.gray[600], colors.gray[400])};
526
602
  }
527
603
 
528
604
  &:focus-visible {
@@ -533,3 +609,6 @@ const getStyles = () => {
533
609
  `,
534
610
  }
535
611
  }
612
+
613
+ const lightStyles = stylesFactory('light')
614
+ const darkStyles = stylesFactory('dark')