srcdev-nuxt-forms 0.0.12 → 0.0.14
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/.editorconfig +12 -0
- package/.eslintignore +7 -0
- package/.eslintrc.cjs +12 -0
- package/.nuxtrc +1 -0
- package/.prettierrc +4 -0
- package/.release-it.json +6 -0
- package/composables/useErrorMessages.ts +47 -0
- package/composables/useFormControl.ts +116 -0
- package/composables/useSleep.ts +5 -0
- package/nuxt.config.ts +6 -6
- package/package.json +1 -8
- package/pages/index.vue +5 -5
- package/tsconfig.json +3 -0
- package/types/types.forms.ts +76 -0
- /package/components/nuxt-forms/scaffolding/{FormField.vue → InputField.vue} +0 -0
package/.editorconfig
ADDED
package/.eslintignore
ADDED
package/.eslintrc.cjs
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
"root": true,
|
|
3
|
+
"extends": [
|
|
4
|
+
"@nuxtjs/eslint-config-typescript",
|
|
5
|
+
"plugin:prettier/recommended"
|
|
6
|
+
],
|
|
7
|
+
"rules": {
|
|
8
|
+
"vue/multi-word-component-names": "off",
|
|
9
|
+
"vue/no-multiple-template-root": "off",
|
|
10
|
+
"@typescript-eslint/no-unused-vars": "off"
|
|
11
|
+
}
|
|
12
|
+
};
|
package/.nuxtrc
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
typescript.includeWorkspace = true
|
package/.prettierrc
ADDED
package/.release-it.json
ADDED
|
@@ -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
|
@@ -5,10 +5,10 @@ const { resolve } = createResolver(import.meta.url)
|
|
|
5
5
|
export default defineNuxtConfig({
|
|
6
6
|
devtools: { enabled: true },
|
|
7
7
|
css: [resolve('./assets/styles/main.css')],
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
components: [
|
|
9
|
+
{
|
|
10
|
+
path: '~/components',
|
|
11
|
+
pathPrefix: false,
|
|
12
|
+
},
|
|
13
|
+
],
|
|
14
14
|
})
|
package/package.json
CHANGED
|
@@ -1,15 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "srcdev-nuxt-forms",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.14",
|
|
5
5
|
"main": "./nuxt.config.ts",
|
|
6
|
-
"files": [
|
|
7
|
-
"assets",
|
|
8
|
-
"components",
|
|
9
|
-
"layouts",
|
|
10
|
-
"pages",
|
|
11
|
-
"nuxt.config.ts"
|
|
12
|
-
],
|
|
13
6
|
"scripts": {
|
|
14
7
|
"dev": "nuxi dev",
|
|
15
8
|
"build": "nuxt build",
|
package/pages/index.vue
CHANGED
|
@@ -5,19 +5,19 @@
|
|
|
5
5
|
<div>
|
|
6
6
|
<h1>Sample form page</h1>
|
|
7
7
|
|
|
8
|
-
<
|
|
8
|
+
<FormWrapper width="medium">
|
|
9
9
|
<template #default>
|
|
10
10
|
<form>
|
|
11
11
|
<p>Form content</p>
|
|
12
|
-
<
|
|
12
|
+
<InputField width="wide" :has-gutter="true">
|
|
13
13
|
<template #default>
|
|
14
14
|
<p>Input text</p>
|
|
15
|
-
<
|
|
15
|
+
<InputText />
|
|
16
16
|
</template>
|
|
17
|
-
</
|
|
17
|
+
</InputField>
|
|
18
18
|
</form>
|
|
19
19
|
</template>
|
|
20
|
-
</
|
|
20
|
+
</FormWrapper>
|
|
21
21
|
</div>
|
|
22
22
|
</template>
|
|
23
23
|
</NuxtLayout>
|
package/tsconfig.json
ADDED
|
@@ -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
|