notform 2.0.0-alpha.1 → 2.0.0-alpha.3
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/index.d.ts +107 -17
- package/dist/index.js +328 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import * as _$vue from "vue";
|
|
1
2
|
import { ComputedRef, MaybeRefOrGetter, Ref } from "vue";
|
|
2
3
|
import { StandardSchemaV1 } from "@standard-schema/spec";
|
|
3
4
|
|
|
@@ -2748,7 +2749,6 @@ type DeepPartial<TData> = PartialDeep<TData, {
|
|
|
2748
2749
|
*/
|
|
2749
2750
|
type Paths<TReference> = Extract<Paths$1<TReference, {
|
|
2750
2751
|
maxRecursionDepth: 10;
|
|
2751
|
-
bracketNotation: true;
|
|
2752
2752
|
}>, string> | (string & {});
|
|
2753
2753
|
/**
|
|
2754
2754
|
* Represents a validation schema for object-based data structures.
|
|
@@ -2793,11 +2793,13 @@ type UseNotFormConfig<TSchema extends ObjectSchema> = {
|
|
|
2793
2793
|
*/
|
|
2794
2794
|
onSubmit?: (values: StandardSchemaV1.InferOutput<TSchema>) => void | Promise<void>;
|
|
2795
2795
|
};
|
|
2796
|
+
//#endregion
|
|
2797
|
+
//#region src/types/not-form.d.ts
|
|
2796
2798
|
/**
|
|
2797
2799
|
* The core state and methods provided by a form instance.
|
|
2798
2800
|
* @template TSchema The validation schema type derived from ObjectSchema.
|
|
2799
2801
|
*/
|
|
2800
|
-
type
|
|
2802
|
+
type NotFormInstance<TSchema extends ObjectSchema> = {
|
|
2801
2803
|
/**
|
|
2802
2804
|
* A convenience self-reference to the form instance.
|
|
2803
2805
|
* Useful when you prefer to destructure the composable return value but still need
|
|
@@ -2806,7 +2808,14 @@ type UseNotFormInstance<TSchema extends ObjectSchema> = Omit<UseNotFormConfig<TS
|
|
|
2806
2808
|
* const { values, submit, instance } = useNotForm({ schema, onSubmit })
|
|
2807
2809
|
* // <NotForm :form="instance" />
|
|
2808
2810
|
*/
|
|
2809
|
-
instance:
|
|
2811
|
+
instance: NotFormInstance<TSchema>; /** The initial values the form was created or last reset with */
|
|
2812
|
+
readonly initialValues: UseNotFormConfig<TSchema>['initialValues']; /** The initial errors the form was created or last reset with */
|
|
2813
|
+
readonly initialErrors: UseNotFormConfig<TSchema>['initialErrors'];
|
|
2814
|
+
/**
|
|
2815
|
+
* The validation triggers of the form.
|
|
2816
|
+
* @default { onBlur: true, onChange: true, onInput: true }
|
|
2817
|
+
*/
|
|
2818
|
+
readonly validateOn: UseNotFormConfig<TSchema>['validateOn']; /** Deeply reactive object of field values */
|
|
2810
2819
|
values: Ref<StandardSchemaV1.InferInput<TSchema>>;
|
|
2811
2820
|
/**
|
|
2812
2821
|
* Updates a specific field value by path.
|
|
@@ -2872,14 +2881,8 @@ type UseNotFormInstance<TSchema extends ObjectSchema> = Omit<UseNotFormConfig<TS
|
|
|
2872
2881
|
* Returns all validation issues for a specific field path.
|
|
2873
2882
|
* @param path Dot-separated path to the field.
|
|
2874
2883
|
*/
|
|
2875
|
-
getFieldErrors: (path: Paths<StandardSchemaV1.InferInput<TSchema>>) => StandardSchemaV1.Issue[];
|
|
2876
|
-
|
|
2877
|
-
* Reactive set of field paths currently being validated.
|
|
2878
|
-
* Empty when no validation is in progress.
|
|
2879
|
-
* Populated with all paths during full-form validation.
|
|
2880
|
-
*/
|
|
2881
|
-
validatingFields: Ref<Set<Paths<StandardSchemaV1.InferInput<TSchema>>>>; /** Whether any field or the full form is currently being validated */
|
|
2882
|
-
isValidating: ComputedRef<boolean>;
|
|
2884
|
+
getFieldErrors: (path: Paths<StandardSchemaV1.InferInput<TSchema>>) => StandardSchemaV1.Issue[]; /** Whether any field or the full form is currently being validated */
|
|
2885
|
+
isValidating: Ref<boolean>;
|
|
2883
2886
|
/**
|
|
2884
2887
|
* Validates the entire form against the schema.
|
|
2885
2888
|
* @returns A promise resolving to the validation result.
|
|
@@ -2910,17 +2913,104 @@ type UseNotFormInstance<TSchema extends ObjectSchema> = Omit<UseNotFormConfig<TS
|
|
|
2910
2913
|
*/
|
|
2911
2914
|
reset: (values?: DeepPartial<StandardSchemaV1.InferInput<TSchema>>, errors?: StandardSchemaV1.Issue[]) => void;
|
|
2912
2915
|
};
|
|
2913
|
-
//#endregion
|
|
2914
|
-
//#region src/types/not-form.d.ts
|
|
2915
2916
|
/**
|
|
2916
2917
|
* Props for the NotForm component
|
|
2917
2918
|
* @template TSchema The validation schema type derived from ObjectSchema.
|
|
2918
2919
|
*/
|
|
2919
2920
|
type NotFormProps<TSchema extends ObjectSchema> = {
|
|
2920
|
-
/** The form instance to use */instance:
|
|
2921
|
+
/** The form instance to use */instance: NotFormInstance<TSchema>;
|
|
2922
|
+
};
|
|
2923
|
+
type NotFormSlots = {
|
|
2924
|
+
/** The default slot */default: [];
|
|
2921
2925
|
};
|
|
2922
|
-
|
|
2923
|
-
|
|
2926
|
+
//#endregion
|
|
2927
|
+
//#region src/composables/use-not-form.d.ts
|
|
2928
|
+
declare function useNotForm<TSchema extends ObjectSchema>(config: UseNotFormConfig<TSchema>): NotFormInstance<TSchema>;
|
|
2929
|
+
//#endregion
|
|
2930
|
+
//#region src/components/not-form.vue.d.ts
|
|
2931
|
+
declare const __VLS_export$1: <TSchema extends ObjectSchema>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal$1<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_exposed?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
|
|
2932
|
+
props: _$vue.PublicProps & __VLS_PrettifyLocal$1<NotFormProps<TSchema>> & (typeof globalThis extends {
|
|
2933
|
+
__VLS_PROPS_FALLBACK: infer P;
|
|
2934
|
+
} ? P : {});
|
|
2935
|
+
expose: (exposed: {}) => void;
|
|
2936
|
+
attrs: any;
|
|
2937
|
+
slots: NotFormSlots;
|
|
2938
|
+
emit: {};
|
|
2939
|
+
}>) => _$vue.VNode & {
|
|
2940
|
+
__ctx?: Awaited<typeof __VLS_setup>;
|
|
2941
|
+
};
|
|
2942
|
+
declare const _default$1: typeof __VLS_export$1;
|
|
2943
|
+
type __VLS_PrettifyLocal$1<T> = (T extends any ? { [K in keyof T]: T[K] } : { [K in keyof T as K]: T[K] }) & {};
|
|
2944
|
+
//#endregion
|
|
2945
|
+
//#region src/types/not-field.d.ts
|
|
2946
|
+
/** The event handlers provided by a field instance for binding to native or custom inputs. */
|
|
2947
|
+
type NotFieldEvents = {
|
|
2948
|
+
/** Triggered when the field loses focus */onBlur: () => void; /** Triggered on every keystroke or value change */
|
|
2949
|
+
onInput: () => void; /** Triggered when the field value is committed */
|
|
2950
|
+
onChange: () => void; /** Triggered when the field gains focus */
|
|
2951
|
+
onFocus: () => void;
|
|
2952
|
+
};
|
|
2953
|
+
/**
|
|
2954
|
+
* Props for the NotField component
|
|
2955
|
+
* @template TSchema The validation schema type derived from ObjectSchema.
|
|
2956
|
+
* @template TPath The dot-separated path to the field within the form state.
|
|
2957
|
+
*/
|
|
2958
|
+
type NotFieldProps<TSchema extends ObjectSchema, TPath extends Paths<StandardSchemaV1.InferInput<TSchema>>> = {
|
|
2959
|
+
/** The dot-separated path to the field within the form state */path: TPath; /** Optional form instance — takes priority over injected context */
|
|
2960
|
+
form?: NotFormInstance<TSchema>;
|
|
2961
|
+
/**
|
|
2962
|
+
* Per-field validation trigger overrides.
|
|
2963
|
+
* Merged over the form-wide validateOn config — only the keys you specify are overridden.
|
|
2964
|
+
*/
|
|
2965
|
+
validateOn?: Partial<Record<ValidationTrigger, boolean>>;
|
|
2966
|
+
};
|
|
2967
|
+
/**
|
|
2968
|
+
* The core state and methods provided by a field instance.
|
|
2969
|
+
* @template TSchema The validation schema type derived from ObjectSchema.
|
|
2970
|
+
* @template TPath The dot-separated path to the field within the form state.
|
|
2971
|
+
*/
|
|
2972
|
+
type NotFieldInstance<TSchema extends ObjectSchema, TPath extends Paths<StandardSchemaV1.InferInput<TSchema>>> = {
|
|
2973
|
+
/** The dot-separated path to the field within the form state */path: TPath; /** The current value of the field */
|
|
2974
|
+
value: any; /** Whether the field is currently being validated */
|
|
2975
|
+
isValidating: boolean; /** Validates this field against the form schema */
|
|
2976
|
+
validate: () => ReturnType<NotFormInstance<TSchema>['validateField']>; /** The validation issues for this field */
|
|
2977
|
+
errors: StandardSchemaV1.Issue[]; /** Marks the field as touched */
|
|
2978
|
+
touch: () => void; /** Marks the field as not touched */
|
|
2979
|
+
unTouch: () => void; /** Marks the field as dirty */
|
|
2980
|
+
dirty: () => void; /** Marks the field as not dirty */
|
|
2981
|
+
unDirty: () => void;
|
|
2982
|
+
/**
|
|
2983
|
+
* All event handlers combined — spread directly onto native inputs.
|
|
2984
|
+
* @example <input v-bind="events" />
|
|
2985
|
+
*/
|
|
2986
|
+
events: NotFieldEvents; /** Triggered when the field loses focus */
|
|
2987
|
+
onBlur: NotFieldEvents['onBlur']; /** Triggered on every keystroke or value change */
|
|
2988
|
+
onInput: NotFieldEvents['onInput']; /** Triggered when the field value is committed */
|
|
2989
|
+
onChange: NotFieldEvents['onChange']; /** Triggered when the field gains focus */
|
|
2990
|
+
onFocus: NotFieldEvents['onFocus'];
|
|
2991
|
+
};
|
|
2992
|
+
/**
|
|
2993
|
+
* Slots for the NotField component
|
|
2994
|
+
* @template TSchema The validation schema type derived from ObjectSchema.
|
|
2995
|
+
* @template TPath The dot-separated path to the field within the form state.
|
|
2996
|
+
*/
|
|
2997
|
+
type NotFieldSlots<TSchema extends ObjectSchema, TPath extends Paths<StandardSchemaV1.InferInput<TSchema>>> = {
|
|
2998
|
+
/** The default slot receives the full field instance */default: (props: NotFieldInstance<TSchema, TPath>) => [];
|
|
2999
|
+
};
|
|
3000
|
+
//#endregion
|
|
3001
|
+
//#region src/components/not-field.vue.d.ts
|
|
3002
|
+
declare const __VLS_export: <TSchema extends ObjectSchema, TPath extends Paths<StandardSchemaV1.InferInput<TSchema>>>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_exposed?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
|
|
3003
|
+
props: _$vue.PublicProps & __VLS_PrettifyLocal<NotFieldProps<TSchema, TPath>> & (typeof globalThis extends {
|
|
3004
|
+
__VLS_PROPS_FALLBACK: infer P;
|
|
3005
|
+
} ? P : {});
|
|
3006
|
+
expose: (exposed: {}) => void;
|
|
3007
|
+
attrs: any;
|
|
3008
|
+
slots: NotFieldSlots<TSchema, TPath>;
|
|
3009
|
+
emit: {};
|
|
3010
|
+
}>) => _$vue.VNode & {
|
|
3011
|
+
__ctx?: Awaited<typeof __VLS_setup>;
|
|
2924
3012
|
};
|
|
3013
|
+
declare const _default: typeof __VLS_export;
|
|
3014
|
+
type __VLS_PrettifyLocal<T> = (T extends any ? { [K in keyof T]: T[K] } : { [K in keyof T as K]: T[K] }) & {};
|
|
2925
3015
|
//#endregion
|
|
2926
|
-
export { ArraySchema, DeepPartial, NotFormProps, NotFormSlots, ObjectSchema, Paths, UseNotFormConfig,
|
|
3016
|
+
export { ArraySchema, DeepPartial, _default as NotField, NotFieldEvents, NotFieldInstance, NotFieldProps, NotFieldSlots, _default$1 as NotForm, NotFormInstance, NotFormProps, NotFormSlots, ObjectSchema, Paths, UseNotFormConfig, ValidationTrigger, useNotForm };
|
package/dist/index.js
CHANGED
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
import { computed, createElementBlock, defineComponent, guardReactiveProps, inject, nextTick, normalizeProps, onMounted, openBlock, provide, reactive, ref, renderSlot, toValue, unref, useAttrs } from "vue";
|
|
2
|
+
import { klona } from "klona/full";
|
|
3
|
+
import { dequal } from "dequal";
|
|
4
|
+
import { deepKeys, getProperty, parsePath, setProperty } from "dot-prop";
|
|
5
|
+
//#region src/utils/form-utils.ts
|
|
6
|
+
/**
|
|
7
|
+
* Normalizes a validation path segment into a standard property key.
|
|
8
|
+
* @param segment The path segment to normalize.
|
|
9
|
+
* @returns The normalized key.
|
|
10
|
+
*/
|
|
11
|
+
function normalizeSegment(segment) {
|
|
12
|
+
if (typeof segment === "object" && segment !== null && "key" in segment) return segment.key;
|
|
13
|
+
return segment;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Checks if a validation issue path matches a target field path.
|
|
17
|
+
* @param issuePath The path array from the validation issue.
|
|
18
|
+
* @param targetPath The normalized path to compare against.
|
|
19
|
+
* @returns True if the paths are equivalent.
|
|
20
|
+
*/
|
|
21
|
+
function isIssuePathEqual(issuePath, targetPath) {
|
|
22
|
+
if (!issuePath) return false;
|
|
23
|
+
if (issuePath.length !== targetPath.length) return false;
|
|
24
|
+
return issuePath.every((segment, index) => {
|
|
25
|
+
const normalizedSegment = normalizeSegment(segment);
|
|
26
|
+
const targetSegment = targetPath[index];
|
|
27
|
+
if (typeof normalizedSegment === "number" || typeof targetSegment === "number") return Number(normalizedSegment) === Number(targetSegment);
|
|
28
|
+
return normalizedSegment === targetSegment;
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
//#endregion
|
|
32
|
+
//#region src/composables/use-not-form.ts
|
|
33
|
+
function useNotForm(config) {
|
|
34
|
+
let initialValues = klona(config.initialValues ?? {});
|
|
35
|
+
let initialErrors = klona(config.initialErrors ?? []);
|
|
36
|
+
const validateOn = {
|
|
37
|
+
onBlur: config.validateOn?.onBlur ?? true,
|
|
38
|
+
onChange: config.validateOn?.onChange ?? true,
|
|
39
|
+
onInput: config.validateOn?.onInput ?? true,
|
|
40
|
+
onMount: config.validateOn?.onMount ?? false,
|
|
41
|
+
onFocus: config.validateOn?.onFocus ?? false
|
|
42
|
+
};
|
|
43
|
+
const values = ref(klona(initialValues));
|
|
44
|
+
const errors = ref([...initialErrors]);
|
|
45
|
+
const touchedFields = ref(/* @__PURE__ */ new Set());
|
|
46
|
+
const dirtyFields = ref(/* @__PURE__ */ new Set());
|
|
47
|
+
const isSubmitting = ref(false);
|
|
48
|
+
const isValidating = ref(false);
|
|
49
|
+
const errorsMap = computed(() => {
|
|
50
|
+
return errors.value.reduce((accumulator, issue) => {
|
|
51
|
+
const path = issue.path?.join(".");
|
|
52
|
+
if (path && !accumulator[path]) accumulator[path] = issue.message;
|
|
53
|
+
return accumulator;
|
|
54
|
+
}, {});
|
|
55
|
+
});
|
|
56
|
+
const isDirty = computed(() => dirtyFields.value.size > 0);
|
|
57
|
+
const isTouched = computed(() => touchedFields.value.size > 0);
|
|
58
|
+
const isValid = computed(() => errors.value.length === 0);
|
|
59
|
+
const touchField = (path) => {
|
|
60
|
+
touchedFields.value.add(path);
|
|
61
|
+
};
|
|
62
|
+
const unTouchField = (path) => {
|
|
63
|
+
touchedFields.value.delete(path);
|
|
64
|
+
};
|
|
65
|
+
const touchAllFields = () => {
|
|
66
|
+
deepKeys(values.value).forEach((path) => touchedFields.value.add(path));
|
|
67
|
+
};
|
|
68
|
+
const unTouchAllFields = () => {
|
|
69
|
+
touchedFields.value.clear();
|
|
70
|
+
};
|
|
71
|
+
const dirtyField = (path) => {
|
|
72
|
+
dirtyFields.value.add(path);
|
|
73
|
+
};
|
|
74
|
+
const unDirtyField = (path) => {
|
|
75
|
+
dirtyFields.value.delete(path);
|
|
76
|
+
};
|
|
77
|
+
const dirtyAllFields = () => {
|
|
78
|
+
deepKeys(values.value).forEach((path) => dirtyFields.value.add(path));
|
|
79
|
+
};
|
|
80
|
+
const unDirtyAllFields = () => {
|
|
81
|
+
dirtyFields.value.clear();
|
|
82
|
+
};
|
|
83
|
+
const setValue = (path, value) => {
|
|
84
|
+
setProperty(values.value, path, value);
|
|
85
|
+
touchField(path);
|
|
86
|
+
if (dequal(value, getProperty(initialValues, path))) unDirtyField(path);
|
|
87
|
+
else dirtyField(path);
|
|
88
|
+
if (validateOn.onChange) validateField(path);
|
|
89
|
+
};
|
|
90
|
+
const setValues = (newValues) => {
|
|
91
|
+
for (const [path, value] of Object.entries(newValues)) setValue(path, value);
|
|
92
|
+
};
|
|
93
|
+
const setError = (newIssue) => {
|
|
94
|
+
const newPath = newIssue.path?.join(".");
|
|
95
|
+
errors.value = [...errors.value.filter((error) => error.path?.join(".") !== newPath), newIssue];
|
|
96
|
+
};
|
|
97
|
+
const setErrors = (newIssues) => {
|
|
98
|
+
errors.value = [...newIssues];
|
|
99
|
+
};
|
|
100
|
+
const clearErrors = () => {
|
|
101
|
+
errors.value = [];
|
|
102
|
+
};
|
|
103
|
+
const getFieldErrors = (path) => {
|
|
104
|
+
const pathArray = parsePath(path);
|
|
105
|
+
return errors.value.filter((error) => isIssuePathEqual(error.path, pathArray));
|
|
106
|
+
};
|
|
107
|
+
const validate = async () => {
|
|
108
|
+
isValidating.value = true;
|
|
109
|
+
try {
|
|
110
|
+
const result = await toValue(config.schema)["~standard"].validate(values.value);
|
|
111
|
+
if (result?.issues) {
|
|
112
|
+
setErrors([...result.issues]);
|
|
113
|
+
return { issues: result.issues };
|
|
114
|
+
}
|
|
115
|
+
clearErrors();
|
|
116
|
+
return { value: result.value };
|
|
117
|
+
} finally {
|
|
118
|
+
isValidating.value = false;
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
const validateField = async (path) => {
|
|
122
|
+
isValidating.value = true;
|
|
123
|
+
try {
|
|
124
|
+
const result = await toValue(config.schema)["~standard"].validate(values.value);
|
|
125
|
+
const pathArray = parsePath(path);
|
|
126
|
+
errors.value = errors.value.filter((error) => !isIssuePathEqual(error.path, pathArray));
|
|
127
|
+
if (result?.issues) {
|
|
128
|
+
const fieldIssues = result.issues.filter((issue) => isIssuePathEqual(issue.path, pathArray));
|
|
129
|
+
if (fieldIssues.length > 0) {
|
|
130
|
+
errors.value = [...errors.value, ...fieldIssues];
|
|
131
|
+
return { issues: fieldIssues };
|
|
132
|
+
}
|
|
133
|
+
return { value: getProperty(values.value, path) };
|
|
134
|
+
}
|
|
135
|
+
return { value: getProperty(result.value, path) };
|
|
136
|
+
} finally {
|
|
137
|
+
isValidating.value = false;
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
const submit = async (event) => {
|
|
141
|
+
isSubmitting.value = true;
|
|
142
|
+
try {
|
|
143
|
+
touchAllFields();
|
|
144
|
+
dirtyAllFields();
|
|
145
|
+
const result = await validate();
|
|
146
|
+
if (result?.issues) {
|
|
147
|
+
event.preventDefault();
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
if (config.onSubmit) {
|
|
151
|
+
event.preventDefault();
|
|
152
|
+
await config.onSubmit(result.value);
|
|
153
|
+
}
|
|
154
|
+
} catch {
|
|
155
|
+
event.preventDefault();
|
|
156
|
+
} finally {
|
|
157
|
+
isSubmitting.value = false;
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
const reset = (newValues, newErrors) => {
|
|
161
|
+
if (newValues) initialValues = klona(newValues);
|
|
162
|
+
if (newErrors) initialErrors = klona(newErrors);
|
|
163
|
+
values.value = klona(initialValues);
|
|
164
|
+
errors.value = klona(initialErrors);
|
|
165
|
+
touchedFields.value.clear();
|
|
166
|
+
dirtyFields.value.clear();
|
|
167
|
+
};
|
|
168
|
+
const resolvedInstance = {
|
|
169
|
+
initialValues,
|
|
170
|
+
initialErrors,
|
|
171
|
+
validateOn,
|
|
172
|
+
values,
|
|
173
|
+
setValue,
|
|
174
|
+
setValues,
|
|
175
|
+
touchedFields,
|
|
176
|
+
touchField,
|
|
177
|
+
unTouchField,
|
|
178
|
+
touchAllFields,
|
|
179
|
+
unTouchAllFields,
|
|
180
|
+
isTouched,
|
|
181
|
+
dirtyFields,
|
|
182
|
+
dirtyField,
|
|
183
|
+
unDirtyField,
|
|
184
|
+
dirtyAllFields,
|
|
185
|
+
unDirtyAllFields,
|
|
186
|
+
isDirty,
|
|
187
|
+
errors,
|
|
188
|
+
errorsMap,
|
|
189
|
+
setError,
|
|
190
|
+
setErrors,
|
|
191
|
+
clearErrors,
|
|
192
|
+
getFieldErrors,
|
|
193
|
+
isValidating,
|
|
194
|
+
validate,
|
|
195
|
+
validateField,
|
|
196
|
+
isValid,
|
|
197
|
+
isSubmitting,
|
|
198
|
+
submit,
|
|
199
|
+
reset
|
|
200
|
+
};
|
|
201
|
+
resolvedInstance.instance = resolvedInstance;
|
|
202
|
+
return resolvedInstance;
|
|
203
|
+
}
|
|
204
|
+
//#endregion
|
|
205
|
+
//#region src/utils/instance-utils.ts
|
|
206
|
+
/** Injection key for the current active form instance */
|
|
207
|
+
const NOT_FORM_INSTANCE_KEY = Symbol("notform:instance");
|
|
208
|
+
/**
|
|
209
|
+
* Provides a form instance to all descendant components.
|
|
210
|
+
* @param instance The form instance to provide.
|
|
211
|
+
*/
|
|
212
|
+
function provideNotFormInstance(instance) {
|
|
213
|
+
provide(NOT_FORM_INSTANCE_KEY, instance);
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Resolves the active form instance from context or an explicit prop override.
|
|
217
|
+
* @param explicitInstance Optional instance passed directly via :form prop — takes priority over injected context.
|
|
218
|
+
* @throws If no instance is found from either source.
|
|
219
|
+
*/
|
|
220
|
+
function useNotFormInstance(explicitInstance) {
|
|
221
|
+
const injected = inject(NOT_FORM_INSTANCE_KEY);
|
|
222
|
+
const instance = explicitInstance ?? injected;
|
|
223
|
+
if (!instance) throw new Error("[NotForm] No form instance found. Add a <NotForm :instance=\"...\"> ancestor or pass :form directly.");
|
|
224
|
+
return instance;
|
|
225
|
+
}
|
|
226
|
+
//#endregion
|
|
227
|
+
//#region src/components/not-form.vue
|
|
228
|
+
var not_form_default = /* @__PURE__ */ defineComponent({
|
|
229
|
+
__name: "not-form",
|
|
230
|
+
props: { instance: {
|
|
231
|
+
type: Object,
|
|
232
|
+
required: true
|
|
233
|
+
} },
|
|
234
|
+
setup(__props) {
|
|
235
|
+
const attributes = useAttrs();
|
|
236
|
+
provideNotFormInstance(__props.instance);
|
|
237
|
+
return (_ctx, _cache) => {
|
|
238
|
+
return openBlock(), createElementBlock("form", normalizeProps(guardReactiveProps(unref(attributes))), [renderSlot(_ctx.$slots, "default")], 16);
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
//#endregion
|
|
243
|
+
//#region src/components/not-field.vue
|
|
244
|
+
var not_field_default = /* @__PURE__ */ defineComponent({
|
|
245
|
+
inheritAttrs: false,
|
|
246
|
+
__name: "not-field",
|
|
247
|
+
props: {
|
|
248
|
+
path: {
|
|
249
|
+
type: null,
|
|
250
|
+
required: true
|
|
251
|
+
},
|
|
252
|
+
form: {
|
|
253
|
+
type: Object,
|
|
254
|
+
required: false
|
|
255
|
+
},
|
|
256
|
+
validateOn: {
|
|
257
|
+
type: Object,
|
|
258
|
+
required: false
|
|
259
|
+
}
|
|
260
|
+
},
|
|
261
|
+
setup(__props) {
|
|
262
|
+
const props = __props;
|
|
263
|
+
const formInstance = useNotFormInstance(props.form);
|
|
264
|
+
const isValidating = ref(false);
|
|
265
|
+
const path = computed(() => props.path);
|
|
266
|
+
const value = computed(() => getProperty(formInstance.values.value, props.path));
|
|
267
|
+
const errors = computed(() => formInstance.getFieldErrors(path.value));
|
|
268
|
+
const touch = () => formInstance.touchField(path.value);
|
|
269
|
+
const unTouch = () => formInstance.unTouchField(path.value);
|
|
270
|
+
const dirty = () => formInstance.dirtyField(path.value);
|
|
271
|
+
const unDirty = () => formInstance.unDirtyField(path.value);
|
|
272
|
+
const validate = async () => {
|
|
273
|
+
isValidating.value = true;
|
|
274
|
+
try {
|
|
275
|
+
return await formInstance.validateField(path.value);
|
|
276
|
+
} finally {
|
|
277
|
+
isValidating.value = false;
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
const validateOn = computed(() => ({
|
|
281
|
+
...formInstance.validateOn,
|
|
282
|
+
...props.validateOn
|
|
283
|
+
}));
|
|
284
|
+
const onBlur = () => {
|
|
285
|
+
touch();
|
|
286
|
+
if (validateOn.value.onBlur) validate();
|
|
287
|
+
};
|
|
288
|
+
const onChange = () => {
|
|
289
|
+
if (validateOn.value.onChange) validate();
|
|
290
|
+
};
|
|
291
|
+
const onInput = () => {
|
|
292
|
+
if (validateOn.value.onInput) validate();
|
|
293
|
+
};
|
|
294
|
+
const onFocus = () => {
|
|
295
|
+
if (validateOn.value.onFocus) validate();
|
|
296
|
+
};
|
|
297
|
+
const fieldInstance = reactive({
|
|
298
|
+
path,
|
|
299
|
+
value,
|
|
300
|
+
isValidating,
|
|
301
|
+
validate,
|
|
302
|
+
errors,
|
|
303
|
+
touch,
|
|
304
|
+
unTouch,
|
|
305
|
+
dirty,
|
|
306
|
+
unDirty,
|
|
307
|
+
events: computed(() => ({
|
|
308
|
+
onBlur,
|
|
309
|
+
onInput,
|
|
310
|
+
onChange,
|
|
311
|
+
onFocus
|
|
312
|
+
})),
|
|
313
|
+
onBlur,
|
|
314
|
+
onInput,
|
|
315
|
+
onChange,
|
|
316
|
+
onFocus
|
|
317
|
+
});
|
|
318
|
+
onMounted(async () => {
|
|
319
|
+
await nextTick();
|
|
320
|
+
if (validateOn.value.onMount) validate();
|
|
321
|
+
});
|
|
322
|
+
return (_ctx, _cache) => {
|
|
323
|
+
return renderSlot(_ctx.$slots, "default", normalizeProps(guardReactiveProps(fieldInstance)));
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
});
|
|
327
|
+
//#endregion
|
|
328
|
+
export { not_field_default as NotField, not_form_default as NotForm, useNotForm };
|