plugin-ui-for-kzt 0.0.9 → 0.0.10

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 (93) hide show
  1. package/example/App.vue +355 -0
  2. package/example/index.html +12 -0
  3. package/example/main.ts +8 -0
  4. package/example/shims-vue.d.ts +5 -0
  5. package/package.json +17 -7
  6. package/src/assets/icons/arrow-down.svg +3 -0
  7. package/src/assets/icons/calendar.svg +12 -0
  8. package/src/assets/icons/checkbox-circle.svg +3 -0
  9. package/src/assets/icons/checkbox.svg +3 -0
  10. package/src/assets/icons/email-sms.svg +4 -0
  11. package/src/assets/icons/help.svg +3 -0
  12. package/src/assets/icons/kg.svg +16 -0
  13. package/src/assets/icons/kz.svg +42 -0
  14. package/src/assets/icons/loader.svg +13 -0
  15. package/src/assets/icons/ru.svg +12 -0
  16. package/src/assets/icons/uz.svg +26 -0
  17. package/src/components/BaseBreadCrumbs/BaseBreadCrumbs.vue +142 -0
  18. package/src/components/BaseBreadCrumbs/README.md +49 -0
  19. package/src/components/BaseButton/BaseButton.vue +489 -0
  20. package/src/components/BaseButton/README.md +53 -0
  21. package/src/components/BaseCalendar/BaseCalendar.vue +231 -0
  22. package/src/components/BaseCalendar/README.md +126 -0
  23. package/src/components/BaseCheckbox/BaseCheckbox.vue +252 -0
  24. package/src/components/BaseCheckbox/README.md +110 -0
  25. package/src/components/BaseDropdown/BaseDropdown.vue +160 -0
  26. package/src/components/BaseDropdown/README.md +91 -0
  27. package/src/components/BaseIcon/BaseIcon.vue +47 -0
  28. package/src/components/BaseIcon/README.md +35 -0
  29. package/src/components/BaseInput/BaseInput.vue +300 -0
  30. package/src/components/BaseInput/README.md +85 -0
  31. package/src/components/BaseInputCalendar/BaseInputCalendar.vue +242 -0
  32. package/src/components/BaseInputCalendar/README.md +84 -0
  33. package/src/components/BaseInputCurrency/BaseInputCurrency.vue +198 -0
  34. package/src/components/BaseInputCurrency/README.md +57 -0
  35. package/src/components/BaseInputEmail/BaseInputEmail.vue +89 -0
  36. package/src/components/BaseInputEmail/README.md +71 -0
  37. package/src/components/BaseInputPhone/BaseInputPhone.vue +175 -0
  38. package/src/components/BaseLoader/BaseLoader.vue +45 -0
  39. package/src/components/BaseLoader/README.md +29 -0
  40. package/src/components/BaseOpenedListItem/BaseOpenedListItem.vue +216 -0
  41. package/src/components/BaseOpenedListItem/README.md +67 -0
  42. package/src/components/BaseRadio/BaseRadio.vue +283 -0
  43. package/src/components/BaseRadio/README.md +74 -0
  44. package/src/components/BaseSegmentedButtons/BaseSegmentedButtons.vue +89 -0
  45. package/src/components/BaseSegmentedButtons/README.md +75 -0
  46. package/src/components/BaseSelect/BaseSelect.vue +370 -0
  47. package/src/components/BaseSelect/README.md +95 -0
  48. package/src/components/BaseSiteInput/BaseSiteInput.vue +153 -0
  49. package/src/components/BaseTextarea/BaseTextarea.vue +212 -0
  50. package/src/components/BaseTextarea/README.md +75 -0
  51. package/src/components/BaseToggle/BaseToggle.vue +271 -0
  52. package/src/components/BaseToggle/README.md +76 -0
  53. package/src/components/BaseTooltip/BaseTooltip.vue +318 -0
  54. package/src/components/BaseTooltip/README.md +74 -0
  55. package/src/components/Modal/Modal.vue +3 -1
  56. package/src/components/Spinner/Spinner.vue +2 -1
  57. package/src/composables/kit/color.ts +14 -0
  58. package/src/composables/kit/interactive.ts +53 -0
  59. package/src/composables/kit/size.ts +15 -0
  60. package/src/composables/kit/state.ts +28 -0
  61. package/src/composables/kit/style.ts +18 -0
  62. package/src/composables/kit/utils.ts +7 -0
  63. package/src/icons/index.ts +9 -0
  64. package/src/index.ts +93 -2
  65. package/src/shims-context.d.ts +19 -0
  66. package/src/styles/index.scss +2 -1
  67. package/src/styles/root.scss +167 -0
  68. package/src/styles/variables.scss +160 -0
  69. package/src/types/breadcrumbs.d.ts +13 -0
  70. package/src/types/button.d.ts +13 -0
  71. package/src/types/calendar.d.ts +16 -0
  72. package/src/types/checkbox-radio.d.ts +15 -0
  73. package/src/types/dropdown.d.ts +20 -0
  74. package/src/types/icon.d.ts +8 -0
  75. package/src/types/input.d.ts +56 -0
  76. package/src/types/toggle.d.ts +12 -0
  77. package/src/types/tooltip.d.ts +8 -0
  78. package/src/types/utils.d.ts +37 -0
  79. package/src/vue-virtual-scroller.d.ts +9 -0
  80. package/tsconfig.json +3 -1
  81. package/webpack.config.js +90 -35
  82. package/dist/components/DataTable/DataTable.vue.d.ts +0 -3
  83. package/dist/components/Modal/Modal.vue.d.ts +0 -3
  84. package/dist/components/Spinner/Spinner.vue.d.ts +0 -3
  85. package/dist/components/Toaster/Toaster.vue.d.ts +0 -3
  86. package/dist/components/Toaster/timer.d.ts +0 -12
  87. package/dist/components/Tooltip/Tooltip.vue.d.ts +0 -3
  88. package/dist/index.d.ts +0 -11
  89. package/dist/index.js +0 -4929
  90. package/dist/plugins/modalPlugin.d.ts +0 -17
  91. package/dist/plugins/toasterPlugin.d.ts +0 -26
  92. package/dist/store/modal.d.ts +0 -11
  93. package/dist/types/index.d.ts +0 -5
@@ -0,0 +1,300 @@
1
+ <template>
2
+ <div class="base-input" :class="classList">
3
+ <label class="base-input__label" :for="id">
4
+ <span v-if="label" class="base-input__label-text">{{ label }}</span>
5
+
6
+ <div class="base-input__wrapper">
7
+ <div v-if="$slots['left-icon']" class="base-input__icon base-input__icon--left">
8
+ <slot name="left-icon"></slot>
9
+ </div>
10
+
11
+ <input
12
+ :id="id"
13
+ :type="type"
14
+ :value="modelValue"
15
+ v-maska="mask"
16
+ v-bind="componentAttrs"
17
+ :placeholder="placeholder || ''"
18
+ class="base-input__field"
19
+ @input="handleInput"
20
+ />
21
+
22
+ <div v-if="$slots['right-icon']" class="base-input__icon base-input__icon--right">
23
+ <slot name="right-icon"></slot>
24
+ </div>
25
+ </div>
26
+
27
+ <div v-if="(error && typeof error === 'string') || hint" class="base-input__hint">
28
+ {{ error || hint }}
29
+ </div>
30
+ </label>
31
+ </div>
32
+ </template>
33
+
34
+ <script setup lang="ts">
35
+ import { computed, useAttrs, useSlots } from 'vue';
36
+ import type { ICoreInputProps } from '../../types/input'
37
+ import { useKitSize } from '../../composables/kit/size'
38
+ import { useKitState } from '../../composables/kit/state'
39
+ import { vMaska } from "maska/vue"
40
+
41
+ const props = withDefaults(defineProps<ICoreInputProps>(), {
42
+ size: 'medium',
43
+ type: 'text',
44
+ modelValue: '',
45
+ placeholder: '' as string,
46
+ error: '',
47
+ hint: '',
48
+ mask: '',
49
+ });
50
+
51
+ const emit: (event: 'update:modelValue', value: string) => void = defineEmits();
52
+
53
+ const { sizeClassList } = useKitSize(props);
54
+ const { stateClassList, stateAttrs } = useKitState(props);
55
+ const attrs = useAttrs();
56
+
57
+ const componentAttrs = computed(() => ({
58
+ ...attrs,
59
+ ...stateAttrs.value,
60
+ }));
61
+
62
+ const slots = useSlots();
63
+
64
+ const classList = computed(() => [
65
+ sizeClassList.value,
66
+ stateClassList.value,
67
+
68
+ {
69
+ '--is-readonly': props.readonly,
70
+ '--icon-left': !!slots['left-icon'],
71
+ '--icon-right': !!slots['right-icon'],
72
+ },
73
+ ]);
74
+
75
+ function handleInput(event: Event) {
76
+ const target = event.target as HTMLInputElement;
77
+ emit('update:modelValue', target.value);
78
+ }
79
+ </script>
80
+
81
+ <style scoped lang="scss">
82
+ @import '../../styles/variables';
83
+ @import '../../styles/root';
84
+
85
+ .base-input {
86
+ width: max-content;
87
+ $input: &;
88
+
89
+ &__wrapper {
90
+ position: relative;
91
+ display: flex;
92
+ flex-direction: column;
93
+ align-items: flex-start;
94
+ width: fit-content;
95
+ }
96
+
97
+ &__field {
98
+ flex: 1;
99
+ width: auto;
100
+ height: 100%;
101
+ color: var(--primary-text-primary);
102
+ background: var(--bg-light);
103
+ border: 1px solid var(--primary-black-300);
104
+ outline: none;
105
+
106
+ &::placeholder {
107
+ color: var(--primary-text-tertiary);
108
+ }
109
+
110
+ @include pressed {
111
+ color: var(--primary-blue);
112
+ }
113
+
114
+ @include focused {
115
+ border: 1px solid var(--primary-blue-500);
116
+ outline: 4px solid var(--effects-primary-focus);
117
+ }
118
+
119
+ @include is-disabled-state {
120
+ & {
121
+ color: var(--primary-text-tertiary);
122
+ background: var(--primary-black-100);
123
+ }
124
+ }
125
+ }
126
+
127
+ &__icon {
128
+ display: flex;
129
+ align-items: center;
130
+ justify-content: center;
131
+ color: var(--primary-black-500);
132
+ }
133
+
134
+ @include is-disabled-state {
135
+ #{$input}__icon--right {
136
+ color: var(--primary-black-400);
137
+ }
138
+
139
+ #{$input}__hint {
140
+ color: var(--primary-text-secondary);
141
+ }
142
+ }
143
+
144
+ &__icon--left, &__icon--right {
145
+ position: absolute;
146
+ top: 50%;
147
+ transform: translateY(-50%);
148
+ }
149
+
150
+ &__label {
151
+ display: flex;
152
+ flex-direction: column;
153
+ gap: 6px;
154
+ }
155
+
156
+ &__label-text {
157
+ color: var(--primary-text-primary);
158
+ }
159
+
160
+ &.--is-error {
161
+ #{$input}__hint {
162
+ color: var(--error-red);
163
+ }
164
+
165
+ #{$input}__field {
166
+ border-color: var(--error-red-light-01);
167
+ }
168
+
169
+ #{$input}__icon--right > * {
170
+ color: var(--error-red);
171
+ }
172
+ }
173
+
174
+ &.--small-size {
175
+ #{$input} {
176
+ &__label {
177
+ font: var(--typography-text-s-medium);
178
+ }
179
+
180
+ &__hint {
181
+ font: var(--typography-text-s-regular);
182
+ }
183
+ }
184
+
185
+ #{$input}__wrapper {
186
+ height: 40px;
187
+ }
188
+
189
+ #{$input}__field {
190
+ padding: 8px 14px;
191
+ font: var(--typography-text-m-regular);
192
+ border-radius: 10px;
193
+ }
194
+
195
+ &.--icon-left #{$input}__field {
196
+ padding-left: 42px;
197
+ }
198
+
199
+ &.--icon-right #{$input}__field {
200
+ padding-right: 38px;
201
+ }
202
+
203
+ #{$input}__icon--left {
204
+ left: 14px;
205
+ width: 20px;
206
+ height: 20px;
207
+ }
208
+
209
+ #{$input}__icon--right {
210
+ right: 14px;
211
+ width: 16px;
212
+ height: 16px;
213
+ }
214
+ }
215
+
216
+ &.--medium-size {
217
+ #{$input} {
218
+ &__label {
219
+ font: var(--typography-text-s-medium);
220
+ }
221
+
222
+ &__hint {
223
+ font: var(--typography-text-s-regular);
224
+ }
225
+
226
+ &__wrapper {
227
+ height: 48px;
228
+ }
229
+
230
+ &__field {
231
+ padding: 12px 14px;
232
+ font: var(--typography-text-m-regular);
233
+ border-radius: 12px;
234
+ }
235
+ }
236
+
237
+ &.--icon-left #{$input}__field {
238
+ padding-left: 46px;
239
+ }
240
+
241
+ &.--icon-right #{$input}__field {
242
+ padding-right: 42px;
243
+ }
244
+
245
+ #{$input}__icon--left {
246
+ left: 14px;
247
+ width: 24px;
248
+ height: 24px;
249
+ }
250
+
251
+ #{$input}__icon--right {
252
+ right: 14px;
253
+ width: 20px;
254
+ height: 20px;
255
+ }
256
+ }
257
+
258
+ &.--large-size {
259
+ #{$input} {
260
+ &__label {
261
+ font: var(--typography-text-m-medium);
262
+ }
263
+
264
+ &__hint {
265
+ font: var(--typography-text-m-regular);
266
+ }
267
+
268
+ &__wrapper {
269
+ height: 56px;
270
+ }
271
+
272
+ &__field {
273
+ padding: 14px 16px;
274
+ font: var(--typography-text-l-regular);
275
+ border-radius: 12px;
276
+ }
277
+ }
278
+
279
+ &.--icon-left #{$input}__field {
280
+ padding-left: 56px;
281
+ }
282
+
283
+ &.--icon-right #{$input}__field {
284
+ padding-right: 48px;
285
+ }
286
+
287
+ #{$input}__icon--left {
288
+ left: 16px;
289
+ width: 32px;
290
+ height: 32px;
291
+ }
292
+
293
+ #{$input}__icon--right {
294
+ right: 16px;
295
+ width: 24px;
296
+ height: 24px;
297
+ }
298
+ }
299
+ }
300
+ </style>
@@ -0,0 +1,85 @@
1
+ ### BaseInput
2
+
3
+ Компонент для ввода текста с поддержкой различных состояний, подсказок, иконок и обработки ошибок.
4
+
5
+ ---
6
+
7
+ #### Props
8
+
9
+ - `modelValue: string`
10
+ Значение поля ввода. Используется для двусторонней привязки данных.
11
+
12
+ - `size: 'small' | 'medium' | 'large'`
13
+ Размер компонента. Определяет размеры иконок и поля ввода. По умолчанию — `medium`.
14
+
15
+ - `type: 'text' | 'password' | 'email' | 'number' | ...`
16
+ Тип поля ввода. Например, `text`, `password`, `email`, и другие.
17
+
18
+ - `placeholder: string`
19
+ Текст-заполнитель для поля ввода.
20
+
21
+ - `error: string | boolean`
22
+ Ошибка, которая будет отображаться под полем ввода. Может быть строкой с сообщением об ошибке или булевым значением, где `true` указывает на наличие ошибки.
23
+
24
+ - `hint: string`
25
+ Подсказка, отображаемая под полем ввода.
26
+
27
+ - `readonly: boolean`
28
+ Указывает, что поле ввода только для чтения. Если `true`, то поле нельзя редактировать.
29
+
30
+ ---
31
+
32
+ #### Важные особенности
33
+
34
+ - **Иконки слева и справа:**
35
+ Компонент поддерживает иконки слева и справа от поля ввода через слоты `left-icon` и `right-icon`. Это удобно для отображения дополнительных иконок (например, иконки для показа пароля).
36
+
37
+ - **Состояния:**
38
+ Используются CSS-классы для отображения разных состояний поля ввода, таких как фокус, ошибка и состояние "disabled".
39
+
40
+ - **Валидация ошибок:**
41
+ В случае ошибки поле ввода будет отображать сообщение об ошибке, и его рамка станет красной. Сообщение об ошибке или подсказка отображается под полем ввода.
42
+
43
+ - **Размеры:**
44
+ Компонент поддерживает три размера: `small`, `medium`, `large`. Для каждого размера задаются различные отступы и размеры шрифта.
45
+
46
+ - **Двусторонняя привязка:**
47
+ Используется `v-model` для двусторонней привязки значения.
48
+
49
+ ---
50
+
51
+ #### Пример использования
52
+
53
+ ```vue
54
+ <BaseInput
55
+ v-model="inputValue"
56
+ placeholder="Введите текст"
57
+ :error="errorMessage"
58
+ :hint="hintMessage"
59
+ :size="'medium'"
60
+ />
61
+ ```
62
+
63
+ ---
64
+
65
+ #### Логика
66
+
67
+ - **Обработка ввода:**
68
+ При изменении значения поля ввода вызывается метод `handleInput`, который обновляет значение через `v-model` и вызывает событие `update:modelValue`.
69
+
70
+ - **CSS стили для состояний:**
71
+ Используются миксины для стилизации поля ввода в различных состояниях, например, для фокуса, нажатия, ошибок или отключенного состояния.
72
+
73
+ ---
74
+
75
+ #### Стилизация
76
+
77
+ - **Иконки:**
78
+ Иконки, если они присутствуют, располагаются влево или вправо от поля ввода, и их размеры и позиционирование изменяются в зависимости от размера поля ввода.
79
+
80
+ - **Состояния ошибок:**
81
+ В случае ошибки или активного состояния, компонент применяет соответствующие стили, такие как изменение цвета рамки и отображение сообщения об ошибке.
82
+
83
+ ---
84
+
85
+ Этот компонент подходит для создания формы с текстовым вводом, где необходимо отображать иконки, подсказки и ошибки, а также поддерживать несколько размеров и состояний.
@@ -0,0 +1,242 @@
1
+ <template>
2
+ <div class="base-input-calendar">
3
+ <div class="base-input-calendar__wrapper">
4
+ <base-calendar
5
+ v-model="calendarModelValue"
6
+ :range="range"
7
+ :size="size"
8
+ :min-date="minDate"
9
+ >
10
+ <base-input
11
+ v-bind="{ ...$props, ...$attrs }"
12
+ :model-value="formattedValue"
13
+ :error="errorMessage || props.error"
14
+ @update:model-value="handleInputUpdate"
15
+ >
16
+ <template #['left-icon']>
17
+ <base-icon
18
+ name="calendar"
19
+ :size="size"
20
+ class="base-input-calendar__icon"
21
+ />
22
+ </template>
23
+ </base-input>
24
+ </base-calendar>
25
+ </div>
26
+ </div>
27
+ </template>
28
+
29
+ <script setup lang="ts">
30
+ import { computed, ref, watch } from 'vue';
31
+ import BaseInput from '../BaseInput/BaseInput.vue';
32
+ import BaseCalendar from '../BaseCalendar/BaseCalendar.vue';
33
+ import type { DateRange, TCoreInputCalendarProps } from '../../types/calendar';
34
+
35
+ const props = withDefaults(defineProps<TCoreInputCalendarProps>(), {
36
+ size: 'medium',
37
+ type: 'text',
38
+ modelValue: '',
39
+ placeholder: '',
40
+ error: '',
41
+ hint: '',
42
+ range: false,
43
+ minDate: null,
44
+ });
45
+
46
+ const emit: (event: 'update:modelValue', value: string) => void = defineEmits();
47
+
48
+ const inputValue = ref<string>('');
49
+
50
+ watch(
51
+ () => props.modelValue,
52
+ (newValue) => {
53
+ inputValue.value = typeof newValue === 'string' ? newValue : '';
54
+ },
55
+ { immediate: true },
56
+ );
57
+
58
+ function parseDateString(value: string | undefined | null): DateRange {
59
+ if (!value || typeof value !== 'string' || value.trim() === '') {
60
+ return null;
61
+ }
62
+
63
+ const parseSingleDate = (dateStr: string): Date | null => {
64
+ const dateRegex = /^(\d{2})\.(\d{2})\.(\d{4})$/;
65
+ const match = dateStr.match(dateRegex);
66
+
67
+ if (!match) {
68
+ console.warn(`Invalid date format: ${dateStr}. Expected format: dd.MM.yyyy`);
69
+ return null;
70
+ }
71
+
72
+ const [_, day, month, year] = match.map(Number);
73
+
74
+ if (day < 1 || day > 31 || month < 1 || month > 12 || year < 1900 || year > 9999) {
75
+ console.warn(`Invalid date values: ${dateStr}`);
76
+ return null;
77
+ }
78
+
79
+ const date = new Date(year, month - 1, day);
80
+ date.setHours(0, 0, 0, 0);
81
+
82
+ if (Number.isNaN(date.getTime())) {
83
+ console.warn(`Invalid date: ${dateStr}`);
84
+ return null;
85
+ }
86
+
87
+ return date;
88
+ };
89
+
90
+ try {
91
+ if (props.range) {
92
+ const [start, end] = value.split(' - ').map(part => part.trim());
93
+ if (!start || !end) {
94
+ return null;
95
+ }
96
+ const startDate = parseSingleDate(start);
97
+ const endDate = parseSingleDate(end);
98
+ if (!startDate || !endDate) {
99
+ return null;
100
+ }
101
+ if (startDate.getTime() > endDate.getTime()) {
102
+ console.warn('End date cannot be earlier than start date');
103
+ return null;
104
+ }
105
+ return [startDate, endDate];
106
+ }
107
+
108
+ const singleDate = parseSingleDate(value);
109
+ return singleDate;
110
+ } catch (error) {
111
+ console.error('Error parsing date string:', error);
112
+ return null;
113
+ }
114
+ }
115
+
116
+ function formatDateRange(value: DateRange | (string | Date)[]): string {
117
+ if (!value)
118
+ return '';
119
+
120
+ const formatSingleDate = (date: string | Date): string => {
121
+ if (typeof date === 'string') {
122
+ const parsed = new Date(date);
123
+ if (isNaN(parsed.getTime())) {
124
+ return '';
125
+ }
126
+ date = parsed;
127
+ }
128
+
129
+ const localDate = new Date(date.getTime() - date.getTimezoneOffset() * 60 * 1000);
130
+ const day = localDate.getDate().toString().padStart(2, '0');
131
+ const month = (localDate.getMonth() + 1).toString().padStart(2, '0');
132
+ const year = localDate.getFullYear();
133
+ return `${day}.${month}.${year}`;
134
+ };
135
+
136
+
137
+ if (Array.isArray(value)) {
138
+ return value.map(formatSingleDate).join(' - ');
139
+ }
140
+
141
+ return formatSingleDate(value);
142
+ }
143
+
144
+ const calendarModelValue = computed<DateRange>({
145
+ get: () => {
146
+ return parseDateString(props.modelValue);
147
+ },
148
+ set: (value: DateRange) => {
149
+ const formatted = formatDateRange(value);
150
+ inputValue.value = formatted;
151
+ emit('update:modelValue', formatted);
152
+ },
153
+ });
154
+
155
+ const formattedValue = computed<string>(() => {
156
+ return inputValue.value;
157
+ });
158
+
159
+ function handleInputUpdate(value: string) {
160
+ let filteredValue = value.replace(/[^0-9.\s-]/g, '');
161
+
162
+ if (props.range) {
163
+ const parts = filteredValue.split(' - ').map(part => part.trim());
164
+ const formattedParts = parts.map((part) => {
165
+ const digits = part.replace(/\D/g, '');
166
+ let formatted = '';
167
+
168
+ if (digits.length > 0) {
169
+ formatted += digits.slice(0, 2);
170
+ if (digits.length > 2) {
171
+ formatted += `.${digits.slice(2, 4)}`;
172
+ }
173
+ if (digits.length > 4) {
174
+ formatted += `.${digits.slice(4, 8)}`;
175
+ }
176
+ }
177
+
178
+ return formatted;
179
+ });
180
+
181
+ filteredValue = formattedParts[0];
182
+ if (formattedParts.length > 1) {
183
+ filteredValue += ` - ${formattedParts[1]}`;
184
+ }
185
+ } else {
186
+ const digits = filteredValue.replace(/\D/g, '');
187
+ let formatted = '';
188
+
189
+ if (digits.length > 0) {
190
+ formatted += digits.slice(0, 2);
191
+ if (digits.length > 2) {
192
+ formatted += `.${digits.slice(2, 4)}`;
193
+ }
194
+ if (digits.length > 4) {
195
+ formatted += `.${digits.slice(4, 8)}`;
196
+ }
197
+ }
198
+
199
+ filteredValue = formatted;
200
+ }
201
+
202
+ inputValue.value = filteredValue;
203
+ emit('update:modelValue', filteredValue);
204
+
205
+ const parsed = parseDateString(filteredValue);
206
+ if (parsed) {
207
+ const formatted = formatDateRange(parsed);
208
+ inputValue.value = formatted;
209
+ emit('update:modelValue', formatted);
210
+ }
211
+ }
212
+
213
+ const errorMessage = computed<string>(() => {
214
+ if (!inputValue.value) {
215
+ return '';
216
+ }
217
+
218
+ const formatRegex = props.range
219
+ ? /^(\d{2}\.\d{2}\.\d{4})\s*-\s*(\d{2}\.\d{2}\.\d{4})$|^(\d{0,2}\.?\d{0,2}\.?\d{0,4})\s*-\s*(\d{0,2}\.?\d{0,2}\.?\d{0,4})$|^(\d{0,2}\.?\d{0,2}\.?\d{0,4})$/
220
+ : /^(\d{2}\.\d{2}\.\d{4})$|^(\d{0,2}\.?\d{0,2}\.?\d{0,4})$/;
221
+
222
+ if (!formatRegex.test(inputValue.value)) {
223
+ return `Некорректный формат. Используйте dd.MM.yyyy${props.range ? ' - dd.MM.yyyy' : ''}`;
224
+ }
225
+
226
+ if (inputValue.value && !parseDateString(inputValue.value)) {
227
+ return `Некорректная дата. Используйте dd.MM.yyyy${props.range ? ' - dd.MM.yyyy' : ''}`;
228
+ }
229
+
230
+ return '';
231
+ });
232
+ </script>
233
+
234
+ <style scoped lang="scss">
235
+ .base-input-calendar {
236
+ width: max-content;
237
+
238
+ &__wrapper {
239
+ position: relative;
240
+ }
241
+ }
242
+ </style>