@effect-app/vue-components 0.2.8 → 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.
@@ -1,25 +1,91 @@
1
- import { useForm as b } from "@tanstack/vue-form";
2
- import { S as g } from "effect-app";
3
- import { generateMetaFromSchema as h } from "./vue-components.es6.js";
4
- const v = (r, e) => {
5
- if (!r) throw new Error("Schema is required");
6
- const a = g.standardSchemaV1(r), { filterItems: t, meta: d } = h(r), u = b({
1
+ import { useForm as E } from "@tanstack/vue-form";
2
+ import { S as j, Match as n } from "effect-app";
3
+ import { generateMetaFromSchema as A } from "./vue-components.es6.js";
4
+ import { computed as b, onUnmounted as I, onMounted as K, onBeforeUnmount as M } from "vue";
5
+ import { constVoid as x } from "./vue-components.es15.js";
6
+ const m = (i, e, u) => {
7
+ if (!i) throw new Error("Schema is required");
8
+ const a = j.standardSchemaV1(i), { filterItems: f, meta: y } = A(i), o = b(() => {
9
+ var d;
10
+ if ((d = u == null ? void 0 : u.persistency) != null && d.id)
11
+ return u.persistency.id;
12
+ const l = window.location.pathname, r = Object.keys(y);
13
+ return `${l}-${r.join("-")}`;
14
+ }), w = b(() => {
15
+ var r;
16
+ if (e != null && e.defaultValues && !((r = u == null ? void 0 : u.persistency) != null && r.overrideDefaultValues))
17
+ return e.defaultValues;
18
+ const l = u == null ? void 0 : u.persistency;
19
+ return n.value(l).pipe(
20
+ n.when(
21
+ { method: (d) => ["local", "session"].includes(d) },
22
+ (d) => {
23
+ const c = d.method === "local" ? localStorage : sessionStorage;
24
+ if (c)
25
+ try {
26
+ const s = JSON.parse(
27
+ c.getItem(o.value) || "{}"
28
+ );
29
+ return c.removeItem(o.value), s;
30
+ } catch (s) {
31
+ return console.error(s), {};
32
+ }
33
+ return {};
34
+ }
35
+ ),
36
+ n.orElse(() => ({}))
37
+ );
38
+ }), h = E({
7
39
  ...e,
8
40
  validators: {
9
41
  onSubmit: a,
10
42
  ...(e == null ? void 0 : e.validators) || {}
11
43
  },
12
- onSubmit: e != null && e.onSubmit ? ({ formApi: S, meta: c, value: f }) => {
13
- var m;
14
- return (m = e.onSubmit) == null ? void 0 : m.call(e, {
15
- formApi: S,
16
- meta: c,
17
- value: f
44
+ onSubmit: e != null && e.onSubmit ? ({ formApi: l, meta: r, value: d }) => {
45
+ var t;
46
+ return (t = e.onSubmit) == null ? void 0 : t.call(e, {
47
+ formApi: l,
48
+ meta: r,
49
+ value: d
18
50
  });
19
- } : void 0
20
- });
21
- return Object.assign(u, { meta: d, filterItems: t });
51
+ } : void 0,
52
+ defaultValues: w.value
53
+ }), V = Object.assign(h, { meta: y, filterItems: f, clear: () => {
54
+ Object.keys(y).forEach((l) => {
55
+ h.setFieldValue(l, void 0);
56
+ });
57
+ } }), v = () => {
58
+ const l = u == null ? void 0 : u.persistency;
59
+ n.value(l).pipe(
60
+ n.when(
61
+ { method: (r) => ["local", "session"].includes(r) },
62
+ (r) => {
63
+ const t = r.method === "local" ? localStorage : sessionStorage;
64
+ if (t)
65
+ return Array.isArray(r.keys) && Object.keys(y).filter(
66
+ (s) => {
67
+ var S;
68
+ return !((S = r.keys) != null && S.includes(s));
69
+ }
70
+ ).forEach((s) => {
71
+ h.setFieldValue(s, void 0);
72
+ }), Array.isArray(r.banKeys) && r.banKeys.forEach((c) => {
73
+ h.setFieldValue(c, void 0);
74
+ }), t.setItem(
75
+ o.value,
76
+ JSON.stringify(h.store.state.values)
77
+ );
78
+ }
79
+ ),
80
+ n.orElse(x)
81
+ );
82
+ };
83
+ return I(v), K(() => {
84
+ window.addEventListener("beforeunload", v);
85
+ }), M(() => {
86
+ window.removeEventListener("beforeunload", v);
87
+ }), V;
22
88
  };
23
89
  export {
24
- v as useOmegaForm
90
+ m as useOmegaForm
25
91
  };
@@ -1,7 +1,7 @@
1
- (function(){"use strict";try{if(typeof document<"u"){var n=document.createElement("style");if(n.appendChild(document.createTextNode("fieldset[data-v-59131cd0]{display:contents}")),document.head.appendChild(n),window.customElements){const e=window.customElements.define;window.customElements.define=function(c,t){const d=t.prototype.connectedCallback;return t.prototype.connectedCallback=function(){if(d&&d.call(this),this.shadowRoot){const o=document.createElement("style");o.appendChild(document.createTextNode("fieldset[data-v-59131cd0]{display:contents}")),this.shadowRoot.appendChild(o)}},e.call(window.customElements,c,t)}}}}catch(e){console.error("vite-plugin-css-injected-by-js",e)}})();
1
+ (function(){"use strict";try{if(typeof document<"u"){var n=document.createElement("style");if(n.appendChild(document.createTextNode("fieldset[data-v-fd152523]{display:contents}")),document.head.appendChild(n),window.customElements){const e=window.customElements.define;window.customElements.define=function(i,t){const d=t.prototype.connectedCallback;return t.prototype.connectedCallback=function(){if(d&&d.call(this),this.shadowRoot){const o=document.createElement("style");o.appendChild(document.createTextNode("fieldset[data-v-fd152523]{display:contents}")),this.shadowRoot.appendChild(o)}},e.call(window.customElements,i,t)}}}}catch(e){console.error("vite-plugin-css-injected-by-js",e)}})();
2
2
  import o from "./vue-components.es11.js";
3
3
  import m from "./vue-components.es12.js";
4
- const a = /* @__PURE__ */ m(o, [["__scopeId", "data-v-59131cd0"]]);
4
+ const a = /* @__PURE__ */ m(o, [["__scopeId", "data-v-fd152523"]]);
5
5
  export {
6
6
  a as default
7
7
  };
@@ -1,6 +1,6 @@
1
1
  import { defineComponent as i, computed as t, createBlock as p, openBlock as d, resolveDynamicComponent as u, withCtx as r, createVNode as f, mergeProps as c, renderSlot as v, normalizeProps as h, guardReactiveProps as g } from "vue";
2
2
  import { generateInputStandardSchemaFromFieldMeta as y } from "./vue-components.es6.js";
3
- import b from "./vue-components.es15.js";
3
+ import b from "./vue-components.es16.js";
4
4
  const S = /* @__PURE__ */ i({
5
5
  inheritAttrs: !1,
6
6
  __name: "OmegaInput",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@effect-app/vue-components",
3
- "version": "0.2.8",
3
+ "version": "0.3.0",
4
4
  "peerDependencies": {
5
5
  "@mdi/js": "^7.4.47",
6
6
  "@tanstack/vue-form": "^1.2.4",
@@ -14,6 +14,12 @@
14
14
  "vuetify": "^3.7.19"
15
15
  },
16
16
  "devDependencies": {
17
+ "@storybook/addon-essentials": "^8.6.12",
18
+ "@storybook/addon-interactions": "^8.6.12",
19
+ "@storybook/blocks": "^8.6.12",
20
+ "@storybook/testing-library": "^0.2.2",
21
+ "@storybook/vue3": "^8.6.12",
22
+ "@storybook/vue3-vite": "^8.6.12",
17
23
  "@types/node": "^22.13.14",
18
24
  "@typescript-eslint/eslint-plugin": "8.29.0",
19
25
  "@typescript-eslint/parser": "8.29.0",
@@ -24,6 +30,7 @@
24
30
  "eslint-plugin-vue": "^10.0.0",
25
31
  "rimraf": "^6.0.1",
26
32
  "sass": "^1.86.0",
33
+ "storybook": "^8.6.12",
27
34
  "typescript": "^5.8.2",
28
35
  "vite": "^6.2.3",
29
36
  "vite-plugin-css-injected-by-js": "^3.5.2",
@@ -52,6 +59,8 @@
52
59
  "docs:build": "vitepress build docs",
53
60
  "docs:serve": "vitepress serve docs",
54
61
  "lint": "NODE_OPTIONS=--max-old-space-size=8192 eslint src",
55
- "autofix": "pnpm lint --fix"
62
+ "autofix": "pnpm lint --fix",
63
+ "storybook": "storybook dev -p 6006",
64
+ "build-storybook": "storybook build"
56
65
  }
57
66
  }
@@ -106,6 +106,12 @@ const setRealDirty = () => {
106
106
  realDirty.value = true
107
107
  }
108
108
 
109
+ onMounted(() => {
110
+ if (fieldValue.value) {
111
+ setRealDirty()
112
+ }
113
+ })
114
+
109
115
  const showedErrors = computed(() => {
110
116
  // single select field can be validated on change
111
117
  if (!realDirty.value && fieldType.value !== "select") return []
@@ -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,26 +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,
57
56
  type ShowErrorsOn,
58
57
  } from "./OmegaFormStuff"
59
58
  import { getOmegaStore } from "./getOmegaStore"
60
59
  import { provideOmegaErrors } from "./OmegaErrorsContext"
61
- import { useOmegaForm } from "./useOmegaForm"
60
+ import {
61
+ type OmegaConfig,
62
+ type OmegaFormReturn,
63
+ useOmegaForm,
64
+ } from "./useOmegaForm"
62
65
  import { watch } from "vue"
63
66
 
64
67
  const props = defineProps<
65
68
  {
69
+ omegaConfig?: OmegaConfig<From>
66
70
  subscribe?: K[]
67
71
  showErrorsOn?: ShowErrorsOn
68
72
  } & (
69
73
  | {
70
- form: OmegaFormApi<To, From> & {
71
- meta: MetaRecord<To>
72
- filterItems?: FilterItems
73
- }
74
+ form: OmegaFormReturn<To, From>
74
75
  schema?: undefined
75
76
  }
76
77
  | (FormProps<To, From> & {
@@ -80,7 +81,8 @@ const props = defineProps<
80
81
  )
81
82
  >()
82
83
 
83
- const form = props.form ?? useOmegaForm<From, To>(props.schema, props)
84
+ const form =
85
+ props.form ?? useOmegaForm<From, To>(props.schema, props, props.omegaConfig)
84
86
 
85
87
  const formIsSubmitting = useStore(form.store, state => state.isSubmitting)
86
88
 
@@ -10,7 +10,7 @@ export function getOmegaStore<
10
10
  form: OmegaFormApi<To, From>,
11
11
  subscribe?: K[],
12
12
  ): Ref<
13
- K[] extends undefined
13
+ K[] extends undefined[]
14
14
  ? Record<string, never>
15
15
  : Pick<OmegaFormState<To, From>, K>
16
16
  > {
@@ -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
- options?: NoInfer<FormProps<To, From>>,
24
- ): OmegaFormApi<To, From> & {
25
- meta: MetaRecord<To>
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
- ...options,
112
+ ...tanstackFormOptions,
46
113
  validators: {
47
114
  onSubmit: standardSchema,
48
- ...(options?.validators || {}),
115
+ ...(tanstackFormOptions?.validators || {}),
49
116
  },
50
- onSubmit: options?.onSubmit
117
+ onSubmit: tanstackFormOptions?.onSubmit
51
118
  ? ({ formApi, meta, value }) =>
52
- options.onSubmit?.({
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 exposed = Object.assign(form, { meta, filterItems })
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>