plugin-ui-for-kzt 0.0.27 → 0.0.29
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.
- package/dist/components/BaseButton/BaseButton.vue.d.ts +9 -0
- package/dist/components/BaseSegmentedButtons/BaseSegmentedButtons.vue.d.ts +8 -1
- package/dist/components/BaseSelect/BaseSelect.vue.d.ts +12 -2
- package/dist/components/BaseTable/BaseTable.vue.d.ts +14 -1
- package/dist/components/BaseTooltip/BaseTooltip.vue.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/sprite.svg +1 -1
- package/example/App.vue +278 -6
- package/package.json +1 -1
- package/src/assets/icons/search.svg +4 -0
- package/src/components/BaseButton/BaseButton.vue +30 -1
- package/src/components/BaseDefaultPages/BaseDefaultPages.vue +9 -1
- package/src/components/BaseSelect/BaseSelect.vue +155 -10
- package/src/components/BaseTable/BaseTable.vue +278 -26
- package/src/components/BaseTable/README.md +96 -1
- package/src/components/BaseTooltip/BaseTooltip.vue +9 -5
- package/src/components/BaseUpload/BaseUpload.vue +97 -25
- package/src/images.d.ts +24 -0
- package/src/types/button.d.ts +1 -1
- package/src/types/input.d.ts +1 -0
- package/src/types/table.d.ts +6 -0
- package/src/vue-virtual-scroller.d.ts +4 -0
package/example/App.vue
CHANGED
|
@@ -1,18 +1,283 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="demo-page">
|
|
3
3
|
<h1>Plugin UI KZT - Компоненты</h1>
|
|
4
|
+
|
|
5
|
+
<section>
|
|
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
|
+
|
|
4
85
|
<section>
|
|
5
|
-
<
|
|
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
|
+
/>
|
|
6
174
|
</section>
|
|
7
175
|
</div>
|
|
8
176
|
</template>
|
|
9
177
|
<script setup lang="ts">
|
|
10
|
-
import { ref, watch } from 'vue'
|
|
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
|
+
})
|
|
11
252
|
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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
|
+
}
|
|
16
281
|
</script>
|
|
17
282
|
|
|
18
283
|
<style lang="scss" scoped>
|
|
@@ -52,4 +317,11 @@ watch(()=> testDate.value,
|
|
|
52
317
|
overflow-x: auto;
|
|
53
318
|
}
|
|
54
319
|
}
|
|
320
|
+
|
|
321
|
+
.table-data__cell-actions {
|
|
322
|
+
display: flex;
|
|
323
|
+
align-items: center;
|
|
324
|
+
gap: var(--spacing-xs);
|
|
325
|
+
justify-content: flex-end;
|
|
326
|
+
}
|
|
55
327
|
</style>
|
package/package.json
CHANGED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path d="M15.3333 28.0001C22.3289 28.0001 28 22.329 28 15.3334C28 8.33781 22.3289 2.66675 15.3333 2.66675C8.33773 2.66675 2.66667 8.33781 2.66667 15.3334C2.66667 22.329 8.33773 28.0001 15.3333 28.0001Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
3
|
+
<path d="M29.3333 29.3334L26.6667 26.6667" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
4
|
+
</svg>
|
|
@@ -30,6 +30,7 @@ const props = withDefaults(defineProps<ICoreButtonProps>(), {
|
|
|
30
30
|
tag: 'button',
|
|
31
31
|
color: 'primary',
|
|
32
32
|
size: 'medium',
|
|
33
|
+
onlyIcon: false,
|
|
33
34
|
});
|
|
34
35
|
|
|
35
36
|
const { componentTag, interactiveClassList } = useKitInteractive(props);
|
|
@@ -64,6 +65,7 @@ const classList = computed(() => [
|
|
|
64
65
|
colorClassList.value,
|
|
65
66
|
stateClassList.value,
|
|
66
67
|
styleClassList.value,
|
|
68
|
+
{ 'base-button--only-icon': props.onlyIcon }
|
|
67
69
|
]);
|
|
68
70
|
</script>
|
|
69
71
|
|
|
@@ -72,6 +74,8 @@ const classList = computed(() => [
|
|
|
72
74
|
@import '@/styles/root';
|
|
73
75
|
|
|
74
76
|
.base-button {
|
|
77
|
+
$button: &;
|
|
78
|
+
|
|
75
79
|
@include reset-button;
|
|
76
80
|
@include is-disabled-state;
|
|
77
81
|
|
|
@@ -83,7 +87,12 @@ const classList = computed(() => [
|
|
|
83
87
|
appearance: none;
|
|
84
88
|
cursor: pointer;
|
|
85
89
|
user-select: none;
|
|
86
|
-
transition:
|
|
90
|
+
transition:
|
|
91
|
+
background-color var(--transition),
|
|
92
|
+
color var(--transition),
|
|
93
|
+
border-color var(--transition),
|
|
94
|
+
box-shadow var(--transition),
|
|
95
|
+
opacity var(--transition);
|
|
87
96
|
|
|
88
97
|
|
|
89
98
|
&.--is-loading {
|
|
@@ -111,6 +120,11 @@ const classList = computed(() => [
|
|
|
111
120
|
}
|
|
112
121
|
}
|
|
113
122
|
|
|
123
|
+
&.--extra-small-size#{&}--only-icon {
|
|
124
|
+
padding: 0;
|
|
125
|
+
width: 32px;
|
|
126
|
+
}
|
|
127
|
+
|
|
114
128
|
&.--small-size {
|
|
115
129
|
column-gap: var(--spacing-xs);
|
|
116
130
|
height: 40px;
|
|
@@ -124,6 +138,11 @@ const classList = computed(() => [
|
|
|
124
138
|
}
|
|
125
139
|
}
|
|
126
140
|
|
|
141
|
+
&.--small-size#{&}--only-icon {
|
|
142
|
+
padding: 0;
|
|
143
|
+
width: 40px;
|
|
144
|
+
}
|
|
145
|
+
|
|
127
146
|
&.--medium-size {
|
|
128
147
|
column-gap: var(--spacing-xs);
|
|
129
148
|
height: 48px;
|
|
@@ -137,6 +156,11 @@ const classList = computed(() => [
|
|
|
137
156
|
}
|
|
138
157
|
}
|
|
139
158
|
|
|
159
|
+
&.--medium-size#{&}--only-icon {
|
|
160
|
+
padding: 0;
|
|
161
|
+
width: 48px;
|
|
162
|
+
}
|
|
163
|
+
|
|
140
164
|
&.--large-size {
|
|
141
165
|
column-gap: var(--spacing-xs);
|
|
142
166
|
height: 56px;
|
|
@@ -150,6 +174,11 @@ const classList = computed(() => [
|
|
|
150
174
|
}
|
|
151
175
|
}
|
|
152
176
|
|
|
177
|
+
&.--large-size#{&}--only-icon {
|
|
178
|
+
padding: 0;
|
|
179
|
+
width: 56px;
|
|
180
|
+
}
|
|
181
|
+
|
|
153
182
|
&.--is-interactive {
|
|
154
183
|
height: 22px;
|
|
155
184
|
padding: 0;
|
|
@@ -33,10 +33,18 @@
|
|
|
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';
|
|
36
38
|
|
|
37
39
|
const props = defineProps<IDefaultPagesProps>();
|
|
38
40
|
|
|
39
|
-
const imagePath = computed(()=>
|
|
41
|
+
const imagePath = computed(() => {
|
|
42
|
+
const images:Record<string, any> = {
|
|
43
|
+
'tech-work': techWorkImage,
|
|
44
|
+
'404': error404Image
|
|
45
|
+
};
|
|
46
|
+
return images[props.type] || '';
|
|
47
|
+
});
|
|
40
48
|
const imageContainer = computed<string[]>(()=> [
|
|
41
49
|
`base-default-pages__image-container--${props.type}`
|
|
42
50
|
])
|