@xen-orchestra/web-core 0.0.2 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/env.d.ts +1 -0
  2. package/lib/assets/css/_typography.pcss +1 -0
  3. package/lib/assets/css/typography/_utils.pcss +6 -0
  4. package/lib/assets/no-result.svg +81 -0
  5. package/lib/assets/under-construction.svg +195 -0
  6. package/lib/components/CardNumbers.vue +94 -0
  7. package/lib/components/DonutChart.vue +92 -0
  8. package/lib/components/LegendTitle.vue +31 -0
  9. package/lib/components/StatusPill.vue +2 -2
  10. package/lib/components/UiCard.vue +29 -0
  11. package/lib/components/UiLegend.vue +68 -0
  12. package/lib/components/UiTag.vue +3 -7
  13. package/lib/components/backdrop/Backdrop.vue +11 -0
  14. package/lib/components/card/CardSubtitle.vue +24 -0
  15. package/lib/components/card/CardTitle.vue +52 -0
  16. package/lib/components/cell-object/CellObject.vue +54 -0
  17. package/lib/components/cell-text/CellText.vue +40 -0
  18. package/lib/components/chip/UiChip.vue +5 -6
  19. package/lib/components/console/RemoteConsole.vue +2 -2
  20. package/lib/components/divider/Divider.vue +25 -0
  21. package/lib/components/dropdown/DropdownItem.vue +1 -4
  22. package/lib/components/dropdown/DropdownTitle.vue +7 -3
  23. package/lib/components/head-bar/HeadBar.vue +78 -0
  24. package/lib/components/icon/VmIcon.vue +1 -1
  25. package/lib/components/input/UiInput.vue +133 -0
  26. package/lib/components/menu/MenuItem.vue +1 -1
  27. package/lib/components/menu/MenuList.vue +5 -5
  28. package/lib/components/object-link/ObjectLink.vue +87 -0
  29. package/lib/components/search-bar/SearchBar.vue +60 -0
  30. package/lib/components/stacked-bar/StackedBar.vue +49 -0
  31. package/lib/components/stacked-bar/StackedBarSegment.vue +67 -0
  32. package/lib/components/state-hero/ComingSoonHero.vue +9 -0
  33. package/lib/components/state-hero/LoadingHero.vue +9 -0
  34. package/lib/components/state-hero/ObjectNotFoundHero.vue +13 -0
  35. package/lib/components/state-hero/StateHero.vue +53 -0
  36. package/lib/components/tab/TabList.vue +1 -0
  37. package/lib/components/table/ColumnTitle.vue +150 -0
  38. package/lib/components/table/UiTable.vue +64 -0
  39. package/lib/components/task/QuickTaskButton.vue +62 -0
  40. package/lib/components/task/QuickTaskItem.vue +91 -0
  41. package/lib/components/task/QuickTaskList.vue +48 -0
  42. package/lib/components/task/QuickTaskPanel.vue +65 -0
  43. package/lib/components/task/QuickTaskTabBar.vue +46 -0
  44. package/lib/components/tooltip/TooltipList.vue +0 -2
  45. package/lib/components/tree/TreeItem.vue +8 -8
  46. package/lib/components/tree/TreeItemLabel.vue +32 -16
  47. package/lib/composables/tab-list.composable.ts +33 -0
  48. package/lib/composables/tree/branch.ts +5 -5
  49. package/lib/composables/tree/types.ts +1 -1
  50. package/lib/i18n.ts +86 -1
  51. package/lib/layouts/CoreLayout.vue +6 -106
  52. package/lib/locales/de.json +37 -4
  53. package/lib/locales/en.json +66 -13
  54. package/lib/locales/fa.json +46 -0
  55. package/lib/locales/fr.json +66 -13
  56. package/lib/types/tab.type.ts +17 -0
  57. package/lib/types/task.type.ts +13 -0
  58. package/lib/types/utility.type.ts +2 -0
  59. package/lib/utils/if-else.utils.ts +1 -1
  60. package/lib/utils/open-url.utils.ts +3 -0
  61. package/package.json +16 -8
@@ -4,7 +4,7 @@
4
4
  <slot name="icon">
5
5
  <UiIcon :icon fixed-width />
6
6
  </slot>
7
- <span class="content"><slot /></span>
7
+ <span class="text-ellipsis"><slot /></span>
8
8
  </span>
9
9
  </template>
10
10
 
@@ -78,7 +78,7 @@ withDefaults(
78
78
 
79
79
  /* IMPLEMENTATION */
80
80
  .ui-tag {
81
- display: inline-flex;
81
+ display: flex;
82
82
  justify-content: center;
83
83
  align-items: center;
84
84
  gap: 0.8rem;
@@ -88,10 +88,6 @@ withDefaults(
88
88
  padding: 0.2rem 0.8rem;
89
89
  border-radius: 0.4rem;
90
90
  vertical-align: middle;
91
-
92
- .content {
93
- overflow: hidden;
94
- text-overflow: ellipsis;
95
- }
91
+ min-width: 0;
96
92
  }
97
93
  </style>
@@ -0,0 +1,11 @@
1
+ <template>
2
+ <div class="vts-backdrop" />
3
+ </template>
4
+
5
+ <style lang="postcss" scoped>
6
+ .vts-backdrop {
7
+ position: fixed;
8
+ inset: 0;
9
+ z-index: 1000;
10
+ }
11
+ </style>
@@ -0,0 +1,24 @@
1
+ <!-- v1.0 -->
2
+ <template>
3
+ <div class="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>
6
+ </div>
7
+ </template>
8
+
9
+ <script setup lang="ts">
10
+ defineSlots<{
11
+ default: () => void
12
+ info: () => void
13
+ }>()
14
+ </script>
15
+
16
+ <style lang="postcss" scoped>
17
+ .card-subtitle {
18
+ color: var(--color-purple-base);
19
+ border-bottom: 0.1rem solid var(--color-purple-base);
20
+ display: flex;
21
+ justify-content: space-between;
22
+ gap: 0.8rem;
23
+ }
24
+ </style>
@@ -0,0 +1,52 @@
1
+ <!-- v1.0 -->
2
+ <template>
3
+ <div class="card-title">
4
+ <div class="main-content">
5
+ <div class="title typo h6-medium"><slot /></div>
6
+
7
+ <div v-if="$slots.info" class="info typo h7-semi-bold"><slot name="info" /></div>
8
+ </div>
9
+ <p v-if="$slots.description" class="description typo p3-regular">
10
+ <slot name="description" />
11
+ </p>
12
+ </div>
13
+ </template>
14
+
15
+ <script lang="ts" setup>
16
+ defineSlots<{
17
+ default: () => void
18
+ info: () => void
19
+ description: () => void
20
+ }>()
21
+ </script>
22
+
23
+ <style lang="postcss" scoped>
24
+ .card-title {
25
+ display: flex;
26
+ flex-direction: column;
27
+ }
28
+
29
+ .main-content {
30
+ display: flex;
31
+ gap: 1.6rem;
32
+ align-items: center;
33
+ }
34
+
35
+ .title {
36
+ display: flex;
37
+ align-items: center;
38
+ gap: 1.6rem;
39
+ }
40
+
41
+ .info {
42
+ margin-left: auto;
43
+ display: flex;
44
+ align-items: center;
45
+ gap: 0.8rem;
46
+ color: var(--color-purple-base);
47
+ }
48
+
49
+ .description {
50
+ color: var(--color-grey-300);
51
+ }
52
+ </style>
@@ -0,0 +1,54 @@
1
+ <!-- v1.0 -->
2
+ <template>
3
+ <td class="cell-object">
4
+ <div class="data">
5
+ <slot />
6
+ <template v-if="id !== undefined">
7
+ <span v-tooltip class="id typo p4-regular-italic text-ellipsis">
8
+ {{ id }}
9
+ </span>
10
+ <UiButton
11
+ v-if="isSupported && copiableId"
12
+ :left-icon="faCopy"
13
+ level="secondary"
14
+ size="extra-small"
15
+ :color="copied ? 'success' : 'info'"
16
+ @click="copy(id)"
17
+ >
18
+ {{ copied ? $t('core.copied') : $t('core.copy-id') }}
19
+ </UiButton>
20
+ </template>
21
+ </div>
22
+ </td>
23
+ </template>
24
+
25
+ <script lang="ts" setup>
26
+ import UiButton from '@core/components/button/UiButton.vue'
27
+ import { vTooltip } from '@core/directives/tooltip.directive'
28
+ import { faCopy } from '@fortawesome/free-solid-svg-icons'
29
+ import { useClipboard } from '@vueuse/core'
30
+
31
+ defineProps<{
32
+ id?: string
33
+ copiableId?: boolean
34
+ }>()
35
+
36
+ const { isSupported, copy, copied } = useClipboard()
37
+ </script>
38
+
39
+ <style lang="postcss" scoped>
40
+ .cell-object {
41
+ padding: 0.8rem;
42
+ border: 0.1rem solid var(--color-grey-500);
43
+ }
44
+
45
+ .data {
46
+ display: flex;
47
+ gap: 1.6rem;
48
+ align-items: center;
49
+ }
50
+
51
+ .id {
52
+ color: var(--color-grey-300);
53
+ }
54
+ </style>
@@ -0,0 +1,40 @@
1
+ <!-- v1.0 -->
2
+ <template>
3
+ <td class="cell-text">
4
+ <div class="data typo p2-regular">
5
+ <span v-tooltip class="text-ellipsis">
6
+ <slot />
7
+ </span>
8
+ <span v-if="slots.secondary" class="info typo p4-regular">
9
+ <slot name="secondary" />
10
+ </span>
11
+ </div>
12
+ </td>
13
+ </template>
14
+
15
+ <script lang="ts" setup>
16
+ import { vTooltip } from '@core/directives/tooltip.directive'
17
+
18
+ const slots = defineSlots<{
19
+ default: () => any
20
+ secondary?: () => any
21
+ }>()
22
+ </script>
23
+
24
+ <style lang="postcss" scoped>
25
+ .cell-text {
26
+ padding: 0.8rem;
27
+ border: 0.1rem solid var(--color-grey-500);
28
+ color: var(--color-grey-000);
29
+ }
30
+
31
+ .data {
32
+ display: flex;
33
+ gap: 1.6rem;
34
+ align-items: center;
35
+ }
36
+
37
+ .info {
38
+ color: var(--color-grey-300);
39
+ }
40
+ </style>
@@ -2,7 +2,7 @@
2
2
  <template>
3
3
  <span :class="[color, { disabled }]" class="ui-chip typo p3-regular" @click="emit('edit')">
4
4
  <ChipIcon :color :disabled :icon />
5
- <span class="content">
5
+ <span class="content text-ellipsis">
6
6
  <slot />
7
7
  </span>
8
8
  <ChipRemoveIcon v-if="!disabled" :color @click.stop="emit('remove')" />
@@ -25,8 +25,8 @@ withDefaults(
25
25
  )
26
26
 
27
27
  const emit = defineEmits<{
28
- (event: 'edit'): void
29
- (event: 'remove'): void
28
+ edit: []
29
+ remove: []
30
30
  }>()
31
31
  </script>
32
32
 
@@ -113,7 +113,7 @@ const emit = defineEmits<{
113
113
 
114
114
  /* IMPLEMENTATION */
115
115
  .ui-chip {
116
- display: inline-flex;
116
+ display: flex;
117
117
  align-items: center;
118
118
  gap: 0.8rem;
119
119
  padding: 0.4rem 0.8rem;
@@ -124,6 +124,7 @@ const emit = defineEmits<{
124
124
  min-height: 2.4rem;
125
125
  vertical-align: middle;
126
126
  white-space: nowrap;
127
+ min-width: 0;
127
128
 
128
129
  &.disabled {
129
130
  pointer-events: none;
@@ -131,8 +132,6 @@ const emit = defineEmits<{
131
132
 
132
133
  .content {
133
134
  line-height: 1.6rem;
134
- overflow: hidden;
135
- text-overflow: ellipsis;
136
135
  }
137
136
  }
138
137
  </style>
@@ -1,12 +1,12 @@
1
1
  <template>
2
2
  <div :class="uiStore.isMobile ? 'mobile' : undefined" class="remote-console">
3
- <div ref="consoleContainer" class="console"></div>
3
+ <div ref="consoleContainer" class="console" />
4
4
  </div>
5
5
  </template>
6
6
 
7
7
  <script lang="ts" setup>
8
8
  import { useUiStore } from '@core/stores/ui.store'
9
- import VncClient from '@novnc/novnc/core/rfb'
9
+ import VncClient from '@novnc/novnc/lib/rfb'
10
10
  import { promiseTimeout } from '@vueuse/shared'
11
11
  import { fibonacci } from 'iterable-backoff'
12
12
  import { onBeforeUnmount, ref, watchEffect } from 'vue'
@@ -0,0 +1,25 @@
1
+ <template>
2
+ <div class="vts-divider" :class="type" />
3
+ </template>
4
+
5
+ <script setup lang="ts">
6
+ defineProps<{
7
+ type: 'stretch' | 'tab'
8
+ }>()
9
+ </script>
10
+
11
+ <style scoped lang="postcss">
12
+ .vts-divider {
13
+ border-left: 0.1rem solid var(--color-grey-500);
14
+ border-bottom: 0.1rem solid var(--color-grey-500);
15
+
16
+ &.tab {
17
+ height: 2.4rem;
18
+ align-self: center;
19
+ }
20
+
21
+ &.stretch {
22
+ align-self: stretch;
23
+ }
24
+ }
25
+ </style>
@@ -4,7 +4,7 @@
4
4
  <slot name="icon">
5
5
  <UiIcon :icon />
6
6
  </slot>
7
- <div class="label p2 medium">
7
+ <div class="label p2 medium text-ellipsis">
8
8
  <slot />
9
9
  </div>
10
10
  <div v-if="info" class="info-text p3 italic">{{ info }}</div>
@@ -181,9 +181,6 @@ const checkbox = inject(
181
181
 
182
182
  .label {
183
183
  margin-right: auto;
184
- white-space: nowrap;
185
- overflow: hidden;
186
- text-overflow: ellipsis;
187
184
  }
188
185
 
189
186
  .info-text {
@@ -9,8 +9,12 @@
9
9
  <slot />
10
10
  </div>
11
11
  <div v-if="onToggleSelectAll" class="buttons">
12
- <span v-if="selected !== 'all'" @click="emit('toggleSelectAll', true)">Select all</span>
13
- <span v-if="selected !== 'none'" @click="emit('toggleSelectAll', false)">Deselect all</span>
12
+ <span v-if="selected !== 'all'" @click="emit('toggleSelectAll', true)">
13
+ {{ $t('core.select.all') }}
14
+ </span>
15
+ <span v-if="selected !== 'none'" @click="emit('toggleSelectAll', false)">
16
+ {{ $t('core.select.none') }}
17
+ </span>
14
18
  </div>
15
19
  </div>
16
20
  </template>
@@ -29,7 +33,7 @@ withDefaults(
29
33
  )
30
34
 
31
35
  const emit = defineEmits<{
32
- (event: 'toggleSelectAll', value: boolean): void
36
+ toggleSelectAll: [value: boolean]
33
37
  }>()
34
38
  </script>
35
39
 
@@ -0,0 +1,78 @@
1
+ <!-- v1.0 -->
2
+ <template>
3
+ <div class="head-bar">
4
+ <div class="label-wrapper">
5
+ <span v-if="slots.icon || icon" class="icon">
6
+ <slot name="icon">
7
+ <UiIcon :icon />
8
+ </slot>
9
+ </span>
10
+ <h4 v-tooltip class="typo h4-medium label text-ellipsis">
11
+ <slot />
12
+ </h4>
13
+ </div>
14
+ <div v-if="slots.status" class="status typo c3-regular">
15
+ <slot name="status" />
16
+ </div>
17
+ <div v-if="slots.actions" class="actions">
18
+ <slot name="actions" />
19
+ </div>
20
+ </div>
21
+ </template>
22
+
23
+ <script lang="ts" setup>
24
+ import UiIcon from '@core/components/icon/UiIcon.vue'
25
+ import { vTooltip } from '@core/directives/tooltip.directive'
26
+ import type { IconDefinition } from '@fortawesome/fontawesome-common-types'
27
+
28
+ defineProps<{
29
+ icon?: IconDefinition
30
+ }>()
31
+
32
+ const slots = defineSlots<{
33
+ default: () => any
34
+ icon?: () => any
35
+ status?: () => any
36
+ actions?: () => any
37
+ }>()
38
+ </script>
39
+
40
+ <style lang="postcss" scoped>
41
+ .head-bar {
42
+ padding: 0.8rem 1.6rem;
43
+ display: flex;
44
+ gap: 4.8rem;
45
+ align-items: center;
46
+ border-bottom: 0.1rem solid var(--color-grey-500);
47
+ background-color: var(--background-color-primary);
48
+ }
49
+
50
+ .label-wrapper {
51
+ display: flex;
52
+ gap: 1.6rem;
53
+ align-items: center;
54
+ min-width: 0;
55
+ }
56
+
57
+ .label {
58
+ color: var(--color-grey-100);
59
+ }
60
+
61
+ .icon {
62
+ font-size: 2.4rem;
63
+ }
64
+
65
+ .status {
66
+ color: var(--color-grey-200);
67
+ display: flex;
68
+ align-items: center;
69
+ gap: 1.6rem;
70
+ }
71
+
72
+ .actions {
73
+ margin-inline-start: auto;
74
+ display: flex;
75
+ align-items: center;
76
+ gap: 0.8rem;
77
+ }
78
+ </style>
@@ -7,8 +7,8 @@
7
7
  </template>
8
8
 
9
9
  <script lang="ts" setup>
10
- import PowerStateIcon from '@core/components/PowerStateIcon.vue'
11
10
  import UiIcon from '@core/components/icon/UiIcon.vue'
11
+ import PowerStateIcon from '@core/components/PowerStateIcon.vue'
12
12
  import type { POWER_STATE } from '@core/types/power-state.type'
13
13
  import { faDisplay } from '@fortawesome/free-solid-svg-icons'
14
14
  import { FontAwesomeLayers } from '@fortawesome/vue-fontawesome'
@@ -0,0 +1,133 @@
1
+ <!-- WIP -->
2
+ <template>
3
+ <div class="ui-input">
4
+ <UiIcon :icon class="before" />
5
+ <input :id v-model.trim="modelValue" class="typo p1-regular input" type="search" v-bind="$attrs" />
6
+ <UiIcon v-if="!$attrs.disabled && modelValue" :icon="faXmark" class="after" color="info" @click="modelValue = ''" />
7
+ </div>
8
+ </template>
9
+
10
+ <script lang="ts" setup>
11
+ import UiIcon from '@core/components/icon/UiIcon.vue'
12
+ import { uniqueId } from '@core/utils/unique-id.util'
13
+ import type { IconDefinition } from '@fortawesome/fontawesome-common-types'
14
+ import { faXmark } from '@fortawesome/free-solid-svg-icons'
15
+ import { computed } from 'vue'
16
+
17
+ defineOptions({
18
+ inheritAttrs: false,
19
+ })
20
+
21
+ defineProps<{
22
+ icon?: IconDefinition
23
+ }>()
24
+
25
+ const modelValue = defineModel<string>({ required: true })
26
+
27
+ const id = computed(() => uniqueId('input-'))
28
+ </script>
29
+
30
+ <style lang="postcss" scoped>
31
+ /* COLOR VARIANTS */
32
+ .input {
33
+ & {
34
+ --border-color: var(--color-grey-500);
35
+ --background-color: var(--background-color-primary);
36
+ }
37
+
38
+ &:is(:hover, :focus-visible) {
39
+ --border-color: var(--color-purple-d20);
40
+ --background-color: var(--background-color-primary);
41
+ }
42
+
43
+ &:focus {
44
+ --border-color: var(--color-purple-base);
45
+ --background-color: var(--background-color-primary);
46
+ }
47
+
48
+ &:active {
49
+ --border-color: var(--color-purple-d40);
50
+ --background-color: var(--background-color-primary);
51
+ }
52
+
53
+ &:disabled {
54
+ --border-color: var(--color-grey-500);
55
+ --background-color: var(--background-color-secondary);
56
+ }
57
+ }
58
+
59
+ /* BORDER VARIANTS */
60
+ .input {
61
+ --border-width: 0.1rem;
62
+
63
+ &:is(:hover, :focus-visible) {
64
+ --border-width: 0.1rem;
65
+ }
66
+
67
+ &:focus {
68
+ --border-width: 0.2rem;
69
+ }
70
+
71
+ &:active {
72
+ --border-width: 0.1rem;
73
+ }
74
+
75
+ &:disabled {
76
+ --border-width: 0.1rem;
77
+ }
78
+ }
79
+
80
+ /* IMPLEMENTATION */
81
+ .ui-input {
82
+ position: relative;
83
+
84
+ .before + .input {
85
+ padding-inline-start: 4.8rem;
86
+ }
87
+ }
88
+
89
+ .input {
90
+ border: var(--border-width) solid var(--border-color);
91
+ border-radius: 0.8rem;
92
+ outline: none;
93
+ width: 100%;
94
+ height: 4rem;
95
+ padding: 0.8rem 1.6rem;
96
+ color: var(--color-grey-000);
97
+ background-color: var(--background-color);
98
+
99
+ &:has(+ .after) {
100
+ padding-inline-end: 4.8rem;
101
+ }
102
+
103
+ &:disabled {
104
+ cursor: not-allowed;
105
+ }
106
+
107
+ &::placeholder {
108
+ color: var(--color-grey-300);
109
+ }
110
+
111
+ &[type='search']::-webkit-search-cancel-button {
112
+ display: none;
113
+ }
114
+ }
115
+
116
+ .before,
117
+ .after {
118
+ position: absolute;
119
+ inset-block: 1.2rem;
120
+ }
121
+
122
+ .before {
123
+ color: var(--color-grey-400);
124
+ inset-inline-start: 1.6rem;
125
+ pointer-events: none;
126
+ z-index: 1;
127
+ }
128
+
129
+ .after {
130
+ inset-inline-end: 1.6rem;
131
+ cursor: pointer;
132
+ }
133
+ </style>
@@ -25,8 +25,8 @@
25
25
 
26
26
  <script lang="ts" setup>
27
27
  import UiIcon from '@core/components/icon/UiIcon.vue'
28
- import MenuTrigger from '@core/components/menu/MenuTrigger.vue'
29
28
  import MenuList from '@core/components/menu/MenuList.vue'
29
+ import MenuTrigger from '@core/components/menu/MenuTrigger.vue'
30
30
  import { useContext } from '@core/composables/context.composable'
31
31
  import { DisabledContext } from '@core/context'
32
32
  import { IK_CLOSE_MENU, IK_MENU_HORIZONTAL } from '@core/utils/injection-keys.util'
@@ -12,9 +12,13 @@
12
12
  import { useContext } from '@core/composables/context.composable'
13
13
  import { DisabledContext } from '@core/context'
14
14
  import { IK_CLOSE_MENU, IK_MENU_HORIZONTAL, IK_MENU_TELEPORTED } from '@core/utils/injection-keys.util'
15
+ import { onClickOutside, unrefElement, whenever } from '@vueuse/core'
15
16
  import placementJs, { type Options } from 'placement.js'
16
17
  import { computed, inject, nextTick, provide, ref, useSlots } from 'vue'
17
- import { onClickOutside, unrefElement, whenever } from '@vueuse/core'
18
+
19
+ defineOptions({
20
+ inheritAttrs: false,
21
+ })
18
22
 
19
23
  const props = withDefaults(
20
24
  defineProps<{
@@ -26,10 +30,6 @@ const props = withDefaults(
26
30
  { disabled: undefined }
27
31
  )
28
32
 
29
- defineOptions({
30
- inheritAttrs: false,
31
- })
32
-
33
33
  const slots = useSlots()
34
34
  const isOpen = ref(false)
35
35
  const menu = ref()
@@ -0,0 +1,87 @@
1
+ <!-- v1.0 -->
2
+ <template>
3
+ <RouterLink v-if="route && !disabled" :to="route" class="object-link is-link typo p3-medium">
4
+ <span class="icon">
5
+ <slot name="icon">
6
+ <UiIcon :icon />
7
+ </slot>
8
+ </span>
9
+ <span v-tooltip class="content text-ellipsis">
10
+ <slot />
11
+ </span>
12
+ </RouterLink>
13
+ <span v-else :class="{ disabled }" class="object-link typo p3-medium">
14
+ <span class="icon">
15
+ <slot name="icon">
16
+ <UiIcon :icon />
17
+ </slot>
18
+ </span>
19
+ <slot />
20
+ </span>
21
+ </template>
22
+
23
+ <script lang="ts" setup>
24
+ import UiIcon from '@core/components/icon/UiIcon.vue'
25
+ import { vTooltip } from '@core/directives/tooltip.directive'
26
+ import type { IconDefinition } from '@fortawesome/fontawesome-common-types'
27
+ import { type RouteLocationRaw } from 'vue-router'
28
+
29
+ defineProps<{
30
+ route?: RouteLocationRaw
31
+ disabled?: boolean
32
+ icon?: IconDefinition
33
+ }>()
34
+
35
+ defineSlots<{
36
+ default: () => any
37
+ icon: () => any
38
+ }>()
39
+ </script>
40
+
41
+ <style lang="postcss" scoped>
42
+ /* COLOR VARIANTS */
43
+ .object-link {
44
+ --color: var(--color-purple-base);
45
+ --border-color: var(--color-purple-base);
46
+
47
+ &.is-link {
48
+ &:is(:hover, .hover, :focus-visible) {
49
+ --color: var(--color-purple-d20);
50
+ --border-color: var(--color-purple-d20);
51
+ }
52
+
53
+ &:is(:active, .pressed) {
54
+ --color: var(--color-purple-d40);
55
+ --border-color: var(--color-purple-d40);
56
+ }
57
+ }
58
+
59
+ &.disabled {
60
+ --color: var(--color-grey-400);
61
+ --border-color: var(--color-grey-400);
62
+ }
63
+ }
64
+
65
+ /* IMPLEMENTATION */
66
+ .object-link {
67
+ display: flex;
68
+ min-width: 0;
69
+ align-items: center;
70
+ color: var(--color);
71
+ gap: 1rem;
72
+ border-top: 0.1rem solid transparent;
73
+ border-bottom: 0.1rem solid var(--border-color);
74
+ line-height: 1;
75
+ padding-block: 0.5rem;
76
+ text-decoration: none;
77
+
78
+ &.disabled {
79
+ cursor: not-allowed;
80
+ }
81
+ }
82
+
83
+ .icon {
84
+ color: var(--color-grey-100);
85
+ font-size: 0.8rem;
86
+ }
87
+ </style>