@xen-orchestra/web-core 0.31.1 → 0.32.0

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 (64) hide show
  1. package/lib/assets/css/_colors.pcss +8 -0
  2. package/lib/components/button-group/VtsButtonGroup.vue +5 -1
  3. package/lib/components/menu/MenuList.vue +1 -0
  4. package/lib/components/modal/VtsModal.vue +82 -0
  5. package/lib/components/modal/VtsModalButton.vue +36 -0
  6. package/lib/components/modal/VtsModalCancelButton.vue +37 -0
  7. package/lib/components/modal/VtsModalConfirmButton.vue +21 -0
  8. package/lib/components/modal/VtsModalList.vue +34 -0
  9. package/lib/components/object-icon/VtsObjectIcon.vue +3 -8
  10. package/lib/components/status/VtsStatus.vue +66 -0
  11. package/lib/components/task/VtsQuickTaskList.vue +17 -5
  12. package/lib/components/tree/VtsTreeItem.vue +2 -2
  13. package/lib/components/ui/button/UiButton.vue +13 -67
  14. package/lib/components/ui/input/UiInput.vue +4 -1
  15. package/lib/components/ui/modal/UiModal.vue +164 -0
  16. package/lib/components/ui/quick-task-item/UiQuickTaskItem.vue +2 -2
  17. package/lib/composables/context.composable.ts +3 -5
  18. package/lib/composables/link-component.composable.ts +3 -2
  19. package/lib/composables/pagination.composable.ts +3 -2
  20. package/lib/composables/tree-filter.composable.ts +5 -3
  21. package/lib/icons/fa-icons.ts +4 -0
  22. package/lib/icons/index.ts +17 -0
  23. package/lib/locales/en.json +14 -1
  24. package/lib/locales/fr.json +14 -1
  25. package/lib/packages/collection/use-collection.ts +3 -2
  26. package/lib/packages/form-select/use-form-option-controller.ts +3 -2
  27. package/lib/packages/form-select/use-form-select.ts +8 -7
  28. package/lib/packages/menu/action.ts +4 -3
  29. package/lib/packages/menu/link.ts +5 -4
  30. package/lib/packages/menu/router-link.ts +3 -2
  31. package/lib/packages/menu/toggle-target.ts +3 -2
  32. package/lib/packages/modal/ModalProvider.vue +17 -0
  33. package/lib/packages/modal/README.md +253 -0
  34. package/lib/packages/modal/create-modal-opener.ts +103 -0
  35. package/lib/packages/modal/modal.store.ts +22 -0
  36. package/lib/packages/modal/types.ts +92 -0
  37. package/lib/packages/modal/use-modal.ts +53 -0
  38. package/lib/packages/progress/use-progress.ts +4 -3
  39. package/lib/packages/table/README.md +336 -0
  40. package/lib/packages/table/apply-extensions.ts +26 -0
  41. package/lib/packages/table/define-columns.ts +62 -0
  42. package/lib/packages/table/define-renderer/define-table-cell-renderer.ts +27 -0
  43. package/lib/packages/table/define-renderer/define-table-renderer.ts +47 -0
  44. package/lib/packages/table/define-renderer/define-table-row-renderer.ts +29 -0
  45. package/lib/packages/table/define-renderer/define-table-section-renderer.ts +29 -0
  46. package/lib/packages/table/define-table/define-multi-source-table.ts +39 -0
  47. package/lib/packages/table/define-table/define-table.ts +13 -0
  48. package/lib/packages/table/define-table/define-typed-table.ts +18 -0
  49. package/lib/packages/table/index.ts +11 -0
  50. package/lib/packages/table/transform-sources.ts +13 -0
  51. package/lib/packages/table/types/extensions.ts +16 -0
  52. package/lib/packages/table/types/index.ts +47 -0
  53. package/lib/packages/table/types/table-cell.ts +18 -0
  54. package/lib/packages/table/types/table-row.ts +20 -0
  55. package/lib/packages/table/types/table-section.ts +19 -0
  56. package/lib/packages/table/types/table.ts +28 -0
  57. package/lib/packages/threshold/use-threshold.ts +4 -3
  58. package/lib/types/vue-virtual-scroller.d.ts +101 -0
  59. package/lib/utils/injection-keys.util.ts +3 -0
  60. package/lib/utils/progress.util.ts +2 -1
  61. package/lib/utils/to-computed.util.ts +15 -0
  62. package/package.json +3 -2
  63. package/lib/components/backup-state/VtsBackupState.vue +0 -37
  64. package/lib/components/connection-status/VtsConnectionStatus.vue +0 -36
@@ -12,6 +12,10 @@
12
12
 
13
13
  --color-neutral-border: #d0d0d5;
14
14
 
15
+ /* OPACITY */
16
+
17
+ --color-opacity-primary: #1a1b3833;
18
+
15
19
  /* BRAND */
16
20
 
17
21
  --color-brand-txt-base: #6b63bf;
@@ -107,6 +111,10 @@
107
111
 
108
112
  --color-neutral-border: #363647;
109
113
 
114
+ /* OPACITY */
115
+
116
+ --color-opacity-primary: #1a1b3899;
117
+
110
118
  /* BRAND */
111
119
 
112
120
  --color-brand-txt-base: #9b92ff;
@@ -28,7 +28,11 @@ const slots = defineSlots<{
28
28
  display: flex;
29
29
  justify-content: center;
30
30
  align-items: center;
31
- gap: 2.4rem;
31
+ gap: 1.6rem;
32
+
33
+ @media (--mobile) {
34
+ flex-direction: column-reverse;
35
+ }
32
36
  }
33
37
  }
34
38
  </style>
@@ -94,6 +94,7 @@ const open = (event: MouseEvent) => {
94
94
  border-radius: 0.4rem;
95
95
  background-color: var(--color-neutral-background-primary);
96
96
  gap: 0.2rem;
97
+ z-index: 1010;
97
98
 
98
99
  &.horizontal {
99
100
  flex-direction: row;
@@ -0,0 +1,82 @@
1
+ <template>
2
+ <UiModal :icon :accent v-on="{ dismiss: handleDismiss, submit: handleSubmit }">
3
+ <template v-if="slots.title" #title>
4
+ <slot name="title" />
5
+ </template>
6
+
7
+ <template #content>
8
+ <slot name="content" />
9
+ </template>
10
+
11
+ <template v-if="slots.buttons" #buttons>
12
+ <slot name="buttons" />
13
+ </template>
14
+ </UiModal>
15
+ </template>
16
+
17
+ <script lang="ts" setup>
18
+ import UiModal, { type ModalAccent } from '@core/components/ui/modal/UiModal.vue'
19
+ import type { IconName } from '@core/icons'
20
+ import { IK_MODAL } from '@core/packages/modal/types.ts'
21
+ import { IK_MODAL_ACCENT } from '@core/utils/injection-keys.util.ts'
22
+ import { useMagicKeys, whenever } from '@vueuse/core'
23
+ import { computed, inject, provide } from 'vue'
24
+
25
+ const { accent, dismissible, onConfirm, onDismiss, current } = defineProps<{
26
+ accent: ModalAccent
27
+ icon?: IconName
28
+ dismissible?: boolean
29
+ onConfirm?: () => void
30
+ onDismiss?: () => void
31
+ current?: boolean
32
+ }>()
33
+
34
+ const emit = defineEmits<{
35
+ confirm: []
36
+ dismiss: []
37
+ }>()
38
+
39
+ const slots = defineSlots<{
40
+ content(): any
41
+ buttons?(): any
42
+ title?(): any
43
+ }>()
44
+
45
+ const modal = inject(IK_MODAL)
46
+
47
+ provide(
48
+ IK_MODAL_ACCENT,
49
+ computed(() => accent)
50
+ )
51
+
52
+ const handleDismiss = computed(() => {
53
+ if (!dismissible) {
54
+ return undefined
55
+ }
56
+
57
+ if (onDismiss) {
58
+ return () => emit('dismiss')
59
+ }
60
+
61
+ return () => modal?.value.onCancel()
62
+ })
63
+
64
+ function handleSubmit(event: SubmitEvent) {
65
+ event.preventDefault()
66
+
67
+ if (onConfirm) {
68
+ emit('confirm')
69
+ return
70
+ }
71
+
72
+ modal?.value.onConfirm()
73
+ }
74
+
75
+ const { escape } = useMagicKeys()
76
+
77
+ whenever(escape, () => {
78
+ if (dismissible && current) {
79
+ handleDismiss.value?.()
80
+ }
81
+ })
82
+ </script>
@@ -0,0 +1,36 @@
1
+ <template>
2
+ <UiButton :accent="buttonAccent" :busy="modal?.isBusy.value" :variant size="medium">
3
+ <slot />
4
+ </UiButton>
5
+ </template>
6
+
7
+ <script lang="ts" setup>
8
+ import UiButton, { type ButtonVariant } from '@core/components/ui/button/UiButton.vue'
9
+ import { useMapper } from '@core/packages/mapper'
10
+ import { IK_MODAL } from '@core/packages/modal/types.ts'
11
+ import { IK_MODAL_ACCENT } from '@core/utils/injection-keys.util.ts'
12
+ import { inject } from 'vue'
13
+
14
+ defineProps<{
15
+ variant: ButtonVariant
16
+ }>()
17
+
18
+ defineSlots<{
19
+ default(): any
20
+ }>()
21
+
22
+ const modal = inject(IK_MODAL)
23
+
24
+ const modalAccent = inject(IK_MODAL_ACCENT)
25
+
26
+ const buttonAccent = useMapper(
27
+ () => modalAccent?.value,
28
+ {
29
+ success: 'brand',
30
+ info: 'brand',
31
+ warning: 'warning',
32
+ danger: 'danger',
33
+ },
34
+ 'info'
35
+ )
36
+ </script>
@@ -0,0 +1,37 @@
1
+ <template>
2
+ <VtsModalButton variant="secondary" @click="handleClick">
3
+ <slot>{{ t('cancel') }}</slot>
4
+ </VtsModalButton>
5
+ </template>
6
+
7
+ <script lang="ts" setup>
8
+ import VtsModalButton from '@core/components/modal/VtsModalButton.vue'
9
+ import { IK_MODAL } from '@core/packages/modal/types.ts'
10
+ import { inject } from 'vue'
11
+ import { useI18n } from 'vue-i18n'
12
+
13
+ const { onClick } = defineProps<{
14
+ onClick?: () => void
15
+ }>()
16
+
17
+ const emit = defineEmits<{
18
+ click: []
19
+ }>()
20
+
21
+ defineSlots<{
22
+ default?(): any
23
+ }>()
24
+
25
+ const { t } = useI18n()
26
+
27
+ const modal = inject(IK_MODAL)
28
+
29
+ function handleClick() {
30
+ if (onClick) {
31
+ emit('click')
32
+ return
33
+ }
34
+
35
+ modal?.value.onCancel()
36
+ }
37
+ </script>
@@ -0,0 +1,21 @@
1
+ <template>
2
+ <VtsModalButton variant="primary" :type="onClick ? 'button' : 'submit'" @click="emit('click')">
3
+ <slot />
4
+ </VtsModalButton>
5
+ </template>
6
+
7
+ <script lang="ts" setup>
8
+ import VtsModalButton from '@core/components/modal/VtsModalButton.vue'
9
+
10
+ const { onClick } = defineProps<{
11
+ onClick?: () => void
12
+ }>()
13
+
14
+ const emit = defineEmits<{
15
+ click: []
16
+ }>()
17
+
18
+ defineSlots<{
19
+ default(): any
20
+ }>()
21
+ </script>
@@ -0,0 +1,34 @@
1
+ <template>
2
+ <div v-if="modalStore.modals.length > 0" class="vts-modal-list">
3
+ <ModalProvider v-for="(modal, index) of modalStore.modals" :key="modal.id" :modal>
4
+ <component
5
+ :is="modal.component"
6
+ class="modal-component"
7
+ v-bind="modal.props"
8
+ :current="index === modalStore.modals.length - 1"
9
+ @confirm="modal.onConfirm"
10
+ @cancel="modal.onCancel"
11
+ />
12
+ </ModalProvider>
13
+ </div>
14
+ </template>
15
+
16
+ <script lang="ts" setup>
17
+ import { useModalStore } from '@core/packages/modal/modal.store.ts'
18
+ import ModalProvider from '@core/packages/modal/ModalProvider.vue'
19
+
20
+ const modalStore = useModalStore()
21
+ </script>
22
+
23
+ <style lang="postcss" scoped>
24
+ .vts-modal-list {
25
+ position: fixed;
26
+ inset: 0;
27
+ background-color: var(--color-opacity-primary);
28
+ z-index: 1010;
29
+
30
+ .modal-component:not(:last-child) {
31
+ filter: brightness(0.8);
32
+ }
33
+ }
34
+ </style>
@@ -2,19 +2,14 @@
2
2
  <VtsIcon :name="iconName" :size />
3
3
  </template>
4
4
 
5
- <script generic="TType extends ObjectIconType" lang="ts" setup>
5
+ <script generic="TType extends ObjectType, TState extends ObjectState<TType>" lang="ts" setup>
6
6
  import VtsIcon, { type IconSize } from '@core/components/icon/VtsIcon.vue'
7
- import type { ObjectIconName } from '@core/icons'
7
+ import type { ObjectIconName, ObjectState, ObjectType } from '@core/icons'
8
8
  import { computed } from 'vue'
9
9
 
10
- export type ObjectIconType = ObjectIconName extends `object:${infer TType}:${string}` ? TType : never
11
-
12
- export type ObjectIconState<TType extends ObjectIconType> =
13
- Extract<ObjectIconName, `object:${TType}:${string}`> extends `object:${TType}:${infer TState}` ? TState : never
14
-
15
10
  const { type, state } = defineProps<{
16
11
  type: TType
17
- state: ObjectIconState<TType>
12
+ state: TState
18
13
  size: IconSize
19
14
  }>()
20
15
 
@@ -0,0 +1,66 @@
1
+ <template>
2
+ <UiInfo v-tooltip="iconOnly ? currentStatus.text : false" class="vts-status" :accent="currentStatus.accent">
3
+ <template v-if="!iconOnly">{{ currentStatus.text }}</template>
4
+ </UiInfo>
5
+ </template>
6
+
7
+ <script setup lang="ts">
8
+ import UiInfo, { type InfoAccent } from '@core/components/ui/info/UiInfo.vue'
9
+ import { vTooltip } from '@core/directives/tooltip.directive'
10
+ import { useMapper } from '@core/packages/mapper'
11
+ import { useI18n } from 'vue-i18n'
12
+
13
+ export type Status =
14
+ | 'connecting'
15
+ | 'connected'
16
+ | 'disconnected'
17
+ | 'partially-connected'
18
+ | 'disconnected-from-physical-device'
19
+ | 'physically-disconnected'
20
+ | 'unable-to-connect-to-the-pool'
21
+ | 'success'
22
+ | 'skipped'
23
+ | 'interrupted'
24
+ | 'failure'
25
+ | 'pending'
26
+ | 'enabled'
27
+ | 'disabled'
28
+ | true
29
+ | false
30
+
31
+ const { status } = defineProps<{
32
+ status: Status
33
+ iconOnly?: boolean
34
+ }>()
35
+
36
+ const { t } = useI18n()
37
+
38
+ const currentStatus = useMapper<Status, { text: string; accent: InfoAccent }>(
39
+ () => status,
40
+ () => [
41
+ ['connecting', { text: t('connecting'), accent: 'info' }],
42
+ ['connected', { text: t('connected'), accent: 'success' }],
43
+ ['disconnected', { text: t('disconnected'), accent: 'danger' }],
44
+ ['partially-connected', { text: t('partially-connected'), accent: 'warning' }],
45
+ ['disconnected-from-physical-device', { text: t('disconnected-from-physical-device'), accent: 'warning' }],
46
+ ['physically-disconnected', { text: t('disconnected-from-physical-device'), accent: 'danger' }],
47
+ ['unable-to-connect-to-the-pool', { text: t('unable-to-connect-to-the-pool'), accent: 'danger' }],
48
+ ['success', { text: t('success'), accent: 'success' }],
49
+ ['skipped', { text: t('skipped'), accent: 'warning' }],
50
+ ['interrupted', { text: t('interrupted'), accent: 'danger' }],
51
+ ['failure', { text: t('failure'), accent: 'danger' }],
52
+ ['pending', { text: t('in-progress'), accent: 'info' }],
53
+ ['enabled', { text: t('enabled'), accent: 'success' }],
54
+ ['disabled', { text: t('disabled'), accent: 'muted' }],
55
+ [true, { text: t('enabled'), accent: 'success' }],
56
+ [false, { text: t('disabled'), accent: 'muted' }],
57
+ ],
58
+ false
59
+ )
60
+ </script>
61
+
62
+ <style lang="postcss" scoped>
63
+ .vts-status {
64
+ align-items: center;
65
+ }
66
+ </style>
@@ -1,15 +1,26 @@
1
1
  <template>
2
- <ul :class="{ sublist }" class="vts-quick-task-list">
2
+ <DynamicScroller
3
+ v-if="!loading && tasks.length > 0"
4
+ :items="tasks"
5
+ :min-item-size="83"
6
+ class="vts-quick-task-list"
7
+ list-tag="ul"
8
+ item-tag="li"
9
+ >
10
+ <template #default="{ item: task, active }">
11
+ <DynamicScrollerItem :item="task" :active :size-dependencies="[task.subtasks]">
12
+ <UiQuickTaskItem :task />
13
+ </DynamicScrollerItem>
14
+ </template>
15
+ </DynamicScroller>
16
+ <ul v-else class="vts-quick-task-list">
3
17
  <li v-if="loading">
4
18
  <div class="loading">
5
19
  <UiLoader />
6
20
  <div>{{ t('loading') }}</div>
7
21
  </div>
8
22
  </li>
9
- <template v-else>
10
- <li v-if="tasks.length === 0" class="typo-body-bold">{{ t('tasks.no-tasks') }}</li>
11
- <UiQuickTaskItem v-for="task of tasks" :key="task.id" :task />
12
- </template>
23
+ <li v-else-if="tasks.length === 0" class="typo-body-bold">{{ t('tasks.no-tasks') }}</li>
13
24
  </ul>
14
25
  </template>
15
26
 
@@ -17,6 +28,7 @@
17
28
  import UiLoader from '@core/components/ui/loader/UiLoader.vue'
18
29
  import UiQuickTaskItem, { type Task } from '@core/components/ui/quick-task-item/UiQuickTaskItem.vue'
19
30
  import { useI18n } from 'vue-i18n'
31
+ import { DynamicScroller, DynamicScrollerItem } from 'vue-virtual-scroller'
20
32
 
21
33
  defineProps<{
22
34
  tasks: Task[]
@@ -1,8 +1,8 @@
1
1
  <template>
2
- <li class="vts-tree-item" @click="handleClick()">
2
+ <div class="vts-tree-item" @click="handleClick()">
3
3
  <slot />
4
4
  <slot v-if="expanded" name="sublist" />
5
- </li>
5
+ </div>
6
6
  </template>
7
7
 
8
8
  <script lang="ts" setup>
@@ -1,4 +1,4 @@
1
- <!-- v6 -->
1
+ <!-- v7 -->
2
2
  <template>
3
3
  <button :class="classNames" :disabled="busy || isDisabled || lockIcon" class="ui-button" type="button">
4
4
  <VtsIcon :busy :name="leftIcon" class="icon" size="current" />
@@ -14,9 +14,9 @@ import type { IconName } from '@core/icons'
14
14
  import { toVariants } from '@core/utils/to-variants.util'
15
15
  import { computed } from 'vue'
16
16
 
17
- type ButtonVariant = 'primary' | 'secondary' | 'tertiary'
18
- type ButtonAccent = 'brand' | 'warning' | 'danger'
19
- type ButtonSize = 'small' | 'medium'
17
+ export type ButtonVariant = 'primary' | 'secondary' | 'tertiary'
18
+ export type ButtonAccent = 'brand' | 'warning' | 'danger'
19
+ export type ButtonSize = 'small' | 'medium'
20
20
 
21
21
  const { accent, variant, size, disabled, busy, lockIcon } = defineProps<{
22
22
  variant: ButtonVariant
@@ -111,17 +111,11 @@ const classNames = computed(() => [
111
111
  color: var(--color-brand-txt-item);
112
112
  }
113
113
 
114
- &:is(:disabled, .disabled) {
114
+ &:is(:disabled, .disabled, .busy) {
115
115
  background-color: var(--color-brand-item-disabled);
116
116
  border-color: var(--color-brand-item-disabled);
117
117
  color: var(--color-neutral-txt-secondary);
118
118
  }
119
-
120
- &.busy {
121
- background-color: var(--color-brand-item-base);
122
- border-color: var(--color-brand-item-base);
123
- color: var(--color-brand-txt-item);
124
- }
125
119
  }
126
120
 
127
121
  &.variant--secondary {
@@ -145,17 +139,11 @@ const classNames = computed(() => [
145
139
  color: var(--color-brand-txt-active);
146
140
  }
147
141
 
148
- &:is(:disabled, .disabled) {
142
+ &:is(:disabled, .disabled, .busy) {
149
143
  background-color: var(--color-neutral-background-disabled);
150
144
  border-color: var(--color-neutral-txt-secondary);
151
145
  color: var(--color-neutral-txt-secondary);
152
146
  }
153
-
154
- &.busy {
155
- background-color: var(--color-neutral-background-primary);
156
- border-color: var(--color-brand-item-base);
157
- color: var(--color-brand-txt-base);
158
- }
159
147
  }
160
148
 
161
149
  &.variant--tertiary {
@@ -179,17 +167,11 @@ const classNames = computed(() => [
179
167
  color: var(--color-brand-txt-active);
180
168
  }
181
169
 
182
- &:is(:disabled, .disabled) {
170
+ &:is(:disabled, .disabled, .busy) {
183
171
  background-color: transparent;
184
172
  border-color: transparent;
185
173
  color: var(--color-neutral-txt-secondary);
186
174
  }
187
-
188
- &.busy {
189
- background-color: var(--color-brand-background-selected);
190
- border-color: var(--color-brand-background-selected);
191
- color: var(--color-brand-txt-base);
192
- }
193
175
  }
194
176
  }
195
177
 
@@ -215,17 +197,11 @@ const classNames = computed(() => [
215
197
  color: var(--color-warning-txt-item);
216
198
  }
217
199
 
218
- &:is(:disabled, .disabled) {
200
+ &:is(:disabled, .disabled, .busy) {
219
201
  background-color: var(--color-warning-item-disabled);
220
202
  border-color: var(--color-warning-item-disabled);
221
203
  color: var(--color-neutral-txt-secondary);
222
204
  }
223
-
224
- &.busy {
225
- background-color: var(--color-warning-item-base);
226
- border-color: var(--color-warning-item-base);
227
- color: var(--color-warning-txt-item);
228
- }
229
205
  }
230
206
 
231
207
  &.variant--secondary {
@@ -249,17 +225,11 @@ const classNames = computed(() => [
249
225
  color: var(--color-warning-txt-active);
250
226
  }
251
227
 
252
- &:is(:disabled, .disabled) {
228
+ &:is(:disabled, .disabled, .busy) {
253
229
  background-color: var(--color-neutral-background-disabled);
254
230
  border-color: var(--color-neutral-txt-secondary);
255
231
  color: var(--color-neutral-txt-secondary);
256
232
  }
257
-
258
- &.busy {
259
- background-color: var(--color-neutral-background-primary);
260
- border-color: var(--color-warning-txt-base);
261
- color: var(--color-warning-txt-base);
262
- }
263
233
  }
264
234
 
265
235
  &.variant--tertiary {
@@ -283,17 +253,11 @@ const classNames = computed(() => [
283
253
  color: var(--color-warning-txt-active);
284
254
  }
285
255
 
286
- &:is(:disabled, .disabled) {
256
+ &:is(:disabled, .disabled, .busy) {
287
257
  background-color: transparent;
288
258
  border-color: transparent;
289
259
  color: var(--color-neutral-txt-secondary);
290
260
  }
291
-
292
- &.busy {
293
- background-color: var(--color-warning-background-selected);
294
- border-color: var(--color-warning-background-selected);
295
- color: var(--color-warning-txt-base);
296
- }
297
261
  }
298
262
  }
299
263
 
@@ -319,17 +283,11 @@ const classNames = computed(() => [
319
283
  color: var(--color-danger-txt-item);
320
284
  }
321
285
 
322
- &:is(:disabled, .disabled) {
286
+ &:is(:disabled, .disabled, .busy) {
323
287
  background-color: var(--color-danger-item-disabled);
324
288
  border-color: var(--color-danger-item-disabled);
325
289
  color: var(--color-neutral-txt-secondary);
326
290
  }
327
-
328
- &.busy {
329
- background-color: var(--color-danger-item-base);
330
- border-color: var(--color-danger-item-base);
331
- color: var(--color-danger-txt-item);
332
- }
333
291
  }
334
292
 
335
293
  &.variant--secondary {
@@ -353,17 +311,11 @@ const classNames = computed(() => [
353
311
  color: var(--color-danger-txt-active);
354
312
  }
355
313
 
356
- &:is(:disabled, .disabled) {
314
+ &:is(:disabled, .disabled, .busy) {
357
315
  background-color: var(--color-neutral-background-disabled);
358
316
  border-color: var(--color-neutral-txt-secondary);
359
317
  color: var(--color-neutral-txt-secondary);
360
318
  }
361
-
362
- &.busy {
363
- background-color: var(--color-neutral-background-primary);
364
- border-color: var(--color-danger-txt-base);
365
- color: var(--color-danger-txt-base);
366
- }
367
319
  }
368
320
 
369
321
  &.variant--tertiary {
@@ -387,17 +339,11 @@ const classNames = computed(() => [
387
339
  color: var(--color-danger-txt-active);
388
340
  }
389
341
 
390
- &:is(:disabled, .disabled) {
342
+ &:is(:disabled, .disabled, .busy) {
391
343
  background-color: transparent;
392
344
  border-color: transparent;
393
345
  color: var(--color-neutral-txt-secondary);
394
346
  }
395
-
396
- &.busy {
397
- background-color: var(--color-danger-background-selected);
398
- border-color: var(--color-danger-background-selected);
399
- color: var(--color-danger-txt-base);
400
- }
401
347
  }
402
348
  }
403
349
 
@@ -1,7 +1,8 @@
1
1
  <!-- v5 -->
2
2
  <template>
3
3
  <div :class="toVariants({ accent, disabled })" class="ui-input" @click.self="focus()">
4
- <VtsIcon :name="icon" size="medium" class="left-icon" />
4
+ <UiLoader v-if="isSearching" />
5
+ <VtsIcon v-else :name="icon" size="medium" class="left-icon" />
5
6
  <input
6
7
  :id="wrapperController?.id ?? id"
7
8
  ref="inputRef"
@@ -30,6 +31,7 @@
30
31
  import VtsIcon from '@core/components/icon/VtsIcon.vue'
31
32
  import UiButtonIcon from '@core/components/ui/button-icon/UiButtonIcon.vue'
32
33
  import type { LabelAccent } from '@core/components/ui/label/UiLabel.vue'
34
+ import UiLoader from '@core/components/ui/loader/UiLoader.vue'
33
35
  import type { IconName } from '@core/icons'
34
36
  import { useMapper } from '@core/packages/mapper/use-mapper.ts'
35
37
  import { IK_INPUT_WRAPPER_CONTROLLER } from '@core/utils/injection-keys.util'
@@ -57,6 +59,7 @@ const {
57
59
  icon?: IconName
58
60
  rightIcon?: IconName
59
61
  clearable?: boolean
62
+ isSearching?: boolean
60
63
  }>()
61
64
 
62
65
  const modelValue = defineModel<string | number>({ required: true })