plugin-ui-for-kzt 0.0.9 → 0.0.11

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 (128) hide show
  1. package/dist/example/App.vue.d.ts +46 -0
  2. package/dist/example/main.d.ts +1 -0
  3. package/dist/index.js +57224 -3923
  4. package/dist/index.js.map +1 -0
  5. package/dist/sprite.svg +4 -0
  6. package/dist/src/components/BaseBreadCrumbs/BaseBreadCrumbs.vue.d.ts +34 -0
  7. package/dist/src/components/BaseButton/BaseButton.vue.d.ts +189 -0
  8. package/dist/src/components/BaseCalendar/BaseCalendar.vue.d.ts +176 -0
  9. package/dist/src/components/BaseCheckbox/BaseCheckbox.vue.d.ts +174 -0
  10. package/dist/src/components/BaseDropdown/BaseDropdown.vue.d.ts +151 -0
  11. package/dist/src/components/BaseIcon/BaseIcon.vue.d.ts +29 -0
  12. package/dist/src/components/BaseInputCalendar/BaseInputCalendar.vue.d.ts +202 -0
  13. package/dist/src/components/BaseInputEmail/BaseInputEmail.vue.d.ts +176 -0
  14. package/dist/src/components/BaseInputPhone/BaseInputPhone.vue.d.ts +251 -0
  15. package/dist/src/components/BaseLoader/BaseLoader.vue.d.ts +4 -0
  16. package/dist/src/components/BaseOpenedListItem/BaseOpenedListItem.vue.d.ts +148 -0
  17. package/dist/src/components/BaseRadio/BaseRadio.vue.d.ts +173 -0
  18. package/dist/src/components/BaseSegmentedButtons/BaseSegmentedButtons.vue.d.ts +142 -0
  19. package/dist/src/components/BaseSelect/BaseSelect.vue.d.ts +265 -0
  20. package/dist/src/components/BaseSiteInput/BaseSiteInput.vue.d.ts +119 -0
  21. package/dist/src/components/BaseTextarea/BaseTextarea.vue.d.ts +195 -0
  22. package/dist/src/components/BaseToggle/BaseToggle.vue.d.ts +166 -0
  23. package/dist/src/components/BaseTooltip/BaseTooltip.vue.d.ts +76 -0
  24. package/dist/src/components/DataTable/DataTable.vue.d.ts +42 -0
  25. package/dist/src/components/Modal/Modal.vue.d.ts +36 -0
  26. package/dist/src/components/Spinner/Spinner.vue.d.ts +46 -0
  27. package/dist/src/components/Toaster/Toaster.vue.d.ts +117 -0
  28. package/dist/src/components/Tooltip/Tooltip.vue.d.ts +43 -0
  29. package/dist/src/composables/kit/color.d.ts +6 -0
  30. package/dist/src/composables/kit/interactive.d.ts +7 -0
  31. package/dist/src/composables/kit/size.d.ts +6 -0
  32. package/dist/src/composables/kit/state.d.ts +17 -0
  33. package/dist/src/composables/kit/style.d.ts +8 -0
  34. package/dist/src/composables/kit/utils.d.ts +1 -0
  35. package/dist/src/icons/index.d.ts +4 -0
  36. package/dist/src/index.d.ts +33 -0
  37. package/example/App.vue +355 -0
  38. package/example/index.html +12 -0
  39. package/example/main.ts +8 -0
  40. package/example/shims-vue.d.ts +5 -0
  41. package/package.json +17 -7
  42. package/src/assets/icons/arrow-down.svg +3 -0
  43. package/src/assets/icons/calendar.svg +12 -0
  44. package/src/assets/icons/checkbox-circle.svg +3 -0
  45. package/src/assets/icons/checkbox.svg +3 -0
  46. package/src/assets/icons/email-sms.svg +4 -0
  47. package/src/assets/icons/help.svg +3 -0
  48. package/src/assets/icons/kg.svg +16 -0
  49. package/src/assets/icons/kz.svg +42 -0
  50. package/src/assets/icons/loader.svg +13 -0
  51. package/src/assets/icons/ru.svg +12 -0
  52. package/src/assets/icons/uz.svg +26 -0
  53. package/src/components/BaseBreadCrumbs/BaseBreadCrumbs.vue +142 -0
  54. package/src/components/BaseBreadCrumbs/README.md +49 -0
  55. package/src/components/BaseButton/BaseButton.vue +489 -0
  56. package/src/components/BaseButton/README.md +53 -0
  57. package/src/components/BaseCalendar/BaseCalendar.vue +231 -0
  58. package/src/components/BaseCalendar/README.md +126 -0
  59. package/src/components/BaseCheckbox/BaseCheckbox.vue +252 -0
  60. package/src/components/BaseCheckbox/README.md +110 -0
  61. package/src/components/BaseDropdown/BaseDropdown.vue +160 -0
  62. package/src/components/BaseDropdown/README.md +91 -0
  63. package/src/components/BaseIcon/BaseIcon.vue +47 -0
  64. package/src/components/BaseIcon/README.md +35 -0
  65. package/src/components/BaseInput/BaseInput.vue +300 -0
  66. package/src/components/BaseInput/README.md +85 -0
  67. package/src/components/BaseInputCalendar/BaseInputCalendar.vue +242 -0
  68. package/src/components/BaseInputCalendar/README.md +84 -0
  69. package/src/components/BaseInputCurrency/BaseInputCurrency.vue +198 -0
  70. package/src/components/BaseInputCurrency/README.md +57 -0
  71. package/src/components/BaseInputEmail/BaseInputEmail.vue +89 -0
  72. package/src/components/BaseInputEmail/README.md +71 -0
  73. package/src/components/BaseInputPhone/BaseInputPhone.vue +175 -0
  74. package/src/components/BaseLoader/BaseLoader.vue +45 -0
  75. package/src/components/BaseLoader/README.md +29 -0
  76. package/src/components/BaseOpenedListItem/BaseOpenedListItem.vue +216 -0
  77. package/src/components/BaseOpenedListItem/README.md +67 -0
  78. package/src/components/BaseRadio/BaseRadio.vue +283 -0
  79. package/src/components/BaseRadio/README.md +74 -0
  80. package/src/components/BaseSegmentedButtons/BaseSegmentedButtons.vue +89 -0
  81. package/src/components/BaseSegmentedButtons/README.md +75 -0
  82. package/src/components/BaseSelect/BaseSelect.vue +370 -0
  83. package/src/components/BaseSelect/README.md +95 -0
  84. package/src/components/BaseSiteInput/BaseSiteInput.vue +153 -0
  85. package/src/components/BaseTextarea/BaseTextarea.vue +212 -0
  86. package/src/components/BaseTextarea/README.md +75 -0
  87. package/src/components/BaseToggle/BaseToggle.vue +271 -0
  88. package/src/components/BaseToggle/README.md +76 -0
  89. package/src/components/BaseTooltip/BaseTooltip.vue +318 -0
  90. package/src/components/BaseTooltip/README.md +74 -0
  91. package/src/components/Modal/Modal.vue +3 -1
  92. package/src/components/Spinner/Spinner.vue +2 -1
  93. package/src/composables/kit/color.ts +14 -0
  94. package/src/composables/kit/interactive.ts +53 -0
  95. package/src/composables/kit/size.ts +15 -0
  96. package/src/composables/kit/state.ts +28 -0
  97. package/src/composables/kit/style.ts +18 -0
  98. package/src/composables/kit/utils.ts +7 -0
  99. package/src/icons/index.ts +9 -0
  100. package/src/index.ts +93 -2
  101. package/src/shims-context.d.ts +19 -0
  102. package/src/styles/index.scss +2 -1
  103. package/src/styles/root.scss +167 -0
  104. package/src/styles/variables.scss +160 -0
  105. package/src/types/breadcrumbs.d.ts +13 -0
  106. package/src/types/button.d.ts +13 -0
  107. package/src/types/calendar.d.ts +16 -0
  108. package/src/types/checkbox-radio.d.ts +15 -0
  109. package/src/types/dropdown.d.ts +20 -0
  110. package/src/types/icon.d.ts +8 -0
  111. package/src/types/input.d.ts +56 -0
  112. package/src/types/toggle.d.ts +12 -0
  113. package/src/types/tooltip.d.ts +8 -0
  114. package/src/types/utils.d.ts +37 -0
  115. package/src/vue-virtual-scroller.d.ts +9 -0
  116. package/tsconfig.json +3 -1
  117. package/webpack.config.js +90 -35
  118. package/dist/components/DataTable/DataTable.vue.d.ts +0 -3
  119. package/dist/components/Modal/Modal.vue.d.ts +0 -3
  120. package/dist/components/Spinner/Spinner.vue.d.ts +0 -3
  121. package/dist/components/Toaster/Toaster.vue.d.ts +0 -3
  122. package/dist/components/Tooltip/Tooltip.vue.d.ts +0 -3
  123. package/dist/index.d.ts +0 -11
  124. /package/dist/{components → src/components}/Toaster/timer.d.ts +0 -0
  125. /package/dist/{plugins → src/plugins}/modalPlugin.d.ts +0 -0
  126. /package/dist/{plugins → src/plugins}/toasterPlugin.d.ts +0 -0
  127. /package/dist/{store → src/store}/modal.d.ts +0 -0
  128. /package/dist/{types → src/types}/index.d.ts +0 -0
@@ -0,0 +1,74 @@
1
+ ### BaseRadio
2
+
3
+ Компонент радиокнопки с поддержкой различных состояний, размеров и стилизованного отображения.
4
+
5
+ ---
6
+
7
+ #### Props
8
+
9
+ - `modelValue: boolean`
10
+ Управляет активным состоянием радиокнопки. Используется с `v-model`.
11
+
12
+ - `label?: string`
13
+ Основной текст рядом с радиокнопкой.
14
+
15
+ - `subLabel?: string`
16
+ Дополнительный текст под основной меткой.
17
+
18
+ - `size?: 'small' | 'medium'`
19
+ Размер компонента. По умолчанию — `'medium'`.
20
+
21
+ - `disabled?: boolean`
22
+ Блокирует взаимодействие с компонентом. Визуально стилизуется как неактивный.
23
+
24
+ - `readonly?: boolean`
25
+ Делает компонент нечувствительным к кликам, но сохраняет внешний вид активного состояния.
26
+
27
+ - `id?: string`
28
+ Уникальный идентификатор для input-элемента (можно использовать для связки с label или в целях тестирования).
29
+
30
+ ---
31
+
32
+ #### Emits
33
+
34
+ - `update:modelValue`
35
+ Срабатывает при выборе радиокнопки. Возвращает `true`.
36
+
37
+ ---
38
+
39
+ #### Поведение
40
+
41
+ - Клик по компоненту устанавливает значение `modelValue` в `true`.
42
+ - Радиокнопка не может быть снята кликом, как чекбокс — выбор можно изменить только извне.
43
+ - Клик работает по всей области — по иконке, тексту или обводке.
44
+ - Компонент автоматически применяет стили фокуса, disabled, readonly и active.
45
+ - Внутренний `input[type="radio"]` скрыт, но полностью участвует в логике формы (включая передачу `id` и `attrs`).
46
+
47
+ ---
48
+
49
+ #### Стилизация
50
+
51
+ Компонент адаптируется под выбранный размер (`small`, `medium`) и применяет дизайн-состояния:
52
+
53
+ - `--is-active` — при `modelValue: true`
54
+ - `--is-readonly` — при `readonly: true`
55
+ - `--small-size`, `--medium-size` — в зависимости от `size`
56
+ - `is-disabled` — через `useKitState`, на основе `disabled`
57
+
58
+ Внутренние элементы оформлены в соответствии с дизайн-системой: иконка выбора, текст, а также эффекты наведения и фокуса.
59
+
60
+ ---
61
+
62
+ #### Пример использования
63
+
64
+ ```vue
65
+ <BaseRadio
66
+ v-model="selected"
67
+ label="Основной вариант"
68
+ sub-label="Дополнительная информация"
69
+ />
70
+ ```
71
+
72
+ ```ts
73
+ const selected = ref(false);
74
+ ```
@@ -0,0 +1,89 @@
1
+ <template>
2
+ <div class="base-segmented-buttons" :class="sizeClassList">
3
+ <div class="base-segmented-buttons__wrapper">
4
+ <base-button
5
+ v-for="option in options"
6
+ :key="option.value"
7
+ v-bind="buttonAttrs"
8
+ :color="modelValue === option.value ? 'primary' : 'secondary'"
9
+ @click="handleClick(option.value)"
10
+ >
11
+ {{ option.label }}
12
+ </base-button>
13
+ </div>
14
+ </div>
15
+ </template>
16
+
17
+ <script setup lang="ts">
18
+ import { computed } from 'vue';
19
+ import BaseButton from '../BaseButton/BaseButton.vue';
20
+ import { useKitSize } from '../../composables/kit/size'
21
+ import type { TSegmentedButtonsProps } from '../../types/button';
22
+
23
+ const props = withDefaults(defineProps<TSegmentedButtonsProps>(), {
24
+ size: 'medium',
25
+ options: () => [],
26
+ });
27
+
28
+ const emit = defineEmits<{
29
+ (e: 'update:modelValue', value: string): void;
30
+ }>();
31
+
32
+ const buttonAttrs = computed(() => {
33
+ return props;
34
+ });
35
+
36
+ const { sizeClassList } = useKitSize(props);
37
+
38
+ const handleClick = (value: string) => {
39
+ emit('update:modelValue', value);
40
+ };
41
+ </script>
42
+
43
+ <style lang="scss" scoped>
44
+ @import '../../styles/variables';
45
+ @import '../../styles/root';
46
+
47
+ .base-segmented-buttons {
48
+ $button: &;
49
+
50
+ &__wrapper {
51
+ display: inline-flex;
52
+ align-items: center;
53
+ background: var(--primary-black-200);
54
+ overflow: hidden;
55
+ }
56
+
57
+ &.--extra-small-size {
58
+ #{$button} {
59
+ &__wrapper {
60
+ border-radius: var(--corner-radius-xs);
61
+ }
62
+ }
63
+ }
64
+
65
+ &.--small-size {
66
+ #{$button} {
67
+ &__wrapper {
68
+ border-radius: var(--corner-radius-s);
69
+ }
70
+ }
71
+ }
72
+
73
+ &.--medium-size {
74
+ #{$button} {
75
+ &__wrapper {
76
+ border-radius: var(--corner-radius-m);
77
+ }
78
+ }
79
+ }
80
+
81
+ &.--large-size {
82
+ #{$button} {
83
+ &__wrapper {
84
+ border-radius: var(--corner-radius-m);
85
+ }
86
+ }
87
+ }
88
+ }
89
+ </style>
@@ -0,0 +1,75 @@
1
+ # BaseSegmentedButtons
2
+
3
+ Vue-компонент для создания сегментированных кнопок (аналог табов). Позволяет переключаться между опциями, выделяя активную кнопку цветом `primary`, а неактивные — цветом `secondary`. Поддерживает минимум 2 кнопки.
4
+
5
+ ## Props
6
+
7
+ | Проп | Тип | По умолчанию | Описание |
8
+ |--------------|---------------------|--------------|-------------------------------------------------------------------------|
9
+ | `modelValue` | `string` | `''` | Значение активной кнопки, соответствует `option.value`. Поддерживает `v-model`. |
10
+ | `size` | `'extra-small' \| 'small' \| 'medium' \| 'large'` | `'medium'` | Размер кнопок, влияет на высоту и отступы. |
11
+ | `options` | `Array<{ label: string, value: string }>` | `[]` | Массив опций, каждая с `label` (текст кнопки) и `value` (уникальное значение). |
12
+
13
+ ## События
14
+
15
+ - **`update:modelValue`**:
16
+ Эмитируется при клике на кнопку, передаёт новое значение `option.value`.
17
+ **Пример**: `update:modelValue="tab2"`.
18
+
19
+ ## Особенности
20
+
21
+ - **Сегментированные кнопки**:
22
+ Кнопки прилегают друг к другу, образуя единый блок с общим фоном. Скругление углов применяется к внешнему контейнеру.
23
+
24
+ - **Цвета**:
25
+ Активная кнопка: `primary` (синий фон).
26
+ Неактивные кнопки: `secondary` (серый фон).
27
+
28
+ - **Размеры**:
29
+ Поддерживает `extra-small`, `small`, `medium`, `large`. Размер влияет на высоту кнопок и радиус углов контейнера.
30
+
31
+ - **Наследование**:
32
+ Кнопки наследуют атрибуты `<base-button>`, такие как `disabled`, `loading`.
33
+
34
+ ## Использование
35
+
36
+ ```vue
37
+ <template>
38
+ <base-segmented-buttons
39
+ :options="[
40
+ { label: 'Tab 1', value: 'tab1' },
41
+ { label: 'Tab 2', value: 'tab2' },
42
+ { label: 'Tab 3', value: 'tab3' },
43
+ ]"
44
+ v-model="activeTab"
45
+ size="medium"
46
+ />
47
+ <p>Активная вкладка: {{ activeTab }}</p>
48
+ </template>
49
+
50
+ <script setup lang="ts">
51
+ import { ref } from 'vue';
52
+
53
+ const activeTab = ref('tab1');
54
+ </script>
55
+ ```
56
+
57
+ ## Зависимости
58
+
59
+ - **Компоненты**:
60
+ `<base-button>` — для рендеринга кнопок.
61
+
62
+ - **Компосаблы**:
63
+ `useKitSize` — для управления размерами.
64
+
65
+ ## Ограничения
66
+
67
+ - **Минимальное количество опций**:
68
+ Требуется минимум 2 опции в `options`. Рекомендуется добавить валидацию.
69
+
70
+
71
+ ## Примечания
72
+
73
+ - Кнопки автоматически стыкуются благодаря `inline-flex` и общему фону контейнера.
74
+ - Скругление углов применяется к `.base-segmented-buttons__wrapper`, а не к отдельным кнопкам.
75
+ - Поддерживает состояния ошибки и фокуса через стили `<base-button>`.
@@ -0,0 +1,370 @@
1
+ <template>
2
+ <div class="base-select" :class="[classList]">
3
+
4
+ <div class="base-select__wrapper">
5
+ <label v-if="label" :for="id" class="base-select__label"> {{ label }}</label>
6
+
7
+ <base-dropdown
8
+ v-model="dropdownVisible"
9
+ transition-name="top"
10
+ auto-close
11
+ :disabled="actualDisabled"
12
+ >
13
+ <template #top>
14
+ <div
15
+ :data-error="Boolean(error)"
16
+ :data-disabled="disabled"
17
+ class="base-select__header"
18
+ >
19
+ <slot
20
+ name="header"
21
+ :value="actualOption"
22
+ >
23
+ <div v-if="$slots.headerIcon" class="base-select__header_prefix_icon">
24
+ <slot name="headerIcon" />
25
+ </div>
26
+
27
+ <div
28
+ v-if="actualOption"
29
+ class="base-select__header_value"
30
+ >
31
+ {{ actualOption.name }}
32
+ </div>
33
+
34
+ <div
35
+ v-else
36
+ class="base-select__placeholder"
37
+ >
38
+ {{ placeholder }}
39
+ </div>
40
+ </slot>
41
+
42
+ <div
43
+ v-if="!readonly"
44
+ class="base-select__arrow-wrapper"
45
+ >
46
+
47
+ <div class='base-select__arrow'>
48
+ <base-icon
49
+ name="arrow-down"
50
+ :size="size"
51
+ />
52
+ </div>
53
+ </div>
54
+ </div>
55
+ </template>
56
+
57
+ <template #dropdown>
58
+ <div
59
+ v-if="(options ?? []).length"
60
+ class="base-select__dropdown"
61
+ >
62
+ <dynamic-scroller
63
+ :items="options"
64
+ :min-item-size="36"
65
+ key-field="id"
66
+ class="base-select__list"
67
+ >
68
+ <template #default="{ item, index, active }">
69
+ <dynamic-scroller-item
70
+ :item="item"
71
+ :active="active"
72
+ :size-dependencies="[item.name]"
73
+ :data-index="index"
74
+ >
75
+ <div class="base-select__item-wrapper">
76
+ <base-opened-list-item
77
+ v-bind="listItemAttrs(item)"
78
+ :key="item.id"
79
+ :size="size"
80
+ >
81
+ <template #icon>
82
+ <div v-if="$slots.iconItem">
83
+ <slot name="iconItem" :item="item" />
84
+ </div>
85
+ </template>
86
+ {{ listItemAttrs(item).title }}
87
+ </base-opened-list-item>
88
+ </div>
89
+ </dynamic-scroller-item>
90
+ </template>
91
+ </dynamic-scroller>
92
+ </div>
93
+ </template>
94
+ </base-dropdown>
95
+
96
+ <div v-if="(error && typeof error === 'string') || hint" class="base-select__hint">
97
+ {{ error || hint }}
98
+ </div>
99
+ </div>
100
+ </div>
101
+ </template>
102
+
103
+ <script setup lang="ts">
104
+ import { computed, ref, watch } from 'vue';
105
+ import 'vue-virtual-scroller/dist/vue-virtual-scroller.css';
106
+ import { DynamicScroller, DynamicScrollerItem } from 'vue-virtual-scroller';
107
+ import type { ICoreSelectProps, TSelectValue, ICoreSelectBaseProps } from '../../types/input';
108
+ import BaseDropdown from '../BaseDropdown/BaseDropdown.vue';
109
+ import BaseOpenedListItem from '../BaseOpenedListItem/BaseOpenedListItem.vue';
110
+ import { useKitSize } from '../../composables/kit/size';
111
+ import { useKitState } from '../../composables/kit/state';
112
+ import { useKitStyle } from '../../composables/kit/style';
113
+
114
+ const props = withDefaults(defineProps<ICoreSelectProps & {
115
+ modelValue?: TSelectValue
116
+ }>(), {
117
+ options: () => [],
118
+ size: 'medium',
119
+ });
120
+
121
+ const emit = defineEmits<{
122
+ (e: 'update:modelValue', value: TSelectValue): void
123
+ (e: 'change', value: TSelectValue): void
124
+ }>();
125
+
126
+ const actualValue = ref<TSelectValue>(props.modelValue ?? '');
127
+ const actualOption = computed(() =>
128
+ props.options?.find((item: ICoreSelectBaseProps) => item.id === actualValue.value) || null
129
+ );
130
+
131
+ watch(() => props.modelValue, (val) => {
132
+ actualValue.value = val;
133
+ }, { immediate: true });
134
+
135
+ function handleInput(value: TSelectValue) {
136
+ actualValue.value = value;
137
+ emit('update:modelValue', value);
138
+ emit('change', value);
139
+ }
140
+ const actualDisabled = computed(() => props.disabled || !props.options?.length);
141
+ const dropdownVisible = ref(false);
142
+ const { sizeClassList } = useKitSize(props);
143
+ const { stateClassList } = useKitState(props);
144
+ const { styleClassList } = useKitStyle(props);
145
+
146
+ const listItemAttrs = computed(() => (item: ICoreSelectBaseProps) => ({
147
+ icon: item.icon,
148
+ active: actualValue.value === item.id,
149
+ title: item.name,
150
+ style: item.style,
151
+ disabled: item.disabled,
152
+ class: 'base-select__item',
153
+ onClick: () => handleInput(item.id),
154
+ }));
155
+
156
+ const classList = computed(() => [
157
+ sizeClassList.value,
158
+ stateClassList.value,
159
+ styleClassList.value,
160
+ {
161
+ '--is-readonly': props.readonly,
162
+ '--is-disabled': actualDisabled.value,
163
+ },
164
+ ]);
165
+ </script>
166
+
167
+
168
+ <style lang="scss">
169
+ @import '../../styles/variables';
170
+ @import '../../styles/root';
171
+
172
+ .base-select {
173
+ $select: &;
174
+
175
+ min-width: 320px;
176
+
177
+ .dropdown {
178
+ &.--is-opened {
179
+ #{$select} {
180
+ &__arrow {
181
+ transform: translate3d(0, -50%, 0) rotate(180deg);
182
+ }
183
+
184
+ &__header {
185
+ border: 1px solid var(--primary-black-300);
186
+ outline: 4px solid var(--effects-primary-focus);
187
+ }
188
+ }
189
+
190
+ .dropdown__dropdown {
191
+ margin-top: 4px;
192
+ transition: all var(--transition);
193
+ }
194
+ }
195
+ }
196
+
197
+ &.--is-error {
198
+ #{$select}__hint {
199
+ color: var(--error-red);
200
+ }
201
+
202
+ #{$select}__label {
203
+ border-color: var(--error-red-light-01);
204
+ }
205
+
206
+ #{$select}__arrow > * {
207
+ color: var(--error-red);
208
+ }
209
+
210
+ #{$select}__header {
211
+ border: 1px solid var(--error-red-light-01);
212
+ }
213
+ }
214
+
215
+ &__wrapper {
216
+ display: flex;
217
+ flex-direction: column;
218
+ gap: var(--spacing-2s);
219
+ width: 100%;
220
+ height: 100%;
221
+ }
222
+
223
+ &__header {
224
+ position: relative;
225
+ display: flex;
226
+ column-gap: var(--spacing-s);
227
+ align-items: center;
228
+ user-select: none;
229
+ background: var(--bg-light);
230
+ border: 1px solid var(--primary-black-300);
231
+ border-radius: var(--corner-radius-m);
232
+ }
233
+
234
+ &__placeholder {
235
+ @include text-clamp(1);
236
+
237
+ color: var(--primary-text-tertiary);
238
+ }
239
+
240
+ &__arrow-wrapper {
241
+ position: relative;
242
+ flex-shrink: 0;
243
+ margin-left: auto;
244
+ }
245
+
246
+ &__arrow {
247
+ position: absolute;
248
+ top: 50%;
249
+ color: var(--ui-colors-input-icon-default);
250
+ transition: transform var(--transition);
251
+ transform: translate3d(0, -50%, 0);
252
+ }
253
+
254
+ &__dropdown {
255
+ width: 100%;
256
+ height: 100%;
257
+ }
258
+
259
+ &__list {
260
+ width: 100%;
261
+ }
262
+
263
+ &__item {
264
+ width: 100%;
265
+ }
266
+
267
+ &.--small-size {
268
+ #{$select} {
269
+ &__label {
270
+ font: var(--typography-text-s-medium);
271
+ }
272
+
273
+ &__hint {
274
+ font: var(--typography-text-s-regular);
275
+ }
276
+
277
+ &__header_value {
278
+ font: var(--typography-text-m-regular);
279
+ }
280
+
281
+ &__dropdown {
282
+ border-radius: var(--corner-radius-s);
283
+ }
284
+
285
+ &__header {
286
+ height: 22px;
287
+ padding: var(--spacing-s) var(--spacing-2l);
288
+ border-radius: var(--corner-radius-s);
289
+ }
290
+
291
+ &__arrow, &__header_prefix_icon, &__arrow-wrapper {
292
+ width: 20px;
293
+ height: 20px;
294
+ }
295
+ }
296
+ }
297
+
298
+ &.--medium-size {
299
+ #{$select} {
300
+ &__label {
301
+ font: var(--typography-text-s-medium);
302
+ }
303
+
304
+ &__hint {
305
+ font: var(--typography-text-s-regular);
306
+ }
307
+
308
+ &__header_value {
309
+ font: var(--typography-text-m-regular);
310
+ }
311
+
312
+ &__header {
313
+ height: 22px;
314
+ padding: var(--spacing-m) var(--spacing-2l);
315
+ }
316
+
317
+ &__arrow, &__header_prefix_icon, &__arrow-wrapper {
318
+ width: 24px;
319
+ height: 24px;
320
+ }
321
+ }
322
+ }
323
+
324
+ &.--large-size {
325
+ #{$select} {
326
+ &__label {
327
+ font: var(--typography-text-m-medium);
328
+ }
329
+
330
+ &__hint {
331
+ font: var(--typography-text-m-regular);
332
+ }
333
+
334
+ &__header_value {
335
+ font: var(--typography-text-l-regular);
336
+ }
337
+
338
+ &__header {
339
+ height: 30px;
340
+ padding: var(--spacing-m) var(--spacing-l);
341
+ }
342
+
343
+ &__arrow, &__header_prefix_icon, &__arrow-wrapper {
344
+ width: 32px;
345
+ height: 32px;
346
+ }
347
+ }
348
+ }
349
+
350
+ &.--is-disabled {
351
+ #{$select} {
352
+ &__placeholder {
353
+ color: var(--ui-colors-input-text-disabled);
354
+ }
355
+
356
+ &__arrow {
357
+ color: var(--ui-colors-input-icon-disabled);
358
+ }
359
+ }
360
+ }
361
+
362
+ &.--is-readonly {
363
+ #{$select} {
364
+ &__header {
365
+ pointer-events: none;
366
+ }
367
+ }
368
+ }
369
+ }
370
+ </style>
@@ -0,0 +1,95 @@
1
+ ## 🔽 BaseSelect
2
+
3
+ Компонент выпадающего списка с поддержкой виртуального скролла, состояний (`readonly`, `disabled`), и пользовательских иконок. Используется для выбора значения из набора опций.
4
+
5
+ ---
6
+
7
+ ### ✅ Пример использования
8
+
9
+ ```vue
10
+ <base-select
11
+ v-model="selected"
12
+ :options="[
13
+ { id: 1, name: 'Вариант 1' },
14
+ { id: 2, name: 'Вариант 2', disabled: true },
15
+ { id: 3, name: 'Вариант 3' }
16
+ ]"
17
+ label="Выбор опции"
18
+ placeholder="Выберите значение"
19
+ hint="Можно выбрать только один вариант"
20
+ :error="showError && 'Обязательное поле'"
21
+ />
22
+ ```
23
+
24
+ ---
25
+
26
+ ### ⚙️ Пропсы
27
+
28
+ - `modelValue?: string | number | boolean | null`
29
+ Управляемое значение выбранной опции (по `id`).
30
+
31
+ - `id: string`
32
+ Уникальный идентификатор для поля и `label`.
33
+
34
+ - `options?: Array<{ id: string | number | boolean | null, name: string, disabled?: boolean, icon?: string, style?: [key: string] }>`
35
+ Список доступных опций. Каждая должна иметь уникальный `id` и `name`.
36
+
37
+ - `label?: string`
38
+ Название поля. Показывается над селектом.
39
+
40
+ - `placeholder?: string`
41
+ Текст-заполнитель, отображаемый, если ничего не выбрано.
42
+
43
+ - `readonly?: boolean`
44
+ Делает поле только для чтения (неактивное выпадающее меню).
45
+
46
+ - `disabled?: boolean`
47
+ Полностью деактивирует селект и скрывает стрелку.
48
+
49
+ - `hint?: string`
50
+ Текст-подсказка под селектом.
51
+
52
+ - `error?: string | boolean`
53
+ Сообщение об ошибке. Если передана строка — она показывается как текст ошибки, а поле окрашивается в красный.
54
+
55
+ - `size?: 'small' | 'medium' | 'large'`
56
+ Управляет размерами шрифта, паддингов и иконок.
57
+
58
+ - `multiple?: boolean`
59
+ Поддержка множественного выбора (не реализовано в текущем компоненте, зарезервировано).
60
+
61
+ - `transitionName?: string`
62
+ Название transition-анимации (например, `"top"`).
63
+
64
+ ---
65
+
66
+ ### 📤 События
67
+
68
+ - `update:modelValue`
69
+ Эмитится при выборе значения. Возвращает `id` выбранного пункта.
70
+
71
+ - `change`
72
+ Также эмитится при выборе. Полезно для побочных эффектов.
73
+
74
+ ---
75
+
76
+ ### 🧩 Слоты
77
+
78
+ - `header`
79
+ Полная кастомизация заголовка выпадашки (вместо названия опции).
80
+
81
+ - `headerIcon`
82
+ Иконка слева от выбранной опции.
83
+
84
+ - `iconItem`
85
+ Иконка внутри опций (например, чекбокс, галочка или иконка элемента).
86
+
87
+ ---
88
+
89
+ ### 🎨 Поведение и стили
90
+
91
+ - Поддерживаются состояния: `--is-disabled`, `--is-readonly`, `--is-error`, `--small-size`, `--medium-size`, `--large-size`.
92
+ - При `disabled` или отсутствии опций (`options.length === 0`) меню не открывается.
93
+ - Ошибки подсвечиваются красной рамкой, текстом и цветом иконки.
94
+ - Используется `DynamicScroller` для отображения большого списка опций с виртуальным скроллом.
95
+ - Анимация открытия выпадашки настраивается через `transition-name`.