@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.
- package/dist/types/components/OmegaForm/OmegaWrapper.vue.d.ts +7 -14
- package/dist/types/components/OmegaForm/getOmegaStore.d.ts +1 -1
- package/dist/types/components/OmegaForm/index.d.ts +3 -4
- package/dist/types/components/OmegaForm/useOmegaForm.d.ts +24 -3
- package/dist/vue-components.es11.js +16 -14
- package/dist/vue-components.es15.js +29 -25
- package/dist/vue-components.es5.js +104 -19
- package/dist/vue-components.es7.js +2 -2
- package/package.json +11 -2
- package/src/components/OmegaForm/OmegaInternalInput.vue +6 -0
- package/src/components/OmegaForm/OmegaWrapper.vue +10 -8
- package/src/components/OmegaForm/getOmegaStore.ts +1 -1
- package/src/components/OmegaForm/useOmegaForm.ts +181 -10
- package/src/stories/OmegaForm/ComplexForm.vue +81 -0
- package/src/stories/OmegaForm/EmailForm.vue +48 -0
- package/src/stories/OmegaForm/PersistencyForm.vue +58 -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,14 +1,13 @@
|
|
|
1
1
|
import { type S } from "effect-app";
|
|
2
|
-
import { type
|
|
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:
|
|
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<
|
|
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:
|
|
31
|
-
|
|
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("./
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
29
|
+
r.subscribe
|
|
29
30
|
), p = i(
|
|
30
31
|
t.store,
|
|
31
32
|
(e) => e.submissionAttempts
|
|
32
|
-
),
|
|
33
|
+
), s = t.useStore((e) => e.errors);
|
|
33
34
|
return b(
|
|
34
|
-
() => [t.filterItems,
|
|
35
|
+
() => [t.filterItems, s.value],
|
|
35
36
|
() => {
|
|
36
37
|
const e = t.filterItems;
|
|
37
|
-
return e ?
|
|
38
|
-
(
|
|
38
|
+
return e ? s.value ? (Object.values(s.value).filter(
|
|
39
|
+
(o) => !!o
|
|
39
40
|
).flatMap(
|
|
40
|
-
(
|
|
41
|
-
).some((
|
|
42
|
-
const m = t.getFieldMeta(
|
|
43
|
-
t.setFieldMeta(
|
|
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: [
|
|
47
|
+
onSubmit: [{ path: [o], message: e.message }]
|
|
47
48
|
}
|
|
48
49
|
});
|
|
49
50
|
}), {}) : {} : {};
|
|
50
51
|
}
|
|
51
|
-
), y(p,
|
|
52
|
-
|
|
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
|
|
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__ */
|
|
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 =
|
|
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
|
-
}),
|
|
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
|
-
() => [!!
|
|
29
|
+
() => [!!l.value],
|
|
30
30
|
() => {
|
|
31
31
|
var t, r;
|
|
32
|
-
d.value.length && !
|
|
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
|
-
),
|
|
36
|
+
), _(() => {
|
|
37
37
|
var t, r;
|
|
38
|
-
!
|
|
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:
|
|
40
|
+
const { addError: I, removeError: O, showErrors: $, showErrorsOn: k } = H(), i = S(!1);
|
|
41
41
|
A(() => {
|
|
42
|
-
(
|
|
42
|
+
($.value || k === "onChange") && (i.value = !0);
|
|
43
43
|
});
|
|
44
|
-
const
|
|
44
|
+
const m = () => {
|
|
45
45
|
i.value = !0;
|
|
46
|
-
}
|
|
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 ?
|
|
51
|
-
inputId:
|
|
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
|
-
}) :
|
|
58
|
+
}) : O(u);
|
|
55
59
|
}
|
|
56
60
|
);
|
|
57
|
-
const
|
|
61
|
+
const p = n(() => {
|
|
58
62
|
var t, r, c, g, y, h, b, E, V, w;
|
|
59
63
|
return {
|
|
60
|
-
id:
|
|
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:
|
|
72
|
-
type:
|
|
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(
|
|
78
|
-
N("div", { onFocusout:
|
|
79
|
-
U(
|
|
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":
|
|
85
|
+
"input-props": p.value
|
|
82
86
|
}, t.$attrs, {
|
|
83
|
-
"vuetify-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
|
|
2
|
-
import { S as
|
|
3
|
-
import { generateMetaFromSchema as
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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:
|
|
10
|
-
...(
|
|
48
|
+
onSubmit: b,
|
|
49
|
+
...(t == null ? void 0 : t.validators) || {}
|
|
11
50
|
},
|
|
12
|
-
onSubmit:
|
|
13
|
-
var
|
|
14
|
-
return (
|
|
15
|
-
formApi:
|
|
16
|
-
meta:
|
|
17
|
-
value:
|
|
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
|
-
|
|
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
|
-
|
|
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-
|
|
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-
|
|
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.
|
|
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 {
|
|
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:
|
|
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 =
|
|
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
|
|
|
@@ -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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
-
...
|
|
135
|
+
...tanstackFormOptions,
|
|
46
136
|
validators: {
|
|
47
137
|
onSubmit: standardSchema,
|
|
48
|
-
...(
|
|
138
|
+
...(tanstackFormOptions?.validators || {}),
|
|
49
139
|
},
|
|
50
|
-
onSubmit:
|
|
140
|
+
onSubmit: tanstackFormOptions?.onSubmit
|
|
51
141
|
? ({ formApi, meta, value }) =>
|
|
52
|
-
|
|
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
|
|
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
|