@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.
- package/lib/assets/css/_typography.pcss +6 -0
- package/lib/assets/no-selection-old.svg +70 -0
- package/lib/assets/no-selection.svg +85 -70
- package/lib/components/backup-state/VtsBackupState.vue +20 -17
- package/lib/components/card/VtsCardRowKeyValue.vue +4 -0
- package/lib/components/cell-object/VtsCellObject.vue +4 -1
- package/lib/components/console/VtsActionsConsole.vue +7 -4
- package/lib/components/console/VtsClipboardConsole.vue +9 -6
- package/lib/components/copy-button/VtsCopyButton.vue +9 -15
- package/lib/components/dropdown/DropdownTitle.vue +5 -2
- package/lib/components/icon/NewVtsIcon.vue +49 -0
- package/lib/components/input-group/VtsInputGroup.vue +41 -0
- package/lib/components/input-wrapper/VtsInputWrapper.vue +2 -2
- package/lib/components/layout/VtsLayoutSidebar.vue +6 -3
- package/lib/components/linear-chart/VtsLinearChart.vue +4 -0
- package/lib/components/object-icon/VtsObjectIcon.vue +22 -0
- package/lib/components/quick-info-card/VtsQuickInfoCard.vue +4 -1
- package/lib/components/select/VtsOption.vue +10 -6
- package/lib/components/select/VtsSelect.vue +74 -50
- package/lib/components/state-hero/VtsAllDoneHero.vue +4 -1
- package/lib/components/state-hero/VtsAllGoodHero.vue +4 -1
- package/lib/components/state-hero/VtsComingSoonHero.vue +4 -1
- package/lib/components/state-hero/VtsErrorNoDataHero.vue +4 -1
- package/lib/components/state-hero/VtsLoadingHero.vue +4 -1
- package/lib/components/state-hero/VtsNoDataHero.vue +4 -1
- package/lib/components/state-hero/VtsNoSelectionHero.vue +4 -1
- package/lib/components/state-hero/VtsObjectNotFoundHero.vue +4 -1
- package/lib/components/state-hero/VtsOfflineHero.vue +4 -1
- package/lib/components/state-hero/VtsPageNotFoundHero.vue +4 -1
- package/lib/components/table/ColumnTitle.vue +2 -2
- package/lib/components/task/VtsQuickTaskButton.vue +4 -1
- package/lib/components/task/VtsQuickTaskList.vue +5 -2
- package/lib/components/task/VtsQuickTaskTabBar.vue +8 -5
- package/lib/components/ui/card-numbers/UiCardNumbers.vue +4 -1
- package/lib/components/ui/character-limit/UiCharacterLimit.vue +4 -1
- package/lib/components/ui/input/UiInput.vue +2 -2
- package/lib/components/ui/label/UiLabel.vue +4 -1
- package/lib/components/ui/panel/UiPanel.vue +16 -3
- package/lib/components/ui/progress-bar/UiProgressBar.vue +5 -2
- package/lib/components/ui/query-search-bar/UiQuerySearchBar.vue +9 -6
- package/lib/components/ui/quick-task-item/UiQuickTaskItem.vue +6 -3
- package/lib/components/ui/quoteCode/UiQuoteCode.vue +104 -0
- package/lib/components/ui/stacked-bar/StackedBarSegment.vue +4 -1
- package/lib/components/ui/table-pagination/UiTablePagination.vue +6 -3
- package/lib/components/ui/text-area/UiTextarea.vue +4 -1
- package/lib/components/ui/top-bottom-table/UiTopBottomTable.vue +6 -3
- package/lib/components/ui/tree-item-label/UiTreeItemLabel.vue +4 -1
- package/lib/composables/local-time-ago.composable.ts +53 -0
- package/lib/composables/locale-time-ago.composable.ts +53 -0
- package/lib/icons/fa-icons.ts +164 -0
- package/lib/icons/index.ts +15 -0
- package/lib/icons/legacy-icons.ts +80 -0
- package/lib/icons/object-icons.ts +187 -0
- package/lib/layouts/CoreLayout.vue +7 -3
- package/lib/locales/cs.json +0 -1
- package/lib/locales/de.json +1 -1
- package/lib/locales/en.json +40 -4
- package/lib/locales/es.json +1 -1
- package/lib/locales/fr.json +39 -3
- package/lib/locales/it.json +1 -1
- package/lib/locales/nl.json +1 -1
- package/lib/locales/ru.json +1 -1
- package/lib/locales/sv.json +1 -2
- package/lib/packages/collection/README.md +23 -18
- package/lib/packages/collection/create-collection.ts +22 -21
- package/lib/packages/collection/create-item.ts +21 -20
- package/lib/packages/collection/create-use-subset.ts +23 -0
- package/lib/packages/collection/guess-item-id.ts +26 -16
- package/lib/packages/collection/index.ts +4 -0
- package/lib/packages/collection/types.ts +65 -37
- package/lib/packages/collection/use-collection.ts +68 -18
- package/lib/packages/collection/use-flag-registry.ts +38 -17
- package/lib/packages/form-select/guess-label.ts +45 -0
- package/lib/packages/form-select/guess-value.ts +23 -0
- package/lib/packages/form-select/index.ts +6 -0
- package/lib/packages/form-select/normalize-search-term.ts +11 -0
- package/lib/packages/form-select/types.ts +90 -42
- package/lib/packages/form-select/use-form-option-controller.ts +7 -3
- package/lib/packages/form-select/use-form-select-controller.ts +38 -27
- package/lib/packages/form-select/use-form-select-keyboard-navigation.ts +1 -1
- package/lib/packages/form-select/use-form-select.ts +308 -130
- package/lib/packages/icon/DisplayIcon.vue +25 -0
- package/lib/packages/icon/DisplayIconAny.vue +16 -0
- package/lib/packages/icon/DisplayIconSingle.vue +35 -0
- package/lib/packages/icon/DisplayIconStack.vue +34 -0
- package/lib/packages/icon/README.md +286 -0
- package/lib/packages/icon/create-icon-bindings.ts +27 -0
- package/lib/packages/icon/define-icon-pack.ts +23 -0
- package/lib/packages/icon/define-icon-single.ts +17 -0
- package/lib/packages/icon/define-icon-stack.ts +20 -0
- package/lib/packages/icon/define-icon.ts +40 -0
- package/lib/packages/icon/generate-icon-variants.ts +17 -0
- package/lib/packages/icon/index.ts +8 -0
- package/lib/packages/icon/is-icon-stack.ts +5 -0
- package/lib/packages/icon/merge-icons.ts +25 -0
- package/lib/packages/icon/merge-transforms.ts +12 -0
- package/lib/packages/icon/normalize-icon.ts +25 -0
- package/lib/packages/icon/to-tuple.ts +7 -0
- package/lib/packages/icon/types.ts +72 -0
- package/lib/packages/job/README.md +2 -2
- package/lib/packages/mapper/README.md +166 -0
- package/lib/packages/mapper/convert-to-map.ts +5 -0
- package/lib/packages/mapper/create-mapper.ts +30 -0
- package/lib/packages/mapper/index.ts +4 -0
- package/lib/packages/mapper/types.ts +1 -0
- package/lib/packages/mapper/use-mapper.ts +31 -0
- package/lib/stores/sidebar.store.ts +1 -1
- package/lib/types/chart.ts +2 -2
- package/lib/types/utility.type.ts +9 -0
- package/lib/utils/object.util.ts +16 -0
- package/lib/utils/size.util.ts +14 -3
- package/package.json +2 -1
- package/lib/assets/zoom.svg +0 -85
- package/lib/components/backup-item/VtsBackupItem.vue +0 -47
- package/lib/composables/mapper.composable.md +0 -74
- 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
|
-
{{
|
|
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
|
-
{{
|
|
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
|
-
{{
|
|
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
|
-
{{
|
|
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
|
-
{{
|
|
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
|
-
{{
|
|
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
|
-
{{
|
|
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/
|
|
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
|
-
|
|
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>{{
|
|
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
|
-
<!--
|
|
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>{{
|
|
9
|
-
<span v-for="step in steps" :key="step">{{
|
|
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
|
-
{{
|
|
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 ?
|
|
12
|
+
:aria-label="uiStore.isMobile ? t('core.query-search-bar.label') : undefined"
|
|
13
13
|
:icon="uiStore.isDesktop ? faMagnifyingGlass : undefined"
|
|
14
|
-
: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">{{
|
|
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="
|
|
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
|
-
{{
|
|
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">{{
|
|
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
|
-
{{
|
|
20
|
+
{{ d(task.start, 'datetime_short') }}
|
|
21
21
|
<template v-if="task.end">
|
|
22
22
|
<VtsIcon :icon="faArrowRight" accent="current" />
|
|
23
|
-
{{
|
|
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
|
-
{{
|
|
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
|
-
{{
|
|
11
|
+
{{ t('core.select.n-object-of', { from, to, total }) }}
|
|
12
12
|
</span>
|
|
13
|
-
<span class="typo-body-regular-small label show">{{
|
|
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 ?
|
|
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
|
-
{{
|
|
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
|
-
{{
|
|
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
|
-
{{
|
|
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
|
-
{{
|
|
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 ?
|
|
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
|
+
}
|