plugin-ui-for-kzt 0.0.29 → 0.0.31

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 (31) hide show
  1. package/dist/components/BaseButton/BaseButton.vue.d.ts +2 -2
  2. package/dist/components/BaseCalendar/BaseCalendar.vue.d.ts +1 -1
  3. package/dist/components/BaseCheckbox/BaseCheckbox.vue.d.ts +2 -2
  4. package/dist/components/BaseDefaultPages/BaseDefaultPages.vue.d.ts +2 -0
  5. package/dist/components/BaseDropdown/BaseDropdown.vue.d.ts +2 -2
  6. package/dist/components/BaseField/BaseField.vue.d.ts +2 -2
  7. package/dist/components/BaseInput/BaseInput.vue.d.ts +3 -3
  8. package/dist/components/BaseInputCalendar/BaseInputCalendar.vue.d.ts +6 -6
  9. package/dist/components/BaseInputCurrency/BaseInputCurrency.vue.d.ts +3 -3
  10. package/dist/components/BaseInputEmail/BaseInputEmail.vue.d.ts +3 -3
  11. package/dist/components/BaseInputPhone/BaseInputPhone.vue.d.ts +3 -3
  12. package/dist/components/BaseOpenedListItem/BaseOpenedListItem.vue.d.ts +2 -2
  13. package/dist/components/BaseRadio/BaseRadio.vue.d.ts +2 -2
  14. package/dist/components/BaseSegmentedButtons/BaseSegmentedButtons.vue.d.ts +3 -3
  15. package/dist/components/BaseSelect/BaseSelect.vue.d.ts +7 -4
  16. package/dist/components/BaseTextarea/BaseTextarea.vue.d.ts +3 -3
  17. package/dist/components/BaseToggle/BaseToggle.vue.d.ts +2 -2
  18. package/dist/components/BaseTooltip/BaseTooltip.vue.d.ts +1 -1
  19. package/dist/index.js +1 -1
  20. package/example/App.vue +3 -270
  21. package/package.json +1 -1
  22. package/src/components/BaseDefaultPages/BaseDefaultPages.vue +0 -9
  23. package/src/components/BaseInputPhone/BaseInputPhone.vue +3 -3
  24. package/src/components/BaseModal/BaseModal.vue +1 -3
  25. package/src/components/BaseSelect/BaseSelect.vue +9 -0
  26. package/src/components/BaseSelect/README.md +110 -1
  27. package/src/types/default-pages.d.ts +1 -0
  28. package/dist/assets/0e28e37419c99ac65b12.png +0 -0
  29. package/dist/assets/264165b2b0e8a6840eb0.png +0 -0
  30. /package/{src/assets → example}/404.png +0 -0
  31. /package/{src/assets → example}/tech-work.png +0 -0
package/example/App.vue CHANGED
@@ -4,280 +4,13 @@
4
4
 
5
5
  <section>
6
6
  <h2>BaseUpload с drag & drop</h2>
7
- <div style="display: flex; gap: 20px;">
8
- <div style="width: 400px;">
9
- <h3>Множественная загрузка (multiple: true)</h3>
10
- <BaseUpload
11
- :accepted-formats="['.pdf', '.docx', '.jpeg', '.jpg', '.png', '.svg']"
12
- :multiple="true"
13
- >
14
- <template #title>
15
- Перетащите файлы сюда или кликните для выбора
16
- </template>
17
- <template #description>
18
- Поддерживаемые форматы: PDF, DOCX, JPEG, JPG, PNG, SVG до 25 MB
19
- </template>
20
- </BaseUpload>
21
- </div>
22
-
23
- <div style="width: 400px;">
24
- <h3>Одиночная загрузка (multiple: false)</h3>
25
- <BaseUpload
26
- :accepted-formats="['.pdf', '.docx', '.jpeg', '.jpg', '.png', '.svg']"
27
- :multiple="false"
28
- >
29
- <template #title>
30
- Перетащите файл сюда или кликните для выбора
31
- </template>
32
- <template #description>
33
- Только один файл: PDF, DOCX, JPEG, JPG, PNG, SVG до 25 MB
34
- </template>
35
- </BaseUpload>
36
- </div>
37
- </div>
38
- </section>
39
-
40
- <section>
41
- <h2>Сравнение режимов</h2>
42
- <div style="display: flex; gap: 20px;">
43
- <div style="width: 300px;">
44
- <h3>Дефолтный режим</h3>
45
- <BaseSelect
46
- id="select4"
47
- :options="options"
48
- :placeholder="'Выберите опцию'"
49
- :label="'Опция'"
50
- :searchable="false"
51
- />
52
- </div>
53
- <div style="width: 300px;">
54
- <h3>Поисковый режим (новое поведение)</h3>
55
- <BaseSelect
56
- id="select5"
57
- :options="searchableOptions"
58
- :placeholder="'Выберите страну'"
59
- :label="'Страна'"
60
- :searchable="true"
61
- :error="errorMessage"
62
- @error="handleSelectError"
63
- >
64
- <template #empty>
65
- <div>
66
- Нет стран
67
- </div>
68
- </template>
69
- </BaseSelect>
70
- <p style="font-size: 12px; color: #666; margin-top: 8px;">
71
- • Клик по стрелке открывает dropdown<br/>
72
- • При точном совпадении элемент выбирается автоматически<br/>
73
- • При выборе элемента иконка меняется на крестик<br/>
74
- • Клик по крестику очищает выбор<br/>
75
- • Клик по инпуту не очищает выбор<br/>
76
- • При отсутствии совпадений эмитится ошибка
77
- </p>
78
- <div v-if="errorMessage" style="color: red; font-size: 12px; margin-top: 4px;">
79
- Элемент не найден
80
- </div>
81
- </div>
82
- </div>
83
- </section>
84
-
85
- <section>
86
- <h2>Тест тултипов (несколько экземпляров)</h2>
87
- <div style="display: flex; gap: 20px; align-items: center;">
88
- <BaseTooltip
89
- id="tooltip1"
90
- title="Первый тултип"
91
- content="Это первый тултип на странице"
92
- trigger="click"
93
- >
94
- <template #trigger>
95
- <button style="padding: 8px 16px; border: 1px solid #ccc; background: white; cursor: pointer;">
96
- Тултип 1 (click)
97
- </button>
98
- </template>
99
- </BaseTooltip>
100
-
101
- <BaseTooltip
102
- id="tooltip2"
103
- title="Второй тултип"
104
- content="Это второй тултип на странице"
105
- trigger="click"
106
- >
107
- <template #trigger>
108
- <button style="padding: 8px 16px; border: 1px solid #ccc; background: white; cursor: pointer;">
109
- Тултип 2 (click)
110
- </button>
111
- </template>
112
- </BaseTooltip>
113
-
114
- <BaseTooltip
115
- id="tooltip3"
116
- title="Третий тултип"
117
- content="Это третий тултип на странице"
118
- trigger="hover"
119
- >
120
- <template #trigger>
121
- <button style="padding: 8px 16px; border: 1px solid #ccc; background: white; cursor: pointer;">
122
- Тултип 3 (hover)
123
- </button>
124
- </template>
125
- </BaseTooltip>
126
- </div>
127
- </section>
128
-
129
- <section>
130
- <h2>BaseTable - Обычная таблица</h2>
131
- <BaseTable
132
- :columns="tableColumns"
133
- :data="tableData"
134
- :show-row-selection="true"
135
- :show-actions="true"
136
- :actions="tableActions"
137
- />
138
- </section>
139
-
140
- <section>
141
- <h2>BaseTable - С пагинацией</h2>
142
- <BaseTable
143
- :columns="tableColumns"
144
- :data="tableData"
145
- :pagination="{ perPage: 5, currentPage: 1 }"
146
- :show-row-selection="true"
147
- :show-actions="true"
148
- :actions="tableActions"
149
- @page-change="handlePageChange"
150
- />
151
- </section>
152
-
153
- <section>
154
- <h2>BaseTable - С виртуальным скроллом (высота: 400px)</h2>
155
- <p style="font-size: 14px; color: #666; margin-bottom: 12px;">
156
- • Колонки ID, Имя, Email, Статус теперь имеют заданные ширины: 80px, 200px, 1fr, 120px<br/>
157
- • Ширины колонок в thead и виртуальном скролле полностью синхронизированы<br/>
158
- • Можно использовать px, fr, auto или числовые значения для ширины<br/>
159
- • Автоматическая компенсация ширины скроллбара - колонки не разъезжаются<br/>
160
- • Настраиваемое выравнивание: ID - center, Имя - left, Email - left, Статус - right
161
- </p>
162
- <BaseTable
163
- :columns="tableColumns"
164
- :data="largeTableData"
165
- :virtual-scroll="true"
166
- :virtual-scroll-threshold="50"
167
- :virtual-scroll-height="400"
168
- :show-row-selection="true"
169
-
170
-
171
- @scroll="handleScroll"
172
- @scroll-end="handleScrollEnd"
173
- />
7
+ <BaseDefaultPages title="title" description="description" buttonText="buttonText" type="tech-work" imagePath="./tech-work.png" />
8
+ <BaseDefaultPages title="title" description="description" buttonText="buttonText" type="404" imagePath="./404.png" />
174
9
  </section>
175
10
  </div>
176
11
  </template>
177
12
  <script setup lang="ts">
178
- import { ref, watch, computed } from 'vue'
179
- import BaseSelect from '../src/components/BaseSelect/BaseSelect.vue'
180
- import BaseTooltip from '../src/components/BaseTooltip/BaseTooltip.vue'
181
- import BaseUpload from '../src/components/BaseUpload/BaseUpload.vue'
182
- import BaseTable from '../src/components/BaseTable/BaseTable.vue'
183
-
184
- const options = ref([
185
- { id: 1, name: ' Option 1 Option 1 Option 1Option 1 Option 1Option 1' },
186
- { id: 2, name: 'Option 2 Option 2 Option 2Option 2 Option 2Option 2' },
187
- { id: 3, name: 'Option 3 Option 3 Option 3Option 3 Option 3Option 3' },
188
- ])
189
-
190
- const searchableOptions = ref([
191
- { id: 'kz', name: 'Казахстан Казахстан Казахстан Казахстан Казахстан Казахстан Казахстан Казахстан Казахстан' },
192
- { id: 'ru', name: 'Россия' },
193
- { id: 'kg', name: 'Кыргызстан' },
194
- { id: 'uz', name: 'Узбекистан' },
195
- { id: 'tj', name: 'Таджикистан' },
196
- { id: 'tm', name: 'Туркменистан' },
197
- { id: 'af', name: 'Афганистан' },
198
- { id: 'mn', name: 'Монголия' },
199
- { id: 'cn', name: 'Китай' },
200
- { id: 'ir', name: 'Иран' },
201
- { id: 'tr', name: 'Турция' },
202
- { id: 'ge', name: 'Грузия' },
203
- { id: 'am', name: 'Армения' },
204
- { id: 'az', name: 'Азербайджан' },
205
- { id: 'by', name: 'Беларусь' },
206
- { id: 'ua', name: 'Украина' },
207
- { id: 'md', name: 'Молдова' },
208
- { id: 'lt', name: 'Литва' },
209
- { id: 'lv', name: 'Латвия' },
210
- { id: 'ee', name: 'Эстония' },
211
- ])
212
-
213
- const errorMessage = ref(false)
214
-
215
- function handleSelectError() {
216
- errorMessage.value = true
217
- }
218
-
219
- // Данные для таблицы
220
- const tableColumns = ref([
221
- { key: 'id', label: 'ID', sortable: true, width: '80px', align: 'center' as const },
222
- { key: 'name', label: 'Имя', sortable: true, width: '200px', align: 'left' as const },
223
- { key: 'email', label: 'Email', sortable: true, width: '1fr', align: 'left' as const },
224
- { key: 'status', label: 'Статус', sortable: true, width: '120px', align: 'right' as const }
225
- ])
226
-
227
- const tableData = ref([
228
- { id: 1, name: 'Иван Иванов', email: 'ivan@example.com', status: 'Активен' },
229
- { id: 2, name: 'Петр Петров', email: 'petr@example.com', status: 'Неактивен Неактивен Неактивен Неактивен Неактивен Неактивен Неактивен Неактивен Неактивен Неактивен Неактивен Неактивен' },
230
- { id: 3, name: 'Мария Сидорова', email: 'maria@example.com', status: 'Активен' },
231
- { id: 4, name: 'Анна Козлова', email: 'anna@example.com', status: 'Активен' },
232
- { id: 5, name: 'Сергей Волков', email: 'sergey@example.com', status: 'Неактивен' },
233
- { id: 6, name: 'Елена Морозова', email: 'elena@example.com', status: 'Активен' },
234
- { id: 7, name: 'Дмитрий Лебедев', email: 'dmitry@example.com', status: 'Активен' },
235
- { id: 8, name: 'Ольга Новикова', email: 'olga@example.com', status: 'Неактивен' }
236
- ])
237
-
238
- // Большой набор данных для виртуального скролла
239
- const largeTableData = computed(() => {
240
- const data: any[] = []
241
- for (let i = 1; i <= 100; i++) {
242
- data.push({
243
- id: i,
244
- name: `Пользователь ${i}`,
245
- email: `user${i}@example.com`,
246
- status: i % 3 === 0 ? 'Неактивен Неактивен Неактивен Неактивен' : 'Активен'
247
- })
248
- }
249
- console.log("🚀 ~ data:", data)
250
- return data
251
- })
252
-
253
- const tableActions = ref([
254
- {
255
- key: 'edit',
256
- icon: 'edit-table',
257
- handler: (row: any) => {
258
- console.log('Редактировать:', row)
259
- }
260
- },
261
- {
262
- key: 'delete',
263
- icon: 'trash-table',
264
- handler: (row: any) => {
265
- console.log('Удалить:', row)
266
- }
267
- }
268
- ])
269
-
270
- const handlePageChange = (page: number) => {
271
- console.log('Смена страницы:', page)
272
- }
273
-
274
- const handleScroll = (payload: any) => {
275
- console.log('Скролл:', payload)
276
- }
277
-
278
- const handleScrollEnd = () => {
279
- console.log('Достигнут конец списка - можно подгрузить еще данные')
280
- }
13
+ import BaseDefaultPages from '../src/components/BaseDefaultPages/BaseDefaultPages.vue'
281
14
  </script>
282
15
 
283
16
  <style lang="scss" scoped>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "plugin-ui-for-kzt",
3
- "version": "0.0.29",
3
+ "version": "0.0.31",
4
4
  "description": "plugin-ui for kazaktelekom",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -33,18 +33,9 @@
33
33
  import { computed } from 'vue';
34
34
  import BaseButton from '../BaseButton/BaseButton.vue';
35
35
  import type { IDefaultPagesProps } from '../../types/default-pages';
36
- import techWorkImage from '../../assets/tech-work.png';
37
- import error404Image from '../../assets/404.png';
38
36
 
39
37
  const props = defineProps<IDefaultPagesProps>();
40
38
 
41
- const imagePath = computed(() => {
42
- const images:Record<string, any> = {
43
- 'tech-work': techWorkImage,
44
- '404': error404Image
45
- };
46
- return images[props.type] || '';
47
- });
48
39
  const imageContainer = computed<string[]>(()=> [
49
40
  `base-default-pages__image-container--${props.type}`
50
41
  ])
@@ -137,7 +137,7 @@ const handleInput = (value: string) => {
137
137
  &.--small-size {
138
138
  #{$input} {
139
139
  :deep(.base-select) {
140
- width: 71px;
140
+ width: auto;
141
141
  }
142
142
  }
143
143
  }
@@ -145,7 +145,7 @@ const handleInput = (value: string) => {
145
145
  &.--medium-size {
146
146
  #{$input} {
147
147
  &__select {
148
- width: 75px;
148
+ width: auto;
149
149
  }
150
150
  }
151
151
  }
@@ -153,7 +153,7 @@ const handleInput = (value: string) => {
153
153
  &.--large-size {
154
154
  #{$input} {
155
155
  &__select {
156
- width: 87px;
156
+ width: auto;
157
157
  }
158
158
  }
159
159
  }
@@ -67,7 +67,6 @@ const handleOverlayClick = (id: string) => {
67
67
  .base-modal {
68
68
  background: var(--primary-black-white);
69
69
  border-radius: var(--corner-radius-m);
70
- overflow: hidden;
71
70
  min-width: 335px;
72
71
  transform: scale(0.7);
73
72
  animation: slideIn 0.3s ease forwards;
@@ -130,8 +129,7 @@ const handleOverlayClick = (id: string) => {
130
129
  }
131
130
 
132
131
  &__content {
133
- max-height: 70vh;
134
- overflow-y: auto;
132
+ max-height: 80vh;
135
133
  color: var(--primary-text-tertiary);
136
134
  font: var(--typography-text-s-regular);
137
135
 
@@ -149,6 +149,9 @@ const emit = defineEmits<{
149
149
  (e: 'update:modelValue', value: TSelectValue): void
150
150
  (e: 'change', value: TSelectValue): void
151
151
  (e: 'error'): void
152
+ (e: 'search', query: string): void
153
+ (e: 'open'): void
154
+ (e: 'clear'): void
152
155
  }>();
153
156
 
154
157
  const actualValue = ref<TSelectValue>(props.modelValue ?? '');
@@ -180,6 +183,8 @@ function handleInputChange(event: Event) {
180
183
  const target = event.target as HTMLInputElement;
181
184
  const inputValue = target.value;
182
185
  searchQuery.value = inputValue;
186
+
187
+ emit('search', inputValue);
183
188
 
184
189
  if (inputValue) {
185
190
  const exactMatch = props.options?.find((option: ICoreSelectOption) =>
@@ -201,6 +206,7 @@ function handleIconClick(event: Event) {
201
206
  event.stopPropagation();
202
207
  if (actualOption.value) {
203
208
  handleInput('');
209
+ emit('clear');
204
210
  } else {
205
211
  dropdownVisible.value = !dropdownVisible.value;
206
212
  }
@@ -225,6 +231,9 @@ watch(() => props.error, (error) => {
225
231
  });
226
232
 
227
233
  watch(dropdownVisible, (isVisible) => {
234
+ if (isVisible) {
235
+ emit('open');
236
+ }
228
237
  if (!isVisible) {
229
238
  if (searchQuery.value && !actualOption.value) {
230
239
  emit('error');
@@ -4,8 +4,9 @@
4
4
 
5
5
  ---
6
6
 
7
- ### ✅ Пример использования
7
+ ### ✅ Примеры использования
8
8
 
9
+ #### Базовый пример
9
10
  ```vue
10
11
  <base-select
11
12
  v-model="selected"
@@ -21,6 +22,99 @@
21
22
  />
22
23
  ```
23
24
 
25
+ #### Server-side поиск (рекомендуемый подход)
26
+ ```vue
27
+ <template>
28
+ <base-select
29
+ id="employee-select"
30
+ v-model="selectedEmployee"
31
+ :options="employeeOptions"
32
+ searchable
33
+ placeholder="Найти сотрудника..."
34
+ label="Выберите сотрудника"
35
+ :error="employeeError"
36
+ @search="handleEmployeeSearch"
37
+ @open="handleEmployeeOpen"
38
+ @clear="handleEmployeeClear"
39
+ @error="handleSearchError"
40
+ />
41
+ </template>
42
+
43
+ <script setup>
44
+ import { ref } from 'vue'
45
+ import axios from 'axios'
46
+
47
+ const selectedEmployee = ref('')
48
+ const employeeOptions = ref([])
49
+ const employeeError = ref('')
50
+ const searchTimeout = ref(null)
51
+
52
+ // Дебаунс поиска для оптимизации запросов
53
+ const debounceSearch = (callback, delay) => {
54
+ if (searchTimeout.value) {
55
+ clearTimeout(searchTimeout.value)
56
+ }
57
+ searchTimeout.value = setTimeout(callback, delay)
58
+ }
59
+
60
+ // Загрузка сотрудников с сервера
61
+ const fetchEmployees = async (query = '') => {
62
+ try {
63
+ const params = {
64
+ limit: 100,
65
+ skip: 0
66
+ }
67
+
68
+ if (query?.trim()) {
69
+ params.name = query.trim()
70
+ }
71
+
72
+ const response = await axios.get('/api/employees', { params })
73
+ const employees = response.data.content || []
74
+
75
+ employeeOptions.value = employees.map(item => ({
76
+ id: item.employeeId,
77
+ name: `${item.lastName} ${item.firstName} ${item.middleName}`.trim(),
78
+ }))
79
+
80
+ employeeError.value = ''
81
+ } catch (error) {
82
+ console.error('Failed to fetch employees:', error)
83
+ employeeOptions.value = []
84
+ employeeError.value = 'Ошибка загрузки данных'
85
+ }
86
+ }
87
+
88
+ // Обработчик поиска с дебаунсом
89
+ const handleEmployeeSearch = (query) => {
90
+ debounceSearch(() => {
91
+ if (query.length >= 2 || query.length === 0) {
92
+ fetchEmployees(query)
93
+ }
94
+ }, 700) // Дебаунс 700мс
95
+ }
96
+
97
+ // Загрузка при открытии если данных нет
98
+ const handleEmployeeOpen = () => {
99
+ if (employeeOptions.value.length === 0) {
100
+ fetchEmployees()
101
+ }
102
+ }
103
+
104
+ // Очистка выбора
105
+ const handleEmployeeClear = () => {
106
+ selectedEmployee.value = ''
107
+ employeeError.value = ''
108
+ fetchEmployees() // Загружаем исходный список
109
+ }
110
+
111
+ // Обработка ошибок поиска
112
+ const handleSearchError = () => {
113
+ employeeError.value = 'Элемент не найден'
114
+ }
115
+ </script>
116
+ ```
117
+
24
118
  ---
25
119
 
26
120
  ### ⚙️ Пропсы
@@ -55,6 +149,9 @@
55
149
  - `size?: 'small' | 'medium' | 'large'`
56
150
  Управляет размерами шрифта, паддингов и иконок.
57
151
 
152
+ - `searchable?: boolean`
153
+ **Включает режим поиска. При `true` заменяет обычный заголовок на поле ввода для поиска. Идеально подходит для server-side поиска больших наборов данных.
154
+
58
155
  - `multiple?: boolean`
59
156
  Поддержка множественного выбора (не реализовано в текущем компоненте, зарезервировано).
60
157
 
@@ -71,6 +168,18 @@
71
168
  - `change`
72
169
  Также эмитится при выборе. Полезно для побочных эффектов.
73
170
 
171
+ - `search`
172
+ Эмитится при вводе в поисковое поле (только при `searchable: true`). Возвращает строку поискового запроса. Используется для server-side поиска.
173
+
174
+ - `open`
175
+ Эмитится при открытии выпадающего меню. Полезно для первоначальной загрузки данных при server-side поиске.
176
+
177
+ - `clear`
178
+ Эмитится при очистке выбранного значения через иконку крестика (только при `searchable: true`).
179
+
180
+ - `error`
181
+ Эмитится когда пользователь вводит текст, но не выбирает ни одну опцию из списка. Полезно для валидации поиска.
182
+
74
183
  ---
75
184
 
76
185
  ### 🧩 Слоты
@@ -3,4 +3,5 @@ export interface IDefaultPagesProps {
3
3
  description: string;
4
4
  buttonText: string;
5
5
  type: 'tech-work' | '404'
6
+ imagePath: string;
6
7
  }
File without changes
File without changes