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.
- package/LICENSE +21 -0
- package/assets/styles/brand/_brand.css +150 -0
- package/assets/styles/brand/_brand_dark.css +152 -0
- package/assets/styles/brand/_palette_dark.css +148 -0
- package/assets/styles/brand/_palette_light.css +148 -0
- package/assets/styles/brand/_typography.css +176 -0
- package/assets/styles/brand/index.css +1 -0
- package/assets/styles/forms/index.css +1 -1
- package/assets/styles/forms/themes/_default.css +3 -0
- package/assets/styles/forms/themes/_error.css +45 -11
- package/assets/styles/forms/themes/_ghost.css +42 -10
- package/assets/styles/forms/themes/_primary.css +42 -10
- package/assets/styles/forms/themes/_secondary.css +42 -10
- package/assets/styles/forms/themes/_success.css +42 -11
- package/assets/styles/forms/themes/_tertiary.css +42 -10
- package/assets/styles/forms/themes/_warning.css +42 -10
- package/assets/styles/forms/themes/index.css +1 -0
- package/assets/styles/forms/variables/_palette.css +104 -0
- package/assets/styles/forms/variables/_theme.css +12 -18
- package/assets/styles/forms/variables/index.css +2 -0
- package/assets/styles/main.css +2 -0
- package/assets/styles/scaffolding/_margin-helpers.css +308 -0
- package/assets/styles/scaffolding/_padding-helpers.css +308 -0
- package/assets/styles/scaffolding/_page.css +23 -0
- package/assets/styles/scaffolding/index.css +3 -0
- package/assets/styles/variables/colors/_blue.css +2 -2
- package/assets/styles/variables/colors/_gray.css +2 -1
- package/assets/styles/variables/colors/_green.css +2 -2
- package/assets/styles/variables/colors/_orange.css +2 -2
- package/assets/styles/variables/colors/_red.css +2 -2
- package/assets/styles/variables/colors/_yellow.css +1 -1
- package/components/forms/c12/prop-validators/index.ts +8 -20
- package/components/forms/c12/utils.ts +14 -0
- package/components/forms/c12/validation-patterns/en.json +12 -0
- package/components/forms/form-errors/InputError.vue +177 -0
- package/components/forms/input-button/InputButtonCore.vue +33 -109
- package/components/forms/input-button/variants/InputButtonConfirm.vue +1 -1
- package/components/forms/input-button/variants/InputButtonSubmit.vue +1 -1
- package/components/forms/input-checkbox/InputCheckboxCore.vue +263 -0
- package/components/forms/input-checkbox/InputCheckboxWithLabel.vue +116 -0
- package/components/forms/input-checkbox/variants/MultipleCheckboxes.vue +167 -0
- package/components/forms/input-checkbox/variants/SingleCheckbox.vue +172 -0
- package/components/forms/input-number/InputNumberCore.vue +184 -0
- package/components/forms/input-number/variants/InputNumberDefault.vue +155 -0
- package/components/forms/input-radio/InputRadiobuttonCore.vue +212 -0
- package/components/forms/input-radio/InputRadiobuttonWithLabel.vue +103 -0
- package/components/forms/input-radio/variants/MultipleRadiobuttons.vue +166 -0
- package/components/forms/input-range/InputRangeCore.vue +153 -0
- package/components/forms/input-range/variants/InputRangeDefault.vue +159 -0
- package/components/forms/input-text/InputTextCore.vue +149 -87
- package/components/forms/input-text/variants/material/InputPasswordWithLabel.vue +99 -0
- package/components/forms/input-text/variants/material/InputTextAsNumberWithLabel.vue +142 -0
- package/components/forms/input-text/variants/material/InputTextWithLabel.vue +125 -0
- package/components/forms/input-textarea/InputTextareaCore.vue +161 -0
- package/components/forms/input-textarea/variants/InputTextareaWithLabel.vue +106 -0
- package/components/scaffolding/footer/NavFooter.vue +62 -0
- package/components/ui/content-grid/ContentGrid.vue +85 -0
- package/composables/useApiRequest.ts +25 -0
- package/composables/useErrorMessages.ts +17 -5
- package/composables/useFormControl.ts +149 -37
- package/composables/useSleep.ts +2 -2
- package/composables/useStyleClassPassthrough.ts +30 -0
- package/composables/useZodValidation.ts +120 -0
- package/layouts/default.vue +26 -16
- package/nuxt.config.ts +22 -0
- package/package.json +13 -8
- package/pages/forms/examples/buttons/index.vue +14 -13
- package/pages/forms/examples/material/cssbattle.vue +60 -0
- package/pages/forms/examples/material/text-fields.vue +551 -93
- package/pages/index.vue +2 -2
- package/pages/limit-text.vue +43 -0
- package/pages/typography.vue +83 -0
- package/server/api/places/list.get.ts +23 -0
- package/server/api/textFields.post.ts +37 -0
- package/server/api/utils/index.get.ts +20 -0
- package/server/data/places/cities.json +43 -0
- package/server/data/places/countries.json +55 -0
- package/server/data/utils/title.json +49 -0
- package/types/types.forms.ts +135 -3
- package/types/types.places.ts +8 -0
- package/types/types.zodFormControl.ts +21 -0
- package/components/forms/input-text/variants/material/InputEmailMaterial.vue +0 -72
- package/components/forms/input-text/variants/material/InputPasswordMaterial.vue +0 -88
- package/components/forms/input-text/variants/material/InputTextMaterial.vue +0 -75
- package/components/forms/input-text/variants/material/InputTextMaterialCore.vue +0 -258
- package/composables/useUpdateStyleClassPassthrough.ts +0 -29
- 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>
|
|
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
|
-
<
|
|
79
|
-
<
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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
|
|
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: '
|
|
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
|
|
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
|
-
|
|
116
|
-
|
|
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
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
console.log('Form
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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
|
-
|
|
134
|
-
|
|
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>
|