srcdev-nuxt-forms 0.0.23 → 0.1.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 (36) hide show
  1. package/.prettierrc +2 -1
  2. package/assets/styles/forms/themes/_error.css +9 -0
  3. package/assets/styles/forms/themes/_ghost.css +11 -0
  4. package/assets/styles/forms/themes/_primary.css +9 -1
  5. package/assets/styles/forms/themes/_secondary.css +11 -0
  6. package/assets/styles/forms/themes/_success.css +12 -0
  7. package/assets/styles/forms/themes/_tertiary.css +11 -0
  8. package/assets/styles/forms/themes/_warning.css +11 -0
  9. package/assets/styles/forms/themes/index.css +5 -0
  10. package/assets/styles/forms/variables/_theme.css +60 -0
  11. package/assets/styles/variables/colors/_orange.css +1 -1
  12. package/assets/styles/variables/colors/_red.css +1 -1
  13. package/components/forms/c12/prop-validators/index.ts +25 -0
  14. package/components/forms/c12/validation-patterns/en.json +1 -1
  15. package/components/forms/input-button/InputButtonCore.vue +367 -0
  16. package/components/forms/input-button/variants/InputButtonConfirm.vue +78 -0
  17. package/components/forms/input-button/variants/InputButtonSubmit.vue +74 -0
  18. package/components/forms/input-text/InputTextCore.vue +72 -66
  19. package/components/forms/input-text/variants/material/InputEmailMaterial.vue +72 -0
  20. package/components/forms/input-text/variants/material/InputPasswordMaterial.vue +88 -0
  21. package/components/forms/input-text/variants/material/InputTextMaterial.vue +75 -0
  22. package/components/forms/input-text/variants/material/InputTextMaterialCore.vue +258 -0
  23. package/components/forms/ui/FormField.vue +7 -2
  24. package/components/forms/ui/FormWrapper.vue +2 -2
  25. package/composables/useErrorMessages.ts +4 -4
  26. package/composables/useFormControl.ts +36 -16
  27. package/layouts/default.vue +33 -2
  28. package/nuxt.config.ts +4 -3
  29. package/package.json +3 -1
  30. package/pages/forms/examples/buttons/index.vue +154 -0
  31. package/pages/forms/examples/material/text-fields-compact.vue +136 -0
  32. package/pages/forms/examples/material/text-fields.vue +136 -0
  33. package/pages/index.vue +2 -70
  34. package/types/types.forms.ts +6 -11
  35. package/components/forms/input-text/InputTextField.vue +0 -22
  36. package/components/forms/input-text/variants/InputTextMaterial.vue +0 -192
@@ -0,0 +1,136 @@
1
+ <template>
2
+ <div>
3
+ <NuxtLayout name="default">
4
+ <template #layout-content>
5
+ <div>
6
+ <h1>Material UI text fields (compact)</h1>
7
+ <p>Example test fields in "compact" 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>
77
+ </div>
78
+ <ClientOnly>
79
+ <p>Client only content</p>
80
+ <pre>
81
+ {{ formData }}
82
+ </pre>
83
+ </ClientOnly>
84
+ </template>
85
+ </NuxtLayout>
86
+ </div>
87
+ </template>
88
+
89
+ <script setup lang="ts">
90
+ import type { IFieldsInitialState, IOptionsConfig } from '@/types/types.forms';
91
+
92
+ definePageMeta({
93
+ layout: false,
94
+ });
95
+
96
+ useHead({
97
+ title: 'Homepage',
98
+ meta: [{ name: 'description', content: 'Homepage' }],
99
+ bodyAttrs: {
100
+ class: '',
101
+ },
102
+ });
103
+
104
+ const compact = ref(true);
105
+
106
+ /*
107
+ * Setup forms
108
+ */
109
+ const fieldsInitialState = ref<IFieldsInitialState>({
110
+ emailAddress: '',
111
+ username: '',
112
+ password: '',
113
+ });
114
+
115
+ // Setup formData
116
+ const { formData, getErrorCount, updateCustomErrors, resetForm, formIsValid, submitDisabled } = useFormControl(fieldsInitialState);
117
+
118
+ 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');
128
+ }
129
+ };
130
+ </script>
131
+
132
+ <style lang="css">
133
+ p {
134
+ color: initial;
135
+ }
136
+ </style>
@@ -0,0 +1,136 @@
1
+ <template>
2
+ <div>
3
+ <NuxtLayout name="default">
4
+ <template #layout-content>
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>
77
+ </div>
78
+ <ClientOnly>
79
+ <p>Client only content</p>
80
+ <pre>
81
+ {{ formData }}
82
+ </pre>
83
+ </ClientOnly>
84
+ </template>
85
+ </NuxtLayout>
86
+ </div>
87
+ </template>
88
+
89
+ <script setup lang="ts">
90
+ import type { IFieldsInitialState, IOptionsConfig } from '@/types/types.forms';
91
+
92
+ definePageMeta({
93
+ layout: false,
94
+ });
95
+
96
+ useHead({
97
+ title: 'Homepage',
98
+ meta: [{ name: 'description', content: 'Homepage' }],
99
+ bodyAttrs: {
100
+ class: '',
101
+ },
102
+ });
103
+
104
+ const compact = ref(false);
105
+
106
+ /*
107
+ * Setup forms
108
+ */
109
+ const fieldsInitialState = ref<IFieldsInitialState>({
110
+ emailAddress: '',
111
+ username: '',
112
+ password: '',
113
+ });
114
+
115
+ // Setup formData
116
+ const { formData, getErrorCount, updateCustomErrors, resetForm, formIsValid, submitDisabled } = useFormControl(fieldsInitialState);
117
+
118
+ 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');
128
+ }
129
+ };
130
+ </script>
131
+
132
+ <style lang="css">
133
+ p {
134
+ color: initial;
135
+ }
136
+ </style>
package/pages/index.vue CHANGED
@@ -3,53 +3,16 @@
3
3
  <NuxtLayout name="default">
4
4
  <template #layout-content>
5
5
  <div>
6
- <h1>Sample form page</h1>
6
+ <h1>Sample form page pages</h1>
7
7
 
8
- <FormWrapper width="medium">
9
- <template #default>
10
- <form @submit.prevent="isPending">
11
- <p>Form content</p>
12
- <FormField width="wide" :has-gutter="true">
13
- <template #default>
14
- <p>Input text</p>
15
- <InputTextMaterial
16
- id="username"
17
- name="username"
18
- type="text"
19
- validation="username"
20
- :required="true"
21
- :c12="{
22
- label: 'Choose Username',
23
- placeholder: 'eg. YourUserName',
24
- errorMessage: 'Please enter a valid username',
25
- }"
26
- v-model="formData"
27
- />
28
- </template>
29
- </FormField>
30
- <input
31
- type="submit"
32
- @click.prevent="isPending"
33
- value="Submit"
34
- />
35
- </form>
36
- </template>
37
- </FormWrapper>
8
+ <p>Example test fields in default material UI</p>
38
9
  </div>
39
- <ClientOnly>
40
- <p>Client only content</p>
41
- <pre>
42
- {{ formData }}
43
- </pre>
44
- </ClientOnly>
45
10
  </template>
46
11
  </NuxtLayout>
47
12
  </div>
48
13
  </template>
49
14
 
50
15
  <script setup lang="ts">
51
- import type { IFieldsInitialState, IOptionsConfig } from '@/types/types.forms';
52
-
53
16
  definePageMeta({
54
17
  layout: false,
55
18
  });
@@ -61,37 +24,6 @@ useHead({
61
24
  class: '',
62
25
  },
63
26
  });
64
-
65
- /*
66
- * Setup forms
67
- */
68
- const fieldsInitialState = ref<IFieldsInitialState>({
69
- username: '',
70
- });
71
-
72
- // Setup formData
73
- const {
74
- formData,
75
- initFormData,
76
- getErrorCount,
77
- updateCustomErrors,
78
- resetForm,
79
- formIsValid,
80
- showErrors,
81
- } = useFormControl(fieldsInitialState);
82
-
83
- await initFormData();
84
-
85
- const isPending = async () => {
86
- formData.value.isPending = true;
87
- await getErrorCount();
88
-
89
- if (formIsValid.value) {
90
- console.log('Form is good - post it!');
91
- } else {
92
- console.warn('Form has errors');
93
- }
94
- };
95
27
  </script>
96
28
 
97
29
  <style lang="css">
@@ -17,14 +17,7 @@ export interface IOptionsValueArr {
17
17
  }
18
18
 
19
19
  export interface IFieldsInitialState {
20
- [key: string]:
21
- | null
22
- | string
23
- | boolean
24
- | number
25
- | URL
26
- | object
27
- | IOptionsValueArr[];
20
+ [key: string]: null | string | boolean | number | URL | object | IOptionsValueArr[];
28
21
  }
29
22
 
30
23
  export interface IValidityState {
@@ -57,7 +50,7 @@ export interface IValidityStateArr {
57
50
  };
58
51
  }
59
52
 
60
- export interface IValidityStateArrShort {
53
+ export interface IFormFieldsState {
61
54
  [key: string]: boolean;
62
55
  }
63
56
 
@@ -79,11 +72,13 @@ export interface ICustomErrorMessagesArr {
79
72
  export interface IFormData {
80
73
  [x: string]: string | boolean | number | URL | object;
81
74
  data: IFieldsInitialState;
82
- validityState: IValidityStateArrShort;
75
+ validityState: IFormFieldsState;
76
+ dirtyFields: IFormFieldsState;
77
+ focusedField: string;
83
78
  isPending: boolean;
84
79
  errorCount: number;
85
80
  customErrorMessages: ICustomErrorMessagesArr;
86
81
  formIsValid: boolean;
87
- showErrors: boolean;
88
82
  submitSuccess: boolean;
83
+ submitDisabled: boolean;
89
84
  }
@@ -1,22 +0,0 @@
1
- <template>
2
- <div>
3
- <input type="text" class="input-text" v-model="formValue" />
4
- </div>
5
- </template>
6
- <script setup lang="ts">
7
- console.log('InputText component loaded')
8
-
9
- const formValue = ref('')
10
-
11
- watchEffect(() => {
12
- console.log('Form value changed to: ', formValue.value)
13
- })
14
- </script>
15
-
16
- <style lang="css">
17
- .input-text {
18
- border: var(--input-border-width-thin) solid var(--input-border);
19
- border-radius: var(--input-border-radius);
20
- padding: 10px;
21
- }
22
- </style>
@@ -1,192 +0,0 @@
1
- <template>
2
- <div class="input-text-material" :class="[{ error: fieldHasError }]">
3
- <label
4
- class="label"
5
- :class="[{ active: isFocused }, { dirty: isDirty }]"
6
- :for="id"
7
- >{{ labelText }}</label
8
- >
9
- <div class="input-text-container">
10
- <InputTextCore
11
- :id
12
- :name
13
- :type
14
- :validation
15
- :required
16
- v-model="modelValue"
17
- v-model:isFocused="isFocused"
18
- v-model:isDirty="isDirty"
19
- :c12
20
- :style-class-passthrough="styleClassPassthroughRef"
21
- />
22
- </div>
23
- </div>
24
- </template>
25
-
26
- <script setup lang="ts">
27
- import type { InpuTextC12, IFormData } from '@/types/types.forms';
28
- import { validationConfig } from '@/components/forms/c12/validation-patterns';
29
-
30
- const props = defineProps({
31
- type: {
32
- // type: String as PropType<"text" | "password" | "tel" | "number" | "email" | "url">, // This breaks props setup in unit tests
33
- type: String,
34
- validator(value: string) {
35
- return ['text', 'password', 'tel', 'number', 'email', 'url'].includes(
36
- value
37
- );
38
- },
39
- },
40
- id: {
41
- // type: String as PropType<string>,
42
- type: String,
43
- required: true,
44
- },
45
- name: {
46
- type: String,
47
- default: null,
48
- },
49
- validation: {
50
- type: String,
51
- default: '',
52
- },
53
- required: {
54
- type: Boolean,
55
- value: false,
56
- },
57
- isPending: {
58
- type: Boolean,
59
- value: false,
60
- },
61
- c12: {
62
- type: Object as PropType<InpuTextC12>,
63
- required: true,
64
- },
65
- styleClassPassthrough: {
66
- type: String,
67
- default: '',
68
- },
69
- });
70
-
71
- const name = computed(() => {
72
- return props.name !== null ? props.name : props.id;
73
- });
74
-
75
- const labelText = computed(() => {
76
- return fieldHasError.value ? errorMessage.value : props.c12.label;
77
- });
78
-
79
- const { styleClassPassthroughRef, updateClasses } =
80
- useUpdateStyleClassPassthrough(props.styleClassPassthrough);
81
-
82
- const modelValue = defineModel() as Ref<IFormData>;
83
- const isFocused = ref(false);
84
- const isDirty = ref(false);
85
-
86
- const { errorMessage, setDefaultError, fieldHasError } = useErrorMessage(
87
- name.value,
88
- modelValue
89
- );
90
- setDefaultError(props.c12.errorMessage);
91
- </script>
92
-
93
- <style lang="css">
94
- .input-text-material {
95
- input {
96
- background-color: transparent;
97
- line-height: var(--line-height);
98
-
99
- &:focus {
100
- outline: none;
101
- box-shadow: none;
102
- border: none;
103
- }
104
- }
105
-
106
- label {
107
- margin: initial;
108
- line-height: var(--line-height);
109
- padding: initial;
110
- }
111
-
112
- --_gutter: 12px;
113
- --_form-theme: var(--theme-primary);
114
- --_border-width: var(--input-border-width-default);
115
-
116
- display: grid;
117
- border-radius: 2px;
118
- outline: var(--_border-width) solid var(--_form-theme);
119
-
120
- margin-bottom: 20px;
121
- overflow: hidden;
122
- /* transition: all linear 0.2s; */
123
-
124
- &:focus-within {
125
- outline: calc(var(--_border-width) * 2) solid var(--_form-theme);
126
- background-color: hsl(from var(--_form-theme) h s 95%);
127
- }
128
-
129
- &.error {
130
- outline: calc(var(--_border-width) * 2) solid var(--theme-error);
131
- background-color: hsl(from var(--theme-error) h s 95%);
132
-
133
- /* .label {
134
- color: var(--theme-error);
135
- } */
136
- }
137
-
138
- .label {
139
- grid-row: 1;
140
- grid-column: 1;
141
-
142
- font-family: var(--font-family);
143
- font-size: 20px;
144
- font-weight: 700;
145
- padding: var(--_gutter);
146
- transform: translateY(12px);
147
- transition: all linear 0.2s;
148
- background-color: transparent;
149
-
150
- &.active,
151
- &.dirty {
152
- font-size: 14px;
153
- transform: translateY(0);
154
- }
155
- }
156
-
157
- .input-text-container {
158
- display: grid;
159
- grid-row: 1;
160
- grid-column: 1;
161
- margin-top: 2rem;
162
- background-color: transparent;
163
-
164
- .input-text {
165
- font-family: var(--font-family);
166
- border: 0px solid green;
167
- padding: calc(var(--_gutter) / 2) var(--_gutter);
168
- font-size: var(--font-size);
169
- margin: 0;
170
- opacity: 0;
171
- transition: opacity linear 0.2s;
172
-
173
- &.active,
174
- &.dirty {
175
- opacity: 1;
176
- }
177
- /*
178
- &::placeholder,
179
- &:-ms-input-placeholder,
180
- &::-moz-placeholder, */
181
- &::-webkit-input-placeholder {
182
- font-family: var(--font-family);
183
- /* color: var(--gray-5); */
184
- color: hsl(from var(--_form-theme) h s 50%);
185
- font-size: var(--font-size);
186
- font-style: italic;
187
- font-weight: 500;
188
- }
189
- }
190
- }
191
- }
192
- </style>