srcdev-nuxt-forms 0.0.13 → 0.0.15
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/composables/useErrorMessages.ts +47 -0
- package/composables/useFormControl.ts +116 -0
- package/composables/useSleep.ts +5 -0
- package/nuxt.config.ts +1 -1
- package/package.json +1 -1
- package/pages/index.vue +5 -5
- package/types/types.forms.ts +76 -0
- /package/components/{nuxt-forms/input-text/InputText.vue → forms/input-text/Core.vue} +0 -0
- /package/components/{nuxt-forms/scaffolding/FormField.vue → forms/ui/Field.vue} +0 -0
- /package/components/{nuxt-forms/scaffolding → forms/ui}/FormWrapper.vue +0 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { IFormData } from "@/types/types.forms";
|
|
2
|
+
|
|
3
|
+
export function useErrorMessage(name: string, formData: Ref<IFormData>) {
|
|
4
|
+
const defaultError = ref("");
|
|
5
|
+
const customErrorMessages = ref(toRaw(formData.value.customErrorMessages));
|
|
6
|
+
|
|
7
|
+
const hasCustomError = () => {
|
|
8
|
+
return customErrorMessages.value[name] !== undefined && customErrorMessages.value[name].useCustomError;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const errorMessage = computed(() => {
|
|
12
|
+
if (hasCustomError()) {
|
|
13
|
+
return customErrorMessages.value[name].message;
|
|
14
|
+
} else {
|
|
15
|
+
return defaultError.value;
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
const setDefaultError = (newDefaultError: string) => {
|
|
20
|
+
defaultError.value = newDefaultError;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const fieldHasError = computed(() => {
|
|
24
|
+
if (formData.value.isPending) {
|
|
25
|
+
if (hasCustomError()) {
|
|
26
|
+
return true;
|
|
27
|
+
} else if (Object.keys(formData.value.validityState).length > 0 && formData.value.validityState[name] !== undefined) {
|
|
28
|
+
return !formData.value.validityState[name];
|
|
29
|
+
}
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const removeCustomError = (valid: boolean = false) => {
|
|
35
|
+
formData.value.validityState[name] = valid;
|
|
36
|
+
// await nextTick();
|
|
37
|
+
delete formData.value.customErrorMessages[name];
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
hasCustomError,
|
|
42
|
+
errorMessage,
|
|
43
|
+
setDefaultError,
|
|
44
|
+
fieldHasError,
|
|
45
|
+
removeCustomError
|
|
46
|
+
};
|
|
47
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import type { IFormData, IFieldsInitialState, ICustomErrorMessage, ICustomErrorMessagesArr } from "@/types/types.forms";
|
|
2
|
+
|
|
3
|
+
export function useFormControl(fieldsInitialState: IFieldsInitialState | Ref<IFieldsInitialState | null>) {
|
|
4
|
+
let savedInitialState = {};
|
|
5
|
+
|
|
6
|
+
const formData = ref<IFormData>({
|
|
7
|
+
data: {} as IFieldsInitialState,
|
|
8
|
+
validityState: {},
|
|
9
|
+
isPending: false,
|
|
10
|
+
errorCount: 0,
|
|
11
|
+
customErrorMessages: {},
|
|
12
|
+
hasCustomErrorMessages: false,
|
|
13
|
+
formIsValid: false,
|
|
14
|
+
showErrors: false,
|
|
15
|
+
submitSuccess: false
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const initValidationState = async () => {
|
|
19
|
+
const fields = Object.keys(fieldsInitialState.value || {});
|
|
20
|
+
const state = fields.reduce((acc, field) => {
|
|
21
|
+
acc[field] = false;
|
|
22
|
+
return acc;
|
|
23
|
+
}, {} as Record<string, boolean>);
|
|
24
|
+
formData.value.validityState = state;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const initFormData = async () => {
|
|
28
|
+
await initValidationState();
|
|
29
|
+
|
|
30
|
+
if (fieldsInitialState !== null) {
|
|
31
|
+
savedInitialState = toRaw(fieldsInitialState.value) as IFieldsInitialState;
|
|
32
|
+
formData.value.data = fieldsInitialState as IFieldsInitialState;
|
|
33
|
+
}
|
|
34
|
+
return;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const getErrorCount = async () => {
|
|
38
|
+
await nextTick();
|
|
39
|
+
|
|
40
|
+
const errorCount = Object.values(formData.value.validityState).filter((value) => !value).length;
|
|
41
|
+
formData.value.errorCount = errorCount;
|
|
42
|
+
formData.value.formIsValid = errorCount === 0;
|
|
43
|
+
return formData.value.errorCount;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// Function to count items with "useCustomError" set to true
|
|
47
|
+
const countItemsWithCustomError = (obj: ICustomErrorMessagesArr) => {
|
|
48
|
+
let count = 0;
|
|
49
|
+
|
|
50
|
+
for (const key in obj) {
|
|
51
|
+
if (obj.hasOwnProperty(key) && obj[key].useCustomError === true) {
|
|
52
|
+
count++;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return count;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/*
|
|
60
|
+
* Useage:
|
|
61
|
+
*
|
|
62
|
+
* const { updateCustomErrors } = useFormControl();
|
|
63
|
+
*
|
|
64
|
+
* Add/Update entry
|
|
65
|
+
* const sampleCustomErrorEmail = {
|
|
66
|
+
* useCustomError: true,
|
|
67
|
+
* message: "This is a sample custom error for error EMAIL",
|
|
68
|
+
* };
|
|
69
|
+
* updateCustomErrors("email", sampleCustomErrorEmail);
|
|
70
|
+
*/
|
|
71
|
+
const updateCustomErrors = (name: string, message: null | string = null, valid: boolean = false) => {
|
|
72
|
+
if (message !== null) {
|
|
73
|
+
formData.value.validityState[name] = valid;
|
|
74
|
+
formData.value.customErrorMessages[name] = {
|
|
75
|
+
useCustomError: true,
|
|
76
|
+
message
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
formData.value.hasCustomErrorMessages = countItemsWithCustomError(formData.value.customErrorMessages) > 0;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const resetForm = () => {
|
|
83
|
+
formData.value.data = toRaw(fieldsInitialState.value) as IFieldsInitialState;
|
|
84
|
+
formData.value.validityState = {};
|
|
85
|
+
formData.value.errorCount = 0;
|
|
86
|
+
formData.value.isPending = false;
|
|
87
|
+
formData.value.customErrorMessages = {};
|
|
88
|
+
formData.value.formIsValid = false;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const showErrors = computed(() => {
|
|
92
|
+
return formData.value.errorCount > 0 && formData.value.isPending;
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
const formIsValid = computed(() => {
|
|
96
|
+
return formData.value.errorCount === 0;
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// Keep an eye on this for performance issue
|
|
100
|
+
watch(
|
|
101
|
+
() => formData.value.validityState,
|
|
102
|
+
() => {
|
|
103
|
+
getErrorCount();
|
|
104
|
+
},
|
|
105
|
+
{ deep: true }
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
// watch(
|
|
109
|
+
// () => savedInitialState,
|
|
110
|
+
// () => {
|
|
111
|
+
// console.log("savedInitialState UPDATED", savedInitialState);
|
|
112
|
+
// }
|
|
113
|
+
// );
|
|
114
|
+
|
|
115
|
+
return { formData, initFormData, getErrorCount, updateCustomErrors, resetForm, showErrors, formIsValid };
|
|
116
|
+
}
|
package/nuxt.config.ts
CHANGED
package/package.json
CHANGED
package/pages/index.vue
CHANGED
|
@@ -5,19 +5,19 @@
|
|
|
5
5
|
<div>
|
|
6
6
|
<h1>Sample form page</h1>
|
|
7
7
|
|
|
8
|
-
<
|
|
8
|
+
<FormsUiFormWrapper width="medium">
|
|
9
9
|
<template #default>
|
|
10
10
|
<form>
|
|
11
11
|
<p>Form content</p>
|
|
12
|
-
<
|
|
12
|
+
<FormsUiField width="wide" :has-gutter="true">
|
|
13
13
|
<template #default>
|
|
14
14
|
<p>Input text</p>
|
|
15
|
-
<
|
|
15
|
+
<FormsInputTextCore />
|
|
16
16
|
</template>
|
|
17
|
-
</
|
|
17
|
+
</FormsUiField>
|
|
18
18
|
</form>
|
|
19
19
|
</template>
|
|
20
|
-
</
|
|
20
|
+
</FormsUiFormWrapper>
|
|
21
21
|
</div>
|
|
22
22
|
</template>
|
|
23
23
|
</NuxtLayout>
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
export interface IValidationPatterns {
|
|
2
|
+
pattern: string;
|
|
3
|
+
minlength: string;
|
|
4
|
+
maxlength: string;
|
|
5
|
+
hint: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface IOptionsConfig {
|
|
9
|
+
id: string;
|
|
10
|
+
name: string;
|
|
11
|
+
value: string;
|
|
12
|
+
label: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface IOptionsValueArr {
|
|
16
|
+
[key: string]: string | boolean | number | URL | object;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface IFieldsInitialState {
|
|
20
|
+
[key: string]: null | string | boolean | number | URL | object | IOptionsValueArr[];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface IValidityState {
|
|
24
|
+
badInput: boolean;
|
|
25
|
+
customError: boolean;
|
|
26
|
+
patternMismatch: boolean;
|
|
27
|
+
rangeOverflow: boolean;
|
|
28
|
+
rangeUnderflow: boolean;
|
|
29
|
+
stepMismatch: boolean;
|
|
30
|
+
tooLong: boolean;
|
|
31
|
+
tooShort: boolean;
|
|
32
|
+
typeMismatch: boolean;
|
|
33
|
+
valid: boolean;
|
|
34
|
+
valueMissing: boolean;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface IValidityStateArr {
|
|
38
|
+
[key: string]: {
|
|
39
|
+
badInput: boolean;
|
|
40
|
+
customError: boolean;
|
|
41
|
+
patternMismatch: boolean;
|
|
42
|
+
rangeOverflow: boolean;
|
|
43
|
+
rangeUnderflow: boolean;
|
|
44
|
+
stepMismatch: boolean;
|
|
45
|
+
tooLong: boolean;
|
|
46
|
+
tooShort: boolean;
|
|
47
|
+
typeMismatch: boolean;
|
|
48
|
+
valid: boolean;
|
|
49
|
+
valueMissing: boolean;
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface IValidityStateArrShort {
|
|
54
|
+
[key: string]: boolean;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface ICustomErrorMessage {
|
|
58
|
+
useCustomError: boolean;
|
|
59
|
+
message: string;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface ICustomErrorMessagesArr {
|
|
63
|
+
[x: string]: ICustomErrorMessage;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface IFormData {
|
|
67
|
+
[x: string]: string | boolean | number | URL | object;
|
|
68
|
+
data: IFieldsInitialState;
|
|
69
|
+
validityState: IValidityStateArrShort;
|
|
70
|
+
isPending: boolean;
|
|
71
|
+
errorCount: number;
|
|
72
|
+
customErrorMessages: ICustomErrorMessagesArr;
|
|
73
|
+
formIsValid: boolean;
|
|
74
|
+
showErrors: boolean;
|
|
75
|
+
submitSuccess: boolean;
|
|
76
|
+
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|