@xen-orchestra/web-core 0.20.1 → 0.21.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 (109) hide show
  1. package/lib/components/backup-state/VtsBackupState.vue +20 -17
  2. package/lib/components/cell-object/VtsCellObject.vue +4 -1
  3. package/lib/components/console/VtsActionsConsole.vue +7 -4
  4. package/lib/components/console/VtsClipboardConsole.vue +9 -6
  5. package/lib/components/copy-button/VtsCopyButton.vue +7 -14
  6. package/lib/components/dropdown/DropdownTitle.vue +5 -2
  7. package/lib/components/icon/NewVtsIcon.vue +49 -0
  8. package/lib/components/input-group/VtsInputGroup.vue +41 -0
  9. package/lib/components/input-wrapper/VtsInputWrapper.vue +2 -2
  10. package/lib/components/layout/VtsLayoutSidebar.vue +6 -3
  11. package/lib/components/linear-chart/VtsLinearChart.vue +4 -0
  12. package/lib/components/object-icon/VtsObjectIcon.vue +22 -0
  13. package/lib/components/quick-info-card/VtsQuickInfoCard.vue +4 -1
  14. package/lib/components/select/VtsOption.vue +10 -6
  15. package/lib/components/select/VtsSelect.vue +74 -50
  16. package/lib/components/state-hero/VtsAllDoneHero.vue +4 -1
  17. package/lib/components/state-hero/VtsAllGoodHero.vue +4 -1
  18. package/lib/components/state-hero/VtsComingSoonHero.vue +4 -1
  19. package/lib/components/state-hero/VtsErrorNoDataHero.vue +4 -1
  20. package/lib/components/state-hero/VtsLoadingHero.vue +4 -1
  21. package/lib/components/state-hero/VtsNoDataHero.vue +4 -1
  22. package/lib/components/state-hero/VtsNoSelectionHero.vue +4 -1
  23. package/lib/components/state-hero/VtsObjectNotFoundHero.vue +4 -1
  24. package/lib/components/state-hero/VtsOfflineHero.vue +4 -1
  25. package/lib/components/state-hero/VtsPageNotFoundHero.vue +4 -1
  26. package/lib/components/table/ColumnTitle.vue +2 -2
  27. package/lib/components/task/VtsQuickTaskButton.vue +4 -1
  28. package/lib/components/task/VtsQuickTaskList.vue +5 -2
  29. package/lib/components/task/VtsQuickTaskTabBar.vue +8 -5
  30. package/lib/components/ui/card-numbers/UiCardNumbers.vue +4 -1
  31. package/lib/components/ui/character-limit/UiCharacterLimit.vue +4 -1
  32. package/lib/components/ui/input/UiInput.vue +2 -2
  33. package/lib/components/ui/label/UiLabel.vue +4 -1
  34. package/lib/components/ui/progress-bar/UiProgressBar.vue +5 -2
  35. package/lib/components/ui/query-search-bar/UiQuerySearchBar.vue +9 -6
  36. package/lib/components/ui/quick-task-item/UiQuickTaskItem.vue +6 -3
  37. package/lib/components/ui/stacked-bar/StackedBarSegment.vue +4 -1
  38. package/lib/components/ui/table-pagination/UiTablePagination.vue +6 -3
  39. package/lib/components/ui/text-area/UiTextarea.vue +4 -1
  40. package/lib/components/ui/top-bottom-table/UiTopBottomTable.vue +6 -3
  41. package/lib/components/ui/tree-item-label/UiTreeItemLabel.vue +4 -1
  42. package/lib/composables/local-time-ago.composable.ts +53 -0
  43. package/lib/composables/locale-time-ago.composable.ts +53 -0
  44. package/lib/icons/fa-icons.ts +164 -0
  45. package/lib/icons/index.ts +15 -0
  46. package/lib/icons/legacy-icons.ts +80 -0
  47. package/lib/icons/object-icons.ts +187 -0
  48. package/lib/layouts/CoreLayout.vue +7 -3
  49. package/lib/locales/cs.json +0 -1
  50. package/lib/locales/de.json +1 -1
  51. package/lib/locales/en.json +32 -4
  52. package/lib/locales/es.json +1 -1
  53. package/lib/locales/fr.json +31 -3
  54. package/lib/locales/it.json +1 -1
  55. package/lib/locales/nl.json +1 -1
  56. package/lib/locales/ru.json +1 -1
  57. package/lib/locales/sv.json +1 -2
  58. package/lib/packages/collection/README.md +23 -18
  59. package/lib/packages/collection/create-collection.ts +22 -21
  60. package/lib/packages/collection/create-item.ts +21 -20
  61. package/lib/packages/collection/create-use-subset.ts +23 -0
  62. package/lib/packages/collection/guess-item-id.ts +26 -16
  63. package/lib/packages/collection/index.ts +4 -0
  64. package/lib/packages/collection/types.ts +65 -37
  65. package/lib/packages/collection/use-collection.ts +68 -18
  66. package/lib/packages/collection/use-flag-registry.ts +38 -17
  67. package/lib/packages/form-select/guess-label.ts +45 -0
  68. package/lib/packages/form-select/guess-value.ts +23 -0
  69. package/lib/packages/form-select/index.ts +6 -0
  70. package/lib/packages/form-select/normalize-search-term.ts +11 -0
  71. package/lib/packages/form-select/types.ts +90 -42
  72. package/lib/packages/form-select/use-form-option-controller.ts +7 -3
  73. package/lib/packages/form-select/use-form-select-controller.ts +38 -27
  74. package/lib/packages/form-select/use-form-select-keyboard-navigation.ts +1 -1
  75. package/lib/packages/form-select/use-form-select.ts +308 -130
  76. package/lib/packages/icon/DisplayIcon.vue +25 -0
  77. package/lib/packages/icon/DisplayIconAny.vue +16 -0
  78. package/lib/packages/icon/DisplayIconSingle.vue +35 -0
  79. package/lib/packages/icon/DisplayIconStack.vue +34 -0
  80. package/lib/packages/icon/README.md +286 -0
  81. package/lib/packages/icon/create-icon-bindings.ts +27 -0
  82. package/lib/packages/icon/define-icon-pack.ts +23 -0
  83. package/lib/packages/icon/define-icon-single.ts +17 -0
  84. package/lib/packages/icon/define-icon-stack.ts +20 -0
  85. package/lib/packages/icon/define-icon.ts +40 -0
  86. package/lib/packages/icon/generate-icon-variants.ts +17 -0
  87. package/lib/packages/icon/index.ts +8 -0
  88. package/lib/packages/icon/is-icon-stack.ts +5 -0
  89. package/lib/packages/icon/merge-icons.ts +25 -0
  90. package/lib/packages/icon/merge-transforms.ts +12 -0
  91. package/lib/packages/icon/normalize-icon.ts +25 -0
  92. package/lib/packages/icon/to-tuple.ts +7 -0
  93. package/lib/packages/icon/types.ts +72 -0
  94. package/lib/packages/job/README.md +2 -2
  95. package/lib/packages/mapper/README.md +166 -0
  96. package/lib/packages/mapper/convert-to-map.ts +5 -0
  97. package/lib/packages/mapper/create-mapper.ts +30 -0
  98. package/lib/packages/mapper/index.ts +4 -0
  99. package/lib/packages/mapper/types.ts +1 -0
  100. package/lib/packages/mapper/use-mapper.ts +31 -0
  101. package/lib/stores/sidebar.store.ts +1 -1
  102. package/lib/types/chart.ts +2 -2
  103. package/lib/types/utility.type.ts +9 -0
  104. package/lib/utils/object.util.ts +16 -0
  105. package/lib/utils/size.util.ts +4 -2
  106. package/package.json +2 -1
  107. package/lib/components/backup-item/VtsBackupItem.vue +0 -47
  108. package/lib/composables/mapper.composable.md +0 -74
  109. package/lib/composables/mapper.composable.ts +0 -18
@@ -1,29 +1,32 @@
1
1
  <template>
2
- <VtsIcon :accent="color" :icon class="vts-backup-state" />
2
+ <UiInfo :accent="state.accent" class="vts-backup-state">
3
+ {{ state.text }}
4
+ </UiInfo>
3
5
  </template>
4
6
 
5
7
  <script lang="ts" setup>
6
- import VtsIcon from '@core/components/icon/VtsIcon.vue'
7
- import type { IconDefinition } from '@fortawesome/fontawesome-common-types'
8
- import { faCheckCircle, faCircleMinus, faCircleXmark } from '@fortawesome/free-solid-svg-icons'
9
- import { computed } from 'vue'
8
+ import UiInfo, { type InfoAccent } from '@core/components/ui/info/UiInfo.vue'
9
+ import { useMapper } from '@core/packages/mapper'
10
+ import { useI18n } from 'vue-i18n'
10
11
 
11
- export type BackupState = 'success' | 'failure' | 'partial'
12
-
13
- type Props = {
12
+ const { state: _state } = defineProps<{
14
13
  state: BackupState
15
- }
14
+ }>()
16
15
 
17
- const props = defineProps<Props>()
16
+ const { t } = useI18n()
18
17
 
19
- const states: Record<Props['state'], { icon: IconDefinition; color: 'success' | 'warning' | 'danger' }> = {
20
- success: { icon: faCheckCircle, color: 'success' },
21
- partial: { icon: faCircleMinus, color: 'warning' },
22
- failure: { icon: faCircleXmark, color: 'danger' },
23
- }
18
+ type BackupState = 'success' | 'failure' | 'skipped' | 'interrupted'
24
19
 
25
- const icon = computed(() => states[props.state].icon)
26
- const color = computed(() => states[props.state].color)
20
+ const state = useMapper<BackupState, { text: string; accent: InfoAccent }>(
21
+ () => _state,
22
+ {
23
+ success: { text: t('success'), accent: 'success' },
24
+ failure: { text: t('failure'), accent: 'danger' },
25
+ skipped: { text: t('skipped'), accent: 'warning' },
26
+ interrupted: { text: t('interrupted'), accent: 'danger' },
27
+ },
28
+ 'failure'
29
+ )
27
30
  </script>
28
31
 
29
32
  <style lang="postcss" scoped>
@@ -15,7 +15,7 @@
15
15
  accent="brand"
16
16
  @click="copy(id)"
17
17
  >
18
- {{ copied ? $t('core.copied') : $t('core.copy-id') }}
18
+ {{ copied ? t('core.copied') : t('core.copy-id') }}
19
19
  </UiButton>
20
20
  </template>
21
21
  </div>
@@ -27,12 +27,15 @@ import UiButton from '@core/components/ui/button/UiButton.vue'
27
27
  import { vTooltip } from '@core/directives/tooltip.directive'
28
28
  import { faCopy } from '@fortawesome/free-solid-svg-icons'
29
29
  import { useClipboard } from '@vueuse/core'
30
+ import { useI18n } from 'vue-i18n'
30
31
 
31
32
  defineProps<{
32
33
  id?: string
33
34
  copiableId?: boolean
34
35
  }>()
35
36
 
37
+ const { t } = useI18n()
38
+
36
39
  const { isSupported, copy, copied } = useClipboard()
37
40
  </script>
38
41
 
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <UiCardTitle>{{ $t('console-actions') }}</UiCardTitle>
2
+ <UiCardTitle>{{ t('console-actions') }}</UiCardTitle>
3
3
  <UiButton
4
4
  class="button"
5
5
  accent="brand"
@@ -8,7 +8,7 @@
8
8
  :left-icon="isFullscreen ? faDownLeftAndUpRightToCenter : faUpRightAndDownLeftFromCenter"
9
9
  @click="toggleFullScreen"
10
10
  >
11
- {{ $t(isFullscreen ? 'exit-fullscreen' : 'fullscreen') }}
11
+ {{ t(isFullscreen ? 'exit-fullscreen' : 'fullscreen') }}
12
12
  </UiButton>
13
13
  <UiButton
14
14
  class="button"
@@ -18,7 +18,7 @@
18
18
  :left-icon="faArrowUpRightFromSquare"
19
19
  @click="openInNewTab"
20
20
  >
21
- {{ $t('open-console-in-new-tab') }}
21
+ {{ t('open-console-in-new-tab') }}
22
22
  </UiButton>
23
23
  <UiButton
24
24
  class="button"
@@ -28,7 +28,7 @@
28
28
  :left-icon="faKeyboard"
29
29
  @click="sendCtrlAltDel"
30
30
  >
31
- {{ $t('send-ctrl-alt-del') }}
31
+ {{ t('send-ctrl-alt-del') }}
32
32
  </UiButton>
33
33
  </template>
34
34
 
@@ -45,12 +45,15 @@ import {
45
45
  import { useActiveElement, useMagicKeys, whenever } from '@vueuse/core'
46
46
  import { logicAnd } from '@vueuse/math'
47
47
  import { computed } from 'vue'
48
+ import { useI18n } from 'vue-i18n'
48
49
  import { useRouter } from 'vue-router'
49
50
 
50
51
  defineProps<{
51
52
  sendCtrlAltDel: () => void
52
53
  }>()
53
54
 
55
+ const { t } = useI18n()
56
+
54
57
  const router = useRouter()
55
58
  const uiStore = useUiStore()
56
59
 
@@ -1,13 +1,13 @@
1
1
  <template>
2
2
  <div class="vts-clipboard-console">
3
- <UiCardTitle>{{ $t('console-clipboard') }}</UiCardTitle>
4
- <UiTextarea v-tooltip="$t('coming-soon')" accent="brand" disabled :model-value="modelValue" />
3
+ <UiCardTitle>{{ t('console-clipboard') }}</UiCardTitle>
4
+ <UiTextarea v-tooltip="t('coming-soon')" accent="brand" disabled :model-value="modelValue" />
5
5
  <div class="buttons-container">
6
- <UiButton v-tooltip="$t('coming-soon')" accent="brand" variant="primary" size="medium" disabled>
7
- {{ $t('send') }}
6
+ <UiButton v-tooltip="t('coming-soon')" accent="brand" variant="primary" size="medium" disabled>
7
+ {{ t('send') }}
8
8
  </UiButton>
9
- <UiButton v-tooltip="$t('coming-soon')" accent="brand" variant="secondary" size="medium" disabled>
10
- {{ $t('receive') }}
9
+ <UiButton v-tooltip="t('coming-soon')" accent="brand" variant="secondary" size="medium" disabled>
10
+ {{ t('receive') }}
11
11
  </UiButton>
12
12
  </div>
13
13
  </div>
@@ -19,8 +19,11 @@ import UiCardTitle from '@core/components/ui/card-title/UiCardTitle.vue'
19
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
+ import { useI18n } from 'vue-i18n'
22
23
 
23
24
  const modelValue = ref('')
25
+
26
+ const { t } = useI18n()
24
27
  </script>
25
28
 
26
29
  <style lang="postcss" scoped>
@@ -1,29 +1,22 @@
1
1
  <template>
2
- <UiButtonIcon v-tooltip="copied && $t('core.copied')" :icon size="medium" accent="brand" @click="copyToClipboard()" />
2
+ <UiButtonIcon v-tooltip="copied && t('core.copied')" :icon size="medium" accent="brand" @click="copy()" />
3
3
  </template>
4
4
 
5
5
  <script setup lang="ts">
6
6
  import UiButtonIcon from '@core/components/ui/button-icon/UiButtonIcon.vue'
7
7
  import { vTooltip } from '@core/directives/tooltip.directive'
8
8
  import { faCheckCircle, faCopy } from '@fortawesome/free-solid-svg-icons'
9
- import { useClipboard, useTimeoutFn } from '@vueuse/core'
10
- import { ref } from 'vue'
9
+ import { useClipboard } from '@vueuse/core'
10
+ import { computed } from 'vue'
11
+ import { useI18n } from 'vue-i18n'
11
12
 
12
13
  const { value } = defineProps<{
13
14
  value: string
14
15
  }>()
15
16
 
16
- const { copy, copied } = useClipboard()
17
+ const { t } = useI18n()
17
18
 
18
- const icon = ref(faCopy)
19
+ const { copy, copied } = useClipboard({ source: () => value })
19
20
 
20
- const { start: changeIcon } = useTimeoutFn(() => {
21
- icon.value = faCopy
22
- }, 1_500) // 1.5s is time to toltips is visible
23
-
24
- function copyToClipboard() {
25
- copy(value)
26
- icon.value = faCheckCircle
27
- changeIcon()
28
- }
21
+ const icon = computed(() => (copied.value ? faCheckCircle : faCopy))
29
22
  </script>
@@ -10,10 +10,10 @@
10
10
  </div>
11
11
  <div v-if="onToggleSelectAll" class="buttons">
12
12
  <span v-if="selected !== 'all'" @click="emit('toggleSelectAll', true)">
13
- {{ $t('core.select.all') }}
13
+ {{ t('core.select.all') }}
14
14
  </span>
15
15
  <span v-if="selected !== 'none'" @click="emit('toggleSelectAll', false)">
16
- {{ $t('core.select.none') }}
16
+ {{ t('core.select.none') }}
17
17
  </span>
18
18
  </div>
19
19
  </div>
@@ -22,6 +22,7 @@
22
22
  <script lang="ts" setup>
23
23
  import VtsIcon from '@core/components/icon/VtsIcon.vue'
24
24
  import type { IconDefinition } from '@fortawesome/fontawesome-common-types'
25
+ import { useI18n } from 'vue-i18n'
25
26
 
26
27
  withDefaults(
27
28
  defineProps<{
@@ -35,6 +36,8 @@ withDefaults(
35
36
  const emit = defineEmits<{
36
37
  toggleSelectAll: [value: boolean]
37
38
  }>()
39
+
40
+ const { t } = useI18n()
38
41
  </script>
39
42
 
40
43
  <style lang="postcss" scoped>
@@ -0,0 +1,49 @@
1
+ <template>
2
+ <DisplayIcon v-if="icon" class="vts-icon" :class="className" :icon />
3
+ </template>
4
+
5
+ <script lang="ts" setup>
6
+ import { type IconName, icons } from '@core/icons'
7
+ import { DisplayIcon } from '@core/packages/icon'
8
+ import { toVariants } from '@core/utils/to-variants.util.ts'
9
+ import { computed } from 'vue'
10
+
11
+ export type IconSize = 'small' | 'medium' | 'large' | 'current'
12
+
13
+ const { size, name } = defineProps<{
14
+ size: IconSize
15
+ name: IconName
16
+ }>()
17
+
18
+ const className = computed(() =>
19
+ toVariants({
20
+ size: size === 'current' ? undefined : size,
21
+ })
22
+ )
23
+
24
+ const icon = computed(() => {
25
+ const icon = icons[name]
26
+
27
+ if (icon === undefined) {
28
+ console.warn(`Icon "${name}" not found.`)
29
+ }
30
+
31
+ return icon
32
+ })
33
+ </script>
34
+
35
+ <style lang="postcss" scoped>
36
+ .vts-icon {
37
+ &.size--small {
38
+ font-size: 1.2rem;
39
+ }
40
+
41
+ &.size--medium {
42
+ font-size: 1.6rem;
43
+ }
44
+
45
+ &.size--large {
46
+ font-size: 2rem;
47
+ }
48
+ }
49
+ </style>
@@ -0,0 +1,41 @@
1
+ <template>
2
+ <div class="vts-input-group">
3
+ <slot />
4
+ </div>
5
+ </template>
6
+
7
+ <style lang="postcss" scoped>
8
+ .vts-input-group {
9
+ display: flex;
10
+ align-items: center;
11
+
12
+ :slotted(> .ui-input),
13
+ :slotted(> .vts-select) {
14
+ &:hover {
15
+ z-index: 1;
16
+ }
17
+
18
+ &:focus-within {
19
+ z-index: 2;
20
+ }
21
+
22
+ &:not(:first-child) {
23
+ margin-left: -1px;
24
+
25
+ &,
26
+ .ui-input {
27
+ border-start-start-radius: 0;
28
+ border-end-start-radius: 0;
29
+ }
30
+ }
31
+
32
+ &:not(:last-child) {
33
+ &,
34
+ .ui-input {
35
+ border-start-end-radius: 0;
36
+ border-end-end-radius: 0;
37
+ }
38
+ }
39
+ }
40
+ }
41
+ </style>
@@ -20,8 +20,8 @@
20
20
  <script lang="ts" setup>
21
21
  import UiInfo, { type InfoAccent } from '@core/components/ui/info/UiInfo.vue'
22
22
  import UiLabel, { type LabelAccent } from '@core/components/ui/label/UiLabel.vue'
23
- import { useMapper } from '@core/composables/mapper.composable'
24
23
  import { useRanked } from '@core/composables/ranked.composable.ts'
24
+ import { useMapper } from '@core/packages/mapper/use-mapper.ts'
25
25
  import type { MaybeArray } from '@core/types/utility.type'
26
26
  import { IK_INPUT_WRAPPER_CONTROLLER } from '@core/utils/injection-keys.util'
27
27
  import { toArray } from '@core/utils/to-array.utils'
@@ -70,7 +70,7 @@ const labelAccent = useMapper<InfoAccent, LabelAccent>(
70
70
  danger: 'danger',
71
71
  muted: 'neutral',
72
72
  },
73
- 'neutral'
73
+ 'info'
74
74
  )
75
75
 
76
76
  const wrapperController = reactive({
@@ -6,12 +6,12 @@
6
6
  <div v-if="!ui.isMobile" class="lock">
7
7
  <UiButtonIcon
8
8
  v-tooltip="{
9
- content: sidebar.isLocked ? $t('core.sidebar.unlock') : $t('core.sidebar.lock'),
9
+ content: sidebar.isLocked ? t('core.sidebar.unlock') : t('core.sidebar.lock'),
10
10
  placement: 'right',
11
11
  }"
12
12
  accent="brand"
13
13
  size="medium"
14
- :icon="sidebar.isLocked ? faLock : faLockOpen"
14
+ :icon="sidebar.isLocked ? faThumbTackSlash : faThumbTack"
15
15
  @click="sidebar.toggleLock()"
16
16
  />
17
17
  </div>
@@ -38,7 +38,8 @@ import UiButtonIcon from '@core/components/ui/button-icon/UiButtonIcon.vue'
38
38
  import { vTooltip } from '@core/directives/tooltip.directive'
39
39
  import { useSidebarStore } from '@core/stores/sidebar.store'
40
40
  import { useUiStore } from '@core/stores/ui.store'
41
- import { faLock, faLockOpen } from '@fortawesome/free-solid-svg-icons'
41
+ import { faThumbTack, faThumbTackSlash } from '@fortawesome/free-solid-svg-icons'
42
+ import { useI18n } from 'vue-i18n'
42
43
 
43
44
  const slots = defineSlots<{
44
45
  default(): any
@@ -46,6 +47,8 @@ const slots = defineSlots<{
46
47
  footer?(): any
47
48
  }>()
48
49
 
50
+ const { t } = useI18n()
51
+
49
52
  const sidebar = useSidebarStore()
50
53
  const ui = useUiStore()
51
54
  </script>
@@ -29,6 +29,10 @@ const valueFormatter = computed<ValueFormatter>(() => {
29
29
  const formatter = _valueFormatter
30
30
 
31
31
  return value => {
32
+ if (value === null) {
33
+ return ''
34
+ }
35
+
32
36
  if (formatter === undefined) {
33
37
  return value.toString()
34
38
  }
@@ -0,0 +1,22 @@
1
+ <template>
2
+ <NewVtsIcon :name="iconName" :size />
3
+ </template>
4
+
5
+ <script generic="TType extends ObjectIconType" lang="ts" setup>
6
+ import NewVtsIcon, { type IconSize } from '@core/components/icon/NewVtsIcon.vue'
7
+ import type { ObjectIconName } from '@core/icons'
8
+ import { computed } from 'vue'
9
+
10
+ export type ObjectIconType = ObjectIconName extends `object:${infer TType}:${string}` ? TType : never
11
+
12
+ export type ObjectIconState<TType extends ObjectIconType> =
13
+ Extract<ObjectIconName, `object:${TType}:${string}`> extends `object:${TType}:${infer TState}` ? TState : never
14
+
15
+ const { type, state } = defineProps<{
16
+ type: TType
17
+ state: ObjectIconState<TType>
18
+ size: IconSize
19
+ }>()
20
+
21
+ const iconName = computed(() => `object:${type}:${state}` as ObjectIconName)
22
+ </script>
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <UiCard class="vts-quick-info-card">
3
- <UiCardTitle>{{ $t('quick-info') }}</UiCardTitle>
3
+ <UiCardTitle>{{ t('quick-info') }}</UiCardTitle>
4
4
  <VtsLoadingHero v-if="loading" type="card" />
5
5
  <div v-else class="info-container">
6
6
  <slot />
@@ -12,10 +12,13 @@
12
12
  import VtsLoadingHero from '@core/components/state-hero/VtsLoadingHero.vue'
13
13
  import UiCard from '@core/components/ui/card/UiCard.vue'
14
14
  import UiCardTitle from '@core/components/ui/card-title/UiCardTitle.vue'
15
+ import { useI18n } from 'vue-i18n'
15
16
 
16
17
  defineProps<{
17
18
  loading: boolean
18
19
  }>()
20
+
21
+ const { t } = useI18n()
19
22
  </script>
20
23
 
21
24
  <style lang="postcss" scoped>
@@ -1,24 +1,28 @@
1
1
  <template>
2
2
  <UiDropdown
3
3
  ref="elementRef"
4
- :checkbox="option.properties.multiple"
4
+ :accent
5
+ :checkbox="isMultiple"
5
6
  :disabled="option.properties.disabled"
6
7
  :hover="option.flags.active"
7
8
  :selected="option.flags.selected"
8
- accent="normal"
9
9
  >
10
10
  <slot>{{ option.properties.label }}</slot>
11
11
  </UiDropdown>
12
12
  </template>
13
13
 
14
- <script generic="TOption extends FormOption" lang="ts" setup>
15
- import UiDropdown from '@core/components/ui/dropdown/UiDropdown.vue'
16
- import type { FormOption } from '@core/packages/form-select/types.ts'
17
- import { useFormOptionController } from '@core/packages/form-select/use-form-option-controller.ts'
14
+ <script generic="TOption extends FormOption<{ accent?: DropdownAccent }>" lang="ts" setup>
15
+ import UiDropdown, { type DropdownAccent } from '@core/components/ui/dropdown/UiDropdown.vue'
16
+ import { type FormOption, IK_FORM_SELECT_CONTROLLER, useFormOptionController } from '@core/packages/form-select'
17
+ import { computed, inject } from 'vue'
18
18
 
19
19
  const { option } = defineProps<{
20
20
  option: TOption
21
21
  }>()
22
22
 
23
+ const accent = computed(() => option.properties.accent ?? 'normal')
24
+
23
25
  const { elementRef } = useFormOptionController(() => option)
26
+
27
+ const { isMultiple } = inject(IK_FORM_SELECT_CONTROLLER)!
24
28
  </script>
@@ -1,78 +1,101 @@
1
1
  <template>
2
2
  <div :class="className" class="vts-select">
3
- <VtsBackdrop v-if="isOpen" />
4
-
5
3
  <UiInput
6
4
  ref="triggerRef"
7
5
  :accent
6
+ :disabled="isDisabled"
7
+ :icon
8
8
  :model-value="selectedLabel"
9
9
  :placeholder
10
- :required
10
+ :required="isRequired"
11
11
  :right-icon="faAngleDown"
12
12
  readonly
13
13
  />
14
14
 
15
- <UiDropdownList v-if="isOpen" ref="dropdownRef" :style="floatingStyles" class="dropdown-list">
16
- <template v-if="searchTerm !== undefined" #before>
17
- <div class="search-container">
18
- <UiInput
19
- ref="searchRef"
20
- v-model="searchTerm"
21
- :placeholder="searchPlaceholder"
22
- :right-icon="faMagnifyingGlass"
23
- accent="brand"
24
- />
25
- </div>
26
- </template>
27
- <UiDropdown v-if="loading || options.length === 0" accent="normal" disabled>
28
- {{ loading ? t('loading-in-progress') : t('no-results') }}
29
- </UiDropdown>
30
- <template v-for="option of options" :key="option.id">
31
- <slot :option>
32
- <VtsOption :option />
33
- </slot>
34
- </template>
35
- </UiDropdownList>
15
+ <Teleport v-if="isOpen" to="body">
16
+ <VtsBackdrop />
17
+
18
+ <UiDropdownList ref="dropdownRef" :style="floatingStyles" class="dropdown-list">
19
+ <template v-if="isSearchable" #before>
20
+ <div class="search-container">
21
+ <UiInput
22
+ ref="searchRef"
23
+ v-model="searchTerm"
24
+ :placeholder="searchPlaceholder"
25
+ :right-icon="faMagnifyingGlass"
26
+ accent="brand"
27
+ />
28
+ </div>
29
+ </template>
30
+ <UiDropdown v-if="isLoading || options.length === 0" accent="normal" disabled>
31
+ {{ isLoading ? t('loading-in-progress') : t('no-results') }}
32
+ </UiDropdown>
33
+ <template v-for="option of options" :key="option.id">
34
+ <slot :option="option as FormSelectIdToOption<TSelectId>">
35
+ <VtsOption :option />
36
+ </slot>
37
+ </template>
38
+ </UiDropdownList>
39
+ </Teleport>
36
40
  </div>
37
41
  </template>
38
42
 
39
- <script generic="TOption extends FormOption" lang="ts" setup>
43
+ <script generic="TSelectId extends FormSelectId" lang="ts" setup>
40
44
  import VtsBackdrop from '@core/components/backdrop/VtsBackdrop.vue'
41
45
  import VtsOption from '@core/components/select/VtsOption.vue'
42
46
  import UiDropdown from '@core/components/ui/dropdown/UiDropdown.vue'
43
47
  import UiDropdownList from '@core/components/ui/dropdown/UiDropdownList.vue'
44
48
  import UiInput from '@core/components/ui/input/UiInput.vue'
45
- import type { FormOption } from '@core/packages/form-select/types.ts'
46
- import { useFormSelectController } from '@core/packages/form-select/use-form-select-controller.ts'
49
+ import {
50
+ type FormSelect,
51
+ type FormSelectId,
52
+ type FormSelectIdToOption,
53
+ useFormSelectController,
54
+ } from '@core/packages/form-select'
47
55
  import { toVariants } from '@core/utils/to-variants.util.ts'
56
+ import type { IconDefinition } from '@fortawesome/fontawesome-common-types'
48
57
  import { faAngleDown, faMagnifyingGlass } from '@fortawesome/free-solid-svg-icons'
49
- import { computed } from 'vue'
58
+ import { useCurrentElement, useElementSize } from '@vueuse/core'
59
+ import { computed, inject } from 'vue'
50
60
  import { useI18n } from 'vue-i18n'
51
61
 
52
- const { accent, options, selectedLabel } = defineProps<{
62
+ const { accent, id } = defineProps<{
53
63
  accent: 'brand' | 'warning' | 'danger'
54
- options: TOption[]
55
- selectedLabel: string
56
- required?: boolean
57
- placeholder?: string
58
- searchPlaceholder?: string
59
- loading?: boolean
64
+ id: TSelectId
65
+ icon?: IconDefinition
60
66
  }>()
61
67
 
62
- const searchTerm = defineModel<string>('search')
63
-
64
68
  defineSlots<{
65
- default(props: { option: TOption }): any
69
+ default(props: { option: FormSelectIdToOption<TSelectId> }): any
66
70
  }>()
67
71
 
68
72
  const { t } = useI18n()
69
73
 
70
- const { triggerRef, dropdownRef, searchRef, isOpen, floatingStyles } = useFormSelectController({
71
- options: () => options,
74
+ const select = inject(id)
75
+
76
+ if (!select) {
77
+ throw new Error(`No select configuration has been found for this ID`)
78
+ }
79
+
80
+ const {
81
+ options,
72
82
  searchTerm,
73
- })
83
+ selectedLabel,
84
+ isSearchable,
85
+ isDisabled,
86
+ isRequired,
87
+ placeholder,
88
+ searchPlaceholder,
89
+ isLoading,
90
+ } = select as FormSelect
91
+
92
+ const { triggerRef, dropdownRef, searchRef, isOpen, floatingStyles } = useFormSelectController(select as FormSelect)
74
93
 
75
94
  const className = computed(() => toVariants({ accent }))
95
+
96
+ const { width } = useElementSize(useCurrentElement())
97
+
98
+ const minWidth = computed(() => `${width.value}px`)
76
99
  </script>
77
100
 
78
101
  <style lang="postcss" scoped>
@@ -80,17 +103,18 @@ const className = computed(() => toVariants({ accent }))
80
103
  .ui-input:deep(input) {
81
104
  cursor: default;
82
105
  }
106
+ }
83
107
 
84
- .dropdown-list {
85
- min-width: 40rem;
86
- max-height: 36.2rem; /* 8 Dropdown items */
87
- overflow: auto;
88
- z-index: 1020;
108
+ /* Teleported */
109
+ .dropdown-list {
110
+ min-width: v-bind(minWidth);
111
+ max-height: 36.2rem; /* 8 Dropdown items */
112
+ overflow: auto;
113
+ z-index: 1020;
89
114
 
90
- .search-container {
91
- background-color: var(--color-neutral-background-primary);
92
- padding: 0.4rem;
93
- }
115
+ .search-container {
116
+ background-color: var(--color-neutral-background-primary);
117
+ padding: 0.4rem;
94
118
  }
95
119
  }
96
120
  </style>
@@ -1,13 +1,16 @@
1
1
  <template>
2
2
  <VtsStateHero :type class="vts-all-done-hero" image="all-done">
3
- {{ $t('all-done') }}
3
+ {{ t('all-done') }}
4
4
  </VtsStateHero>
5
5
  </template>
6
6
 
7
7
  <script lang="ts" setup>
8
8
  import VtsStateHero, { type StateHeroType } from '@core/components/state-hero/VtsStateHero.vue'
9
+ import { useI18n } from 'vue-i18n'
9
10
 
10
11
  defineProps<{
11
12
  type: StateHeroType
12
13
  }>()
14
+
15
+ const { t } = useI18n()
13
16
  </script>