srcdev-nuxt-forms 0.1.0 → 0.2.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 (50) hide show
  1. package/LICENSE +21 -0
  2. package/assets/styles/forms/index.css +1 -0
  3. package/assets/styles/forms/themes/_primary.css +2 -0
  4. package/assets/styles/forms/themes/_secondary.css +2 -0
  5. package/assets/styles/forms/utils/_a11y.css +5 -0
  6. package/assets/styles/forms/utils/index.css +1 -0
  7. package/assets/styles/forms/variables/_theme.css +11 -17
  8. package/assets/styles/variables/colors/_gray.css +1 -0
  9. package/components/forms/c12/prop-validators/index.ts +8 -20
  10. package/components/forms/c12/utils.ts +14 -0
  11. package/components/forms/c12/validation-patterns/en.json +12 -0
  12. package/components/forms/form-errors/InputError.vue +132 -0
  13. package/components/forms/input-button/InputButtonCore.vue +11 -8
  14. package/components/forms/input-button/variants/InputButtonConfirm.vue +1 -1
  15. package/components/forms/input-button/variants/InputButtonSubmit.vue +1 -1
  16. package/components/forms/input-checkbox/InputCheckboxCore.vue +407 -0
  17. package/components/forms/input-checkbox/InputCheckboxWithLabel.vue +125 -0
  18. package/components/forms/input-checkbox/variants/MultipleCheckboxes.vue +194 -0
  19. package/components/forms/input-checkbox/variants/SingleCheckbox.vue +157 -0
  20. package/components/forms/input-radio/InputRadioCore.vue +226 -0
  21. package/components/forms/input-radio/InputRadioWithLabel.vue +118 -0
  22. package/components/forms/input-radio/variants/MultipleRadio.vue +183 -0
  23. package/components/forms/input-radio/variants/SingleRadio.vue +131 -0
  24. package/components/forms/input-range/InputRangeCore.vue +171 -0
  25. package/components/forms/input-range/variants/InputRangeDefault.vue +131 -0
  26. package/components/forms/input-text/InputTextCore.vue +61 -31
  27. package/components/forms/input-text/variants/material/InputPasswordMaterial.vue +27 -1
  28. package/components/forms/input-text/variants/material/InputTextMaterial.vue +1 -8
  29. package/components/forms/input-text/variants/material/InputTextMaterialCore.vue +83 -28
  30. package/components/forms/input-textarea/InputTextareaCore.vue +170 -0
  31. package/components/forms/input-textarea/variants/material/InputTextareaMaterial.vue +75 -0
  32. package/components/forms/input-textarea/variants/material/InputTextareaMaterialCore.vue +290 -0
  33. package/components/ui/content-grid/ContentGrid.vue +85 -0
  34. package/composables/useErrorMessages.ts +17 -5
  35. package/composables/useFormControl.ts +147 -37
  36. package/layouts/default.vue +7 -13
  37. package/nuxt.config.ts +22 -0
  38. package/package.json +9 -8
  39. package/pages/forms/examples/buttons/index.vue +14 -13
  40. package/pages/forms/examples/material/text-fields.vue +320 -84
  41. package/pages/limit-text.vue +43 -0
  42. package/server/api/places/list.get.ts +23 -0
  43. package/server/api/textFields.post.ts +37 -0
  44. package/server/api/utils/index.get.ts +20 -0
  45. package/server/data/places/cities.json +37 -0
  46. package/server/data/places/countries.json +55 -0
  47. package/server/data/utils/title.json +49 -0
  48. package/types/types.forms.ts +33 -3
  49. package/types/types.places.ts +8 -0
  50. package/pages/forms/examples/material/text-fields-compact.vue +0 -136
@@ -4,25 +4,15 @@
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="/forms/examples/material/text-fields">Material UI text fields</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/buttons">Buttons</NuxtLink>
15
11
  </li>
16
12
  </ul>
17
13
  </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
14
 
25
- <div>
15
+ <div class="page-layout-content">
26
16
  <slot name="layout-content"></slot>
27
17
  </div>
28
18
 
@@ -47,6 +37,10 @@ useHead({
47
37
  grid-template-rows: auto 1fr auto;
48
38
  }
49
39
 
40
+ .page-layout-content {
41
+ container: content / inline-size;
42
+ }
43
+
50
44
  .flex-group {
51
45
  align-items: flex-start;
52
46
  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": "0.2.0",
5
5
  "main": "./nuxt.config.ts",
6
6
  "scripts": {
7
7
  "reinstall": "rm -rf node_modules && npm install",
@@ -14,12 +14,13 @@
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/material-symbols": "^1.1.88",
18
+ "@iconify-json/radix-icons": "^1.1.15",
19
+ "@nuxt/eslint-config": "^0.5.0",
20
+ "@nuxt/icon": "^1.4.5",
21
+ "eslint": "^9.9.0",
22
+ "nuxt": "^3.12.4",
23
+ "release-it": "^17.6.0",
24
+ "typescript": "^5.5.4"
24
25
  }
25
26
  }
@@ -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);
@@ -3,98 +3,266 @@
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>
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>
8
15
 
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>
16
+ <p>Example test fields in default material UI</p>
17
+ <p>Use 'test@test.com' to trigger server errors</p>
77
18
  </div>
78
- <ClientOnly>
79
- <p>Client only content</p>
80
- <pre>
81
- {{ formData }}
82
- </pre>
83
- </ClientOnly>
19
+ <ContentGrid>
20
+ <template #slot1>
21
+ <FormWrapper width="medium">
22
+ <template #default>
23
+ <ClientOnly>
24
+ <form class="form-wrapper" @submit.stop.prevent="submitForm()">
25
+ <div aria-live="assertive" id="aria-live-message"></div>
26
+ <FormField width="wide" :has-gutter="false">
27
+ <template #default>
28
+ <InputEmailMaterial
29
+ id="emailAddress"
30
+ name="emailAddress"
31
+ validation="emailaddress"
32
+ :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
+ />
42
+ </template>
43
+ </FormField>
44
+
45
+ <FormField width="wide" :has-gutter="false">
46
+ <template #default>
47
+ <InputTextMaterial
48
+ id="username"
49
+ name="username"
50
+ validation="username"
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
+ />
61
+ </template>
62
+ </FormField>
63
+
64
+ <FormField width="wide" :has-gutter="false">
65
+ <template #default>
66
+ <InputPasswordMaterial
67
+ id="password"
68
+ name="password"
69
+ validation="password"
70
+ :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
+ />
80
+ </template>
81
+ </FormField>
82
+
83
+ <FormField width="wide" :has-gutter="false">
84
+ <template #default>
85
+ <InputTextareaMaterial
86
+ id="message"
87
+ name="message"
88
+ validation="message"
89
+ :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
+ />
99
+ </template>
100
+ </FormField>
101
+
102
+ <FormField width="wide" :has-gutter="false">
103
+ <template #default>
104
+ <InputRangeDefault
105
+ id="score"
106
+ name="score"
107
+ :min="0"
108
+ :max="100"
109
+ :step="1"
110
+ :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"
119
+ >
120
+ <template #description>
121
+ <p class="label-description">This is a description of what the user is required to do</p>
122
+ </template>
123
+ <template #left>&lt;</template>
124
+ <template #right>&gt;</template>
125
+ </InputRangeDefault>
126
+ </template>
127
+ </FormField>
128
+
129
+ <FormField v-if="citiesData !== null" width="wide" :has-gutter="false">
130
+ <template #default>
131
+ <MultipleCheckboxes
132
+ id="cities"
133
+ name="cities"
134
+ legend="Choose a location"
135
+ :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"
142
+ v-model:fieldData="citiesData"
143
+ theme="secondary"
144
+ size="normal"
145
+ checkbox-style="check"
146
+ checkbox-appearance="with-decorator"
147
+ >
148
+ <template #description>
149
+ <p class="label-description">This is description: optionsLayout = 'equal-widths'</p>
150
+ </template>
151
+ </MultipleCheckboxes>
152
+ </template>
153
+ </FormField>
154
+
155
+ <FormField v-if="countriesData !== null" width="wide" :has-gutter="false">
156
+ <template #default>
157
+ <MultipleCheckboxes
158
+ id="countries"
159
+ name="countries"
160
+ legend="Choose a country"
161
+ :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"
168
+ v-model:fieldData="countriesData"
169
+ theme="secondary"
170
+ size="normal"
171
+ options-layout="inline"
172
+ checkbox-style="cross"
173
+ checkbox-appearance="with-decorator"
174
+ >
175
+ <template #description>
176
+ <p class="label-description">This is description: optionsLayout = 'inline'</p>
177
+ </template>
178
+ </MultipleCheckboxes>
179
+ </template>
180
+ </FormField>
181
+
182
+ <FormField v-if="titleData !== null" width="wide" :has-gutter="false">
183
+ <template #default>
184
+ <MultipleRadio
185
+ id="title"
186
+ name="title"
187
+ legend="Choose a title"
188
+ :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"
197
+ size="normal"
198
+ options-layout="equal-widths"
199
+ radio-appearance="with-decorator"
200
+ >
201
+ <template #description>
202
+ <p class="label-description">This is description: optionsLayout = 'equal-widths'</p>
203
+ </template>
204
+ </MultipleRadio>
205
+ </template>
206
+ </FormField>
207
+
208
+ <FormField width="wide" :has-gutter="false">
209
+ <template #default>
210
+ <SingleCheckbox
211
+ id="terms"
212
+ name="terms"
213
+ legend="Accept terms and conditions"
214
+ :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"
222
+ size="normal"
223
+ checkbox-appearance="with-decorator"
224
+ checkbox-style="check"
225
+ >
226
+ <template #description>
227
+ <p class="label-description">This is a description of what the user is required to do</p>
228
+ </template>
229
+ </SingleCheckbox>
230
+ </template>
231
+ </FormField>
232
+
233
+ <FormField width="wide" :has-gutter="false">
234
+ <template #default>
235
+ <InputButtonSubmit type="button" @click.stop.prevent="submitForm()" :is-pending="false" :readonly="submitDisabled" button-text="Submit" theme="secondary" size="medium" />
236
+ </template>
237
+ </FormField>
238
+ </form>
239
+ </ClientOnly>
240
+ </template>
241
+ </FormWrapper>
242
+ </template>
243
+ <template #slot2>
244
+ <ClientOnly>
245
+ <p>Client only content</p>
246
+ <pre>
247
+ {{ formData }}
248
+ </pre>
249
+ </ClientOnly>
250
+ </template>
251
+ </ContentGrid>
84
252
  </template>
85
253
  </NuxtLayout>
86
254
  </div>
87
255
  </template>
88
256
 
89
257
  <script setup lang="ts">
90
- import type { IFieldsInitialState, IOptionsConfig } from '@/types/types.forms';
258
+ import type { IFieldsInitialState, IOptionsConfig, IFormMultipleOptions } from '@/types/types.forms';
91
259
 
92
260
  definePageMeta({
93
261
  layout: false,
94
262
  });
95
263
 
96
264
  useHead({
97
- title: 'Homepage',
265
+ title: 'Text Field Example',
98
266
  meta: [{ name: 'description', content: 'Homepage' }],
99
267
  bodyAttrs: {
100
268
  class: '',
@@ -102,27 +270,86 @@ useHead({
102
270
  });
103
271
 
104
272
  const compact = ref(false);
273
+ const swapCompact = (newStyle: boolean) => {
274
+ compact.value = newStyle;
275
+ };
276
+
277
+ /*
278
+ * Fetch some sample data
279
+ **/
280
+ const { data: citiesData } = await useFetch<IFormMultipleOptions>('/api/places/list?category=cities');
281
+ const { data: countriesData } = await useFetch<IFormMultipleOptions>('/api/places/list?category=countries');
282
+ const { data: titleData } = await useFetch<IFormMultipleOptions>('/api/utils?category=title');
105
283
 
106
284
  /*
107
285
  * Setup forms
108
286
  */
109
287
  const fieldsInitialState = ref<IFieldsInitialState>({
288
+ // emailAddress: "simon@simon.com",
289
+ // emailAddress: "test@test.com",
110
290
  emailAddress: '',
291
+ // username: "",
111
292
  username: '',
293
+ // password: "!+Password123",
112
294
  password: '',
295
+ message: '',
296
+ // message: 'This is test 1234567890,.<>?@;:',
297
+ score: 50,
298
+ cities: [],
299
+ countries: [],
300
+ title: [],
301
+ terms: false,
113
302
  });
114
303
 
115
304
  // Setup formData
116
- const { formData, getErrorCount, updateCustomErrors, resetForm, formIsValid, submitDisabled } = useFormControl(fieldsInitialState);
305
+ const { formData, initFormData, getErrorCount, updateErrorMessages, formIsValid, submitDisabled, useApiErrors } = useFormControl();
306
+ await initFormData(fieldsInitialState);
307
+
308
+ async function postFormData() {
309
+ try {
310
+ const data = await $fetch('/api/textFields', {
311
+ method: 'post',
312
+ body: formData.value.data,
313
+ onResponse({ response }) {
314
+ if (response.status === 400) {
315
+ console.log('onResponse', response);
316
+ console.log(response.status);
317
+
318
+ useApiErrors(response._data.data.errors);
319
+ // for (const [key, message] of Object.entries(response._data.data.errors)) {
320
+ // console.log(`${key}: ${message}`);
321
+ // updateErrorMessages(key, message);
322
+ // }
323
+ }
324
+ if (response.status === 200) {
325
+ formData.value.isPending = false;
326
+ formData.value.submitSuccess = true;
327
+ }
328
+ },
329
+ });
330
+ console.log('3: Finished data', data);
331
+ // return data;
332
+ } catch (error) {
333
+ console.warn('2: An error occured posting form data', error);
334
+ }
335
+ }
117
336
 
118
337
  const submitForm = async () => {
119
- await getErrorCount(true);
338
+ getErrorCount(true);
120
339
 
121
340
  if (formIsValid.value) {
122
341
  formData.value.isPending = true;
342
+ formData.value.submitDisabled = true;
123
343
  console.log('Form is good - post it!');
124
- // await useSleep(2000);
125
- // formData.value.isPending = false;
344
+
345
+ postFormData();
346
+
347
+ // formData.value.errorMessages['emailAddress'] = {
348
+ // useCustomError: true,
349
+ // message: 'This is a custom error message',
350
+ // };
351
+
352
+ // executePost();
126
353
  } else {
127
354
  console.warn('Form has errors');
128
355
  }
@@ -130,7 +357,16 @@ const submitForm = async () => {
130
357
  </script>
131
358
 
132
359
  <style lang="css">
133
- p {
134
- color: initial;
360
+ .flex-group {
361
+ align-items: flex-start;
362
+ display: flex;
363
+ flex-wrap: wrap;
364
+ gap: 24px;
365
+ margin-bottom: 32px;
366
+ }
367
+
368
+ ul.flex-group {
369
+ list-style-type: none;
370
+ padding: 0;
135
371
  }
136
372
  </style>
@@ -0,0 +1,43 @@
1
+ <template>
2
+ <div class="wrapper">
3
+ <div class="text-wrapper">
4
+ <p>Lorem ipsum dolor sit amet consectetur adipisicing elit.</p>
5
+ </div>
6
+ <div class="text-wrapper">
7
+ <p>Nisi harum impedit, expedita eius doloremque dicta obcaecati tempora dolorem eum fuga deserunt minus facere error mollitia pariatur cum tempore, reiciendis molestiae?</p>
8
+ </div>
9
+ <div class="text-wrapper">
10
+ <p>
11
+ Lorem ipsum dolor sit amet consectetur adipisicing elit. Nisi harum impedit, expedita eius doloremque dicta obcaecati tempora dolorem eum fuga deserunt minus facere error mollitia pariatur cum
12
+ tempore, reiciendis molestiae?
13
+ </p>
14
+ </div>
15
+ </div>
16
+ </template>
17
+ <script setup lang="ts">
18
+ definePageMeta({
19
+ layout: false,
20
+ });
21
+ </script>
22
+ <style lang="css">
23
+ .wrapper {
24
+ display: grid;
25
+ grid-template-columns: 1fr 1fr 1fr;
26
+ gap: 20px;
27
+ }
28
+
29
+ .text-wrapper {
30
+ max-height: 100px;
31
+ width: 200px;
32
+ padding: 20px;
33
+ outline: 1px solid black;
34
+
35
+ p {
36
+ display: -webkit-box;
37
+ -webkit-box-orient: vertical;
38
+ line-clamp: 3;
39
+ -webkit-line-clamp: 3;
40
+ overflow: hidden;
41
+ }
42
+ }
43
+ </style>