@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.
- package/lib/components/button-group/VtsButtonGroup.vue +11 -1
- package/lib/components/card-object-title/VtsCardObjectTitle.vue +36 -0
- package/lib/components/code-snippet/VtsCodeSnippet.vue +11 -5
- package/lib/components/drawer/VtsDrawer.vue +89 -0
- package/lib/components/drawer/VtsDrawerButton.vue +22 -0
- package/lib/components/drawer/VtsDrawerCancelButton.vue +33 -0
- package/lib/components/drawer/VtsDrawerConfirmButton.vue +32 -0
- package/lib/components/drawer/VtsDrawerList.vue +34 -0
- package/lib/components/icon/VtsIcon.vue +3 -1
- package/lib/components/layout/VtsContentSidePanel.vue +41 -0
- package/lib/components/layout/VtsLayoutSidebar.vue +5 -2
- package/lib/components/menu/VtsActionsMenu.vue +37 -3
- package/lib/components/modal/VtsModalButton.vue +0 -1
- package/lib/components/operation-card/VtsOperationCard.vue +2 -1
- package/lib/components/panel/VtsPanel.vue +80 -0
- package/lib/components/panel/VtsSidePanel.vue +140 -0
- package/lib/components/query-builder/VtsQueryBuilderModal.vue +0 -1
- package/lib/components/select/VtsSelect.vue +1 -0
- package/lib/components/state-hero/VtsStateHero.vue +26 -24
- package/lib/components/table/VtsTable.vue +2 -1
- package/lib/components/table/cells/VtsActionCell.vue +7 -0
- package/lib/components/table/cells/VtsHeaderCell.vue +1 -17
- package/lib/components/table/cells/VtsLinkCell.vue +3 -1
- package/lib/components/table/cells/VtsTagCell.vue +4 -2
- package/lib/components/tree/VtsTreeItem.vue +9 -3
- package/lib/components/tree/VtsTreeList.vue +6 -3
- package/lib/components/tree/VtsTreeLoadingItem.vue +3 -3
- package/lib/components/ui/drawer/UiDrawer.vue +123 -0
- package/lib/components/ui/head-bar/UiHeadBar.vue +1 -1
- package/lib/components/ui/input/UiInput.vue +3 -1
- package/lib/components/ui/link/UiLink.vue +3 -1
- package/lib/components/ui/modal/UiModal.vue +10 -18
- package/lib/components/ui/panel/UiPanel.vue +7 -24
- package/lib/components/ui/tree-item-label/UiTreeItemLabel.vue +1 -1
- package/lib/composables/default-tab.composable.ts +4 -0
- package/lib/composables/table-state.composable.ts +1 -1
- package/lib/icons/action-icons.ts +20 -2
- package/lib/icons/custom-icons.ts +16 -0
- package/lib/icons/object-icons.ts +33 -18
- package/lib/locales/cs.json +110 -11
- package/lib/locales/da.json +1 -3
- package/lib/locales/de.json +1 -12
- package/lib/locales/en.json +76 -12
- package/lib/locales/es.json +15 -6
- package/lib/locales/fa.json +0 -1
- package/lib/locales/fr.json +76 -12
- package/lib/locales/it.json +0 -1
- package/lib/locales/ko.json +0 -2
- package/lib/locales/nb-NO.json +18 -20
- package/lib/locales/nl.json +0 -11
- package/lib/locales/pt-BR.json +0 -4
- package/lib/locales/pt.json +0 -4
- package/lib/locales/ru.json +0 -1
- package/lib/locales/sk.json +110 -11
- package/lib/locales/sv.json +2 -13
- package/lib/locales/uk.json +0 -1
- package/lib/locales/zh-Hans.json +79 -16
- package/lib/packages/drawer/DrawerProvider.vue +17 -0
- package/lib/packages/drawer/README.md +189 -0
- package/lib/packages/drawer/create-drawer-opener.ts +119 -0
- package/lib/packages/drawer/drawer.store.ts +22 -0
- package/lib/packages/drawer/types.ts +93 -0
- package/lib/packages/drawer/use-drawer.ts +53 -0
- package/lib/packages/modal/create-modal-opener.ts +12 -10
- package/lib/packages/remote-resource/define-remote-resource.ts +64 -29
- package/lib/packages/tree/types.ts +6 -0
- package/lib/packages/tree/use-tree.ts +38 -37
- package/lib/stores/panel.store.ts +56 -5
- package/lib/tables/column-definitions/action-column.ts +22 -10
- package/lib/tables/column-definitions/address-column.ts +1 -1
- package/lib/tables/column-definitions/button-column.ts +1 -1
- package/lib/tables/column-definitions/button-icon-column.ts +1 -1
- package/lib/tables/column-definitions/collapsed-list-column.ts +1 -1
- package/lib/tables/column-definitions/date-column.ts +1 -1
- package/lib/tables/column-definitions/double-link-column.ts +1 -1
- package/lib/tables/column-definitions/info-column.ts +1 -1
- package/lib/tables/column-definitions/input-column.ts +1 -5
- package/lib/tables/column-definitions/link-column.ts +1 -1
- package/lib/tables/column-definitions/link-or-text-column.ts +1 -1
- package/lib/tables/column-definitions/literal-column.ts +1 -1
- package/lib/tables/column-definitions/number-column.ts +1 -1
- package/lib/tables/column-definitions/percent-column.ts +1 -1
- package/lib/tables/column-definitions/progress-bar-column.ts +1 -1
- package/lib/tables/column-definitions/select-column.ts +1 -1
- package/lib/tables/column-definitions/status-column.ts +2 -2
- package/lib/tables/column-definitions/tag-column.ts +3 -2
- package/lib/tables/column-definitions/text-column.ts +1 -1
- package/lib/tables/column-definitions/truncated-text-column.ts +1 -1
- package/lib/tables/column-sets/backup-job-schedule-columns.ts +2 -2
- package/lib/tables/column-sets/backup-log-columns.ts +1 -1
- package/lib/tables/column-sets/new-vm-network-columns.ts +0 -2
- package/lib/tables/column-sets/new-vm-sr-columns.ts +0 -1
- package/lib/tables/column-sets/server-columns.ts +1 -1
- package/lib/tables/column-sets/snapshot-columns.ts +1 -1
- package/lib/tables/column-sets/sr-columns.ts +2 -2
- package/lib/tables/column-sets/traffic-rules-columns.ts +5 -7
- package/lib/tables/column-sets/vif-network-columns.ts +24 -0
- package/lib/tables/column-sets/vm-columns.ts +2 -3
- package/lib/tables/helpers/render-head-cell.ts +2 -4
- package/lib/tables/types.ts +0 -2
- package/lib/types/connection.ts +14 -0
- package/lib/types/state-hero.type.ts +19 -0
- package/lib/types/storage-repository.type.ts +14 -0
- package/lib/types/vue-virtual-scroller.d.ts +8 -2
- package/lib/utils/injection-keys.util.ts +1 -1
- package/lib/utils/sr.utils.ts +34 -0
- 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
|
-
<
|
|
4
|
-
|
|
5
|
-
|
|
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
|
-
|
|
87
|
-
|
|
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-
|
|
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
|
-
|
|
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>
|
|
@@ -12,9 +12,10 @@
|
|
|
12
12
|
</template>
|
|
13
13
|
|
|
14
14
|
<script lang="ts" setup>
|
|
15
|
-
import VtsStateHero
|
|
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>
|