@xen-orchestra/web-core 0.54.0 → 0.56.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 (107) hide show
  1. package/lib/components/button-group/VtsButtonGroup.vue +11 -1
  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 +89 -0
  5. package/lib/components/drawer/VtsDrawerButton.vue +22 -0
  6. package/lib/components/drawer/VtsDrawerCancelButton.vue +33 -0
  7. package/lib/components/drawer/VtsDrawerConfirmButton.vue +32 -0
  8. package/lib/components/drawer/VtsDrawerList.vue +34 -0
  9. package/lib/components/icon/VtsIcon.vue +3 -1
  10. package/lib/components/layout/VtsContentSidePanel.vue +41 -0
  11. package/lib/components/layout/VtsLayoutSidebar.vue +5 -2
  12. package/lib/components/menu/VtsActionsMenu.vue +37 -3
  13. package/lib/components/modal/VtsModalButton.vue +0 -1
  14. package/lib/components/operation-card/VtsOperationCard.vue +2 -1
  15. package/lib/components/panel/VtsPanel.vue +80 -0
  16. package/lib/components/panel/VtsSidePanel.vue +140 -0
  17. package/lib/components/query-builder/VtsQueryBuilderModal.vue +0 -1
  18. package/lib/components/select/VtsSelect.vue +1 -0
  19. package/lib/components/state-hero/VtsStateHero.vue +26 -24
  20. package/lib/components/table/VtsTable.vue +2 -1
  21. package/lib/components/table/cells/VtsActionCell.vue +7 -0
  22. package/lib/components/table/cells/VtsHeaderCell.vue +1 -17
  23. package/lib/components/table/cells/VtsLinkCell.vue +3 -1
  24. package/lib/components/table/cells/VtsTagCell.vue +4 -2
  25. package/lib/components/tree/VtsTreeItem.vue +9 -3
  26. package/lib/components/tree/VtsTreeList.vue +6 -3
  27. package/lib/components/tree/VtsTreeLoadingItem.vue +3 -3
  28. package/lib/components/ui/drawer/UiDrawer.vue +123 -0
  29. package/lib/components/ui/head-bar/UiHeadBar.vue +1 -1
  30. package/lib/components/ui/input/UiInput.vue +3 -1
  31. package/lib/components/ui/link/UiLink.vue +3 -1
  32. package/lib/components/ui/modal/UiModal.vue +10 -18
  33. package/lib/components/ui/panel/UiPanel.vue +7 -24
  34. package/lib/components/ui/tree-item-label/UiTreeItemLabel.vue +1 -1
  35. package/lib/composables/default-tab.composable.ts +4 -0
  36. package/lib/composables/table-state.composable.ts +1 -1
  37. package/lib/icons/action-icons.ts +20 -2
  38. package/lib/icons/custom-icons.ts +16 -0
  39. package/lib/icons/object-icons.ts +33 -18
  40. package/lib/locales/cs.json +110 -11
  41. package/lib/locales/da.json +1 -3
  42. package/lib/locales/de.json +1 -12
  43. package/lib/locales/en.json +76 -12
  44. package/lib/locales/es.json +15 -6
  45. package/lib/locales/fa.json +0 -1
  46. package/lib/locales/fr.json +76 -12
  47. package/lib/locales/it.json +0 -1
  48. package/lib/locales/ko.json +0 -2
  49. package/lib/locales/nb-NO.json +18 -20
  50. package/lib/locales/nl.json +0 -11
  51. package/lib/locales/pt-BR.json +0 -4
  52. package/lib/locales/pt.json +0 -4
  53. package/lib/locales/ru.json +0 -1
  54. package/lib/locales/sk.json +110 -11
  55. package/lib/locales/sv.json +2 -13
  56. package/lib/locales/uk.json +0 -1
  57. package/lib/locales/zh-Hans.json +79 -16
  58. package/lib/packages/drawer/DrawerProvider.vue +17 -0
  59. package/lib/packages/drawer/README.md +189 -0
  60. package/lib/packages/drawer/create-drawer-opener.ts +119 -0
  61. package/lib/packages/drawer/drawer.store.ts +22 -0
  62. package/lib/packages/drawer/types.ts +93 -0
  63. package/lib/packages/drawer/use-drawer.ts +53 -0
  64. package/lib/packages/modal/create-modal-opener.ts +12 -10
  65. package/lib/packages/remote-resource/define-remote-resource.ts +64 -29
  66. package/lib/packages/tree/types.ts +6 -0
  67. package/lib/packages/tree/use-tree.ts +38 -37
  68. package/lib/stores/panel.store.ts +56 -5
  69. package/lib/tables/column-definitions/action-column.ts +22 -10
  70. package/lib/tables/column-definitions/address-column.ts +1 -1
  71. package/lib/tables/column-definitions/button-column.ts +1 -1
  72. package/lib/tables/column-definitions/button-icon-column.ts +1 -1
  73. package/lib/tables/column-definitions/collapsed-list-column.ts +1 -1
  74. package/lib/tables/column-definitions/date-column.ts +1 -1
  75. package/lib/tables/column-definitions/double-link-column.ts +1 -1
  76. package/lib/tables/column-definitions/info-column.ts +1 -1
  77. package/lib/tables/column-definitions/input-column.ts +1 -5
  78. package/lib/tables/column-definitions/link-column.ts +1 -1
  79. package/lib/tables/column-definitions/link-or-text-column.ts +1 -1
  80. package/lib/tables/column-definitions/literal-column.ts +1 -1
  81. package/lib/tables/column-definitions/number-column.ts +1 -1
  82. package/lib/tables/column-definitions/percent-column.ts +1 -1
  83. package/lib/tables/column-definitions/progress-bar-column.ts +1 -1
  84. package/lib/tables/column-definitions/select-column.ts +1 -1
  85. package/lib/tables/column-definitions/status-column.ts +2 -2
  86. package/lib/tables/column-definitions/tag-column.ts +3 -2
  87. package/lib/tables/column-definitions/text-column.ts +1 -1
  88. package/lib/tables/column-definitions/truncated-text-column.ts +1 -1
  89. package/lib/tables/column-sets/backup-job-schedule-columns.ts +2 -2
  90. package/lib/tables/column-sets/backup-log-columns.ts +1 -1
  91. package/lib/tables/column-sets/new-vm-network-columns.ts +0 -2
  92. package/lib/tables/column-sets/new-vm-sr-columns.ts +0 -1
  93. package/lib/tables/column-sets/server-columns.ts +1 -1
  94. package/lib/tables/column-sets/snapshot-columns.ts +1 -1
  95. package/lib/tables/column-sets/sr-columns.ts +2 -2
  96. package/lib/tables/column-sets/traffic-rules-columns.ts +5 -7
  97. package/lib/tables/column-sets/vif-network-columns.ts +24 -0
  98. package/lib/tables/column-sets/vm-columns.ts +2 -3
  99. package/lib/tables/helpers/render-head-cell.ts +2 -4
  100. package/lib/tables/types.ts +0 -2
  101. package/lib/types/connection.ts +14 -0
  102. package/lib/types/state-hero.type.ts +19 -0
  103. package/lib/types/storage-repository.type.ts +14 -0
  104. package/lib/types/vue-virtual-scroller.d.ts +8 -2
  105. package/lib/utils/injection-keys.util.ts +1 -1
  106. package/lib/utils/sr.utils.ts +34 -0
  107. package/package.json +4 -4
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div class="vts-button-group">
2
+ <div class="vts-button-group" :class="{ 'no-stack': noStack }">
3
3
  <div class="line">
4
4
  <slot />
5
5
  </div>
@@ -10,6 +10,10 @@
10
10
  </template>
11
11
 
12
12
  <script lang="ts" setup>
13
+ defineProps<{
14
+ noStack?: boolean
15
+ }>()
16
+
13
17
  const slots = defineSlots<{
14
18
  default(): any
15
19
  tertiary?(): any
@@ -34,5 +38,11 @@ const slots = defineSlots<{
34
38
  flex-direction: column-reverse;
35
39
  }
36
40
  }
41
+
42
+ &.no-stack .line {
43
+ @media (--small) {
44
+ flex-direction: row;
45
+ }
46
+ }
37
47
  }
38
48
  </style>
@@ -0,0 +1,36 @@
1
+ <template>
2
+ <div class="vts-card-object-title">
3
+ <UiLink v-if="label" size="medium" :to :href :icon>
4
+ {{ label }}
5
+ </UiLink>
6
+ <VtsCodeSnippet class="id" :content="id" copy />
7
+ </div>
8
+ </template>
9
+
10
+ <script setup lang="ts">
11
+ import VtsCodeSnippet from '@core/components/code-snippet/VtsCodeSnippet.vue'
12
+ import UiLink from '@core/components/ui/link/UiLink.vue'
13
+ import type { IconName } from '@core/icons/index.ts'
14
+ import type { RouteLocationRaw } from 'vue-router'
15
+
16
+ defineProps<{
17
+ id: string
18
+ label?: string
19
+ icon?: IconName
20
+ to?: RouteLocationRaw
21
+ href?: string
22
+ }>()
23
+ </script>
24
+
25
+ <style scoped lang="postcss">
26
+ .vts-card-object-title {
27
+ display: flex;
28
+ flex-direction: column;
29
+ align-items: start;
30
+ gap: 0.8rem;
31
+
32
+ .id {
33
+ width: 100%;
34
+ }
35
+ }
36
+ </style>
@@ -1,8 +1,10 @@
1
1
  <template>
2
2
  <div class="vts-code-snippet">
3
- <slot>
4
- {{ content }}
5
- </slot>
3
+ <div v-tooltip="{ placement: 'bottom' }" class="content text-ellipsis">
4
+ <slot>
5
+ {{ content }}
6
+ </slot>
7
+ </div>
6
8
  <div v-if="copy || slots.addons" class="addons">
7
9
  <slot name="addons">
8
10
  <VtsCopyButton v-if="copy && content" :value="String(content)" />
@@ -13,6 +15,7 @@
13
15
 
14
16
  <script lang="ts" setup>
15
17
  import VtsCopyButton from '@core/components/copy-button/VtsCopyButton.vue'
18
+ import { vTooltip } from '@core/directives/tooltip.directive.ts'
16
19
 
17
20
  defineProps<{
18
21
  content?: string | number
@@ -27,13 +30,16 @@ const slots = defineSlots<{
27
30
 
28
31
  <style lang="postcss" scoped>
29
32
  .vts-code-snippet {
30
- font-family: 'Courier New', Courier, monospace;
31
33
  font-size: 1.4rem;
32
- color: var(--color-neutral-txt-primary);
33
34
  display: flex;
34
35
  align-items: center;
35
36
  gap: 0.8rem;
36
37
 
38
+ .content {
39
+ color: var(--color-neutral-txt-primary);
40
+ font-family: 'Courier New', Courier, monospace;
41
+ }
42
+
37
43
  .addons {
38
44
  margin-inline-start: auto;
39
45
  display: flex;
@@ -0,0 +1,89 @@
1
+ <template>
2
+ <UiDrawer
3
+ :is-open="effectiveIsOpen"
4
+ :is-dismissible="dismissible"
5
+ v-on="{ dismiss: handleDismiss, afterLeave: onAfterLeave }"
6
+ >
7
+ <template v-if="slots.title" #title>
8
+ <slot name="title" />
9
+ </template>
10
+
11
+ <template #content>
12
+ <slot name="content" />
13
+ </template>
14
+
15
+ <template v-if="slots.buttons" #buttons>
16
+ <slot name="buttons" />
17
+ </template>
18
+ </UiDrawer>
19
+ </template>
20
+
21
+ <script lang="ts" setup>
22
+ import UiDrawer from '@core/components/ui/drawer/UiDrawer.vue'
23
+ import { IK_DRAWER } from '@core/packages/drawer/types.ts'
24
+ import { useMagicKeys, whenever } from '@vueuse/core'
25
+ import { computed, inject, onMounted, ref } from 'vue'
26
+
27
+ const {
28
+ dismissible,
29
+ isOpen = undefined,
30
+ current,
31
+ } = defineProps<{
32
+ dismissible?: boolean
33
+ isOpen?: boolean
34
+ current?: boolean
35
+ }>()
36
+
37
+ const emit = defineEmits<{
38
+ dismiss: []
39
+ }>()
40
+
41
+ const slots = defineSlots<{
42
+ content(): any
43
+ title?(): any
44
+ buttons?(): any
45
+ }>()
46
+
47
+ const drawer = inject(IK_DRAWER, undefined)
48
+
49
+ const internalVisible = ref(false)
50
+
51
+ onMounted(() => {
52
+ if (isOpen === undefined) {
53
+ internalVisible.value = true
54
+ }
55
+ })
56
+
57
+ const effectiveIsOpen = computed(() => isOpen ?? internalVisible.value)
58
+
59
+ let afterLeaveCallback: (() => void) | undefined
60
+
61
+ function onAfterLeave() {
62
+ afterLeaveCallback?.()
63
+ afterLeaveCallback = undefined
64
+ }
65
+
66
+ const handleDismiss = computed(() => {
67
+ if (!dismissible) {
68
+ return undefined
69
+ }
70
+
71
+ return () => {
72
+ if (isOpen !== undefined) {
73
+ emit('dismiss')
74
+ return
75
+ }
76
+
77
+ afterLeaveCallback = () => drawer?.value.onCancel()
78
+ internalVisible.value = false
79
+ }
80
+ })
81
+
82
+ const { escape } = useMagicKeys()
83
+
84
+ whenever(escape, () => {
85
+ if (dismissible && current) {
86
+ handleDismiss.value?.()
87
+ }
88
+ })
89
+ </script>
@@ -0,0 +1,22 @@
1
+ <template>
2
+ <UiButton :accent :busy="drawer?.isBusy.value" :variant size="medium">
3
+ <slot />
4
+ </UiButton>
5
+ </template>
6
+
7
+ <script lang="ts" setup>
8
+ import UiButton, { type ButtonAccent, type ButtonVariant } from '@core/components/ui/button/UiButton.vue'
9
+ import { IK_DRAWER } from '@core/packages/drawer/types.ts'
10
+ import { inject } from 'vue'
11
+
12
+ const { accent = 'brand' } = defineProps<{
13
+ variant: ButtonVariant
14
+ accent?: ButtonAccent
15
+ }>()
16
+
17
+ defineSlots<{
18
+ default(): any
19
+ }>()
20
+
21
+ const drawer = inject(IK_DRAWER)
22
+ </script>
@@ -0,0 +1,33 @@
1
+ <template>
2
+ <VtsDrawerButton variant="secondary" @click="handleClick()">
3
+ <slot>{{ t('cancel') }}</slot>
4
+ </VtsDrawerButton>
5
+ </template>
6
+
7
+ <script lang="ts" setup>
8
+ import VtsDrawerButton from '@core/components/drawer/VtsDrawerButton.vue'
9
+ import { IK_DRAWER } from '@core/packages/drawer/types.ts'
10
+ import { inject } from 'vue'
11
+ import { useI18n } from 'vue-i18n'
12
+
13
+ const { onClick } = defineProps<{
14
+ onClick?: () => void
15
+ }>()
16
+
17
+ defineSlots<{
18
+ default?(): any
19
+ }>()
20
+
21
+ const { t } = useI18n()
22
+
23
+ const drawer = inject(IK_DRAWER)
24
+
25
+ function handleClick() {
26
+ if (onClick) {
27
+ onClick()
28
+ return
29
+ }
30
+
31
+ drawer?.value.onCancel()
32
+ }
33
+ </script>
@@ -0,0 +1,32 @@
1
+ <template>
2
+ <VtsDrawerButton :accent variant="primary" @click="handleClick()">
3
+ <slot />
4
+ </VtsDrawerButton>
5
+ </template>
6
+
7
+ <script lang="ts" setup>
8
+ import VtsDrawerButton from '@core/components/drawer/VtsDrawerButton.vue'
9
+ import type { ButtonAccent } from '@core/components/ui/button/UiButton.vue'
10
+ import { IK_DRAWER } from '@core/packages/drawer/types.ts'
11
+ import { inject } from 'vue'
12
+
13
+ const { onClick, accent = 'brand' } = defineProps<{
14
+ onClick?: () => void
15
+ accent?: ButtonAccent
16
+ }>()
17
+
18
+ defineSlots<{
19
+ default(): any
20
+ }>()
21
+
22
+ const drawer = inject(IK_DRAWER)
23
+
24
+ function handleClick() {
25
+ if (onClick) {
26
+ onClick()
27
+ return
28
+ }
29
+
30
+ drawer?.value.onConfirm()
31
+ }
32
+ </script>
@@ -0,0 +1,34 @@
1
+ <template>
2
+ <div v-if="drawerStore.drawers.length > 0" class="vts-drawer-list">
3
+ <DrawerProvider v-for="(drawer, index) of drawerStore.drawers" :key="drawer.id" :drawer>
4
+ <component
5
+ :is="drawer.component"
6
+ class="drawer-component"
7
+ v-bind="drawer.props"
8
+ :current="index === drawerStore.drawers.length - 1"
9
+ @confirm="drawer.onConfirm"
10
+ @cancel="drawer.onCancel"
11
+ />
12
+ </DrawerProvider>
13
+ </div>
14
+ </template>
15
+
16
+ <script lang="ts" setup>
17
+ import { useDrawerStore } from '@core/packages/drawer/drawer.store.ts'
18
+ import DrawerProvider from '@core/packages/drawer/DrawerProvider.vue'
19
+
20
+ const drawerStore = useDrawerStore()
21
+ </script>
22
+
23
+ <style lang="postcss" scoped>
24
+ .vts-drawer-list {
25
+ position: fixed;
26
+ inset: 0;
27
+ background-color: var(--color-opacity-primary);
28
+ z-index: 1020;
29
+
30
+ .drawer-component:not(:last-child) {
31
+ filter: brightness(0.8);
32
+ }
33
+ }
34
+ </style>
@@ -1,10 +1,11 @@
1
1
  <template>
2
- <UiLoader v-if="busy" />
2
+ <UiLoader v-if="busy" v-tooltip="busyTooltip ?? false" />
3
3
  <DisplayIcon v-else-if="icon" class="vts-icon" :class="className" :icon />
4
4
  </template>
5
5
 
6
6
  <script lang="ts" setup>
7
7
  import UiLoader from '@core/components/ui/loader/UiLoader.vue'
8
+ import { vTooltip } from '@core/directives/tooltip.directive'
8
9
  import { type IconName, icons } from '@core/icons'
9
10
  import { DisplayIcon } from '@core/packages/icon'
10
11
  import { toVariants } from '@core/utils/to-variants.util.ts'
@@ -16,6 +17,7 @@ const { size, name } = defineProps<{
16
17
  size: IconSize
17
18
  name: IconName | undefined
18
19
  busy?: boolean
20
+ busyTooltip?: string
19
21
  }>()
20
22
 
21
23
  const className = computed(() =>
@@ -0,0 +1,41 @@
1
+ <template>
2
+ <div
3
+ class="vts-content-side-panel"
4
+ :class="{
5
+ mobile: uiStore.isSmall,
6
+ 'has-panel-column': hasPanelColumn,
7
+ }"
8
+ >
9
+ <slot />
10
+ </div>
11
+ </template>
12
+
13
+ <script setup lang="ts">
14
+ import { usePanelStore } from '@core/stores/panel.store'
15
+ import { useUiStore } from '@core/stores/ui.store'
16
+ import { computed } from 'vue'
17
+
18
+ const panelStore = usePanelStore()
19
+ const uiStore = useUiStore()
20
+
21
+ const hasPanelColumn = computed(() => (panelStore.isLocked || panelStore.isExpanded) && !uiStore.isSmall)
22
+ </script>
23
+
24
+ <style scoped lang="postcss">
25
+ .vts-content-side-panel {
26
+ --side-panel-width: 40rem;
27
+
28
+ &:not(.mobile) {
29
+ display: grid;
30
+ grid-template-columns: minmax(0, 1fr) 0;
31
+ align-items: start;
32
+ overflow-x: clip;
33
+ min-height: min-content;
34
+ transition: grid-template-columns 0.25s;
35
+
36
+ &.has-panel-column {
37
+ grid-template-columns: minmax(0, 1fr) var(--side-panel-width);
38
+ }
39
+ }
40
+ }
41
+ </style>
@@ -59,6 +59,7 @@ const ui = useUiStore()
59
59
 
60
60
  <style lang="postcss" scoped>
61
61
  .vts-layout-sidebar {
62
+ position: relative;
62
63
  display: flex;
63
64
  flex-direction: column;
64
65
  height: 100%;
@@ -83,8 +84,10 @@ const ui = useUiStore()
83
84
  }
84
85
 
85
86
  .lock {
86
- text-align: right;
87
- padding: 0.8rem 0.8rem 0;
87
+ position: absolute;
88
+ inset-block-start: 0.8rem;
89
+ inset-inline-end: 1.2rem;
90
+ z-index: 1;
88
91
  }
89
92
 
90
93
  .content {
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <MenuList placement="bottom-end">
2
+ <MenuList placement="bottom-start">
3
3
  <template #trigger="{ open }">
4
4
  <UiButtonIcon
5
5
  v-tooltip="{
@@ -12,6 +12,7 @@
12
12
  @click="open($event)"
13
13
  />
14
14
  </template>
15
+ <slot>
15
16
  <MenuItem
16
17
  v-for="(action, index) of actions"
17
18
  :key="index"
@@ -22,7 +23,21 @@
22
23
  >
23
24
  {{ action.label }}
24
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>
25
39
  </MenuItem>
40
+ </slot>
26
41
  </MenuList>
27
42
  </template>
28
43
 
@@ -40,14 +55,33 @@ const { size = 'small', actions = [] } = defineProps<{
40
55
  actions?: ActionItem[]
41
56
  }>()
42
57
 
58
+ defineSlots<{
59
+ default(): any
60
+ }>()
61
+
43
62
  const { t } = useI18n()
44
63
 
45
- export type ActionItem = {
64
+ type BaseActionItem = {
46
65
  label: string
47
66
  hint?: string
48
67
  icon?: IconName
49
- onClick: () => any
50
68
  disabled?: boolean
51
69
  busy?: boolean
52
70
  }
71
+
72
+ export type LeafActionItem = BaseActionItem & {
73
+ onClick: () => unknown
74
+ children?: never
75
+ }
76
+
77
+ export type GroupActionItem = BaseActionItem & {
78
+ onClick?: never
79
+ children: LeafActionItem[]
80
+ }
81
+
82
+ export type ActionItem = LeafActionItem | GroupActionItem
83
+
84
+ function isGroupAction(action: ActionItem): action is GroupActionItem {
85
+ return 'children' in action
86
+ }
53
87
  </script>
@@ -26,7 +26,6 @@ const modalAccent = inject(IK_MODAL_ACCENT)
26
26
  const buttonAccent = useMapper(
27
27
  () => modalAccent?.value,
28
28
  {
29
- success: 'brand',
30
29
  info: 'brand',
31
30
  warning: 'warning',
32
31
  danger: 'danger',
@@ -12,9 +12,10 @@
12
12
  </template>
13
13
 
14
14
  <script lang="ts" setup>
15
- import VtsStateHero, { type StateHeroType } from '@core/components/state-hero/VtsStateHero.vue'
15
+ import VtsStateHero from '@core/components/state-hero/VtsStateHero.vue'
16
16
  import UiCard from '@core/components/ui/card/UiCard.vue'
17
17
  import { useUiStore } from '@core/stores/ui.store.ts'
18
+ import type { StateHeroType } from '@core/types/state-hero.type.ts'
18
19
 
19
20
  defineProps<{
20
21
  title: string
@@ -0,0 +1,80 @@
1
+ <!-- v5 -->
2
+ <template>
3
+ <UiPanel :error>
4
+ <template v-if="needsHeader" #header>
5
+ <slot v-if="slots.header" name="header" />
6
+ <div v-if="slots.actions || slots['more-actions']" class="action-buttons">
7
+ <template v-if="slots.actions">
8
+ <slot name="actions" />
9
+ </template>
10
+ <MenuList v-if="slots['more-actions']" placement="bottom-end">
11
+ <template #trigger="{ open }">
12
+ <UiButtonIcon icon="action:more-actions" accent="brand" size="medium" @click="open($event)" />
13
+ </template>
14
+ <slot name="more-actions" />
15
+ </MenuList>
16
+ </div>
17
+ <div v-if="slots['corner-actions'] || closable" class="corner-actions">
18
+ <slot v-if="slots['corner-actions']" name="corner-actions" />
19
+ <UiButtonIcon
20
+ v-if="closable"
21
+ v-tooltip="t('action:close')"
22
+ size="small"
23
+ variant="tertiary"
24
+ accent="brand"
25
+ :icon="closeIcon"
26
+ @click="emit('close')"
27
+ />
28
+ </div>
29
+ </template>
30
+ <slot />
31
+ </UiPanel>
32
+ </template>
33
+
34
+ <script setup lang="ts">
35
+ import MenuList from '@core/components/menu/MenuList.vue'
36
+ import UiButtonIcon from '@core/components/ui/button-icon/UiButtonIcon.vue'
37
+ import UiPanel from '@core/components/ui/panel/UiPanel.vue'
38
+ import { vTooltip } from '@core/directives/tooltip.directive'
39
+ import type { IconName } from '@core/icons'
40
+ import { computed } from 'vue'
41
+ import { useI18n } from 'vue-i18n'
42
+
43
+ const { closable, closeIcon = 'action:close-cancel-clear' } = defineProps<{
44
+ error?: boolean
45
+ closable?: boolean
46
+ closeIcon?: IconName
47
+ }>()
48
+
49
+ const emit = defineEmits<{
50
+ close: []
51
+ }>()
52
+
53
+ const slots = defineSlots<{
54
+ default(): any
55
+ header?(): any
56
+ actions?(): any
57
+ 'more-actions'?(): any
58
+ 'corner-actions'?(): any
59
+ }>()
60
+
61
+ const { t } = useI18n()
62
+
63
+ const needsHeader = computed(
64
+ () => slots.header || slots.actions || slots['more-actions'] || slots['corner-actions'] || closable
65
+ )
66
+ </script>
67
+
68
+ <style scoped lang="postcss">
69
+ .action-buttons {
70
+ display: flex;
71
+ align-items: center;
72
+ gap: 1.6rem;
73
+ }
74
+ .corner-actions {
75
+ display: flex;
76
+ align-items: center;
77
+ margin-inline-start: auto;
78
+ gap: 0.8rem;
79
+ }
80
+ </style>