@xen-orchestra/web-core 0.0.3 → 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 (39) hide show
  1. package/lib/assets/css/_typography.pcss +1 -0
  2. package/lib/assets/css/typography/_utils.pcss +6 -0
  3. package/lib/components/UiTag.vue +3 -7
  4. package/lib/components/backdrop/Backdrop.vue +11 -0
  5. package/lib/components/cell-object/CellObject.vue +54 -0
  6. package/lib/components/cell-text/CellText.vue +40 -0
  7. package/lib/components/chip/UiChip.vue +3 -4
  8. package/lib/components/console/RemoteConsole.vue +1 -1
  9. package/lib/components/divider/Divider.vue +25 -0
  10. package/lib/components/dropdown/DropdownItem.vue +1 -4
  11. package/lib/components/head-bar/HeadBar.vue +78 -0
  12. package/lib/components/input/UiInput.vue +133 -0
  13. package/lib/components/object-link/ObjectLink.vue +87 -0
  14. package/lib/components/search-bar/SearchBar.vue +60 -0
  15. package/lib/components/stacked-bar/StackedBarSegment.vue +2 -8
  16. package/lib/components/tab/TabList.vue +1 -0
  17. package/lib/components/table/UiTable.vue +6 -0
  18. package/lib/components/task/QuickTaskButton.vue +62 -0
  19. package/lib/components/task/QuickTaskItem.vue +91 -0
  20. package/lib/components/task/QuickTaskList.vue +48 -0
  21. package/lib/components/task/QuickTaskPanel.vue +65 -0
  22. package/lib/components/task/QuickTaskTabBar.vue +46 -0
  23. package/lib/components/tree/TreeItem.vue +8 -8
  24. package/lib/components/tree/TreeItemLabel.vue +28 -16
  25. package/lib/composables/tab-list.composable.ts +33 -0
  26. package/lib/composables/tree/branch.ts +5 -5
  27. package/lib/i18n.ts +53 -0
  28. package/lib/layouts/CoreLayout.vue +2 -11
  29. package/lib/locales/de.json +7 -1
  30. package/lib/locales/en.json +22 -1
  31. package/lib/locales/fa.json +46 -0
  32. package/lib/locales/fr.json +22 -1
  33. package/lib/types/tab.type.ts +17 -0
  34. package/lib/types/task.type.ts +13 -0
  35. package/lib/types/utility.type.ts +1 -1
  36. package/lib/utils/if-else.utils.ts +3 -3
  37. package/lib/utils/open-url.utils.ts +3 -0
  38. package/package.json +3 -3
  39. package/lib/components/UiSeparator.vue +0 -11
@@ -4,3 +4,4 @@
4
4
  @import 'typography/_line-height.pcss';
5
5
  @import 'typography/_letter-spacing.pcss';
6
6
  @import 'typography/_legacy.pcss';
7
+ @import 'typography/_utils.pcss';
@@ -0,0 +1,6 @@
1
+ .text-ellipsis {
2
+ overflow: hidden;
3
+ white-space: nowrap;
4
+ text-overflow: ellipsis;
5
+ min-width: 0;
6
+ }
@@ -4,7 +4,7 @@
4
4
  <slot name="icon">
5
5
  <UiIcon :icon fixed-width />
6
6
  </slot>
7
- <span class="content"><slot /></span>
7
+ <span class="text-ellipsis"><slot /></span>
8
8
  </span>
9
9
  </template>
10
10
 
@@ -78,7 +78,7 @@ withDefaults(
78
78
 
79
79
  /* IMPLEMENTATION */
80
80
  .ui-tag {
81
- display: inline-flex;
81
+ display: flex;
82
82
  justify-content: center;
83
83
  align-items: center;
84
84
  gap: 0.8rem;
@@ -88,10 +88,6 @@ withDefaults(
88
88
  padding: 0.2rem 0.8rem;
89
89
  border-radius: 0.4rem;
90
90
  vertical-align: middle;
91
-
92
- .content {
93
- overflow: hidden;
94
- text-overflow: ellipsis;
95
- }
91
+ min-width: 0;
96
92
  }
97
93
  </style>
@@ -0,0 +1,11 @@
1
+ <template>
2
+ <div class="vts-backdrop" />
3
+ </template>
4
+
5
+ <style lang="postcss" scoped>
6
+ .vts-backdrop {
7
+ position: fixed;
8
+ inset: 0;
9
+ z-index: 1000;
10
+ }
11
+ </style>
@@ -0,0 +1,54 @@
1
+ <!-- v1.0 -->
2
+ <template>
3
+ <td class="cell-object">
4
+ <div class="data">
5
+ <slot />
6
+ <template v-if="id !== undefined">
7
+ <span v-tooltip class="id typo p4-regular-italic text-ellipsis">
8
+ {{ id }}
9
+ </span>
10
+ <UiButton
11
+ v-if="isSupported && copiableId"
12
+ :left-icon="faCopy"
13
+ level="secondary"
14
+ size="extra-small"
15
+ :color="copied ? 'success' : 'info'"
16
+ @click="copy(id)"
17
+ >
18
+ {{ copied ? $t('core.copied') : $t('core.copy-id') }}
19
+ </UiButton>
20
+ </template>
21
+ </div>
22
+ </td>
23
+ </template>
24
+
25
+ <script lang="ts" setup>
26
+ import UiButton from '@core/components/button/UiButton.vue'
27
+ import { vTooltip } from '@core/directives/tooltip.directive'
28
+ import { faCopy } from '@fortawesome/free-solid-svg-icons'
29
+ import { useClipboard } from '@vueuse/core'
30
+
31
+ defineProps<{
32
+ id?: string
33
+ copiableId?: boolean
34
+ }>()
35
+
36
+ const { isSupported, copy, copied } = useClipboard()
37
+ </script>
38
+
39
+ <style lang="postcss" scoped>
40
+ .cell-object {
41
+ padding: 0.8rem;
42
+ border: 0.1rem solid var(--color-grey-500);
43
+ }
44
+
45
+ .data {
46
+ display: flex;
47
+ gap: 1.6rem;
48
+ align-items: center;
49
+ }
50
+
51
+ .id {
52
+ color: var(--color-grey-300);
53
+ }
54
+ </style>
@@ -0,0 +1,40 @@
1
+ <!-- v1.0 -->
2
+ <template>
3
+ <td class="cell-text">
4
+ <div class="data typo p2-regular">
5
+ <span v-tooltip class="text-ellipsis">
6
+ <slot />
7
+ </span>
8
+ <span v-if="slots.secondary" class="info typo p4-regular">
9
+ <slot name="secondary" />
10
+ </span>
11
+ </div>
12
+ </td>
13
+ </template>
14
+
15
+ <script lang="ts" setup>
16
+ import { vTooltip } from '@core/directives/tooltip.directive'
17
+
18
+ const slots = defineSlots<{
19
+ default: () => any
20
+ secondary?: () => any
21
+ }>()
22
+ </script>
23
+
24
+ <style lang="postcss" scoped>
25
+ .cell-text {
26
+ padding: 0.8rem;
27
+ border: 0.1rem solid var(--color-grey-500);
28
+ color: var(--color-grey-000);
29
+ }
30
+
31
+ .data {
32
+ display: flex;
33
+ gap: 1.6rem;
34
+ align-items: center;
35
+ }
36
+
37
+ .info {
38
+ color: var(--color-grey-300);
39
+ }
40
+ </style>
@@ -2,7 +2,7 @@
2
2
  <template>
3
3
  <span :class="[color, { disabled }]" class="ui-chip typo p3-regular" @click="emit('edit')">
4
4
  <ChipIcon :color :disabled :icon />
5
- <span class="content">
5
+ <span class="content text-ellipsis">
6
6
  <slot />
7
7
  </span>
8
8
  <ChipRemoveIcon v-if="!disabled" :color @click.stop="emit('remove')" />
@@ -113,7 +113,7 @@ const emit = defineEmits<{
113
113
 
114
114
  /* IMPLEMENTATION */
115
115
  .ui-chip {
116
- display: inline-flex;
116
+ display: flex;
117
117
  align-items: center;
118
118
  gap: 0.8rem;
119
119
  padding: 0.4rem 0.8rem;
@@ -124,6 +124,7 @@ const emit = defineEmits<{
124
124
  min-height: 2.4rem;
125
125
  vertical-align: middle;
126
126
  white-space: nowrap;
127
+ min-width: 0;
127
128
 
128
129
  &.disabled {
129
130
  pointer-events: none;
@@ -131,8 +132,6 @@ const emit = defineEmits<{
131
132
 
132
133
  .content {
133
134
  line-height: 1.6rem;
134
- overflow: hidden;
135
- text-overflow: ellipsis;
136
135
  }
137
136
  }
138
137
  </style>
@@ -6,7 +6,7 @@
6
6
 
7
7
  <script lang="ts" setup>
8
8
  import { useUiStore } from '@core/stores/ui.store'
9
- import VncClient from '@novnc/novnc/core/rfb'
9
+ import VncClient from '@novnc/novnc/lib/rfb'
10
10
  import { promiseTimeout } from '@vueuse/shared'
11
11
  import { fibonacci } from 'iterable-backoff'
12
12
  import { onBeforeUnmount, ref, watchEffect } from 'vue'
@@ -0,0 +1,25 @@
1
+ <template>
2
+ <div class="vts-divider" :class="type" />
3
+ </template>
4
+
5
+ <script setup lang="ts">
6
+ defineProps<{
7
+ type: 'stretch' | 'tab'
8
+ }>()
9
+ </script>
10
+
11
+ <style scoped lang="postcss">
12
+ .vts-divider {
13
+ border-left: 0.1rem solid var(--color-grey-500);
14
+ border-bottom: 0.1rem solid var(--color-grey-500);
15
+
16
+ &.tab {
17
+ height: 2.4rem;
18
+ align-self: center;
19
+ }
20
+
21
+ &.stretch {
22
+ align-self: stretch;
23
+ }
24
+ }
25
+ </style>
@@ -4,7 +4,7 @@
4
4
  <slot name="icon">
5
5
  <UiIcon :icon />
6
6
  </slot>
7
- <div class="label p2 medium">
7
+ <div class="label p2 medium text-ellipsis">
8
8
  <slot />
9
9
  </div>
10
10
  <div v-if="info" class="info-text p3 italic">{{ info }}</div>
@@ -181,9 +181,6 @@ const checkbox = inject(
181
181
 
182
182
  .label {
183
183
  margin-right: auto;
184
- white-space: nowrap;
185
- overflow: hidden;
186
- text-overflow: ellipsis;
187
184
  }
188
185
 
189
186
  .info-text {
@@ -0,0 +1,78 @@
1
+ <!-- v1.0 -->
2
+ <template>
3
+ <div class="head-bar">
4
+ <div class="label-wrapper">
5
+ <span v-if="slots.icon || icon" class="icon">
6
+ <slot name="icon">
7
+ <UiIcon :icon />
8
+ </slot>
9
+ </span>
10
+ <h4 v-tooltip class="typo h4-medium label text-ellipsis">
11
+ <slot />
12
+ </h4>
13
+ </div>
14
+ <div v-if="slots.status" class="status typo c3-regular">
15
+ <slot name="status" />
16
+ </div>
17
+ <div v-if="slots.actions" class="actions">
18
+ <slot name="actions" />
19
+ </div>
20
+ </div>
21
+ </template>
22
+
23
+ <script lang="ts" setup>
24
+ import UiIcon from '@core/components/icon/UiIcon.vue'
25
+ import { vTooltip } from '@core/directives/tooltip.directive'
26
+ import type { IconDefinition } from '@fortawesome/fontawesome-common-types'
27
+
28
+ defineProps<{
29
+ icon?: IconDefinition
30
+ }>()
31
+
32
+ const slots = defineSlots<{
33
+ default: () => any
34
+ icon?: () => any
35
+ status?: () => any
36
+ actions?: () => any
37
+ }>()
38
+ </script>
39
+
40
+ <style lang="postcss" scoped>
41
+ .head-bar {
42
+ padding: 0.8rem 1.6rem;
43
+ display: flex;
44
+ gap: 4.8rem;
45
+ align-items: center;
46
+ border-bottom: 0.1rem solid var(--color-grey-500);
47
+ background-color: var(--background-color-primary);
48
+ }
49
+
50
+ .label-wrapper {
51
+ display: flex;
52
+ gap: 1.6rem;
53
+ align-items: center;
54
+ min-width: 0;
55
+ }
56
+
57
+ .label {
58
+ color: var(--color-grey-100);
59
+ }
60
+
61
+ .icon {
62
+ font-size: 2.4rem;
63
+ }
64
+
65
+ .status {
66
+ color: var(--color-grey-200);
67
+ display: flex;
68
+ align-items: center;
69
+ gap: 1.6rem;
70
+ }
71
+
72
+ .actions {
73
+ margin-inline-start: auto;
74
+ display: flex;
75
+ align-items: center;
76
+ gap: 0.8rem;
77
+ }
78
+ </style>
@@ -0,0 +1,133 @@
1
+ <!-- WIP -->
2
+ <template>
3
+ <div class="ui-input">
4
+ <UiIcon :icon class="before" />
5
+ <input :id v-model.trim="modelValue" class="typo p1-regular input" type="search" v-bind="$attrs" />
6
+ <UiIcon v-if="!$attrs.disabled && modelValue" :icon="faXmark" class="after" color="info" @click="modelValue = ''" />
7
+ </div>
8
+ </template>
9
+
10
+ <script lang="ts" setup>
11
+ import UiIcon from '@core/components/icon/UiIcon.vue'
12
+ import { uniqueId } from '@core/utils/unique-id.util'
13
+ import type { IconDefinition } from '@fortawesome/fontawesome-common-types'
14
+ import { faXmark } from '@fortawesome/free-solid-svg-icons'
15
+ import { computed } from 'vue'
16
+
17
+ defineOptions({
18
+ inheritAttrs: false,
19
+ })
20
+
21
+ defineProps<{
22
+ icon?: IconDefinition
23
+ }>()
24
+
25
+ const modelValue = defineModel<string>({ required: true })
26
+
27
+ const id = computed(() => uniqueId('input-'))
28
+ </script>
29
+
30
+ <style lang="postcss" scoped>
31
+ /* COLOR VARIANTS */
32
+ .input {
33
+ & {
34
+ --border-color: var(--color-grey-500);
35
+ --background-color: var(--background-color-primary);
36
+ }
37
+
38
+ &:is(:hover, :focus-visible) {
39
+ --border-color: var(--color-purple-d20);
40
+ --background-color: var(--background-color-primary);
41
+ }
42
+
43
+ &:focus {
44
+ --border-color: var(--color-purple-base);
45
+ --background-color: var(--background-color-primary);
46
+ }
47
+
48
+ &:active {
49
+ --border-color: var(--color-purple-d40);
50
+ --background-color: var(--background-color-primary);
51
+ }
52
+
53
+ &:disabled {
54
+ --border-color: var(--color-grey-500);
55
+ --background-color: var(--background-color-secondary);
56
+ }
57
+ }
58
+
59
+ /* BORDER VARIANTS */
60
+ .input {
61
+ --border-width: 0.1rem;
62
+
63
+ &:is(:hover, :focus-visible) {
64
+ --border-width: 0.1rem;
65
+ }
66
+
67
+ &:focus {
68
+ --border-width: 0.2rem;
69
+ }
70
+
71
+ &:active {
72
+ --border-width: 0.1rem;
73
+ }
74
+
75
+ &:disabled {
76
+ --border-width: 0.1rem;
77
+ }
78
+ }
79
+
80
+ /* IMPLEMENTATION */
81
+ .ui-input {
82
+ position: relative;
83
+
84
+ .before + .input {
85
+ padding-inline-start: 4.8rem;
86
+ }
87
+ }
88
+
89
+ .input {
90
+ border: var(--border-width) solid var(--border-color);
91
+ border-radius: 0.8rem;
92
+ outline: none;
93
+ width: 100%;
94
+ height: 4rem;
95
+ padding: 0.8rem 1.6rem;
96
+ color: var(--color-grey-000);
97
+ background-color: var(--background-color);
98
+
99
+ &:has(+ .after) {
100
+ padding-inline-end: 4.8rem;
101
+ }
102
+
103
+ &:disabled {
104
+ cursor: not-allowed;
105
+ }
106
+
107
+ &::placeholder {
108
+ color: var(--color-grey-300);
109
+ }
110
+
111
+ &[type='search']::-webkit-search-cancel-button {
112
+ display: none;
113
+ }
114
+ }
115
+
116
+ .before,
117
+ .after {
118
+ position: absolute;
119
+ inset-block: 1.2rem;
120
+ }
121
+
122
+ .before {
123
+ color: var(--color-grey-400);
124
+ inset-inline-start: 1.6rem;
125
+ pointer-events: none;
126
+ z-index: 1;
127
+ }
128
+
129
+ .after {
130
+ inset-inline-end: 1.6rem;
131
+ cursor: pointer;
132
+ }
133
+ </style>
@@ -0,0 +1,87 @@
1
+ <!-- v1.0 -->
2
+ <template>
3
+ <RouterLink v-if="route && !disabled" :to="route" class="object-link is-link typo p3-medium">
4
+ <span class="icon">
5
+ <slot name="icon">
6
+ <UiIcon :icon />
7
+ </slot>
8
+ </span>
9
+ <span v-tooltip class="content text-ellipsis">
10
+ <slot />
11
+ </span>
12
+ </RouterLink>
13
+ <span v-else :class="{ disabled }" class="object-link typo p3-medium">
14
+ <span class="icon">
15
+ <slot name="icon">
16
+ <UiIcon :icon />
17
+ </slot>
18
+ </span>
19
+ <slot />
20
+ </span>
21
+ </template>
22
+
23
+ <script lang="ts" setup>
24
+ import UiIcon from '@core/components/icon/UiIcon.vue'
25
+ import { vTooltip } from '@core/directives/tooltip.directive'
26
+ import type { IconDefinition } from '@fortawesome/fontawesome-common-types'
27
+ import { type RouteLocationRaw } from 'vue-router'
28
+
29
+ defineProps<{
30
+ route?: RouteLocationRaw
31
+ disabled?: boolean
32
+ icon?: IconDefinition
33
+ }>()
34
+
35
+ defineSlots<{
36
+ default: () => any
37
+ icon: () => any
38
+ }>()
39
+ </script>
40
+
41
+ <style lang="postcss" scoped>
42
+ /* COLOR VARIANTS */
43
+ .object-link {
44
+ --color: var(--color-purple-base);
45
+ --border-color: var(--color-purple-base);
46
+
47
+ &.is-link {
48
+ &:is(:hover, .hover, :focus-visible) {
49
+ --color: var(--color-purple-d20);
50
+ --border-color: var(--color-purple-d20);
51
+ }
52
+
53
+ &:is(:active, .pressed) {
54
+ --color: var(--color-purple-d40);
55
+ --border-color: var(--color-purple-d40);
56
+ }
57
+ }
58
+
59
+ &.disabled {
60
+ --color: var(--color-grey-400);
61
+ --border-color: var(--color-grey-400);
62
+ }
63
+ }
64
+
65
+ /* IMPLEMENTATION */
66
+ .object-link {
67
+ display: flex;
68
+ min-width: 0;
69
+ align-items: center;
70
+ color: var(--color);
71
+ gap: 1rem;
72
+ border-top: 0.1rem solid transparent;
73
+ border-bottom: 0.1rem solid var(--border-color);
74
+ line-height: 1;
75
+ padding-block: 0.5rem;
76
+ text-decoration: none;
77
+
78
+ &.disabled {
79
+ cursor: not-allowed;
80
+ }
81
+ }
82
+
83
+ .icon {
84
+ color: var(--color-grey-100);
85
+ font-size: 0.8rem;
86
+ }
87
+ </style>
@@ -0,0 +1,60 @@
1
+ <!-- v1.1 -->
2
+ <template>
3
+ <form class="search-bar" @submit.prevent="emit('search', value)">
4
+ <label v-if="uiStore.isDesktop" :for="id" class="typo p2-regular label">
5
+ {{ $t('core.search-bar.label') }}
6
+ </label>
7
+ <UiInput
8
+ :id
9
+ v-model="value"
10
+ :aria-label="uiStore.isMobile ? $t('core.search-bar.label') : undefined"
11
+ :icon="uiStore.isDesktop ? faMagnifyingGlass : undefined"
12
+ :placeholder="$t('core.search-bar.placeholder')"
13
+ />
14
+ <template v-if="uiStore.isDesktop">
15
+ <UiButton type="submit">{{ $t('core.search') }}</UiButton>
16
+ <Divider type="stretch" />
17
+ <UiButton v-tooltip="$t('coming-soon')" level="secondary" :left-icon="faFilter" disabled>
18
+ {{ $t('core.search-bar.use-query-builder') }}
19
+ </UiButton>
20
+ </template>
21
+ <template v-else>
22
+ <ButtonIcon type="submit" :icon="faMagnifyingGlass" />
23
+ <ButtonIcon disabled :icon="faFilter" />
24
+ </template>
25
+ </form>
26
+ </template>
27
+
28
+ <script lang="ts" setup>
29
+ import ButtonIcon from '@core/components/button/ButtonIcon.vue'
30
+ import UiButton from '@core/components/button/UiButton.vue'
31
+ import Divider from '@core/components/divider/Divider.vue'
32
+ import UiInput from '@core/components/input/UiInput.vue'
33
+ import { vTooltip } from '@core/directives/tooltip.directive'
34
+ import { useUiStore } from '@core/stores/ui.store'
35
+ import { uniqueId } from '@core/utils/unique-id.util'
36
+ import { faFilter, faMagnifyingGlass } from '@fortawesome/free-solid-svg-icons'
37
+ import { ref } from 'vue'
38
+
39
+ const emit = defineEmits<{
40
+ search: [value: string]
41
+ }>()
42
+
43
+ const id = uniqueId('search-input-')
44
+
45
+ const uiStore = useUiStore()
46
+
47
+ const value = ref<string>('')
48
+ </script>
49
+
50
+ <style lang="postcss" scoped>
51
+ .search-bar {
52
+ display: flex;
53
+ gap: 1.6rem;
54
+ align-items: center;
55
+ }
56
+
57
+ .label {
58
+ color: var(--color-grey-200);
59
+ }
60
+ </style>
@@ -1,11 +1,11 @@
1
1
  <template>
2
2
  <div
3
- v-tooltip="{ selector: '.ellipsis' }"
3
+ v-tooltip="{ selector: '.text-ellipsis' }"
4
4
  :class="color"
5
5
  :style="{ width: percentage + '%' }"
6
6
  class="stacked-bar-segment typo c4-semi-bold"
7
7
  >
8
- <div ref="ellipsisElement" :class="{ hidden }" class="ellipsis">
8
+ <div ref="ellipsisElement" :class="{ hidden }" class="text-ellipsis">
9
9
  {{ $n(percentage / 100, 'percent') }}
10
10
  </div>
11
11
  </div>
@@ -61,12 +61,6 @@ useResizeObserver(ellipsisElement, ([entry]) => {
61
61
  background-color: var(--background-color);
62
62
  }
63
63
 
64
- .ellipsis {
65
- overflow: hidden;
66
- white-space: nowrap;
67
- min-width: 0;
68
- }
69
-
70
64
  .hidden {
71
65
  visibility: hidden;
72
66
  }
@@ -28,5 +28,6 @@ useContext(DisabledContext, () => props.disabled)
28
28
  background-color: var(--background-color-primary);
29
29
  max-width: 100%;
30
30
  overflow: auto;
31
+ flex-shrink: 0;
31
32
  }
32
33
  </style>
@@ -22,6 +22,8 @@ provide('tableName', props.name)
22
22
  background-color: var(--background-color-primary);
23
23
  line-height: 2.4rem;
24
24
  color: var(--color-grey-200);
25
+ border-collapse: collapse;
26
+ table-layout: fixed;
25
27
 
26
28
  :deep(th),
27
29
  :deep(td) {
@@ -49,6 +51,10 @@ provide('tableName', props.name)
49
51
  :deep(td) {
50
52
  border-right: 0.1rem solid var(--color-grey-500);
51
53
 
54
+ &:first-child {
55
+ border-left: none;
56
+ }
57
+
52
58
  &:last-child {
53
59
  border-right: none;
54
60
  }