-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathuseCombinedControlSignal.ts
59 lines (44 loc) · 1.71 KB
/
useCombinedControlSignal.ts
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
import { Accessor, createSignal, untrack } from 'solid-js'
const resolveValueOrAccessor = <T>(valueOrAccessor: T | Accessor<T>): T => {
if (valueOrAccessor instanceof Function) {
return valueOrAccessor()
}
return valueOrAccessor
}
type TUseCombinedControlSignalProps<T> = {
value?: T | Accessor<T>
initialValue: T | Accessor<T>
onChange?: (value: T) => void
}
type TUseCombinedControlSignalSetter<T> = (prev: T) => T
type TUseCombinedControlSignalNextStateOrSetter<T> = TUseCombinedControlSignalSetter<T> | T
const useCombinedControlSignal = <T>(props: TUseCombinedControlSignalProps<T>) => {
const [innerValue, setInnerValue] = createSignal<T>(resolveValueOrAccessor(props.initialValue))
const isControlled = () => {
return resolveValueOrAccessor(props.value) !== undefined && !!props.onChange
}
const value = (): T => {
if (resolveValueOrAccessor(props.value) && isControlled()) {
return resolveValueOrAccessor(props.value) as T
}
return innerValue()
}
const setValue = (nextStateOrSetter: TUseCombinedControlSignalNextStateOrSetter<T>) => {
untrack(() => {
const nextValue = () => {
return nextStateOrSetter instanceof Function ? nextStateOrSetter(value()) : nextStateOrSetter
}
const shouldChange = !Object.is(nextValue(), value())
if (shouldChange) {
if (!isControlled()) {
setInnerValue(nextValue)
}
if (props.onChange) {
props.onChange(nextValue())
}
}
})
}
return [value, setValue] as const
}
export { useCombinedControlSignal }