@xen-orchestra/web-core 0.14.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.
Files changed (69) hide show
  1. package/lib/assets/css/_typography.pcss +148 -6
  2. package/lib/assets/css/base.pcss +4 -4
  3. package/lib/components/backup-item/VtsBackupItem.vue +1 -1
  4. package/lib/components/card/VtsCardRowKeyValue.vue +4 -2
  5. package/lib/components/cell-object/VtsCellObject.vue +1 -1
  6. package/lib/components/cell-text/VtsCellText.vue +2 -2
  7. package/lib/components/connection-status/VtsConnectionStatus.vue +3 -0
  8. package/lib/components/console/VtsClipboardConsole.vue +2 -2
  9. package/lib/components/data-table/VtsDataTable.vue +11 -3
  10. package/lib/components/menu/MenuTrigger.vue +2 -2
  11. package/lib/components/resources/VtsResource.vue +4 -5
  12. package/lib/components/state-hero/VtsPageNotFoundHero.vue +1 -1
  13. package/lib/components/state-hero/VtsStateHero.vue +2 -2
  14. package/lib/components/tab/TabItem.vue +10 -10
  15. package/lib/components/table/ColumnTitle.vue +8 -8
  16. package/lib/components/table/VtsTable.vue +2 -2
  17. package/lib/components/task/VtsQuickTaskList.vue +1 -1
  18. package/lib/components/tree/VtsTreeItemError.vue +1 -1
  19. package/lib/components/ui/account-menu-button/UiAccountMenuButton.vue +1 -1
  20. package/lib/components/ui/actions-title/UiActionsTitle.vue +1 -1
  21. package/lib/components/ui/button/UiButton.vue +12 -12
  22. package/lib/components/ui/button-icon/UiButtonIcon.vue +3 -3
  23. package/lib/components/ui/card-numbers/UiCardNumbers.vue +4 -6
  24. package/lib/components/ui/card-subtitle/UiCardSubtitle.vue +3 -3
  25. package/lib/components/ui/card-title/UiCardTitle.vue +3 -3
  26. package/lib/components/ui/character-limit/UiCharacterLimit.vue +35 -0
  27. package/lib/components/ui/checkbox/UiCheckbox.vue +4 -4
  28. package/lib/components/ui/chip/UiChip.vue +1 -1
  29. package/lib/components/ui/counter/UiCounter.vue +7 -7
  30. package/lib/components/ui/dropdown-button/UiDropdownButton.vue +2 -2
  31. package/lib/components/ui/head-bar/UiHeadBar.vue +2 -2
  32. package/lib/components/ui/info/UiInfo.vue +1 -1
  33. package/lib/components/ui/input/UiInput.vue +15 -9
  34. package/lib/components/ui/label/UiLabel.vue +1 -1
  35. package/lib/components/ui/legend/UiLegend.vue +3 -3
  36. package/lib/components/ui/legend-title/UiLegendTitle.vue +1 -1
  37. package/lib/components/ui/link/UiLink.vue +3 -3
  38. package/lib/components/ui/object-link/UiObjectLink.vue +7 -7
  39. package/lib/components/ui/panel/UiPanel.vue +4 -2
  40. package/lib/components/ui/query-search-bar/UiQuerySearchBar.vue +2 -2
  41. package/lib/components/ui/quick-task-item/UiQuickTaskItem.vue +3 -3
  42. package/lib/components/ui/radio-button/UiRadioButton.vue +2 -2
  43. package/lib/components/ui/stacked-bar/StackedBarSegment.vue +1 -1
  44. package/lib/components/ui/table-pagination/UiTablePagination.vue +4 -4
  45. package/lib/components/ui/tag/UiTag.vue +1 -1
  46. package/lib/components/ui/{input → text-area}/UiTextarea.vue +54 -12
  47. package/lib/components/ui/title/UiTitle.vue +3 -3
  48. package/lib/components/ui/toaster/UiToaster.vue +2 -2
  49. package/lib/components/ui/toggle/UiToggle.vue +3 -3
  50. package/lib/components/ui/tooltip/UiTooltip.vue +1 -1
  51. package/lib/components/ui/top-bottom-table/UiTopBottomTable.vue +1 -1
  52. package/lib/components/ui/tree-item-label/UiTreeItemLabel.vue +1 -1
  53. package/lib/components/ui/user-link/UiUserLink.vue +8 -8
  54. package/lib/i18n.ts +4 -0
  55. package/lib/locales/cs.json +362 -16
  56. package/lib/locales/de.json +245 -41
  57. package/lib/locales/en.json +316 -14
  58. package/lib/locales/es.json +355 -29
  59. package/lib/locales/fa.json +206 -9
  60. package/lib/locales/fr.json +320 -18
  61. package/lib/locales/sv.json +421 -0
  62. package/lib/utils/to-variants.util.md +3 -3
  63. package/package.json +1 -1
  64. package/lib/assets/css/typography/_legacy.pcss +0 -123
  65. package/lib/assets/css/typography/_letter-spacing.pcss +0 -27
  66. package/lib/assets/css/typography/_line-height.pcss +0 -19
  67. package/lib/assets/css/typography/_size.pcss +0 -95
  68. package/lib/assets/css/typography/_style.pcss +0 -35
  69. package/lib/assets/css/typography/_weight.pcss +0 -57
@@ -1,9 +1,9 @@
1
1
  <!-- v1 -->
2
2
  <template>
3
3
  <div class="ui-card-numbers" :class="sizeClass">
4
- <span class="label" :class="labelFontClass">{{ label }}</span>
4
+ <span class="label typo-caption-small">{{ label }}</span>
5
5
  <div class="values" :class="sizeClass">
6
- <span v-if="percentValue" class="value typo c2-semi-bold">
6
+ <span v-if="percentValue" class="value typo-caption-small">
7
7
  {{ percentValue }}
8
8
  </span>
9
9
 
@@ -31,11 +31,9 @@ const { n } = useI18n()
31
31
 
32
32
  const sizeClass = computed(() => toVariants({ size: props.size }))
33
33
 
34
- const labelFontClass = computed(() => (props.size === 'medium' ? 'typo c3-semi-bold' : 'typo c2-semi-bold'))
34
+ const valueFontClass = computed(() => (props.size === 'medium' ? 'typo-h3' : 'typo-caption-small'))
35
35
 
36
- const valueFontClass = computed(() => (props.size === 'medium' ? 'typo h3-semi-bold' : 'typo c2-semi-bold'))
37
-
38
- const unitFontClass = computed(() => (props.size === 'medium' ? 'typo p2-medium' : 'typo c2-semi-bold'))
36
+ const unitFontClass = computed(() => (props.size === 'medium' ? 'typo-body-bold-small' : 'typo-caption-small'))
39
37
 
40
38
  const percentValue = computed(() => {
41
39
  if (props.size !== 'small' || props.max === undefined || props.max === 0) {
@@ -1,8 +1,8 @@
1
1
  <!-- v1 -->
2
2
  <template>
3
- <div class="ui-card-subtitle">
4
- <span class="typo h7-semi-bold"><slot /></span>
5
- <span v-if="slots.info" class="typo p3-medium"><slot name="info" /></span>
3
+ <div class="ui-card-subtitle typo-body-bold-small">
4
+ <span><slot /></span>
5
+ <span v-if="slots.info"><slot name="info" /></span>
6
6
  </div>
7
7
  </template>
8
8
 
@@ -2,14 +2,14 @@
2
2
  <template>
3
3
  <div class="ui-card-title">
4
4
  <div class="main-content">
5
- <div class="title typo h6-medium">
5
+ <div class="title typo-body-bold">
6
6
  <slot />
7
7
  </div>
8
- <div v-if="slots.info" class="info typo h7-semi-bold">
8
+ <div v-if="slots.info" class="info typo-body-bold-small">
9
9
  <slot name="info" />
10
10
  </div>
11
11
  </div>
12
- <p v-if="slots.description" class="description typo p3-regular">
12
+ <p v-if="slots.description" class="description typo-body-regular-small">
13
13
  <slot name="description" />
14
14
  </p>
15
15
  </div>
@@ -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>
@@ -12,7 +12,7 @@
12
12
  <span class="fake-checkbox">
13
13
  <VtsIcon :icon accent="current" class="icon" />
14
14
  </span>
15
- <span v-if="slots.default" class="typo p1-regular">
15
+ <span v-if="slots.default" class="typo-body-regular">
16
16
  <slot />
17
17
  </span>
18
18
  </label>
@@ -156,7 +156,7 @@ const attrs = useAttrs()
156
156
  }
157
157
 
158
158
  &:focus-visible + .fake-checkbox::before {
159
- border: 0.2rem solid var(--color-info-txt-base);
159
+ border: 0.2rem solid var(--color-brand-txt-base);
160
160
  }
161
161
 
162
162
  &:active + .fake-checkbox {
@@ -207,7 +207,7 @@ const attrs = useAttrs()
207
207
  }
208
208
 
209
209
  &:focus-visible + .fake-checkbox::before {
210
- border: 0.2rem solid var(--color-warning-txt-base);
210
+ border: 0.2rem solid var(--color-brand-txt-base);
211
211
  }
212
212
 
213
213
  &:active + .fake-checkbox {
@@ -262,7 +262,7 @@ const attrs = useAttrs()
262
262
  }
263
263
 
264
264
  &:focus-visible + .fake-checkbox::before {
265
- border: 0.2rem solid var(--color-danger-txt-base);
265
+ border: 0.2rem solid var(--color-brand-txt-base);
266
266
  }
267
267
 
268
268
  &:active + .fake-checkbox {
@@ -1,6 +1,6 @@
1
1
  <!-- v4 -->
2
2
  <template>
3
- <span :class="classNames" class="ui-chip typo p3-regular" @click="emit('edit')">
3
+ <span :class="classNames" class="ui-chip typo-body-regular-small" @click="emit('edit')">
4
4
  <ChipIcon :disabled :icon />
5
5
  <span class="content text-ellipsis">
6
6
  <slot />
@@ -21,8 +21,8 @@ const { size, accent, variant } = defineProps<{
21
21
  }>()
22
22
 
23
23
  const typoClasses = {
24
- small: 'typo p4-semi-bold',
25
- medium: 'typo p1-medium',
24
+ small: 'typo-body-bold-small',
25
+ medium: 'typo-body-bold',
26
26
  }
27
27
 
28
28
  const classNames = computed(() => {
@@ -57,16 +57,16 @@ const classNames = computed(() => {
57
57
  color: var(--color-brand-txt-item);
58
58
  }
59
59
 
60
- &.accent--info {
61
- background-color: var(--color-info-item-base);
62
- color: var(--color-info-txt-item);
63
- }
64
-
65
60
  &.accent--neutral {
66
61
  background-color: var(--color-neutral-txt-primary);
67
62
  color: var(--color-neutral-background-primary);
68
63
  }
69
64
 
65
+ &.accent--info {
66
+ background-color: var(--color-info-item-base);
67
+ color: var(--color-info-txt-item);
68
+ }
69
+
70
70
  &.accent--success {
71
71
  background-color: var(--color-success-item-base);
72
72
  color: var(--color-success-txt-item);
@@ -2,7 +2,7 @@
2
2
  <template>
3
3
  <button :class="{ selected }" :disabled="isDisabled" class="ui-dropdown-item" type="button">
4
4
  <VtsIcon :icon accent="current" class="left-icon" fixed-width />
5
- <span class="typo p1-regular label">
5
+ <span class="typo-action-button label">
6
6
  <slot />
7
7
  </span>
8
8
  <VtsIcon :icon="faAngleDown" accent="current" class="right-icon" fixed-width />
@@ -62,7 +62,7 @@ const isDisabled = useDisabled(() => disabled)
62
62
  content: '';
63
63
  position: absolute;
64
64
  inset: -0.5rem;
65
- border: 0.2rem solid var(--color-info-txt-base);
65
+ border: 0.2rem solid var(--color-brand-txt-base);
66
66
  border-radius: 0.4rem;
67
67
  }
68
68
  }
@@ -7,11 +7,11 @@
7
7
  <VtsIcon :icon accent="current" />
8
8
  </slot>
9
9
  </span>
10
- <h4 v-tooltip class="typo h4-medium label text-ellipsis">
10
+ <h4 v-tooltip class="typo-h4 label text-ellipsis">
11
11
  <slot />
12
12
  </h4>
13
13
  </div>
14
- <div v-if="slots.status" class="status typo c3-regular">
14
+ <div v-if="slots.status" class="status typo-caption-small">
15
15
  <slot name="status" />
16
16
  </div>
17
17
  <div v-if="slots.actions" class="actions">
@@ -2,7 +2,7 @@
2
2
  <template>
3
3
  <div class="ui-info">
4
4
  <VtsIcon :accent class="icon" :icon="faCircle" :overlay-icon="icon" />
5
- <p v-tooltip="!wrap" class="typo p3-regular" :class="{ 'text-ellipsis': !wrap }">
5
+ <p v-tooltip="!wrap" class="typo-form-info" :class="{ 'text-ellipsis': !wrap }">
6
6
  <slot />
7
7
  </p>
8
8
  </div>
@@ -6,7 +6,14 @@
6
6
  </UiLabel>
7
7
  <div>
8
8
  <VtsIcon :icon accent="current" class="before" />
9
- <input :id v-model.trim="modelValue" class="typo p1-regular input text-ellipsis" :type :disabled v-bind="attrs" />
9
+ <input
10
+ :id
11
+ v-model.trim="modelValue"
12
+ class="typo-body-regular input text-ellipsis"
13
+ :type
14
+ :disabled
15
+ v-bind="attrs"
16
+ />
10
17
  <VtsIcon
11
18
  v-if="!attrs.disabled && modelValue && clearable"
12
19
  :icon="faXmark"
@@ -15,7 +22,7 @@
15
22
  @click="modelValue = ''"
16
23
  />
17
24
  </div>
18
- <UiInfo v-if="slots.info || info" :accent>
25
+ <UiInfo v-if="slots.info || info" :accent="accent === 'brand' ? 'info' : accent">
19
26
  <slot name="info">{{ info }}</slot>
20
27
  </UiInfo>
21
28
  </div>
@@ -30,7 +37,7 @@ import type { IconDefinition } from '@fortawesome/fontawesome-common-types'
30
37
  import { faXmark } from '@fortawesome/free-solid-svg-icons'
31
38
  import { computed, useAttrs, useId } from 'vue'
32
39
 
33
- type InputAccent = 'info' | 'warning' | 'danger'
40
+ type InputAccent = 'brand' | 'warning' | 'danger'
34
41
  type InputType = 'text' | 'number' | 'password' | 'search'
35
42
 
36
43
  defineOptions({
@@ -60,7 +67,7 @@ const slots = defineSlots<{
60
67
 
61
68
  const attrs = useAttrs()
62
69
 
63
- const labelAccent = computed(() => (accent === 'info' ? 'neutral' : accent))
70
+ const labelAccent = computed(() => (accent === 'brand' ? 'neutral' : accent))
64
71
  </script>
65
72
 
66
73
  <style lang="postcss" scoped>
@@ -69,7 +76,6 @@ const labelAccent = computed(() => (accent === 'info' ? 'neutral' : accent))
69
76
  position: relative;
70
77
  display: flex;
71
78
  flex-direction: column;
72
- flex: 1;
73
79
  gap: 0.4rem;
74
80
  flex: 1;
75
81
 
@@ -100,20 +106,20 @@ const labelAccent = computed(() => (accent === 'info' ? 'neutral' : accent))
100
106
 
101
107
  /* VARIANT */
102
108
 
103
- &.accent--info {
109
+ &.accent--brand {
104
110
  .input {
105
111
  border-color: var(--color-neutral-border);
106
112
 
107
113
  &:hover {
108
- border-color: var(--color-info-item-hover);
114
+ border-color: var(--color-brand-item-hover);
109
115
  }
110
116
 
111
117
  &:focus {
112
- border-color: var(--color-info-item-base);
118
+ border-color: var(--color-brand-item-base);
113
119
  }
114
120
 
115
121
  &:active {
116
- border-color: var(--color-info-item-active);
122
+ border-color: var(--color-brand-item-active);
117
123
  }
118
124
 
119
125
  &:disabled {
@@ -2,7 +2,7 @@
2
2
  <template>
3
3
  <div :class="toVariants({ accent })" class="ui-label">
4
4
  <VtsIcon accent="current" :icon class="icon" />
5
- <label :for="htmlFor" :class="{ required }" class="typo c2-semi-bold label">
5
+ <label :for="htmlFor" :class="{ required }" class="typo-caption label">
6
6
  <slot />
7
7
  </label>
8
8
  <UiLink v-if="href" class="learn-more-link" size="small" :href>{{ $t('learn-more') }}</UiLink>
@@ -2,9 +2,9 @@
2
2
  <template>
3
3
  <li :class="classNames" class="ui-legend">
4
4
  <VtsIcon :icon="faCircle" accent="current" class="circle-icon" />
5
- <span class="label typo p3-regular"><slot /></span>
6
- <VtsIcon v-if="tooltip" v-tooltip="tooltip" :icon="faCircleInfo" class="tooltip-icon" accent="brand" />
7
- <span v-if="valueLabel" class="value-and-unit typo c3-semi-bold">{{ valueLabel }}</span>
5
+ <span class="label typo-body-regular-small"><slot /></span>
6
+ <VtsIcon v-if="tooltip" v-tooltip="tooltip" :icon="faCircleInfo" class="tooltip-icon" accent="info" />
7
+ <span v-if="valueLabel" class="value-and-unit typo-caption-small">{{ valueLabel }}</span>
8
8
  </li>
9
9
  </template>
10
10
 
@@ -1,6 +1,6 @@
1
1
  <!-- v3 -->
2
2
  <template>
3
- <div class="ui-legend-title typo c3-semi-bold">
3
+ <div class="ui-legend-title typo-caption-small">
4
4
  <slot />
5
5
  <VtsIcon v-tooltip="iconTooltip ?? false" :icon accent="info" />
6
6
  </div>
@@ -22,8 +22,8 @@ const props = defineProps<
22
22
  >()
23
23
 
24
24
  const typoClasses = {
25
- small: 'typo p3-regular-underline',
26
- medium: 'typo p1-regular-underline',
25
+ small: 'typo-action-link-small',
26
+ medium: 'typo-action-link',
27
27
  }
28
28
 
29
29
  const { component, attributes, isDisabled } = useLinkComponent('span', () => props)
@@ -53,7 +53,7 @@ const classes = computed(() => [typoClasses[props.size], { disabled: isDisabled.
53
53
  content: '';
54
54
  position: absolute;
55
55
  inset: -0.4rem;
56
- border: 0.2rem solid var(--color-info-txt-base);
56
+ border: 0.2rem solid var(--color-brand-txt-base);
57
57
  border-radius: 0.4rem;
58
58
  }
59
59
  }
@@ -1,6 +1,6 @@
1
1
  <!-- v3 -->
2
2
  <template>
3
- <RouterLink v-if="route && !disabled" :to="route" class="ui-object-link is-link typo p3-regular-underline">
3
+ <RouterLink v-if="route && !disabled" :to="route" class="ui-object-link is-link typo-action-link-small">
4
4
  <span class="icon">
5
5
  <slot name="icon">
6
6
  <VtsIcon :icon accent="current" />
@@ -10,7 +10,7 @@
10
10
  <slot />
11
11
  </span>
12
12
  </RouterLink>
13
- <span v-else :class="{ disabled }" class="ui-object-link typo p3-regular-underline">
13
+ <span v-else :class="{ disabled }" class="ui-object-link typo-action-link-small">
14
14
  <span class="icon">
15
15
  <slot name="icon">
16
16
  <VtsIcon :icon accent="current" />
@@ -43,7 +43,7 @@ defineSlots<{
43
43
  display: flex;
44
44
  min-width: 0;
45
45
  align-items: center;
46
- color: var(--color-info-txt-base);
46
+ color: var(--color-brand-txt-base);
47
47
  gap: 1rem;
48
48
 
49
49
  &.disabled {
@@ -53,18 +53,18 @@ defineSlots<{
53
53
 
54
54
  .icon {
55
55
  color: var(--color-neutral-txt-primary);
56
- font-size: 0.8rem;
56
+ font-size: 1.6rem;
57
57
  }
58
58
 
59
59
  /* INTERACTION VARIANTS */
60
60
 
61
61
  &.is-link {
62
62
  &:hover {
63
- color: var(--color-info-txt-hover);
63
+ color: var(--color-brand-txt-hover);
64
64
  }
65
65
 
66
66
  &:active {
67
- color: var(--color-info-txt-active);
67
+ color: var(--color-brand-txt-active);
68
68
  }
69
69
 
70
70
  &:focus-visible {
@@ -74,7 +74,7 @@ defineSlots<{
74
74
  content: '';
75
75
  position: absolute;
76
76
  inset: -0.6rem;
77
- border: 0.2rem solid var(--color-info-txt-base);
77
+ border: 0.2rem solid var(--color-brand-txt-base);
78
78
  border-radius: 0.4rem;
79
79
  }
80
80
  }
@@ -23,10 +23,12 @@ const slots = defineSlots<{
23
23
 
24
24
  <style scoped lang="postcss">
25
25
  .ui-panel {
26
- height: 100%;
26
+ max-height: calc(100dvh - 5.5rem);
27
+ position: sticky;
28
+ top: 0;
27
29
  display: flex;
28
30
  flex-direction: column;
29
- border: 0.1rem solid var(--color-neutral-border);
31
+ border-inline-start: 0.1rem solid var(--color-neutral-border);
30
32
  background-color: var(--color-neutral-background-secondary);
31
33
 
32
34
  .header {
@@ -1,14 +1,14 @@
1
1
  <!-- v3 -->
2
2
  <template>
3
3
  <form class="ui-query-search-bar" @submit.prevent="emit('search', value)">
4
- <label v-if="uiStore.isDesktop" :for="id" class="typo p2-regular label">
4
+ <label v-if="uiStore.isDesktop" :for="id" class="typo-body-regular-small label">
5
5
  {{ $t('core.query-search-bar.label') }}
6
6
  </label>
7
7
  <UiInput
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')"
@@ -5,7 +5,7 @@
5
5
  <UiButtonIcon accent="brand" :icon="isExpanded ? faAngleDown : faAngleRight" size="small" />
6
6
  </div>
7
7
  <div class="content">
8
- <div class="typo p1-medium">
8
+ <div class="typo-body-bold">
9
9
  {{ task.name }}
10
10
  </div>
11
11
  <div class="informations">
@@ -13,10 +13,10 @@
13
13
  <UiTag v-if="task.tag" accent="neutral" variant="primary">{{ task.tag }}</UiTag>
14
14
  <div v-if="hasSubTasks" class="subtasks">
15
15
  <VtsIcon :icon="faCircleNotch" accent="current" />
16
- <span class="typo p4-medium">{{ $t('tasks.n-subtasks', { n: subTasksCount }) }}</span>
16
+ <span class="typo-body-regular-small">{{ $t('tasks.n-subtasks', { n: subTasksCount }) }}</span>
17
17
  </div>
18
18
  </div>
19
- <div v-if="task.start" class="line-2 typo p4-regular">
19
+ <div v-if="task.start" class="line-2 typo-body-regular-small">
20
20
  {{ $d(task.start, 'datetime_short') }}
21
21
  <template v-if="task.end">
22
22
  <VtsIcon :icon="faArrowRight" accent="current" />
@@ -1,6 +1,6 @@
1
1
  <!-- v4 -->
2
2
  <template>
3
- <label :class="variant" class="ui-radio-button typo p1-regular">
3
+ <label :class="variant" class="ui-radio-button typo-body-regular">
4
4
  <span class="radio-container">
5
5
  <input v-model="model" :disabled="isDisabled" :value class="input" type="radio" />
6
6
  <VtsIcon :icon="faCircle" accent="current" class="radio-icon" />
@@ -60,7 +60,7 @@ const isDisabled = useDisabled(() => disabled)
60
60
  position: absolute;
61
61
  content: '';
62
62
  inset: -0.5rem;
63
- border: 0.2rem solid var(--color-info-txt-base);
63
+ border: 0.2rem solid var(--color-brand-txt-base);
64
64
  border-radius: 0.4rem;
65
65
  }
66
66
  }
@@ -4,7 +4,7 @@
4
4
  v-tooltip="{ selector: '.text-ellipsis' }"
5
5
  :class="`accent--${accent}`"
6
6
  :style="{ width: percentage + '%' }"
7
- class="stacked-bar-segment typo c4-semi-bold"
7
+ class="stacked-bar-segment typo-caption-small"
8
8
  >
9
9
  <div ref="ellipsisElement" :class="{ hidden }" class="text-ellipsis">
10
10
  {{ $n(percentage / 100, 'percent') }}
@@ -7,13 +7,13 @@
7
7
  <PaginationButton :disabled="isLastPage || disabled" :icon="faAngleRight" @click="goToNextPage()" />
8
8
  <PaginationButton :disabled="isLastPage || disabled" :icon="faAngleDoubleRight" @click="goToLastPage()" />
9
9
  </div>
10
- <span class="typo p3-regular label">
10
+ <span class="typo-body-regular-small label">
11
11
  {{ $t('core.select.n-object-of', { from: startIndex, to: endIndex, total: totalItems }) }}
12
12
  </span>
13
- <span class="typo p3-regular label show">{{ $t('core.show-by') }}</span>
13
+ <span class="typo-body-regular-small label show">{{ $t('core.show-by') }}</span>
14
14
  <div class="dropdown-wrapper">
15
- <select v-model="pageSize" :disabled class="dropdown typo c3-regular" @change="goToFirstPage">
16
- <option v-for="option in pageSizeOptions" :key="option" :value="option" class="typo p2-medium">
15
+ <select v-model="pageSize" :disabled class="dropdown typo-body-regular-small" @change="goToFirstPage">
16
+ <option v-for="option in pageSizeOptions" :key="option" :value="option" class="typo-body-bold-small">
17
17
  {{ option }}
18
18
  </option>
19
19
  </select>
@@ -1,7 +1,7 @@
1
1
  <!-- v3 -->
2
2
  <!-- TODO: implement tertiary variant to bump to v4 -->
3
3
  <template>
4
- <span :class="toVariants({ accent, variant })" class="ui-tag typo p3-regular">
4
+ <span :class="toVariants({ accent, variant })" class="ui-tag typo-body-regular-small">
5
5
  <slot name="icon">
6
6
  <VtsIcon :icon accent="current" fixed-width />
7
7
  </slot>
@@ -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);