@effect-app/vue-components 0.2.7 → 0.3.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/dist/types/components/OmegaForm/OmegaErrors.vue.d.ts +2 -2
- package/dist/types/components/OmegaForm/OmegaErrorsContext.d.ts +8 -5
- package/dist/types/components/OmegaForm/OmegaFormStuff.d.ts +1 -0
- package/dist/types/components/OmegaForm/OmegaInput.vue.d.ts +0 -1
- package/dist/types/components/OmegaForm/OmegaInternalInput.vue.d.ts +0 -1
- package/dist/types/components/OmegaForm/OmegaWrapper.vue.d.ts +8 -14
- package/dist/types/components/OmegaForm/getOmegaStore.d.ts +1 -1
- package/dist/types/components/OmegaForm/index.d.ts +5 -4
- package/dist/types/components/OmegaForm/useOmegaForm.d.ts +20 -3
- package/dist/vue-components.es11.js +27 -24
- package/dist/vue-components.es14.js +29 -29
- package/dist/vue-components.es15.js +4 -87
- package/dist/vue-components.es16.js +93 -11
- package/dist/vue-components.es17.js +11 -2
- package/dist/vue-components.es18.js +2 -115
- package/dist/vue-components.es19.js +117 -0
- package/dist/{vue-components.es20.js → vue-components.es21.js} +1 -1
- package/dist/vue-components.es4.js +29 -21
- package/dist/vue-components.es5.js +82 -16
- package/dist/vue-components.es7.js +2 -2
- package/dist/vue-components.es8.js +1 -1
- package/dist/vue-components.es9.js +2 -2
- package/package.json +11 -2
- package/src/components/OmegaForm/OmegaErrors.vue +2 -5
- package/src/components/OmegaForm/OmegaErrorsContext.ts +16 -3
- package/src/components/OmegaForm/OmegaFormStuff.ts +2 -0
- package/src/components/OmegaForm/OmegaInternalInput.vue +20 -12
- package/src/components/OmegaForm/OmegaWrapper.vue +13 -9
- package/src/components/OmegaForm/getOmegaStore.ts +1 -1
- package/src/components/OmegaForm/useOmegaForm.ts +133 -11
- package/src/stories/OmegaForm/ComplexForm.vue +81 -0
- package/src/stories/OmegaForm/EmailForm.vue +48 -0
- package/src/stories/OmegaForm/PersistencyForm.vue +65 -0
- package/src/stories/OmegaForm/SimpleForm.vue +30 -0
- package/src/stories/OmegaForm/SimpleFormVuetifyDefault.vue +15 -0
- package/src/stories/OmegaForm/SumExample.vue +39 -0
- package/src/stories/OmegaForm.stories.ts +83 -0
- package/src/stories/README.md +29 -0
- package/src/stories/tsconfig.json +4 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<form @submit.prevent.stop="form.handleSubmit()">
|
|
2
|
+
<form novalidate @submit.prevent.stop="form.handleSubmit()">
|
|
3
3
|
<fieldset :disabled="formIsSubmitting">
|
|
4
4
|
<slot :form="form" :subscribed-values="subscribedValues" />
|
|
5
5
|
</fieldset>
|
|
@@ -51,24 +51,27 @@ import { type S } from "effect-app"
|
|
|
51
51
|
import {
|
|
52
52
|
type FilterItems,
|
|
53
53
|
type FormProps,
|
|
54
|
-
type MetaRecord,
|
|
55
54
|
type OmegaFormApi,
|
|
56
55
|
type OmegaFormState,
|
|
56
|
+
type ShowErrorsOn,
|
|
57
57
|
} from "./OmegaFormStuff"
|
|
58
58
|
import { getOmegaStore } from "./getOmegaStore"
|
|
59
59
|
import { provideOmegaErrors } from "./OmegaErrorsContext"
|
|
60
|
-
import {
|
|
60
|
+
import {
|
|
61
|
+
type OmegaConfig,
|
|
62
|
+
type OmegaFormReturn,
|
|
63
|
+
useOmegaForm,
|
|
64
|
+
} from "./useOmegaForm"
|
|
61
65
|
import { watch } from "vue"
|
|
62
66
|
|
|
63
67
|
const props = defineProps<
|
|
64
68
|
{
|
|
69
|
+
omegaConfig?: OmegaConfig<From>
|
|
65
70
|
subscribe?: K[]
|
|
71
|
+
showErrorsOn?: ShowErrorsOn
|
|
66
72
|
} & (
|
|
67
73
|
| {
|
|
68
|
-
form:
|
|
69
|
-
meta: MetaRecord<To>
|
|
70
|
-
filterItems?: FilterItems
|
|
71
|
-
}
|
|
74
|
+
form: OmegaFormReturn<To, From>
|
|
72
75
|
schema?: undefined
|
|
73
76
|
}
|
|
74
77
|
| (FormProps<To, From> & {
|
|
@@ -78,7 +81,8 @@ const props = defineProps<
|
|
|
78
81
|
)
|
|
79
82
|
>()
|
|
80
83
|
|
|
81
|
-
const form =
|
|
84
|
+
const form =
|
|
85
|
+
props.form ?? useOmegaForm<From, To>(props.schema, props, props.omegaConfig)
|
|
82
86
|
|
|
83
87
|
const formIsSubmitting = useStore(form.store, state => state.isSubmitting)
|
|
84
88
|
|
|
@@ -127,7 +131,7 @@ watch(
|
|
|
127
131
|
},
|
|
128
132
|
)
|
|
129
133
|
|
|
130
|
-
provideOmegaErrors(formSubmissionAttempts, errors)
|
|
134
|
+
provideOmegaErrors(formSubmissionAttempts, errors, props.showErrorsOn)
|
|
131
135
|
</script>
|
|
132
136
|
|
|
133
137
|
<style scoped>
|
|
@@ -1,17 +1,45 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
1
2
|
import {
|
|
2
3
|
useForm,
|
|
3
4
|
type FormValidateOrFn,
|
|
4
5
|
type FormAsyncValidateOrFn,
|
|
5
6
|
type StandardSchemaV1,
|
|
6
7
|
} from "@tanstack/vue-form"
|
|
7
|
-
import { S } from "effect-app"
|
|
8
|
+
import { Match, S } from "effect-app"
|
|
8
9
|
import {
|
|
9
10
|
generateMetaFromSchema,
|
|
11
|
+
type NestedKeyOf,
|
|
10
12
|
type FilterItems,
|
|
11
13
|
type FormProps,
|
|
12
14
|
type MetaRecord,
|
|
13
15
|
type OmegaFormApi,
|
|
14
16
|
} from "./OmegaFormStuff"
|
|
17
|
+
import { computed, onBeforeUnmount, onMounted, onUnmounted } from "vue"
|
|
18
|
+
import { constVoid } from "effect/Function"
|
|
19
|
+
|
|
20
|
+
type keysRule<T> =
|
|
21
|
+
| {
|
|
22
|
+
keys?: NestedKeyOf<T>[]
|
|
23
|
+
banKeys?: "You should only use one of banKeys or keys, not both, moron"
|
|
24
|
+
}
|
|
25
|
+
| {
|
|
26
|
+
keys?: "You should only use one of banKeys or keys, not both, moron"
|
|
27
|
+
banKeys?: NestedKeyOf<T>[]
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export type OmegaConfig<T> = {
|
|
31
|
+
persistency?: {
|
|
32
|
+
method?: "session" | "local" | "none"
|
|
33
|
+
overrideDefaultValues?: boolean
|
|
34
|
+
id?: string
|
|
35
|
+
} & keysRule<T>
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface OmegaFormReturn<To, From> extends OmegaFormApi<To, From> {
|
|
39
|
+
meta: MetaRecord<To>
|
|
40
|
+
filterItems?: FilterItems
|
|
41
|
+
clear: () => void
|
|
42
|
+
}
|
|
15
43
|
|
|
16
44
|
export const useOmegaForm = <
|
|
17
45
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -20,16 +48,55 @@ export const useOmegaForm = <
|
|
|
20
48
|
To extends Record<PropertyKey, any>,
|
|
21
49
|
>(
|
|
22
50
|
schema: S.Schema<From, To, never>,
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
filterItems?: FilterItems
|
|
27
|
-
} => {
|
|
51
|
+
tanstackFormOptions?: NoInfer<FormProps<To, From>>,
|
|
52
|
+
omegaConfig?: OmegaConfig<From>,
|
|
53
|
+
): OmegaFormReturn<To, From> => {
|
|
28
54
|
if (!schema) throw new Error("Schema is required")
|
|
29
55
|
const standardSchema = S.standardSchemaV1(schema)
|
|
30
56
|
|
|
31
57
|
const { filterItems, meta } = generateMetaFromSchema(schema)
|
|
32
58
|
|
|
59
|
+
const persistencyKey = computed(() => {
|
|
60
|
+
if (omegaConfig?.persistency?.id) {
|
|
61
|
+
return omegaConfig.persistency.id
|
|
62
|
+
}
|
|
63
|
+
const path = window.location.pathname
|
|
64
|
+
const keys = Object.keys(meta)
|
|
65
|
+
return `${path}-${keys.join("-")}`
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
const defaultValues = computed(() => {
|
|
69
|
+
if (
|
|
70
|
+
tanstackFormOptions?.defaultValues &&
|
|
71
|
+
!omegaConfig?.persistency?.overrideDefaultValues
|
|
72
|
+
)
|
|
73
|
+
return tanstackFormOptions.defaultValues
|
|
74
|
+
const persistency = omegaConfig?.persistency
|
|
75
|
+
return Match.value(persistency).pipe(
|
|
76
|
+
Match.when(
|
|
77
|
+
{ method: method => ["local", "session"].includes(method) },
|
|
78
|
+
persistency => {
|
|
79
|
+
const method = persistency.method
|
|
80
|
+
const storage = method === "local" ? localStorage : sessionStorage
|
|
81
|
+
if (storage) {
|
|
82
|
+
try {
|
|
83
|
+
const value = JSON.parse(
|
|
84
|
+
storage.getItem(persistencyKey.value) || "{}",
|
|
85
|
+
)
|
|
86
|
+
storage.removeItem(persistencyKey.value)
|
|
87
|
+
return value
|
|
88
|
+
} catch (error) {
|
|
89
|
+
console.error(error)
|
|
90
|
+
return {}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return {}
|
|
94
|
+
},
|
|
95
|
+
),
|
|
96
|
+
Match.orElse(() => ({})),
|
|
97
|
+
)
|
|
98
|
+
})
|
|
99
|
+
|
|
33
100
|
const form = useForm<
|
|
34
101
|
To,
|
|
35
102
|
FormValidateOrFn<To> | undefined,
|
|
@@ -42,22 +109,77 @@ export const useOmegaForm = <
|
|
|
42
109
|
FormAsyncValidateOrFn<To> | undefined,
|
|
43
110
|
FormAsyncValidateOrFn<To> | undefined
|
|
44
111
|
>({
|
|
45
|
-
...
|
|
112
|
+
...tanstackFormOptions,
|
|
46
113
|
validators: {
|
|
47
114
|
onSubmit: standardSchema,
|
|
48
|
-
...(
|
|
115
|
+
...(tanstackFormOptions?.validators || {}),
|
|
49
116
|
},
|
|
50
|
-
onSubmit:
|
|
117
|
+
onSubmit: tanstackFormOptions?.onSubmit
|
|
51
118
|
? ({ formApi, meta, value }) =>
|
|
52
|
-
|
|
119
|
+
tanstackFormOptions.onSubmit?.({
|
|
53
120
|
formApi: formApi as OmegaFormApi<To, From>,
|
|
54
121
|
meta,
|
|
55
122
|
value: value as unknown as From,
|
|
56
123
|
})
|
|
57
124
|
: undefined,
|
|
125
|
+
defaultValues: defaultValues.value as any,
|
|
58
126
|
}) satisfies OmegaFormApi<To, From>
|
|
59
127
|
|
|
60
|
-
const
|
|
128
|
+
const clear = () => {
|
|
129
|
+
Object.keys(meta).forEach((key: any) => {
|
|
130
|
+
form.setFieldValue(key, undefined)
|
|
131
|
+
})
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const exposed = Object.assign(form, { meta, filterItems, clear })
|
|
135
|
+
|
|
136
|
+
// This is fragile as fuck. It's an experiment, it's only used because
|
|
137
|
+
// it's not a core feature of our products, so even if the this is not consistent
|
|
138
|
+
// it's not a big deal. So not take this code as a good example of how to do things.
|
|
139
|
+
// This is done only because this function is called before the component is destroyed,
|
|
140
|
+
// so the state would be lost anyway. So in this case we can play with the state, without
|
|
141
|
+
// worrying about the side effects.
|
|
142
|
+
const persistData = () => {
|
|
143
|
+
const persistency = omegaConfig?.persistency
|
|
144
|
+
Match.value(persistency).pipe(
|
|
145
|
+
Match.when(
|
|
146
|
+
{ method: method => ["local", "session"].includes(method) },
|
|
147
|
+
persistency => {
|
|
148
|
+
const method = persistency.method
|
|
149
|
+
const storage = method === "local" ? localStorage : sessionStorage
|
|
150
|
+
if (storage) {
|
|
151
|
+
if (Array.isArray(persistency.keys)) {
|
|
152
|
+
const subs = Object.keys(meta).filter(
|
|
153
|
+
metakey => !persistency.keys?.includes(metakey as any),
|
|
154
|
+
)
|
|
155
|
+
subs.forEach(key => {
|
|
156
|
+
form.setFieldValue(key as any, undefined)
|
|
157
|
+
})
|
|
158
|
+
}
|
|
159
|
+
if (Array.isArray(persistency.banKeys)) {
|
|
160
|
+
persistency.banKeys.forEach(key => {
|
|
161
|
+
form.setFieldValue(key as any, undefined)
|
|
162
|
+
})
|
|
163
|
+
}
|
|
164
|
+
return storage.setItem(
|
|
165
|
+
persistencyKey.value,
|
|
166
|
+
JSON.stringify(form.store.state.values),
|
|
167
|
+
)
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
),
|
|
171
|
+
Match.orElse(constVoid),
|
|
172
|
+
)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
onUnmounted(persistData)
|
|
176
|
+
|
|
177
|
+
onMounted(() => {
|
|
178
|
+
window.addEventListener("beforeunload", persistData)
|
|
179
|
+
})
|
|
180
|
+
onBeforeUnmount(() => {
|
|
181
|
+
window.removeEventListener("beforeunload", persistData)
|
|
182
|
+
})
|
|
61
183
|
|
|
62
184
|
return exposed
|
|
63
185
|
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<OmegaForm :form="form">
|
|
3
|
+
<template #default="{ form }">
|
|
4
|
+
<OmegaInput label="aString" :form="form" name="aString" />
|
|
5
|
+
<OmegaInput label="aStringMin2" :form="form" name="aStringMin2" />
|
|
6
|
+
<OmegaInput label="aStringMin2Max4" :form="form" name="aStringMin2Max4" />
|
|
7
|
+
<OmegaInput
|
|
8
|
+
label="aStringMin2Max3Nullable"
|
|
9
|
+
:form="form"
|
|
10
|
+
name="aStringMin2Max3Nullable"
|
|
11
|
+
/>
|
|
12
|
+
<OmegaInput label="aNumber" :form="form" name="aNumber" />
|
|
13
|
+
<OmegaInput label="aNumberMin2" :form="form" name="aNumberMin2" />
|
|
14
|
+
<OmegaInput label="aNumberMin2Max" :form="form" name="aNumberMin2Max" />
|
|
15
|
+
<OmegaInput
|
|
16
|
+
label="aNumberMin2Max4Nullable"
|
|
17
|
+
:form="form"
|
|
18
|
+
name="aNumberMin2Max4Nullable"
|
|
19
|
+
/>
|
|
20
|
+
<OmegaInput
|
|
21
|
+
label="aSelect"
|
|
22
|
+
:form="form"
|
|
23
|
+
name="aSelect"
|
|
24
|
+
:options="[
|
|
25
|
+
{ title: 'a', value: 'a' },
|
|
26
|
+
{ title: 'b', value: 'b' },
|
|
27
|
+
{ title: 'c', value: 'c' },
|
|
28
|
+
]"
|
|
29
|
+
/>
|
|
30
|
+
<button>Submit</button>
|
|
31
|
+
<button type="reset" @click.prevent="form.clear()">Clear</button>
|
|
32
|
+
<button type="button" @click="form.reset()">Reset</button>
|
|
33
|
+
</template>
|
|
34
|
+
</OmegaForm>
|
|
35
|
+
</template>
|
|
36
|
+
|
|
37
|
+
<script setup lang="ts">
|
|
38
|
+
import { S } from "effect-app"
|
|
39
|
+
import { OmegaForm, OmegaInput, useOmegaForm } from "../../components/OmegaForm"
|
|
40
|
+
|
|
41
|
+
const form = useOmegaForm(
|
|
42
|
+
S.Struct({
|
|
43
|
+
aString: S.String,
|
|
44
|
+
aStringMin2: S.String.pipe(S.minLength(2)),
|
|
45
|
+
aStringMin2Max4: S.String.pipe(S.minLength(2)).pipe(S.maxLength(4)),
|
|
46
|
+
aStringMin2Max3Nullable: S.UndefinedOr(
|
|
47
|
+
S.String.pipe(S.minLength(2)).pipe(S.maxLength(3)),
|
|
48
|
+
),
|
|
49
|
+
aNumber: S.Number,
|
|
50
|
+
aNumberMin2: S.Number.pipe(S.greaterThan(2)),
|
|
51
|
+
aNumberMin2Max: S.Number.pipe(S.greaterThan(2)).pipe(S.lessThan(4)),
|
|
52
|
+
aNumberMin2Max4Nullable: S.NullOr(S.Number.pipe(S.between(2, 4))),
|
|
53
|
+
aSelect: S.Union(S.Literal("a"), S.Literal("b"), S.Literal("c")),
|
|
54
|
+
}),
|
|
55
|
+
{
|
|
56
|
+
onSubmit: ({
|
|
57
|
+
value,
|
|
58
|
+
}: {
|
|
59
|
+
value: {
|
|
60
|
+
aString: string
|
|
61
|
+
aStringMin2: string
|
|
62
|
+
aStringMin2Max4: string
|
|
63
|
+
aStringMin2Max3Nullable?: string
|
|
64
|
+
aNumber: number
|
|
65
|
+
aNumberMin2: number
|
|
66
|
+
aNumberMin2Max: number
|
|
67
|
+
aNumberMin2Max4Nullable: number | null
|
|
68
|
+
aSelect: "a" | "b" | "c"
|
|
69
|
+
}
|
|
70
|
+
}) => {
|
|
71
|
+
console.log(value)
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
persistency: {
|
|
76
|
+
method: "local",
|
|
77
|
+
overrideDefaultValues: true,
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
)
|
|
81
|
+
</script>
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<OmegaForm
|
|
3
|
+
:schema="schema"
|
|
4
|
+
:on-submit="onSubmit"
|
|
5
|
+
:default-values="defaultValues"
|
|
6
|
+
>
|
|
7
|
+
<template #default="{ form }">
|
|
8
|
+
<OmegaInput label="email" name="email" :form="form" />
|
|
9
|
+
<OmegaInput label="confirm" name="confirm" :form="form" />
|
|
10
|
+
<button>submit</button>
|
|
11
|
+
<OmegaErrors />
|
|
12
|
+
</template>
|
|
13
|
+
</OmegaForm>
|
|
14
|
+
</template>
|
|
15
|
+
|
|
16
|
+
<script setup lang="ts">
|
|
17
|
+
import { S } from "effect-app"
|
|
18
|
+
import { OmegaForm, OmegaInput, OmegaErrors } from "../../components/OmegaForm"
|
|
19
|
+
|
|
20
|
+
const schema = S.Struct({
|
|
21
|
+
email: S.Email,
|
|
22
|
+
confirm: S.Email,
|
|
23
|
+
}).pipe(
|
|
24
|
+
S.filter(
|
|
25
|
+
form => {
|
|
26
|
+
if (form.email !== form.confirm) {
|
|
27
|
+
return false
|
|
28
|
+
}
|
|
29
|
+
return true
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
message: () => "Email and confirmation must match",
|
|
33
|
+
jsonSchema: {
|
|
34
|
+
items: ["confirm"],
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
),
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
const defaultValues = {
|
|
41
|
+
email: "mimmo@asd.it",
|
|
42
|
+
confirm: "amerelli@asd.it",
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const onSubmit = ({ value }: { value: { email: string; confirm: string } }) => {
|
|
46
|
+
console.log(value)
|
|
47
|
+
}
|
|
48
|
+
</script>
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<OmegaForm
|
|
3
|
+
:form="addForm"
|
|
4
|
+
:subscribe="['errors', 'values']"
|
|
5
|
+
show-errors-on="onChange"
|
|
6
|
+
>
|
|
7
|
+
<template #default="{ form, subscribedValues: { errors, values } }">
|
|
8
|
+
<div>Errors: {{ errors }}</div>
|
|
9
|
+
<div>Values: {{ values }}</div>
|
|
10
|
+
<OmegaInput label="first" :form="form" name="first" />
|
|
11
|
+
<div>+</div>
|
|
12
|
+
<OmegaInput label="second" :form="form" name="second" />
|
|
13
|
+
<br />
|
|
14
|
+
<hr />
|
|
15
|
+
<br />
|
|
16
|
+
<OmegaInput label="third.fourth" :form="form" name="third.fourth" />
|
|
17
|
+
<OmegaInput label="third.fifth" :form="form" name="third.fifth" />
|
|
18
|
+
</template>
|
|
19
|
+
</OmegaForm>
|
|
20
|
+
|
|
21
|
+
<!-- Technically you can do this only with a subscribe but only inside OmegaForm Context -->
|
|
22
|
+
<div>
|
|
23
|
+
<div>Sum: {{ sum }}</div>
|
|
24
|
+
</div>
|
|
25
|
+
</template>
|
|
26
|
+
|
|
27
|
+
<script setup lang="ts">
|
|
28
|
+
import { S } from "effect-app"
|
|
29
|
+
import { OmegaForm, OmegaInput, useOmegaForm } from "../../components/OmegaForm"
|
|
30
|
+
import { ref, watch } from "vue"
|
|
31
|
+
|
|
32
|
+
const sum = ref(0)
|
|
33
|
+
const AddSchema = S.Struct({
|
|
34
|
+
first: S.Number,
|
|
35
|
+
second: S.Number.pipe(S.greaterThan(3)),
|
|
36
|
+
third: S.Struct({
|
|
37
|
+
fourth: S.Number,
|
|
38
|
+
fifth: S.Number,
|
|
39
|
+
}),
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
const addForm = useOmegaForm(
|
|
43
|
+
AddSchema,
|
|
44
|
+
{},
|
|
45
|
+
{
|
|
46
|
+
persistency: {
|
|
47
|
+
method: "session",
|
|
48
|
+
keys: ["first", "third.fourth"],
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
const values = addForm.useStore(({ values }) => values)
|
|
54
|
+
|
|
55
|
+
watch(values, ({ first, second }) => {
|
|
56
|
+
sum.value = first + second
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
// TODO: Implement this when we have a way to persist the form values
|
|
60
|
+
// {
|
|
61
|
+
// persist: "session",
|
|
62
|
+
// persistKeys: ["riskCategoryPeriod"],
|
|
63
|
+
// persistBanKeys: ["riskCategory"],
|
|
64
|
+
// }
|
|
65
|
+
</script>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<OmegaForm :schema="schema" :on-submit="onSubmit" :subscribe="['values']">
|
|
3
|
+
<template #default="{ form, subscribedValues: { values } }">
|
|
4
|
+
<div>values: {{ values }}</div>
|
|
5
|
+
<OmegaInput label="asder2" name="asder2" :form="form">
|
|
6
|
+
<template #default="inputProps">
|
|
7
|
+
<label :for="inputProps.name">{{ inputProps.label }}</label>
|
|
8
|
+
<input
|
|
9
|
+
:id="inputProps.name"
|
|
10
|
+
v-model="inputProps.field.state.value"
|
|
11
|
+
:name="inputProps.name"
|
|
12
|
+
style="border: 1px solid red"
|
|
13
|
+
@change="(e: any) => inputProps.field.handleChange(e.target.value)"
|
|
14
|
+
/>
|
|
15
|
+
</template>
|
|
16
|
+
</OmegaInput>
|
|
17
|
+
<button>submit</button>
|
|
18
|
+
</template>
|
|
19
|
+
</OmegaForm>
|
|
20
|
+
</template>
|
|
21
|
+
|
|
22
|
+
<script setup lang="ts">
|
|
23
|
+
import { S } from "effect-app"
|
|
24
|
+
import { OmegaForm, OmegaInput } from "../../components/OmegaForm"
|
|
25
|
+
|
|
26
|
+
const schema = S.Struct({ asder2: S.String })
|
|
27
|
+
const onSubmit = ({ value }: { value: { asder2: string } }) => {
|
|
28
|
+
console.log(value)
|
|
29
|
+
}
|
|
30
|
+
</script>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<OmegaForm :schema="schema" :subscribe="['values']">
|
|
3
|
+
<template #default="{ form, subscribedValues: { values } }">
|
|
4
|
+
<OmegaInput label="aString" :form="form" name="aString" />
|
|
5
|
+
<pre>{{ values }}</pre>
|
|
6
|
+
</template>
|
|
7
|
+
</OmegaForm>
|
|
8
|
+
</template>
|
|
9
|
+
|
|
10
|
+
<script setup lang="ts">
|
|
11
|
+
import { S } from "effect-app"
|
|
12
|
+
import { OmegaForm, OmegaInput } from "../../components/OmegaForm"
|
|
13
|
+
|
|
14
|
+
const schema = S.Struct({ aString: S.UndefinedOr(S.String) })
|
|
15
|
+
</script>
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<OmegaForm :form="addForm">
|
|
3
|
+
<template #default="{ form }">
|
|
4
|
+
<OmegaInput label="first" :form="form" name="first" />
|
|
5
|
+
<div>+</div>
|
|
6
|
+
<OmegaInput label="second" :form="form" name="second" />
|
|
7
|
+
</template>
|
|
8
|
+
</OmegaForm>
|
|
9
|
+
|
|
10
|
+
<!-- Technically you can do this only with a subscribe but only inside OmegaForm Context -->
|
|
11
|
+
<div>
|
|
12
|
+
<div>Sum: {{ sum }}</div>
|
|
13
|
+
</div>
|
|
14
|
+
</template>
|
|
15
|
+
|
|
16
|
+
<script setup lang="ts">
|
|
17
|
+
import { S } from "effect-app"
|
|
18
|
+
import { OmegaForm, OmegaInput, useOmegaForm } from "../../components/OmegaForm"
|
|
19
|
+
import { ref, watch } from "vue"
|
|
20
|
+
|
|
21
|
+
const sum = ref(0)
|
|
22
|
+
const AddSchema = S.Struct({
|
|
23
|
+
first: S.Number,
|
|
24
|
+
second: S.Number,
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
const addForm = useOmegaForm(AddSchema, {
|
|
28
|
+
defaultValues: {
|
|
29
|
+
first: 0,
|
|
30
|
+
second: 0,
|
|
31
|
+
},
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
const values = addForm.useStore(({ values }) => values)
|
|
35
|
+
|
|
36
|
+
watch(values, ({ first, second }) => {
|
|
37
|
+
sum.value = first + second
|
|
38
|
+
})
|
|
39
|
+
</script>
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/vue3"
|
|
2
|
+
import { OmegaForm } from "../components/OmegaForm"
|
|
3
|
+
import { provideIntl } from "../utils"
|
|
4
|
+
import { type makeIntl } from "@effect-app/vue"
|
|
5
|
+
import { ref } from "vue"
|
|
6
|
+
import SimpleForm from "./OmegaForm/SimpleForm.vue"
|
|
7
|
+
import EmailForm from "./OmegaForm/EmailForm.vue"
|
|
8
|
+
import ComplexForm from "./OmegaForm/ComplexForm.vue"
|
|
9
|
+
import SimpleFormVuetifyDefault from "./OmegaForm/SimpleFormVuetifyDefault.vue"
|
|
10
|
+
import SumExample from "./OmegaForm/SumExample.vue"
|
|
11
|
+
import PersistencyForm from "./OmegaForm/PersistencyForm.vue"
|
|
12
|
+
|
|
13
|
+
const mockIntl = {
|
|
14
|
+
locale: ref("en"),
|
|
15
|
+
trans: (id: string) => id,
|
|
16
|
+
intl: ref({ formatMessage: (msg: { id: string }) => msg.id }),
|
|
17
|
+
} as unknown as ReturnType<ReturnType<typeof makeIntl<string>>["useIntl"]>
|
|
18
|
+
|
|
19
|
+
const meta: Meta<typeof OmegaForm> = {
|
|
20
|
+
title: "Components/OmegaForm",
|
|
21
|
+
component: OmegaForm,
|
|
22
|
+
tags: ["autodocs"],
|
|
23
|
+
argTypes: {
|
|
24
|
+
schema: { control: "object" },
|
|
25
|
+
onSubmit: { action: "submitted" },
|
|
26
|
+
defaultValues: { control: "object" },
|
|
27
|
+
},
|
|
28
|
+
decorators: [
|
|
29
|
+
story => ({
|
|
30
|
+
components: { story },
|
|
31
|
+
setup() {
|
|
32
|
+
provideIntl(mockIntl)
|
|
33
|
+
return {}
|
|
34
|
+
},
|
|
35
|
+
template: "<story />",
|
|
36
|
+
}),
|
|
37
|
+
],
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export default meta
|
|
41
|
+
type Story = StoryObj<typeof meta>
|
|
42
|
+
|
|
43
|
+
export const SimpleFormStory: Story = {
|
|
44
|
+
render: () => ({
|
|
45
|
+
components: { SimpleForm },
|
|
46
|
+
template: "<SimpleForm />",
|
|
47
|
+
}),
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export const EmailFormStory: Story = {
|
|
51
|
+
render: () => ({
|
|
52
|
+
components: { EmailForm },
|
|
53
|
+
template: "<EmailForm />",
|
|
54
|
+
}),
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export const ComplexFormStory: Story = {
|
|
58
|
+
render: () => ({
|
|
59
|
+
components: { ComplexForm },
|
|
60
|
+
template: "<ComplexForm />",
|
|
61
|
+
}),
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export const SimpleFormVuetifyDefaultStory: Story = {
|
|
65
|
+
render: () => ({
|
|
66
|
+
components: { SimpleFormVuetifyDefault },
|
|
67
|
+
template: "<SimpleFormVuetifyDefault />",
|
|
68
|
+
}),
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export const SumExampleStory: Story = {
|
|
72
|
+
render: () => ({
|
|
73
|
+
components: { SumExample },
|
|
74
|
+
template: "<SumExample />",
|
|
75
|
+
}),
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export const PersistencyFormStory: Story = {
|
|
79
|
+
render: () => ({
|
|
80
|
+
components: { PersistencyForm },
|
|
81
|
+
template: "<PersistencyForm />",
|
|
82
|
+
}),
|
|
83
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Storybook Stories
|
|
2
|
+
|
|
3
|
+
This folder contains all the Storybook stories for the Vue Components package. Each story file corresponds to a component and demonstrates its various use cases and configurations.
|
|
4
|
+
|
|
5
|
+
## Structure
|
|
6
|
+
|
|
7
|
+
- Each story file is named after the component it's documenting (e.g., `OmegaForm.stories.ts`)
|
|
8
|
+
- Stories are organized by component type and functionality
|
|
9
|
+
- Each story includes proper TypeScript typing and documentation
|
|
10
|
+
|
|
11
|
+
## Running Stories
|
|
12
|
+
|
|
13
|
+
To run the Storybook:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pnpm storybook
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
This will start Storybook on port 6006, and you can view your components at http://localhost:6006.
|
|
20
|
+
|
|
21
|
+
## Adding New Stories
|
|
22
|
+
|
|
23
|
+
When adding a new story:
|
|
24
|
+
|
|
25
|
+
1. Create a new file in this folder with the naming pattern `ComponentName.stories.ts`
|
|
26
|
+
2. Import the component and any dependencies
|
|
27
|
+
3. Define the meta object with component information
|
|
28
|
+
4. Create story objects that demonstrate different use cases
|
|
29
|
+
5. Use proper TypeScript typing for all parameters
|