@xen-orchestra/web-core 0.15.0 → 0.16.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.
@@ -14,6 +14,7 @@ export type ConnectionStatus =
14
14
  | 'disconnected'
15
15
  | 'partially-connected'
16
16
  | 'disconnected-from-physical-device'
17
+ | 'physically-disconnected'
17
18
  type ConnectionStatusesMap = Record<ConnectionStatus, { text: string; accent: InfoAccent }>
18
19
 
19
20
  const { status } = defineProps<{
@@ -27,6 +28,8 @@ const statuses: ComputedRef<ConnectionStatusesMap> = computed(() => ({
27
28
  disconnected: { text: t('disconnected'), accent: 'danger' },
28
29
  'partially-connected': { text: t('partially-connected'), accent: 'warning' },
29
30
  'disconnected-from-physical-device': { text: t('disconnected-from-physical-device'), accent: 'warning' },
31
+ // This status is used in host pif side panel
32
+ 'physically-disconnected': { text: t('disconnected-from-physical-device'), accent: 'danger' },
30
33
  }))
31
34
 
32
35
  const currentStatus = computed(() => statuses.value[status])
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
  <div class="vts-clipboard-console">
3
3
  <UiCardTitle>{{ $t('console-clipboard') }}</UiCardTitle>
4
- <UiTextarea v-tooltip="$t('coming-soon')" accent="info" disabled :model-value="modelValue" />
4
+ <UiTextarea v-tooltip="$t('coming-soon')" accent="brand" disabled :model-value="modelValue" />
5
5
  <div class="buttons-container">
6
6
  <UiButton v-tooltip="$t('coming-soon')" accent="brand" variant="primary" size="medium" disabled>
7
7
  {{ $t('send') }}
@@ -16,7 +16,7 @@
16
16
  <script setup lang="ts">
17
17
  import UiButton from '@core/components/ui/button/UiButton.vue'
18
18
  import UiCardTitle from '@core/components/ui/card-title/UiCardTitle.vue'
19
- import UiTextarea from '@core/components/ui/input/UiTextarea.vue'
19
+ import UiTextarea from '@core/components/ui/text-area/UiTextarea.vue'
20
20
  import { vTooltip } from '@core/directives/tooltip.directive'
21
21
  import { ref } from 'vue'
22
22
 
@@ -0,0 +1,35 @@
1
+ <!-- v1 -->
2
+ <template>
3
+ <span class="ui-character-limit" :class="classes">
4
+ {{ $t('core.character-limit', { count, max }) }}
5
+ </span>
6
+ </template>
7
+
8
+ <script lang="ts" setup>
9
+ import { computed } from 'vue'
10
+
11
+ const { count, max } = defineProps<{
12
+ count: number
13
+ max: number
14
+ }>()
15
+
16
+ const isTooLong = computed(() => count > max)
17
+
18
+ const classes = computed(() => {
19
+ if (isTooLong.value) {
20
+ return ['has-error', 'typo-body-bold-small']
21
+ }
22
+
23
+ return 'typo-body-regular-small'
24
+ })
25
+ </script>
26
+
27
+ <style lang="postcss" scoped>
28
+ .ui-character-limit {
29
+ color: var(--color-neutral-txt-secondary);
30
+
31
+ &.has-error {
32
+ color: var(--color-danger-txt-base);
33
+ }
34
+ }
35
+ </style>
@@ -22,7 +22,7 @@
22
22
  @click="modelValue = ''"
23
23
  />
24
24
  </div>
25
- <UiInfo v-if="slots.info || info" :accent>
25
+ <UiInfo v-if="slots.info || info" :accent="accent === 'brand' ? 'info' : accent">
26
26
  <slot name="info">{{ info }}</slot>
27
27
  </UiInfo>
28
28
  </div>
@@ -37,7 +37,7 @@ import type { IconDefinition } from '@fortawesome/fontawesome-common-types'
37
37
  import { faXmark } from '@fortawesome/free-solid-svg-icons'
38
38
  import { computed, useAttrs, useId } from 'vue'
39
39
 
40
- type InputAccent = 'info' | 'warning' | 'danger'
40
+ type InputAccent = 'brand' | 'warning' | 'danger'
41
41
  type InputType = 'text' | 'number' | 'password' | 'search'
42
42
 
43
43
  defineOptions({
@@ -67,7 +67,7 @@ const slots = defineSlots<{
67
67
 
68
68
  const attrs = useAttrs()
69
69
 
70
- const labelAccent = computed(() => (accent === 'info' ? 'neutral' : accent))
70
+ const labelAccent = computed(() => (accent === 'brand' ? 'neutral' : accent))
71
71
  </script>
72
72
 
73
73
  <style lang="postcss" scoped>
@@ -76,7 +76,6 @@ const labelAccent = computed(() => (accent === 'info' ? 'neutral' : accent))
76
76
  position: relative;
77
77
  display: flex;
78
78
  flex-direction: column;
79
- flex: 1;
80
79
  gap: 0.4rem;
81
80
  flex: 1;
82
81
 
@@ -107,20 +106,20 @@ const labelAccent = computed(() => (accent === 'info' ? 'neutral' : accent))
107
106
 
108
107
  /* VARIANT */
109
108
 
110
- &.accent--info {
109
+ &.accent--brand {
111
110
  .input {
112
111
  border-color: var(--color-neutral-border);
113
112
 
114
113
  &:hover {
115
- border-color: var(--color-info-item-hover);
114
+ border-color: var(--color-brand-item-hover);
116
115
  }
117
116
 
118
117
  &:focus {
119
- border-color: var(--color-info-item-base);
118
+ border-color: var(--color-brand-item-base);
120
119
  }
121
120
 
122
121
  &:active {
123
- border-color: var(--color-info-item-active);
122
+ border-color: var(--color-brand-item-active);
124
123
  }
125
124
 
126
125
  &:disabled {
@@ -8,7 +8,7 @@
8
8
  :id
9
9
  v-model="value"
10
10
  type="text"
11
- accent="info"
11
+ accent="brand"
12
12
  :aria-label="uiStore.isMobile ? $t('core.query-search-bar.label') : undefined"
13
13
  :icon="uiStore.isDesktop ? faMagnifyingGlass : undefined"
14
14
  :placeholder="$t('core.query-search-bar.placeholder')"
@@ -1,25 +1,41 @@
1
1
  <!-- v2 -->
2
2
  <template>
3
- <div class="ui-textarea" :class="toVariants({ accent })">
4
- <UiLabel v-if="slots.default" :accent="labelAccent" :required :icon :href><slot /></UiLabel>
5
- <textarea v-model="model" :disabled class="textarea" v-bind="attrs" />
6
- <UiInfo v-if="slots.info" :accent><slot name="info" /></UiInfo>
3
+ <div class="ui-textarea" :class="toVariants({ accent: hasMaxCharactersError ? 'danger' : accent })">
4
+ <UiLabel v-if="slots.default" :accent="labelAccent" :required :icon :href :for="id">
5
+ <slot />
6
+ </UiLabel>
7
+ <textarea v-bind="attrs" :id ref="textarea" v-model="model" :disabled class="textarea" />
8
+ <UiCharacterLimit v-if="maxCharacters" :count="model.trim().length" :max="maxCharacters" />
9
+ <UiInfo v-if="isExceedingMaxCharacters" accent="danger">
10
+ {{ $t('core.textarea.exceeds-max-characters', { max: maxCharacters }) }}
11
+ </UiInfo>
12
+ <UiInfo v-if="slots.info" :accent="accent === 'brand' ? 'info' : accent">
13
+ <slot name="info" />
14
+ </UiInfo>
7
15
  </div>
8
16
  </template>
9
17
 
10
18
  <script lang="ts" setup>
19
+ import UiCharacterLimit from '@core/components/ui/character-limit/UiCharacterLimit.vue'
11
20
  import UiInfo from '@core/components/ui/info/UiInfo.vue'
12
21
  import UiLabel from '@core/components/ui/label/UiLabel.vue'
13
22
  import { toVariants } from '@core/utils/to-variants.util'
14
23
  import type { IconDefinition } from '@fortawesome/fontawesome-common-types'
15
- import { computed, useAttrs } from 'vue'
24
+ import { useFocus } from '@vueuse/core'
25
+ import { computed, useAttrs, useId, useTemplateRef } from 'vue'
16
26
 
17
27
  defineOptions({
18
28
  inheritAttrs: false,
19
29
  })
20
30
 
21
- const props = defineProps<{
22
- accent: 'info' | 'warning' | 'danger'
31
+ const {
32
+ accent,
33
+ maxCharacters,
34
+ id = useId(),
35
+ } = defineProps<{
36
+ accent: 'brand' | 'warning' | 'danger'
37
+ id?: string
38
+ maxCharacters?: number
23
39
  disabled?: boolean
24
40
  href?: string
25
41
  icon?: IconDefinition
@@ -35,7 +51,24 @@ const slots = defineSlots<{
35
51
 
36
52
  const attrs = useAttrs()
37
53
 
38
- const labelAccent = computed(() => (props.accent === 'info' ? 'neutral' : props.accent))
54
+ const textAreaElement = useTemplateRef('textarea')
55
+
56
+ const { focused } = useFocus(textAreaElement)
57
+
58
+ // WIP: To update when using VeeValidate and custom validation rules
59
+ const isExceedingMaxCharacters = computed(() =>
60
+ maxCharacters !== undefined ? model.value.trim().length > maxCharacters : false
61
+ )
62
+
63
+ const hasMaxCharactersError = computed(() => !focused.value && isExceedingMaxCharacters.value)
64
+
65
+ const labelAccent = computed(() => {
66
+ if (hasMaxCharactersError.value) {
67
+ return 'danger'
68
+ }
69
+
70
+ return accent === 'brand' ? 'neutral' : accent
71
+ })
39
72
  </script>
40
73
 
41
74
  <style lang="postcss" scoped>
@@ -54,20 +87,23 @@ const labelAccent = computed(() => (props.accent === 'info' ? 'neutral' : props.
54
87
  width: 100%;
55
88
  }
56
89
 
57
- &.accent--info {
90
+ &.accent--brand {
58
91
  .textarea {
59
92
  border-color: var(--color-neutral-border);
60
93
 
61
94
  &:hover {
62
- border-color: var(--color-info-item-hover);
95
+ border-color: var(--color-brand-item-hover);
63
96
  }
97
+
64
98
  &:active {
65
- border-color: var(--color-info-item-active);
99
+ border-color: var(--color-brand-item-active);
66
100
  }
101
+
67
102
  &:focus:not(:active) {
68
103
  border-width: 0.2rem;
69
- border-color: var(--color-info-item-base);
104
+ border-color: var(--color-brand-item-base);
70
105
  }
106
+
71
107
  &:disabled {
72
108
  background-color: var(--color-neutral-background-disabled);
73
109
  border-color: var(--color-neutral-border);
@@ -82,13 +118,16 @@ const labelAccent = computed(() => (props.accent === 'info' ? 'neutral' : props.
82
118
  &:hover {
83
119
  border-color: var(--color-warning-item-hover);
84
120
  }
121
+
85
122
  &:active {
86
123
  border-color: var(--color-warning-item-active);
87
124
  }
125
+
88
126
  &:focus:not(:active) {
89
127
  border-width: 0.2rem;
90
128
  border-color: var(--color-warning-item-base);
91
129
  }
130
+
92
131
  &:disabled {
93
132
  background-color: var(--color-neutral-background-disabled);
94
133
  border-color: var(--color-neutral-border);
@@ -103,13 +142,16 @@ const labelAccent = computed(() => (props.accent === 'info' ? 'neutral' : props.
103
142
  &:hover {
104
143
  border-color: var(--color-danger-item-hover);
105
144
  }
145
+
106
146
  &:active {
107
147
  border-color: var(--color-danger-item-active);
108
148
  }
149
+
109
150
  &:focus:not(:active) {
110
151
  border-width: 0.2rem;
111
152
  border-color: var(--color-danger-item-base);
112
153
  }
154
+
113
155
  &:disabled {
114
156
  background-color: var(--color-neutral-background-disabled);
115
157
  border-color: var(--color-neutral-border);
@@ -44,6 +44,9 @@
44
44
  "backups.vms-protection.protected": "In at least 1 job & protected",
45
45
  "backups.vms-protection.tooltip": "A VM is protected if it's in a backup job, with an enabled schedule, and the last run succeeded",
46
46
  "backups.vms-protection.unprotected": "In at least 1 job but unprotected",
47
+ "bond": "Bond",
48
+ "bond-devices": "Bond devices",
49
+ "bond-status": "Bond status",
47
50
  "bytes.gi": "GiB",
48
51
  "bytes.ki": "KiB",
49
52
  "bytes.mi": "MiB",
@@ -66,6 +69,7 @@
66
69
  "console-unavailable": "Console unavailable",
67
70
  "copy": "Copy",
68
71
  "copy-info-json": "Copy information into JSON",
72
+ "core.character-limit": "{count}/{max} character | {count}/{max} characters",
69
73
  "core.close": "Close",
70
74
  "core.copied": "Copied",
71
75
  "core.copy-id": "Copy ID",
@@ -91,6 +95,7 @@
91
95
  "core.sidebar.unlock": "Unlock sidebar",
92
96
  "core.sort.ascending": "Sort ascending",
93
97
  "core.sort.descending": "Sort descending",
98
+ "core.textarea.exceeds-max-characters": "Field value must be {max} characters or less.",
94
99
  "cpu-provisioning": "CPU provisioning",
95
100
  "cpu-provisioning-warning": "The number of vCPUs allocated exceeds the number of physical CPUs available. System performance could be affected",
96
101
  "cpu-usage": "CPU usage",
@@ -238,6 +243,7 @@
238
243
  "log-out": "Log out",
239
244
  "login": "Login",
240
245
  "login-only-on-master": "Login is only possible on the master host",
246
+ "mac-address": "MAC address",
241
247
  "mac-addresses": "MAC addresses",
242
248
  "management": "Management",
243
249
  "master": "Primary host",
@@ -257,6 +263,7 @@
257
263
  "network": "Network",
258
264
  "network-block-device": "Network block device",
259
265
  "network-download": "Download",
266
+ "network-information": "Network information",
260
267
  "network-throughput": "Network throughput",
261
268
  "network-upload": "Upload",
262
269
  "networks": "Networks",
@@ -290,6 +297,9 @@
290
297
  "password-invalid": "Password invalid",
291
298
  "patches": "Patches",
292
299
  "pause": "Pause",
300
+ "physical-interface-status": "Physical interface status",
301
+ "pif": "PIF",
302
+ "pif-status": "PIF status",
293
303
  "pifs": "PIFs",
294
304
  "pifs-status": "PIFs status",
295
305
  "please-confirm": "Please confirm",
@@ -306,6 +316,7 @@
306
316
  "power-on-vm-for-console": "Power on your VM to access its console",
307
317
  "power-state": "Power state",
308
318
  "professional-support": "Professional support",
319
+ "properties": "Properties",
309
320
  "property": "Property",
310
321
  "ram-usage": "RAM usage",
311
322
  "reboot": "Reboot",
@@ -363,6 +374,7 @@
363
374
  "switch-theme": "Switch theme",
364
375
  "system": "System",
365
376
  "table-actions": "Table actions",
377
+ "tags": "Tags",
366
378
  "task.estimated-end": "Estimated end",
367
379
  "task.progress": "Progress",
368
380
  "task.started": "Started",
@@ -44,6 +44,9 @@
44
44
  "backups.vms-protection.protected": "Dans au moins 1 job et protégé",
45
45
  "backups.vms-protection.tooltip": "Une VM est protégée si elle se trouve dans un job, avec une planification activée, et si la dernière exécution a réussi",
46
46
  "backups.vms-protection.unprotected": "Dans au moins 1 job mais sans protection",
47
+ "bond": "Bond",
48
+ "bond-devices": "Devices du bond",
49
+ "bond-status": "Statut du bond",
47
50
  "bytes.gi": "Gio",
48
51
  "bytes.ki": "Kio",
49
52
  "bytes.mi": "Mio",
@@ -66,6 +69,7 @@
66
69
  "console-unavailable": "Console indisponible",
67
70
  "copy": "Copier",
68
71
  "copy-info-json": "Copier les informations en JSON",
72
+ "core.character-limit": "{count}/{max} caractère | {count}/{max} caractères",
69
73
  "core.close": "Fermer",
70
74
  "core.copied": "Copié",
71
75
  "core.copy-id": "Copier l'ID",
@@ -91,6 +95,7 @@
91
95
  "core.sidebar.unlock": "Déverrouiller la barre latérale",
92
96
  "core.sort.ascending": "Trier par ordre croissant",
93
97
  "core.sort.descending": "Trier par ordre décroissant",
98
+ "core.textarea.exceeds-max-characters": "Le champ doit contenir {max} caractères ou moins.",
94
99
  "cpu-provisioning": "Provisionnement CPU",
95
100
  "cpu-provisioning-warning": "Le nombre de vCPU alloués dépasse le nombre de CPU physique disponible. Les performances du système pourraient être affectées",
96
101
  "cpu-usage": "Utilisation CPU",
@@ -238,6 +243,7 @@
238
243
  "log-out": "Se déconnecter",
239
244
  "login": "Connexion",
240
245
  "login-only-on-master": "La connexion n'est possible que sur l'hôte primaire",
246
+ "mac-address": "Adresse MAC",
241
247
  "mac-addresses": "Adresses MAC",
242
248
  "management": "Gestion",
243
249
  "master": "Hôte primaire",
@@ -257,6 +263,7 @@
257
263
  "network": "Réseau",
258
264
  "network-block-device": "Protocole NBD (Network Block Device)",
259
265
  "network-download": "Descendant",
266
+ "network-information": "Informations du réseau",
260
267
  "network-throughput": "Débit du réseau",
261
268
  "network-upload": "Montant",
262
269
  "networks": "Réseaux",
@@ -290,6 +297,9 @@
290
297
  "password-invalid": "Mot de passe incorrect",
291
298
  "patches": "Patches",
292
299
  "pause": "Pause",
300
+ "physical-interface-status": "Statut de l'interface physique",
301
+ "pif": "PIF",
302
+ "pif-status": "Statut du PIF",
293
303
  "pifs": "PIFs",
294
304
  "pifs-status": "Statut des PIFs",
295
305
  "please-confirm": "Veuillez confirmer",
@@ -306,6 +316,7 @@
306
316
  "power-on-vm-for-console": "Allumez votre VM pour accéder à sa console",
307
317
  "power-state": "État d'alimentation",
308
318
  "professional-support": "Support professionnel",
319
+ "properties": "Propriétés",
309
320
  "property": "Propriété",
310
321
  "ram-usage": "Utilisation de la RAM",
311
322
  "reboot": "Redémarrer",
@@ -363,6 +374,7 @@
363
374
  "switch-theme": "Changer de thème",
364
375
  "system": "Système",
365
376
  "table-actions": "Actions du tableau",
377
+ "tags": "Tags",
366
378
  "task.estimated-end": "Fin estimée",
367
379
  "task.progress": "Progression",
368
380
  "task.started": "Démarré",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@xen-orchestra/web-core",
3
3
  "type": "module",
4
- "version": "0.15.0",
4
+ "version": "0.16.0",
5
5
  "private": false,
6
6
  "exports": {
7
7
  "./*": {