@xen-orchestra/web-core 0.0.2 → 0.0.4

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 (61) hide show
  1. package/env.d.ts +1 -0
  2. package/lib/assets/css/_typography.pcss +1 -0
  3. package/lib/assets/css/typography/_utils.pcss +6 -0
  4. package/lib/assets/no-result.svg +81 -0
  5. package/lib/assets/under-construction.svg +195 -0
  6. package/lib/components/CardNumbers.vue +94 -0
  7. package/lib/components/DonutChart.vue +92 -0
  8. package/lib/components/LegendTitle.vue +31 -0
  9. package/lib/components/StatusPill.vue +2 -2
  10. package/lib/components/UiCard.vue +29 -0
  11. package/lib/components/UiLegend.vue +68 -0
  12. package/lib/components/UiTag.vue +3 -7
  13. package/lib/components/backdrop/Backdrop.vue +11 -0
  14. package/lib/components/card/CardSubtitle.vue +24 -0
  15. package/lib/components/card/CardTitle.vue +52 -0
  16. package/lib/components/cell-object/CellObject.vue +54 -0
  17. package/lib/components/cell-text/CellText.vue +40 -0
  18. package/lib/components/chip/UiChip.vue +5 -6
  19. package/lib/components/console/RemoteConsole.vue +2 -2
  20. package/lib/components/divider/Divider.vue +25 -0
  21. package/lib/components/dropdown/DropdownItem.vue +1 -4
  22. package/lib/components/dropdown/DropdownTitle.vue +7 -3
  23. package/lib/components/head-bar/HeadBar.vue +78 -0
  24. package/lib/components/icon/VmIcon.vue +1 -1
  25. package/lib/components/input/UiInput.vue +133 -0
  26. package/lib/components/menu/MenuItem.vue +1 -1
  27. package/lib/components/menu/MenuList.vue +5 -5
  28. package/lib/components/object-link/ObjectLink.vue +87 -0
  29. package/lib/components/search-bar/SearchBar.vue +60 -0
  30. package/lib/components/stacked-bar/StackedBar.vue +49 -0
  31. package/lib/components/stacked-bar/StackedBarSegment.vue +67 -0
  32. package/lib/components/state-hero/ComingSoonHero.vue +9 -0
  33. package/lib/components/state-hero/LoadingHero.vue +9 -0
  34. package/lib/components/state-hero/ObjectNotFoundHero.vue +13 -0
  35. package/lib/components/state-hero/StateHero.vue +53 -0
  36. package/lib/components/tab/TabList.vue +1 -0
  37. package/lib/components/table/ColumnTitle.vue +150 -0
  38. package/lib/components/table/UiTable.vue +64 -0
  39. package/lib/components/task/QuickTaskButton.vue +62 -0
  40. package/lib/components/task/QuickTaskItem.vue +91 -0
  41. package/lib/components/task/QuickTaskList.vue +48 -0
  42. package/lib/components/task/QuickTaskPanel.vue +65 -0
  43. package/lib/components/task/QuickTaskTabBar.vue +46 -0
  44. package/lib/components/tooltip/TooltipList.vue +0 -2
  45. package/lib/components/tree/TreeItem.vue +8 -8
  46. package/lib/components/tree/TreeItemLabel.vue +32 -16
  47. package/lib/composables/tab-list.composable.ts +33 -0
  48. package/lib/composables/tree/branch.ts +5 -5
  49. package/lib/composables/tree/types.ts +1 -1
  50. package/lib/i18n.ts +86 -1
  51. package/lib/layouts/CoreLayout.vue +6 -106
  52. package/lib/locales/de.json +37 -4
  53. package/lib/locales/en.json +66 -13
  54. package/lib/locales/fa.json +46 -0
  55. package/lib/locales/fr.json +66 -13
  56. package/lib/types/tab.type.ts +17 -0
  57. package/lib/types/task.type.ts +13 -0
  58. package/lib/types/utility.type.ts +2 -0
  59. package/lib/utils/if-else.utils.ts +1 -1
  60. package/lib/utils/open-url.utils.ts +3 -0
  61. package/package.json +16 -8
@@ -0,0 +1,65 @@
1
+ <template>
2
+ <div :class="{ mobile: isMobile }" class="vts-quick-tasks">
3
+ <QuickTaskTabBar
4
+ v-model="currentTab"
5
+ :failure-count="failureTasks.length"
6
+ :loading
7
+ :pending-count="pendingTasks.length"
8
+ :success-count="successTasks.length"
9
+ />
10
+ <QuickTaskList :loading :tasks="currentTasks" />
11
+ </div>
12
+ </template>
13
+
14
+ <script lang="ts" setup>
15
+ import QuickTaskList from '@core/components/task/QuickTaskList.vue'
16
+ import QuickTaskTabBar from '@core/components/task/QuickTaskTabBar.vue'
17
+ import { useUiStore } from '@core/stores/ui.store'
18
+ import type { Task, TaskTab } from '@core/types/task.type'
19
+ import { computed, ref } from 'vue'
20
+
21
+ const props = defineProps<{
22
+ tasks: Task[]
23
+ loading?: boolean
24
+ }>()
25
+
26
+ const { isMobile } = useUiStore()
27
+
28
+ const currentTab = ref<TaskTab>('pending')
29
+
30
+ const pendingTasks = computed(() => props.tasks.filter(task => task.status === 'pending'))
31
+ const successTasks = computed(() => props.tasks.filter(task => task.status === 'success'))
32
+ const failureTasks = computed(() => props.tasks.filter(task => task.status === 'failure'))
33
+
34
+ const currentTasks = computed(() => {
35
+ switch (currentTab.value) {
36
+ case 'pending':
37
+ return pendingTasks.value
38
+ case 'success':
39
+ return successTasks.value
40
+ case 'failure':
41
+ return failureTasks.value
42
+ case 'all':
43
+ return props.tasks
44
+ default:
45
+ return []
46
+ }
47
+ })
48
+ </script>
49
+
50
+ <style lang="postcss" scoped>
51
+ .vts-quick-tasks {
52
+ width: fit-content;
53
+ min-width: 65rem;
54
+ border-radius: 0.8rem;
55
+ overflow: hidden;
56
+ border: 0.1rem solid var(--color-grey-500);
57
+ z-index: 1001;
58
+
59
+ &.mobile {
60
+ width: 100%;
61
+ min-width: 0;
62
+ border-radius: 0;
63
+ }
64
+ }
65
+ </style>
@@ -0,0 +1,46 @@
1
+ <template>
2
+ <TabList :disabled="loading">
3
+ <TabItem v-bind="tabs.pending.bindings">
4
+ {{ $t('tasks.quick-view.in-progress') }}
5
+ <UiCounter v-if="pendingCount !== undefined" :value="pendingCount" color="info" />
6
+ </TabItem>
7
+ <TabItem v-bind="tabs.success.bindings">
8
+ {{ $t('tasks.quick-view.done') }}
9
+ <UiCounter v-if="successCount !== undefined" :value="successCount" color="success" />
10
+ </TabItem>
11
+ <TabItem v-bind="tabs.failure.bindings">
12
+ {{ $t('tasks.quick-view.failed') }}
13
+ <UiCounter v-if="failureCount !== undefined" :value="failureCount" color="danger" />
14
+ </TabItem>
15
+ <Divider type="tab" />
16
+ <TabItem v-bind="tabs.all.bindings">
17
+ {{ $t('tasks.quick-view.all') }}
18
+ </TabItem>
19
+ <!--
20
+ TODO
21
+ <UiButton :right-icon="faAngleRight" class="see-all" level="tertiary" size="extra-small">
22
+ {{ $t('see-all') }}
23
+ </UiButton>
24
+ -->
25
+ </TabList>
26
+ </template>
27
+
28
+ <script lang="ts" setup>
29
+ import Divider from '@core/components/divider/Divider.vue'
30
+ import TabItem from '@core/components/tab/TabItem.vue'
31
+ import TabList from '@core/components/tab/TabList.vue'
32
+ import UiCounter from '@core/components/UiCounter.vue'
33
+ import { useTabList } from '@core/composables/tab-list.composable'
34
+ import type { TaskTab } from '@core/types/task.type'
35
+
36
+ defineProps<{
37
+ loading?: boolean
38
+ pendingCount?: number
39
+ successCount?: number
40
+ failureCount?: number
41
+ }>()
42
+
43
+ const currentTab = defineModel<TaskTab>({ required: true })
44
+
45
+ const { tabs } = useTabList<TaskTab>(['pending', 'success', 'failure', 'all'], currentTab)
46
+ </script>
@@ -11,5 +11,3 @@ import { storeToRefs } from 'pinia'
11
11
  const tooltipStore = useTooltipStore()
12
12
  const { tooltips } = storeToRefs(tooltipStore)
13
13
  </script>
14
-
15
- <style scoped></style>
@@ -2,22 +2,23 @@
2
2
  <template>
3
3
  <li class="tree-item">
4
4
  <slot />
5
- <slot v-if="isExpanded" name="sublist" />
5
+ <slot v-if="expanded" name="sublist" />
6
6
  </li>
7
7
  </template>
8
8
 
9
9
  <script lang="ts" setup>
10
- import { IK_TREE_ITEM_EXPANDED, IK_TREE_ITEM_HAS_CHILDREN, IK_TREE_ITEM_TOGGLE } from '@core/utils/injection-keys.util'
11
- import { useToggle } from '@vueuse/core'
12
- import { onBeforeMount, onBeforeUpdate, provide, ref, useSlots } from 'vue'
10
+ import { IK_TREE_ITEM_EXPANDED, IK_TREE_ITEM_HAS_CHILDREN } from '@core/utils/injection-keys.util'
11
+ import { onBeforeMount, onBeforeUpdate, provide, ref, toRef, useSlots } from 'vue'
12
+
13
+ const props = defineProps<{
14
+ expanded?: boolean
15
+ }>()
13
16
 
14
17
  defineSlots<{
15
18
  default: () => void
16
19
  sublist: () => void
17
20
  }>()
18
21
 
19
- const [isExpanded, toggle] = useToggle(true)
20
-
21
22
  const hasChildren = ref(false)
22
23
 
23
24
  const updateHasChildren = () => {
@@ -29,6 +30,5 @@ onBeforeMount(() => updateHasChildren())
29
30
  onBeforeUpdate(() => updateHasChildren())
30
31
 
31
32
  provide(IK_TREE_ITEM_HAS_CHILDREN, hasChildren)
32
- provide(IK_TREE_ITEM_TOGGLE, toggle)
33
- provide(IK_TREE_ITEM_EXPANDED, isExpanded)
33
+ provide(IK_TREE_ITEM_EXPANDED, toRef(props, 'expanded'))
34
34
  </script>
@@ -1,8 +1,8 @@
1
1
  <!-- v1.1 -->
2
2
  <template>
3
- <RouterLink v-slot="{ isExactActive, href, navigate }" :to="route" custom>
3
+ <RouterLink v-slot="{ isExactActive, isActive, href, navigate }" :to="route" custom>
4
4
  <div
5
- :class="isExactActive ? 'exact-active' : active ? 'active' : undefined"
5
+ :class="isExactActive ? 'exact-active' : active || isActive ? 'active' : undefined"
6
6
  class="tree-item-label"
7
7
  v-bind="$attrs"
8
8
  >
@@ -17,16 +17,17 @@
17
17
  <ButtonIcon
18
18
  v-if="hasToggle"
19
19
  v-tooltip="isExpanded ? $t('core.close') : $t('core.open')"
20
+ class="toggle"
20
21
  :icon="isExpanded ? faAngleDown : faAngleRight"
21
22
  size="small"
22
- @click="toggle()"
23
+ @click="emit('toggle')"
23
24
  />
24
- <TreeLine v-else-if="!noIndent" />
25
+ <div v-else class="h-line" />
25
26
  <a v-tooltip="{ selector: '.text' }" :href class="link typo p2-medium" @click="navigate">
26
27
  <slot name="icon">
27
28
  <UiIcon :icon class="icon" />
28
29
  </slot>
29
- <div class="text">
30
+ <div class="text text-ellipsis">
30
31
  <slot />
31
32
  </div>
32
33
  </a>
@@ -40,27 +41,28 @@ import ButtonIcon from '@core/components/button/ButtonIcon.vue'
40
41
  import UiIcon from '@core/components/icon/UiIcon.vue'
41
42
  import TreeLine from '@core/components/tree/TreeLine.vue'
42
43
  import { vTooltip } from '@core/directives/tooltip.directive'
43
- import {
44
- IK_TREE_ITEM_EXPANDED,
45
- IK_TREE_ITEM_HAS_CHILDREN,
46
- IK_TREE_ITEM_TOGGLE,
47
- IK_TREE_LIST_DEPTH,
48
- } from '@core/utils/injection-keys.util'
44
+ import { IK_TREE_ITEM_EXPANDED, IK_TREE_ITEM_HAS_CHILDREN, IK_TREE_LIST_DEPTH } from '@core/utils/injection-keys.util'
49
45
  import type { IconDefinition } from '@fortawesome/fontawesome-common-types'
50
46
  import { faAngleDown, faAngleRight } from '@fortawesome/free-solid-svg-icons'
51
47
  import { inject, ref } from 'vue'
52
48
  import type { RouteLocationRaw } from 'vue-router'
53
49
 
50
+ defineOptions({
51
+ inheritAttrs: false,
52
+ })
53
+
54
54
  defineProps<{
55
55
  icon?: IconDefinition
56
56
  route: RouteLocationRaw
57
57
  active?: boolean
58
- noIndent?: boolean
58
+ }>()
59
+
60
+ const emit = defineEmits<{
61
+ toggle: []
59
62
  }>()
60
63
 
61
64
  const hasToggle = inject(IK_TREE_ITEM_HAS_CHILDREN, ref(false))
62
65
 
63
- const toggle = inject(IK_TREE_ITEM_TOGGLE, () => undefined)
64
66
  const isExpanded = inject(IK_TREE_ITEM_EXPANDED, ref(true))
65
67
 
66
68
  const depth = inject(IK_TREE_LIST_DEPTH, 0)
@@ -116,13 +118,27 @@ const depth = inject(IK_TREE_LIST_DEPTH, 0)
116
118
  }
117
119
 
118
120
  .text {
119
- overflow: hidden;
120
- white-space: nowrap;
121
- text-overflow: ellipsis;
122
121
  padding-inline-end: 0.4rem;
123
122
  }
124
123
 
125
124
  .icon {
126
125
  font-size: 1.6rem;
127
126
  }
127
+
128
+ .h-line {
129
+ width: 2rem;
130
+ border-bottom: 0.1rem solid var(--color-purple-base);
131
+ margin-left: -0.4rem;
132
+ }
133
+
134
+ /*
135
+ * Increase the size of the clickable area,
136
+ * without changing the padding of the ButtonIcon component
137
+ */
138
+ .toggle::after {
139
+ content: '';
140
+ position: absolute;
141
+ inset: 0;
142
+ transform: scale(1.5, 2);
143
+ }
128
144
  </style>
@@ -0,0 +1,33 @@
1
+ import type { Tab, TabList } from '@core/types/tab.type'
2
+ import { computed, type MaybeRefOrGetter, type Ref, toRef } from 'vue'
3
+
4
+ export function useTabList<TName extends string>(names: TName[], initialTab?: MaybeRefOrGetter<TName>) {
5
+ const currentTab = toRef(initialTab) as Ref<TName | undefined>
6
+
7
+ const activate = (name: TName | undefined) => {
8
+ currentTab.value = name
9
+ }
10
+
11
+ const isActive = (name: TName) => currentTab.value === name
12
+
13
+ const tabs = computed(() => {
14
+ return Object.fromEntries(
15
+ names.map(name => [
16
+ name,
17
+ {
18
+ isActive: isActive(name),
19
+ activate: () => activate(name),
20
+ bindings: {
21
+ onClick: (event: MouseEvent) => {
22
+ event.preventDefault()
23
+ activate(name)
24
+ },
25
+ active: isActive(name),
26
+ },
27
+ },
28
+ ])
29
+ ) as Record<TName, Tab>
30
+ })
31
+
32
+ return { currentTab, activate, isActive, tabs } as TabList<TName>
33
+ }
@@ -40,7 +40,11 @@ export class Branch<
40
40
  }
41
41
 
42
42
  get failsFilterDownwards(): boolean {
43
- return this.passesFilter !== true && this.rawChildren.every(child => child.failsFilterDownwards)
43
+ if (this.passesFilter !== undefined) {
44
+ return !this.passesFilter
45
+ }
46
+
47
+ return this.rawChildren.length > 0 && this.rawChildren.every(child => child.failsFilterDownwards)
44
48
  }
45
49
 
46
50
  get isExcluded() {
@@ -48,10 +52,6 @@ export class Branch<
48
52
  return true
49
53
  }
50
54
 
51
- if (!this.hasChildren) {
52
- return true
53
- }
54
-
55
55
  if (this.passesFilterUpwards || this.passesFilterDownwards) {
56
56
  return false
57
57
  }
@@ -1,9 +1,9 @@
1
- import { useTree } from '@core/composables/tree.composable'
2
1
  import type { Branch } from '@core/composables/tree/branch'
3
2
  import type { BranchDefinition } from '@core/composables/tree/branch-definition'
4
3
  import type { Leaf } from '@core/composables/tree/leaf'
5
4
  import type { LeafDefinition } from '@core/composables/tree/leaf-definition'
6
5
  import type { TreeNodeBase } from '@core/composables/tree/tree-node-base'
6
+ import { useTree } from '@core/composables/tree.composable'
7
7
 
8
8
  export type TreeNodeId = string | number
9
9
 
package/lib/i18n.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { createI18n } from 'vue-i18n'
2
1
  import messages from '@intlify/unplugin-vue-i18n/messages'
2
+ import { createI18n } from 'vue-i18n'
3
3
 
4
4
  interface Locales {
5
5
  [key: string]: {
@@ -21,12 +21,25 @@ export const locales: Locales = {
21
21
  code: 'de',
22
22
  name: 'Deutsch',
23
23
  },
24
+ fa: {
25
+ code: 'fa',
26
+ name: 'Persian',
27
+ },
24
28
  }
25
29
 
26
30
  export default createI18n({
27
31
  locale: localStorage.getItem('lang') ?? 'en',
28
32
  fallbackLocale: 'en',
29
33
  messages,
34
+ missing: (locale, key, vm) => {
35
+ if (!import.meta.env.DEV) {
36
+ return key
37
+ }
38
+
39
+ console.warn(`i18n key not found: ${key}`, `Used in ${vm?.type.__name ?? 'unknown component'}`)
40
+
41
+ return `🌎❗⟨${key}⟩`
42
+ },
30
43
  datetimeFormats: {
31
44
  en: {
32
45
  date_short: {
@@ -154,5 +167,77 @@ export default createI18n({
154
167
  minute: '2-digit',
155
168
  },
156
169
  },
170
+ fa: {
171
+ date_short: {
172
+ year: 'numeric',
173
+ month: 'numeric',
174
+ day: 'numeric',
175
+ },
176
+ date_medium: {
177
+ year: 'numeric',
178
+ month: 'short',
179
+ day: 'numeric',
180
+ },
181
+ date_long: {
182
+ year: 'numeric',
183
+ month: 'long',
184
+ day: 'numeric',
185
+ },
186
+ datetime_short: {
187
+ year: 'numeric',
188
+ month: 'numeric',
189
+ day: 'numeric',
190
+ hour: '2-digit',
191
+ minute: '2-digit',
192
+ },
193
+ datetime_medium: {
194
+ year: 'numeric',
195
+ month: 'short',
196
+ day: 'numeric',
197
+ hour: '2-digit',
198
+ minute: '2-digit',
199
+ },
200
+ datetime_long: {
201
+ year: 'numeric',
202
+ month: 'long',
203
+ day: 'numeric',
204
+ hour: '2-digit',
205
+ minute: '2-digit',
206
+ },
207
+ time: {
208
+ hour: '2-digit',
209
+ minute: '2-digit',
210
+ },
211
+ },
212
+ },
213
+ numberFormats: {
214
+ en: {
215
+ percent: {
216
+ style: 'percent',
217
+ minimumFractionDigits: 0,
218
+ maximumFractionDigits: 2,
219
+ },
220
+ },
221
+ fr: {
222
+ percent: {
223
+ style: 'percent',
224
+ minimumFractionDigits: 0,
225
+ maximumFractionDigits: 2,
226
+ },
227
+ },
228
+ de: {
229
+ percent: {
230
+ style: 'percent',
231
+ minimumFractionDigits: 0,
232
+ maximumFractionDigits: 2,
233
+ },
234
+ },
235
+ fa: {
236
+ percent: {
237
+ style: 'percent',
238
+ minimumFractionDigits: 0,
239
+ maximumFractionDigits: 2,
240
+ },
241
+ },
157
242
  },
158
243
  })
@@ -14,11 +14,7 @@
14
14
  <slot name="app-header" />
15
15
  </header>
16
16
  <div class="container">
17
- <div
18
- v-if="sidebarStore.isExpanded && !sidebarStore.isLocked"
19
- class="sidebar-overlay"
20
- @click="sidebarStore.toggleExpand(false)"
21
- />
17
+ <Backdrop v-if="sidebarStore.isExpanded && !sidebarStore.isLocked" @click="sidebarStore.toggleExpand(false)" />
22
18
  <LayoutSidebar class="sidebar">
23
19
  <template #header>
24
20
  <slot name="sidebar-header" />
@@ -30,76 +26,25 @@
30
26
  <slot name="sidebar-footer" />
31
27
  </template>
32
28
  </LayoutSidebar>
33
- <div class="main-container">
34
- <header>
35
- <slot name="content-header" />
36
- </header>
37
- <main class="main">
38
- <div class="content">
39
- <slot name="content" />
40
- </div>
41
- <div v-if="isPanelVisible" :class="{ mobile: uiStore.isMobile }" class="panel">
42
- <header v-if="$slots['panel-header'] || uiStore.isMobile" class="panel-header">
43
- <UiButtonIcon
44
- v-if="uiStore.isMobile"
45
- :icon="faAngleLeft"
46
- class="panel-close-icon"
47
- @click="panelStore.close()"
48
- />
49
- <slot name="panel-header" />
50
- </header>
51
- <div v-if="$slots['panel-content']" class="panel-content">
52
- <slot name="panel-content" />
53
- </div>
54
- </div>
55
- </main>
56
- </div>
29
+ <main class="main-container">
30
+ <slot name="content" />
31
+ </main>
57
32
  </div>
58
33
  </div>
59
34
  </template>
60
35
 
61
36
  <script lang="ts" setup>
37
+ import Backdrop from '@core/components/backdrop/Backdrop.vue'
62
38
  import UiButtonIcon from '@core/components/button/ButtonIcon.vue'
63
39
  import LayoutSidebar from '@core/components/layout/LayoutSidebar.vue'
64
40
  import { vTooltip } from '@core/directives/tooltip.directive'
65
- import { usePanelStore } from '@core/stores/panel.store'
66
41
  import { useSidebarStore } from '@core/stores/sidebar.store'
67
- import { useUiStore } from '@core/stores/ui.store'
68
- import { faAngleDoubleLeft, faAngleLeft, faBars } from '@fortawesome/free-solid-svg-icons'
69
- import { computed } from 'vue'
42
+ import { faAngleDoubleLeft, faBars } from '@fortawesome/free-solid-svg-icons'
70
43
 
71
44
  const sidebarStore = useSidebarStore()
72
- const panelStore = usePanelStore()
73
- const uiStore = useUiStore()
74
-
75
- const slots = defineSlots<{
76
- 'app-logo'(): any
77
- 'app-header'(): any
78
- 'sidebar-header'(): any
79
- 'sidebar-content'(): any
80
- 'sidebar-footer'(): any
81
- 'content-header'(): any
82
- content(): any
83
- 'panel-header'(): any
84
- 'panel-content'(): any
85
- }>()
86
-
87
- const isPanelVisible = computed(() => {
88
- if (!slots['panel-header'] && !slots['panel-content']) {
89
- return false
90
- }
91
-
92
- return panelStore.isExpanded
93
- })
94
45
  </script>
95
46
 
96
47
  <style lang="postcss" scoped>
97
- .sidebar-overlay {
98
- position: fixed;
99
- inset: 0;
100
- z-index: 1000;
101
- }
102
-
103
48
  .core-layout {
104
49
  display: flex;
105
50
  height: 100dvh;
@@ -132,51 +77,6 @@ const isPanelVisible = computed(() => {
132
77
  overflow: auto;
133
78
  display: flex;
134
79
  flex-direction: column;
135
- }
136
-
137
- .main {
138
- background-color: var(--background-color-secondary);
139
- display: flex;
140
- flex: 1;
141
- }
142
-
143
- .content {
144
- padding: 0.8rem;
145
- flex: 1;
146
- border-right: 0.1rem solid var(--color-grey-500);
147
- }
148
-
149
- .panel {
150
- display: flex;
151
- flex-direction: column;
152
- width: 40rem;
153
80
  background-color: var(--background-color-secondary);
154
-
155
- &.mobile {
156
- width: 100%;
157
- position: fixed;
158
- inset: 0 0 0 auto;
159
- z-index: 1000;
160
- }
161
- }
162
-
163
- .panel-header {
164
- display: flex;
165
- align-items: center;
166
- padding: 0.4rem 1.6rem;
167
- background-color: var(--background-color-primary);
168
- border-bottom: 0.1rem solid var(--color-grey-500);
169
- min-height: 4.8rem;
170
- }
171
-
172
- .panel-close-icon {
173
- margin-right: auto;
174
- }
175
-
176
- .panel-content {
177
- flex: 1;
178
- padding: 0.8rem;
179
- overflow: auto;
180
- min-height: 0;
181
81
  }
182
82
  </style>
@@ -1,6 +1,39 @@
1
1
  {
2
- "core": {
3
- "close": "Schließen",
4
- "log-out": "Abmelden"
5
- }
2
+ "alarms": "Alarme",
3
+
4
+ "bytes.ki": "KiB",
5
+ "bytes.mi": "MiB",
6
+ "bytes.gi": "GiB",
7
+
8
+ "coming-soon": "Bald verfügbar!",
9
+ "console": "Konsole",
10
+
11
+ "core.close": "Schließen",
12
+
13
+ "core.select.all": "Alles auswählen",
14
+ "core.select.none": "Alle abwählen",
15
+
16
+ "dark-mode.enable": "Aktivieren Sie den Dunkelmodus",
17
+ "dark-mode.disable": "Deaktivieren Sie den Dunkelmodus",
18
+ "dark-mode.auto": "Automatischer Dunkelmodus",
19
+
20
+ "dashboard": "Dashboard",
21
+ "documentation-name": "{name} Dokumentation",
22
+ "hosts": "Hosts",
23
+ "loading-in-progress": "Ladevorgang läuft…",
24
+ "log-out": "Abmelden",
25
+ "master": "Primärer Host",
26
+ "n-vms": "1 VM | {n} VMs",
27
+ "network": "Netzwerk",
28
+ "object-not-found": "Objekt {id} wurde nicht gefunden…",
29
+ "power-on-for-console": "Konsole ist nach Start der VM verfügbar",
30
+ "stats": "Statistiken",
31
+ "storage": "Speicher",
32
+ "support-name": "{name} pro support",
33
+ "system": "System",
34
+
35
+ "tasks": "Aufgaben",
36
+ "tasks.quick-view": "Schnellansicht der Aufgaben",
37
+
38
+ "vms": "VMs"
6
39
  }