srcdev-nuxt-forms 0.0.23 → 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 (64) hide show
  1. package/.prettierrc +2 -1
  2. package/LICENSE +21 -0
  3. package/assets/styles/forms/index.css +1 -0
  4. package/assets/styles/forms/themes/_error.css +9 -0
  5. package/assets/styles/forms/themes/_ghost.css +11 -0
  6. package/assets/styles/forms/themes/_primary.css +11 -1
  7. package/assets/styles/forms/themes/_secondary.css +13 -0
  8. package/assets/styles/forms/themes/_success.css +12 -0
  9. package/assets/styles/forms/themes/_tertiary.css +11 -0
  10. package/assets/styles/forms/themes/_warning.css +11 -0
  11. package/assets/styles/forms/themes/index.css +5 -0
  12. package/assets/styles/forms/utils/_a11y.css +5 -0
  13. package/assets/styles/forms/utils/index.css +1 -0
  14. package/assets/styles/forms/variables/_theme.css +56 -2
  15. package/assets/styles/variables/colors/_gray.css +1 -0
  16. package/assets/styles/variables/colors/_orange.css +1 -1
  17. package/assets/styles/variables/colors/_red.css +1 -1
  18. package/components/forms/c12/prop-validators/index.ts +13 -0
  19. package/components/forms/c12/utils.ts +14 -0
  20. package/components/forms/c12/validation-patterns/en.json +13 -1
  21. package/components/forms/form-errors/InputError.vue +132 -0
  22. package/components/forms/input-button/InputButtonCore.vue +370 -0
  23. package/components/forms/input-button/variants/InputButtonConfirm.vue +78 -0
  24. package/components/forms/input-button/variants/InputButtonSubmit.vue +74 -0
  25. package/components/forms/input-checkbox/InputCheckboxCore.vue +407 -0
  26. package/components/forms/input-checkbox/InputCheckboxWithLabel.vue +125 -0
  27. package/components/forms/input-checkbox/variants/MultipleCheckboxes.vue +194 -0
  28. package/components/forms/input-checkbox/variants/SingleCheckbox.vue +157 -0
  29. package/components/forms/input-radio/InputRadioCore.vue +226 -0
  30. package/components/forms/input-radio/InputRadioWithLabel.vue +118 -0
  31. package/components/forms/input-radio/variants/MultipleRadio.vue +183 -0
  32. package/components/forms/input-radio/variants/SingleRadio.vue +131 -0
  33. package/components/forms/input-range/InputRangeCore.vue +171 -0
  34. package/components/forms/input-range/variants/InputRangeDefault.vue +131 -0
  35. package/components/forms/input-text/InputTextCore.vue +115 -79
  36. package/components/forms/input-text/variants/material/InputEmailMaterial.vue +72 -0
  37. package/components/forms/input-text/variants/material/InputPasswordMaterial.vue +114 -0
  38. package/components/forms/input-text/variants/material/InputTextMaterial.vue +68 -0
  39. package/components/forms/input-text/variants/material/InputTextMaterialCore.vue +313 -0
  40. package/components/forms/input-textarea/InputTextareaCore.vue +170 -0
  41. package/components/forms/input-textarea/variants/material/InputTextareaMaterial.vue +75 -0
  42. package/components/forms/input-textarea/variants/material/InputTextareaMaterialCore.vue +290 -0
  43. package/components/forms/ui/FormField.vue +7 -2
  44. package/components/forms/ui/FormWrapper.vue +2 -2
  45. package/components/ui/content-grid/ContentGrid.vue +85 -0
  46. package/composables/useErrorMessages.ts +21 -9
  47. package/composables/useFormControl.ts +171 -41
  48. package/layouts/default.vue +28 -3
  49. package/nuxt.config.ts +26 -3
  50. package/package.json +9 -6
  51. package/pages/forms/examples/buttons/index.vue +155 -0
  52. package/pages/forms/examples/material/text-fields.vue +372 -0
  53. package/pages/index.vue +2 -70
  54. package/pages/limit-text.vue +43 -0
  55. package/server/api/places/list.get.ts +23 -0
  56. package/server/api/textFields.post.ts +37 -0
  57. package/server/api/utils/index.get.ts +20 -0
  58. package/server/data/places/cities.json +37 -0
  59. package/server/data/places/countries.json +55 -0
  60. package/server/data/utils/title.json +49 -0
  61. package/types/types.forms.ts +38 -13
  62. package/types/types.places.ts +8 -0
  63. package/components/forms/input-text/InputTextField.vue +0 -22
  64. package/components/forms/input-text/variants/InputTextMaterial.vue +0 -192
@@ -0,0 +1,290 @@
1
+ <template>
2
+ <div class="input-textarea-material" :class="[`theme-${theme}`, { error: fieldHasError }, { compact: compact }]">
3
+ <label class="input-textarea-label" :for="id" :class="[{ active: isFocused }, { error: fieldHasError }, { dirty: fieldIsDirty }, { compact: compact }]">
4
+ <span>{{ c12.label }}</span>
5
+ </label>
6
+ <div class="input-textarea-container" :class="[{ active: isFocused }, { error: fieldHasError }, { dirty: fieldIsDirty }, { compact: compact }]">
7
+ <slot name="input"></slot>
8
+ </div>
9
+ <InputError :errorMessaging :fieldHasError :id :isDetached="false" />
10
+ </div>
11
+ </template>
12
+
13
+ <script setup lang="ts">
14
+ import type { InpuTextC12, IFormData } from '@/types/types.forms';
15
+
16
+ import propValidators from '../../../c12/prop-validators';
17
+
18
+ const props = defineProps({
19
+ size: {
20
+ type: String as PropType<string>,
21
+ default: 'normal',
22
+ validator(value: string) {
23
+ return propValidators.size.includes(value);
24
+ },
25
+ },
26
+ weight: {
27
+ type: String as PropType<string>,
28
+ default: 'wght-400',
29
+ validator(value: string) {
30
+ return propValidators.weight.includes(value);
31
+ },
32
+ },
33
+ theme: {
34
+ type: String as PropType<string>,
35
+ default: 'primary',
36
+ validator(value: string) {
37
+ return propValidators.theme.includes(value);
38
+ },
39
+ },
40
+ id: {
41
+ type: String,
42
+ required: true,
43
+ },
44
+ name: {
45
+ type: String,
46
+ default: null,
47
+ },
48
+ required: {
49
+ type: Boolean,
50
+ value: false,
51
+ },
52
+ c12: {
53
+ type: Object as PropType<InpuTextC12>,
54
+ required: true,
55
+ },
56
+ styleClassPassthrough: {
57
+ type: String,
58
+ default: '',
59
+ },
60
+ compact: {
61
+ type: Boolean,
62
+ value: false,
63
+ },
64
+ });
65
+
66
+ const errorMessaging = computed(() => {
67
+ if (
68
+ typeof modelValue.value!.formFieldsC12[props.name] !== 'undefined' &&
69
+ modelValue.value!.formFieldsC12[props.name].useCustomError &&
70
+ modelValue.value.data[props.name] === modelValue.value.formFieldsC12[props.name].previousValue
71
+ ) {
72
+ return modelValue.value.formFieldsC12[props.name]?.customErrors || [];
73
+ } else {
74
+ return props.c12.errorMessage;
75
+ }
76
+ });
77
+
78
+ const modelValue = defineModel() as Ref<IFormData>;
79
+
80
+ const isFocused = computed(() => {
81
+ return modelValue.value.focusedField == props.name;
82
+ });
83
+
84
+ const fieldIsDirty = computed(() => {
85
+ if (typeof modelValue.value!.formFieldsC12[props.name] !== 'undefined') {
86
+ return modelValue.value!.formFieldsC12[props.name].isDirty;
87
+ } else {
88
+ return false;
89
+ }
90
+ });
91
+
92
+ const fieldHasError = computed(() => {
93
+ if (typeof modelValue.value!.formFieldsC12[props.name] !== 'undefined') {
94
+ return modelValue.value!.submitAttempted && !modelValue.value!.formFieldsC12[props.name].isValid;
95
+ }
96
+ return false;
97
+ });
98
+ </script>
99
+
100
+ <style lang="css">
101
+ .input-textarea-material {
102
+ --_form-theme: var(--theme-form-primary);
103
+ --_focus-colour: var(--theme-form-primary-focus);
104
+ --_gutter: 12px;
105
+ --_border-width: var(--input-border-width-thin);
106
+ --_border-color: var(--_form-theme);
107
+ --_outline-width: var(--input-outline-width-thin);
108
+
109
+ &.theme-secondary {
110
+ --_form-theme: var(--theme-form-secondary);
111
+ --_focus-colour: var(--theme-form-secondary-focus);
112
+ }
113
+
114
+ &.error {
115
+ --_form-theme: var(--theme-error);
116
+ }
117
+
118
+ .input-textarea-core {
119
+ color: var(--_form-theme);
120
+ background-color: transparent;
121
+ line-height: var(--line-height);
122
+ field-sizing: content;
123
+ min-height: 3rem;
124
+
125
+ &:focus {
126
+ outline: none;
127
+ box-shadow: none;
128
+ border: none;
129
+ }
130
+
131
+ &:-internal-autofill-selected,
132
+ &:autofill {
133
+ background-color: transparent !important;
134
+ }
135
+ }
136
+
137
+ .input-textarea-label {
138
+ color: var(--_form-theme);
139
+ margin: initial;
140
+ line-height: var(--line-height);
141
+ padding: initial;
142
+ transition: color 0.2s ease-in-out;
143
+ }
144
+
145
+ display: grid;
146
+ border-radius: 2px;
147
+ border: var(--_border-width) solid var(--_border-color);
148
+
149
+ margin-bottom: 20px;
150
+ overflow: hidden;
151
+
152
+ &:focus-within {
153
+ --_border-color: white;
154
+ outline: var(--_outline-width) solid hsl(from var(--_form-theme) h s 50%);
155
+ background-color: hsl(from var(--_form-theme) h s 95%);
156
+ }
157
+
158
+ &:has(.input-textarea-core:focus-visible) {
159
+ box-shadow: 0 0 2px 3px var(--_focus-colour);
160
+ outline-color: var(--_focus-colour);
161
+ }
162
+
163
+ &.error {
164
+ /* outline: calc(var(--_border-width) * 2) solid var(--theme-error); */
165
+
166
+ border: var(--_border-width) solid var(--theme-error);
167
+ outline: var(--_outline-width) solid hsl(from var(--theme-error) h s 75%);
168
+
169
+ background-color: hsl(from var(--theme-error) h s 95%);
170
+ }
171
+
172
+ .input-textarea-label {
173
+ grid-row: 1;
174
+ grid-column: 1;
175
+
176
+ font-family: var(--font-family);
177
+ font-size: 20px;
178
+ font-weight: 700;
179
+ padding: var(--_gutter);
180
+ transform: translateY(12px);
181
+ transition: all linear 0.2s;
182
+ background-color: transparent;
183
+ z-index: 2;
184
+ height: 3.5rem;
185
+ transition: color 0.2s ease-in-out;
186
+
187
+ &.active,
188
+ &.dirty,
189
+ &.error {
190
+ font-size: 16px;
191
+ height: 1.5em;
192
+ transform: translateY(-2px);
193
+ z-index: auto;
194
+ }
195
+ }
196
+
197
+ .input-textarea-container {
198
+ display: grid;
199
+ grid-row: 1;
200
+ grid-column: 1;
201
+ margin-top: 2rem;
202
+ background-color: transparent;
203
+ opacity: 0;
204
+ transition: opacity linear 0.2s;
205
+
206
+ &.active,
207
+ &.dirty,
208
+ &.error {
209
+ opacity: 1;
210
+ }
211
+
212
+ .input-textarea-core {
213
+ font-family: var(--font-family);
214
+ border: 0px solid green;
215
+ padding: calc(var(--_gutter) / 2) var(--_gutter);
216
+ font-size: var(--font-size);
217
+ margin: 0;
218
+ /* opacity: 0;
219
+ transition: opacity linear 0.2s;
220
+
221
+ &.active,
222
+ &.dirty {
223
+ opacity: 1;
224
+ } */
225
+ /*
226
+ &::placeholder,
227
+ &:-ms-input-placeholder,
228
+ &::-moz-placeholder, */
229
+ &::-webkit-input-placeholder {
230
+ font-family: var(--font-family);
231
+ /* color: var(--gray-5); */
232
+ color: hsl(from var(--_form-theme) h s 50%);
233
+ font-size: var(--font-size);
234
+ font-style: italic;
235
+ font-weight: 500;
236
+ }
237
+ }
238
+ }
239
+ }
240
+
241
+ /*
242
+ * Compact UI
243
+ **/
244
+
245
+ .input-textarea-material {
246
+ &.compact {
247
+ overflow: initial;
248
+
249
+ &:focus-within {
250
+ background-color: initial;
251
+ }
252
+
253
+ &.error {
254
+ background-color: initial;
255
+ }
256
+ }
257
+
258
+ .input-textarea-label {
259
+ &.compact {
260
+ align-content: center;
261
+ font-size: 16px;
262
+ padding: 0 12px;
263
+ transform: translateY(0);
264
+
265
+ span {
266
+ padding: 0 8px;
267
+ }
268
+
269
+ &.active,
270
+ &.dirty,
271
+ &.error {
272
+ font-size: 16px;
273
+ font-weight: 500;
274
+ transform: translateY(-14px);
275
+ z-index: auto;
276
+
277
+ span {
278
+ background-color: white;
279
+ }
280
+ }
281
+ }
282
+ }
283
+
284
+ .input-textarea-container {
285
+ &.compact {
286
+ margin: 10px 8px 6px 8px;
287
+ }
288
+ }
289
+ }
290
+ </style>
@@ -1,6 +1,5 @@
1
1
  <template>
2
- <div class="form-field" :class="[width, { 'has-gutter': hasGutter }]">
3
- <h1>FormField.vue</h1>
2
+ <div class="form-field" :class="[width, styleClassPassthrough, { 'has-gutter': hasGutter }]">
4
3
  <slot name="default"></slot>
5
4
  </div>
6
5
  </template>
@@ -16,6 +15,10 @@ defineProps({
16
15
  type: Boolean as PropType<boolean>,
17
16
  default: true,
18
17
  },
18
+ styleClassPassthrough: {
19
+ type: String,
20
+ default: '',
21
+ },
19
22
  });
20
23
  </script>
21
24
 
@@ -25,6 +28,8 @@ defineProps({
25
28
  --_max-width: 400px;
26
29
 
27
30
  margin-inline: auto;
31
+ margin-bottom: 16px;
32
+
28
33
  width: min(100% - calc(2 * var(--_gutter-width)), var(--_max-width));
29
34
  outline: 0px solid var(--gray-5);
30
35
 
@@ -1,6 +1,5 @@
1
1
  <template>
2
2
  <div class="form-wrapper" :class="width">
3
- <h1>FormWrapper.vue</h1>
4
3
  <slot name="default"></slot>
5
4
  </div>
6
5
  </template>
@@ -17,7 +16,8 @@ defineProps({
17
16
 
18
17
  <style lang="css">
19
18
  .form-wrapper {
20
- outline: 1px solid var(--gray-12);
19
+ outline: 0px solid var(--gray-12);
20
+ padding-bottom: 20px;
21
21
 
22
22
  &.narrow {
23
23
  max-width: 400px;
@@ -0,0 +1,85 @@
1
+ <template>
2
+ <div class="ui-content-grid" :class="[applyClasses]" :data-testid="dataTestid">
3
+ <div v-if="hasSlot1" class="col-1">
4
+ <slot name="slot1"></slot>
5
+ </div>
6
+ <div v-if="hasSlot2" class="col-2">
7
+ <slot name="slot2"></slot>
8
+ </div>
9
+ </div>
10
+ </template>
11
+
12
+ <script setup lang="ts">
13
+ const props = defineProps({
14
+ dataTestid: {
15
+ type: String,
16
+ default: 'ui-content-grid',
17
+ },
18
+ applyClasses: {
19
+ type: String,
20
+ default: '',
21
+ },
22
+ });
23
+
24
+ const slots = useSlots();
25
+ const hasSlot1 = ref(slots.slot1 !== undefined);
26
+ const hasSlot2 = ref(slots.slot2 !== undefined);
27
+ </script>
28
+
29
+ <style lang="css">
30
+ .ui-content-grid {
31
+ --_margin-inline: 0;
32
+ --_grid-template-columns: repeat(4, 1fr);
33
+ --_grid-template-rows: repeat(2, auto);
34
+ --_grid-gap: 16px;
35
+
36
+ display: grid;
37
+ gap: var(--_grid-gap);
38
+ grid-template-columns: var(--_grid-template-columns);
39
+ grid-template-rows: var(--_grid-template-rows);
40
+ margin-inline: var(--_margin-inline);
41
+
42
+ @container content (min-width: 768px) {
43
+ --_margin-inline: 0;
44
+ --_grid-template-columns: repeat(6, 1fr);
45
+ --_grid-gap: 32px;
46
+ }
47
+
48
+ @container content (min-width: 1024px) {
49
+ --_margin-inline: 0;
50
+ --_grid-template-columns: repeat(12, 1fr);
51
+ --_grid-template-rows: initial;
52
+ }
53
+
54
+ .col-1 {
55
+ --_grid-column: 1 / span 4;
56
+ --_grid-row: 1;
57
+ grid-column: var(--_grid-column);
58
+ grid-row: var(--_grid-row);
59
+
60
+ @container content (min-width: 768px) {
61
+ --_grid-column: 1 / span 6;
62
+ }
63
+
64
+ @container content (min-width: 1024px) {
65
+ --_grid-column: 1 / span 6;
66
+ }
67
+ }
68
+
69
+ .col-2 {
70
+ --_grid-column: 1 / span 4;
71
+ --_grid-row: 2;
72
+ grid-column: var(--_grid-column);
73
+ grid-row: var(--_grid-row);
74
+
75
+ @container content (min-width: 768px) {
76
+ --_grid-column: 1 / span 6;
77
+ }
78
+
79
+ @container content (min-width: 1024px) {
80
+ --_grid-column: 7 / span 6;
81
+ --_grid-row: 1;
82
+ }
83
+ }
84
+ }
85
+ </style>
@@ -1,16 +1,18 @@
1
- import type { IFormData } from "@/types/types.forms";
1
+ import type { IFormData } from '@/types/types.forms';
2
2
 
3
3
  export function useErrorMessage(name: string, formData: Ref<IFormData>) {
4
- const defaultError = ref("");
5
- const customErrorMessages = ref(toRaw(formData.value.customErrorMessages));
4
+ const defaultError = ref('');
5
+ const errorMessages = ref(formData.value.errorMessages);
6
6
 
7
7
  const hasCustomError = () => {
8
- return customErrorMessages.value[name] !== undefined && customErrorMessages.value[name].useCustomError;
8
+ return errorMessages.value[name] !== undefined && errorMessages.value[name].useCustomError;
9
9
  };
10
10
 
11
11
  const errorMessage = computed(() => {
12
+ console.log(`errorMessage()`);
12
13
  if (hasCustomError()) {
13
- return customErrorMessages.value[name].message;
14
+ console.log(`errorMessage() | IF`);
15
+ return errorMessages.value[name].message;
14
16
  } else {
15
17
  return defaultError.value;
16
18
  }
@@ -21,20 +23,30 @@ export function useErrorMessage(name: string, formData: Ref<IFormData>) {
21
23
  };
22
24
 
23
25
  const fieldHasError = computed(() => {
24
- if (formData.value.isPending) {
26
+ // console.log(`fieldHasError() | name(${name})`);
27
+ nextTick();
28
+ if (formData.value.submitDisabled) {
29
+ console.log(`fieldHasError() | name(${name}) | IF`);
25
30
  if (hasCustomError()) {
31
+ console.log(`fieldHasError() | name(${name}) | IF | IF`);
32
+
26
33
  return true;
27
34
  } else if (Object.keys(formData.value.validityState).length > 0 && formData.value.validityState[name] !== undefined) {
35
+ console.log(`fieldHasError() | name(${name}) | IF | ELSE IF`);
36
+
28
37
  return !formData.value.validityState[name];
29
38
  }
39
+ console.log(`fieldHasError() | name(${name}) | IF | ELSE`);
40
+
30
41
  return false;
31
42
  }
32
43
  });
33
44
 
34
45
  const removeCustomError = (valid: boolean = false) => {
35
- formData.value.validityState[name] = valid;
46
+ console.log(`useErrorMessage | removeCustomError | name(${name}) | valid(${valid})`);
47
+ // formData.value.validityState[name] = valid;
36
48
  // await nextTick();
37
- delete formData.value.customErrorMessages[name];
49
+ // delete formData.value.errorMessages[name];
38
50
  };
39
51
 
40
52
  return {
@@ -42,6 +54,6 @@ export function useErrorMessage(name: string, formData: Ref<IFormData>) {
42
54
  errorMessage,
43
55
  setDefaultError,
44
56
  fieldHasError,
45
- removeCustomError
57
+ removeCustomError,
46
58
  };
47
59
  }