@tanstack/query-devtools 5.0.0-alpha.27
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/LICENSE +21 -0
- package/build/.tsbuildinfo +1 -0
- package/build/cjs/index.js +7966 -0
- package/build/cjs/index.js.map +1 -0
- package/build/esm/index.js +7964 -0
- package/build/esm/index.js.map +1 -0
- package/build/source/Context.js +10 -0
- package/build/source/Devtools.jsx +1485 -0
- package/build/source/Explorer.jsx +266 -0
- package/build/source/__tests__/devtools.test.jsx +6 -0
- package/build/source/fonts.js +7 -0
- package/build/source/icons/index.jsx +344 -0
- package/build/source/index.jsx +64 -0
- package/build/source/theme.js +304 -0
- package/build/source/utils.jsx +74 -0
- package/build/types/Context.d.ts +29 -0
- package/build/types/Context.d.ts.map +1 -0
- package/build/types/Devtools.d.ts +23 -0
- package/build/types/Devtools.d.ts.map +1 -0
- package/build/types/Explorer.d.ts +22 -0
- package/build/types/Explorer.d.ts.map +1 -0
- package/build/types/__tests__/devtools.test.d.ts +1 -0
- package/build/types/__tests__/devtools.test.d.ts.map +1 -0
- package/build/types/fonts.d.ts +2 -0
- package/build/types/fonts.d.ts.map +1 -0
- package/build/types/icons/index.d.ts +12 -0
- package/build/types/icons/index.d.ts.map +1 -0
- package/build/types/index.d.ts +27 -0
- package/build/types/index.d.ts.map +1 -0
- package/build/types/theme.d.ts +297 -0
- package/build/types/theme.d.ts.map +1 -0
- package/build/types/utils.d.ts +22 -0
- package/build/types/utils.d.ts.map +1 -0
- package/build/umd/index.js +872 -0
- package/build/umd/index.js.map +1 -0
- package/package.json +53 -0
- package/src/Context.ts +41 -0
- package/src/Devtools.tsx +1890 -0
- package/src/Explorer.tsx +348 -0
- package/src/__tests__/devtools.test.tsx +5 -0
- package/src/fonts.ts +7 -0
- package/src/icons/index.tsx +1065 -0
- package/src/index.tsx +113 -0
- package/src/theme.ts +321 -0
- package/src/utils.tsx +103 -0
package/src/Explorer.tsx
ADDED
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
import { displayValue } from './utils'
|
|
2
|
+
import superjson from 'superjson'
|
|
3
|
+
import { css, cx } from '@emotion/css'
|
|
4
|
+
import { tokens } from './theme'
|
|
5
|
+
import { createMemo, createSignal, Index, Match, Show, Switch } from 'solid-js'
|
|
6
|
+
import { Key } from '@solid-primitives/keyed'
|
|
7
|
+
import { CopiedCopier, Copier, ErrorCopier } from './icons'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Chunk elements in the array by size
|
|
11
|
+
*
|
|
12
|
+
* when the array cannot be chunked evenly by size, the last chunk will be
|
|
13
|
+
* filled with the remaining elements
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* chunkArray(['a','b', 'c', 'd', 'e'], 2) // returns [['a','b'], ['c', 'd'], ['e']]
|
|
17
|
+
*/
|
|
18
|
+
export function chunkArray<T extends { label: string; value: unknown }>(
|
|
19
|
+
array: T[],
|
|
20
|
+
size: number,
|
|
21
|
+
): T[][] {
|
|
22
|
+
if (size < 1) return []
|
|
23
|
+
let i = 0
|
|
24
|
+
const result: T[][] = []
|
|
25
|
+
while (i < array.length) {
|
|
26
|
+
result.push(array.slice(i, i + size))
|
|
27
|
+
i = i + size
|
|
28
|
+
}
|
|
29
|
+
return result
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const Expander = (props: { expanded: boolean }) => {
|
|
33
|
+
const styles = getStyles()
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<span
|
|
37
|
+
class={cx(
|
|
38
|
+
styles.expander,
|
|
39
|
+
css`
|
|
40
|
+
transform: rotate(${props.expanded ? 90 : 0}deg);
|
|
41
|
+
`,
|
|
42
|
+
)}
|
|
43
|
+
>
|
|
44
|
+
<svg
|
|
45
|
+
width="16"
|
|
46
|
+
height="16"
|
|
47
|
+
viewBox="0 0 16 16"
|
|
48
|
+
fill="none"
|
|
49
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
50
|
+
>
|
|
51
|
+
<path
|
|
52
|
+
d="M6 12L10 8L6 4"
|
|
53
|
+
stroke-width="2"
|
|
54
|
+
stroke-linecap="round"
|
|
55
|
+
stroke-linejoin="round"
|
|
56
|
+
/>
|
|
57
|
+
</svg>
|
|
58
|
+
</span>
|
|
59
|
+
)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
type CopyState = 'NoCopy' | 'SuccessCopy' | 'ErrorCopy'
|
|
63
|
+
const CopyButton = (props: { value: unknown }) => {
|
|
64
|
+
const styles = getStyles()
|
|
65
|
+
const [copyState, setCopyState] = createSignal<CopyState>('NoCopy')
|
|
66
|
+
|
|
67
|
+
return (
|
|
68
|
+
<button
|
|
69
|
+
class={styles.copyButton}
|
|
70
|
+
aria-label={`${
|
|
71
|
+
copyState() === 'NoCopy'
|
|
72
|
+
? 'Copy object to clipboard'
|
|
73
|
+
: copyState() === 'SuccessCopy'
|
|
74
|
+
? 'Object copied to clipboard'
|
|
75
|
+
: 'Error copying object to clipboard'
|
|
76
|
+
}`}
|
|
77
|
+
onClick={
|
|
78
|
+
copyState() === 'NoCopy'
|
|
79
|
+
? () => {
|
|
80
|
+
navigator.clipboard
|
|
81
|
+
.writeText(superjson.stringify(props.value))
|
|
82
|
+
.then(
|
|
83
|
+
() => {
|
|
84
|
+
setCopyState('SuccessCopy')
|
|
85
|
+
setTimeout(() => {
|
|
86
|
+
setCopyState('NoCopy')
|
|
87
|
+
}, 1500)
|
|
88
|
+
},
|
|
89
|
+
(err) => {
|
|
90
|
+
console.error('Failed to copy: ', err)
|
|
91
|
+
setCopyState('ErrorCopy')
|
|
92
|
+
setTimeout(() => {
|
|
93
|
+
setCopyState('NoCopy')
|
|
94
|
+
}, 1500)
|
|
95
|
+
},
|
|
96
|
+
)
|
|
97
|
+
}
|
|
98
|
+
: undefined
|
|
99
|
+
}
|
|
100
|
+
>
|
|
101
|
+
<Switch>
|
|
102
|
+
<Match when={copyState() === 'NoCopy'}>
|
|
103
|
+
<Copier />
|
|
104
|
+
</Match>
|
|
105
|
+
<Match when={copyState() === 'SuccessCopy'}>
|
|
106
|
+
<CopiedCopier />
|
|
107
|
+
</Match>
|
|
108
|
+
<Match when={copyState() === 'ErrorCopy'}>
|
|
109
|
+
<ErrorCopier />
|
|
110
|
+
</Match>
|
|
111
|
+
</Switch>
|
|
112
|
+
</button>
|
|
113
|
+
)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
type ExplorerProps = {
|
|
117
|
+
copyable?: boolean
|
|
118
|
+
label: string
|
|
119
|
+
value: unknown
|
|
120
|
+
defaultExpanded?: string[]
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function isIterable(x: any): x is Iterable<unknown> {
|
|
124
|
+
return Symbol.iterator in x
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export default function Explorer(props: ExplorerProps) {
|
|
128
|
+
const styles = getStyles()
|
|
129
|
+
|
|
130
|
+
const [expanded, setExpanded] = createSignal(
|
|
131
|
+
(props.defaultExpanded || []).includes(props.label),
|
|
132
|
+
)
|
|
133
|
+
const toggleExpanded = () => setExpanded((old) => !old)
|
|
134
|
+
const [expandedPages, setExpandedPages] = createSignal<number[]>([])
|
|
135
|
+
|
|
136
|
+
const subEntries = createMemo(() => {
|
|
137
|
+
if (Array.isArray(props.value)) {
|
|
138
|
+
return props.value.map((d, i) => ({
|
|
139
|
+
label: i.toString(),
|
|
140
|
+
value: d,
|
|
141
|
+
}))
|
|
142
|
+
} else if (
|
|
143
|
+
props.value !== null &&
|
|
144
|
+
typeof props.value === 'object' &&
|
|
145
|
+
isIterable(props.value) &&
|
|
146
|
+
typeof props.value[Symbol.iterator] === 'function'
|
|
147
|
+
) {
|
|
148
|
+
if (props.value instanceof Map) {
|
|
149
|
+
return Array.from(props.value, ([key, val]) => ({
|
|
150
|
+
label: key,
|
|
151
|
+
value: val,
|
|
152
|
+
}))
|
|
153
|
+
}
|
|
154
|
+
return Array.from(props.value, (val, i) => ({
|
|
155
|
+
label: i.toString(),
|
|
156
|
+
value: val,
|
|
157
|
+
}))
|
|
158
|
+
} else if (typeof props.value === 'object' && props.value !== null) {
|
|
159
|
+
return Object.entries(props.value).map(([key, val]) => ({
|
|
160
|
+
label: key,
|
|
161
|
+
value: val,
|
|
162
|
+
}))
|
|
163
|
+
}
|
|
164
|
+
return []
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
const type = createMemo<string>(() => {
|
|
168
|
+
if (Array.isArray(props.value)) {
|
|
169
|
+
return 'array'
|
|
170
|
+
} else if (
|
|
171
|
+
props.value !== null &&
|
|
172
|
+
typeof props.value === 'object' &&
|
|
173
|
+
isIterable(props.value) &&
|
|
174
|
+
typeof props.value[Symbol.iterator] === 'function'
|
|
175
|
+
) {
|
|
176
|
+
return 'Iterable'
|
|
177
|
+
} else if (typeof props.value === 'object' && props.value !== null) {
|
|
178
|
+
return 'object'
|
|
179
|
+
}
|
|
180
|
+
return typeof props.value
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
const subEntryPages = createMemo(() => chunkArray(subEntries(), 100))
|
|
184
|
+
|
|
185
|
+
return (
|
|
186
|
+
<div class={styles.entry}>
|
|
187
|
+
<Show when={subEntryPages().length}>
|
|
188
|
+
<button class={styles.expanderButton} onClick={() => toggleExpanded()}>
|
|
189
|
+
<Expander expanded={expanded()} /> <span>{props.label}</span>{' '}
|
|
190
|
+
<span class={styles.info}>
|
|
191
|
+
{String(type()).toLowerCase() === 'iterable' ? '(Iterable) ' : ''}
|
|
192
|
+
{subEntries().length} {subEntries().length > 1 ? `items` : `item`}
|
|
193
|
+
</span>
|
|
194
|
+
</button>
|
|
195
|
+
<Show when={props.copyable}>
|
|
196
|
+
<CopyButton value={props.value} />
|
|
197
|
+
</Show>
|
|
198
|
+
<Show when={expanded()}>
|
|
199
|
+
<Show when={subEntryPages().length === 1}>
|
|
200
|
+
<div class={styles.subEntry}>
|
|
201
|
+
<Key each={subEntries()} by={(item) => item.label}>
|
|
202
|
+
{(entry) => {
|
|
203
|
+
return (
|
|
204
|
+
<Explorer
|
|
205
|
+
defaultExpanded={props.defaultExpanded}
|
|
206
|
+
label={entry().label}
|
|
207
|
+
value={entry().value}
|
|
208
|
+
copyable={props.copyable}
|
|
209
|
+
/>
|
|
210
|
+
)
|
|
211
|
+
}}
|
|
212
|
+
</Key>
|
|
213
|
+
</div>
|
|
214
|
+
</Show>
|
|
215
|
+
<Show when={subEntryPages().length > 1}>
|
|
216
|
+
<div class={styles.subEntry}>
|
|
217
|
+
<Index each={subEntryPages()}>
|
|
218
|
+
{(entries, index) => (
|
|
219
|
+
<div>
|
|
220
|
+
<div class={styles.entry}>
|
|
221
|
+
<button
|
|
222
|
+
onClick={() =>
|
|
223
|
+
setExpandedPages((old) =>
|
|
224
|
+
old.includes(index)
|
|
225
|
+
? old.filter((d) => d !== index)
|
|
226
|
+
: [...old, index],
|
|
227
|
+
)
|
|
228
|
+
}
|
|
229
|
+
class={styles.expanderButton}
|
|
230
|
+
>
|
|
231
|
+
<Expander expanded={expandedPages().includes(index)} />{' '}
|
|
232
|
+
[{index * 100}...
|
|
233
|
+
{index * 100 + 100 - 1}]
|
|
234
|
+
</button>
|
|
235
|
+
<Show when={expandedPages().includes(index)}>
|
|
236
|
+
<div class={styles.subEntry}>
|
|
237
|
+
<Key each={entries()} by={(entry) => entry.label}>
|
|
238
|
+
{(entry) => (
|
|
239
|
+
<Explorer
|
|
240
|
+
defaultExpanded={props.defaultExpanded}
|
|
241
|
+
label={entry().label}
|
|
242
|
+
value={entry().value}
|
|
243
|
+
copyable={props.copyable}
|
|
244
|
+
/>
|
|
245
|
+
)}
|
|
246
|
+
</Key>
|
|
247
|
+
</div>
|
|
248
|
+
</Show>
|
|
249
|
+
</div>
|
|
250
|
+
</div>
|
|
251
|
+
)}
|
|
252
|
+
</Index>
|
|
253
|
+
</div>
|
|
254
|
+
</Show>
|
|
255
|
+
</Show>
|
|
256
|
+
</Show>
|
|
257
|
+
<Show when={subEntryPages().length === 0}>
|
|
258
|
+
<span class={styles.label}>{props.label}:</span>{' '}
|
|
259
|
+
<span class={styles.value}>{displayValue(props.value)}</span>
|
|
260
|
+
</Show>
|
|
261
|
+
</div>
|
|
262
|
+
)
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const getStyles = () => {
|
|
266
|
+
const { colors, font, size, border } = tokens
|
|
267
|
+
|
|
268
|
+
return {
|
|
269
|
+
entry: css`
|
|
270
|
+
& * {
|
|
271
|
+
font-size: ${font.size.sm};
|
|
272
|
+
font-family: 'Menlo', 'Fira Code', monospace;
|
|
273
|
+
line-height: 1.7;
|
|
274
|
+
}
|
|
275
|
+
position: relative;
|
|
276
|
+
outline: none;
|
|
277
|
+
word-break: break-word;
|
|
278
|
+
`,
|
|
279
|
+
subEntry: css`
|
|
280
|
+
margin: 0 0 0 0.5em;
|
|
281
|
+
padding-left: 0.75em;
|
|
282
|
+
border-left: 2px solid ${colors.darkGray[400]};
|
|
283
|
+
`,
|
|
284
|
+
expander: css`
|
|
285
|
+
& path {
|
|
286
|
+
stroke: ${colors.gray[400]};
|
|
287
|
+
}
|
|
288
|
+
display: inline-flex;
|
|
289
|
+
align-items: center;
|
|
290
|
+
transition: all 0.1s ease;
|
|
291
|
+
`,
|
|
292
|
+
expanderButton: css`
|
|
293
|
+
cursor: pointer;
|
|
294
|
+
color: inherit;
|
|
295
|
+
font: inherit;
|
|
296
|
+
outline: inherit;
|
|
297
|
+
line-height: ${font.size.sm};
|
|
298
|
+
background: transparent;
|
|
299
|
+
border: none;
|
|
300
|
+
padding: 0;
|
|
301
|
+
display: inline-flex;
|
|
302
|
+
align-items: center;
|
|
303
|
+
gap: ${size[1]};
|
|
304
|
+
|
|
305
|
+
&:focus-visible {
|
|
306
|
+
border-radius: ${border.radius.xs};
|
|
307
|
+
outline: 2px solid ${colors.blue[800]};
|
|
308
|
+
}
|
|
309
|
+
`,
|
|
310
|
+
info: css`
|
|
311
|
+
color: ${colors.gray[500]};
|
|
312
|
+
font-size: ${font.size.xs};
|
|
313
|
+
line-height: ${font.size.xs};
|
|
314
|
+
margin-left: ${size[1]};
|
|
315
|
+
`,
|
|
316
|
+
label: css`
|
|
317
|
+
color: ${colors.gray[300]};
|
|
318
|
+
`,
|
|
319
|
+
value: css`
|
|
320
|
+
color: ${colors.purple[400]};
|
|
321
|
+
`,
|
|
322
|
+
copyButton: css`
|
|
323
|
+
background-color: transparent;
|
|
324
|
+
border: none;
|
|
325
|
+
display: inline-flex;
|
|
326
|
+
padding: 0px;
|
|
327
|
+
align-items: center;
|
|
328
|
+
justify-content: center;
|
|
329
|
+
cursor: pointer;
|
|
330
|
+
width: ${size[3.5]};
|
|
331
|
+
height: ${size[3.5]};
|
|
332
|
+
position: relative;
|
|
333
|
+
top: 4px;
|
|
334
|
+
left: ${size[2]};
|
|
335
|
+
z-index: 1;
|
|
336
|
+
|
|
337
|
+
&:hover svg .copier {
|
|
338
|
+
stroke: ${colors.gray[500]} !important;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
&:focus-visible {
|
|
342
|
+
border-radius: ${border.radius.xs};
|
|
343
|
+
outline: 2px solid ${colors.blue[800]};
|
|
344
|
+
outline-offset: 2px;
|
|
345
|
+
}
|
|
346
|
+
`,
|
|
347
|
+
}
|
|
348
|
+
}
|
package/src/fonts.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export const loadFonts = () => {
|
|
2
|
+
const link = document.createElement('link')
|
|
3
|
+
link.href =
|
|
4
|
+
'https://fonts.googleapis.com/css2?family=Fira+Code&family=Inter:wght@100;200;300;400;500;600;700;800;900&family=Roboto&display=swap'
|
|
5
|
+
link.rel = 'stylesheet'
|
|
6
|
+
document.head.appendChild(link)
|
|
7
|
+
}
|