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.
- package/LICENSE +21 -0
- package/assets/styles/forms/index.css +1 -0
- package/assets/styles/forms/themes/_primary.css +2 -0
- package/assets/styles/forms/themes/_secondary.css +2 -0
- package/assets/styles/forms/utils/_a11y.css +5 -0
- package/assets/styles/forms/utils/index.css +1 -0
- package/assets/styles/forms/variables/_theme.css +11 -17
- package/assets/styles/variables/colors/_gray.css +1 -0
- 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 +132 -0
- package/components/forms/input-button/InputButtonCore.vue +11 -8
- 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 +407 -0
- package/components/forms/input-checkbox/InputCheckboxWithLabel.vue +125 -0
- package/components/forms/input-checkbox/variants/MultipleCheckboxes.vue +194 -0
- package/components/forms/input-checkbox/variants/SingleCheckbox.vue +157 -0
- package/components/forms/input-radio/InputRadioCore.vue +226 -0
- package/components/forms/input-radio/InputRadioWithLabel.vue +118 -0
- package/components/forms/input-radio/variants/MultipleRadio.vue +183 -0
- package/components/forms/input-radio/variants/SingleRadio.vue +131 -0
- package/components/forms/input-range/InputRangeCore.vue +171 -0
- package/components/forms/input-range/variants/InputRangeDefault.vue +131 -0
- package/components/forms/input-text/InputTextCore.vue +61 -31
- package/components/forms/input-text/variants/material/InputPasswordMaterial.vue +27 -1
- package/components/forms/input-text/variants/material/InputTextMaterial.vue +1 -8
- package/components/forms/input-text/variants/material/InputTextMaterialCore.vue +83 -28
- package/components/forms/input-textarea/InputTextareaCore.vue +170 -0
- package/components/forms/input-textarea/variants/material/InputTextareaMaterial.vue +75 -0
- package/components/forms/input-textarea/variants/material/InputTextareaMaterialCore.vue +290 -0
- package/components/ui/content-grid/ContentGrid.vue +85 -0
- package/composables/useErrorMessages.ts +17 -5
- package/composables/useFormControl.ts +147 -37
- package/layouts/default.vue +7 -13
- package/nuxt.config.ts +22 -0
- package/package.json +9 -8
- package/pages/forms/examples/buttons/index.vue +14 -13
- package/pages/forms/examples/material/text-fields.vue +320 -84
- package/pages/limit-text.vue +43 -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 +37 -0
- package/server/data/places/countries.json +55 -0
- package/server/data/utils/title.json +49 -0
- package/types/types.forms.ts +33 -3
- package/types/types.places.ts +8 -0
- package/pages/forms/examples/material/text-fields-compact.vue +0 -136
package/layouts/default.vue
CHANGED
|
@@ -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/
|
|
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.
|
|
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.
|
|
18
|
-
"@
|
|
19
|
-
"@nuxt/
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"
|
|
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,
|
|
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
|
-
<
|
|
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
|
-
<
|
|
10
|
-
|
|
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
|
-
<
|
|
79
|
-
<
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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><</template>
|
|
124
|
+
<template #right>></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: '
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
125
|
-
|
|
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
|
-
|
|
134
|
-
|
|
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>
|