@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.
@@ -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
+ }
@@ -1,6 +1,6 @@
1
- import cloneDeep from 'lodash-es/cloneDeep'
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 initialData = cloneDeep(options.data)
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
- Object.assign(data, initialData)
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,
@@ -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
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@globalbrain/sefirot",
3
- "version": "2.3.0",
3
+ "version": "2.4.0",
4
4
  "description": "Vue Components for Global Brain Design System.",
5
5
  "author": "Kia Ishii <ka.ishii@globalbrains.com>",
6
6
  "license": "MIT",