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.
@@ -8,7 +8,9 @@
8
8
  :disabled="actualDisabled"
9
9
  >
10
10
  <template #top>
11
+ <!-- Старый header для дефолтного поведения -->
11
12
  <div
13
+ v-if="!searchable"
12
14
  :data-error="Boolean(error)"
13
15
  :data-disabled="disabled"
14
16
  class="base-select__header"
@@ -21,12 +23,9 @@
21
23
  <slot name="headerIcon" />
22
24
  </div>
23
25
 
24
- <div
25
- v-if="actualOption"
26
- class="base-select__header_value"
27
- >
28
- {{ actualOption.name }}
29
- </div>
26
+ <div v-if="actualOption" class="base-select__header_value">
27
+ {{ actualOption.name }}
28
+ </div>
30
29
 
31
30
  <div
32
31
  v-else
@@ -48,15 +47,45 @@
48
47
  </div>
49
48
  </div>
50
49
  </div>
50
+
51
+ <BaseInput
52
+ v-else
53
+ :id="id"
54
+ :model-value="displayValue || ''"
55
+ :placeholder="placeholder"
56
+ :disabled="disabled"
57
+ :readonly="readonly"
58
+ :size="size"
59
+ :error="Boolean(error)"
60
+ class="base-select__input"
61
+ @input="handleInputChange"
62
+ >
63
+ <template #left-icon>
64
+ <base-icon
65
+ name="search"
66
+ :size="size"
67
+ />
68
+ </template>
69
+
70
+ <template #right-icon>
71
+ <base-icon
72
+ v-if="!readonly"
73
+ :name="actualOption ? 'close' : 'arrow-down'"
74
+ :size="size"
75
+ class="base-select__toggle-icon"
76
+ @click="handleIconClick"
77
+ />
78
+ </template>
79
+ </BaseInput>
51
80
  </template>
52
81
 
53
82
  <template #dropdown>
54
83
  <div
55
- v-if="(options ?? []).length"
84
+ v-if="(filteredOptions ?? []).length"
56
85
  class="base-select__dropdown"
57
- >
86
+ >
58
87
  <dynamic-scroller
59
- :items="options as ICoreSelectBaseProps[]"
88
+ :items="filteredOptions as ICoreSelectBaseProps[]"
60
89
  :min-item-size="36"
61
90
  key-field="id"
62
91
  class="base-select__list"
@@ -86,6 +115,9 @@
86
115
  </template>
87
116
  </dynamic-scroller>
88
117
  </div>
118
+ <div class="base-select__empty">
119
+ <slot name="empty" />
120
+ </div>
89
121
  </template>
90
122
  </base-dropdown>
91
123
  </div>
@@ -99,6 +131,7 @@ import { DynamicScroller, DynamicScrollerItem } from 'vue-virtual-scroller';
99
131
  import type { ICoreSelectProps, TSelectValue, ICoreSelectBaseProps, ICoreSelectOption, ISelectSlotProps } from '../../types/input';
100
132
  import BaseDropdown from '../BaseDropdown/BaseDropdown.vue';
101
133
  import BaseIcon from '../BaseIcon/BaseIcon.vue';
134
+ import BaseInput from '../BaseInput/BaseInput.vue';
102
135
  import BaseOpenedListItem from '../BaseOpenedListItem/BaseOpenedListItem.vue';
103
136
  import { useKitSize } from '../../composables/kit/size';
104
137
  import { useKitState } from '../../composables/kit/state';
@@ -109,18 +142,28 @@ const props = withDefaults(defineProps<ICoreSelectProps & {
109
142
  }>(), {
110
143
  options: () => [],
111
144
  size: 'medium',
145
+ searchable: false,
112
146
  });
113
147
 
114
148
  const emit = defineEmits<{
115
149
  (e: 'update:modelValue', value: TSelectValue): void
116
150
  (e: 'change', value: TSelectValue): void
151
+ (e: 'error'): void
117
152
  }>();
118
153
 
119
154
  const actualValue = ref<TSelectValue>(props.modelValue ?? '');
155
+ const searchQuery = ref<string>('');
120
156
  const actualOption = computed(() =>
121
157
  props.options?.find((item: ICoreSelectOption) => item?.id === actualValue.value) || null
122
158
  );
123
159
 
160
+ const displayValue = computed(() => {
161
+ if (actualOption.value) {
162
+ return actualOption.value.name;
163
+ }
164
+ return searchQuery.value;
165
+ });
166
+
124
167
  watch(() => props.modelValue, (val) => {
125
168
  actualValue.value = val ?? '';
126
169
  }, { immediate: true });
@@ -129,10 +172,66 @@ function handleInput(value: TSelectValue) {
129
172
  actualValue.value = value;
130
173
  emit('update:modelValue', value);
131
174
  emit('change', value);
175
+ dropdownVisible.value = false;
176
+ searchQuery.value = '';
177
+ }
178
+
179
+ function handleInputChange(event: Event) {
180
+ const target = event.target as HTMLInputElement;
181
+ const inputValue = target.value;
182
+ searchQuery.value = inputValue;
183
+
184
+ if (inputValue) {
185
+ const exactMatch = props.options?.find((option: ICoreSelectOption) =>
186
+ option.name.toLowerCase() === inputValue.toLowerCase()
187
+ );
188
+
189
+ if (exactMatch) {
190
+ handleInput(exactMatch.id);
191
+ return;
192
+ }
193
+
194
+ if (!dropdownVisible.value) {
195
+ dropdownVisible.value = true;
196
+ }
197
+ }
198
+ }
199
+
200
+ function handleIconClick(event: Event) {
201
+ event.stopPropagation();
202
+ if (actualOption.value) {
203
+ handleInput('');
204
+ } else {
205
+ dropdownVisible.value = !dropdownVisible.value;
206
+ }
132
207
  }
133
208
 
134
209
  const actualDisabled = computed(() => props.disabled || !props.options?.length);
135
210
  const dropdownVisible = ref(false);
211
+
212
+ const filteredOptions = computed(() => {
213
+ if (!props.searchable || !searchQuery.value.trim()) {
214
+ return props.options || [];
215
+ }
216
+
217
+ const query = searchQuery.value.toLowerCase().trim();
218
+ return (props.options || []).filter((option: ICoreSelectOption) =>
219
+ option.name.toLowerCase().includes(query)
220
+ );
221
+ });
222
+
223
+ watch(() => props.error, (error) => {
224
+ console.log('ERROR', error);
225
+ });
226
+
227
+ watch(dropdownVisible, (isVisible) => {
228
+ if (!isVisible) {
229
+ if (searchQuery.value && !actualOption.value) {
230
+ emit('error');
231
+ }
232
+ searchQuery.value = '';
233
+ }
234
+ });
136
235
  const { sizeClassList } = useKitSize(props);
137
236
  const { stateClassList } = useKitState(props);
138
237
  const { styleClassList } = useKitStyle(props);
@@ -162,6 +261,7 @@ defineSlots<{
162
261
  iconItem(props: { item: ICoreSelectBaseProps }): any;
163
262
  header(props: { value: ICoreSelectProps['options'] }): any;
164
263
  headerIcon(): any;
264
+ empty(): any;
165
265
  }>();
166
266
  </script>
167
267
 
@@ -185,6 +285,10 @@ defineSlots<{
185
285
  border: 1px solid var(--primary-black-300);
186
286
  outline: 4px solid var(--effects-primary-focus);
187
287
  }
288
+
289
+ &__toggle-icon {
290
+ transform: rotate(180deg);
291
+ }
188
292
  }
189
293
 
190
294
  .dropdown__dropdown {
@@ -202,6 +306,10 @@ defineSlots<{
202
306
  #{$select}__header {
203
307
  border: 1px solid var(--error-red-light-01);
204
308
  }
309
+
310
+ #{$select}__toggle-icon {
311
+ color: var(--error-red);
312
+ }
205
313
  }
206
314
 
207
315
  &__wrapper {
@@ -225,6 +333,8 @@ defineSlots<{
225
333
 
226
334
  &__header_value {
227
335
  color: var(--primary-text-primary);
336
+
337
+ @include text-clamp(1);
228
338
  }
229
339
 
230
340
  &__placeholder {
@@ -247,6 +357,25 @@ defineSlots<{
247
357
  transform: translate3d(0, -50%, 0);
248
358
  }
249
359
 
360
+ &__input {
361
+ cursor: pointer;
362
+
363
+ @include text-clamp(1);
364
+
365
+ &.--is-readonly {
366
+ cursor: default;
367
+ }
368
+ }
369
+
370
+ &__toggle-icon {
371
+ cursor: pointer;
372
+ transition: transform var(--transition);
373
+
374
+ &:hover {
375
+ opacity: 0.7;
376
+ }
377
+ }
378
+
250
379
  &__dropdown {
251
380
  width: 100%;
252
381
  height: 100%;
@@ -266,7 +395,7 @@ defineSlots<{
266
395
  font: var(--typography-text-m-regular);
267
396
  }
268
397
 
269
- &__dropdown {
398
+ &__dropdown, &__input {
270
399
  border-radius: var(--corner-radius-s);
271
400
  }
272
401
 
@@ -289,6 +418,10 @@ defineSlots<{
289
418
  font: var(--typography-text-m-regular);
290
419
  }
291
420
 
421
+ &__input {
422
+ border-radius: var(--corner-radius-m);
423
+ }
424
+
292
425
  &__header {
293
426
  height: 48px;
294
427
  padding: var(--spacing-m) var(--spacing-2l);
@@ -307,6 +440,10 @@ defineSlots<{
307
440
  font: var(--typography-text-l-regular);
308
441
  }
309
442
 
443
+ &__input {
444
+ border-radius: var(--corner-radius-l);
445
+ }
446
+
310
447
  &__header {
311
448
  height: 56px;
312
449
  padding: var(--spacing-m) var(--spacing-l);
@@ -328,6 +465,10 @@ defineSlots<{
328
465
  &__arrow {
329
466
  color: var(--ui-colors-input-icon-disabled);
330
467
  }
468
+
469
+ &__toggle-icon {
470
+ color: var(--ui-colors-input-icon-disabled);
471
+ }
331
472
  }
332
473
  }
333
474
 
@@ -336,6 +477,10 @@ defineSlots<{
336
477
  &__header {
337
478
  pointer-events: none;
338
479
  }
480
+
481
+ &__input {
482
+ pointer-events: none;
483
+ }
339
484
  }
340
485
  }
341
486
  }