Repository navigation
Expand file tree
/
Copy pathutils.ts
More file actions
149 lines (131 loc) · 3.69 KB
/
Copy pathutils.ts
File metadata and controls
149 lines (131 loc) · 3.69 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
import React from 'react'
import { Query } from '../core'
import { Theme, useTheme } from './theme'
import useMediaQuery from './useMediaQuery'
export const isServer = typeof window === 'undefined'
type StyledComponent<T> = T extends 'button'
? React.DetailedHTMLProps<
React.ButtonHTMLAttributes<HTMLButtonElement>,
HTMLButtonElement
>
: T extends 'input'
? React.DetailedHTMLProps<
React.InputHTMLAttributes<HTMLInputElement>,
HTMLInputElement
>
: T extends 'select'
? React.DetailedHTMLProps<
React.SelectHTMLAttributes<HTMLSelectElement>,
HTMLSelectElement
>
: T extends keyof HTMLElementTagNameMap
? React.HTMLAttributes<HTMLElementTagNameMap[T]>
: never
export function getQueryStatusColor(query: Query, theme: Theme) {
return query.state.isFetching
? theme.active
: !query.getObserversCount()
? theme.gray
: query.isStale()
? theme.warning
: theme.success
}
export function getQueryStatusLabel(query: Query) {
return query.state.isFetching
? 'fetching'
: !query.getObserversCount()
? 'inactive'
: query.isStale()
? 'stale'
: 'fresh'
}
type Styles =
| React.CSSProperties
| ((props: Record<string, any>, theme: Theme) => React.CSSProperties)
export function styled<T extends keyof HTMLElementTagNameMap>(
type: T,
newStyles: Styles,
queries: Record<string, Styles> = {}
) {
return React.forwardRef<HTMLElementTagNameMap[T], StyledComponent<T>>(
({ style, ...rest }, ref) => {
const theme = useTheme()
const mediaStyles = Object.entries(queries).reduce(
(current, [key, value]) => {
// eslint-disable-next-line react-hooks/rules-of-hooks
return useMediaQuery(key)
? {
...current,
...(typeof value === 'function' ? value(rest, theme) : value),
}
: current
},
{}
)
return React.createElement(type, {
...rest,
style: {
...(typeof newStyles === 'function'
? newStyles(rest, theme)
: newStyles),
...style,
...mediaStyles,
},
ref,
})
}
)
}
export function useIsMounted() {
const mountedRef = React.useRef(false)
const isMounted = React.useCallback(() => mountedRef.current, [])
React[isServer ? 'useEffect' : 'useLayoutEffect'](() => {
mountedRef.current = true
return () => {
mountedRef.current = false
}
}, [])
return isMounted
}
/**
* This hook is a safe useState version which schedules state updates in microtasks
* to prevent updating a component state while React is rendering different components
* or when the component is not mounted anymore.
*/
export function useSafeState<T>(initialState: T): [T, (value: T) => void] {
const isMounted = useIsMounted()
const [state, setState] = React.useState(initialState)
const safeSetState = React.useCallback(
(value: T) => {
scheduleMicrotask(() => {
if (isMounted()) {
setState(value)
}
})
},
[isMounted]
)
return [state, safeSetState]
}
/**
* Displays a string regardless the type of the data
* @param {unknown} value Value to be stringified
*/
export const displayValue = (value: unknown) => {
const name = Object.getOwnPropertyNames(Object(value))
const newValue = typeof value === 'bigint' ? `${value.toString()}n` : value
return JSON.stringify(newValue, name)
}
/**
* Schedules a microtask.
* This can be useful to schedule state updates after rendering.
*/
function scheduleMicrotask(callback: () => void) {
Promise.resolve()
.then(callback)
.catch(error =>
setTimeout(() => {
throw error
})
)
}