@xen-orchestra/web-core 0.1.0 → 0.2.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/_colors.pcss +148 -116
- package/lib/assets/css/_context.pcss +44 -44
- package/lib/assets/css/base.pcss +9 -8
- package/lib/assets/no-data.svg +67 -0
- package/lib/components/CardNumbers.vue +5 -5
- package/lib/components/LegendTitle.vue +3 -3
- package/lib/components/PowerStateIcon.vue +6 -6
- package/lib/components/UiCard.vue +2 -2
- package/lib/components/UiTag.vue +39 -31
- package/lib/components/backup-item/BackupItem.vue +5 -2
- package/lib/components/backup-state/BackupState.vue +4 -5
- package/lib/components/button/ButtonIcon.vue +40 -40
- package/lib/components/button/UiButton.vue +356 -93
- package/lib/components/card/CardSubtitle.vue +2 -2
- package/lib/components/card/CardTitle.vue +9 -4
- package/lib/components/cell-object/CellObject.vue +4 -4
- package/lib/components/cell-text/CellText.vue +3 -3
- package/lib/components/chip/ChipIcon.vue +6 -5
- package/lib/components/chip/UiChip.vue +20 -20
- package/lib/components/counter/VtsCounter.vue +147 -0
- package/lib/components/divider/Divider.vue +2 -2
- package/lib/components/donut-chart/DonutChart.vue +9 -9
- package/lib/components/dropdown/DropdownItem.vue +53 -73
- package/lib/components/dropdown/DropdownList.vue +1 -1
- package/lib/components/dropdown/DropdownTitle.vue +6 -6
- package/lib/components/head-bar/HeadBar.vue +6 -6
- package/lib/components/icon/ComplexIcon.vue +1 -1
- package/lib/components/icon/ObjectIcon.vue +19 -19
- package/lib/components/icon/VtsIcon.vue +96 -0
- package/lib/components/info/VtsInfo.vue +55 -0
- package/lib/components/input/UiInput.vue +22 -16
- package/lib/components/layout/LayoutSidebar.vue +3 -3
- package/lib/components/legend/LegendItem.vue +11 -11
- package/lib/components/menu/MenuItem.vue +4 -4
- package/lib/components/menu/MenuList.vue +6 -6
- package/lib/components/menu/MenuSeparator.vue +2 -2
- package/lib/components/menu/MenuTrigger.vue +6 -6
- package/lib/components/object-link/ObjectLink.vue +12 -12
- package/lib/components/query-search-bar/QuerySearchBar.vue +10 -3
- package/lib/components/stacked-bar/StackedBar.vue +2 -2
- package/lib/components/stacked-bar/StackedBarSegment.vue +6 -5
- package/lib/components/state-hero/NoDataHero.vue +11 -0
- package/lib/components/state-hero/StateHero.vue +6 -3
- package/lib/components/tab/TabItem.vue +11 -11
- package/lib/components/tab/TabList.vue +2 -2
- package/lib/components/table/ColumnTitle.vue +19 -16
- package/lib/components/table/UiTable.vue +5 -5
- package/lib/components/task/QuickTaskItem.vue +6 -6
- package/lib/components/task/QuickTaskList.vue +1 -1
- package/lib/components/task/QuickTaskPanel.vue +1 -1
- package/lib/components/task/QuickTaskTabBar.vue +22 -4
- package/lib/components/tooltip/TooltipItem.vue +15 -9
- package/lib/components/tree/TreeItemError.vue +1 -1
- package/lib/components/tree/TreeItemLabel.vue +12 -12
- package/lib/components/tree/TreeLine.vue +2 -2
- package/lib/components/tree/TreeLoadingItem.vue +4 -8
- package/lib/components/user/UserLink.vue +8 -8
- package/lib/components/user/UserLogo.vue +2 -2
- package/lib/composables/hide-route-query.composable.ts +10 -0
- package/lib/composables/route-query/actions/handle-delete.ts +9 -6
- package/lib/composables/route-query/actions/handle-set.ts +6 -4
- package/lib/composables/route-query/types.ts +10 -1
- package/lib/composables/sort-route-query.composable.ts +18 -0
- package/lib/composables/table/create-base-definition.ts +20 -0
- package/lib/composables/table/create-define-column.ts +26 -0
- package/lib/composables/table/create-sorting-definition.ts +48 -0
- package/lib/composables/table/create-visibility-definition.ts +44 -0
- package/lib/composables/table/type.ts +112 -0
- package/lib/composables/table.composable.ts +76 -0
- package/lib/layouts/CoreLayout.vue +3 -3
- package/lib/locales/en.json +1 -0
- package/lib/locales/fr.json +1 -0
- package/lib/types/backup.type.ts +1 -1
- package/lib/types/button.type.ts +1 -1
- package/lib/types/color.type.ts +2 -4
- package/lib/types/size.type.ts +0 -2
- package/lib/types/utility.type.ts +2 -0
- package/lib/utils/to-variants.util.ts +9 -0
- package/package.json +3 -3
- package/lib/components/UiCounter.vue +0 -89
- package/lib/components/icon/UiIcon.vue +0 -47
- package/lib/components/icon/VmIcon.vue +0 -30
|
@@ -17,7 +17,7 @@ withDefaults(
|
|
|
17
17
|
<style lang="postcss" scoped>
|
|
18
18
|
/* COLOR VARIANTS */
|
|
19
19
|
.user-logo {
|
|
20
|
-
--border-color: var(--color-
|
|
20
|
+
--border-color: var(--color-normal-txt-base);
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
/* SIZE VARIANTS */
|
|
@@ -49,7 +49,7 @@ withDefaults(
|
|
|
49
49
|
display: block;
|
|
50
50
|
width: var(--size);
|
|
51
51
|
height: var(--size);
|
|
52
|
-
background: var(--color-
|
|
52
|
+
background: var(--color-neutral-txt-primary) url('../../assets/user.png') no-repeat var(--background-position) /
|
|
53
53
|
var(--background-size);
|
|
54
54
|
border: var(--border-size) solid var(--border-color);
|
|
55
55
|
border-radius: 20rem;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { useRouteQuery } from '@core/composables/route-query.composable'
|
|
2
|
+
|
|
3
|
+
export function useHideRouteQuery(id: string) {
|
|
4
|
+
return useRouteQuery(id, {
|
|
5
|
+
toData: query => new Set(query ? query.split(',') : undefined),
|
|
6
|
+
toQuery: data => Array.from(data).join(','),
|
|
7
|
+
})
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export type HideRouteQuery = ReturnType<typeof useHideRouteQuery>
|
|
@@ -4,13 +4,16 @@ export function handleDelete(source: WritableComputedRef<any>, value: any) {
|
|
|
4
4
|
if (Array.isArray(source.value)) {
|
|
5
5
|
source.value = [...source.value].splice(value, 1)
|
|
6
6
|
} else if (source.value instanceof Set) {
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
const newSet = new Set(source.value)
|
|
8
|
+
newSet.delete(value)
|
|
9
|
+
source.value = newSet
|
|
9
10
|
} else if (source.value instanceof Map) {
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
const newMap = new Map(source.value)
|
|
12
|
+
newMap.delete(value)
|
|
13
|
+
source.value = newMap
|
|
12
14
|
} else if (typeof source.value === 'object' && source.value !== null) {
|
|
13
|
-
|
|
14
|
-
delete
|
|
15
|
+
const newObject = { ...source.value }
|
|
16
|
+
delete newObject[value]
|
|
17
|
+
source.value = newObject
|
|
15
18
|
}
|
|
16
19
|
}
|
|
@@ -2,11 +2,13 @@ import type { WritableComputedRef } from 'vue'
|
|
|
2
2
|
|
|
3
3
|
export function handleSet(source: WritableComputedRef<any>, key: any, value: any) {
|
|
4
4
|
if (Array.isArray(source.value)) {
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
const newArray = source.value.slice()
|
|
6
|
+
newArray[key] = value
|
|
7
|
+
source.value = newArray
|
|
7
8
|
} else if (source.value instanceof Map) {
|
|
8
|
-
|
|
9
|
-
|
|
9
|
+
const newMap = new Map(source.value)
|
|
10
|
+
newMap.set(key, value)
|
|
11
|
+
source.value = newMap
|
|
10
12
|
} else if (typeof source.value === 'object') {
|
|
11
13
|
if (source.value === null) {
|
|
12
14
|
return
|
|
@@ -39,4 +39,13 @@ export type GuessActions<TData> =
|
|
|
39
39
|
? MapActions<TKey, TValue>
|
|
40
40
|
: EmptyObject
|
|
41
41
|
|
|
42
|
-
export type RouteQuery<TData> = WritableComputedRef<
|
|
42
|
+
export type RouteQuery<TData> = WritableComputedRef<
|
|
43
|
+
TData extends Set<infer V>
|
|
44
|
+
? ReadonlySet<V>
|
|
45
|
+
: TData extends Map<infer K, infer V>
|
|
46
|
+
? ReadonlyMap<K, V>
|
|
47
|
+
: TData extends object
|
|
48
|
+
? Readonly<TData>
|
|
49
|
+
: TData
|
|
50
|
+
> &
|
|
51
|
+
GuessActions<TData>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { useRouteQuery } from '@core/composables/route-query.composable'
|
|
2
|
+
|
|
3
|
+
export function useSortRouteQuery(id: string) {
|
|
4
|
+
return useRouteQuery(id, {
|
|
5
|
+
toData: query => {
|
|
6
|
+
if (!query) {
|
|
7
|
+
return undefined
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const [id, direction] = query.split(',') as [string, 'asc' | 'desc']
|
|
11
|
+
|
|
12
|
+
return { id, direction }
|
|
13
|
+
},
|
|
14
|
+
toQuery: data => (data ? [data.id, data.direction].join(',') : ''),
|
|
15
|
+
})
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export type SortRouteQuery = ReturnType<typeof useSortRouteQuery>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { BaseDefinition, ColumnOptions } from '@core/composables/table/type'
|
|
2
|
+
|
|
3
|
+
export function createBaseDefinition<TId extends string, TRecord>(
|
|
4
|
+
columnId: TId,
|
|
5
|
+
optionsOrGetter: any,
|
|
6
|
+
options: ColumnOptions<any, any, any>
|
|
7
|
+
): BaseDefinition<TId, TRecord, any> {
|
|
8
|
+
const getter =
|
|
9
|
+
typeof optionsOrGetter === 'function'
|
|
10
|
+
? optionsOrGetter
|
|
11
|
+
: typeof optionsOrGetter === 'string'
|
|
12
|
+
? (item: TRecord) => item[optionsOrGetter as keyof TRecord]
|
|
13
|
+
: (item: TRecord) => item[columnId as unknown as keyof TRecord]
|
|
14
|
+
|
|
15
|
+
return {
|
|
16
|
+
id: columnId,
|
|
17
|
+
label: options.label,
|
|
18
|
+
getter,
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { HideRouteQuery } from '@core/composables/hide-route-query.composable'
|
|
2
|
+
import type { SortRouteQuery } from '@core/composables/sort-route-query.composable'
|
|
3
|
+
import { createBaseDefinition } from '@core/composables/table/create-base-definition'
|
|
4
|
+
import { createSortingDefinition } from '@core/composables/table/create-sorting-definition'
|
|
5
|
+
import { createVisibilityDefinition } from '@core/composables/table/create-visibility-definition'
|
|
6
|
+
import type { ColumnDefinition, ColumnOptions, DefineColumn } from '@core/composables/table/type'
|
|
7
|
+
import { reactive } from 'vue'
|
|
8
|
+
|
|
9
|
+
export function createDefineColumn<TRecord>(
|
|
10
|
+
hideRouteQuery: HideRouteQuery,
|
|
11
|
+
sortRouteQuery: SortRouteQuery
|
|
12
|
+
): DefineColumn<TRecord> {
|
|
13
|
+
return function defineColumn<TId extends string>(
|
|
14
|
+
columnId: TId,
|
|
15
|
+
optionsOrGetter: any,
|
|
16
|
+
optionsOrNone?: any
|
|
17
|
+
): ColumnDefinition<TId, TRecord, any, any, any> {
|
|
18
|
+
const options = (optionsOrNone ?? optionsOrGetter) as ColumnOptions<any, any, any>
|
|
19
|
+
|
|
20
|
+
return reactive({
|
|
21
|
+
...createBaseDefinition(columnId, optionsOrGetter, options),
|
|
22
|
+
...createVisibilityDefinition(columnId, hideRouteQuery, options.isHideable),
|
|
23
|
+
...createSortingDefinition(columnId, sortRouteQuery, options.compareFn),
|
|
24
|
+
})
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { SortRouteQuery } from '@core/composables/sort-route-query.composable'
|
|
2
|
+
import type { CompareFn, SortingDefinition } from '@core/composables/table/type'
|
|
3
|
+
import { computed } from 'vue'
|
|
4
|
+
|
|
5
|
+
export function createSortingDefinition<TCompareReturn extends number | unknown>(
|
|
6
|
+
columnId: string,
|
|
7
|
+
sortRouteQuery: SortRouteQuery,
|
|
8
|
+
compareFn: CompareFn<any, TCompareReturn> | undefined
|
|
9
|
+
): SortingDefinition<any, TCompareReturn> {
|
|
10
|
+
if (compareFn === undefined) {
|
|
11
|
+
return {
|
|
12
|
+
isSortable: false,
|
|
13
|
+
} as SortingDefinition<any, TCompareReturn>
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const isSorted = computed(() => sortRouteQuery.value?.id === columnId)
|
|
17
|
+
|
|
18
|
+
const isSortedAsc = computed(() => isSorted.value && sortRouteQuery.value?.direction === 'asc')
|
|
19
|
+
|
|
20
|
+
const isSortedDesc = computed(() => isSorted.value && sortRouteQuery.value?.direction === 'desc')
|
|
21
|
+
|
|
22
|
+
function sort(direction: 'asc' | 'desc' | false, toggleOffIfSameDirection = false) {
|
|
23
|
+
const shouldToggleOff =
|
|
24
|
+
direction === false ||
|
|
25
|
+
(toggleOffIfSameDirection && isSorted.value && sortRouteQuery.value?.direction === direction)
|
|
26
|
+
|
|
27
|
+
sortRouteQuery.value = shouldToggleOff ? undefined : { id: columnId, direction }
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function sortAsc(toggleOffIfSameDirection = false) {
|
|
31
|
+
sort('asc', toggleOffIfSameDirection)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function sortDesc(toggleOffIfSameDirection = false) {
|
|
35
|
+
sort('desc', toggleOffIfSameDirection)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return {
|
|
39
|
+
isSortable: true,
|
|
40
|
+
isSorted,
|
|
41
|
+
isSortedAsc,
|
|
42
|
+
isSortedDesc,
|
|
43
|
+
sort,
|
|
44
|
+
sortAsc,
|
|
45
|
+
sortDesc,
|
|
46
|
+
compareFn,
|
|
47
|
+
} as SortingDefinition<any, TCompareReturn>
|
|
48
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { HideRouteQuery } from '@core/composables/hide-route-query.composable'
|
|
2
|
+
import type { VisibilityDefinition } from '@core/composables/table/type'
|
|
3
|
+
import { computed } from 'vue'
|
|
4
|
+
|
|
5
|
+
export function createVisibilityDefinition<THideable extends boolean | undefined>(
|
|
6
|
+
columnId: string,
|
|
7
|
+
hideRouteQuery: HideRouteQuery,
|
|
8
|
+
isHideable: THideable
|
|
9
|
+
): VisibilityDefinition<THideable> {
|
|
10
|
+
if (isHideable === false) {
|
|
11
|
+
return {
|
|
12
|
+
isHideable: false,
|
|
13
|
+
isVisible: true,
|
|
14
|
+
} as VisibilityDefinition<THideable>
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const isVisible = computed(() => !hideRouteQuery.value.has(columnId))
|
|
18
|
+
|
|
19
|
+
function show() {
|
|
20
|
+
hideRouteQuery.delete(columnId)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function hide() {
|
|
24
|
+
hideRouteQuery.add(columnId)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function toggle(value?: boolean) {
|
|
28
|
+
const shouldBeVisible = value ?? !isVisible.value
|
|
29
|
+
|
|
30
|
+
if (shouldBeVisible) {
|
|
31
|
+
show()
|
|
32
|
+
} else {
|
|
33
|
+
hide()
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
isHideable: true,
|
|
39
|
+
isVisible,
|
|
40
|
+
show,
|
|
41
|
+
hide,
|
|
42
|
+
toggle,
|
|
43
|
+
} as VisibilityDefinition<THideable>
|
|
44
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import type { StringKeyOf } from '@core/types/utility.type'
|
|
2
|
+
import type { ComputedRef, UnwrapRef } from 'vue'
|
|
3
|
+
|
|
4
|
+
export type CompareFn<TValue, TCompareReturn> = (a: TValue, b: TValue) => TCompareReturn
|
|
5
|
+
|
|
6
|
+
type Getter<TRecord, TValue> = (item: TRecord) => TValue
|
|
7
|
+
|
|
8
|
+
export type ColumnOptions<TValue, THideable extends boolean | unknown, TCompareReturn extends number | unknown> = {
|
|
9
|
+
label: string
|
|
10
|
+
isHideable?: THideable
|
|
11
|
+
compareFn?: CompareFn<TValue, TCompareReturn>
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export type BaseDefinition<TId, TRecord, TValue> = {
|
|
15
|
+
id: TId
|
|
16
|
+
label: string
|
|
17
|
+
getter: Getter<TRecord, TValue>
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export type VisibilityDefinition<THideable extends boolean | unknown> = THideable extends false
|
|
21
|
+
? {
|
|
22
|
+
isHideable: false
|
|
23
|
+
isVisible: true
|
|
24
|
+
}
|
|
25
|
+
: {
|
|
26
|
+
isHideable: true
|
|
27
|
+
isVisible: ComputedRef<boolean>
|
|
28
|
+
show(): void
|
|
29
|
+
hide(): void
|
|
30
|
+
toggle(value?: boolean): void
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export type SortingDefinition<TValue, TCompareReturn extends number | unknown> = TCompareReturn extends number
|
|
34
|
+
? {
|
|
35
|
+
isSortable: true
|
|
36
|
+
isSorted: ComputedRef<boolean>
|
|
37
|
+
isSortedAsc: ComputedRef<boolean>
|
|
38
|
+
isSortedDesc: ComputedRef<boolean>
|
|
39
|
+
sort(direction: 'asc' | 'desc' | false, toggleOffIfSameDirection?: boolean): void
|
|
40
|
+
sortAsc(toggleOffIfSameDirection?: boolean): void
|
|
41
|
+
sortDesc(toggleOffIfSameDirection?: boolean): void
|
|
42
|
+
compareFn: (a: TValue, b: TValue) => TCompareReturn
|
|
43
|
+
}
|
|
44
|
+
: {
|
|
45
|
+
isSortable: false
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export type ColumnDefinition<
|
|
49
|
+
TId,
|
|
50
|
+
TRecord,
|
|
51
|
+
TValue,
|
|
52
|
+
THideable extends boolean | unknown,
|
|
53
|
+
TCompareReturn extends number | unknown,
|
|
54
|
+
> = UnwrapRef<
|
|
55
|
+
BaseDefinition<TId, TRecord, TValue> & VisibilityDefinition<THideable> & SortingDefinition<TValue, TCompareReturn>
|
|
56
|
+
>
|
|
57
|
+
|
|
58
|
+
export type DefineColumn<TRecord> = {
|
|
59
|
+
<
|
|
60
|
+
const TId extends StringKeyOf<TRecord>,
|
|
61
|
+
TCompareReturn extends number | unknown,
|
|
62
|
+
THideable extends boolean | unknown,
|
|
63
|
+
>(
|
|
64
|
+
columnId: TId,
|
|
65
|
+
options: ColumnOptions<TRecord[TId], THideable, TCompareReturn>
|
|
66
|
+
): ColumnDefinition<TId, TRecord, TRecord[TId], THideable, TCompareReturn>
|
|
67
|
+
|
|
68
|
+
<
|
|
69
|
+
const TId extends string,
|
|
70
|
+
TProperty extends keyof TRecord,
|
|
71
|
+
TCompareReturn extends number | unknown,
|
|
72
|
+
THideable extends boolean | unknown,
|
|
73
|
+
>(
|
|
74
|
+
columnId: TId,
|
|
75
|
+
property: TProperty,
|
|
76
|
+
options: ColumnOptions<TRecord[TProperty], THideable, TCompareReturn>
|
|
77
|
+
): ColumnDefinition<TId, TRecord, TRecord[TProperty], THideable, TCompareReturn>
|
|
78
|
+
|
|
79
|
+
<const TId extends string, TOutput, TCompareReturn extends number | unknown, THideable extends boolean | unknown>(
|
|
80
|
+
columnId: TId,
|
|
81
|
+
getter: Getter<TRecord, TOutput>,
|
|
82
|
+
options: ColumnOptions<TOutput, THideable, TCompareReturn>
|
|
83
|
+
): ColumnDefinition<TId, TRecord, TOutput, THideable, TCompareReturn>
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export type TableOptions<TRecord, TRowId, TDefinition extends ColumnDefinition<any, any, any, any, any>> = {
|
|
87
|
+
rowId: (item: TRecord) => TRowId
|
|
88
|
+
columns: (define: DefineColumn<TRecord>) => TDefinition[]
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export type RowColumn<TColumnDefinition extends ColumnDefinition<any, any, any, any, any>> =
|
|
92
|
+
TColumnDefinition extends ColumnDefinition<infer TColumnId, any, infer TColumnValue, any, any>
|
|
93
|
+
? {
|
|
94
|
+
id: TColumnId
|
|
95
|
+
value: TColumnValue
|
|
96
|
+
}
|
|
97
|
+
: never
|
|
98
|
+
|
|
99
|
+
export type Row<TRowId, TDefinitions extends ColumnDefinition<any, any, any, any, any>[]> = {
|
|
100
|
+
id: TRowId
|
|
101
|
+
value: TDefinitions extends ColumnDefinition<any, infer TRecord, any, any, any> ? TRecord : never
|
|
102
|
+
visibleColumns: { [TIndex in keyof TDefinitions]: RowColumn<TDefinitions[TIndex]> }
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export type Table<TRowId, TDefinitions extends ColumnDefinition<any, any, any, any, any>[]> = {
|
|
106
|
+
columns: ComputedRef<TDefinitions>
|
|
107
|
+
visibleColumns: ComputedRef<TDefinitions[number][]>
|
|
108
|
+
columnsById: ComputedRef<{
|
|
109
|
+
[TColumn in TDefinitions[number] as TColumn['id']]: TColumn
|
|
110
|
+
}>
|
|
111
|
+
rows: ComputedRef<Row<TRowId, TDefinitions>[]>
|
|
112
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { useHideRouteQuery } from '@core/composables/hide-route-query.composable'
|
|
2
|
+
import { useSortRouteQuery } from '@core/composables/sort-route-query.composable'
|
|
3
|
+
import { createDefineColumn } from '@core/composables/table/create-define-column'
|
|
4
|
+
import type { ColumnDefinition, Table, TableOptions } from '@core/composables/table/type'
|
|
5
|
+
import type { MaybeRefOrGetter } from 'vue'
|
|
6
|
+
import { computed, reactive, toValue } from 'vue'
|
|
7
|
+
|
|
8
|
+
export function useTable<TRecord, TRowId, const TDefinitions extends ColumnDefinition<any, TRecord, any, any, any>>(
|
|
9
|
+
id: string,
|
|
10
|
+
records: MaybeRefOrGetter<TRecord[]>,
|
|
11
|
+
options: TableOptions<TRecord, TRowId, TDefinitions>
|
|
12
|
+
): Table<TRowId, TDefinitions[]> {
|
|
13
|
+
const hideRouteQuery = useHideRouteQuery(`table.${id}.hide`)
|
|
14
|
+
|
|
15
|
+
const sortRouteQuery = useSortRouteQuery(`table.${id}.sort`)
|
|
16
|
+
|
|
17
|
+
const defineColumn = createDefineColumn<TRecord>(hideRouteQuery, sortRouteQuery)
|
|
18
|
+
|
|
19
|
+
const columns = options.columns(defineColumn)
|
|
20
|
+
|
|
21
|
+
const columnsById = Object.fromEntries(columns.map(column => [column.id, column])) as Record<
|
|
22
|
+
string,
|
|
23
|
+
ColumnDefinition<any, TRecord, any, any, any>
|
|
24
|
+
>
|
|
25
|
+
|
|
26
|
+
const visibleColumns = computed(() => columns.filter(column => column.isVisible))
|
|
27
|
+
|
|
28
|
+
const rows = computed(() =>
|
|
29
|
+
toValue(records).map(record => {
|
|
30
|
+
const rowId = options.rowId(record)
|
|
31
|
+
|
|
32
|
+
const visibleRowColumns = computed(() =>
|
|
33
|
+
visibleColumns.value.map(column => ({
|
|
34
|
+
id: column.id,
|
|
35
|
+
value: column.getter(record),
|
|
36
|
+
}))
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
return reactive({
|
|
40
|
+
id: rowId,
|
|
41
|
+
value: record,
|
|
42
|
+
visibleColumns: visibleRowColumns,
|
|
43
|
+
})
|
|
44
|
+
})
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
const sortedRows = computed(() => {
|
|
48
|
+
const sort = sortRouteQuery.value
|
|
49
|
+
|
|
50
|
+
if (sort === undefined) {
|
|
51
|
+
return rows.value
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const sortColumn = columnsById[sort.id]
|
|
55
|
+
|
|
56
|
+
if (sortColumn === undefined || !sortColumn.isSortable) {
|
|
57
|
+
return rows.value
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const compareFn = sortColumn.compareFn
|
|
61
|
+
|
|
62
|
+
return rows.value.slice().sort((row1, row2) => {
|
|
63
|
+
const value1 = sortColumn.getter(row1.value as TRecord)
|
|
64
|
+
const value2 = sortColumn.getter(row2.value as TRecord)
|
|
65
|
+
|
|
66
|
+
return sort.direction === 'asc' ? compareFn(value1, value2) : compareFn(value2, value1)
|
|
67
|
+
})
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
columns: computed(() => columns),
|
|
72
|
+
columnsById: computed(() => columnsById),
|
|
73
|
+
visibleColumns,
|
|
74
|
+
rows: sortedRows,
|
|
75
|
+
} as Table<TRowId, TDefinitions[]>
|
|
76
|
+
}
|
|
@@ -61,8 +61,8 @@ const sidebarStore = useSidebarStore()
|
|
|
61
61
|
display: flex;
|
|
62
62
|
align-items: center;
|
|
63
63
|
height: 5.6rem;
|
|
64
|
-
background-color: var(--background-
|
|
65
|
-
border-bottom: 0.1rem solid var(--color-
|
|
64
|
+
background-color: var(--color-neutral-background-secondary);
|
|
65
|
+
border-bottom: 0.1rem solid var(--color-neutral-border);
|
|
66
66
|
flex-shrink: 0;
|
|
67
67
|
gap: 1.6rem;
|
|
68
68
|
padding: 0 1.6rem;
|
|
@@ -77,6 +77,6 @@ const sidebarStore = useSidebarStore()
|
|
|
77
77
|
overflow: auto;
|
|
78
78
|
display: flex;
|
|
79
79
|
flex-direction: column;
|
|
80
|
-
background-color: var(--background-
|
|
80
|
+
background-color: var(--color-neutral-background-secondary);
|
|
81
81
|
}
|
|
82
82
|
</style>
|
package/lib/locales/en.json
CHANGED
package/lib/locales/fr.json
CHANGED
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
"master": "Hôte primaire",
|
|
48
48
|
"n-vms": "1 VM | {n} VMs",
|
|
49
49
|
"network": "Réseau",
|
|
50
|
+
"no-data": "Aucune donnée",
|
|
50
51
|
"object-not-found": "L'objet {id} est introuvable…",
|
|
51
52
|
"patches": "Patches",
|
|
52
53
|
"power-on-for-console": "Allumez votre VM pour accéder à sa console",
|
package/lib/types/backup.type.ts
CHANGED
package/lib/types/button.type.ts
CHANGED
package/lib/types/color.type.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
export type Color = '
|
|
1
|
+
export type Color = 'normal' | 'success' | 'warning' | 'danger'
|
|
2
2
|
|
|
3
|
-
export type
|
|
4
|
-
|
|
5
|
-
export type TagColor = Color | 'grey'
|
|
3
|
+
export type TagColor = 'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'disabled'
|
package/lib/types/size.type.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xen-orchestra/web-core",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.2.0",
|
|
5
5
|
"private": false,
|
|
6
6
|
"exports": {
|
|
7
7
|
"./*": {
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
},
|
|
25
25
|
"peerDependencies": {
|
|
26
26
|
"pinia": "^2.1.7",
|
|
27
|
-
"vue": "
|
|
27
|
+
"vue": "~3.4.13",
|
|
28
28
|
"vue-i18n": "^9.9.0",
|
|
29
29
|
"vue-router": "^4.4.0"
|
|
30
30
|
},
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"@types/novnc__novnc": "^1.5.0",
|
|
34
34
|
"@vue/tsconfig": "^0.5.1",
|
|
35
35
|
"pinia": "^2.1.7",
|
|
36
|
-
"vue": "
|
|
36
|
+
"vue": "~3.4.13",
|
|
37
37
|
"vue-i18n": "^9.9.0",
|
|
38
38
|
"vue-router": "^4.4.0"
|
|
39
39
|
},
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
<!-- v1.0 -->
|
|
2
|
-
<template>
|
|
3
|
-
<span :class="classNames" class="ui-counter typo">
|
|
4
|
-
<span class="inner">{{ value }}</span>
|
|
5
|
-
</span>
|
|
6
|
-
</template>
|
|
7
|
-
|
|
8
|
-
<script lang="ts" setup>
|
|
9
|
-
import type { CounterColor } from '@core/types/color.type'
|
|
10
|
-
import type { CounterSize } from '@core/types/size.type'
|
|
11
|
-
import { computed } from 'vue'
|
|
12
|
-
|
|
13
|
-
const props = withDefaults(
|
|
14
|
-
defineProps<{
|
|
15
|
-
value: number | string
|
|
16
|
-
color?: CounterColor
|
|
17
|
-
size?: CounterSize
|
|
18
|
-
}>(),
|
|
19
|
-
{ size: 'small' }
|
|
20
|
-
)
|
|
21
|
-
|
|
22
|
-
const fontClasses = {
|
|
23
|
-
small: 'p4-semi-bold',
|
|
24
|
-
medium: 'p1-medium',
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const classNames = computed(() => {
|
|
28
|
-
return [props.color, props.size, fontClasses[props.size]]
|
|
29
|
-
})
|
|
30
|
-
</script>
|
|
31
|
-
|
|
32
|
-
<style lang="postcss" scoped>
|
|
33
|
-
/* COLOR VARIANTS */
|
|
34
|
-
.ui-counter {
|
|
35
|
-
--background-color: var(--color-grey-300);
|
|
36
|
-
|
|
37
|
-
&.info {
|
|
38
|
-
--background-color: var(--color-purple-base);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
&.success {
|
|
42
|
-
--background-color: var(--color-green-base);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
&.warning {
|
|
46
|
-
--background-color: var(--color-orange-base);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
&:is(.error, .danger) {
|
|
50
|
-
--background-color: var(--color-red-base);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
&.black {
|
|
54
|
-
--background-color: var(--color-grey-100);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/* SIZE VARIANTS */
|
|
59
|
-
.ui-counter {
|
|
60
|
-
&.small {
|
|
61
|
-
--height: 1.5rem;
|
|
62
|
-
--padding: 0 0.4rem;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
&.medium {
|
|
66
|
-
--height: 2.4rem;
|
|
67
|
-
--padding: 0 0.6rem;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/* IMPLEMENTATION */
|
|
72
|
-
.ui-counter {
|
|
73
|
-
display: inline-flex;
|
|
74
|
-
align-items: center;
|
|
75
|
-
justify-content: center;
|
|
76
|
-
vertical-align: middle;
|
|
77
|
-
text-transform: lowercase;
|
|
78
|
-
color: var(--color-grey-600);
|
|
79
|
-
height: var(--height);
|
|
80
|
-
min-width: var(--height);
|
|
81
|
-
padding: var(--padding);
|
|
82
|
-
background-color: var(--background-color);
|
|
83
|
-
border-radius: calc(var(--height) / 2);
|
|
84
|
-
|
|
85
|
-
.inner {
|
|
86
|
-
line-height: 0;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
</style>
|