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.
Files changed (87) hide show
  1. package/LICENSE +21 -0
  2. package/assets/styles/brand/_brand.css +150 -0
  3. package/assets/styles/brand/_brand_dark.css +152 -0
  4. package/assets/styles/brand/_palette_dark.css +148 -0
  5. package/assets/styles/brand/_palette_light.css +148 -0
  6. package/assets/styles/brand/_typography.css +176 -0
  7. package/assets/styles/brand/index.css +1 -0
  8. package/assets/styles/forms/index.css +1 -1
  9. package/assets/styles/forms/themes/_default.css +3 -0
  10. package/assets/styles/forms/themes/_error.css +45 -11
  11. package/assets/styles/forms/themes/_ghost.css +42 -10
  12. package/assets/styles/forms/themes/_primary.css +42 -10
  13. package/assets/styles/forms/themes/_secondary.css +42 -10
  14. package/assets/styles/forms/themes/_success.css +42 -11
  15. package/assets/styles/forms/themes/_tertiary.css +42 -10
  16. package/assets/styles/forms/themes/_warning.css +42 -10
  17. package/assets/styles/forms/themes/index.css +1 -0
  18. package/assets/styles/forms/variables/_palette.css +104 -0
  19. package/assets/styles/forms/variables/_theme.css +12 -18
  20. package/assets/styles/forms/variables/index.css +2 -0
  21. package/assets/styles/main.css +2 -0
  22. package/assets/styles/scaffolding/_margin-helpers.css +308 -0
  23. package/assets/styles/scaffolding/_padding-helpers.css +308 -0
  24. package/assets/styles/scaffolding/_page.css +23 -0
  25. package/assets/styles/scaffolding/index.css +3 -0
  26. package/assets/styles/variables/colors/_blue.css +2 -2
  27. package/assets/styles/variables/colors/_gray.css +2 -1
  28. package/assets/styles/variables/colors/_green.css +2 -2
  29. package/assets/styles/variables/colors/_orange.css +2 -2
  30. package/assets/styles/variables/colors/_red.css +2 -2
  31. package/assets/styles/variables/colors/_yellow.css +1 -1
  32. package/components/forms/c12/prop-validators/index.ts +8 -20
  33. package/components/forms/c12/utils.ts +14 -0
  34. package/components/forms/c12/validation-patterns/en.json +12 -0
  35. package/components/forms/form-errors/InputError.vue +177 -0
  36. package/components/forms/input-button/InputButtonCore.vue +33 -109
  37. package/components/forms/input-button/variants/InputButtonConfirm.vue +1 -1
  38. package/components/forms/input-button/variants/InputButtonSubmit.vue +1 -1
  39. package/components/forms/input-checkbox/InputCheckboxCore.vue +263 -0
  40. package/components/forms/input-checkbox/InputCheckboxWithLabel.vue +116 -0
  41. package/components/forms/input-checkbox/variants/MultipleCheckboxes.vue +167 -0
  42. package/components/forms/input-checkbox/variants/SingleCheckbox.vue +172 -0
  43. package/components/forms/input-number/InputNumberCore.vue +184 -0
  44. package/components/forms/input-number/variants/InputNumberDefault.vue +155 -0
  45. package/components/forms/input-radio/InputRadiobuttonCore.vue +212 -0
  46. package/components/forms/input-radio/InputRadiobuttonWithLabel.vue +103 -0
  47. package/components/forms/input-radio/variants/MultipleRadiobuttons.vue +166 -0
  48. package/components/forms/input-range/InputRangeCore.vue +153 -0
  49. package/components/forms/input-range/variants/InputRangeDefault.vue +159 -0
  50. package/components/forms/input-text/InputTextCore.vue +149 -87
  51. package/components/forms/input-text/variants/material/InputPasswordWithLabel.vue +99 -0
  52. package/components/forms/input-text/variants/material/InputTextAsNumberWithLabel.vue +142 -0
  53. package/components/forms/input-text/variants/material/InputTextWithLabel.vue +125 -0
  54. package/components/forms/input-textarea/InputTextareaCore.vue +161 -0
  55. package/components/forms/input-textarea/variants/InputTextareaWithLabel.vue +106 -0
  56. package/components/scaffolding/footer/NavFooter.vue +62 -0
  57. package/components/ui/content-grid/ContentGrid.vue +85 -0
  58. package/composables/useApiRequest.ts +25 -0
  59. package/composables/useErrorMessages.ts +17 -5
  60. package/composables/useFormControl.ts +149 -37
  61. package/composables/useSleep.ts +2 -2
  62. package/composables/useStyleClassPassthrough.ts +30 -0
  63. package/composables/useZodValidation.ts +120 -0
  64. package/layouts/default.vue +26 -16
  65. package/nuxt.config.ts +22 -0
  66. package/package.json +13 -8
  67. package/pages/forms/examples/buttons/index.vue +14 -13
  68. package/pages/forms/examples/material/cssbattle.vue +60 -0
  69. package/pages/forms/examples/material/text-fields.vue +551 -93
  70. package/pages/index.vue +2 -2
  71. package/pages/limit-text.vue +43 -0
  72. package/pages/typography.vue +83 -0
  73. package/server/api/places/list.get.ts +23 -0
  74. package/server/api/textFields.post.ts +37 -0
  75. package/server/api/utils/index.get.ts +20 -0
  76. package/server/data/places/cities.json +43 -0
  77. package/server/data/places/countries.json +55 -0
  78. package/server/data/utils/title.json +49 -0
  79. package/types/types.forms.ts +135 -3
  80. package/types/types.places.ts +8 -0
  81. package/types/types.zodFormControl.ts +21 -0
  82. package/components/forms/input-text/variants/material/InputEmailMaterial.vue +0 -72
  83. package/components/forms/input-text/variants/material/InputPasswordMaterial.vue +0 -88
  84. package/components/forms/input-text/variants/material/InputTextMaterial.vue +0 -75
  85. package/components/forms/input-text/variants/material/InputTextMaterialCore.vue +0 -258
  86. package/composables/useUpdateStyleClassPassthrough.ts +0 -29
  87. package/pages/forms/examples/material/text-fields-compact.vue +0 -136
@@ -0,0 +1,167 @@
1
+ <template>
2
+ <fieldset class="multiple-checkboxes-fieldset" :class="[{ error: fieldHasError }]">
3
+ <legend :class="[{ 'has-description': hasDescription }]">{{ legend }}</legend>
4
+ <template v-if="hasDescription">
5
+ <slot name="description"></slot>
6
+ </template>
7
+ <div class="multiple-checkboxes-items" :class="[optionsLayout]">
8
+ <template v-for="item in fieldData.data" :key="item.id">
9
+ <InputCheckboxWithLabel
10
+ :id="item.value"
11
+ :name
12
+ :required
13
+ :label="item.label"
14
+ :fieldHasError
15
+ v-model="modelValue"
16
+ :true-value="item.value"
17
+ :size
18
+ :checkboxAppearance
19
+ :optionsLayout
20
+ :checkboxStyle
21
+ :theme
22
+ />
23
+ </template>
24
+ </div>
25
+ <InputError :errorMessage="errorMessage" :fieldHasError :id="name" :isDetached="true" />
26
+ </fieldset>
27
+ </template>
28
+
29
+ <script setup lang="ts">
30
+ import propValidators from '../../c12/prop-validators';
31
+ import type { IOptionsConfig, IFormMultipleOptions } from '@/types/types.forms';
32
+
33
+ import type { C12nMultipleCheckboxes, IFormFieldC12, IFormData } from '@/types/types.forms';
34
+
35
+ const { id, name, legend, label, required, fieldHasError, placeholder, errorMessage, size, optionsLayout, equalCols, checkboxAppearance, checkboxStyle, styleClassPassthrough, theme } = defineProps({
36
+ id: {
37
+ type: String,
38
+ required: true,
39
+ },
40
+ name: {
41
+ type: String,
42
+ required: true,
43
+ },
44
+ legend: {
45
+ type: String,
46
+ required: true,
47
+ },
48
+ label: {
49
+ type: String,
50
+ required: true,
51
+ },
52
+ placeholder: {
53
+ type: String,
54
+ default: '',
55
+ },
56
+ errorMessage: {
57
+ type: [Object, String],
58
+ required: true,
59
+ },
60
+ required: {
61
+ type: Boolean,
62
+ default: false,
63
+ },
64
+ fieldHasError: {
65
+ type: Boolean,
66
+ default: false,
67
+ },
68
+ multipleOptions: {
69
+ type: Boolean,
70
+ default: false,
71
+ },
72
+ size: {
73
+ type: String as PropType<string>,
74
+ default: 'medium',
75
+ validator(value: string) {
76
+ return propValidators.size.includes(value);
77
+ },
78
+ },
79
+ optionsLayout: {
80
+ type: String as PropType<string>,
81
+ default: 'equal-widths',
82
+ validator(value: string) {
83
+ return propValidators.optionsLayout.includes(value);
84
+ },
85
+ },
86
+ equalCols: {
87
+ type: Boolean,
88
+ default: true,
89
+ },
90
+ checkboxAppearance: {
91
+ type: String as PropType<string>,
92
+ default: null,
93
+ validator(value: string) {
94
+ return propValidators.checkboxAppearance.includes(value);
95
+ },
96
+ },
97
+ checkboxStyle: {
98
+ type: String as PropType<string>,
99
+ default: 'check',
100
+ validator(value: string) {
101
+ return propValidators.checkboxStyle.includes(value);
102
+ },
103
+ },
104
+ styleClassPassthrough: {
105
+ type: Array as PropType<string[]>,
106
+ default: () => [],
107
+ },
108
+ theme: {
109
+ type: String as PropType<string>,
110
+ default: 'primary',
111
+ validator(value: string) {
112
+ return propValidators.theme.includes(value);
113
+ },
114
+ },
115
+ });
116
+
117
+ const slots = useSlots();
118
+ const hasDescription = computed(() => slots.description !== undefined);
119
+ const { elementClasses, updateElementClasses } = useStyleClassPassthrough(styleClassPassthrough);
120
+
121
+ const modelValue = defineModel();
122
+ const fieldData = defineModel('fieldData') as Ref<IFormMultipleOptions>;
123
+ </script>
124
+
125
+ <style lang="css">
126
+ .multiple-checkboxes-fieldset {
127
+ margin: 0;
128
+ padding: 0;
129
+ border: 0;
130
+
131
+ legend {
132
+ font-family: var(--font-family);
133
+ font-size: 16px;
134
+ font-weight: 500;
135
+
136
+ &.has-description {
137
+ margin-bottom: 0;
138
+ }
139
+ }
140
+
141
+ .label-description {
142
+ font-family: var(--font-family);
143
+ font-size: 16px;
144
+ margin-top: 12px;
145
+ }
146
+ }
147
+
148
+ .multiple-checkboxes-items {
149
+ display: flex;
150
+ gap: 12px;
151
+ margin-top: 12px;
152
+
153
+ &.inline {
154
+ flex-direction: row;
155
+ flex-wrap: wrap;
156
+ }
157
+
158
+ &.block {
159
+ flex-direction: column;
160
+ }
161
+
162
+ &.equal-widths {
163
+ display: grid;
164
+ grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
165
+ }
166
+ }
167
+ </style>
@@ -0,0 +1,172 @@
1
+ <template>
2
+ <fieldset class="single-checkbox-fieldset" :class="[{ error: fieldHasError }]">
3
+ <legend :class="[{ 'has-description': hasDescription }]">{{ legend }}</legend>
4
+ <template v-if="hasDescription">
5
+ <slot name="description"></slot>
6
+ </template>
7
+ <div class="single-checkbox-items" :class="[optionsLayout]">
8
+ <InputCheckboxWithLabel :id :name :required :label :fieldHasError v-model="modelValue" :trueValue :falseValue :size :checkboxAppearance :checkboxStyle :theme>
9
+ <template v-if="hasLabelContent" #labelContent>
10
+ <slot name="labelContent"></slot>
11
+ </template>
12
+ </InputCheckboxWithLabel>
13
+ </div>
14
+ <InputError :errorMessage :fieldHasError :id="name" :isDetached="true" :styleClassPassthrough="inputErrorStyles" />
15
+ </fieldset>
16
+ </template>
17
+
18
+ <script setup lang="ts">
19
+ import propValidators from '../../c12/prop-validators';
20
+ import type { IFormMultipleOptions } from '@/types/types.forms';
21
+
22
+ const { id, name, legend, label, required, fieldHasError, errorMessage, size, optionsLayout, equalCols, trueValue, falseValue, checkboxAppearance, checkboxStyle, styleClassPassthrough, theme } =
23
+ defineProps({
24
+ id: {
25
+ type: String,
26
+ required: true,
27
+ },
28
+ name: {
29
+ type: String,
30
+ required: true,
31
+ },
32
+ legend: {
33
+ type: String,
34
+ required: true,
35
+ },
36
+ label: {
37
+ type: String,
38
+ required: false,
39
+ default: '',
40
+ },
41
+ errorMessage: {
42
+ type: [Object, String],
43
+ required: true,
44
+ },
45
+ required: {
46
+ type: Boolean,
47
+ default: false,
48
+ },
49
+ fieldHasError: {
50
+ type: Boolean,
51
+ default: false,
52
+ },
53
+ multipleOptions: {
54
+ type: Boolean,
55
+ default: false,
56
+ },
57
+ size: {
58
+ type: String as PropType<string>,
59
+ default: 'medium',
60
+ validator(value: string) {
61
+ return propValidators.size.includes(value);
62
+ },
63
+ },
64
+ trueValue: {
65
+ type: [String, Number, Boolean],
66
+ default: true,
67
+ },
68
+ falseValue: {
69
+ type: [String, Number, Boolean],
70
+ default: false,
71
+ },
72
+ optionsLayout: {
73
+ type: String as PropType<string>,
74
+ default: 'equal-widths',
75
+ validator(value: string) {
76
+ return propValidators.optionsLayout.includes(value);
77
+ },
78
+ },
79
+ equalCols: {
80
+ type: Boolean,
81
+ default: true,
82
+ },
83
+ checkboxAppearance: {
84
+ type: String as PropType<string>,
85
+ default: null,
86
+ validator(value: string) {
87
+ return propValidators.checkboxAppearance.includes(value);
88
+ },
89
+ },
90
+ checkboxStyle: {
91
+ type: String as PropType<string>,
92
+ default: 'check',
93
+ validator(value: string) {
94
+ return propValidators.checkboxStyle.includes(value);
95
+ },
96
+ },
97
+ styleClassPassthrough: {
98
+ type: Array as PropType<string[]>,
99
+ default: () => [],
100
+ },
101
+ theme: {
102
+ type: String as PropType<string>,
103
+ default: 'primary',
104
+ validator(value: string) {
105
+ return propValidators.theme.includes(value);
106
+ },
107
+ },
108
+ });
109
+
110
+ const slots = useSlots();
111
+ const hasDescription = computed(() => slots.description !== undefined);
112
+ const hasLabelContent = computed(() => slots.labelContent !== undefined);
113
+
114
+ const { elementClasses, updateElementClasses } = useStyleClassPassthrough(styleClassPassthrough);
115
+
116
+ const modelValue = defineModel();
117
+ const fieldData = defineModel('fieldData') as Ref<IFormMultipleOptions>;
118
+
119
+ const inputErrorStyles = ref<string[]>(styleClassPassthrough);
120
+
121
+ watchEffect(() => {
122
+ if (!hasDescription.value && fieldHasError) {
123
+ inputErrorStyles.value.push('mbs-12');
124
+ } else {
125
+ inputErrorStyles.value = inputErrorStyles.value.filter((style) => style !== 'mbs-12');
126
+ }
127
+ });
128
+ </script>
129
+
130
+ <style lang="css">
131
+ .single-checkbox-fieldset {
132
+ margin: 0;
133
+ padding: 0;
134
+ border: 0;
135
+
136
+ legend {
137
+ font-family: var(--font-family);
138
+ font-size: 16px;
139
+ font-weight: 500;
140
+
141
+ &.has-description {
142
+ margin-bottom: 0;
143
+ }
144
+ }
145
+
146
+ .label-description {
147
+ font-family: var(--font-family);
148
+ font-size: 16px;
149
+ margin-top: 12px;
150
+ }
151
+ }
152
+
153
+ .single-checkbox-items {
154
+ display: flex;
155
+ gap: 12px;
156
+ margin-top: 12px;
157
+
158
+ &.inline {
159
+ flex-direction: row;
160
+ flex-wrap: wrap;
161
+ }
162
+
163
+ &.block {
164
+ flex-direction: column;
165
+ }
166
+
167
+ &.equal-widths {
168
+ display: grid;
169
+ grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
170
+ }
171
+ }
172
+ </style>
@@ -0,0 +1,184 @@
1
+ <template>
2
+ <div class="input-number-wrapper" :data-form-theme="formTheme" :class="elementClasses">
3
+ <div v-if="hasLeftContent" class="slot left">
4
+ <slot name="left"></slot>
5
+ </div>
6
+
7
+ <div class="input-number-container">
8
+ <input
9
+ type="number"
10
+ :id
11
+ :name
12
+ :required
13
+ :min
14
+ :max
15
+ :step
16
+ :class="['input-number-core', `input-number--${theme}`, `input-number--${size}`, `input-number--${weight}`, styleClassPassthrough]"
17
+ v-model="modelValue"
18
+ ref="inputField"
19
+ inputmode="numeric"
20
+ pattern="[0-9]+"
21
+ />
22
+ </div>
23
+ <div v-if="hasRightContent" class="slot right">
24
+ <slot name="right"></slot>
25
+ </div>
26
+ </div>
27
+ </template>
28
+
29
+ <script setup lang="ts">
30
+ import propValidators from '../c12/prop-validators';
31
+
32
+ const { id, name, required, min, max, step, theme, size, weight, fieldHasError, styleClassPassthrough } = defineProps({
33
+ id: {
34
+ type: String,
35
+ required: true,
36
+ },
37
+ name: {
38
+ type: String,
39
+ required: true,
40
+ },
41
+ min: {
42
+ type: Number,
43
+ required: true,
44
+ },
45
+ max: {
46
+ type: Number,
47
+ required: true,
48
+ },
49
+ step: {
50
+ type: Number,
51
+ default: 1,
52
+ },
53
+ placeholder: {
54
+ type: String,
55
+ default: '',
56
+ },
57
+ required: {
58
+ type: Boolean,
59
+ default: false,
60
+ },
61
+ theme: {
62
+ type: String as PropType<string>,
63
+ default: 'primary',
64
+ validator(value: string) {
65
+ return propValidators.theme.includes(value);
66
+ },
67
+ },
68
+ size: {
69
+ type: String as PropType<string>,
70
+ default: 'medium',
71
+ validator(value: string) {
72
+ return propValidators.size.includes(value);
73
+ },
74
+ },
75
+ weight: {
76
+ type: String as PropType<string>,
77
+ default: 'wght-400',
78
+ validator(value: string) {
79
+ return propValidators.weight.includes(value);
80
+ },
81
+ },
82
+ fieldHasError: {
83
+ type: Boolean,
84
+ default: false,
85
+ },
86
+ styleClassPassthrough: {
87
+ type: Array as PropType<string[]>,
88
+ default: () => [],
89
+ },
90
+ });
91
+
92
+ const slots = useSlots();
93
+ const hasLeftContent = computed(() => slots.left !== undefined);
94
+ const hasRightContent = computed(() => slots.right !== undefined);
95
+
96
+ const formTheme = computed(() => {
97
+ return fieldHasError ? 'error' : theme;
98
+ });
99
+
100
+ const modelValue = defineModel<number | readonly number[]>();
101
+
102
+ const { elementClasses, updateElementClasses } = useStyleClassPassthrough(styleClassPassthrough);
103
+ const minLength = computed(() => `${max.toString().length + 3}ch`);
104
+
105
+ onMounted(() => {
106
+ updateElementClasses(['number-1']);
107
+ });
108
+ </script>
109
+
110
+ <style lang="css">
111
+ .input-number-wrapper {
112
+ --_gutter: 12px;
113
+ --_border-width: var(--input-border-width-thin);
114
+ --_min-width: v-bind(minLength);
115
+
116
+ display: flex;
117
+ align-items: center;
118
+
119
+ width: fit-content;
120
+
121
+ .slot {
122
+ display: inline-block;
123
+ padding-inline: 8px;
124
+
125
+ .icon {
126
+ font-weight: 900;
127
+ }
128
+ }
129
+
130
+ &.has-left-slot {
131
+ .left-slot {
132
+ display: flex;
133
+ align-items: center;
134
+ }
135
+ }
136
+
137
+ &.has-right-slot {
138
+ .right-slot {
139
+ display: flex;
140
+ align-items: center;
141
+ }
142
+ }
143
+
144
+ .input-number-core {
145
+ background-color: transparent;
146
+ border: none;
147
+ outline: none;
148
+ box-shadow: none;
149
+
150
+ background-color: var(--theme-form-input-bg);
151
+ border-radius: var(--input-border-width-default);
152
+ border: var(--_border-width) solid var(--theme-form-input-border);
153
+ outline: var(--_outline-width) solid var(--theme-form-input-outline);
154
+
155
+ /* color: var(--theme-form-input-text); */
156
+ font-family: var(--font-family);
157
+ font-size: var(--theme-form-button-font-size-normal);
158
+ line-height: var(--line-height);
159
+ padding: 4px 16px;
160
+ text-align: center;
161
+ min-width: var(--_min-width);
162
+
163
+ &:focus-visible {
164
+ border: var(--_border-width) solid var(--theme-form-input-border);
165
+ outline: var(--_outline-width) solid hsl(from var(--theme-form-input-outline-focus) h s 50%);
166
+ box-shadow: var(--theme-form-focus-box-shadow);
167
+ }
168
+
169
+ &::placeholder,
170
+ &::-webkit-input-placeholder {
171
+ font-family: var(--font-family);
172
+ font-size: var(--font-size);
173
+ font-style: italic;
174
+ font-weight: 400;
175
+ }
176
+ }
177
+ }
178
+
179
+ input[type='number']::-webkit-inner-spin-button,
180
+ input[type='number']::-webkit-outer-spin-button {
181
+ -webkit-appearance: none;
182
+ margin: 0;
183
+ }
184
+ </style>
@@ -0,0 +1,155 @@
1
+ <template>
2
+ <div class="input-number-with-label" :data-form-theme="formTheme" :class="[elementClasses, `theme-${theme}`, { error: fieldHasError }]">
3
+ <label class="input-number-label body-normal-bold" :for="id">{{ label }}</label>
4
+ <template v-if="hasDescription">
5
+ <slot name="description"></slot>
6
+ </template>
7
+
8
+ <InputNumberCore v-model="modelValue" :id :name :min :max :step :theme :required :size :weight :fieldHasError>
9
+ <template v-if="hasLeftContent" #left>
10
+ <InputButtonCore
11
+ type="button"
12
+ @click.stop.prevent="updateValue(-step, Number(modelValue) > min)"
13
+ :readonly="Number(modelValue) === min"
14
+ :is-pending="false"
15
+ buttonText="Step down"
16
+ theme="primary"
17
+ size="x-small"
18
+ >
19
+ <template #iconOnly>
20
+ <slot name="left"></slot>
21
+ </template>
22
+ </InputButtonCore>
23
+ </template>
24
+ <template v-if="hasRightContent" #right>
25
+ <InputButtonCore
26
+ type="button"
27
+ @click.stop.prevent="updateValue(step, Number(modelValue) < max)"
28
+ :readonly="Number(modelValue) === max"
29
+ :is-pending="false"
30
+ buttonText="Step up"
31
+ theme="primary"
32
+ size="x-small"
33
+ >
34
+ <template #iconOnly>
35
+ <slot name="right"></slot>
36
+ </template>
37
+ </InputButtonCore>
38
+ </template>
39
+ </InputNumberCore>
40
+ <InputError :errorMessage :fieldHasError :id :isDetached="true" />
41
+ </div>
42
+ </template>
43
+
44
+ <script setup lang="ts">
45
+ import propValidators from '../../c12/prop-validators';
46
+
47
+ const { id, name, label, required, min, max, step, theme, size, weight, styleClassPassthrough, errorMessage, fieldHasError } = defineProps({
48
+ id: {
49
+ type: String,
50
+ required: true,
51
+ },
52
+ name: {
53
+ type: String,
54
+ required: true,
55
+ },
56
+ label: {
57
+ type: String,
58
+ required: true,
59
+ },
60
+ min: {
61
+ type: Number,
62
+ required: true,
63
+ },
64
+ max: {
65
+ type: Number,
66
+ required: true,
67
+ },
68
+ step: {
69
+ type: Number,
70
+ default: 1,
71
+ },
72
+ placeholder: {
73
+ type: String,
74
+ default: '',
75
+ },
76
+ errorMessage: {
77
+ type: [Object, String],
78
+ required: true,
79
+ },
80
+ fieldHasError: {
81
+ type: Boolean,
82
+ default: false,
83
+ },
84
+ required: {
85
+ type: Boolean,
86
+ default: false,
87
+ },
88
+ theme: {
89
+ type: String as PropType<string>,
90
+ default: 'primary',
91
+ validator(value: string) {
92
+ return propValidators.theme.includes(value);
93
+ },
94
+ },
95
+ size: {
96
+ type: String as PropType<string>,
97
+ default: 'medium',
98
+ validator(value: string) {
99
+ return propValidators.size.includes(value);
100
+ },
101
+ },
102
+ weight: {
103
+ type: String as PropType<string>,
104
+ default: 'wght-400',
105
+ validator(value: string) {
106
+ return propValidators.weight.includes(value);
107
+ },
108
+ },
109
+ styleClassPassthrough: {
110
+ type: Array as PropType<string[]>,
111
+ default: () => [],
112
+ },
113
+ });
114
+
115
+ const slots = useSlots();
116
+ const hasDescription = computed(() => slots.description !== undefined);
117
+ const hasLeftContent = computed(() => slots.left !== undefined);
118
+ const hasRightContent = computed(() => slots.right !== undefined);
119
+ const { elementClasses, updateElementClasses } = useStyleClassPassthrough(styleClassPassthrough);
120
+
121
+ const formTheme = computed(() => {
122
+ return fieldHasError ? 'error' : theme;
123
+ });
124
+
125
+ const modelValue = defineModel<number | readonly number[]>();
126
+
127
+ const updateValue = (step: number, withinRangeLimit: boolean) => {
128
+ if (withinRangeLimit) {
129
+ modelValue.value = (Number(modelValue.value) + step) as number;
130
+ }
131
+ };
132
+ </script>
133
+
134
+ <style lang="css">
135
+ .input-number-with-label {
136
+ --_border-width: var(--input-border-width-default);
137
+ --_outline-width: var(--input-outline-width-thin);
138
+ --_label-padding-inline: 10px;
139
+
140
+ .input-number-label {
141
+ display: block;
142
+ margin-block: 8px;
143
+
144
+ &:hover {
145
+ cursor: pointer;
146
+ }
147
+ }
148
+
149
+ .label-description {
150
+ font-family: var(--font-family);
151
+ font-size: 16px;
152
+ margin-top: 12px;
153
+ }
154
+ }
155
+ </style>