@xen-orchestra/web-core 0.20.1 → 0.22.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 (116) hide show
  1. package/lib/assets/css/_typography.pcss +6 -0
  2. package/lib/assets/no-selection-old.svg +70 -0
  3. package/lib/assets/no-selection.svg +85 -70
  4. package/lib/components/backup-state/VtsBackupState.vue +20 -17
  5. package/lib/components/card/VtsCardRowKeyValue.vue +4 -0
  6. package/lib/components/cell-object/VtsCellObject.vue +4 -1
  7. package/lib/components/console/VtsActionsConsole.vue +7 -4
  8. package/lib/components/console/VtsClipboardConsole.vue +9 -6
  9. package/lib/components/copy-button/VtsCopyButton.vue +9 -15
  10. package/lib/components/dropdown/DropdownTitle.vue +5 -2
  11. package/lib/components/icon/NewVtsIcon.vue +49 -0
  12. package/lib/components/input-group/VtsInputGroup.vue +41 -0
  13. package/lib/components/input-wrapper/VtsInputWrapper.vue +2 -2
  14. package/lib/components/layout/VtsLayoutSidebar.vue +6 -3
  15. package/lib/components/linear-chart/VtsLinearChart.vue +4 -0
  16. package/lib/components/object-icon/VtsObjectIcon.vue +22 -0
  17. package/lib/components/quick-info-card/VtsQuickInfoCard.vue +4 -1
  18. package/lib/components/select/VtsOption.vue +10 -6
  19. package/lib/components/select/VtsSelect.vue +74 -50
  20. package/lib/components/state-hero/VtsAllDoneHero.vue +4 -1
  21. package/lib/components/state-hero/VtsAllGoodHero.vue +4 -1
  22. package/lib/components/state-hero/VtsComingSoonHero.vue +4 -1
  23. package/lib/components/state-hero/VtsErrorNoDataHero.vue +4 -1
  24. package/lib/components/state-hero/VtsLoadingHero.vue +4 -1
  25. package/lib/components/state-hero/VtsNoDataHero.vue +4 -1
  26. package/lib/components/state-hero/VtsNoSelectionHero.vue +4 -1
  27. package/lib/components/state-hero/VtsObjectNotFoundHero.vue +4 -1
  28. package/lib/components/state-hero/VtsOfflineHero.vue +4 -1
  29. package/lib/components/state-hero/VtsPageNotFoundHero.vue +4 -1
  30. package/lib/components/table/ColumnTitle.vue +2 -2
  31. package/lib/components/task/VtsQuickTaskButton.vue +4 -1
  32. package/lib/components/task/VtsQuickTaskList.vue +5 -2
  33. package/lib/components/task/VtsQuickTaskTabBar.vue +8 -5
  34. package/lib/components/ui/card-numbers/UiCardNumbers.vue +4 -1
  35. package/lib/components/ui/character-limit/UiCharacterLimit.vue +4 -1
  36. package/lib/components/ui/input/UiInput.vue +2 -2
  37. package/lib/components/ui/label/UiLabel.vue +4 -1
  38. package/lib/components/ui/panel/UiPanel.vue +16 -3
  39. package/lib/components/ui/progress-bar/UiProgressBar.vue +5 -2
  40. package/lib/components/ui/query-search-bar/UiQuerySearchBar.vue +9 -6
  41. package/lib/components/ui/quick-task-item/UiQuickTaskItem.vue +6 -3
  42. package/lib/components/ui/quoteCode/UiQuoteCode.vue +104 -0
  43. package/lib/components/ui/stacked-bar/StackedBarSegment.vue +4 -1
  44. package/lib/components/ui/table-pagination/UiTablePagination.vue +6 -3
  45. package/lib/components/ui/text-area/UiTextarea.vue +4 -1
  46. package/lib/components/ui/top-bottom-table/UiTopBottomTable.vue +6 -3
  47. package/lib/components/ui/tree-item-label/UiTreeItemLabel.vue +4 -1
  48. package/lib/composables/local-time-ago.composable.ts +53 -0
  49. package/lib/composables/locale-time-ago.composable.ts +53 -0
  50. package/lib/icons/fa-icons.ts +164 -0
  51. package/lib/icons/index.ts +15 -0
  52. package/lib/icons/legacy-icons.ts +80 -0
  53. package/lib/icons/object-icons.ts +187 -0
  54. package/lib/layouts/CoreLayout.vue +7 -3
  55. package/lib/locales/cs.json +0 -1
  56. package/lib/locales/de.json +1 -1
  57. package/lib/locales/en.json +40 -4
  58. package/lib/locales/es.json +1 -1
  59. package/lib/locales/fr.json +39 -3
  60. package/lib/locales/it.json +1 -1
  61. package/lib/locales/nl.json +1 -1
  62. package/lib/locales/ru.json +1 -1
  63. package/lib/locales/sv.json +1 -2
  64. package/lib/packages/collection/README.md +23 -18
  65. package/lib/packages/collection/create-collection.ts +22 -21
  66. package/lib/packages/collection/create-item.ts +21 -20
  67. package/lib/packages/collection/create-use-subset.ts +23 -0
  68. package/lib/packages/collection/guess-item-id.ts +26 -16
  69. package/lib/packages/collection/index.ts +4 -0
  70. package/lib/packages/collection/types.ts +65 -37
  71. package/lib/packages/collection/use-collection.ts +68 -18
  72. package/lib/packages/collection/use-flag-registry.ts +38 -17
  73. package/lib/packages/form-select/guess-label.ts +45 -0
  74. package/lib/packages/form-select/guess-value.ts +23 -0
  75. package/lib/packages/form-select/index.ts +6 -0
  76. package/lib/packages/form-select/normalize-search-term.ts +11 -0
  77. package/lib/packages/form-select/types.ts +90 -42
  78. package/lib/packages/form-select/use-form-option-controller.ts +7 -3
  79. package/lib/packages/form-select/use-form-select-controller.ts +38 -27
  80. package/lib/packages/form-select/use-form-select-keyboard-navigation.ts +1 -1
  81. package/lib/packages/form-select/use-form-select.ts +308 -130
  82. package/lib/packages/icon/DisplayIcon.vue +25 -0
  83. package/lib/packages/icon/DisplayIconAny.vue +16 -0
  84. package/lib/packages/icon/DisplayIconSingle.vue +35 -0
  85. package/lib/packages/icon/DisplayIconStack.vue +34 -0
  86. package/lib/packages/icon/README.md +286 -0
  87. package/lib/packages/icon/create-icon-bindings.ts +27 -0
  88. package/lib/packages/icon/define-icon-pack.ts +23 -0
  89. package/lib/packages/icon/define-icon-single.ts +17 -0
  90. package/lib/packages/icon/define-icon-stack.ts +20 -0
  91. package/lib/packages/icon/define-icon.ts +40 -0
  92. package/lib/packages/icon/generate-icon-variants.ts +17 -0
  93. package/lib/packages/icon/index.ts +8 -0
  94. package/lib/packages/icon/is-icon-stack.ts +5 -0
  95. package/lib/packages/icon/merge-icons.ts +25 -0
  96. package/lib/packages/icon/merge-transforms.ts +12 -0
  97. package/lib/packages/icon/normalize-icon.ts +25 -0
  98. package/lib/packages/icon/to-tuple.ts +7 -0
  99. package/lib/packages/icon/types.ts +72 -0
  100. package/lib/packages/job/README.md +2 -2
  101. package/lib/packages/mapper/README.md +166 -0
  102. package/lib/packages/mapper/convert-to-map.ts +5 -0
  103. package/lib/packages/mapper/create-mapper.ts +30 -0
  104. package/lib/packages/mapper/index.ts +4 -0
  105. package/lib/packages/mapper/types.ts +1 -0
  106. package/lib/packages/mapper/use-mapper.ts +31 -0
  107. package/lib/stores/sidebar.store.ts +1 -1
  108. package/lib/types/chart.ts +2 -2
  109. package/lib/types/utility.type.ts +9 -0
  110. package/lib/utils/object.util.ts +16 -0
  111. package/lib/utils/size.util.ts +14 -3
  112. package/package.json +2 -1
  113. package/lib/assets/zoom.svg +0 -85
  114. package/lib/components/backup-item/VtsBackupItem.vue +0 -47
  115. package/lib/composables/mapper.composable.md +0 -74
  116. package/lib/composables/mapper.composable.ts +0 -18
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
  <TabList :disabled="loading">
3
3
  <TabItem v-bind="tabs.pending.bindings">
4
- {{ $t('tasks.quick-view.in-progress') }}
4
+ {{ t('tasks.quick-view.in-progress') }}
5
5
  <UiCounter
6
6
  v-if="pendingCount !== undefined"
7
7
  :value="pendingCount"
@@ -11,7 +11,7 @@
11
11
  />
12
12
  </TabItem>
13
13
  <TabItem v-bind="tabs.success.bindings">
14
- {{ $t('tasks.quick-view.done') }}
14
+ {{ t('tasks.quick-view.done') }}
15
15
  <UiCounter
16
16
  v-if="successCount !== undefined"
17
17
  :value="successCount"
@@ -21,7 +21,7 @@
21
21
  />
22
22
  </TabItem>
23
23
  <TabItem v-bind="tabs.failure.bindings">
24
- {{ $t('tasks.quick-view.failed') }}
24
+ {{ t('tasks.quick-view.failed') }}
25
25
  <UiCounter
26
26
  v-if="failureCount !== undefined"
27
27
  :value="failureCount"
@@ -32,12 +32,12 @@
32
32
  </TabItem>
33
33
  <VtsDivider type="tab" />
34
34
  <TabItem v-bind="tabs.all.bindings">
35
- {{ $t('tasks.quick-view.all') }}
35
+ {{ t('tasks.quick-view.all') }}
36
36
  </TabItem>
37
37
  <!--
38
38
  TODO
39
39
  <UiButton :right-icon="faAngleRight" class="see-all" level="tertiary" size="extra-small">
40
- {{ $t('see-all') }}
40
+ {{ t('see-all') }}
41
41
  </UiButton>
42
42
  -->
43
43
  </TabList>
@@ -50,6 +50,7 @@ import TabList from '@core/components/tab/TabList.vue'
50
50
  import UiCounter from '@core/components/ui/counter/UiCounter.vue'
51
51
  import type { TaskStatus } from '@core/components/ui/quick-task-item/UiQuickTaskItem.vue'
52
52
  import { useTabList } from '@core/composables/tab-list.composable'
53
+ import { useI18n } from 'vue-i18n'
53
54
 
54
55
  export type TaskTab = TaskStatus | 'all'
55
56
 
@@ -62,5 +63,7 @@ defineProps<{
62
63
 
63
64
  const currentTab = defineModel<TaskTab>({ required: true })
64
65
 
66
+ const { t } = useI18n()
67
+
65
68
  const { tabs } = useTabList<TaskTab>(['pending', 'success', 'failure', 'all'], currentTab)
66
69
  </script>
@@ -4,7 +4,7 @@
4
4
  <span class="label typo-caption-small">{{ label }}</span>
5
5
  <div class="values" :class="fontClass">
6
6
  <span v-if="percentValue !== undefined">
7
- {{ $n(percentValue, 'percent') }}
7
+ {{ n(percentValue, 'percent') }}
8
8
  </span>
9
9
  <span>
10
10
  {{ `${value ?? '-'} ${unit ?? ''}` }}
@@ -16,6 +16,7 @@
16
16
  <script setup lang="ts">
17
17
  import { toVariants } from '@core/utils/to-variants.util'
18
18
  import { computed } from 'vue'
19
+ import { useI18n } from 'vue-i18n'
19
20
 
20
21
  const { size, value, max } = defineProps<{
21
22
  label: string
@@ -25,6 +26,8 @@ const { size, value, max } = defineProps<{
25
26
  max?: number
26
27
  }>()
27
28
 
29
+ const { n } = useI18n()
30
+
28
31
  const className = computed(() => toVariants({ size }))
29
32
  const fontClass = computed(() => (size === 'medium' ? 'typo-h3' : 'typo-caption-small'))
30
33
 
@@ -1,18 +1,21 @@
1
1
  <!-- v1 -->
2
2
  <template>
3
3
  <span class="ui-character-limit" :class="classes">
4
- {{ $t('core.character-limit', { count, max }) }}
4
+ {{ t('core.character-limit', { count, max }) }}
5
5
  </span>
6
6
  </template>
7
7
 
8
8
  <script lang="ts" setup>
9
9
  import { computed } from 'vue'
10
+ import { useI18n } from 'vue-i18n'
10
11
 
11
12
  const { count, max } = defineProps<{
12
13
  count: number
13
14
  max: number
14
15
  }>()
15
16
 
17
+ const { t } = useI18n()
18
+
16
19
  const isTooLong = computed(() => count > max)
17
20
 
18
21
  const classes = computed(() => {
@@ -30,7 +30,7 @@
30
30
  import VtsIcon from '@core/components/icon/VtsIcon.vue'
31
31
  import UiButtonIcon from '@core/components/ui/button-icon/UiButtonIcon.vue'
32
32
  import type { LabelAccent } from '@core/components/ui/label/UiLabel.vue'
33
- import { useMapper } from '@core/composables/mapper.composable.ts'
33
+ import { useMapper } from '@core/packages/mapper/use-mapper.ts'
34
34
  import { IK_INPUT_WRAPPER_CONTROLLER } from '@core/utils/injection-keys.util'
35
35
  import { toVariants } from '@core/utils/to-variants.util'
36
36
  import type { IconDefinition } from '@fortawesome/fontawesome-common-types'
@@ -79,7 +79,7 @@ const accent = useMapper<LabelAccent, InputAccent>(
79
79
  warning: 'warning',
80
80
  danger: 'danger',
81
81
  },
82
- () => _accent
82
+ 'neutral'
83
83
  )
84
84
 
85
85
  if (wrapperController) {
@@ -5,7 +5,7 @@
5
5
  <label :for="htmlFor" :class="{ required }" class="typo-caption label">
6
6
  <slot />
7
7
  </label>
8
- <UiLink v-if="href" class="learn-more-link" size="small" :href>{{ $t('learn-more') }}</UiLink>
8
+ <UiLink v-if="href" class="learn-more-link" size="small" :href>{{ t('learn-more') }}</UiLink>
9
9
  </div>
10
10
  </template>
11
11
 
@@ -14,6 +14,7 @@ import VtsIcon from '@core/components/icon/VtsIcon.vue'
14
14
  import UiLink from '@core/components/ui/link/UiLink.vue'
15
15
  import { toVariants } from '@core/utils/to-variants.util'
16
16
  import type { IconDefinition } from '@fortawesome/fontawesome-common-types'
17
+ import { useI18n } from 'vue-i18n'
17
18
 
18
19
  export type LabelAccent = 'neutral' | 'warning' | 'danger'
19
20
 
@@ -24,6 +25,8 @@ const { for: htmlFor } = defineProps<{
24
25
  required?: boolean
25
26
  href?: string
26
27
  }>()
28
+
29
+ const { t } = useI18n()
27
30
  </script>
28
31
 
29
32
  <style lang="postcss" scoped>
@@ -1,6 +1,6 @@
1
- <!-- wip -->
1
+ <!-- v2 -->
2
2
  <template>
3
- <div class="ui-panel" :class="{ error }">
3
+ <div class="ui-panel" :class="{ error, 'mobile-drawer': uiStore.isMobile }">
4
4
  <div v-if="slots.header" class="header">
5
5
  <slot name="header" />
6
6
  </div>
@@ -11,6 +11,8 @@
11
11
  </template>
12
12
 
13
13
  <script setup lang="ts">
14
+ import { useUiStore } from '@core/stores/ui.store'
15
+
14
16
  defineProps<{
15
17
  error?: boolean
16
18
  }>()
@@ -19,11 +21,12 @@ const slots = defineSlots<{
19
21
  default(): any
20
22
  header?(): any
21
23
  }>()
24
+
25
+ const uiStore = useUiStore()
22
26
  </script>
23
27
 
24
28
  <style scoped lang="postcss">
25
29
  .ui-panel {
26
- max-height: calc(100dvh - 5.5rem);
27
30
  position: sticky;
28
31
  top: 0;
29
32
  display: flex;
@@ -31,6 +34,10 @@ const slots = defineSlots<{
31
34
  border-inline-start: 0.1rem solid var(--color-neutral-border);
32
35
  background-color: var(--color-neutral-background-secondary);
33
36
 
37
+ &:not(.mobile-drawer) {
38
+ height: calc(100dvh - 16rem);
39
+ }
40
+
34
41
  .header {
35
42
  border-bottom: 0.1rem solid var(--color-neutral-border);
36
43
  background-color: var(--color-neutral-background-primary);
@@ -48,6 +55,12 @@ const slots = defineSlots<{
48
55
  gap: 0.8rem;
49
56
  }
50
57
 
58
+ &.mobile-drawer {
59
+ .content {
60
+ overflow: auto;
61
+ }
62
+ }
63
+
51
64
  &.error {
52
65
  background-color: var(--color-danger-background-selected);
53
66
 
@@ -5,8 +5,8 @@
5
5
  <div class="fill" :style="{ width: `${fillWidth}%` }" />
6
6
  </div>
7
7
  <div v-if="shouldShowSteps" class="steps typo-body-regular-small">
8
- <span>{{ $n(0, 'percent') }}</span>
9
- <span v-for="step in steps" :key="step">{{ $n(step, 'percent') }}</span>
8
+ <span>{{ n(0, 'percent') }}</span>
9
+ <span v-for="step in steps" :key="step">{{ n(step, 'percent') }}</span>
10
10
  </div>
11
11
  <VtsLegendList class="legend">
12
12
  <UiLegend :accent :value="Math.round(percentage)" unit="%">{{ legend }}</UiLegend>
@@ -20,6 +20,7 @@ import UiLegend from '@core/components/ui/legend/UiLegend.vue'
20
20
  import { toVariants } from '@core/utils/to-variants.util'
21
21
  import { useClamp, useMax } from '@vueuse/math'
22
22
  import { computed } from 'vue'
23
+ import { useI18n } from 'vue-i18n'
23
24
 
24
25
  const {
25
26
  value: _value,
@@ -32,6 +33,8 @@ const {
32
33
  showSteps?: boolean
33
34
  }>()
34
35
 
36
+ const { n } = useI18n()
37
+
35
38
  const value = useMax(0, () => _value)
36
39
 
37
40
  const percentage = computed(() => (max <= 0 ? 0 : (value.value / max) * 100))
@@ -2,29 +2,29 @@
2
2
  <template>
3
3
  <form class="ui-query-search-bar" @submit.prevent="emit('search', value)">
4
4
  <label v-if="uiStore.isDesktop" :for="id" class="typo-body-regular-small label">
5
- {{ $t('core.query-search-bar.label') }}
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
11
  accent="brand"
12
- :aria-label="uiStore.isMobile ? $t('core.query-search-bar.label') : undefined"
12
+ :aria-label="uiStore.isMobile ? t('core.query-search-bar.label') : undefined"
13
13
  :icon="uiStore.isDesktop ? faMagnifyingGlass : undefined"
14
- :placeholder="$t('core.query-search-bar.placeholder')"
14
+ :placeholder="t('core.query-search-bar.placeholder')"
15
15
  />
16
16
  <template v-if="uiStore.isDesktop">
17
- <UiButton size="medium" accent="brand" variant="primary" type="submit">{{ $t('core.search') }}</UiButton>
17
+ <UiButton size="medium" accent="brand" variant="primary" type="submit">{{ t('core.search') }}</UiButton>
18
18
  <VtsDivider type="stretch" />
19
19
  <UiButton
20
- v-tooltip="$t('coming-soon')"
20
+ v-tooltip="t('coming-soon')"
21
21
  size="medium"
22
22
  accent="brand"
23
23
  variant="secondary"
24
24
  :left-icon="faFilter"
25
25
  disabled
26
26
  >
27
- {{ $t('core.query-search-bar.use-query-builder') }}
27
+ {{ t('core.query-search-bar.use-query-builder') }}
28
28
  </UiButton>
29
29
  </template>
30
30
  <template v-else>
@@ -44,11 +44,14 @@ import { useUiStore } from '@core/stores/ui.store'
44
44
  import { uniqueId } from '@core/utils/unique-id.util'
45
45
  import { faFilter, faMagnifyingGlass } from '@fortawesome/free-solid-svg-icons'
46
46
  import { ref } from 'vue'
47
+ import { useI18n } from 'vue-i18n'
47
48
 
48
49
  const emit = defineEmits<{
49
50
  search: [value: string]
50
51
  }>()
51
52
 
53
+ const { t } = useI18n()
54
+
52
55
  const id = uniqueId('search-input-')
53
56
 
54
57
  const uiStore = useUiStore()
@@ -13,14 +13,14 @@
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-body-regular-small">{{ $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
19
  <div v-if="task.start" class="line-2 typo-body-regular-small">
20
- {{ $d(task.start, 'datetime_short') }}
20
+ {{ d(task.start, 'datetime_short') }}
21
21
  <template v-if="task.end">
22
22
  <VtsIcon :icon="faArrowRight" accent="current" />
23
- {{ $d(new Date(task.end), 'datetime_short') }}
23
+ {{ d(new Date(task.end), 'datetime_short') }}
24
24
  </template>
25
25
  </div>
26
26
  </div>
@@ -37,6 +37,7 @@ import UiTag from '@core/components/ui/tag/UiTag.vue'
37
37
  import { faAngleDown, faAngleRight, faArrowRight, faCircleNotch } from '@fortawesome/free-solid-svg-icons'
38
38
  import { useToggle } from '@vueuse/core'
39
39
  import { computed } from 'vue'
40
+ import { useI18n } from 'vue-i18n'
40
41
 
41
42
  export type TaskStatus = 'pending' | 'success' | 'failure'
42
43
 
@@ -54,6 +55,8 @@ const props = defineProps<{
54
55
  task: Task
55
56
  }>()
56
57
 
58
+ const { t, d } = useI18n()
59
+
57
60
  const [isExpanded, toggleExpand] = useToggle()
58
61
 
59
62
  const subTasks = computed(() => props.task.subtasks ?? [])
@@ -0,0 +1,104 @@
1
+ <template>
2
+ <div class="ui-quote-code" :class="className">
3
+ <div class="label-container">
4
+ <div :class="fontClasses.labelClass" class="label">
5
+ {{ label }}
6
+ </div>
7
+ <div v-if="slots.actions || copy" class="actions">
8
+ <VtsCopyButton v-if="copy" :value="codeTextValue ?? ''" />
9
+ <slot name="actions" />
10
+ </div>
11
+ </div>
12
+ <code ref="code-element" :class="fontClasses.codeClass" class="code-container">
13
+ <slot />
14
+ </code>
15
+ </div>
16
+ </template>
17
+
18
+ <script setup lang="ts">
19
+ import VtsCopyButton from '@core/components/copy-button/VtsCopyButton.vue'
20
+ import { useMapper } from '@core/packages/mapper'
21
+ import { toVariants } from '@core/utils/to-variants.util.ts'
22
+ import { computed, useTemplateRef } from 'vue'
23
+
24
+ type QuoteCodeAccent = 'brand' | 'danger'
25
+ type QuoteCodeSize = 'small' | 'medium'
26
+
27
+ const { size, accent } = defineProps<{
28
+ label: string
29
+ size: QuoteCodeSize
30
+ accent: QuoteCodeAccent
31
+ copy?: boolean
32
+ }>()
33
+
34
+ const slots = defineSlots<{
35
+ default(): any
36
+ actions?(): any
37
+ }>()
38
+
39
+ const codeElement = useTemplateRef('code-element')
40
+ const codeTextValue = computed(() => codeElement.value?.textContent)
41
+
42
+ const mapping = {
43
+ small: {
44
+ labelClass: 'typo-body-regular-small',
45
+ codeClass: 'typo-form-value-small',
46
+ },
47
+ medium: {
48
+ labelClass: 'typo-body-regular',
49
+ codeClass: 'typo-form-value',
50
+ },
51
+ }
52
+
53
+ const fontClasses = useMapper(() => size, mapping, 'medium')
54
+
55
+ const className = computed(() =>
56
+ toVariants({
57
+ accent,
58
+ })
59
+ )
60
+ </script>
61
+
62
+ <style lang="postcss" scoped>
63
+ .ui-quote-code {
64
+ display: flex;
65
+ flex-direction: column;
66
+ gap: 0.4rem;
67
+
68
+ .label-container {
69
+ display: flex;
70
+ justify-content: space-between;
71
+
72
+ .label {
73
+ color: var(--color-neutral-txt-secondary);
74
+ }
75
+
76
+ .actions {
77
+ display: flex;
78
+ gap: 0.8rem;
79
+ align-items: center;
80
+ }
81
+ }
82
+
83
+ .code-container {
84
+ background-color: var(--color-neutral-background-disabled);
85
+ padding: 0.8rem 1.2rem;
86
+ border-radius: 0.4rem;
87
+ border-inline-start: 0.2rem solid;
88
+ white-space: pre-wrap;
89
+ word-break: break-word;
90
+ }
91
+
92
+ &.accent--brand {
93
+ .code-container {
94
+ border-inline-start-color: var(--color-brand-item-base);
95
+ }
96
+ }
97
+
98
+ &.accent--danger {
99
+ .code-container {
100
+ border-inline-start-color: var(--color-danger-item-base);
101
+ }
102
+ }
103
+ }
104
+ </style>
@@ -7,7 +7,7 @@
7
7
  class="stacked-bar-segment typo-caption-small"
8
8
  >
9
9
  <div ref="ellipsisElement" :class="{ hidden }" class="text-ellipsis">
10
- {{ $n(percentage / 100, 'percent') }}
10
+ {{ n(percentage / 100, 'percent') }}
11
11
  </div>
12
12
  </div>
13
13
  </template>
@@ -17,6 +17,7 @@ import { vTooltip } from '@core/directives/tooltip.directive'
17
17
  import { hasEllipsis } from '@core/utils/has-ellipsis.util'
18
18
  import { useResizeObserver } from '@vueuse/core'
19
19
  import { ref } from 'vue'
20
+ import { useI18n } from 'vue-i18n'
20
21
 
21
22
  export type StackedBarSegmentAccent = 'info' | 'success' | 'warning' | 'danger'
22
23
 
@@ -27,6 +28,8 @@ export type StackedBarSegmentProps = {
27
28
 
28
29
  defineProps<StackedBarSegmentProps>()
29
30
 
31
+ const { n } = useI18n()
32
+
30
33
  const hidden = ref(false)
31
34
  const ellipsisElement = ref<HTMLElement | null>(null)
32
35
 
@@ -8,13 +8,13 @@
8
8
  <PaginationButton :disabled="isLastPage" :icon="faAngleDoubleRight" @click="emit('last')" />
9
9
  </div>
10
10
  <span class="typo-body-regular-small label">
11
- {{ $t('core.select.n-object-of', { from, to, total }) }}
11
+ {{ t('core.select.n-object-of', { from, to, total }) }}
12
12
  </span>
13
- <span class="typo-body-regular-small label show">{{ $t('core.pagination.show-by') }}</span>
13
+ <span class="typo-body-regular-small label show">{{ t('core.pagination.show-by') }}</span>
14
14
  <div class="dropdown-wrapper">
15
15
  <select v-model="showBy" class="dropdown typo-body-regular-small">
16
16
  <option v-for="option in [50, 100, 150, 200, -1]" :key="option" :value="option" class="typo-body-bold-small">
17
- {{ option === -1 ? $t('core.pagination.all') : option }}
17
+ {{ option === -1 ? t('core.pagination.all') : option }}
18
18
  </option>
19
19
  </select>
20
20
  <VtsIcon :icon="faAngleDown" accent="current" class="icon" />
@@ -32,6 +32,7 @@ import {
32
32
  faAngleLeft,
33
33
  faAngleRight,
34
34
  } from '@fortawesome/free-solid-svg-icons'
35
+ import { useI18n } from 'vue-i18n'
35
36
 
36
37
  defineProps<{
37
38
  from: number
@@ -49,6 +50,8 @@ const emit = defineEmits<{
49
50
  }>()
50
51
 
51
52
  const showBy = defineModel<number>('showBy', { default: 50 })
53
+
54
+ const { t } = useI18n()
52
55
  </script>
53
56
 
54
57
  <style lang="postcss" scoped>
@@ -7,7 +7,7 @@
7
7
  <textarea v-bind="attrs" :id ref="textarea" v-model="model" :disabled class="textarea" />
8
8
  <UiCharacterLimit v-if="maxCharacters" :count="model.trim().length" :max="maxCharacters" />
9
9
  <UiInfo v-if="isExceedingMaxCharacters" accent="danger">
10
- {{ $t('core.textarea.exceeds-max-characters', { max: maxCharacters }) }}
10
+ {{ t('core.textarea.exceeds-max-characters', { max: maxCharacters }) }}
11
11
  </UiInfo>
12
12
  <UiInfo v-if="slots.info" :accent="accent === 'brand' ? 'info' : accent">
13
13
  <slot name="info" />
@@ -23,6 +23,7 @@ import { toVariants } from '@core/utils/to-variants.util'
23
23
  import type { IconDefinition } from '@fortawesome/fontawesome-common-types'
24
24
  import { useFocus } from '@vueuse/core'
25
25
  import { computed, useAttrs, useId, useTemplateRef } from 'vue'
26
+ import { useI18n } from 'vue-i18n'
26
27
 
27
28
  defineOptions({
28
29
  inheritAttrs: false,
@@ -49,6 +50,8 @@ const slots = defineSlots<{
49
50
  info?(): any
50
51
  }>()
51
52
 
53
+ const { t } = useI18n()
54
+
52
55
  const attrs = useAttrs()
53
56
 
54
57
  const textAreaElement = useTemplateRef('textarea')
@@ -3,7 +3,7 @@
3
3
  <div class="ui-top-bottom-table">
4
4
  <div class="content">
5
5
  <span class="typo-body-regular-small label">
6
- {{ $t('core.select.n-selected-of', { count: selectedItems, total: totalItems }) }}
6
+ {{ t('core.select.n-selected-of', { count: selectedItems, total: totalItems }) }}
7
7
  </span>
8
8
 
9
9
  <UiButton
@@ -13,7 +13,7 @@
13
13
  variant="tertiary"
14
14
  @click="emit('toggleSelectAll', true)"
15
15
  >
16
- {{ $t('core.select.all') }}
16
+ {{ t('core.select.all') }}
17
17
  </UiButton>
18
18
  <UiButton
19
19
  :disabled="selectedItems === 0"
@@ -22,7 +22,7 @@
22
22
  variant="tertiary"
23
23
  @click="emit('toggleSelectAll', false)"
24
24
  >
25
- {{ $t('core.select.unselect') }}
25
+ {{ t('core.select.unselect') }}
26
26
  </UiButton>
27
27
  </div>
28
28
  <slot />
@@ -31,6 +31,7 @@
31
31
 
32
32
  <script setup lang="ts">
33
33
  import UiButton from '@core/components/ui/button/UiButton.vue'
34
+ import { useI18n } from 'vue-i18n'
34
35
 
35
36
  defineProps<{
36
37
  selectedItems: number
@@ -44,6 +45,8 @@ const emit = defineEmits<{
44
45
  defineSlots<{
45
46
  default(): any
46
47
  }>()
48
+
49
+ const { t } = useI18n()
47
50
  </script>
48
51
 
49
52
  <style scoped lang="postcss">
@@ -16,7 +16,7 @@
16
16
  </template>
17
17
  <UiButtonIcon
18
18
  v-if="hasToggle"
19
- v-tooltip="isExpanded ? $t('core.close') : $t('core.open')"
19
+ v-tooltip="isExpanded ? t('core.close') : t('core.open')"
20
20
  class="toggle"
21
21
  accent="brand"
22
22
  :icon="isExpanded ? faAngleDown : faAngleRight"
@@ -47,6 +47,7 @@ import { IK_TREE_ITEM_EXPANDED, IK_TREE_ITEM_HAS_CHILDREN, IK_TREE_LIST_DEPTH }
47
47
  import type { IconDefinition } from '@fortawesome/fontawesome-common-types'
48
48
  import { faAngleDown, faAngleRight } from '@fortawesome/free-solid-svg-icons'
49
49
  import { inject, ref, useAttrs } from 'vue'
50
+ import { useI18n } from 'vue-i18n'
50
51
  import type { RouteLocationRaw } from 'vue-router'
51
52
 
52
53
  defineOptions({
@@ -69,6 +70,8 @@ defineSlots<{
69
70
  addons?(): any
70
71
  }>()
71
72
 
73
+ const { t } = useI18n()
74
+
72
75
  const attrs = useAttrs()
73
76
 
74
77
  const hasToggle = inject(IK_TREE_ITEM_HAS_CHILDREN, ref(false))
@@ -0,0 +1,53 @@
1
+ import { useTimestamp } from '@vueuse/core'
2
+ import { computed, type MaybeRefOrGetter, toValue } from 'vue'
3
+ import { useI18n } from 'vue-i18n'
4
+
5
+ export enum SECONDS {
6
+ MINUTE = 60,
7
+ HOUR = SECONDS.MINUTE * 60,
8
+ DAY = SECONDS.HOUR * 24,
9
+ WEEK = SECONDS.DAY * 7,
10
+ MONTH = SECONDS.DAY * 30,
11
+ YEAR = SECONDS.DAY * 365,
12
+ }
13
+
14
+ export type TimeThreshold = [number, Intl.RelativeTimeFormatUnit]
15
+
16
+ const timeThresholds: TimeThreshold[] = [
17
+ [SECONDS.MINUTE, 'second'],
18
+ [SECONDS.HOUR, 'minute'],
19
+ [SECONDS.DAY, 'hour'],
20
+ [SECONDS.WEEK, 'day'],
21
+ [SECONDS.MONTH, 'week'],
22
+ [SECONDS.YEAR, 'month'],
23
+ [Infinity, 'year'],
24
+ ]
25
+
26
+ export function useTimeAgo(referenceDate: MaybeRefOrGetter<Date | number | string>) {
27
+ const { locale } = useI18n()
28
+
29
+ const formatter = computed(() => new Intl.RelativeTimeFormat(locale.value, { numeric: 'auto' }))
30
+
31
+ const now = useTimestamp({ interval: 1000 })
32
+
33
+ const referenceTime = computed(() => new Date(toValue(referenceDate)).getTime())
34
+
35
+ const distance = computed(() => {
36
+ const diff = Math.trunc((referenceTime.value - now.value) / 1000)
37
+
38
+ if (Math.abs(diff) < 60) {
39
+ return Math.trunc(diff / 10) * 10 || 0 // Avoid recomputing when the value changes from 0 to -0
40
+ }
41
+
42
+ return timeThresholds.reduce(
43
+ (acc, [threshold]) => (Math.abs(acc) < threshold ? acc : Math.trunc(acc / threshold) * threshold),
44
+ diff
45
+ )
46
+ })
47
+
48
+ return computed(() => {
49
+ const index = timeThresholds.findIndex(([threshold]) => Math.abs(distance.value) < threshold)
50
+ const divisor = index > 0 ? timeThresholds[index - 1][0] : 1
51
+ return formatter.value.format(Math.trunc(distance.value / divisor), timeThresholds[index][1])
52
+ })
53
+ }