@globalbrain/sefirot 2.3.0 → 2.4.0
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/lib/composables/Data.ts +108 -0
- package/lib/composables/Form.ts +8 -10
- package/lib/support/Utils.ts +4 -0
- package/package.json +1 -1
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { watchOnce } from '@vueuse/core'
|
|
2
|
+
import cloneDeep from 'lodash-es/cloneDeep'
|
|
3
|
+
import { WatchSource, reactive } from 'vue'
|
|
4
|
+
import { isObject } from '../support/Utils'
|
|
5
|
+
|
|
6
|
+
export interface Data<T extends Record<string, any>> {
|
|
7
|
+
state: T
|
|
8
|
+
init(): void
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export type DataWithDef = Record<string, any>
|
|
12
|
+
|
|
13
|
+
export interface Def<T = any> {
|
|
14
|
+
__isDef: true
|
|
15
|
+
value: any
|
|
16
|
+
source: WatchSource<T>
|
|
17
|
+
cb: (value: Exclude<T, undefined>) => void
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export type UseDataInput<
|
|
21
|
+
T extends Record<string, any>
|
|
22
|
+
> = T | ((utils: UseDataInputUtils) => DataWithDef)
|
|
23
|
+
|
|
24
|
+
export interface UseDataInputUtils {
|
|
25
|
+
def<T>(
|
|
26
|
+
value: any,
|
|
27
|
+
source: WatchSource<T>,
|
|
28
|
+
cb: (value: Exclude<T, undefined>) => void
|
|
29
|
+
): Def<T>
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function useData<T extends Record<string, any>>(
|
|
33
|
+
data: UseDataInput<T>
|
|
34
|
+
): Data<T> {
|
|
35
|
+
const { state, defs } = createState(data)
|
|
36
|
+
|
|
37
|
+
const initialState = cloneDeep(state)
|
|
38
|
+
const reactiveState = reactive(state)
|
|
39
|
+
|
|
40
|
+
handleDefs(defs, reactiveState)
|
|
41
|
+
|
|
42
|
+
function init(): void {
|
|
43
|
+
Object.assign(reactiveState, initialState)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
state: reactiveState,
|
|
48
|
+
init
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function createState<T extends Record<string, any>>(
|
|
53
|
+
data: UseDataInput<T>
|
|
54
|
+
): { state: T; defs: [string, Def][] } {
|
|
55
|
+
if (typeof data !== 'function') {
|
|
56
|
+
return { state: data, defs: [] }
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const dataWithDef = data({ def })
|
|
60
|
+
|
|
61
|
+
const state = {} as T
|
|
62
|
+
const defs = [] as [string, Def][]
|
|
63
|
+
|
|
64
|
+
for (const key in dataWithDef) {
|
|
65
|
+
const maybeDef = dataWithDef[key]
|
|
66
|
+
|
|
67
|
+
if (!isDef(maybeDef)) {
|
|
68
|
+
(state as any)[key] = maybeDef
|
|
69
|
+
continue
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
(state as any)[key] = maybeDef.value
|
|
73
|
+
|
|
74
|
+
defs.push([key, maybeDef])
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
state,
|
|
79
|
+
defs
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function handleDefs<T extends Record<string, any>>(
|
|
84
|
+
defs: [string, Def][], state: T
|
|
85
|
+
): void {
|
|
86
|
+
defs.forEach(([key, def]) => {
|
|
87
|
+
watchOnce(def.source, (value: any) => {
|
|
88
|
+
(state as any)[key] = def.cb(value)
|
|
89
|
+
})
|
|
90
|
+
})
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function def<T>(
|
|
94
|
+
value: any,
|
|
95
|
+
source: WatchSource<T>,
|
|
96
|
+
cb: (value: Exclude<T, undefined>) => void
|
|
97
|
+
): Def {
|
|
98
|
+
return {
|
|
99
|
+
__isDef: true,
|
|
100
|
+
value,
|
|
101
|
+
source,
|
|
102
|
+
cb
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function isDef(value: any): boolean {
|
|
107
|
+
return isObject(value) ? !!value.__isDef : false
|
|
108
|
+
}
|
package/lib/composables/Form.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { Ref, computed, reactive } from 'vue'
|
|
1
|
+
import { Ref, computed } from 'vue'
|
|
3
2
|
import { useSnackbars } from '../stores/Snackbars'
|
|
3
|
+
import { UseDataInput, useData } from './Data'
|
|
4
4
|
import { Validation, useValidation } from './Validation'
|
|
5
5
|
|
|
6
6
|
export interface Form<T extends Record<string, any>> {
|
|
@@ -13,7 +13,7 @@ export interface Form<T extends Record<string, any>> {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
export interface UseFormOptions<T extends Record<string, any>> {
|
|
16
|
-
data: T
|
|
16
|
+
data: UseDataInput<T>
|
|
17
17
|
rules?: Record<string, any> | ((state: T) => Record<string, any>)
|
|
18
18
|
}
|
|
19
19
|
|
|
@@ -22,20 +22,18 @@ export function useForm<
|
|
|
22
22
|
>(options: UseFormOptions<T>): Form<T> {
|
|
23
23
|
const snackbars = useSnackbars()
|
|
24
24
|
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
const data = reactive(options.data)
|
|
25
|
+
const data = useData(options.data)
|
|
28
26
|
|
|
29
27
|
const rules = computed(() => {
|
|
30
28
|
return options.rules
|
|
31
|
-
? typeof options.rules === 'function' ? options.rules(data) : options.rules
|
|
29
|
+
? typeof options.rules === 'function' ? options.rules(data.state) : options.rules
|
|
32
30
|
: {}
|
|
33
31
|
})
|
|
34
32
|
|
|
35
|
-
const validation = useValidation(data, rules)
|
|
33
|
+
const validation = useValidation(data.state, rules)
|
|
36
34
|
|
|
37
35
|
function init(): void {
|
|
38
|
-
|
|
36
|
+
data.init()
|
|
39
37
|
reset()
|
|
40
38
|
}
|
|
41
39
|
|
|
@@ -61,7 +59,7 @@ export function useForm<
|
|
|
61
59
|
}
|
|
62
60
|
|
|
63
61
|
return {
|
|
64
|
-
data,
|
|
62
|
+
data: data.state,
|
|
65
63
|
init,
|
|
66
64
|
reset,
|
|
67
65
|
validation,
|
package/lib/support/Utils.ts
CHANGED
|
@@ -13,3 +13,7 @@ export function isNumber(value: unknown): value is number {
|
|
|
13
13
|
export function isArray(value: unknown): value is unknown[] {
|
|
14
14
|
return Array.isArray(value)
|
|
15
15
|
}
|
|
16
|
+
|
|
17
|
+
export function isObject(value: unknown): value is Record<string, any> {
|
|
18
|
+
return typeof value === 'object' && value !== null && !isArray(value)
|
|
19
|
+
}
|