srcdev-nuxt-forms 0.2.0 → 1.0.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.
Files changed (77) hide show
  1. package/assets/styles/brand/_brand.css +150 -0
  2. package/assets/styles/brand/_brand_dark.css +152 -0
  3. package/assets/styles/brand/_palette_dark.css +148 -0
  4. package/assets/styles/brand/_palette_light.css +148 -0
  5. package/assets/styles/brand/_typography.css +176 -0
  6. package/assets/styles/brand/index.css +1 -0
  7. package/assets/styles/forms/index.css +1 -2
  8. package/assets/styles/forms/themes/_default.css +3 -0
  9. package/assets/styles/forms/themes/_error.css +45 -11
  10. package/assets/styles/forms/themes/_ghost.css +42 -10
  11. package/assets/styles/forms/themes/_primary.css +42 -12
  12. package/assets/styles/forms/themes/_secondary.css +42 -12
  13. package/assets/styles/forms/themes/_success.css +42 -11
  14. package/assets/styles/forms/themes/_tertiary.css +42 -10
  15. package/assets/styles/forms/themes/_warning.css +42 -10
  16. package/assets/styles/forms/themes/index.css +1 -0
  17. package/assets/styles/forms/variables/_palette.css +104 -0
  18. package/assets/styles/forms/variables/_theme.css +1 -1
  19. package/assets/styles/forms/variables/index.css +2 -0
  20. package/assets/styles/main.css +2 -0
  21. package/assets/styles/scaffolding/_margin-helpers.css +308 -0
  22. package/assets/styles/scaffolding/_padding-helpers.css +308 -0
  23. package/assets/styles/scaffolding/_page.css +23 -0
  24. package/assets/styles/scaffolding/index.css +3 -0
  25. package/assets/styles/variables/colors/_blue.css +2 -2
  26. package/assets/styles/variables/colors/_gray.css +1 -1
  27. package/assets/styles/variables/colors/_green.css +2 -2
  28. package/assets/styles/variables/colors/_orange.css +2 -2
  29. package/assets/styles/variables/colors/_red.css +2 -2
  30. package/assets/styles/variables/colors/_yellow.css +1 -1
  31. package/components/forms/form-errors/InputError.vue +82 -37
  32. package/components/forms/input-button/InputButtonCore.vue +25 -104
  33. package/components/forms/input-checkbox/InputCheckboxCore.vue +37 -181
  34. package/components/forms/input-checkbox/InputCheckboxWithLabel.vue +42 -51
  35. package/components/forms/input-checkbox/variants/MultipleCheckboxes.vue +42 -69
  36. package/components/forms/input-checkbox/variants/SingleCheckbox.vue +126 -111
  37. package/components/forms/input-number/InputNumberCore.vue +184 -0
  38. package/components/forms/input-number/variants/InputNumberDefault.vue +155 -0
  39. package/components/forms/input-radio/InputRadiobuttonCore.vue +212 -0
  40. package/components/forms/input-radio/InputRadiobuttonWithLabel.vue +103 -0
  41. package/components/forms/input-radio/variants/MultipleRadiobuttons.vue +166 -0
  42. package/components/forms/input-range/InputRangeCore.vue +70 -88
  43. package/components/forms/input-range/variants/InputRangeDefault.vue +74 -46
  44. package/components/forms/input-text/InputTextCore.vue +141 -109
  45. package/components/forms/input-text/variants/material/InputPasswordWithLabel.vue +99 -0
  46. package/components/forms/input-text/variants/material/InputTextAsNumberWithLabel.vue +142 -0
  47. package/components/forms/input-text/variants/material/InputTextWithLabel.vue +125 -0
  48. package/components/forms/input-textarea/InputTextareaCore.vue +96 -105
  49. package/components/forms/input-textarea/variants/InputTextareaWithLabel.vue +106 -0
  50. package/components/scaffolding/footer/NavFooter.vue +62 -0
  51. package/composables/useApiRequest.ts +25 -0
  52. package/composables/useFormControl.ts +2 -0
  53. package/composables/useSleep.ts +2 -2
  54. package/composables/useStyleClassPassthrough.ts +30 -0
  55. package/composables/useZodValidation.ts +120 -0
  56. package/layouts/default.vue +21 -5
  57. package/package.json +13 -9
  58. package/pages/forms/examples/material/cssbattle.vue +60 -0
  59. package/pages/forms/examples/material/text-fields.vue +375 -153
  60. package/pages/index.vue +2 -2
  61. package/pages/typography.vue +83 -0
  62. package/server/data/places/cities.json +7 -1
  63. package/types/types.forms.ts +102 -0
  64. package/types/types.zodFormControl.ts +21 -0
  65. package/assets/styles/forms/utils/_a11y.css +0 -5
  66. package/assets/styles/forms/utils/index.css +0 -1
  67. package/components/forms/input-radio/InputRadioCore.vue +0 -226
  68. package/components/forms/input-radio/InputRadioWithLabel.vue +0 -118
  69. package/components/forms/input-radio/variants/MultipleRadio.vue +0 -183
  70. package/components/forms/input-radio/variants/SingleRadio.vue +0 -131
  71. package/components/forms/input-text/variants/material/InputEmailMaterial.vue +0 -72
  72. package/components/forms/input-text/variants/material/InputPasswordMaterial.vue +0 -114
  73. package/components/forms/input-text/variants/material/InputTextMaterial.vue +0 -68
  74. package/components/forms/input-text/variants/material/InputTextMaterialCore.vue +0 -313
  75. package/components/forms/input-textarea/variants/material/InputTextareaMaterial.vue +0 -75
  76. package/components/forms/input-textarea/variants/material/InputTextareaMaterialCore.vue +0 -290
  77. package/composables/useUpdateStyleClassPassthrough.ts +0 -29
@@ -3,18 +3,8 @@
3
3
  <NuxtLayout name="default">
4
4
  <template #layout-content>
5
5
  <div>
6
- <h1>Material UI text fields ({{ compact ? 'compact' : 'default' }})</h1>
7
- <ul class="flex-group">
8
- <li>
9
- <InputButtonSubmit type="button" @click.stop.prevent="swapCompact(false)" button-text="Use Default UI" theme="secondary" size="normal" />
10
- </li>
11
- <li>
12
- <InputButtonSubmit type="button" @click.stop.prevent="swapCompact(true)" button-text="Use Compact UI" theme="secondary" size="normal" />
13
- </li>
14
- </ul>
15
-
16
- <p>Example test fields in default material UI</p>
17
- <p>Use 'test@test.com' to trigger server errors</p>
6
+ <h1 class="header-1">Material UI text fields ({{ compact ? 'compact' : 'default' }})</h1>
7
+ <p class="body-normal">Use 'test@test.com' to trigger server errors</p>
18
8
  </div>
19
9
  <ContentGrid>
20
10
  <template #slot1>
@@ -25,77 +15,148 @@
25
15
  <div aria-live="assertive" id="aria-live-message"></div>
26
16
  <FormField width="wide" :has-gutter="false">
27
17
  <template #default>
28
- <InputEmailMaterial
18
+ <InputTextWithLabel
19
+ v-model="state.emailAddress"
20
+ type="email"
21
+ :maxlength="fieldMaxLength('email')"
29
22
  id="emailAddress"
30
23
  name="emailAddress"
31
- validation="emailaddress"
24
+ placeholder="eg. name@domain.com"
25
+ label="Email address"
26
+ :errorMessage="formErrors?.emailAddress?._errors[0] ?? ''"
27
+ :fieldHasError="Boolean(zodFormControl.submitAttempted && formErrors?.emailAddress)"
32
28
  :required="true"
33
- :c12="{
34
- label: 'Your Email Address',
35
- placeholder: 'eg. joe@example.com',
36
- errorMessage: 'Please enter a valid email address',
37
- }"
38
- v-model="formData"
39
- theme="secondary"
40
- :compact
41
- />
29
+ :styleClassPassthrough="['style-1', 'style-2']"
30
+ theme="primary"
31
+ >
32
+ <template #left>
33
+ <Icon name="radix-icons:envelope-closed" class="icon" />
34
+ </template>
35
+ </InputTextWithLabel>
42
36
  </template>
43
37
  </FormField>
44
38
 
45
39
  <FormField width="wide" :has-gutter="false">
46
40
  <template #default>
47
- <InputTextMaterial
41
+ <InputTextWithLabel
42
+ v-model="state.username"
43
+ type="text"
44
+ :maxlength="fieldMaxLength('username')"
48
45
  id="username"
49
46
  name="username"
50
- validation="username"
47
+ placeholder="eg. name@domain.com"
48
+ label="Username"
49
+ :errorMessage="formErrors?.username?._errors[0] ?? ''"
50
+ :fieldHasError="Boolean(zodFormControl.submitAttempted && formErrors?.username)"
51
51
  :required="true"
52
- :c12="{
53
- label: 'Your Username',
54
- placeholder: 'eg. YourUserName',
55
- errorMessage: 'Please enter a valid username',
56
- }"
57
- v-model="formData"
58
- theme="secondary"
59
- :compact
60
- />
52
+ :styleClassPassthrough="['style-1', 'style-2']"
53
+ theme="primary"
54
+ >
55
+ <template #left>
56
+ <Icon name="radix-icons:person" class="icon" />
57
+ </template>
58
+ </InputTextWithLabel>
61
59
  </template>
62
60
  </FormField>
63
61
 
64
62
  <FormField width="wide" :has-gutter="false">
65
63
  <template #default>
66
- <InputPasswordMaterial
64
+ <InputPasswordWithLabel
65
+ v-model="state.password"
66
+ :maxlength="fieldMaxLength('password')"
67
67
  id="password"
68
68
  name="password"
69
- validation="password"
69
+ placeholder="eg. a mixure of numbers and letters"
70
+ label="Password"
71
+ :errorMessage="formErrors?.password?._errors[0] ?? ''"
72
+ :fieldHasError="Boolean(zodFormControl.submitAttempted && formErrors?.password)"
70
73
  :required="true"
71
- :c12="{
72
- label: 'Password',
73
- placeholder: 'eg. Your5illYPa55w0rd',
74
- errorMessage: 'Please enter a valid password',
75
- }"
76
- v-model="formData"
77
- theme="secondary"
78
- :compact
79
- />
74
+ :styleClassPassthrough="['style-1', 'style-2']"
75
+ theme="primary"
76
+ >
77
+ <template #right>
78
+ <Icon name="radix-icons:eye-open" class="icon" />
79
+ </template>
80
+ </InputPasswordWithLabel>
80
81
  </template>
81
82
  </FormField>
82
83
 
83
84
  <FormField width="wide" :has-gutter="false">
84
85
  <template #default>
85
- <InputTextareaMaterial
86
+ <InputTextareaWithLabel
87
+ v-model="state.message"
88
+ :maxlength="fieldMaxLength('message')"
86
89
  id="message"
87
90
  name="message"
88
- validation="message"
91
+ placeholder="Type your message here"
92
+ label="Your mesage"
93
+ :errorMessage="formErrors?.message?._errors[0] ?? ''"
94
+ :fieldHasError="Boolean(zodFormControl.submitAttempted && formErrors?.message)"
89
95
  :required="true"
90
- :c12="{
91
- label: 'Message',
92
- placeholder: 'eg. Type something here',
93
- errorMessage: 'Bad characters in message',
94
- }"
95
- v-model="formData"
96
- theme="secondary"
97
- :compact
98
- />
96
+ :styleClassPassthrough="['style-1', 'style-2']"
97
+ >
98
+ </InputTextareaWithLabel>
99
+ </template>
100
+ </FormField>
101
+
102
+ <FormField width="wide" :has-gutter="false">
103
+ <template #default>
104
+ <InputTextAsNumberWithLabel
105
+ v-model.number="state.count2"
106
+ :maxlength="fieldMaxLength('count2')"
107
+ :min="25"
108
+ :max="75"
109
+ :step="5"
110
+ id="count2"
111
+ name="count2"
112
+ placeholder="eg. 10"
113
+ label="How many things? Between 25 & 75"
114
+ :errorMessage="formErrors?.count2?._errors[0] ?? ''"
115
+ :fieldHasError="Boolean(zodFormControl.submitAttempted && formErrors?.count2)"
116
+ :required="true"
117
+ :styleClassPassthrough="['style-1', 'style-2']"
118
+ theme="primary"
119
+ >
120
+ <template #description>
121
+ <p class="label-description">Input type="text" inputmode="numeric"</p>
122
+ </template>
123
+ <template #left>
124
+ <Icon name="gridicons:minus-small" class="icon" />
125
+ </template>
126
+ <template #right>
127
+ <Icon name="gridicons:plus-small" class="icon" />
128
+ </template>
129
+ </InputTextAsNumberWithLabel>
130
+ </template>
131
+ </FormField>
132
+
133
+ <FormField width="wide" :has-gutter="false">
134
+ <template #default>
135
+ <InputNumberDefault
136
+ id="count"
137
+ name="count"
138
+ label="How many things? Between 25 & 75 , step 5"
139
+ :min="25"
140
+ :max="75"
141
+ :step="5"
142
+ placeholder="eg. What\'s your count?"
143
+ :errorMessage="formErrors?.count?._errors[0] ?? ''"
144
+ :fieldHasError="Boolean(zodFormControl.submitAttempted && formErrors?.count)"
145
+ :required="true"
146
+ :styleClassPassthrough="['count-1', 'count-2']"
147
+ v-model.number="state.count"
148
+ theme="primary"
149
+ >
150
+ <template #description>
151
+ <p class="label-description">Input type="number" inputmode="numeric"</p>
152
+ </template>
153
+ <template #left>
154
+ <Icon name="gridicons:minus-small" class="icon" />
155
+ </template>
156
+ <template #right>
157
+ <Icon name="gridicons:plus-small" class="icon" />
158
+ </template>
159
+ </InputNumberDefault>
99
160
  </template>
100
161
  </FormField>
101
162
 
@@ -104,28 +165,66 @@
104
165
  <InputRangeDefault
105
166
  id="score"
106
167
  name="score"
168
+ label="Score between 0 & 100"
107
169
  :min="0"
108
170
  :max="100"
109
- :step="1"
171
+ :step="10"
172
+ placeholder="eg. What\'s your score?"
173
+ :errorMessage="formErrors?.score?._errors[0] ?? ''"
174
+ :fieldHasError="Boolean(zodFormControl.submitAttempted && formErrors?.score)"
110
175
  :required="true"
111
- validation="positiveNumber0to100"
112
- :c12="{
113
- label: 'Score between 0 & 100',
114
- placeholder: 'eg. What\'s your score?',
115
- errorMessage: 'Score between 0 & 100',
116
- }"
117
- v-model="formData"
118
- theme="secondary"
176
+ :styleClassPassthrough="['style-1', 'style-2']"
177
+ v-model.number="state.score"
178
+ theme="primary"
119
179
  >
120
180
  <template #description>
121
181
  <p class="label-description">This is a description of what the user is required to do</p>
122
182
  </template>
123
- <template #left>&lt;</template>
124
- <template #right>&gt;</template>
183
+ <template #datalist>
184
+ <datalist class="input-range-datalist" id="score-datalist">
185
+ <option value="0" label="Rubbish!"></option>
186
+ <option value="25" label="Below par"></option>
187
+ <option value="50" label="Average"></option>
188
+ <option value="75" label="Quite good"></option>
189
+ <option value="100" label="Excellent"></option>
190
+ </datalist>
191
+ </template>
192
+ <template #left>
193
+ <Icon name="gridicons:minus-small" class="icon" />
194
+ </template>
195
+ <template #right>
196
+ <Icon name="gridicons:plus-small" class="icon" />
197
+ </template>
125
198
  </InputRangeDefault>
126
199
  </template>
127
200
  </FormField>
128
201
 
202
+ <FormField v-if="titleData !== null" width="wide" :has-gutter="false">
203
+ <template #default>
204
+ <MultipleRadiobuttons
205
+ id="title"
206
+ name="title"
207
+ legend="What is your title"
208
+ :required="true"
209
+ label="Check one"
210
+ placeholder="eg. Type something here"
211
+ :errorMessage="formErrors?.title?._errors[0] ?? ''"
212
+ :fieldHasError="Boolean(zodFormControl.submitAttempted && formErrors?.title)"
213
+ v-model="state.title"
214
+ v-model:fieldData="titleData"
215
+ size="normal"
216
+ checkbox-style="cross"
217
+ checkbox-appearance="with-decorator"
218
+ optionsLayout="equal-widths"
219
+ theme="primary"
220
+ >
221
+ <template #description>
222
+ <p class="label-description">This is description: optionsLayout = 'equal-widths/inline'</p>
223
+ </template>
224
+ </MultipleRadiobuttons>
225
+ </template>
226
+ </FormField>
227
+
129
228
  <FormField v-if="citiesData !== null" width="wide" :has-gutter="false">
130
229
  <template #default>
131
230
  <MultipleCheckboxes
@@ -133,17 +232,17 @@
133
232
  name="cities"
134
233
  legend="Choose a location"
135
234
  :required="true"
136
- :c12="{
137
- label: 'Check all Cities you like',
138
- placeholder: 'eg. Type something here',
139
- errorMessage: 'Please choose at least 1 location',
140
- }"
141
- v-model="formData"
235
+ label="Check all Cities you like"
236
+ placeholder="eg. Type something here"
237
+ :errorMessage="formErrors?.cities?._errors[0] ?? ''"
238
+ :fieldHasError="Boolean(zodFormControl.submitAttempted && formErrors?.cities)"
239
+ v-model="state.cities"
142
240
  v-model:fieldData="citiesData"
143
- theme="secondary"
144
241
  size="normal"
145
- checkbox-style="check"
242
+ checkbox-style="cross"
146
243
  checkbox-appearance="with-decorator"
244
+ optionsLayout="inline"
245
+ theme="primary"
147
246
  >
148
247
  <template #description>
149
248
  <p class="label-description">This is description: optionsLayout = 'equal-widths'</p>
@@ -159,18 +258,17 @@
159
258
  name="countries"
160
259
  legend="Choose a country"
161
260
  :required="true"
162
- :c12="{
163
- label: 'Check all countries you like',
164
- placeholder: 'eg. Choose some locations',
165
- errorMessage: 'Please select a country',
166
- }"
167
- v-model="formData"
261
+ label="Check all Countries you like"
262
+ placeholder="eg. Type something here"
263
+ :errorMessage="formErrors?.countries?._errors[0] ?? ''"
264
+ :fieldHasError="Boolean(zodFormControl.submitAttempted && formErrors?.countries)"
265
+ v-model="state.countries"
168
266
  v-model:fieldData="countriesData"
169
- theme="secondary"
170
267
  size="normal"
171
- options-layout="inline"
172
- checkbox-style="cross"
268
+ checkbox-style="check"
173
269
  checkbox-appearance="with-decorator"
270
+ optionsLayout="equal-widths"
271
+ theme="primary"
174
272
  >
175
273
  <template #description>
176
274
  <p class="label-description">This is description: optionsLayout = 'inline'</p>
@@ -179,29 +277,46 @@
179
277
  </template>
180
278
  </FormField>
181
279
 
182
- <FormField v-if="titleData !== null" width="wide" :has-gutter="false">
280
+ <FormField width="wide" :has-gutter="false">
183
281
  <template #default>
184
- <MultipleRadio
185
- id="title"
186
- name="title"
187
- legend="Choose a title"
282
+ <SingleCheckbox
283
+ id="agreed"
284
+ name="agreed"
285
+ legend="I agree (label with description)"
286
+ label="Click to agree to something"
188
287
  :required="true"
189
- :c12="{
190
- label: 'Check all title you like',
191
- placeholder: 'eg. Choose some title',
192
- errorMessage: 'Please select a title',
193
- }"
194
- v-model="formData"
195
- v-model:fieldData="titleData"
196
- theme="secondary"
288
+ :errorMessage="formErrors?.agreed?._errors[0] ?? ''"
289
+ :fieldHasError="Boolean(zodFormControl.submitAttempted && formErrors?.agreed)"
290
+ v-model="state.agreed"
197
291
  size="normal"
198
- options-layout="equal-widths"
199
- radio-appearance="with-decorator"
292
+ checkbox-style="check"
293
+ checkbox-appearance="with-decorator"
294
+ theme="primary"
200
295
  >
201
296
  <template #description>
202
- <p class="label-description">This is description: optionsLayout = 'equal-widths'</p>
297
+ <p class="label-description">You must <strong>agree</strong> to continue</p>
203
298
  </template>
204
- </MultipleRadio>
299
+ </SingleCheckbox>
300
+ </template>
301
+ </FormField>
302
+
303
+ <FormField width="wide" :has-gutter="false">
304
+ <template #default>
305
+ <SingleCheckbox
306
+ id="agree"
307
+ name="agree"
308
+ legend="I agree (label no description)"
309
+ label="Click to agree to something"
310
+ :required="true"
311
+ :errorMessage="formErrors?.agree?._errors[0] ?? ''"
312
+ :fieldHasError="Boolean(zodFormControl.submitAttempted && formErrors?.agree)"
313
+ v-model="state.agree"
314
+ size="normal"
315
+ checkbox-style="check"
316
+ checkbox-appearance="with-decorator"
317
+ theme="primary"
318
+ >
319
+ </SingleCheckbox>
205
320
  </template>
206
321
  </FormField>
207
322
 
@@ -210,21 +325,18 @@
210
325
  <SingleCheckbox
211
326
  id="terms"
212
327
  name="terms"
213
- legend="Accept terms and conditions"
328
+ legend="Terms and conditions"
214
329
  :required="true"
215
- :c12="{
216
- label: 'Accept terms and conditions',
217
- placeholder: 'eg. Type something here',
218
- errorMessage: 'Please accept our terms and conditions',
219
- }"
220
- v-model="formData"
221
- theme="secondary"
330
+ :errorMessage="formErrors?.terms?._errors[0] ?? ''"
331
+ :fieldHasError="Boolean(zodFormControl.submitAttempted && formErrors?.terms)"
332
+ v-model="state.terms"
222
333
  size="normal"
223
- checkbox-appearance="with-decorator"
224
334
  checkbox-style="check"
335
+ checkbox-appearance="with-decorator"
336
+ theme="primary"
225
337
  >
226
- <template #description>
227
- <p class="label-description">This is a description of what the user is required to do</p>
338
+ <template #labelContent>
339
+ <span class="body-normal">You must agree to our <NuxtLink to="/typography" class="link-normal">terms and conditions</NuxtLink> to continue</span>
228
340
  </template>
229
341
  </SingleCheckbox>
230
342
  </template>
@@ -232,7 +344,15 @@
232
344
 
233
345
  <FormField width="wide" :has-gutter="false">
234
346
  <template #default>
235
- <InputButtonSubmit type="button" @click.stop.prevent="submitForm()" :is-pending="false" :readonly="submitDisabled" button-text="Submit" theme="secondary" size="medium" />
347
+ <InputButtonSubmit
348
+ type="button"
349
+ @click.stop.prevent="submitForm()"
350
+ :is-pending="false"
351
+ :readonly="zodFormControl.submitDisabled"
352
+ button-text="Submit"
353
+ theme="primary"
354
+ size="medium"
355
+ />
236
356
  </template>
237
357
  </FormField>
238
358
  </form>
@@ -242,9 +362,18 @@
242
362
  </template>
243
363
  <template #slot2>
244
364
  <ClientOnly>
245
- <p>Client only content</p>
365
+ <p>Client only state</p>
246
366
  <pre>
247
- {{ formData }}
367
+ {{ state }}
368
+ </pre>
369
+ <p>Client only zodFormControl</p>
370
+ <pre>
371
+ {{ zodFormControl }}
372
+ </pre>
373
+ <p>Client only formErrors</p>
374
+
375
+ <pre>
376
+ {{ formErrors }}
248
377
  </pre>
249
378
  </ClientOnly>
250
379
  </template>
@@ -255,7 +384,8 @@
255
384
  </template>
256
385
 
257
386
  <script setup lang="ts">
258
- import type { IFieldsInitialState, IOptionsConfig, IFormMultipleOptions } from '@/types/types.forms';
387
+ import { z, ZodError } from 'zod';
388
+ import type { IFieldsInitialState, TFieldsInitialState, IOptionsConfig, IFormMultipleOptions } from '@/types/types.forms';
259
389
 
260
390
  definePageMeta({
261
391
  layout: false,
@@ -284,46 +414,143 @@ const { data: titleData } = await useFetch<IFormMultipleOptions>('/api/utils?cat
284
414
  /*
285
415
  * Setup forms
286
416
  */
287
- const fieldsInitialState = ref<IFieldsInitialState>({
288
- // emailAddress: "simon@simon.com",
289
- // emailAddress: "test@test.com",
417
+ const formSchema = reactive(
418
+ z
419
+ .object({
420
+ emailAddress: z
421
+ .string({
422
+ required_error: 'Email address is required',
423
+ })
424
+ .email({ message: 'Invalid email address' })
425
+ .refine((email) => email !== zodFormControl.previousState.emailAddress.value, {
426
+ message: 'This email address has already been used',
427
+ }),
428
+ username: z
429
+ .string({
430
+ required_error: 'Username is required',
431
+ })
432
+ .trim()
433
+ .min(2, 'Username is too short')
434
+ .max(25, 'Username is too long')
435
+ .refine((email) => email !== zodFormControl.previousState.username.value, {
436
+ message: 'This username has already been used',
437
+ }),
438
+ password: z
439
+ .string()
440
+ .trim()
441
+ .min(8, 'Password is too short')
442
+ .max(25, 'Password is too long')
443
+ .refine((email) => email !== zodFormControl.previousState.password.value, {
444
+ message: "You've already used this password",
445
+ }),
446
+ message: z.string().trim().min(2, 'Message is too short').max(255, 'Message is too long'),
447
+ count: z
448
+ .number({
449
+ required_error: 'Count is required',
450
+ invalid_type_error: 'Count must be a number',
451
+ })
452
+ .int({ message: 'Count must be a whole number' })
453
+ .gte(25, 'Count must be between 25 and 75')
454
+ .lte(75, 'Count must be between 25 and 75')
455
+ .multipleOf(5, 'Count must be a multiple of 5'),
456
+ count2: z
457
+ .number({
458
+ required_error: 'Count is required',
459
+ invalid_type_error: 'Count must be a number',
460
+ })
461
+ .int({ message: 'Count must be a whole number' })
462
+ .gte(25, 'Count must be between 25 and 75')
463
+ .lte(75, 'Count must be between 25 and 75'),
464
+ score: z
465
+ .number({
466
+ required_error: 'Score is required',
467
+ invalid_type_error: 'Score must be a number',
468
+ })
469
+ .gte(0)
470
+ .lte(100),
471
+ cities: z.array(z.string()).min(1, 'Please select at least one city'),
472
+ countries: z.array(z.string()).min(2, 'Please select at least 2 countries').max(5, 'Please select no more than 5 countries'),
473
+ title: z.string().min(1, { message: 'Title is required' }),
474
+ agreed: z.boolean().refine((val) => val === true, { message: 'You must tick this box' }),
475
+ agree: z.boolean().refine((val) => val === true, { message: 'You must tick this box' }),
476
+ terms: z.boolean().refine((val) => val === true, { message: 'You must accept our terms' }),
477
+ })
478
+ .required({
479
+ emailAddress: true,
480
+ username: true,
481
+ password: true,
482
+ message: true,
483
+ count: true,
484
+ count2: true,
485
+ score: true,
486
+ cities: true,
487
+ countries: true,
488
+ title: true,
489
+ agreed: true,
490
+ agree: true,
491
+ terms: true,
492
+ })
493
+ );
494
+
495
+ type formSchema = z.infer<typeof formSchema>;
496
+ const formErrors = computed<z.ZodFormattedError<formSchema> | null>(() => zodErrorObj.value);
497
+
498
+ const state = reactive({
290
499
  emailAddress: '',
291
- // username: "",
292
500
  username: '',
293
- // password: "!+Password123",
294
501
  password: '',
295
502
  message: '',
296
- // message: 'This is test 1234567890,.<>?@;:',
503
+ count: 25,
504
+ count2: 25,
297
505
  score: 50,
298
506
  cities: [],
299
507
  countries: [],
300
- title: [],
508
+ title: '',
509
+ agreed: false,
510
+ agree: false,
301
511
  terms: false,
302
512
  });
303
513
 
304
- // Setup formData
305
- const { formData, initFormData, getErrorCount, updateErrorMessages, formIsValid, submitDisabled, useApiErrors } = useFormControl();
306
- await initFormData(fieldsInitialState);
514
+ const {
515
+ initZodForm,
516
+ zodFormControl,
517
+ zodErrorObj,
518
+ // formErrors,
519
+ pushCustomErrors,
520
+ doZodValidate,
521
+ fieldMaxLength,
522
+ } = useZodValidation(formSchema);
307
523
 
308
- async function postFormData() {
524
+ initZodForm();
525
+
526
+ const submitForm = async () => {
527
+ zodFormControl.submitAttempted = true;
528
+ if (!(await doZodValidate(state))) return;
529
+ zodFormControl.displayLoader = true;
309
530
  try {
531
+ console.log('Form valid - post it');
310
532
  const data = await $fetch('/api/textFields', {
311
533
  method: 'post',
312
- body: formData.value.data,
313
- onResponse({ response }) {
534
+ body: state,
535
+ async onResponse({ response }) {
314
536
  if (response.status === 400) {
315
537
  console.log('onResponse', response);
316
538
  console.log(response.status);
317
539
 
318
- useApiErrors(response._data.data.errors);
540
+ // useApiErrors(response._data.data.errors);
319
541
  // for (const [key, message] of Object.entries(response._data.data.errors)) {
320
542
  // console.log(`${key}: ${message}`);
321
543
  // updateErrorMessages(key, message);
322
544
  // }
545
+
546
+ // if (error instanceof Error) {
547
+ await pushCustomErrors(response._data, state);
548
+ // zodFormControl.formIsValid = false;
549
+ // }
550
+ // zodFormControl.submitAttempted = false;
323
551
  }
324
552
  if (response.status === 200) {
325
- formData.value.isPending = false;
326
- formData.value.submitSuccess = true;
553
+ zodFormControl.submitSuccessful = true;
327
554
  }
328
555
  },
329
556
  });
@@ -331,29 +558,19 @@ async function postFormData() {
331
558
  // return data;
332
559
  } catch (error) {
333
560
  console.warn('2: An error occured posting form data', error);
334
- }
335
- }
336
-
337
- const submitForm = async () => {
338
- getErrorCount(true);
339
-
340
- if (formIsValid.value) {
341
- formData.value.isPending = true;
342
- formData.value.submitDisabled = true;
343
- console.log('Form is good - post it!');
344
-
345
- postFormData();
346
-
347
- // formData.value.errorMessages['emailAddress'] = {
348
- // useCustomError: true,
349
- // message: 'This is a custom error message',
350
- // };
351
-
352
- // executePost();
353
- } else {
354
- console.warn('Form has errors');
561
+ } finally {
562
+ zodFormControl.displayLoader = false;
355
563
  }
356
564
  };
565
+
566
+ watch(
567
+ () => state,
568
+ () => {
569
+ // console.log('Watching state');
570
+ doZodValidate(state);
571
+ },
572
+ { deep: true }
573
+ );
357
574
  </script>
358
575
 
359
576
  <style lang="css">
@@ -369,4 +586,9 @@ ul.flex-group {
369
586
  list-style-type: none;
370
587
  padding: 0;
371
588
  }
589
+
590
+ .header-1 {
591
+ font-family: var(--font-family);
592
+ color: var(--brand-success-text-text);
593
+ }
372
594
  </style>