@effect-app/vue-components 0.2.8 → 0.3.1

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,14 +1,13 @@
1
1
  import { type S } from "effect-app";
2
- import { type FilterItems, type MetaRecord, type OmegaFormApi, type OmegaFormState, type ShowErrorsOn } from "./OmegaFormStuff";
2
+ import { type OmegaFormState, type ShowErrorsOn } from "./OmegaFormStuff";
3
+ import { type OmegaConfig, type OmegaFormReturn } from "./useOmegaForm";
3
4
  declare const _default: <From extends Record<PropertyKey, any>, To extends Record<PropertyKey, any>, K extends keyof OmegaFormState<To, From> = keyof OmegaFormState<To, From>>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_expose?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
4
5
  props: __VLS_PrettifyLocal<Pick<Partial<{}> & Omit<{} & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, never>, never> & ({
6
+ omegaConfig?: OmegaConfig<From>;
5
7
  subscribe?: K[];
6
8
  showErrorsOn?: ShowErrorsOn;
7
9
  } & ({
8
- form: OmegaFormApi<To, From> & {
9
- meta: MetaRecord<To>;
10
- filterItems?: FilterItems;
11
- };
10
+ form: OmegaFormReturn<To, From>;
12
11
  schema?: undefined;
13
12
  } | (Omit<import("@tanstack/vue-form").FormOptions<To, import("@tanstack/vue-form").FormValidateOrFn<To> | undefined, import("@tanstack/vue-form").FormValidateOrFn<To> | undefined, import("@tanstack/vue-form").StandardSchemaV1<To, From>, import("@tanstack/vue-form").FormValidateOrFn<To> | undefined, import("@tanstack/vue-form").FormAsyncValidateOrFn<To> | undefined, import("@tanstack/vue-form").FormValidateOrFn<To> | undefined, import("@tanstack/vue-form").FormAsyncValidateOrFn<To> | undefined, import("@tanstack/vue-form").FormAsyncValidateOrFn<To> | undefined, import("@tanstack/vue-form").FormAsyncValidateOrFn<To> | undefined>, "onSubmit"> & {
14
13
  onSubmit?: ((props: {
@@ -20,18 +19,12 @@ declare const _default: <From extends Record<PropertyKey, any>, To extends Recor
20
19
  form?: undefined;
21
20
  schema: S.Schema<From, To, never>;
22
21
  }))) & Partial<{}>> & import("vue").PublicProps;
23
- expose(exposed: import("vue").ShallowUnwrapRef<import("./OmegaFormStuff").OmegaFormParams<To, From> & import("@tanstack/vue-form").VueFormApi<To, import("@tanstack/vue-form").FormValidateOrFn<To> | undefined, import("@tanstack/vue-form").FormValidateOrFn<To> | undefined, import("@tanstack/vue-form").StandardSchemaV1<To, From>, import("@tanstack/vue-form").FormValidateOrFn<To> | undefined, import("@tanstack/vue-form").FormAsyncValidateOrFn<To> | undefined, import("@tanstack/vue-form").FormValidateOrFn<To> | undefined, import("@tanstack/vue-form").FormAsyncValidateOrFn<To> | undefined, import("@tanstack/vue-form").FormAsyncValidateOrFn<To> | undefined, import("@tanstack/vue-form").FormAsyncValidateOrFn<To> | undefined> & {
24
- meta: MetaRecord<To>;
25
- filterItems?: FilterItems;
26
- }>): void;
22
+ expose(exposed: import("vue").ShallowUnwrapRef<OmegaFormReturn<To, From>>): void;
27
23
  attrs: any;
28
24
  slots: {
29
25
  default?: ((props: {
30
- form: import("./OmegaFormStuff").OmegaFormParams<To, From> & import("@tanstack/vue-form").VueFormApi<To, import("@tanstack/vue-form").FormValidateOrFn<To> | undefined, import("@tanstack/vue-form").FormValidateOrFn<To> | undefined, import("@tanstack/vue-form").StandardSchemaV1<To, From>, import("@tanstack/vue-form").FormValidateOrFn<To> | undefined, import("@tanstack/vue-form").FormAsyncValidateOrFn<To> | undefined, import("@tanstack/vue-form").FormValidateOrFn<To> | undefined, import("@tanstack/vue-form").FormAsyncValidateOrFn<To> | undefined, import("@tanstack/vue-form").FormAsyncValidateOrFn<To> | undefined, import("@tanstack/vue-form").FormAsyncValidateOrFn<To> | undefined> & {
31
- meta: MetaRecord<To>;
32
- filterItems?: FilterItems;
33
- };
34
- subscribedValues: Pick<OmegaFormState<To, From>, K>;
26
+ form: OmegaFormReturn<To, From>;
27
+ subscribedValues: K[] extends undefined[] ? Record<string, never> : Pick<OmegaFormState<To, From>, K>;
35
28
  }) => any) | undefined;
36
29
  };
37
30
  emit: {};
@@ -1,3 +1,3 @@
1
1
  import { type Ref } from "vue";
2
2
  import type { OmegaFormState, OmegaFormApi } from "./OmegaFormStuff";
3
- export declare function getOmegaStore<To, From, K extends keyof OmegaFormState<To, From> = keyof OmegaFormState<To, From>>(form: OmegaFormApi<To, From>, subscribe?: K[]): Ref<K[] extends undefined ? Record<string, never> : Pick<OmegaFormState<To, From>, K>>;
3
+ export declare function getOmegaStore<To, From, K extends keyof OmegaFormState<To, From> = keyof OmegaFormState<To, From>>(form: OmegaFormApi<To, From>, subscribe?: K[]): Ref<K[] extends undefined[] ? Record<string, never> : Pick<OmegaFormState<To, From>, K>>;
@@ -7,14 +7,13 @@ export { useOmegaForm } from "./useOmegaForm";
7
7
  export { default } from "./OmegaWrapper.vue";
8
8
  export { OmegaForm, OmegaInput, OmegaErrors };
9
9
  declare const OmegaFormCE: new <From extends Record<PropertyKey, any>, To extends Record<PropertyKey, any>, K extends keyof import("./OmegaFormStuff").OmegaFormState<To, From> = keyof import("./OmegaFormStuff").OmegaFormState<To, From>>(initialProps?: Record<string, any>) => import("vue").VueElement & ((({
10
+ omegaConfig?: import("./useOmegaForm").OmegaConfig<From> | undefined;
10
11
  subscribe?: K[] | undefined;
11
12
  showErrorsOn?: import("./OmegaFormStuff").ShowErrorsOn | undefined;
12
- form: import("./OmegaFormStuff").OmegaFormApi<To, From> & {
13
- meta: import("./OmegaFormStuff").MetaRecord<To>;
14
- filterItems?: import("./OmegaFormStuff").FilterItems;
15
- };
13
+ form: import("./useOmegaForm").OmegaFormReturn<To, From>;
16
14
  schema?: undefined | undefined;
17
15
  } | {
16
+ omegaConfig?: import("./useOmegaForm").OmegaConfig<From> | undefined;
18
17
  subscribe?: K[] | undefined;
19
18
  showErrorsOn?: import("./OmegaFormStuff").ShowErrorsOn | undefined;
20
19
  defaultValues?: To | undefined;
@@ -1,6 +1,27 @@
1
1
  import { S } from "effect-app";
2
- import { type FilterItems, type FormProps, type MetaRecord, type OmegaFormApi } from "./OmegaFormStuff";
3
- export declare const useOmegaForm: <From extends Record<PropertyKey, any>, To extends Record<PropertyKey, any>>(schema: S.Schema<From, To, never>, options?: NoInfer<FormProps<To, From>>) => OmegaFormApi<To, From> & {
2
+ import { type NestedKeyOf, type FilterItems, type FormProps, type MetaRecord, type OmegaFormApi } from "./OmegaFormStuff";
3
+ type keysRule<T> = {
4
+ keys?: NestedKeyOf<T>[];
5
+ banKeys?: "You should only use one of banKeys or keys, not both, moron";
6
+ } | {
7
+ keys?: "You should only use one of banKeys or keys, not both, moron";
8
+ banKeys?: NestedKeyOf<T>[];
9
+ };
10
+ export type OmegaConfig<T> = {
11
+ persistency?: {
12
+ /** Order of importance:
13
+ * - "querystring": Highest priority when persisting
14
+ * - "local" and then "session": Lower priority storage options
15
+ */
16
+ policies?: ("local" | "session" | "querystring")[];
17
+ overrideDefaultValues?: boolean;
18
+ id?: string;
19
+ } & keysRule<T>;
20
+ };
21
+ export interface OmegaFormReturn<To, From> extends OmegaFormApi<To, From> {
4
22
  meta: MetaRecord<To>;
5
23
  filterItems?: FilterItems;
6
- };
24
+ clear: () => void;
25
+ }
26
+ export declare const useOmegaForm: <From extends Record<PropertyKey, any>, To extends Record<PropertyKey, any>>(schema: S.Schema<From, To, never>, tanstackFormOptions?: NoInfer<FormProps<To, From>>, omegaConfig?: OmegaConfig<From>) => OmegaFormReturn<To, From>;
27
+ export {};
@@ -1,4 +1,4 @@
1
- import { defineComponent as d, watch as b, createElementBlock as S, openBlock as g, withModifiers as v, unref as n, createElementVNode as h, renderSlot as O } from "vue";
1
+ import { defineComponent as d, watch as b, createElementBlock as g, openBlock as S, withModifiers as v, unref as n, createElementVNode as h, renderSlot as O } from "vue";
2
2
  import { useStore as i } from "@tanstack/vue-form";
3
3
  import { getOmegaStore as M } from "./vue-components.es16.js";
4
4
  import { provideOmegaErrors as y } from "./vue-components.es4.js";
@@ -6,6 +6,7 @@ import { useOmegaForm as E } from "./vue-components.es5.js";
6
6
  const w = ["disabled"], A = /* @__PURE__ */ d({
7
7
  __name: "OmegaWrapper",
8
8
  props: {
9
+ omegaConfig: {},
9
10
  subscribe: {},
10
11
  showErrorsOn: {},
11
12
  form: {},
@@ -21,35 +22,36 @@ const w = ["disabled"], A = /* @__PURE__ */ d({
21
22
  onSubmit: { type: Function }
22
23
  },
23
24
  setup(u, { expose: l }) {
24
- const s = u, t = s.form ?? E(s.schema, s), f = i(t.store, (e) => e.isSubmitting);
25
+ const r = u, t = r.form ?? E(r.schema, r, r.omegaConfig), f = i(t.store, (e) => e.isSubmitting);
25
26
  l(t);
26
27
  const c = M(
27
28
  t,
28
- s.subscribe
29
+ r.subscribe
29
30
  ), p = i(
30
31
  t.store,
31
32
  (e) => e.submissionAttempts
32
- ), o = t.useStore((e) => e.errors);
33
+ ), s = t.useStore((e) => e.errors);
33
34
  return b(
34
- () => [t.filterItems, o.value],
35
+ () => [t.filterItems, s.value],
35
36
  () => {
36
37
  const e = t.filterItems;
37
- return e ? o.value ? (Object.values(o.value).filter(
38
- (r) => !!r
38
+ return e ? s.value ? (Object.values(s.value).filter(
39
+ (o) => !!o
39
40
  ).flatMap(
40
- (r) => Object.values(r).flat().map((m) => m.message)
41
- ).some((r) => r === e.message) && e.items.forEach((r) => {
42
- const m = t.getFieldMeta(r);
43
- t.setFieldMeta(r, {
41
+ (o) => Object.values(o).flat().map((m) => m.message)
42
+ ).some((o) => o === e.message) && e.items.forEach((o) => {
43
+ const m = t.getFieldMeta(o);
44
+ t.setFieldMeta(o, {
44
45
  ...m,
45
46
  errorMap: {
46
- onSubmit: [{ path: [r], message: e.message }]
47
+ onSubmit: [{ path: [o], message: e.message }]
47
48
  }
48
49
  });
49
50
  }), {}) : {} : {};
50
51
  }
51
- ), y(p, o, s.showErrorsOn), (e, a) => (g(), S("form", {
52
- onSubmit: a[0] || (a[0] = v((r) => n(t).handleSubmit(), ["prevent", "stop"]))
52
+ ), y(p, s, r.showErrorsOn), (e, a) => (S(), g("form", {
53
+ novalidate: "",
54
+ onSubmit: a[0] || (a[0] = v((o) => n(t).handleSubmit(), ["prevent", "stop"]))
53
55
  }, [
54
56
  h("fieldset", { disabled: n(f) }, [
55
57
  O(e.$slots, "default", {
@@ -1,10 +1,10 @@
1
1
  (function(){"use strict";try{if(typeof document<"u"){var n=document.createElement("style");if(n.appendChild(document.createTextNode(".omega-input .v-input__details:has(.v-messages:empty){grid-template-rows:0fr;transition:all .2s}.omega-input .v-messages:empty{min-height:0}.omega-input .v-input__details:has(.v-messages){transition:all .2s;overflow:hidden;min-height:0;display:grid;grid-template-rows:1fr}.omega-input .v-messages{transition:all .2s}.omega-input .v-messages>*{transition-duration:0s!important}.omega-input [role=alert]:has(.v-messages:empty){padding:0}.omega-input .v-btn{cursor:pointer;width:auto;-webkit-appearance:none;-moz-appearance:none;appearance:none;box-shadow:none;display:block;min-width:auto;height:auto;padding:.5em .5em .5em 1em}")),document.head.appendChild(n),window.customElements){const e=window.customElements.define;window.customElements.define=function(o,t){const a=t.prototype.connectedCallback;return t.prototype.connectedCallback=function(){if(a&&a.call(this),this.shadowRoot){const i=document.createElement("style");i.appendChild(document.createTextNode(".omega-input .v-input__details:has(.v-messages:empty){grid-template-rows:0fr;transition:all .2s}.omega-input .v-messages:empty{min-height:0}.omega-input .v-input__details:has(.v-messages){transition:all .2s;overflow:hidden;min-height:0;display:grid;grid-template-rows:1fr}.omega-input .v-messages{transition:all .2s}.omega-input .v-messages>*{transition-duration:0s!important}.omega-input [role=alert]:has(.v-messages:empty){padding:0}.omega-input .v-btn{cursor:pointer;width:auto;-webkit-appearance:none;-moz-appearance:none;appearance:none;box-shadow:none;display:block;min-width:auto;height:auto;padding:.5em .5em .5em 1em}")),this.shadowRoot.appendChild(i)}},e.call(window.customElements,o,t)}}}}catch(e){console.error("vite-plugin-css-injected-by-js",e)}})();
2
- import { defineComponent as k, getCurrentInstance as B, useId as L, computed as n, watch as x, onMounted as P, ref as S, watchEffect as A, renderSlot as D, normalizeProps as F, guardReactiveProps as M, createElementVNode as N, createBlock as R, createCommentVNode as T, unref as U, openBlock as z, mergeProps as j } from "vue";
2
+ import { defineComponent as B, getCurrentInstance as L, useId as P, computed as n, watch as x, onMounted as _, ref as S, watchEffect as A, renderSlot as D, normalizeProps as F, guardReactiveProps as M, createElementVNode as N, createBlock as R, createCommentVNode as T, unref as U, openBlock as z, mergeProps as j } from "vue";
3
3
  import { useStore as G } from "@tanstack/vue-form";
4
4
  import { useOmegaErrors as H } from "./vue-components.es4.js";
5
5
  import J from "./vue-components.es18.js";
6
6
 
7
- const Z = /* @__PURE__ */ k({
7
+ const Z = /* @__PURE__ */ B({
8
8
  inheritAttrs: !1,
9
9
  __name: "OmegaInternalInput",
10
10
  props: {
@@ -15,49 +15,53 @@ const Z = /* @__PURE__ */ k({
15
15
  type: {},
16
16
  validators: {}
17
17
  },
18
- setup(_) {
19
- const e = _, l = B(), q = l == null ? void 0 : l.appContext.components.VTextField, o = L(), u = e.field, a = G(u.store, (t) => t), p = n(() => {
18
+ setup(q) {
19
+ const e = q, o = L(), C = o == null ? void 0 : o.appContext.components.VTextField, u = P(), s = e.field, a = G(s.store, (t) => t), f = n(() => {
20
20
  var t, r;
21
21
  return e.type ? e.type : ((t = e.meta) == null ? void 0 : t.type) === "string" ? e.meta.format === "email" ? "email" : "string" : ((r = e.meta) == null ? void 0 : r.type) || "unknown";
22
- }), s = n(() => a.value.value), d = n(
22
+ }), l = n(() => a.value.value), d = n(
23
23
  () => (
24
24
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
25
25
  a.value.meta.errors.map((t) => t == null ? void 0 : t.message).filter(Boolean)
26
26
  )
27
27
  );
28
28
  x(
29
- () => [!!s.value],
29
+ () => [!!l.value],
30
30
  () => {
31
31
  var t, r;
32
- d.value.length && !s.value && !((t = e.meta) != null && t.required) && u.setValue(
32
+ d.value.length && !l.value && !((t = e.meta) != null && t.required) && s.setValue(
33
33
  ((r = e.meta) == null ? void 0 : r.nullableOrUndefined) === "undefined" ? void 0 : null
34
34
  );
35
35
  }
36
- ), P(() => {
36
+ ), _(() => {
37
37
  var t, r;
38
- !s.value && !((t = e.meta) != null && t.required) && ((r = e.meta) == null ? void 0 : r.nullableOrUndefined) === "null" && u.setValue(null);
38
+ !l.value && !((t = e.meta) != null && t.required) && ((r = e.meta) == null ? void 0 : r.nullableOrUndefined) === "null" && s.setValue(null);
39
39
  });
40
- const { addError: C, removeError: I, showErrors: O, showErrorsOn: $ } = H(), i = S(!1);
40
+ const { addError: I, removeError: O, showErrors: $, showErrorsOn: k } = H(), i = S(!1);
41
41
  A(() => {
42
- (O.value || $ === "onChange") && (i.value = !0);
42
+ ($.value || k === "onChange") && (i.value = !0);
43
43
  });
44
- const f = () => {
44
+ const m = () => {
45
45
  i.value = !0;
46
- }, v = n(() => !i.value && p.value !== "select" ? [] : d.value);
46
+ };
47
+ _(() => {
48
+ l.value && m();
49
+ });
50
+ const v = n(() => !i.value && f.value !== "select" ? [] : d.value);
47
51
  x(
48
52
  () => a.value.meta.errors,
49
53
  () => {
50
- a.value.meta.errors.length ? C({
51
- inputId: o,
54
+ a.value.meta.errors.length ? I({
55
+ inputId: u,
52
56
  errors: a.value.meta.errors.map((t) => t.message).filter(Boolean),
53
57
  label: e.label
54
- }) : I(o);
58
+ }) : O(u);
55
59
  }
56
60
  );
57
- const m = n(() => {
61
+ const p = n(() => {
58
62
  var t, r, c, g, y, h, b, E, V, w;
59
63
  return {
60
- id: o,
64
+ id: u,
61
65
  required: (t = e.meta) == null ? void 0 : t.required,
62
66
  minLength: ((r = e.meta) == null ? void 0 : r.type) === "string" && ((c = e.meta) == null ? void 0 : c.minLength),
63
67
  maxLength: ((g = e.meta) == null ? void 0 : g.type) === "string" && ((y = e.meta) == null ? void 0 : y.maxLength),
@@ -68,19 +72,19 @@ const Z = /* @__PURE__ */ k({
68
72
  errorMessages: v.value,
69
73
  error: !!v.value.length,
70
74
  field: e.field,
71
- setRealDirty: f,
72
- type: p.value,
75
+ setRealDirty: m,
76
+ type: f.value,
73
77
  label: `${e.label}${(w = e.meta) != null && w.required ? " *" : ""}`,
74
78
  options: e.options
75
79
  };
76
80
  });
77
- return (t, r) => D(t.$slots, "default", F(M(m.value)), () => [
78
- N("div", { onFocusout: f }, [
79
- U(q) ? (z(), R(J, j({
81
+ return (t, r) => D(t.$slots, "default", F(M(p.value)), () => [
82
+ N("div", { onFocusout: m }, [
83
+ U(C) ? (z(), R(J, j({
80
84
  key: 0,
81
- "input-props": m.value
85
+ "input-props": p.value
82
86
  }, t.$attrs, {
83
- "vuetify-value": m.value.field.state.value
87
+ "vuetify-value": p.value.field.state.value
84
88
  }), null, 16, ["input-props", "vuetify-value"])) : T("", !0)
85
89
  ], 32)
86
90
  ]);
@@ -1,25 +1,110 @@
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({
7
- ...e,
1
+ import { useForm as p } from "@tanstack/vue-form";
2
+ import { S as E } from "effect-app";
3
+ import { generateMetaFromSchema as P } from "./vue-components.es6.js";
4
+ import { computed as y, onUnmounted as I, onMounted as N, onBeforeUnmount as R } from "vue";
5
+ const K = (a, t, u) => {
6
+ if (!a) throw new Error("Schema is required");
7
+ const b = E.standardSchemaV1(a), { filterItems: f, meta: c } = P(a), i = y(() => {
8
+ var r;
9
+ if ((r = u == null ? void 0 : u.persistency) != null && r.id)
10
+ return u.persistency.id;
11
+ const e = window.location.pathname, l = Object.keys(c);
12
+ return `${e}-${l.join("-")}`;
13
+ }), L = () => {
14
+ const e = new URLSearchParams(window.location.search);
15
+ e.delete(i.value);
16
+ const l = new URL(window.location.href);
17
+ l.search = e.toString(), window.history.replaceState({}, "", l.toString());
18
+ }, U = y(() => {
19
+ var l;
20
+ if (t != null && t.defaultValues && !((l = u == null ? void 0 : u.persistency) != null && l.overrideDefaultValues))
21
+ return t == null ? void 0 : t.defaultValues;
22
+ const e = u == null ? void 0 : u.persistency;
23
+ if (!(e != null && e.policies) || e.policies.length === 0) return {};
24
+ if (e.policies.includes("querystring"))
25
+ try {
26
+ const s = new URLSearchParams(window.location.search).get(i.value);
27
+ if (L(), s)
28
+ return JSON.parse(s);
29
+ } catch (r) {
30
+ console.error(r);
31
+ }
32
+ if (e.policies.includes("local") || e.policies.includes("session")) {
33
+ const r = e.policies.includes("local") ? localStorage : sessionStorage;
34
+ if (r)
35
+ try {
36
+ const s = JSON.parse(
37
+ r.getItem(i.value) || "{}"
38
+ );
39
+ return r.removeItem(i.value), s;
40
+ } catch (s) {
41
+ console.error(s);
42
+ }
43
+ }
44
+ return {};
45
+ }), n = p({
46
+ ...t,
8
47
  validators: {
9
- onSubmit: a,
10
- ...(e == null ? void 0 : e.validators) || {}
48
+ onSubmit: b,
49
+ ...(t == null ? void 0 : t.validators) || {}
11
50
  },
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
51
+ onSubmit: t != null && t.onSubmit ? ({ formApi: e, meta: l, value: r }) => {
52
+ var s;
53
+ return (s = t.onSubmit) == null ? void 0 : s.call(t, {
54
+ formApi: e,
55
+ meta: l,
56
+ value: r
18
57
  });
19
- } : void 0
20
- });
21
- return Object.assign(u, { meta: d, filterItems: t });
58
+ } : void 0,
59
+ defaultValues: U.value
60
+ }), V = () => {
61
+ Object.keys(c).forEach((e) => {
62
+ n.setFieldValue(e, void 0);
63
+ });
64
+ }, S = (e) => e.reduce(
65
+ (l, r) => {
66
+ const s = r.split(".");
67
+ return s.reduce((d, o, j) => (j === s.length - 1 ? d[o] = n.getFieldValue(r) : d[o] = d[o] || {}, d[o]), l);
68
+ },
69
+ {}
70
+ ), h = (e) => {
71
+ if (e) {
72
+ if (Array.isArray(e.keys))
73
+ return S(e.keys);
74
+ if (Array.isArray(e.banKeys)) {
75
+ const l = Object.keys(c).filter(
76
+ (r) => {
77
+ var s;
78
+ return (s = e.banKeys) == null ? void 0 : s.includes(r);
79
+ }
80
+ );
81
+ return S(l);
82
+ }
83
+ return n.store.state.values;
84
+ }
85
+ }, w = () => {
86
+ const e = u == null ? void 0 : u.persistency;
87
+ if (!(!(e != null && e.policies) || e.policies.length === 0) && (e.policies.includes("local") || e.policies.includes("session"))) {
88
+ const l = e.policies.includes("local") ? localStorage : sessionStorage;
89
+ if (!l) return;
90
+ const r = h(e);
91
+ return l.setItem(i.value, JSON.stringify(r));
92
+ }
93
+ }, v = () => {
94
+ const e = u == null ? void 0 : u.persistency;
95
+ if (!(!(e != null && e.policies) || e.policies.length === 0) && e.policies.includes("querystring")) {
96
+ const l = h(e), r = new URLSearchParams(window.location.search);
97
+ r.set(i.value, JSON.stringify(l));
98
+ const s = new URL(window.location.href);
99
+ s.search = r.toString(), window.history.replaceState({}, "", s.toString());
100
+ }
101
+ };
102
+ return I(w), N(() => {
103
+ window.addEventListener("beforeunload", w), window.addEventListener("blur", v);
104
+ }), R(() => {
105
+ window.removeEventListener("beforeunload", w), window.removeEventListener("blur", v);
106
+ }), Object.assign(n, { meta: c, filterItems: f, clear: V });
22
107
  };
23
108
  export {
24
- v as useOmegaForm
109
+ K as useOmegaForm
25
110
  };
@@ -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
  };
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.1",
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,3 +1,4 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
1
2
  import {
2
3
  useForm,
3
4
  type FormValidateOrFn,
@@ -7,11 +8,41 @@ import {
7
8
  import { 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
+
19
+ type keysRule<T> =
20
+ | {
21
+ keys?: NestedKeyOf<T>[]
22
+ banKeys?: "You should only use one of banKeys or keys, not both, moron"
23
+ }
24
+ | {
25
+ keys?: "You should only use one of banKeys or keys, not both, moron"
26
+ banKeys?: NestedKeyOf<T>[]
27
+ }
28
+
29
+ export type OmegaConfig<T> = {
30
+ persistency?: {
31
+ /** Order of importance:
32
+ * - "querystring": Highest priority when persisting
33
+ * - "local" and then "session": Lower priority storage options
34
+ */
35
+ policies?: ("local" | "session" | "querystring")[]
36
+ overrideDefaultValues?: boolean
37
+ id?: string
38
+ } & keysRule<T>
39
+ }
40
+
41
+ export interface OmegaFormReturn<To, From> extends OmegaFormApi<To, From> {
42
+ meta: MetaRecord<To>
43
+ filterItems?: FilterItems
44
+ clear: () => void
45
+ }
15
46
 
16
47
  export const useOmegaForm = <
17
48
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -20,16 +51,75 @@ export const useOmegaForm = <
20
51
  To extends Record<PropertyKey, any>,
21
52
  >(
22
53
  schema: S.Schema<From, To, never>,
23
- options?: NoInfer<FormProps<To, From>>,
24
- ): OmegaFormApi<To, From> & {
25
- meta: MetaRecord<To>
26
- filterItems?: FilterItems
27
- } => {
54
+ tanstackFormOptions?: NoInfer<FormProps<To, From>>,
55
+ omegaConfig?: OmegaConfig<From>,
56
+ ): OmegaFormReturn<To, From> => {
28
57
  if (!schema) throw new Error("Schema is required")
29
58
  const standardSchema = S.standardSchemaV1(schema)
30
59
 
31
60
  const { filterItems, meta } = generateMetaFromSchema(schema)
32
61
 
62
+ const persistencyKey = computed(() => {
63
+ if (omegaConfig?.persistency?.id) {
64
+ return omegaConfig.persistency.id
65
+ }
66
+ const path = window.location.pathname
67
+ const keys = Object.keys(meta)
68
+ return `${path}-${keys.join("-")}`
69
+ })
70
+
71
+ const clearUrlParams = () => {
72
+ const params = new URLSearchParams(window.location.search)
73
+ params.delete(persistencyKey.value)
74
+ const url = new URL(window.location.href)
75
+ url.search = params.toString()
76
+ window.history.replaceState({}, "", url.toString())
77
+ }
78
+
79
+ const defaultValues = computed(() => {
80
+ if (
81
+ tanstackFormOptions?.defaultValues &&
82
+ !omegaConfig?.persistency?.overrideDefaultValues
83
+ ) {
84
+ return tanstackFormOptions?.defaultValues
85
+ }
86
+ const persistency = omegaConfig?.persistency
87
+ if (!persistency?.policies || persistency.policies.length === 0) return {}
88
+ if (persistency.policies.includes("querystring")) {
89
+ try {
90
+ const params = new URLSearchParams(window.location.search)
91
+ const value = params.get(persistencyKey.value)
92
+ clearUrlParams()
93
+ if (value) {
94
+ return JSON.parse(value)
95
+ }
96
+ } catch (error) {
97
+ console.error(error)
98
+ }
99
+ }
100
+
101
+ if (
102
+ persistency.policies.includes("local") ||
103
+ persistency.policies.includes("session")
104
+ ) {
105
+ const storage = persistency.policies.includes("local")
106
+ ? localStorage
107
+ : sessionStorage
108
+ if (storage) {
109
+ try {
110
+ const value = JSON.parse(
111
+ storage.getItem(persistencyKey.value) || "{}",
112
+ )
113
+ storage.removeItem(persistencyKey.value)
114
+ return value
115
+ } catch (error) {
116
+ console.error(error)
117
+ }
118
+ }
119
+ }
120
+ return {}
121
+ })
122
+
33
123
  const form = useForm<
34
124
  To,
35
125
  FormValidateOrFn<To> | undefined,
@@ -42,22 +132,103 @@ export const useOmegaForm = <
42
132
  FormAsyncValidateOrFn<To> | undefined,
43
133
  FormAsyncValidateOrFn<To> | undefined
44
134
  >({
45
- ...options,
135
+ ...tanstackFormOptions,
46
136
  validators: {
47
137
  onSubmit: standardSchema,
48
- ...(options?.validators || {}),
138
+ ...(tanstackFormOptions?.validators || {}),
49
139
  },
50
- onSubmit: options?.onSubmit
140
+ onSubmit: tanstackFormOptions?.onSubmit
51
141
  ? ({ formApi, meta, value }) =>
52
- options.onSubmit?.({
142
+ tanstackFormOptions.onSubmit?.({
53
143
  formApi: formApi as OmegaFormApi<To, From>,
54
144
  meta,
55
145
  value: value as unknown as From,
56
146
  })
57
147
  : undefined,
148
+ defaultValues: defaultValues.value as any,
58
149
  }) satisfies OmegaFormApi<To, From>
59
150
 
60
- const exposed = Object.assign(form, { meta, filterItems })
151
+ const clear = () => {
152
+ Object.keys(meta).forEach((key: any) => {
153
+ form.setFieldValue(key, undefined)
154
+ })
155
+ }
156
+
157
+ const createNestedObjectFromPaths = (paths: string[]) =>
158
+ paths.reduce(
159
+ (result, path) => {
160
+ const parts = path.split(".")
161
+ return parts.reduce((acc, part, i) => {
162
+ if (i === parts.length - 1) {
163
+ acc[part] = form.getFieldValue(path as any)
164
+ } else {
165
+ acc[part] = acc[part] || {}
166
+ }
167
+ return acc[part]
168
+ }, result)
169
+ },
170
+ {} as Record<string, any>,
171
+ )
172
+
173
+ const persistFilter = (persistency: OmegaConfig<From>["persistency"]) => {
174
+ if (!persistency) return
175
+ if (Array.isArray(persistency.keys)) {
176
+ return createNestedObjectFromPaths(persistency.keys)
177
+ }
178
+ if (Array.isArray(persistency.banKeys)) {
179
+ const subs = Object.keys(meta).filter(metakey =>
180
+ persistency.banKeys?.includes(metakey as any),
181
+ )
182
+ return createNestedObjectFromPaths(subs)
183
+ }
184
+ return form.store.state.values
185
+ }
186
+
187
+ const persistData = () => {
188
+ const persistency = omegaConfig?.persistency
189
+ if (!persistency?.policies || persistency.policies.length === 0) {
190
+ return
191
+ }
192
+ if (
193
+ persistency.policies.includes("local") ||
194
+ persistency.policies.includes("session")
195
+ ) {
196
+ const storage = persistency.policies.includes("local")
197
+ ? localStorage
198
+ : sessionStorage
199
+ if (!storage) return
200
+ const values = persistFilter(persistency)
201
+ return storage.setItem(persistencyKey.value, JSON.stringify(values))
202
+ }
203
+ }
204
+
205
+ const saveDataInUrl = () => {
206
+ const persistency = omegaConfig?.persistency
207
+ if (!persistency?.policies || persistency.policies.length === 0) {
208
+ return
209
+ }
210
+ if (persistency.policies.includes("querystring")) {
211
+ const values = persistFilter(persistency)
212
+ const searchParams = new URLSearchParams(window.location.search)
213
+ searchParams.set(persistencyKey.value, JSON.stringify(values))
214
+ const url = new URL(window.location.href)
215
+ url.search = searchParams.toString()
216
+ window.history.replaceState({}, "", url.toString())
217
+ }
218
+ }
219
+
220
+ onUnmounted(persistData)
221
+
222
+ onMounted(() => {
223
+ window.addEventListener("beforeunload", persistData)
224
+ window.addEventListener("blur", saveDataInUrl)
225
+ })
226
+ onBeforeUnmount(() => {
227
+ window.removeEventListener("beforeunload", persistData)
228
+ window.removeEventListener("blur", saveDataInUrl)
229
+ })
230
+
231
+ const exposed = Object.assign(form, { meta, filterItems, clear })
61
232
 
62
233
  return exposed
63
234
  }
@@ -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
+ policies: ["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,58 @@
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
+ policies: ["session", "querystring"],
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
+ </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
@@ -0,0 +1,4 @@
1
+ {
2
+ "extends": "../../tsconfig.json",
3
+ "exclude": []
4
+ }