@tanstack/router-devtools 1.20.1 → 1.20.3-alpha.1

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 (57) hide show
  1. package/README.md +3 -1
  2. package/dist/cjs/index.cjs +12 -3
  3. package/dist/cjs/index.cjs.map +1 -1
  4. package/dist/cjs/index.d.cts +2 -1
  5. package/dist/esm/index.d.ts +2 -1
  6. package/dist/esm/index.js +6 -3
  7. package/dist/esm/index.js.map +1 -1
  8. package/package.json +34 -34
  9. package/src/index.tsx +6 -1
  10. package/dist/cjs/Explorer.cjs +0 -306
  11. package/dist/cjs/Explorer.cjs.map +0 -1
  12. package/dist/cjs/Explorer.d.cts +0 -46
  13. package/dist/cjs/devtools.cjs +0 -1181
  14. package/dist/cjs/devtools.cjs.map +0 -1
  15. package/dist/cjs/devtools.d.cts +0 -65
  16. package/dist/cjs/logo.cjs +0 -1012
  17. package/dist/cjs/logo.cjs.map +0 -1
  18. package/dist/cjs/logo.d.cts +0 -2
  19. package/dist/cjs/theme.d.cts +0 -34
  20. package/dist/cjs/tokens.cjs +0 -302
  21. package/dist/cjs/tokens.cjs.map +0 -1
  22. package/dist/cjs/tokens.d.cts +0 -298
  23. package/dist/cjs/useLocalStorage.cjs +0 -45
  24. package/dist/cjs/useLocalStorage.cjs.map +0 -1
  25. package/dist/cjs/useLocalStorage.d.cts +0 -1
  26. package/dist/cjs/useMediaQuery.d.cts +0 -1
  27. package/dist/cjs/utils.cjs +0 -82
  28. package/dist/cjs/utils.cjs.map +0 -1
  29. package/dist/cjs/utils.d.cts +0 -23
  30. package/dist/esm/Explorer.d.ts +0 -46
  31. package/dist/esm/Explorer.js +0 -289
  32. package/dist/esm/Explorer.js.map +0 -1
  33. package/dist/esm/devtools.d.ts +0 -65
  34. package/dist/esm/devtools.js +0 -1181
  35. package/dist/esm/devtools.js.map +0 -1
  36. package/dist/esm/logo.d.ts +0 -2
  37. package/dist/esm/logo.js +0 -1012
  38. package/dist/esm/logo.js.map +0 -1
  39. package/dist/esm/theme.d.ts +0 -34
  40. package/dist/esm/tokens.d.ts +0 -298
  41. package/dist/esm/tokens.js +0 -302
  42. package/dist/esm/tokens.js.map +0 -1
  43. package/dist/esm/useLocalStorage.d.ts +0 -1
  44. package/dist/esm/useLocalStorage.js +0 -46
  45. package/dist/esm/useLocalStorage.js.map +0 -1
  46. package/dist/esm/useMediaQuery.d.ts +0 -1
  47. package/dist/esm/utils.d.ts +0 -23
  48. package/dist/esm/utils.js +0 -82
  49. package/dist/esm/utils.js.map +0 -1
  50. package/src/Explorer.tsx +0 -357
  51. package/src/devtools.tsx +0 -1401
  52. package/src/logo.tsx +0 -817
  53. package/src/theme.tsx +0 -31
  54. package/src/tokens.ts +0 -305
  55. package/src/useLocalStorage.ts +0 -52
  56. package/src/useMediaQuery.ts +0 -39
  57. package/src/utils.ts +0 -183
package/src/theme.tsx DELETED
@@ -1,31 +0,0 @@
1
- import React from 'react'
2
-
3
- export const defaultTheme = {
4
- background: '#222222',
5
- backgroundAlt: '#292929',
6
- foreground: 'white',
7
- gray: '#444',
8
- grayAlt: '#444',
9
- inputBackgroundColor: '#fff',
10
- inputTextColor: '#000',
11
- success: '#80cb00',
12
- danger: '#ff0085',
13
- active: '#0099ff',
14
- warning: '#ffb200',
15
- } as const
16
-
17
- export type Theme = typeof defaultTheme
18
- interface ProviderProps {
19
- theme: Theme
20
- children?: React.ReactNode
21
- }
22
-
23
- const ThemeContext = React.createContext(defaultTheme)
24
-
25
- export function ThemeProvider({ theme, ...rest }: ProviderProps) {
26
- return <ThemeContext.Provider value={theme} {...rest} />
27
- }
28
-
29
- export function useTheme() {
30
- return React.useContext(ThemeContext)
31
- }
package/src/tokens.ts DELETED
@@ -1,305 +0,0 @@
1
- export const tokens = {
2
- colors: {
3
- inherit: 'inherit',
4
- current: 'currentColor',
5
- transparent: 'transparent',
6
- black: '#000000',
7
- white: '#ffffff',
8
- neutral: {
9
- 50: '#f9fafb',
10
- 100: '#f2f4f7',
11
- 200: '#eaecf0',
12
- 300: '#d0d5dd',
13
- 400: '#98a2b3',
14
- 500: '#667085',
15
- 600: '#475467',
16
- 700: '#344054',
17
- 800: '#1d2939',
18
- 900: '#101828',
19
- },
20
- darkGray: {
21
- 50: '#525c7a',
22
- 100: '#49536e',
23
- 200: '#414962',
24
- 300: '#394056',
25
- 400: '#313749',
26
- 500: '#292e3d',
27
- 600: '#212530',
28
- 700: '#191c24',
29
- 800: '#111318',
30
- 900: '#0b0d10',
31
- },
32
- gray: {
33
- 50: '#f9fafb',
34
- 100: '#f2f4f7',
35
- 200: '#eaecf0',
36
- 300: '#d0d5dd',
37
- 400: '#98a2b3',
38
- 500: '#667085',
39
- 600: '#475467',
40
- 700: '#344054',
41
- 800: '#1d2939',
42
- 900: '#101828',
43
- },
44
- blue: {
45
- 25: '#F5FAFF',
46
- 50: '#EFF8FF',
47
- 100: '#D1E9FF',
48
- 200: '#B2DDFF',
49
- 300: '#84CAFF',
50
- 400: '#53B1FD',
51
- 500: '#2E90FA',
52
- 600: '#1570EF',
53
- 700: '#175CD3',
54
- 800: '#1849A9',
55
- 900: '#194185',
56
- },
57
- green: {
58
- 25: '#F6FEF9',
59
- 50: '#ECFDF3',
60
- 100: '#D1FADF',
61
- 200: '#A6F4C5',
62
- 300: '#6CE9A6',
63
- 400: '#32D583',
64
- 500: '#12B76A',
65
- 600: '#039855',
66
- 700: '#027A48',
67
- 800: '#05603A',
68
- 900: '#054F31',
69
- },
70
- red: {
71
- 50: '#fef2f2',
72
- 100: '#fee2e2',
73
- 200: '#fecaca',
74
- 300: '#fca5a5',
75
- 400: '#f87171',
76
- 500: '#ef4444',
77
- 600: '#dc2626',
78
- 700: '#b91c1c',
79
- 800: '#991b1b',
80
- 900: '#7f1d1d',
81
- 950: '#450a0a',
82
- },
83
- yellow: {
84
- 25: '#FFFCF5',
85
- 50: '#FFFAEB',
86
- 100: '#FEF0C7',
87
- 200: '#FEDF89',
88
- 300: '#FEC84B',
89
- 400: '#FDB022',
90
- 500: '#F79009',
91
- 600: '#DC6803',
92
- 700: '#B54708',
93
- 800: '#93370D',
94
- 900: '#7A2E0E',
95
- },
96
- purple: {
97
- 25: '#FAFAFF',
98
- 50: '#F4F3FF',
99
- 100: '#EBE9FE',
100
- 200: '#D9D6FE',
101
- 300: '#BDB4FE',
102
- 400: '#9B8AFB',
103
- 500: '#7A5AF8',
104
- 600: '#6938EF',
105
- 700: '#5925DC',
106
- 800: '#4A1FB8',
107
- 900: '#3E1C96',
108
- },
109
- teal: {
110
- 25: '#F6FEFC',
111
- 50: '#F0FDF9',
112
- 100: '#CCFBEF',
113
- 200: '#99F6E0',
114
- 300: '#5FE9D0',
115
- 400: '#2ED3B7',
116
- 500: '#15B79E',
117
- 600: '#0E9384',
118
- 700: '#107569',
119
- 800: '#125D56',
120
- 900: '#134E48',
121
- },
122
- pink: {
123
- 25: '#fdf2f8',
124
- 50: '#fce7f3',
125
- 100: '#fbcfe8',
126
- 200: '#f9a8d4',
127
- 300: '#f472b6',
128
- 400: '#ec4899',
129
- 500: '#db2777',
130
- 600: '#be185d',
131
- 700: '#9d174d',
132
- 800: '#831843',
133
- 900: '#500724',
134
- },
135
- cyan: {
136
- 25: '#ecfeff',
137
- 50: '#cffafe',
138
- 100: '#a5f3fc',
139
- 200: '#67e8f9',
140
- 300: '#22d3ee',
141
- 400: '#06b6d4',
142
- 500: '#0891b2',
143
- 600: '#0e7490',
144
- 700: '#155e75',
145
- 800: '#164e63',
146
- 900: '#083344',
147
- },
148
- },
149
- alpha: {
150
- 100: 'ff',
151
- 90: 'e5',
152
- 80: 'cc',
153
- 70: 'b3',
154
- 60: '99',
155
- 50: '80',
156
- 40: '66',
157
- 30: '4d',
158
- 20: '33',
159
- 10: '1a',
160
- 0: '00',
161
- },
162
- font: {
163
- size: {
164
- '2xs': 'calc(var(--tsrd-font-size) * 0.625)',
165
- xs: 'calc(var(--tsrd-font-size) * 0.75)',
166
- sm: 'calc(var(--tsrd-font-size) * 0.875)',
167
- md: 'var(--tsrd-font-size)',
168
- lg: 'calc(var(--tsrd-font-size) * 1.125)',
169
- xl: 'calc(var(--tsrd-font-size) * 1.25)',
170
- '2xl': 'calc(var(--tsrd-font-size) * 1.5)',
171
- '3xl': 'calc(var(--tsrd-font-size) * 1.875)',
172
- '4xl': 'calc(var(--tsrd-font-size) * 2.25)',
173
- '5xl': 'calc(var(--tsrd-font-size) * 3)',
174
- '6xl': 'calc(var(--tsrd-font-size) * 3.75)',
175
- '7xl': 'calc(var(--tsrd-font-size) * 4.5)',
176
- '8xl': 'calc(var(--tsrd-font-size) * 6)',
177
- '9xl': 'calc(var(--tsrd-font-size) * 8)',
178
- },
179
- lineHeight: {
180
- '3xs': 'calc(var(--tsrd-font-size) * 0.75)',
181
- '2xs': 'calc(var(--tsrd-font-size) * 0.875)',
182
- xs: 'calc(var(--tsrd-font-size) * 1)',
183
- sm: 'calc(var(--tsrd-font-size) * 1.25)',
184
- md: 'calc(var(--tsrd-font-size) * 1.5)',
185
- lg: 'calc(var(--tsrd-font-size) * 1.75)',
186
- xl: 'calc(var(--tsrd-font-size) * 2)',
187
- '2xl': 'calc(var(--tsrd-font-size) * 2.25)',
188
- '3xl': 'calc(var(--tsrd-font-size) * 2.5)',
189
- '4xl': 'calc(var(--tsrd-font-size) * 2.75)',
190
- '5xl': 'calc(var(--tsrd-font-size) * 3)',
191
- '6xl': 'calc(var(--tsrd-font-size) * 3.25)',
192
- '7xl': 'calc(var(--tsrd-font-size) * 3.5)',
193
- '8xl': 'calc(var(--tsrd-font-size) * 3.75)',
194
- '9xl': 'calc(var(--tsrd-font-size) * 4)',
195
- },
196
- weight: {
197
- thin: '100',
198
- extralight: '200',
199
- light: '300',
200
- normal: '400',
201
- medium: '500',
202
- semibold: '600',
203
- bold: '700',
204
- extrabold: '800',
205
- black: '900',
206
- },
207
- fontFamily: {
208
- sans: 'ui-sans-serif, Inter, system-ui, sans-serif, sans-serif',
209
- mono: `ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace`,
210
- },
211
- },
212
- breakpoints: {
213
- xs: '320px',
214
- sm: '640px',
215
- md: '768px',
216
- lg: '1024px',
217
- xl: '1280px',
218
- '2xl': '1536px',
219
- },
220
- border: {
221
- radius: {
222
- none: '0px',
223
- xs: 'calc(var(--tsrd-font-size) * 0.125)',
224
- sm: 'calc(var(--tsrd-font-size) * 0.25)',
225
- md: 'calc(var(--tsrd-font-size) * 0.375)',
226
- lg: 'calc(var(--tsrd-font-size) * 0.5)',
227
- xl: 'calc(var(--tsrd-font-size) * 0.75)',
228
- '2xl': 'calc(var(--tsrd-font-size) * 1)',
229
- '3xl': 'calc(var(--tsrd-font-size) * 1.5)',
230
- full: '9999px',
231
- },
232
- },
233
- size: {
234
- 0: '0px',
235
- 0.25: 'calc(var(--tsrd-font-size) * 0.0625)',
236
- 0.5: 'calc(var(--tsrd-font-size) * 0.125)',
237
- 1: 'calc(var(--tsrd-font-size) * 0.25)',
238
- 1.5: 'calc(var(--tsrd-font-size) * 0.375)',
239
- 2: 'calc(var(--tsrd-font-size) * 0.5)',
240
- 2.5: 'calc(var(--tsrd-font-size) * 0.625)',
241
- 3: 'calc(var(--tsrd-font-size) * 0.75)',
242
- 3.5: 'calc(var(--tsrd-font-size) * 0.875)',
243
- 4: 'calc(var(--tsrd-font-size) * 1)',
244
- 4.5: 'calc(var(--tsrd-font-size) * 1.125)',
245
- 5: 'calc(var(--tsrd-font-size) * 1.25)',
246
- 5.5: 'calc(var(--tsrd-font-size) * 1.375)',
247
- 6: 'calc(var(--tsrd-font-size) * 1.5)',
248
- 6.5: 'calc(var(--tsrd-font-size) * 1.625)',
249
- 7: 'calc(var(--tsrd-font-size) * 1.75)',
250
- 8: 'calc(var(--tsrd-font-size) * 2)',
251
- 9: 'calc(var(--tsrd-font-size) * 2.25)',
252
- 10: 'calc(var(--tsrd-font-size) * 2.5)',
253
- 11: 'calc(var(--tsrd-font-size) * 2.75)',
254
- 12: 'calc(var(--tsrd-font-size) * 3)',
255
- 14: 'calc(var(--tsrd-font-size) * 3.5)',
256
- 16: 'calc(var(--tsrd-font-size) * 4)',
257
- 20: 'calc(var(--tsrd-font-size) * 5)',
258
- 24: 'calc(var(--tsrd-font-size) * 6)',
259
- 28: 'calc(var(--tsrd-font-size) * 7)',
260
- 32: 'calc(var(--tsrd-font-size) * 8)',
261
- 36: 'calc(var(--tsrd-font-size) * 9)',
262
- 40: 'calc(var(--tsrd-font-size) * 10)',
263
- 44: 'calc(var(--tsrd-font-size) * 11)',
264
- 48: 'calc(var(--tsrd-font-size) * 12)',
265
- 52: 'calc(var(--tsrd-font-size) * 13)',
266
- 56: 'calc(var(--tsrd-font-size) * 14)',
267
- 60: 'calc(var(--tsrd-font-size) * 15)',
268
- 64: 'calc(var(--tsrd-font-size) * 16)',
269
- 72: 'calc(var(--tsrd-font-size) * 18)',
270
- 80: 'calc(var(--tsrd-font-size) * 20)',
271
- 96: 'calc(var(--tsrd-font-size) * 24)',
272
- },
273
- shadow: {
274
- xs: (_: string = 'rgb(0 0 0 / 0.1)') =>
275
- `0 1px 2px 0 rgb(0 0 0 / 0.05)` as const,
276
- sm: (color: string = 'rgb(0 0 0 / 0.1)') =>
277
- `0 1px 3px 0 ${color}, 0 1px 2px -1px ${color}` as const,
278
- md: (color: string = 'rgb(0 0 0 / 0.1)') =>
279
- `0 4px 6px -1px ${color}, 0 2px 4px -2px ${color}` as const,
280
- lg: (color: string = 'rgb(0 0 0 / 0.1)') =>
281
- `0 10px 15px -3px ${color}, 0 4px 6px -4px ${color}` as const,
282
- xl: (color: string = 'rgb(0 0 0 / 0.1)') =>
283
- `0 20px 25px -5px ${color}, 0 8px 10px -6px ${color}` as const,
284
- '2xl': (color: string = 'rgb(0 0 0 / 0.25)') =>
285
- `0 25px 50px -12px ${color}` as const,
286
- inner: (color: string = 'rgb(0 0 0 / 0.05)') =>
287
- `inset 0 2px 4px 0 ${color}` as const,
288
- none: () => `none` as const,
289
- },
290
- zIndices: {
291
- hide: -1,
292
- auto: 'auto',
293
- base: 0,
294
- docked: 10,
295
- dropdown: 1000,
296
- sticky: 1100,
297
- banner: 1200,
298
- overlay: 1300,
299
- modal: 1400,
300
- popover: 1500,
301
- skipLink: 1600,
302
- toast: 1700,
303
- tooltip: 1800,
304
- },
305
- } as const
@@ -1,52 +0,0 @@
1
- import React from 'react'
2
-
3
- const getItem = (key: string): unknown => {
4
- try {
5
- const itemValue = localStorage.getItem(key)
6
- if (typeof itemValue === 'string') {
7
- return JSON.parse(itemValue)
8
- }
9
- return undefined
10
- } catch {
11
- return undefined
12
- }
13
- }
14
-
15
- export default function useLocalStorage<T>(
16
- key: string,
17
- defaultValue: T | undefined,
18
- ): [T | undefined, (newVal: T | ((prevVal: T) => T)) => void] {
19
- const [value, setValue] = React.useState<T>()
20
-
21
- React.useEffect(() => {
22
- const initialValue = getItem(key) as T | undefined
23
-
24
- if (typeof initialValue === 'undefined' || initialValue === null) {
25
- setValue(
26
- typeof defaultValue === 'function' ? defaultValue() : defaultValue,
27
- )
28
- } else {
29
- setValue(initialValue)
30
- }
31
- }, [defaultValue, key])
32
-
33
- const setter = React.useCallback(
34
- (updater: any) => {
35
- setValue((old) => {
36
- let newVal = updater
37
-
38
- if (typeof updater == 'function') {
39
- newVal = updater(old)
40
- }
41
- try {
42
- localStorage.setItem(key, JSON.stringify(newVal))
43
- } catch {}
44
-
45
- return newVal
46
- })
47
- },
48
- [key],
49
- )
50
-
51
- return [value, setter]
52
- }
@@ -1,39 +0,0 @@
1
- import React from 'react'
2
-
3
- export default function useMediaQuery(query: string): boolean | undefined {
4
- // Keep track of the preference in state, start with the current match
5
- const [isMatch, setIsMatch] = React.useState(() => {
6
- if (typeof window !== 'undefined') {
7
- return window.matchMedia && window.matchMedia(query).matches
8
- }
9
- return
10
- })
11
-
12
- // Watch for changes
13
- React.useEffect(() => {
14
- if (typeof window !== 'undefined') {
15
- if (!window.matchMedia) {
16
- return
17
- }
18
-
19
- // Create a matcher
20
- const matcher = window.matchMedia(query)
21
-
22
- // Create our handler
23
- const onChange = ({ matches }: { matches: boolean }) =>
24
- setIsMatch(matches)
25
-
26
- // Listen for changes
27
- matcher.addListener(onChange)
28
-
29
- return () => {
30
- // Stop listening for changes
31
- matcher.removeListener(onChange)
32
- }
33
- }
34
-
35
- return
36
- }, [isMatch, query, setIsMatch])
37
-
38
- return isMatch
39
- }
package/src/utils.ts DELETED
@@ -1,183 +0,0 @@
1
- import React from 'react'
2
- import { AnyRootRoute, AnyRoute, AnyRouteMatch } from '@tanstack/react-router'
3
-
4
- import { Theme, useTheme } from './theme'
5
- import useMediaQuery from './useMediaQuery'
6
-
7
- export const isServer = typeof window === 'undefined'
8
-
9
- type StyledComponent<T> = T extends 'button'
10
- ? React.DetailedHTMLProps<
11
- React.ButtonHTMLAttributes<HTMLButtonElement>,
12
- HTMLButtonElement
13
- >
14
- : T extends 'input'
15
- ? React.DetailedHTMLProps<
16
- React.InputHTMLAttributes<HTMLInputElement>,
17
- HTMLInputElement
18
- >
19
- : T extends 'select'
20
- ? React.DetailedHTMLProps<
21
- React.SelectHTMLAttributes<HTMLSelectElement>,
22
- HTMLSelectElement
23
- >
24
- : T extends keyof HTMLElementTagNameMap
25
- ? React.HTMLAttributes<HTMLElementTagNameMap[T]>
26
- : never
27
-
28
- export function getStatusColor(match: AnyRouteMatch) {
29
- return match.status === 'success' && match.isFetching
30
- ? 'blue'
31
- : match.status === 'pending'
32
- ? 'yellow'
33
- : match.status === 'error'
34
- ? 'red'
35
- : match.status === 'success'
36
- ? 'green'
37
- : 'gray'
38
- }
39
-
40
- export function getRouteStatusColor(
41
- matches: AnyRouteMatch[],
42
- route: AnyRoute | AnyRootRoute,
43
- ) {
44
- const found = matches.find((d) => d.routeId === route.id)
45
- if (!found) return 'gray'
46
- return getStatusColor(found)
47
- }
48
-
49
- type Styles =
50
- | React.CSSProperties
51
- | ((props: Record<string, any>, theme: Theme) => React.CSSProperties)
52
-
53
- export function styled<T extends keyof HTMLElementTagNameMap>(
54
- type: T,
55
- newStyles: Styles,
56
- queries: Record<string, Styles> = {},
57
- ) {
58
- return React.forwardRef<HTMLElementTagNameMap[T], StyledComponent<T>>(
59
- ({ style, ...rest }, ref) => {
60
- const theme = useTheme()
61
-
62
- const mediaStyles = Object.entries(queries).reduce(
63
- (current, [key, value]) => {
64
- // eslint-disable-next-line react-hooks/rules-of-hooks
65
- return useMediaQuery(key)
66
- ? {
67
- ...current,
68
- ...(typeof value === 'function' ? value(rest, theme) : value),
69
- }
70
- : current
71
- },
72
- {},
73
- )
74
-
75
- return React.createElement(type, {
76
- ...rest,
77
- style: {
78
- ...(typeof newStyles === 'function'
79
- ? newStyles(rest, theme)
80
- : newStyles),
81
- ...style,
82
- ...mediaStyles,
83
- },
84
- ref,
85
- })
86
- },
87
- )
88
- }
89
-
90
- export function useIsMounted() {
91
- const mountedRef = React.useRef(false)
92
- const isMounted = React.useCallback(() => mountedRef.current, [])
93
-
94
- React[isServer ? 'useEffect' : 'useLayoutEffect'](() => {
95
- mountedRef.current = true
96
- return () => {
97
- mountedRef.current = false
98
- }
99
- }, [])
100
-
101
- return isMounted
102
- }
103
-
104
- /**
105
- * Displays a string regardless the type of the data
106
- * @param {unknown} value Value to be stringified
107
- */
108
- export const displayValue = (value: unknown) => {
109
- const name = Object.getOwnPropertyNames(Object(value))
110
- const newValue = typeof value === 'bigint' ? `${value.toString()}n` : value
111
- try {
112
- return JSON.stringify(newValue, name)
113
- } catch (e) {
114
- return `unable to stringify`
115
- }
116
- }
117
-
118
- /**
119
- * This hook is a safe useState version which schedules state updates in microtasks
120
- * to prevent updating a component state while React is rendering different components
121
- * or when the component is not mounted anymore.
122
- */
123
- export function useSafeState<T>(initialState: T): [T, (value: T) => void] {
124
- const isMounted = useIsMounted()
125
- const [state, setState] = React.useState(initialState)
126
-
127
- const safeSetState = React.useCallback(
128
- (value: T) => {
129
- scheduleMicrotask(() => {
130
- if (isMounted()) {
131
- setState(value)
132
- }
133
- })
134
- },
135
- [isMounted],
136
- )
137
-
138
- return [state, safeSetState]
139
- }
140
-
141
- /**
142
- * Schedules a microtask.
143
- * This can be useful to schedule state updates after rendering.
144
- */
145
- function scheduleMicrotask(callback: () => void) {
146
- Promise.resolve()
147
- .then(callback)
148
- .catch((error) =>
149
- setTimeout(() => {
150
- throw error
151
- }),
152
- )
153
- }
154
-
155
- export function multiSortBy<T>(
156
- arr: T[],
157
- accessors: ((item: T) => any)[] = [(d) => d],
158
- ): T[] {
159
- return arr
160
- .map((d, i) => [d, i] as const)
161
- .sort(([a, ai], [b, bi]) => {
162
- for (const accessor of accessors) {
163
- const ao = accessor(a)
164
- const bo = accessor(b)
165
-
166
- if (typeof ao === 'undefined') {
167
- if (typeof bo === 'undefined') {
168
- continue
169
- }
170
- return 1
171
- }
172
-
173
- if (ao === bo) {
174
- continue
175
- }
176
-
177
- return ao > bo ? 1 : -1
178
- }
179
-
180
- return ai - bi
181
- })
182
- .map(([d]) => d)
183
- }