@xen-orchestra/web-core 0.55.0 → 0.57.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 (111) hide show
  1. package/lib/assets/css/base.pcss +5 -0
  2. package/lib/components/card-object-title/VtsCardObjectTitle.vue +36 -0
  3. package/lib/components/code-snippet/VtsCodeSnippet.vue +11 -5
  4. package/lib/components/drawer/VtsDrawer.vue +5 -1
  5. package/lib/components/drawer/VtsDrawerButton.vue +4 -3
  6. package/lib/components/drawer/VtsDrawerConfirmButton.vue +4 -2
  7. package/lib/components/icon/VtsIcon.vue +3 -1
  8. package/lib/components/input-wrapper/VtsInputWrapper.vue +2 -2
  9. package/lib/components/layout/VtsContentSidePanel.vue +41 -0
  10. package/lib/components/layout/VtsLayoutSidebar.vue +116 -43
  11. package/lib/components/menu/MenuItem.vue +9 -1
  12. package/lib/components/menu/MenuList.vue +4 -2
  13. package/lib/components/menu/VtsActionsMenu.vue +48 -14
  14. package/lib/components/modal/VtsActionModal.vue +44 -9
  15. package/lib/components/modal/VtsBlockedModal.vue +1 -1
  16. package/lib/components/modal/VtsDeleteModal.vue +1 -1
  17. package/lib/components/operation-card/VtsOperationCard.vue +2 -1
  18. package/lib/components/overlay/VtsOverlayButton.vue +35 -0
  19. package/lib/components/overlay/VtsOverlayCancelButton.vue +16 -0
  20. package/lib/components/overlay/VtsOverlayConfirmButton.vue +25 -0
  21. package/lib/components/overlay/VtsOverlayList.vue +82 -0
  22. package/lib/components/panel/VtsPanel.vue +80 -0
  23. package/lib/components/panel/VtsSidePanel.vue +140 -0
  24. package/lib/components/progress-bar/VtsProgressBar.vue +5 -3
  25. package/lib/components/select/VtsSelect.vue +1 -3
  26. package/lib/components/space-card/VtsSpaceCard.vue +1 -1
  27. package/lib/components/state-hero/VtsStateHero.vue +9 -17
  28. package/lib/components/table/VtsTable.vue +2 -1
  29. package/lib/components/table/cells/VtsActionCell.vue +7 -0
  30. package/lib/components/table/cells/VtsLinkCell.vue +3 -1
  31. package/lib/components/table/cells/VtsProgressBarCell.vue +1 -1
  32. package/lib/components/table/cells/VtsUserNameCell.vue +29 -0
  33. package/lib/components/task/VtsQuickTaskButton.vue +1 -1
  34. package/lib/components/task/VtsQuickTaskList.vue +1 -1
  35. package/lib/components/tree/VtsTreeItem.vue +13 -7
  36. package/lib/components/tree/VtsTreeList.vue +6 -3
  37. package/lib/components/tree/VtsTreeLoadingItem.vue +3 -3
  38. package/lib/components/ui/head-bar/UiHeadBar.vue +2 -2
  39. package/lib/components/ui/input/UiInput.vue +56 -10
  40. package/lib/components/ui/link/UiLink.vue +3 -1
  41. package/lib/components/ui/panel/UiPanel.vue +7 -24
  42. package/lib/components/ui/query-search-bar/UiQuerySearchBar.vue +1 -15
  43. package/lib/components/ui/quick-task-item/UiQuickTaskItem.vue +1 -1
  44. package/lib/components/ui/quick-task-panel/UiQuickTaskPanel.vue +1 -1
  45. package/lib/components/ui/task-item/UiTaskItem.vue +14 -6
  46. package/lib/components/ui/task-list/UiTaskList.vue +1 -1
  47. package/lib/components/ui/text-area/UiTextarea.vue +12 -14
  48. package/lib/components/ui/tree-item-label/UiTreeItemLabel.vue +1 -1
  49. package/lib/components/vif-connection-toggle-modal/VtsVifConnectionToggleModal.vue +43 -0
  50. package/lib/composables/table/multi-select.composable.md +33 -0
  51. package/lib/composables/table-state.composable.ts +1 -1
  52. package/lib/icons/action-icons.ts +14 -0
  53. package/lib/layouts/CoreLayout.vue +74 -62
  54. package/lib/locales/cs.json +56 -1
  55. package/lib/locales/de.json +3 -2
  56. package/lib/locales/en.json +85 -4
  57. package/lib/locales/es.json +11 -1
  58. package/lib/locales/fa.json +0 -1
  59. package/lib/locales/fr.json +85 -4
  60. package/lib/locales/it.json +0 -1
  61. package/lib/locales/ko.json +2 -0
  62. package/lib/locales/nl.json +2 -1
  63. package/lib/locales/pt-BR.json +0 -1
  64. package/lib/locales/pt.json +0 -1
  65. package/lib/locales/ru.json +0 -1
  66. package/lib/locales/sk.json +56 -1
  67. package/lib/locales/sv.json +3 -2
  68. package/lib/locales/uk.json +0 -1
  69. package/lib/locales/zh-Hans.json +79 -4
  70. package/lib/packages/form-validation/custom-rules/ip-addresses.rule.ts +15 -0
  71. package/lib/packages/form-validation/custom-rules/ip.regex.ts +26 -0
  72. package/lib/packages/form-validation/custom-rules/ipv4-or-cidr.rule.ts +2 -3
  73. package/lib/packages/form-validation/index.ts +2 -0
  74. package/lib/packages/hide-permanently/README.md +60 -0
  75. package/lib/packages/hide-permanently/types.ts +3 -0
  76. package/lib/packages/hide-permanently/use-hide-permanently.ts +15 -0
  77. package/lib/packages/overlay/OverlayComponent.vue +18 -0
  78. package/lib/packages/overlay/README.md +285 -0
  79. package/lib/packages/overlay/create-event-handler.ts +80 -0
  80. package/lib/packages/overlay/injection-keys.ts +3 -0
  81. package/lib/packages/overlay/is-thenable.ts +3 -0
  82. package/lib/packages/overlay/symbols.ts +5 -0
  83. package/lib/packages/overlay/types.ts +94 -0
  84. package/lib/packages/overlay/use-overlay-escape.ts +34 -0
  85. package/lib/packages/overlay/use-overlay-store.ts +33 -0
  86. package/lib/packages/overlay/use-overlay-trigger.ts +33 -0
  87. package/lib/packages/overlay/use-overlay.ts +99 -0
  88. package/lib/packages/progress/use-progress.ts +12 -2
  89. package/lib/packages/remote-resource/define-remote-resource.ts +64 -29
  90. package/lib/packages/sidebar/index.ts +2 -0
  91. package/lib/packages/sidebar/sidebar.store.ts +55 -0
  92. package/lib/packages/sidebar/types.ts +21 -0
  93. package/lib/packages/sidebar/use-sidebar-resize.ts +47 -0
  94. package/lib/packages/sidebar/use-sidebar-responsive-expand.ts +19 -0
  95. package/lib/packages/tree/types.ts +6 -0
  96. package/lib/packages/tree/use-tree.ts +38 -37
  97. package/lib/stores/panel.store.ts +56 -5
  98. package/lib/tables/column-definitions/action-column.ts +21 -9
  99. package/lib/tables/column-definitions/user-name-column.ts +10 -0
  100. package/lib/tables/column-sets/server-columns.ts +2 -2
  101. package/lib/tables/column-sets/user-columns.ts +16 -0
  102. package/lib/tables/column-sets/vm-columns.ts +2 -3
  103. package/lib/types/object.type.ts +9 -1
  104. package/lib/types/state-hero.type.ts +19 -0
  105. package/lib/types/task.type.ts +5 -12
  106. package/lib/types/vue-virtual-scroller.d.ts +8 -2
  107. package/lib/utils/injection-keys.util.ts +4 -2
  108. package/lib/utils/ip-address.utils.ts +7 -0
  109. package/lib/utils/sr.utils.ts +4 -0
  110. package/package.json +2 -1
  111. package/lib/stores/sidebar.store.ts +0 -79
@@ -6,12 +6,15 @@
6
6
 
7
7
  <script lang="ts" setup>
8
8
  import { IK_TREE_LIST_DEPTH } from '@core/utils/injection-keys.util'
9
- import { inject, provide } from 'vue'
9
+ import { computed, inject, provide, ref } from 'vue'
10
10
 
11
11
  defineSlots<{
12
12
  default(): any
13
13
  }>()
14
14
 
15
- const depth = inject(IK_TREE_LIST_DEPTH, 0)
16
- provide(IK_TREE_LIST_DEPTH, depth + 1)
15
+ const depth = inject(IK_TREE_LIST_DEPTH, ref(0))
16
+ provide(
17
+ IK_TREE_LIST_DEPTH,
18
+ computed(() => depth.value + 1)
19
+ )
17
20
  </script>
@@ -3,7 +3,7 @@
3
3
  <div class="label-placeholder">
4
4
  <div class="link-placeholder">
5
5
  <template v-if="depth > 1">
6
- <VtsTreeLine v-for="i in depth - 1" :key="i" :right="i === depth - 1" full-height />
6
+ <VtsTreeLine v-for="i in depth - 1" :key="i" :right="i === depth - 1" />
7
7
  </template>
8
8
  <VtsIcon :name="icon" size="medium" />
9
9
  <div class="loader">&nbsp;</div>
@@ -17,13 +17,13 @@ import VtsIcon from '@core/components/icon/VtsIcon.vue'
17
17
  import VtsTreeLine from '@core/components/tree/VtsTreeLine.vue'
18
18
  import type { IconName } from '@core/icons'
19
19
  import { IK_TREE_LIST_DEPTH } from '@core/utils/injection-keys.util'
20
- import { inject } from 'vue'
20
+ import { inject, ref } from 'vue'
21
21
 
22
22
  defineProps<{
23
23
  icon: IconName
24
24
  }>()
25
25
 
26
- const depth = inject(IK_TREE_LIST_DEPTH, 0)
26
+ const depth = inject(IK_TREE_LIST_DEPTH, ref(0))
27
27
  </script>
28
28
 
29
29
  <style lang="postcss" scoped>
@@ -3,7 +3,7 @@
3
3
  <div class="ui-head-bar">
4
4
  <div class="label-wrapper">
5
5
  <slot name="icon">
6
- <VtsIcon :name="icon" size="medium" />
6
+ <VtsIcon :name="icon" size="large" />
7
7
  </slot>
8
8
  <h4 v-tooltip class="typo-h4 label text-ellipsis">
9
9
  <slot />
@@ -37,7 +37,7 @@ const slots = defineSlots<{
37
37
 
38
38
  <style lang="postcss" scoped>
39
39
  .ui-head-bar {
40
- min-height: 5.6rem;
40
+ min-height: 6rem;
41
41
  padding: 0.8rem 1.6rem;
42
42
  display: flex;
43
43
  gap: 1.6rem;
@@ -1,6 +1,11 @@
1
- <!-- v5 -->
1
+ <!-- v9 -->
2
2
  <template>
3
- <div :class="toVariants({ accent, disabled })" class="ui-input" @click.self="focus()">
3
+ <div :class="toVariants({ accent, disabled, selected })" class="ui-input" @click.self="focus()">
4
+ <span v-if="slots.prefix || prefix" class="prefix">
5
+ <slot name="prefix">
6
+ {{ prefix }}
7
+ </slot>
8
+ </span>
4
9
  <span v-if="readonly" class="typo-body-regular input text-ellipsis">
5
10
  <input ref="inputRef" class="readonly-input" readonly type="text" />
6
11
  {{ modelValue }}
@@ -19,14 +24,19 @@
19
24
  />
20
25
  <UiButtonIcon
21
26
  v-if="!disabled && modelValue && clearable"
22
- icon="fa:xmark"
23
- accent="brand"
24
27
  size="small"
28
+ icon="action:close-cancel-clear"
29
+ accent="brand"
25
30
  @click="clear()"
26
31
  />
27
32
  <slot v-if="slots['right-icon'] || rightIcon" name="right-icon">
28
33
  <VtsIcon :name="rightIcon" size="medium" class="right-icon" />
29
34
  </slot>
35
+ <span v-if="slots.suffix || suffix" class="suffix">
36
+ <slot name="suffix">
37
+ {{ suffix }}
38
+ </slot>
39
+ </span>
30
40
  </div>
31
41
  </template>
32
42
 
@@ -52,16 +62,20 @@ const {
52
62
  required,
53
63
  disabled,
54
64
  id,
65
+ detached,
55
66
  } = defineProps<{
56
67
  accent: InputAccent
57
68
  id?: string
58
69
  required?: boolean
59
70
  disabled?: boolean
71
+ selected?: boolean
60
72
  readonly?: boolean
73
+ detached?: boolean
61
74
  type?: InputType
62
- icon?: IconName
63
75
  rightIcon?: IconName
64
76
  clearable?: boolean
77
+ prefix?: string
78
+ suffix?: string
65
79
  }>()
66
80
 
67
81
  const emit = defineEmits<{
@@ -72,6 +86,8 @@ const modelValue = defineModel<string | number | undefined>({ required: true })
72
86
 
73
87
  const slots = defineSlots<{
74
88
  'right-icon'?(): any
89
+ prefix?(): any
90
+ suffix?(): any
75
91
  }>()
76
92
 
77
93
  function handleInput(event: Event) {
@@ -82,7 +98,7 @@ const attrs = useAttrs()
82
98
 
83
99
  const inputRef = ref<HTMLInputElement>()
84
100
 
85
- const wrapperController = inject(IK_INPUT_WRAPPER_CONTROLLER, undefined)
101
+ const wrapperController = detached ? undefined : inject(IK_INPUT_WRAPPER_CONTROLLER, undefined)
86
102
 
87
103
  const accent = useMapper<LabelAccent, InputAccent>(
88
104
  () => wrapperController?.labelAccent,
@@ -133,6 +149,31 @@ defineExpose({ focus })
133
149
  color: var(--color-brand-txt-base);
134
150
  }
135
151
 
152
+ .prefix,
153
+ .suffix {
154
+ display: flex;
155
+ align-items: center;
156
+ justify-content: center;
157
+ align-self: stretch;
158
+ background-color: var(--color-neutral-background-secondary);
159
+ padding: 0.8rem 1.6rem;
160
+ color: var(--color-neutral-txt-secondary);
161
+ }
162
+
163
+ .prefix {
164
+ border-end-start-radius: 0.4rem;
165
+ border-start-start-radius: 0.4rem;
166
+ border-inline-end: 0.1rem solid var(--color-neutral-border);
167
+ margin-inline-start: -1.6rem;
168
+ }
169
+
170
+ .suffix {
171
+ border-end-end-radius: 0.4rem;
172
+ border-start-end-radius: 0.4rem;
173
+ border-inline-start: 0.1rem solid var(--color-neutral-border);
174
+ margin-inline-end: -1.6rem;
175
+ }
176
+
136
177
  &.disabled .right-icon {
137
178
  color: var(--color-neutral-txt-secondary);
138
179
  }
@@ -161,12 +202,9 @@ defineExpose({ focus })
161
202
  /* VARIANT */
162
203
 
163
204
  &.accent--brand {
205
+ --selected-color: var(--color-brand-item-base);
164
206
  border-color: var(--color-neutral-border);
165
207
 
166
- &:focus-within {
167
- border-color: var(--color-brand-item-base);
168
- }
169
-
170
208
  &:hover {
171
209
  border-color: var(--color-brand-item-hover);
172
210
  }
@@ -182,6 +220,7 @@ defineExpose({ focus })
182
220
  }
183
221
 
184
222
  &.accent--warning {
223
+ --selected-color: var(--color-warning-item-base);
185
224
  border-color: var(--color-warning-item-base);
186
225
 
187
226
  &:hover {
@@ -199,6 +238,7 @@ defineExpose({ focus })
199
238
  }
200
239
 
201
240
  &.accent--danger {
241
+ --selected-color: var(--color-danger-item-base);
202
242
  border-color: var(--color-danger-item-base);
203
243
 
204
244
  &:hover {
@@ -214,5 +254,11 @@ defineExpose({ focus })
214
254
  background-color: var(--color-neutral-background-disabled);
215
255
  }
216
256
  }
257
+
258
+ &:focus-within,
259
+ &.selected {
260
+ border-color: var(--selected-color);
261
+ border-width: 0.2rem;
262
+ }
217
263
  }
218
264
  </style>
@@ -1,7 +1,7 @@
1
1
  <!-- v6 -->
2
2
  <template>
3
3
  <component :is="component" :class="classes" class="ui-link" v-bind="attributes">
4
- <VtsIcon :name="icon" size="medium" />
4
+ <VtsIcon :name="icon" :busy :busy-tooltip size="medium" />
5
5
  <slot />
6
6
  <VtsIcon v-if="attributes.target === '_blank'" name="action:open-in-new-tab" size="medium" class="external-icon" />
7
7
  <VtsIcon v-if="isPrimary" v-tooltip="primaryTooltip" name="status:primary-circle" size="medium" />
@@ -19,6 +19,8 @@ const props = defineProps<
19
19
  LinkOptions & {
20
20
  size: 'small' | 'medium'
21
21
  icon?: IconName
22
+ busy?: boolean
23
+ busyTooltip?: string
22
24
  isPrimary?: boolean
23
25
  primaryTooltip?: string
24
26
  }
@@ -1,6 +1,6 @@
1
- <!-- v2 -->
1
+ <!-- v5 -->
2
2
  <template>
3
- <div class="ui-panel" :class="{ error, 'mobile-drawer': uiStore.isSmall }">
3
+ <div class="ui-panel" :class="{ error }">
4
4
  <div v-if="slots.header" class="header">
5
5
  <slot name="header" />
6
6
  </div>
@@ -11,8 +11,6 @@
11
11
  </template>
12
12
 
13
13
  <script setup lang="ts">
14
- import { useUiStore } from '@core/stores/ui.store'
15
-
16
14
  defineProps<{
17
15
  error?: boolean
18
16
  }>()
@@ -21,31 +19,25 @@ const slots = defineSlots<{
21
19
  default(): any
22
20
  header?(): any
23
21
  }>()
24
-
25
- const uiStore = useUiStore()
26
22
  </script>
27
23
 
28
24
  <style scoped lang="postcss">
29
25
  .ui-panel {
30
- position: sticky;
31
- top: 0;
32
26
  display: flex;
33
27
  flex-direction: column;
34
28
  border-inline-start: 0.1rem solid var(--color-neutral-border);
29
+ border-block-end: 0.1rem solid var(--color-neutral-border);
30
+ border-end-start-radius: 0.8rem;
35
31
  background-color: var(--color-neutral-background-secondary);
36
32
 
37
- &:not(.mobile-drawer) {
38
- min-height: calc(100dvh - 16rem);
39
- }
40
-
41
33
  .header {
34
+ flex-shrink: 0;
35
+ padding: 0.4rem 1.6rem;
42
36
  border-bottom: 0.1rem solid var(--color-neutral-border);
43
37
  background-color: var(--color-neutral-background-primary);
44
38
  display: flex;
45
- justify-content: flex-end;
46
39
  align-items: center;
47
40
  gap: 1.6rem;
48
- padding: 0.4rem 1.6rem;
49
41
  }
50
42
 
51
43
  .content {
@@ -53,20 +45,11 @@ const uiStore = useUiStore()
53
45
  flex-direction: column;
54
46
  padding: 0.8rem;
55
47
  gap: 0.8rem;
56
- }
57
-
58
- &.mobile-drawer {
59
- .content {
60
- overflow: auto;
61
- }
48
+ scrollbar-width: thin;
62
49
  }
63
50
 
64
51
  &.error {
65
52
  background-color: var(--color-danger-background-selected);
66
-
67
- .content {
68
- padding-top: 15rem;
69
- }
70
53
  }
71
54
  }
72
55
  </style>
@@ -10,24 +10,12 @@
10
10
  type="text"
11
11
  accent="brand"
12
12
  :aria-label="uiStore.isSmall ? t('query-search-bar:label') : undefined"
13
- :icon="!uiStore.isSmall ? 'fa:magnifying-glass' : undefined"
14
13
  :placeholder="t('query-search-bar:placeholder')"
15
14
  />
16
15
  <template v-if="!uiStore.isSmall">
17
- <UiButton size="medium" accent="brand" variant="primary" type="submit" class="action-button">
16
+ <UiButton size="medium" accent="brand" variant="secondary" type="submit" class="action-button">
18
17
  {{ t('action:search') }}
19
18
  </UiButton>
20
- <VtsDivider type="stretch" />
21
- <UiButton
22
- v-tooltip="t('coming-soon!')"
23
- size="medium"
24
- accent="brand"
25
- variant="secondary"
26
- disabled
27
- class="action-button"
28
- >
29
- {{ t('query-search-bar:use-query-builder') }}
30
- </UiButton>
31
19
  </template>
32
20
 
33
21
  <!-- Mobile icons: search + filter -->
@@ -39,11 +27,9 @@
39
27
  </template>
40
28
 
41
29
  <script lang="ts" setup>
42
- import VtsDivider from '@core/components/divider/VtsDivider.vue'
43
30
  import UiButton from '@core/components/ui/button/UiButton.vue'
44
31
  import UiButtonIcon from '@core/components/ui/button-icon/UiButtonIcon.vue'
45
32
  import UiInput from '@core/components/ui/input/UiInput.vue'
46
- import { vTooltip } from '@core/directives/tooltip.directive'
47
33
  import { useUiStore } from '@core/stores/ui.store'
48
34
  import { uniqueId } from '@core/utils/unique-id.util'
49
35
  import { ref } from 'vue'
@@ -34,7 +34,7 @@ import VtsIcon from '@core/components/icon/VtsIcon.vue'
34
34
  import VtsQuickTaskList from '@core/components/task/VtsQuickTaskList.vue'
35
35
  import UiButtonIcon from '@core/components/ui/button-icon/UiButtonIcon.vue'
36
36
  import UiTag from '@core/components/ui/tag/UiTag.vue'
37
- import type { Task } from '@core/types/task.type.ts'
37
+ import type { Task } from '@core/components/ui/task-item/UiTaskItem.vue'
38
38
  import { useToggle } from '@vueuse/core'
39
39
  import { computed } from 'vue'
40
40
  import { useI18n } from 'vue-i18n'
@@ -15,8 +15,8 @@
15
15
  <script lang="ts" setup>
16
16
  import VtsQuickTaskList from '@core/components/task/VtsQuickTaskList.vue'
17
17
  import VtsQuickTaskTabBar, { type TaskTab } from '@core/components/task/VtsQuickTaskTabBar.vue'
18
+ import type { Task } from '@core/components/ui/task-item/UiTaskItem.vue'
18
19
  import { useUiStore } from '@core/stores/ui.store'
19
- import type { Task } from '@core/types/task.type.ts'
20
20
  import { computed, ref } from 'vue'
21
21
 
22
22
  const props = defineProps<{
@@ -21,10 +21,13 @@
21
21
  </div>
22
22
 
23
23
  <div class="main-content">
24
- <div v-if="task.name" class="content-left">
25
- <UiLink size="medium">
26
- {{ task.name }}
27
- </UiLink>
24
+ <div v-if="task.nameParts || task.name" class="content-left">
25
+ <template v-if="task.nameParts">
26
+ <template v-for="(part, index) in task.nameParts" :key="index">
27
+ <UiLink size="medium" :to="part.to">{{ part.text }}</UiLink>
28
+ </template>
29
+ </template>
30
+ <UiLink v-else size="small">{{ task.name }}</UiLink>
28
31
  <div v-if="shouldShowInfos || hasSubTasks" class="infos">
29
32
  <UiCounter v-if="hasSubTasks" :value="subTasksCount" accent="brand" variant="secondary" size="small" />
30
33
  <UiInfo v-if="hasInfos" accent="info" />
@@ -41,7 +44,7 @@
41
44
  <UiCircleProgressBar :accent="progressAccent" size="small" :value="progress" />
42
45
  </div>
43
46
  <div class="actions">
44
- <UiButtonIcon icon="fa:eye" size="medium" accent="brand" @click="emit('select', task.id)" />
47
+ <UiButtonIcon icon="fa:eye" size="small" accent="brand" @click="emit('select', task.id)" />
45
48
  </div>
46
49
  </div>
47
50
  </div>
@@ -61,6 +64,7 @@ import UiLink from '@core/components/ui/link/UiLink.vue'
61
64
  import UiTaskList from '@core/components/ui/task-list/UiTaskList.vue'
62
65
  import { useTimeAgo } from '@core/composables/locale-time-ago.composable.ts'
63
66
  import { vTooltip } from '@core/directives/tooltip.directive'
67
+ import type { TaskObjectSegment, TaskStatus } from '@core/types/task.type.ts'
64
68
  import { logicOr } from '@vueuse/math'
65
69
  import { computed } from 'vue'
66
70
  import { useI18n } from 'vue-i18n'
@@ -69,9 +73,13 @@ export type Task = {
69
73
  id: string
70
74
  infos?: { data: unknown; message: string }[]
71
75
  name?: string
76
+ nameParts?: TaskObjectSegment[]
72
77
  progress?: number
78
+ tag?: string
79
+ userName?: string
80
+ start?: number
73
81
  end?: number
74
- status: 'failure' | 'interrupted' | 'pending' | 'success'
82
+ status: TaskStatus
75
83
  subtasks?: Task[]
76
84
  warnings?: { data: unknown; message: string }[]
77
85
  }
@@ -16,8 +16,8 @@
16
16
 
17
17
  <script lang="ts" setup>
18
18
  import UiTaskItem from '@core/components/ui/task-item/UiTaskItem.vue'
19
+ import type { Task } from '@core/components/ui/task-item/UiTaskItem.vue'
19
20
  import { useCollection } from '@core/packages/collection'
20
- import type { Task } from '@core/types/task.type.ts'
21
21
 
22
22
  const { tasks, depth = 0 } = defineProps<{
23
23
  tasks: Task[]
@@ -1,16 +1,19 @@
1
- <!-- v2 -->
1
+ <!-- v5 -->
2
2
  <template>
3
- <div class="ui-textarea" :class="toVariants({ accent: hasMaxCharactersError ? 'danger' : accent })">
3
+ <div class="ui-textarea" :class="toVariants({ accent: isExceedingMaxCharacters ? 'danger' : accent })">
4
4
  <UiLabel v-if="slots.default" :accent="labelAccent" :required :href :for="id">
5
5
  <slot />
6
6
  </UiLabel>
7
7
  <textarea v-bind="attrs" :id ref="textarea" v-model="model" :disabled class="textarea" />
8
8
  <UiCharacterLimit v-if="maxCharacters" :count="model.trim().length" :max="maxCharacters" />
9
+ <UiInfo v-if="slots.info" accent="info">
10
+ <slot name="info" />
11
+ </UiInfo>
9
12
  <UiInfo v-if="isExceedingMaxCharacters" accent="danger">
10
13
  {{ t('field:exceeds-max-characters', { max: maxCharacters }) }}
11
14
  </UiInfo>
12
- <UiInfo v-if="slots.info" :accent="accent === 'brand' ? 'info' : accent">
13
- <slot name="info" />
15
+ <UiInfo v-if="slots.message && labelAccent !== 'neutral'" :accent="labelAccent">
16
+ <slot name="message" />
14
17
  </UiInfo>
15
18
  </div>
16
19
  </template>
@@ -46,6 +49,7 @@ const model = defineModel<string>({ required: true })
46
49
  const slots = defineSlots<{
47
50
  default?(): any
48
51
  info?(): any
52
+ message?(): any
49
53
  }>()
50
54
 
51
55
  const { t } = useI18n()
@@ -58,16 +62,10 @@ const { focused } = useFocus(textAreaElement)
58
62
 
59
63
  // WIP: To update when using VeeValidate and custom validation rules
60
64
  const isExceedingMaxCharacters = computed(() =>
61
- maxCharacters !== undefined ? model.value.trim().length > maxCharacters : false
65
+ maxCharacters !== undefined ? !focused.value && model.value.trim().length > maxCharacters : false
62
66
  )
63
67
 
64
- const hasMaxCharactersError = computed(() => !focused.value && isExceedingMaxCharacters.value)
65
-
66
68
  const labelAccent = computed(() => {
67
- if (hasMaxCharactersError.value) {
68
- return 'danger'
69
- }
70
-
71
69
  return accent === 'brand' ? 'neutral' : accent
72
70
  })
73
71
  </script>
@@ -101,8 +99,8 @@ const labelAccent = computed(() => {
101
99
  }
102
100
 
103
101
  &:focus:not(:active) {
104
- border-width: 0.2rem;
105
102
  border-color: var(--color-brand-item-base);
103
+ box-shadow: inset 0 0 0 0.1rem var(--color-brand-item-base);
106
104
  }
107
105
 
108
106
  &:disabled {
@@ -125,8 +123,8 @@ const labelAccent = computed(() => {
125
123
  }
126
124
 
127
125
  &:focus:not(:active) {
128
- border-width: 0.2rem;
129
126
  border-color: var(--color-warning-item-base);
127
+ box-shadow: inset 0 0 0 0.1rem var(--color-warning-item-base);
130
128
  }
131
129
 
132
130
  &:disabled {
@@ -149,8 +147,8 @@ const labelAccent = computed(() => {
149
147
  }
150
148
 
151
149
  &:focus:not(:active) {
152
- border-width: 0.2rem;
153
150
  border-color: var(--color-danger-item-base);
151
+ box-shadow: inset 0 0 0 0.1rem var(--color-danger-item-base);
154
152
  }
155
153
 
156
154
  &:disabled {
@@ -72,7 +72,7 @@ const hasToggle = inject(IK_TREE_ITEM_HAS_CHILDREN, ref(false))
72
72
 
73
73
  const isExpanded = inject(IK_TREE_ITEM_EXPANDED, ref(true))
74
74
 
75
- const depth = inject(IK_TREE_LIST_DEPTH, 0)
75
+ const depth = inject(IK_TREE_LIST_DEPTH, ref(0))
76
76
  </script>
77
77
 
78
78
  <style lang="postcss" scoped>
@@ -0,0 +1,43 @@
1
+ <template>
2
+ <VtsModal accent="info" icon="status:info-picto" dismissible>
3
+ <template #title>
4
+ {{ connection.title }}
5
+ </template>
6
+
7
+ <template v-if="action === CONNECTION_ACTION.DISCONNECT" #content>
8
+ {{ t('vif-disconnect-info') }}
9
+ </template>
10
+
11
+ <template #buttons>
12
+ <VtsModalCancelButton>{{ t('action:go-back') }}</VtsModalCancelButton>
13
+ <VtsModalConfirmButton>
14
+ {{ connection.action }}
15
+ </VtsModalConfirmButton>
16
+ </template>
17
+ </VtsModal>
18
+ </template>
19
+
20
+ <script lang="ts" setup>
21
+ import VtsModal from '@core/components/modal/VtsModal.vue'
22
+ import VtsModalCancelButton from '@core/components/modal/VtsModalCancelButton.vue'
23
+ import VtsModalConfirmButton from '@core/components/modal/VtsModalConfirmButton.vue'
24
+ import { useMapper } from '@core/packages/mapper'
25
+ import { CONNECTION_ACTION, type ConnectionAction } from '@core/types/connection.ts'
26
+ import { useI18n } from 'vue-i18n'
27
+
28
+ const { action, count } = defineProps<{
29
+ action: ConnectionAction
30
+ count: number
31
+ }>()
32
+
33
+ const { t } = useI18n()
34
+
35
+ const connection = useMapper(
36
+ () => action,
37
+ () => ({
38
+ connect: { title: t('vif-connect-title', { n: count }), action: t('action:connect-n-vifs', { n: count }) },
39
+ disconnect: { title: t('vif-disconnect-title', { n: count }), action: t('action:disconnect-n-vifs', { n: count }) },
40
+ }),
41
+ 'connect'
42
+ )
43
+ </script>
@@ -0,0 +1,33 @@
1
+ # useMultiSelect composable
2
+
3
+ ```vue
4
+ <template>
5
+ <table>
6
+ <thead>
7
+ <tr>
8
+ <th>
9
+ <input type="checkbox" v-model="areAllSelected" />
10
+ </th>
11
+ <th>Name</th>
12
+ </tr>
13
+ </thead>
14
+ <tbody>
15
+ <tr v-for="item in items">
16
+ <td>
17
+ <input type="checkbox" :value="item.id" v-model="selected" />
18
+ </td>
19
+ <td>{{ item.name }}</td>
20
+ </tr>
21
+ </tbody>
22
+ </table>
23
+
24
+ <!-- You can use something else than a "Select All" checkbox -->
25
+ <button @click="areAllSelected = !areAllSelected">Toggle all selected</button>
26
+ </template>
27
+
28
+ <script lang="ts" setup>
29
+ import useMultiSelect from './multi-select.composable'
30
+
31
+ const { selected, areAllSelected } = useMultiSelect()
32
+ </script>
33
+ ```
@@ -1,5 +1,5 @@
1
- import type { StateHeroType } from '@core/components/state-hero/VtsStateHero.vue'
2
1
  import type { TableState } from '@core/components/table/VtsTable.vue'
2
+ import type { StateHeroType } from '@core/types/state-hero.type.ts'
3
3
  import { computed, toValue, type MaybeRefOrGetter } from 'vue'
4
4
  import { useI18n } from 'vue-i18n'
5
5
 
@@ -4,9 +4,11 @@ import { faCopy, faSquare as checkboxEmpty, type IconDefinition } from '@fortawe
4
4
  import {
5
5
  faAdd,
6
6
  faArrowCircleRight,
7
+ faArrowDownLong,
7
8
  faArrowRight,
8
9
  faArrowRotateLeft,
9
10
  faArrowRotateRight,
11
+ faArrowUpLong,
10
12
  faArrowUpRightFromSquare,
11
13
  faBan,
12
14
  faBars,
@@ -146,6 +148,18 @@ export const actionIcons = defineIconPack({
146
148
  color: 'var(--color-neutral-txt-primary)',
147
149
  rotate: 90,
148
150
  },
151
+ 'import-export': [
152
+ {
153
+ icon: faArrowUpLong,
154
+ color: 'var(--color-neutral-txt-primary)',
155
+ translate: [-5, 0],
156
+ },
157
+ {
158
+ icon: faArrowDownLong,
159
+ color: 'var(--color-neutral-txt-primary)',
160
+ translate: [5, 0],
161
+ },
162
+ ],
149
163
  reboot: constructIcon(faArrowRotateRight),
150
164
  'force-reboot': [
151
165
  constructIcon(faArrowRotateRight),