@xen-orchestra/web-core 0.56.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 (68) hide show
  1. package/lib/assets/css/base.pcss +5 -0
  2. package/lib/components/input-wrapper/VtsInputWrapper.vue +2 -2
  3. package/lib/components/layout/VtsLayoutSidebar.vue +116 -43
  4. package/lib/components/menu/MenuItem.vue +9 -1
  5. package/lib/components/menu/MenuList.vue +4 -2
  6. package/lib/components/menu/VtsActionsMenu.vue +24 -24
  7. package/lib/components/modal/VtsActionModal.vue +44 -9
  8. package/lib/components/modal/VtsBlockedModal.vue +1 -1
  9. package/lib/components/modal/VtsDeleteModal.vue +1 -1
  10. package/lib/components/overlay/VtsOverlayButton.vue +35 -0
  11. package/lib/components/overlay/VtsOverlayCancelButton.vue +16 -0
  12. package/lib/components/overlay/VtsOverlayConfirmButton.vue +25 -0
  13. package/lib/components/overlay/VtsOverlayList.vue +82 -0
  14. package/lib/components/progress-bar/VtsProgressBar.vue +5 -3
  15. package/lib/components/select/VtsSelect.vue +0 -3
  16. package/lib/components/space-card/VtsSpaceCard.vue +1 -1
  17. package/lib/components/table/cells/VtsProgressBarCell.vue +1 -1
  18. package/lib/components/table/cells/VtsUserNameCell.vue +29 -0
  19. package/lib/components/task/VtsQuickTaskButton.vue +1 -1
  20. package/lib/components/task/VtsQuickTaskList.vue +1 -1
  21. package/lib/components/tree/VtsTreeItem.vue +4 -4
  22. package/lib/components/ui/head-bar/UiHeadBar.vue +1 -1
  23. package/lib/components/ui/input/UiInput.vue +53 -9
  24. package/lib/components/ui/query-search-bar/UiQuerySearchBar.vue +1 -15
  25. package/lib/components/ui/quick-task-item/UiQuickTaskItem.vue +1 -1
  26. package/lib/components/ui/quick-task-panel/UiQuickTaskPanel.vue +1 -1
  27. package/lib/components/ui/task-item/UiTaskItem.vue +14 -6
  28. package/lib/components/ui/task-list/UiTaskList.vue +1 -1
  29. package/lib/components/ui/text-area/UiTextarea.vue +12 -14
  30. package/lib/components/vif-connection-toggle-modal/VtsVifConnectionToggleModal.vue +43 -0
  31. package/lib/composables/table/multi-select.composable.md +33 -0
  32. package/lib/layouts/CoreLayout.vue +74 -62
  33. package/lib/locales/en.json +63 -2
  34. package/lib/locales/fr.json +63 -2
  35. package/lib/packages/form-validation/custom-rules/ip-addresses.rule.ts +15 -0
  36. package/lib/packages/form-validation/custom-rules/ip.regex.ts +26 -0
  37. package/lib/packages/form-validation/custom-rules/ipv4-or-cidr.rule.ts +2 -3
  38. package/lib/packages/form-validation/index.ts +2 -0
  39. package/lib/packages/hide-permanently/README.md +60 -0
  40. package/lib/packages/hide-permanently/types.ts +3 -0
  41. package/lib/packages/hide-permanently/use-hide-permanently.ts +15 -0
  42. package/lib/packages/overlay/OverlayComponent.vue +18 -0
  43. package/lib/packages/overlay/README.md +285 -0
  44. package/lib/packages/overlay/create-event-handler.ts +80 -0
  45. package/lib/packages/overlay/injection-keys.ts +3 -0
  46. package/lib/packages/overlay/is-thenable.ts +3 -0
  47. package/lib/packages/overlay/symbols.ts +5 -0
  48. package/lib/packages/overlay/types.ts +94 -0
  49. package/lib/packages/overlay/use-overlay-escape.ts +34 -0
  50. package/lib/packages/overlay/use-overlay-store.ts +33 -0
  51. package/lib/packages/overlay/use-overlay-trigger.ts +33 -0
  52. package/lib/packages/overlay/use-overlay.ts +99 -0
  53. package/lib/packages/progress/use-progress.ts +12 -2
  54. package/lib/packages/sidebar/index.ts +2 -0
  55. package/lib/packages/sidebar/sidebar.store.ts +55 -0
  56. package/lib/packages/sidebar/types.ts +21 -0
  57. package/lib/packages/sidebar/use-sidebar-resize.ts +47 -0
  58. package/lib/packages/sidebar/use-sidebar-responsive-expand.ts +19 -0
  59. package/lib/tables/column-definitions/user-name-column.ts +10 -0
  60. package/lib/tables/column-sets/server-columns.ts +2 -2
  61. package/lib/tables/column-sets/user-columns.ts +16 -0
  62. package/lib/types/object.type.ts +9 -1
  63. package/lib/types/task.type.ts +5 -12
  64. package/lib/utils/injection-keys.util.ts +3 -1
  65. package/lib/utils/ip-address.utils.ts +7 -0
  66. package/lib/utils/sr.utils.ts +4 -0
  67. package/package.json +2 -1
  68. package/lib/stores/sidebar.store.ts +0 -79
@@ -90,3 +90,8 @@ pre {
90
90
  --scrollbar-border: 0.2rem;
91
91
  }
92
92
  }
93
+
94
+ .em-dash-prefix::before {
95
+ content: '\2014'; /* em dash — */
96
+ margin-inline-end: 0.8rem;
97
+ }
@@ -51,10 +51,10 @@ const unsortedMessages = useArrayMap(
51
51
  })
52
52
  )
53
53
 
54
- const messages = useRanked(unsortedMessages, ({ accent }) => accent, ['danger', 'warning', 'success', 'info'])
54
+ const messages = useRanked(unsortedMessages, ({ accent }) => accent, ['muted', 'info', 'success', 'warning', 'danger'])
55
55
 
56
56
  const labelAccent = useMapper<InfoAccent, LabelAccent>(
57
- () => messages.value[0]?.accent,
57
+ () => messages.value.at(-1)?.accent,
58
58
  {
59
59
  info: 'neutral',
60
60
  success: 'neutral',
@@ -1,27 +1,30 @@
1
1
  <template>
2
- <div
3
- :class="{
4
- locked: sidebar.isLocked && !ui.isSmall,
5
- expanded: sidebar.isExpanded,
6
- mobile: ui.isSmall,
7
- 'border-right': !ui.isSmall,
8
- }"
9
- class="vts-layout-sidebar"
10
- >
11
- <div v-if="!ui.isSmall" class="lock">
12
- <UiButtonIcon
13
- v-tooltip="{
14
- content: sidebar.isLocked ? t('action:sidebar-unlock') : t('action:sidebar-lock'),
15
- placement: 'right',
16
- }"
17
- accent="brand"
18
- size="small"
19
- :icon="sidebar.isLocked ? 'fa:thumb-tack-slash' : 'fa:thumb-tack'"
20
- @click="sidebar.toggleLock()"
21
- />
2
+ <div :class="className" class="vts-layout-sidebar">
3
+ <div
4
+ v-if="!ui.isSmall"
5
+ :class="{ active: sidebar.isResizing }"
6
+ class="resize-handle"
7
+ @mousedown="sidebar.startResize"
8
+ />
9
+ <div v-if="slots.header || (showLock && !ui.isSmall)" class="header">
10
+ <div v-if="slots.header" class="start">
11
+ <slot name="header" />
12
+ </div>
13
+ <div v-if="showLock && !ui.isSmall" class="lock">
14
+ <UiButtonIcon
15
+ v-tooltip="{
16
+ content: sidebar.isLocked ? t('action:sidebar-unlock') : t('action:sidebar-lock'),
17
+ placement: lockTooltipPosition,
18
+ }"
19
+ accent="brand"
20
+ size="small"
21
+ :icon="sidebar.isLocked ? 'fa:thumb-tack-slash' : 'fa:thumb-tack'"
22
+ @click="sidebar.toggleLock()"
23
+ />
24
+ </div>
22
25
  </div>
23
- <div v-if="slots.header">
24
- <slot name="header" />
26
+ <div v-if="slots.subheader" class="subheader">
27
+ <slot name="subheader" />
25
28
  </div>
26
29
  <div class="content">
27
30
  <slot />
@@ -29,32 +32,47 @@
29
32
  <div v-if="slots.footer">
30
33
  <slot name="footer" />
31
34
  </div>
32
- <div
33
- v-if="!ui.isSmall"
34
- :class="{ active: sidebar.isResizing }"
35
- class="resize-handle"
36
- @mousedown="sidebar.startResize"
37
- />
38
35
  </div>
39
36
  </template>
40
37
 
41
38
  <script lang="ts" setup>
42
39
  import UiButtonIcon from '@core/components/ui/button-icon/UiButtonIcon.vue'
43
- import { vTooltip } from '@core/directives/tooltip.directive'
44
- import { useSidebarStore } from '@core/stores/sidebar.store'
45
- import { useUiStore } from '@core/stores/ui.store'
40
+ import { vTooltip } from '@core/directives/tooltip.directive.ts'
41
+ import { type SidebarSide, useLeftSidebarStore, useRightSidebarStore } from '@core/packages/sidebar'
42
+ import { useUiStore } from '@core/stores/ui.store.ts'
43
+ import { toVariants } from '@core/utils/to-variants.util.ts'
44
+ import { computed } from 'vue'
46
45
  import { useI18n } from 'vue-i18n'
47
46
 
47
+ const { side = 'left', showLock = true } = defineProps<{
48
+ side?: SidebarSide
49
+ showLock?: boolean
50
+ }>()
51
+
48
52
  const slots = defineSlots<{
49
53
  default(): any
50
54
  header?(): any
55
+ subheader?(): any
51
56
  footer?(): any
52
57
  }>()
53
58
 
54
59
  const { t } = useI18n()
55
60
 
56
- const sidebar = useSidebarStore()
57
61
  const ui = useUiStore()
62
+
63
+ const leftSidebar = useLeftSidebarStore()
64
+ const rightSidebar = useRightSidebarStore()
65
+ const sidebar = computed(() => (side === 'right' ? rightSidebar : leftSidebar))
66
+
67
+ const lockTooltipPosition = computed(() => (side === 'left' ? 'right' : 'left'))
68
+
69
+ const className = computed(() => [
70
+ {
71
+ locked: sidebar.value.isLocked && !ui.isSmall,
72
+ bordered: !ui.isSmall,
73
+ },
74
+ ...toVariants({ side }),
75
+ ])
58
76
  </script>
59
77
 
60
78
  <style lang="postcss" scoped>
@@ -62,32 +80,74 @@ const ui = useUiStore()
62
80
  position: relative;
63
81
  display: flex;
64
82
  flex-direction: column;
83
+ flex-shrink: 0;
65
84
  height: 100%;
66
85
  background-color: var(--color-neutral-background-secondary);
67
86
  width: v-bind('sidebar.cssWidth');
68
87
  z-index: 1010;
69
88
  transition:
70
- margin-left 0.25s,
89
+ margin-inline 0.25s,
71
90
  transform 0.25s;
72
91
 
73
- &.border-right {
74
- border-right: 0.1rem solid var(--color-neutral-border);
92
+ &.bordered {
93
+ &.side--left {
94
+ border-inline-end: 0.1rem solid var(--color-neutral-border);
95
+ }
96
+
97
+ &.side--right {
98
+ border-inline-start: 0.1rem solid var(--color-neutral-border);
99
+ }
75
100
  }
76
101
 
77
102
  &.locked {
78
- margin-left: v-bind('sidebar.cssOffset');
103
+ &.side--left {
104
+ margin-inline-start: v-bind('sidebar.cssLockedOffset');
105
+ }
106
+
107
+ &.side--right {
108
+ margin-inline-end: v-bind('sidebar.cssLockedOffset');
109
+ }
79
110
  }
80
111
 
81
112
  &:not(.locked) {
82
113
  position: absolute;
83
- transform: translateX(v-bind('sidebar.cssOffset'));
114
+
115
+ &.side--left {
116
+ inset-inline-start: 0;
117
+ }
118
+
119
+ &.side--right {
120
+ inset-inline-end: 0;
121
+ }
122
+
123
+ transform: translateX(v-bind('sidebar.cssUnlockedOffset'));
84
124
  }
85
125
 
86
- .lock {
87
- position: absolute;
88
- inset-block-start: 0.8rem;
89
- inset-inline-end: 1.2rem;
90
- z-index: 1;
126
+ .header,
127
+ .subheader,
128
+ .content {
129
+ &:not(:last-child) {
130
+ border-block-end: 0.1rem solid var(--color-neutral-border);
131
+ }
132
+ }
133
+
134
+ .header {
135
+ display: flex;
136
+ align-items: center;
137
+ background-color: var(--color-neutral-background-primary);
138
+
139
+ .start {
140
+ flex: 1;
141
+ overflow-x: auto;
142
+ }
143
+
144
+ .lock {
145
+ flex-shrink: 0;
146
+ margin-inline-start: auto;
147
+ margin-inline-end: 0.8rem;
148
+ margin-block: 0.4rem;
149
+ z-index: 2;
150
+ }
91
151
  }
92
152
 
93
153
  .content {
@@ -97,12 +157,13 @@ const ui = useUiStore()
97
157
 
98
158
  .resize-handle {
99
159
  position: absolute;
100
- inset: 0 0 0 auto;
160
+ inset-block: 0;
101
161
  width: 0.8rem;
102
162
  background-color: transparent;
103
163
  cursor: col-resize;
104
164
  transition: background-color 0.4s;
105
165
  user-select: none;
166
+ z-index: 1;
106
167
 
107
168
  &:hover,
108
169
  &.active {
@@ -110,5 +171,17 @@ const ui = useUiStore()
110
171
  transition: background-color 0.05s;
111
172
  }
112
173
  }
174
+
175
+ &.side--left {
176
+ .resize-handle {
177
+ inset-inline-end: 0;
178
+ }
179
+ }
180
+
181
+ &.side--right {
182
+ .resize-handle {
183
+ inset-inline-start: 0;
184
+ }
185
+ }
113
186
  }
114
187
  </style>
@@ -7,13 +7,21 @@
7
7
  :busy="isBusy"
8
8
  :disabled="isDisabled"
9
9
  :icon
10
+ class="typo-body-bold-small"
10
11
  @click="handleClick"
11
12
  >
12
13
  <slot />
13
14
  </MenuTrigger>
14
15
  <MenuList v-else :disabled="isDisabled">
15
16
  <template #trigger="{ open, isOpen }">
16
- <MenuTrigger :active="isOpen" :busy="isBusy" :disabled="isDisabled" :icon @click="open">
17
+ <MenuTrigger
18
+ :active="isOpen"
19
+ :busy="isBusy"
20
+ :disabled="isDisabled"
21
+ class="typo-body-bold-small"
22
+ :icon
23
+ @click="open"
24
+ >
17
25
  <slot />
18
26
  <VtsIcon :name="submenuIcon" size="medium" class="submenu-icon" />
19
27
  </MenuTrigger>
@@ -69,13 +69,15 @@ const open = (event: MouseEvent) => {
69
69
 
70
70
  isOpen.value = true
71
71
 
72
+ const trigger = event.currentTarget as HTMLElement
73
+
72
74
  nextTick(() => {
73
75
  clearClickOutsideEvent = onClickOutside(menu, () => (isOpen.value = false), {
74
- ignore: [event.currentTarget as HTMLElement],
76
+ ignore: () => [trigger, '.menu-list'],
75
77
  controls: false,
76
78
  })
77
79
 
78
- placementJs(event.currentTarget as HTMLElement, unrefElement(menu), {
80
+ placementJs(trigger, unrefElement(menu), {
79
81
  placement: props.placement ?? (isParentHorizontal.value ? 'bottom-start' : 'right-start'),
80
82
  })
81
83
  })
@@ -13,30 +13,30 @@
13
13
  />
14
14
  </template>
15
15
  <slot>
16
- <MenuItem
17
- v-for="(action, index) of actions"
18
- :key="index"
19
- :icon="action.icon"
20
- :disabled="action.disabled"
21
- :busy="action.busy"
22
- :on-click="action.onClick"
23
- >
24
- {{ action.label }}
25
- <i v-if="action.hint">{{ action.hint }}</i>
26
- <template v-if="isGroupAction(action)" #submenu>
27
- <MenuItem
28
- v-for="(child, childIndex) of action.children"
29
- :key="childIndex"
30
- :icon="child.icon"
31
- :disabled="child.disabled"
32
- :busy="child.busy"
33
- :on-click="child.onClick"
34
- >
35
- {{ child.label }}
36
- <i v-if="child.hint">{{ child.hint }}</i>
37
- </MenuItem>
38
- </template>
39
- </MenuItem>
16
+ <MenuItem
17
+ v-for="(action, index) of actions"
18
+ :key="index"
19
+ :icon="action.icon"
20
+ :disabled="action.disabled"
21
+ :busy="action.busy"
22
+ :on-click="action.onClick"
23
+ >
24
+ {{ action.label }}
25
+ <i v-if="action.hint" class="em-dash-prefix">{{ action.hint }}</i>
26
+ <template v-if="isGroupAction(action)" #submenu>
27
+ <MenuItem
28
+ v-for="(child, childIndex) of action.children"
29
+ :key="childIndex"
30
+ :icon="child.icon"
31
+ :disabled="child.disabled"
32
+ :busy="child.busy"
33
+ :on-click="child.onClick"
34
+ >
35
+ {{ child.label }}
36
+ <i v-if="child.hint" class="em-dash-prefix">{{ child.hint }}</i>
37
+ </MenuItem>
38
+ </template>
39
+ </MenuItem>
40
40
  </slot>
41
41
  </MenuList>
42
42
  </template>
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <VtsModal :accent icon="status:info-picto">
2
+ <VtsModal :accent :icon dismissible>
3
3
  <template #title>
4
4
  <span>{{ modalTexts.title }}</span>
5
5
  </template>
@@ -20,8 +20,9 @@ import VtsModal from '@core/components/modal/VtsModal.vue'
20
20
  import VtsModalCancelButton from '@core/components/modal/VtsModalCancelButton.vue'
21
21
  import VtsModalConfirmButton from '@core/components/modal/VtsModalConfirmButton.vue'
22
22
  import type { ModalAccent } from '@core/components/ui/modal/UiModal.vue'
23
+ import type { IconName } from '@core/icons'
23
24
  import { useMapper } from '@core/packages/mapper/use-mapper.ts'
24
- import type { ObjectType, VmActions } from '@core/types/object.type.ts'
25
+ import type { ActionsByObject, HostActions, ObjectType, VmActions } from '@core/types/object.type.ts'
25
26
  import { useI18n } from 'vue-i18n'
26
27
 
27
28
  type ActionTexts = {
@@ -30,15 +31,37 @@ type ActionTexts = {
30
31
  action: string
31
32
  }
32
33
 
33
- const { action, object } = defineProps<{
34
+ type TextMappingByObject = {
35
+ [O in ObjectType]: Record<ActionsByObject[O], ActionTexts>
36
+ }
37
+
38
+ type VtsActionModalVmProps = {
39
+ object: 'vm'
34
40
  action: VmActions
35
- accent: ModalAccent
36
- object: ObjectType
37
- }>()
41
+ hostName?: never
42
+ }
43
+
44
+ type VtsActionModalHostProps = {
45
+ object: 'host'
46
+ action: HostActions
47
+ hostName: string
48
+ }
49
+
50
+ const { action, object, hostName } = defineProps<
51
+ (VtsActionModalVmProps | VtsActionModalHostProps) & {
52
+ accent: ModalAccent
53
+ icon: IconName
54
+ }
55
+ >()
56
+
57
+ const defaultActionByObject: { [O in ObjectType]: ActionsByObject[O] } = {
58
+ vm: 'shutdown',
59
+ host: 'disable',
60
+ }
38
61
 
39
62
  const { t } = useI18n()
40
63
 
41
- const textMappingsByObject: Record<ObjectType, Record<VmActions, ActionTexts>> = {
64
+ const textMappingsByObject: TextMappingByObject = {
42
65
  vm: {
43
66
  'force-reboot': {
44
67
  title: t('modal:confirm-vm-force-reboot'),
@@ -61,11 +84,23 @@ const textMappingsByObject: Record<ObjectType, Record<VmActions, ActionTexts>> =
61
84
  action: t('modal:action:vm-shutdown'),
62
85
  },
63
86
  },
87
+ host: {
88
+ enable: {
89
+ title: t('modal:confirm-host-enable', { host: hostName }),
90
+ message: t('modal:host-enable-message'),
91
+ action: t('action:enable-host'),
92
+ },
93
+ disable: {
94
+ title: t('modal:confirm-host-disable', { host: hostName }),
95
+ message: t('modal:host-disable-message'),
96
+ action: t('action:disable-host'),
97
+ },
98
+ },
64
99
  }
65
100
 
66
101
  const modalTexts = useMapper(
67
102
  () => action,
68
- () => textMappingsByObject[object],
69
- () => 'shutdown'
103
+ () => textMappingsByObject[object] as Record<ActionsByObject[ObjectType], ActionTexts>,
104
+ () => defaultActionByObject[object]
70
105
  )
71
106
  </script>
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <VtsModal accent="danger" icon="status:danger-picto">
2
+ <VtsModal accent="danger" icon="status:danger-picto" dismissible>
3
3
  <template #title>
4
4
  <span>{{ modalTexts.title }}</span>
5
5
  </template>
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <VtsModal accent="warning" icon="status:warning-picto">
2
+ <VtsModal accent="warning" icon="status:warning-picto" dismissible>
3
3
  <template #title>
4
4
  <I18nT keypath="confirm-delete" scope="global" tag="div">
5
5
  <span class="n-delete">
@@ -0,0 +1,35 @@
1
+ <template>
2
+ <UiButton :accent="buttonAccent" :busy="isBusy" :disabled="isDisabled" :variant size="medium" @click="trigger">
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 { useOverlayTrigger } from '@core/packages/overlay/use-overlay-trigger.ts'
11
+ import { IK_OVERLAY_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 { isBusy, isDisabled, trigger } = useOverlayTrigger()
23
+
24
+ const overlayAccent = inject(IK_OVERLAY_ACCENT, undefined)
25
+
26
+ const buttonAccent = useMapper(
27
+ () => overlayAccent?.value,
28
+ {
29
+ info: 'brand',
30
+ warning: 'warning',
31
+ danger: 'danger',
32
+ },
33
+ 'info'
34
+ )
35
+ </script>
@@ -0,0 +1,16 @@
1
+ <template>
2
+ <VtsOverlayButton variant="secondary">
3
+ <slot>{{ t('cancel') }}</slot>
4
+ </VtsOverlayButton>
5
+ </template>
6
+
7
+ <script lang="ts" setup>
8
+ import VtsOverlayButton from '@core/components/overlay/VtsOverlayButton.vue'
9
+ import { useI18n } from 'vue-i18n'
10
+
11
+ defineSlots<{
12
+ default?(): any
13
+ }>()
14
+
15
+ const { t } = useI18n()
16
+ </script>
@@ -0,0 +1,25 @@
1
+ <template>
2
+ <VtsOverlayButton
3
+ variant="primary"
4
+ :type="onClick ? 'button' : 'submit'"
5
+ v-on="onClick ? { click: () => emit('click') } : {}"
6
+ >
7
+ <slot />
8
+ </VtsOverlayButton>
9
+ </template>
10
+
11
+ <script lang="ts" setup>
12
+ import VtsOverlayButton from '@core/components/overlay/VtsOverlayButton.vue'
13
+
14
+ const { onClick } = defineProps<{
15
+ onClick?: () => void
16
+ }>()
17
+
18
+ const emit = defineEmits<{
19
+ click: []
20
+ }>()
21
+
22
+ defineSlots<{
23
+ default(): any
24
+ }>()
25
+ </script>
@@ -0,0 +1,82 @@
1
+ <template>
2
+ <div class="vts-overlay-list">
3
+ <Transition name="fade">
4
+ <div v-if="overlayStore.overlays.length > 0" class="backdrop" />
5
+ </Transition>
6
+
7
+ <TransitionGroup tag="div" name="overlay" class="overlays">
8
+ <OverlayComponent
9
+ v-for="overlay of overlayStore.overlays"
10
+ :key="overlay.key"
11
+ class="overlay"
12
+ :overlay
13
+ :class="{ dimmed: !overlayStore.isCurrent(overlay.key) }"
14
+ />
15
+ </TransitionGroup>
16
+ </div>
17
+ </template>
18
+
19
+ <script lang="ts" setup>
20
+ import OverlayComponent from '@core/packages/overlay/OverlayComponent.vue'
21
+ import { useOverlayStore } from '@core/packages/overlay/use-overlay-store.ts'
22
+
23
+ const overlayStore = useOverlayStore()
24
+ </script>
25
+
26
+ <style lang="postcss" scoped>
27
+ .vts-overlay-list {
28
+ .backdrop {
29
+ position: fixed;
30
+ inset: 0;
31
+ z-index: 1011;
32
+ background-color: var(--color-opacity-primary);
33
+ pointer-events: none;
34
+
35
+ &.fade-enter-active,
36
+ &.fade-leave-active {
37
+ transition: opacity 0.3s ease;
38
+ }
39
+
40
+ &.fade-enter-from,
41
+ &.fade-leave-to {
42
+ opacity: 0;
43
+ }
44
+ }
45
+
46
+ .overlays {
47
+ position: fixed;
48
+ inset: 0;
49
+ z-index: 1012;
50
+
51
+ /* The container only positions the stacked modals; each modal catches its own
52
+ clicks, so an empty container must let clicks through. */
53
+ pointer-events: none;
54
+
55
+ .overlay {
56
+ pointer-events: auto;
57
+ transition:
58
+ opacity 0.3s ease,
59
+ transform 0.3s ease,
60
+ filter 0.3s ease;
61
+
62
+ &.dimmed {
63
+ filter: brightness(0.8);
64
+ }
65
+
66
+ &.overlay-enter-from.ui-modal,
67
+ &.overlay-leave-to.ui-modal {
68
+ opacity: 0;
69
+ }
70
+
71
+ &.overlay-enter-from.ui-drawer,
72
+ &.overlay-leave-to.ui-drawer {
73
+ transform: translateX(100%);
74
+
75
+ &:dir(rtl) {
76
+ transform: translateX(-100%);
77
+ }
78
+ }
79
+ }
80
+ }
81
+ }
82
+ </style>
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <div class="vts-progress-bar">
3
- <UiDataRuler v-if="!noruler" :max="percentageCap" :warning="threshold.payload" />
3
+ <UiDataRuler v-if="!noRuler" :max="percentageCap" :warning="threshold.payload" />
4
4
  <UiProgressBar :accent="threshold.payload.accent ?? 'info'" :fill-width :legend />
5
5
  </div>
6
6
  </template>
@@ -25,18 +25,20 @@ const {
25
25
  label,
26
26
  thresholds = defaultProgressThresholds(),
27
27
  legendType,
28
+ noRuler,
28
29
  } = defineProps<{
29
30
  current: number
30
31
  total: number
31
32
  label?: string
32
33
  legendType?: ProgressBarLegendType
33
34
  thresholds?: ProgressBarThresholdConfig
34
- noruler?: boolean
35
+ noRuler?: boolean
35
36
  }>()
36
37
 
37
38
  const progress = useProgress(
38
39
  () => current,
39
- () => total
40
+ () => total,
41
+ () => noRuler
40
42
  )
41
43
 
42
44
  const { percentageCap, percentage, fillWidth } = progress
@@ -4,7 +4,6 @@
4
4
  ref="triggerRef"
5
5
  :accent
6
6
  :disabled="isDisabled"
7
- :icon
8
7
  :model-value="selectedLabel"
9
8
  :placeholder
10
9
  :required="isRequired"
@@ -47,7 +46,6 @@ import VtsOption from '@core/components/select/VtsOption.vue'
47
46
  import UiDropdown from '@core/components/ui/dropdown/UiDropdown.vue'
48
47
  import UiDropdownList from '@core/components/ui/dropdown/UiDropdownList.vue'
49
48
  import UiInput from '@core/components/ui/input/UiInput.vue'
50
- import type { IconName } from '@core/icons'
51
49
  import {
52
50
  type FormSelect,
53
51
  type FormSelectId,
@@ -62,7 +60,6 @@ import { useI18n } from 'vue-i18n'
62
60
  const { accent, id } = defineProps<{
63
61
  accent: 'brand' | 'warning' | 'danger'
64
62
  id: TSelectId
65
- icon?: IconName
66
63
  }>()
67
64
 
68
65
  defineSlots<{
@@ -5,7 +5,7 @@
5
5
  </UiCardTitle>
6
6
 
7
7
  <div class="content">
8
- <VtsProgressBar noruler :current="used" :total :label legend-type="percent" class="progress" />
8
+ <VtsProgressBar no-ruler :current="used" :total :label legend-type="percent" class="progress" />
9
9
 
10
10
  <VtsCardRowKeyValue>
11
11
  <template #key>{{ t('used-space') }}</template>