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
@@ -3,98 +3,396 @@
3
3
  <NuxtLayout name="default">
4
4
  <template #layout-content>
5
5
  <div>
6
- <h1>Material UI text fields (default)</h1>
7
- <p>Example test fields in default material UI</p>
8
-
9
- <FormWrapper width="medium">
10
- <template #default>
11
- <form @submit.prevent="submitForm">
12
- <FormField width="wide" :has-gutter="true">
13
- <template #default>
14
- <InputEmailMaterial
15
- id="emailAddress"
16
- name="emailAddress"
17
- validation="emailaddress"
18
- :required="true"
19
- :c12="{
20
- label: 'Your Email Address',
21
- placeholder: 'eg. joe@example.com',
22
- errorMessage: 'Please enter a valid email address',
23
- }"
24
- v-model="formData"
25
- theme="secondary"
26
- :compact
27
- />
28
- </template>
29
- </FormField>
30
-
31
- <FormField width="wide" :has-gutter="true">
32
- <template #default>
33
- <InputTextMaterial
34
- id="username"
35
- name="username"
36
- validation="username"
37
- :required="true"
38
- :c12="{
39
- label: 'Your Username',
40
- placeholder: 'eg. YourUserName',
41
- errorMessage: 'Please enter a valid username',
42
- }"
43
- v-model="formData"
44
- theme="secondary"
45
- :compact
46
- />
47
- </template>
48
- </FormField>
49
-
50
- <FormField width="wide" :has-gutter="true">
51
- <template #default>
52
- <InputPasswordMaterial
53
- id="password"
54
- name="password"
55
- validation="password"
56
- :required="true"
57
- :c12="{
58
- label: 'Password',
59
- placeholder: 'eg. Your5illYPa55w0rd',
60
- errorMessage: 'Please enter a valid password',
61
- }"
62
- v-model="formData"
63
- theme="secondary"
64
- :compact
65
- />
66
- </template>
67
- </FormField>
68
-
69
- <FormField width="wide" :has-gutter="true">
70
- <template #default>
71
- <InputButtonSubmit @click.stop.prevent="submitForm" :is-pending="false" :readonly="submitDisabled" button-text="Submit" theme="secondary" size="medium" />
72
- </template>
73
- </FormField>
74
- </form>
75
- </template>
76
- </FormWrapper>
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>
77
8
  </div>
78
- <ClientOnly>
79
- <p>Client only content</p>
80
- <pre>
81
- {{ formData }}
82
- </pre>
83
- </ClientOnly>
9
+ <ContentGrid>
10
+ <template #slot1>
11
+ <FormWrapper width="medium">
12
+ <template #default>
13
+ <ClientOnly>
14
+ <form class="form-wrapper" @submit.stop.prevent="submitForm()">
15
+ <div aria-live="assertive" id="aria-live-message"></div>
16
+ <FormField width="wide" :has-gutter="false">
17
+ <template #default>
18
+ <InputTextWithLabel
19
+ v-model="state.emailAddress"
20
+ type="email"
21
+ :maxlength="fieldMaxLength('email')"
22
+ id="emailAddress"
23
+ name="emailAddress"
24
+ placeholder="eg. name@domain.com"
25
+ label="Email address"
26
+ :errorMessage="formErrors?.emailAddress?._errors[0] ?? ''"
27
+ :fieldHasError="Boolean(zodFormControl.submitAttempted && formErrors?.emailAddress)"
28
+ :required="true"
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>
36
+ </template>
37
+ </FormField>
38
+
39
+ <FormField width="wide" :has-gutter="false">
40
+ <template #default>
41
+ <InputTextWithLabel
42
+ v-model="state.username"
43
+ type="text"
44
+ :maxlength="fieldMaxLength('username')"
45
+ id="username"
46
+ name="username"
47
+ placeholder="eg. name@domain.com"
48
+ label="Username"
49
+ :errorMessage="formErrors?.username?._errors[0] ?? ''"
50
+ :fieldHasError="Boolean(zodFormControl.submitAttempted && formErrors?.username)"
51
+ :required="true"
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>
59
+ </template>
60
+ </FormField>
61
+
62
+ <FormField width="wide" :has-gutter="false">
63
+ <template #default>
64
+ <InputPasswordWithLabel
65
+ v-model="state.password"
66
+ :maxlength="fieldMaxLength('password')"
67
+ id="password"
68
+ name="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)"
73
+ :required="true"
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>
81
+ </template>
82
+ </FormField>
83
+
84
+ <FormField width="wide" :has-gutter="false">
85
+ <template #default>
86
+ <InputTextareaWithLabel
87
+ v-model="state.message"
88
+ :maxlength="fieldMaxLength('message')"
89
+ id="message"
90
+ name="message"
91
+ placeholder="Type your message here"
92
+ label="Your mesage"
93
+ :errorMessage="formErrors?.message?._errors[0] ?? ''"
94
+ :fieldHasError="Boolean(zodFormControl.submitAttempted && formErrors?.message)"
95
+ :required="true"
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>
160
+ </template>
161
+ </FormField>
162
+
163
+ <FormField width="wide" :has-gutter="false">
164
+ <template #default>
165
+ <InputRangeDefault
166
+ id="score"
167
+ name="score"
168
+ label="Score between 0 & 100"
169
+ :min="0"
170
+ :max="100"
171
+ :step="10"
172
+ placeholder="eg. What\'s your score?"
173
+ :errorMessage="formErrors?.score?._errors[0] ?? ''"
174
+ :fieldHasError="Boolean(zodFormControl.submitAttempted && formErrors?.score)"
175
+ :required="true"
176
+ :styleClassPassthrough="['style-1', 'style-2']"
177
+ v-model.number="state.score"
178
+ theme="primary"
179
+ >
180
+ <template #description>
181
+ <p class="label-description">This is a description of what the user is required to do</p>
182
+ </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>
198
+ </InputRangeDefault>
199
+ </template>
200
+ </FormField>
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
+
228
+ <FormField v-if="citiesData !== null" width="wide" :has-gutter="false">
229
+ <template #default>
230
+ <MultipleCheckboxes
231
+ id="cities"
232
+ name="cities"
233
+ legend="Choose a location"
234
+ :required="true"
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"
240
+ v-model:fieldData="citiesData"
241
+ size="normal"
242
+ checkbox-style="cross"
243
+ checkbox-appearance="with-decorator"
244
+ optionsLayout="inline"
245
+ theme="primary"
246
+ >
247
+ <template #description>
248
+ <p class="label-description">This is description: optionsLayout = 'equal-widths'</p>
249
+ </template>
250
+ </MultipleCheckboxes>
251
+ </template>
252
+ </FormField>
253
+
254
+ <FormField v-if="countriesData !== null" width="wide" :has-gutter="false">
255
+ <template #default>
256
+ <MultipleCheckboxes
257
+ id="countries"
258
+ name="countries"
259
+ legend="Choose a country"
260
+ :required="true"
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"
266
+ v-model:fieldData="countriesData"
267
+ size="normal"
268
+ checkbox-style="check"
269
+ checkbox-appearance="with-decorator"
270
+ optionsLayout="equal-widths"
271
+ theme="primary"
272
+ >
273
+ <template #description>
274
+ <p class="label-description">This is description: optionsLayout = 'inline'</p>
275
+ </template>
276
+ </MultipleCheckboxes>
277
+ </template>
278
+ </FormField>
279
+
280
+ <FormField width="wide" :has-gutter="false">
281
+ <template #default>
282
+ <SingleCheckbox
283
+ id="agreed"
284
+ name="agreed"
285
+ legend="I agree (label with description)"
286
+ label="Click to agree to something"
287
+ :required="true"
288
+ :errorMessage="formErrors?.agreed?._errors[0] ?? ''"
289
+ :fieldHasError="Boolean(zodFormControl.submitAttempted && formErrors?.agreed)"
290
+ v-model="state.agreed"
291
+ size="normal"
292
+ checkbox-style="check"
293
+ checkbox-appearance="with-decorator"
294
+ theme="primary"
295
+ >
296
+ <template #description>
297
+ <p class="label-description">You must <strong>agree</strong> to continue</p>
298
+ </template>
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>
320
+ </template>
321
+ </FormField>
322
+
323
+ <FormField width="wide" :has-gutter="false">
324
+ <template #default>
325
+ <SingleCheckbox
326
+ id="terms"
327
+ name="terms"
328
+ legend="Terms and conditions"
329
+ :required="true"
330
+ :errorMessage="formErrors?.terms?._errors[0] ?? ''"
331
+ :fieldHasError="Boolean(zodFormControl.submitAttempted && formErrors?.terms)"
332
+ v-model="state.terms"
333
+ size="normal"
334
+ checkbox-style="check"
335
+ checkbox-appearance="with-decorator"
336
+ theme="primary"
337
+ >
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>
340
+ </template>
341
+ </SingleCheckbox>
342
+ </template>
343
+ </FormField>
344
+
345
+ <FormField width="wide" :has-gutter="false">
346
+ <template #default>
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
+ />
356
+ </template>
357
+ </FormField>
358
+ </form>
359
+ </ClientOnly>
360
+ </template>
361
+ </FormWrapper>
362
+ </template>
363
+ <template #slot2>
364
+ <ClientOnly>
365
+ <p>Client only state</p>
366
+ <pre>
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 }}
377
+ </pre>
378
+ </ClientOnly>
379
+ </template>
380
+ </ContentGrid>
84
381
  </template>
85
382
  </NuxtLayout>
86
383
  </div>
87
384
  </template>
88
385
 
89
386
  <script setup lang="ts">
90
- import type { IFieldsInitialState, IOptionsConfig } from '@/types/types.forms';
387
+ import { z, ZodError } from 'zod';
388
+ import type { IFieldsInitialState, TFieldsInitialState, IOptionsConfig, IFormMultipleOptions } from '@/types/types.forms';
91
389
 
92
390
  definePageMeta({
93
391
  layout: false,
94
392
  });
95
393
 
96
394
  useHead({
97
- title: 'Homepage',
395
+ title: 'Text Field Example',
98
396
  meta: [{ name: 'description', content: 'Homepage' }],
99
397
  bodyAttrs: {
100
398
  class: '',
@@ -102,35 +400,195 @@ useHead({
102
400
  });
103
401
 
104
402
  const compact = ref(false);
403
+ const swapCompact = (newStyle: boolean) => {
404
+ compact.value = newStyle;
405
+ };
406
+
407
+ /*
408
+ * Fetch some sample data
409
+ **/
410
+ const { data: citiesData } = await useFetch<IFormMultipleOptions>('/api/places/list?category=cities');
411
+ const { data: countriesData } = await useFetch<IFormMultipleOptions>('/api/places/list?category=countries');
412
+ const { data: titleData } = await useFetch<IFormMultipleOptions>('/api/utils?category=title');
105
413
 
106
414
  /*
107
415
  * Setup forms
108
416
  */
109
- const fieldsInitialState = ref<IFieldsInitialState>({
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({
110
499
  emailAddress: '',
111
500
  username: '',
112
501
  password: '',
502
+ message: '',
503
+ count: 25,
504
+ count2: 25,
505
+ score: 50,
506
+ cities: [],
507
+ countries: [],
508
+ title: '',
509
+ agreed: false,
510
+ agree: false,
511
+ terms: false,
113
512
  });
114
513
 
115
- // Setup formData
116
- const { formData, getErrorCount, updateCustomErrors, resetForm, formIsValid, submitDisabled } = useFormControl(fieldsInitialState);
514
+ const {
515
+ initZodForm,
516
+ zodFormControl,
517
+ zodErrorObj,
518
+ // formErrors,
519
+ pushCustomErrors,
520
+ doZodValidate,
521
+ fieldMaxLength,
522
+ } = useZodValidation(formSchema);
523
+
524
+ initZodForm();
117
525
 
118
526
  const submitForm = async () => {
119
- await getErrorCount(true);
120
-
121
- if (formIsValid.value) {
122
- formData.value.isPending = true;
123
- console.log('Form is good - post it!');
124
- // await useSleep(2000);
125
- // formData.value.isPending = false;
126
- } else {
127
- console.warn('Form has errors');
527
+ zodFormControl.submitAttempted = true;
528
+ if (!(await doZodValidate(state))) return;
529
+ zodFormControl.displayLoader = true;
530
+ try {
531
+ console.log('Form valid - post it');
532
+ const data = await $fetch('/api/textFields', {
533
+ method: 'post',
534
+ body: state,
535
+ async onResponse({ response }) {
536
+ if (response.status === 400) {
537
+ console.log('onResponse', response);
538
+ console.log(response.status);
539
+
540
+ // useApiErrors(response._data.data.errors);
541
+ // for (const [key, message] of Object.entries(response._data.data.errors)) {
542
+ // console.log(`${key}: ${message}`);
543
+ // updateErrorMessages(key, message);
544
+ // }
545
+
546
+ // if (error instanceof Error) {
547
+ await pushCustomErrors(response._data, state);
548
+ // zodFormControl.formIsValid = false;
549
+ // }
550
+ // zodFormControl.submitAttempted = false;
551
+ }
552
+ if (response.status === 200) {
553
+ zodFormControl.submitSuccessful = true;
554
+ }
555
+ },
556
+ });
557
+ console.log('3: Finished data', data);
558
+ // return data;
559
+ } catch (error) {
560
+ console.warn('2: An error occured posting form data', error);
561
+ } finally {
562
+ zodFormControl.displayLoader = false;
128
563
  }
129
564
  };
565
+
566
+ watch(
567
+ () => state,
568
+ () => {
569
+ // console.log('Watching state');
570
+ doZodValidate(state);
571
+ },
572
+ { deep: true }
573
+ );
130
574
  </script>
131
575
 
132
576
  <style lang="css">
133
- p {
134
- color: initial;
577
+ .flex-group {
578
+ align-items: flex-start;
579
+ display: flex;
580
+ flex-wrap: wrap;
581
+ gap: 24px;
582
+ margin-bottom: 32px;
583
+ }
584
+
585
+ ul.flex-group {
586
+ list-style-type: none;
587
+ padding: 0;
588
+ }
589
+
590
+ .header-1 {
591
+ font-family: var(--font-family);
592
+ color: var(--brand-success-text-text);
135
593
  }
136
594
  </style>