srcdev-nuxt-forms 0.1.0 → 1.0.0

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.
Files changed (87) hide show
  1. package/LICENSE +21 -0
  2. package/assets/styles/brand/_brand.css +150 -0
  3. package/assets/styles/brand/_brand_dark.css +152 -0
  4. package/assets/styles/brand/_palette_dark.css +148 -0
  5. package/assets/styles/brand/_palette_light.css +148 -0
  6. package/assets/styles/brand/_typography.css +176 -0
  7. package/assets/styles/brand/index.css +1 -0
  8. package/assets/styles/forms/index.css +1 -1
  9. package/assets/styles/forms/themes/_default.css +3 -0
  10. package/assets/styles/forms/themes/_error.css +45 -11
  11. package/assets/styles/forms/themes/_ghost.css +42 -10
  12. package/assets/styles/forms/themes/_primary.css +42 -10
  13. package/assets/styles/forms/themes/_secondary.css +42 -10
  14. package/assets/styles/forms/themes/_success.css +42 -11
  15. package/assets/styles/forms/themes/_tertiary.css +42 -10
  16. package/assets/styles/forms/themes/_warning.css +42 -10
  17. package/assets/styles/forms/themes/index.css +1 -0
  18. package/assets/styles/forms/variables/_palette.css +104 -0
  19. package/assets/styles/forms/variables/_theme.css +12 -18
  20. package/assets/styles/forms/variables/index.css +2 -0
  21. package/assets/styles/main.css +2 -0
  22. package/assets/styles/scaffolding/_margin-helpers.css +308 -0
  23. package/assets/styles/scaffolding/_padding-helpers.css +308 -0
  24. package/assets/styles/scaffolding/_page.css +23 -0
  25. package/assets/styles/scaffolding/index.css +3 -0
  26. package/assets/styles/variables/colors/_blue.css +2 -2
  27. package/assets/styles/variables/colors/_gray.css +2 -1
  28. package/assets/styles/variables/colors/_green.css +2 -2
  29. package/assets/styles/variables/colors/_orange.css +2 -2
  30. package/assets/styles/variables/colors/_red.css +2 -2
  31. package/assets/styles/variables/colors/_yellow.css +1 -1
  32. package/components/forms/c12/prop-validators/index.ts +8 -20
  33. package/components/forms/c12/utils.ts +14 -0
  34. package/components/forms/c12/validation-patterns/en.json +12 -0
  35. package/components/forms/form-errors/InputError.vue +177 -0
  36. package/components/forms/input-button/InputButtonCore.vue +33 -109
  37. package/components/forms/input-button/variants/InputButtonConfirm.vue +1 -1
  38. package/components/forms/input-button/variants/InputButtonSubmit.vue +1 -1
  39. package/components/forms/input-checkbox/InputCheckboxCore.vue +263 -0
  40. package/components/forms/input-checkbox/InputCheckboxWithLabel.vue +116 -0
  41. package/components/forms/input-checkbox/variants/MultipleCheckboxes.vue +167 -0
  42. package/components/forms/input-checkbox/variants/SingleCheckbox.vue +172 -0
  43. package/components/forms/input-number/InputNumberCore.vue +184 -0
  44. package/components/forms/input-number/variants/InputNumberDefault.vue +155 -0
  45. package/components/forms/input-radio/InputRadiobuttonCore.vue +212 -0
  46. package/components/forms/input-radio/InputRadiobuttonWithLabel.vue +103 -0
  47. package/components/forms/input-radio/variants/MultipleRadiobuttons.vue +166 -0
  48. package/components/forms/input-range/InputRangeCore.vue +153 -0
  49. package/components/forms/input-range/variants/InputRangeDefault.vue +159 -0
  50. package/components/forms/input-text/InputTextCore.vue +149 -87
  51. package/components/forms/input-text/variants/material/InputPasswordWithLabel.vue +99 -0
  52. package/components/forms/input-text/variants/material/InputTextAsNumberWithLabel.vue +142 -0
  53. package/components/forms/input-text/variants/material/InputTextWithLabel.vue +125 -0
  54. package/components/forms/input-textarea/InputTextareaCore.vue +161 -0
  55. package/components/forms/input-textarea/variants/InputTextareaWithLabel.vue +106 -0
  56. package/components/scaffolding/footer/NavFooter.vue +62 -0
  57. package/components/ui/content-grid/ContentGrid.vue +85 -0
  58. package/composables/useApiRequest.ts +25 -0
  59. package/composables/useErrorMessages.ts +17 -5
  60. package/composables/useFormControl.ts +149 -37
  61. package/composables/useSleep.ts +2 -2
  62. package/composables/useStyleClassPassthrough.ts +30 -0
  63. package/composables/useZodValidation.ts +120 -0
  64. package/layouts/default.vue +26 -16
  65. package/nuxt.config.ts +22 -0
  66. package/package.json +13 -8
  67. package/pages/forms/examples/buttons/index.vue +14 -13
  68. package/pages/forms/examples/material/cssbattle.vue +60 -0
  69. package/pages/forms/examples/material/text-fields.vue +551 -93
  70. package/pages/index.vue +2 -2
  71. package/pages/limit-text.vue +43 -0
  72. package/pages/typography.vue +83 -0
  73. package/server/api/places/list.get.ts +23 -0
  74. package/server/api/textFields.post.ts +37 -0
  75. package/server/api/utils/index.get.ts +20 -0
  76. package/server/data/places/cities.json +43 -0
  77. package/server/data/places/countries.json +55 -0
  78. package/server/data/utils/title.json +49 -0
  79. package/types/types.forms.ts +135 -3
  80. package/types/types.places.ts +8 -0
  81. package/types/types.zodFormControl.ts +21 -0
  82. package/components/forms/input-text/variants/material/InputEmailMaterial.vue +0 -72
  83. package/components/forms/input-text/variants/material/InputPasswordMaterial.vue +0 -88
  84. package/components/forms/input-text/variants/material/InputTextMaterial.vue +0 -75
  85. package/components/forms/input-text/variants/material/InputTextMaterialCore.vue +0 -258
  86. package/composables/useUpdateStyleClassPassthrough.ts +0 -29
  87. package/pages/forms/examples/material/text-fields-compact.vue +0 -136
@@ -1,6 +1,8 @@
1
- import type { IFormData, IFieldsInitialState, ICustomErrorMessage, ICustomErrorMessagesArr } from '@/types/types.forms';
1
+ import type { IFormData, IFieldsInitialState, IFormFieldC12, IApiErrorMessages, ICustomErrorMessage, IErrorMessagesArr } from '@/types/types.forms';
2
+ import { formFieldC12 } from '@/components/forms/c12/utils';
2
3
 
3
- export function useFormControl(fieldsInitialState: IFieldsInitialState | Ref<IFieldsInitialState | null>) {
4
+ // export function useFormControl(name: string = '') {
5
+ export function useFormControl(name: string = '') {
4
6
  let savedInitialState = {};
5
7
 
6
8
  const formData = ref<IFormData>({
@@ -10,24 +12,27 @@ export function useFormControl(fieldsInitialState: IFieldsInitialState | Ref<IFi
10
12
  focusedField: '',
11
13
  isPending: false,
12
14
  errorCount: 0,
13
- customErrorMessages: {},
15
+ errorMessages: {},
16
+ formFieldsC12: {},
14
17
  hasCustomErrorMessages: false,
15
18
  formIsValid: false,
16
- submitSuccess: false,
19
+ submitAttempted: false,
17
20
  submitDisabled: false,
21
+ submitSuccess: false,
22
+ displayErrorMessages: false,
18
23
  });
19
24
 
20
- const initValidationState = async () => {
25
+ const initValidationState = async (fieldsInitialState: IFieldsInitialState | Ref<IFieldsInitialState | null>) => {
21
26
  const fields = Object.keys(fieldsInitialState.value || {});
22
- const state = fields.reduce((acc, field) => {
23
- acc[field] = false;
24
- return acc;
27
+ const state = fields.reduce((accumulatedFields, field) => {
28
+ accumulatedFields[field] = false;
29
+ return accumulatedFields;
25
30
  }, {} as Record<string, boolean>);
26
31
  formData.value.validityState = state;
27
32
  };
28
33
 
29
- const initFormData = async () => {
30
- await initValidationState();
34
+ const initFormData = async (fieldsInitialState: IFieldsInitialState | Ref<IFieldsInitialState | null>) => {
35
+ initValidationState(fieldsInitialState);
31
36
 
32
37
  if (fieldsInitialState !== null) {
33
38
  savedInitialState = toRaw(fieldsInitialState.value) as IFieldsInitialState;
@@ -36,6 +41,19 @@ export function useFormControl(fieldsInitialState: IFieldsInitialState | Ref<IFi
36
41
  return;
37
42
  };
38
43
 
44
+ const initFormFieldsC12 = (name: string, formFieldC12: IFormFieldC12) => {
45
+ formData.value.formFieldsC12[name] = formFieldC12;
46
+ return;
47
+ };
48
+
49
+ const updatePreviousValues = () => {
50
+ console.log(`useFormControl | updatePreviousValues`);
51
+
52
+ Object.keys(formData.value.data).forEach((key) => {
53
+ formData.value.formFieldsC12[key].previousValue = formData.value.data[key];
54
+ });
55
+ };
56
+
39
57
  const getErrorCount = async (updateState: boolean = false) => {
40
58
  await nextTick();
41
59
 
@@ -45,17 +63,26 @@ export function useFormControl(fieldsInitialState: IFieldsInitialState | Ref<IFi
45
63
 
46
64
  if (updateState) {
47
65
  formData.value.submitDisabled = true;
66
+ formData.value.displayErrorMessages = formData.value.errorCount > 0;
67
+ formData.value.submitAttempted = true;
48
68
  }
49
69
 
50
70
  if (formData.value.submitDisabled) {
51
71
  formData.value.submitDisabled = !formData.value.formIsValid;
52
72
  }
53
73
 
74
+ // update fieldHasError ref
75
+ // if (typeof formData.value!.formFieldsC12[name] !== 'undefined') {
76
+ // fieldHasError.value = formData.value!.submitAttempted && !formData.value!.formFieldsC12[name].isValid;
77
+ // } else {
78
+ // fieldHasError.value = false;
79
+ // }
80
+
54
81
  return formData.value.errorCount;
55
82
  };
56
83
 
57
84
  // Function to count items with "useCustomError" set to true
58
- const countItemsWithCustomError = (obj: ICustomErrorMessagesArr) => {
85
+ const countItemsWithCustomError = (obj: IErrorMessagesArr) => {
59
86
  let count = 0;
60
87
 
61
88
  for (const key in obj) {
@@ -70,37 +97,100 @@ export function useFormControl(fieldsInitialState: IFieldsInitialState | Ref<IFi
70
97
  /*
71
98
  * Useage:
72
99
  *
73
- * const { updateCustomErrors } = useFormControl();
100
+ * const { updateErrorMessages } = useFormControl();
74
101
  *
75
102
  * Add/Update entry
76
103
  * const sampleCustomErrorEmail = {
77
104
  * useCustomError: true,
78
105
  * message: "This is a sample custom error for error EMAIL",
79
106
  * };
80
- * updateCustomErrors("email", sampleCustomErrorEmail);
107
+ * updateErrorMessages("email", sampleCustomErrorEmail);
81
108
  */
82
- const updateCustomErrors = (name: string, message: null | string = null, valid: boolean = false) => {
83
- if (message !== null) {
84
- formData.value.validityState[name] = valid;
85
- formData.value.customErrorMessages[name] = {
86
- useCustomError: true,
87
- message,
88
- };
109
+ const updateErrorMessages = async (name: string, message: string = '', valid: boolean = false) => {
110
+ if (!valid) {
111
+ // formData.value.validityState[name] = valid;
112
+ // formData.value.errorMessages[name] = {
113
+ // useCustomError: true,
114
+ // message,
115
+ // };
116
+
117
+ formData.value.formFieldsC12[name].useCustomError = true;
118
+
119
+ // if (typeof message === 'string') {
120
+ // formData.value.formFieldsC12[name].customErrors = message;
121
+ // } else if (typeof message === 'object') {
122
+ // formData.value.formFieldsC12[name].customErrors = message;
123
+ // }
124
+
125
+ formData.value.formFieldsC12[name].customErrors = message;
126
+ formData.value.formFieldsC12[name].isValid = valid;
127
+
128
+ // formData.value.errorMessages[name].useCustomError = true;
129
+ // formData.value.errorMessages[name].message = message;
89
130
  }
90
- formData.value.hasCustomErrorMessages = countItemsWithCustomError(formData.value.customErrorMessages) > 0;
131
+ formData.value.hasCustomErrorMessages = countItemsWithCustomError(formData.value.errorMessages) > 0;
91
132
  };
92
133
 
93
- const resetForm = () => {
94
- formData.value.data = toRaw(fieldsInitialState.value) as IFieldsInitialState;
95
- formData.value.validityState = {};
96
- formData.value.errorCount = 0;
97
- formData.value.isPending = false;
98
- formData.value.customErrorMessages = {};
99
- formData.value.formIsValid = false;
134
+ const useApiErrors = async (errors: IApiErrorMessages) => {
135
+ // Object.keys(errors).forEach((key) => {
136
+ // updateErrorMessages(key, errors[key]);
137
+ // });
138
+
139
+ for (const [key, message] of Object.entries(errors)) {
140
+ // console.log(`${key}: ${message}`);
141
+ updateErrorMessages(key, message);
142
+ }
143
+ };
144
+
145
+ // const resetForm = () => {
146
+ // console.log('resetForm()');
147
+ // formData.value.data = toRaw(fieldsInitialState.value) as IFieldsInitialState;
148
+ // formData.value.validityState = {};
149
+ // formData.value.errorCount = 0;
150
+ // formData.value.isPending = false;
151
+ // formData.value.errorMessages = {};
152
+ // formData.value.formIsValid = false;
153
+ // };
154
+
155
+ const fieldIsDirty = (name: string) => {
156
+ if (typeof formData.value.formFieldsC12[name] !== 'undefined') {
157
+ return formData.value.formFieldsC12[name].isDirty;
158
+ } else {
159
+ return false;
160
+ }
100
161
  };
101
162
 
163
+ // const fieldHasError = (name: string) => {
164
+ // const currentValidityState = formData.value.validityState[name];
165
+
166
+ // if (formData.value.submitAttempted) {
167
+ // return currentValidityState;
168
+ // }
169
+ // return false;
170
+ // };
171
+
172
+ // const fieldHasError = computed({
173
+ // // getter
174
+ // get() {
175
+ // console.log(`fieldHasError getter: ${name}`);
176
+ // if (typeof formData.value!.formFieldsC12[name] !== 'undefined') {
177
+ // return !formData.value!.formFieldsC12[name].isValid;
178
+ // }
179
+ // return formData.value.validityState[name];
180
+ // },
181
+ // // setter
182
+ // set(newValue) {
183
+ // if (formData.value.submitAttempted) {
184
+ // return newValue;
185
+ // }
186
+ // return false;
187
+ // },
188
+ // });
189
+
190
+ // const fieldHasError = ref(false);
191
+
102
192
  const formIsValid = computed(() => {
103
- return formData.value.errorCount === 0;
193
+ return formData.value.formIsValid;
104
194
  });
105
195
 
106
196
  const submitDisabled = computed(() => {
@@ -109,9 +199,12 @@ export function useFormControl(fieldsInitialState: IFieldsInitialState | Ref<IFi
109
199
 
110
200
  // Keep an eye on this for performance issue
111
201
 
112
- watchEffect(() => {
113
- console.log('watchEffect: formData.value', formData.value.validityState);
114
- });
202
+ // const updateFieldValidity = (name: string, valid: boolean) => {
203
+ // console.log(`updateFieldValidity: name:${name} - valid:${valid}`);
204
+ // console.log(formData.value);
205
+ // // formData.value.formFieldsC12[name].isValid = valid;
206
+ // formData.value.validityState[name] = valid;
207
+ // };
115
208
 
116
209
  watch(
117
210
  () => formData.value.validityState,
@@ -121,16 +214,35 @@ export function useFormControl(fieldsInitialState: IFieldsInitialState | Ref<IFi
121
214
  { deep: true }
122
215
  );
123
216
 
124
- onMounted(() => {
125
- initFormData();
126
- });
217
+ watch(
218
+ () => formData.value.formFieldsC12,
219
+ () => {
220
+ formData.value.formFieldsC12;
221
+ },
222
+ { deep: true }
223
+ );
224
+
225
+ watch(
226
+ () => formData.value.isPending,
227
+ (newValue, oldValue) => {
228
+ if (newValue) {
229
+ updatePreviousValues();
230
+ }
231
+ }
232
+ );
127
233
 
128
234
  return {
129
235
  formData,
236
+ initFormData,
237
+ initFormFieldsC12,
130
238
  getErrorCount,
131
- updateCustomErrors,
132
- resetForm,
239
+ updateErrorMessages,
240
+ // resetForm,
133
241
  formIsValid,
134
242
  submitDisabled,
243
+ useApiErrors,
244
+ // fieldHasError,
245
+ fieldIsDirty,
246
+ // updateFieldValidity,
135
247
  };
136
248
  }
@@ -1,5 +1,5 @@
1
- const useSleep = (ms: number) => {
1
+ async function useSleep(ms: number) {
2
2
  return new Promise((resolve) => setTimeout(resolve, ms));
3
- };
3
+ }
4
4
 
5
5
  export default useSleep;
@@ -0,0 +1,30 @@
1
+ export const useStyleClassPassthrough = (styleClassPassthrough: string[]) => {
2
+ const styleClassPassthroughRef = ref(styleClassPassthrough);
3
+
4
+ const elementClasses = computed(() => {
5
+ return styleClassPassthroughRef.value.join(' ');
6
+ });
7
+
8
+ const updateElementClasses = (cssClass: string | string[]) => {
9
+ let cssClasses = [] as string[];
10
+ if (typeof cssClass === 'string') {
11
+ cssClasses = [cssClass];
12
+ } else if (Array.isArray(cssClass)) {
13
+ cssClasses = cssClass;
14
+ }
15
+
16
+ cssClasses.forEach((cssClass) => {
17
+ if (styleClassPassthroughRef.value.includes(cssClass)) {
18
+ styleClassPassthroughRef.value = styleClassPassthroughRef.value.filter((className) => className !== cssClass);
19
+ } else {
20
+ styleClassPassthroughRef.value.push(cssClass);
21
+ }
22
+ });
23
+ };
24
+
25
+ return {
26
+ elementClasses,
27
+ updateElementClasses,
28
+ styleClassPassthroughRef,
29
+ };
30
+ };
@@ -0,0 +1,120 @@
1
+ import { z, ZodError } from 'zod';
2
+ import type { IFormFieldStateObj, ApiErrorResponse } from '@/types/types.forms';
3
+
4
+ const useZodValidation = (formSchema: any) => {
5
+ const zodFormControl = reactive({
6
+ errorCount: 0,
7
+ displayLoader: false,
8
+ submitDisabled: false,
9
+ submitAttempted: false,
10
+ submitSuccessful: false,
11
+ formIsValid: false,
12
+ isPending: false,
13
+ isDisabled: false,
14
+ previousState: {} as Record<string, any>,
15
+ });
16
+
17
+ type formSchema = z.infer<typeof formSchema>;
18
+ const zodErrorObj = ref<z.ZodFormattedError<formSchema> | null>(null);
19
+
20
+ const resetPreviousValues = () => {
21
+ for (const [field] of Object.entries(formSchema.shape)) {
22
+ const previousValue = {
23
+ value: null,
24
+ message: '',
25
+ };
26
+
27
+ zodFormControl.previousState[field] = previousValue;
28
+ }
29
+ };
30
+
31
+ const initZodForm = () => {
32
+ resetPreviousValues();
33
+ };
34
+
35
+ const getErrorCount = (zodErrorObj: Ref<z.ZodFormattedError<formSchema> | null>) => {
36
+ const zodCountErrors = zodErrorObj.value ?? [];
37
+ // @ts-ignore
38
+ delete zodCountErrors._errors;
39
+ const errorCount = Object.keys(zodCountErrors ?? []).length;
40
+ return errorCount;
41
+ };
42
+
43
+ const transformErrorMessages = (errors: any) => {
44
+ const apiErrors = ref({}) as any;
45
+ for (const [key, value] of Object.entries(errors)) {
46
+ const fieldPath = key.split('.').map((key: string) => key.charAt(0).toLowerCase() + key.slice(1));
47
+ apiErrors.value[fieldPath.join('.')] = value;
48
+ }
49
+
50
+ return apiErrors.value;
51
+ };
52
+
53
+ const updatePreviousValue = async (field: string, message: string, state: Record<string, any>) => {
54
+ const previousValue = {
55
+ value: state[field],
56
+ message: message,
57
+ };
58
+ zodFormControl.previousState[field] = previousValue;
59
+ };
60
+
61
+ const pushCustomErrors = async (apiErrorResponse: ApiErrorResponse, state: Record<string, any>) => {
62
+ const apiErrors = transformErrorMessages(apiErrorResponse.data.errors);
63
+
64
+ // 1: Create a ZodError object to hold the issues
65
+ const zodError = new ZodError([]);
66
+
67
+ // 2: Reset previous state values
68
+ resetPreviousValues();
69
+
70
+ // 3: Add issues to the ZodError object
71
+ for (const [path, message] of Object.entries(apiErrors)) {
72
+ zodError.addIssue({
73
+ path: path.split('.'),
74
+ message: message as string,
75
+ code: z.ZodIssueCode.custom,
76
+ });
77
+ await updatePreviousValue(path, message as string, state);
78
+ }
79
+
80
+ zodErrorObj.value = zodError.format();
81
+ zodFormControl.errorCount = getErrorCount(zodErrorObj);
82
+ zodFormControl.formIsValid = zodFormControl.errorCount === 0;
83
+ zodFormControl.displayLoader = false;
84
+ zodFormControl.submitAttempted = true;
85
+ return zodErrorObj.value;
86
+ };
87
+
88
+ const doZodValidate = async (state: Record<string, any>) => {
89
+ const valid = formSchema.safeParse(toRaw(state));
90
+ if (!valid.success) {
91
+ zodErrorObj.value = valid.error.format();
92
+ } else {
93
+ zodErrorObj.value = null;
94
+ }
95
+
96
+ zodFormControl.errorCount = getErrorCount(zodErrorObj);
97
+ zodFormControl.formIsValid = valid.success;
98
+
99
+ return valid.success;
100
+ };
101
+
102
+ const fieldMaxLength = (name: string) => {
103
+ const fieldSchema = formSchema.shape[name];
104
+ if (fieldSchema instanceof z.ZodString) {
105
+ return fieldSchema._def.checks.find((check) => check.kind === 'max')?.value || null;
106
+ }
107
+ return null;
108
+ };
109
+
110
+ return {
111
+ initZodForm,
112
+ zodFormControl,
113
+ zodErrorObj,
114
+ pushCustomErrors,
115
+ doZodValidate,
116
+ fieldMaxLength,
117
+ };
118
+ };
119
+
120
+ export default useZodValidation;
@@ -4,31 +4,22 @@
4
4
  <h1><NuxtLink to="/">Home</NuxtLink></h1>
5
5
  <ul class="flex-group">
6
6
  <li>
7
- <NuxtLink to="/forms/examples/material/text-fields"
8
- >Material UI text fields</NuxtLink
9
- >
7
+ <NuxtLink to="/typography" class="link-normal">Typography</NuxtLink>
10
8
  </li>
11
9
  <li>
12
- <NuxtLink to="/forms/examples/material/text-fields-compact"
13
- >Material UI text fields (compact)</NuxtLink
14
- >
10
+ <NuxtLink to="/forms/examples/material/text-fields" class="link-normal">Material UI text fields</NuxtLink>
11
+ </li>
12
+ <li>
13
+ <NuxtLink to="/forms/examples/buttons" class="link-normal">Buttons</NuxtLink>
15
14
  </li>
16
15
  </ul>
17
16
  </div>
18
- <h2>Buttons</h2>
19
- <ul class="flex-group">
20
- <li>
21
- <NuxtLink to="/forms/examples/buttons">Buttons</NuxtLink>
22
- </li>
23
- </ul>
24
17
 
25
- <div>
18
+ <div class="page-layout-content">
26
19
  <slot name="layout-content"></slot>
27
20
  </div>
28
21
 
29
- <div>
30
- <h1>Footer</h1>
31
- </div>
22
+ <NavFooter />
32
23
  </div>
33
24
  </template>
34
25
 
@@ -38,6 +29,21 @@ useHead({
38
29
  class: 'body-default',
39
30
  id: 'body',
40
31
  },
32
+ // link: [
33
+ // {
34
+ // rel: 'preconnect',
35
+ // href: 'https://fonts.googleapis.com',
36
+ // },
37
+ // {
38
+ // rel: 'preconnect',
39
+ // href: 'https://fonts.gstatic.com',
40
+ // crossorigin: 'use-credentials',
41
+ // },
42
+ // {
43
+ // href: 'https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap',
44
+ // rel: 'stylesheet',
45
+ // },
46
+ // ],
41
47
  });
42
48
  </script>
43
49
 
@@ -47,6 +53,10 @@ useHead({
47
53
  grid-template-rows: auto 1fr auto;
48
54
  }
49
55
 
56
+ .page-layout-content {
57
+ container: content / inline-size;
58
+ }
59
+
50
60
  .flex-group {
51
61
  align-items: flex-start;
52
62
  display: flex;
package/nuxt.config.ts CHANGED
@@ -4,7 +4,26 @@ const { resolve } = createResolver(import.meta.url);
4
4
 
5
5
  export default defineNuxtConfig({
6
6
  devtools: { enabled: true },
7
+
8
+ app: {
9
+ head: {
10
+ htmlAttrs: {
11
+ lang: 'en',
12
+ },
13
+ titleTemplate: '%s - Website name',
14
+ meta: [{ charset: 'utf-8' }, { name: 'viewport', content: 'width=device-width, initial-scale=1' }],
15
+ },
16
+ pageTransition: {
17
+ name: 'page',
18
+ mode: 'out-in',
19
+ },
20
+ layoutTransition: {
21
+ name: 'layout',
22
+ mode: 'out-in',
23
+ },
24
+ },
7
25
  css: [resolve('./assets/styles/main.css')],
26
+
8
27
  runtimeConfig: {
9
28
  public: {
10
29
  validatorLocale: 'en-GB',
@@ -12,10 +31,13 @@ export default defineNuxtConfig({
12
31
  },
13
32
 
14
33
  modules: ['@nuxt/icon'],
34
+
15
35
  components: [
16
36
  {
17
37
  path: './components',
18
38
  pathPrefix: false,
19
39
  },
20
40
  ],
41
+
42
+ compatibilityDate: '2024-07-13',
21
43
  });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "srcdev-nuxt-forms",
3
3
  "type": "module",
4
- "version": "0.1.0",
4
+ "version": "1.0.0",
5
5
  "main": "./nuxt.config.ts",
6
6
  "scripts": {
7
7
  "reinstall": "rm -rf node_modules && npm install",
@@ -14,12 +14,17 @@
14
14
  "release": "release-it"
15
15
  },
16
16
  "devDependencies": {
17
- "@iconify-json/material-symbols": "^1.1.83",
18
- "@nuxt/eslint-config": "^0.3.13",
19
- "@nuxt/icon": "^1.0.0",
20
- "eslint": "^9.5.0",
21
- "nuxt": "^3.12.2",
22
- "release-it": "^17.4.0",
23
- "typescript": "^5.5.2"
17
+ "@iconify-json/gridicons": "1.2.1",
18
+ "@iconify-json/material-symbols": "1.2.5",
19
+ "@iconify-json/radix-icons": "1.2.1",
20
+ "@nuxt/eslint-config": "0.6.1",
21
+ "@nuxt/icon": "1.6.1",
22
+ "eslint": "9.13.0",
23
+ "nuxt": "3.13.2",
24
+ "release-it": "17.10.0",
25
+ "typescript": "5.6.3"
26
+ },
27
+ "dependencies": {
28
+ "zod": "3.23.8"
24
29
  }
25
30
  }
@@ -9,25 +9,25 @@
9
9
  <p>Themes switcher</p>
10
10
  <ul class="flex-group">
11
11
  <li>
12
- <InputButtonSubmit @click.stop.prevent="swapTheme('primary')" :is-pending="false" button-text="Primary" theme="primary" size="normal" />
12
+ <InputButtonSubmit type="button" @click.stop.prevent="swapTheme('primary')" :is-pending="false" button-text="Primary" theme="primary" size="normal" />
13
13
  </li>
14
14
  <li>
15
- <InputButtonSubmit @click.stop.prevent="swapTheme('secondary')" :is-pending="false" button-text="Secondary" theme="secondary" size="normal" />
15
+ <InputButtonSubmit type="button" @click.stop.prevent="swapTheme('secondary')" :is-pending="false" button-text="Secondary" theme="secondary" size="normal" />
16
16
  </li>
17
17
  <li>
18
- <InputButtonSubmit @click.stop.prevent="swapTheme('tertiary')" :is-pending="false" button-text="Tertiary" theme="tertiary" size="normal" />
18
+ <InputButtonSubmit type="button" @click.stop.prevent="swapTheme('tertiary')" :is-pending="false" button-text="Tertiary" theme="tertiary" size="normal" />
19
19
  </li>
20
20
  <li>
21
- <InputButtonSubmit @click.stop.prevent="swapTheme('warning')" :is-pending="false" button-text="Warning" theme="warning" size="normal" />
21
+ <InputButtonSubmit type="button" @click.stop.prevent="swapTheme('warning')" :is-pending="false" button-text="Warning" theme="warning" size="normal" />
22
22
  </li>
23
23
  <li>
24
- <InputButtonSubmit @click.stop.prevent="swapTheme('success')" :is-pending="false" button-text="Success" theme="success" size="normal" />
24
+ <InputButtonSubmit type="button" @click.stop.prevent="swapTheme('success')" :is-pending="false" button-text="Success" theme="success" size="normal" />
25
25
  </li>
26
26
  <li>
27
- <InputButtonSubmit @click.stop.prevent="swapTheme('error')" :is-pending="false" button-text="Error" theme="error" size="normal" />
27
+ <InputButtonSubmit type="button" @click.stop.prevent="swapTheme('error')" :is-pending="false" button-text="Error" theme="error" size="normal" />
28
28
  </li>
29
29
  <li>
30
- <InputButtonSubmit @click.stop.prevent="swapTheme('ghost')" :is-pending="false" button-text="Ghost" theme="ghost" size="normal" />
30
+ <InputButtonSubmit type="button" @click.stop.prevent="swapTheme('ghost')" :is-pending="false" button-text="Ghost" theme="ghost" size="normal" />
31
31
  </li>
32
32
  </ul>
33
33
 
@@ -35,13 +35,13 @@
35
35
  <template #default>
36
36
  <form @submit.prevent="submitForm">
37
37
  <div class="flex-group">
38
- <InputButtonSubmit @click.stop.prevent="submitForm" :is-pending="false" button-text="Submit" :theme size="x-small" />
38
+ <InputButtonSubmit type="button" @click.stop.prevent="submitForm" :is-pending="false" button-text="Submit" :theme size="x-small" />
39
39
 
40
- <InputButtonSubmit @click.stop.prevent="submitForm" :is-pending="false" button-text="Submit" :theme size="small" />
40
+ <InputButtonSubmit type="button" @click.stop.prevent="submitForm" :is-pending="false" button-text="Submit" :theme size="small" />
41
41
 
42
- <InputButtonSubmit @click.stop.prevent="submitForm" :is-pending="false" button-text="Submit" :theme size="normal" />
43
- <InputButtonSubmit @click.stop.prevent="submitForm" :is-pending="false" button-text="Submit" :theme size="medium" />
44
- <InputButtonSubmit @click.stop.prevent="submitForm" :is-pending="false" button-text="Submit" :theme size="large" />
42
+ <InputButtonSubmit type="button" @click.stop.prevent="submitForm" :is-pending="false" button-text="Submit" :theme size="normal" />
43
+ <InputButtonSubmit type="button" @click.stop.prevent="submitForm" :is-pending="false" button-text="Submit" :theme size="medium" />
44
+ <InputButtonSubmit type="button" @click.stop.prevent="submitForm" :is-pending="false" button-text="Submit" :theme size="large" />
45
45
  </div>
46
46
 
47
47
  <div class="flex-group">
@@ -122,7 +122,8 @@ const fieldsInitialState = ref<IFieldsInitialState>({
122
122
  });
123
123
 
124
124
  // Setup formData
125
- const { formData, getErrorCount, updateCustomErrors, resetForm, formIsValid, submitDisabled } = useFormControl(fieldsInitialState);
125
+ const { formData, initFormData, getErrorCount, updateErrorMessages, formIsValid, submitDisabled, useApiErrors } = useFormControl();
126
+ await initFormData(fieldsInitialState);
126
127
 
127
128
  const submitForm = async () => {
128
129
  await getErrorCount(true);