@xen-orchestra/web-core 0.55.0 → 0.57.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 (111) hide show
  1. package/lib/assets/css/base.pcss +5 -0
  2. package/lib/components/card-object-title/VtsCardObjectTitle.vue +36 -0
  3. package/lib/components/code-snippet/VtsCodeSnippet.vue +11 -5
  4. package/lib/components/drawer/VtsDrawer.vue +5 -1
  5. package/lib/components/drawer/VtsDrawerButton.vue +4 -3
  6. package/lib/components/drawer/VtsDrawerConfirmButton.vue +4 -2
  7. package/lib/components/icon/VtsIcon.vue +3 -1
  8. package/lib/components/input-wrapper/VtsInputWrapper.vue +2 -2
  9. package/lib/components/layout/VtsContentSidePanel.vue +41 -0
  10. package/lib/components/layout/VtsLayoutSidebar.vue +116 -43
  11. package/lib/components/menu/MenuItem.vue +9 -1
  12. package/lib/components/menu/MenuList.vue +4 -2
  13. package/lib/components/menu/VtsActionsMenu.vue +48 -14
  14. package/lib/components/modal/VtsActionModal.vue +44 -9
  15. package/lib/components/modal/VtsBlockedModal.vue +1 -1
  16. package/lib/components/modal/VtsDeleteModal.vue +1 -1
  17. package/lib/components/operation-card/VtsOperationCard.vue +2 -1
  18. package/lib/components/overlay/VtsOverlayButton.vue +35 -0
  19. package/lib/components/overlay/VtsOverlayCancelButton.vue +16 -0
  20. package/lib/components/overlay/VtsOverlayConfirmButton.vue +25 -0
  21. package/lib/components/overlay/VtsOverlayList.vue +82 -0
  22. package/lib/components/panel/VtsPanel.vue +80 -0
  23. package/lib/components/panel/VtsSidePanel.vue +140 -0
  24. package/lib/components/progress-bar/VtsProgressBar.vue +5 -3
  25. package/lib/components/select/VtsSelect.vue +1 -3
  26. package/lib/components/space-card/VtsSpaceCard.vue +1 -1
  27. package/lib/components/state-hero/VtsStateHero.vue +9 -17
  28. package/lib/components/table/VtsTable.vue +2 -1
  29. package/lib/components/table/cells/VtsActionCell.vue +7 -0
  30. package/lib/components/table/cells/VtsLinkCell.vue +3 -1
  31. package/lib/components/table/cells/VtsProgressBarCell.vue +1 -1
  32. package/lib/components/table/cells/VtsUserNameCell.vue +29 -0
  33. package/lib/components/task/VtsQuickTaskButton.vue +1 -1
  34. package/lib/components/task/VtsQuickTaskList.vue +1 -1
  35. package/lib/components/tree/VtsTreeItem.vue +13 -7
  36. package/lib/components/tree/VtsTreeList.vue +6 -3
  37. package/lib/components/tree/VtsTreeLoadingItem.vue +3 -3
  38. package/lib/components/ui/head-bar/UiHeadBar.vue +2 -2
  39. package/lib/components/ui/input/UiInput.vue +56 -10
  40. package/lib/components/ui/link/UiLink.vue +3 -1
  41. package/lib/components/ui/panel/UiPanel.vue +7 -24
  42. package/lib/components/ui/query-search-bar/UiQuerySearchBar.vue +1 -15
  43. package/lib/components/ui/quick-task-item/UiQuickTaskItem.vue +1 -1
  44. package/lib/components/ui/quick-task-panel/UiQuickTaskPanel.vue +1 -1
  45. package/lib/components/ui/task-item/UiTaskItem.vue +14 -6
  46. package/lib/components/ui/task-list/UiTaskList.vue +1 -1
  47. package/lib/components/ui/text-area/UiTextarea.vue +12 -14
  48. package/lib/components/ui/tree-item-label/UiTreeItemLabel.vue +1 -1
  49. package/lib/components/vif-connection-toggle-modal/VtsVifConnectionToggleModal.vue +43 -0
  50. package/lib/composables/table/multi-select.composable.md +33 -0
  51. package/lib/composables/table-state.composable.ts +1 -1
  52. package/lib/icons/action-icons.ts +14 -0
  53. package/lib/layouts/CoreLayout.vue +74 -62
  54. package/lib/locales/cs.json +56 -1
  55. package/lib/locales/de.json +3 -2
  56. package/lib/locales/en.json +85 -4
  57. package/lib/locales/es.json +11 -1
  58. package/lib/locales/fa.json +0 -1
  59. package/lib/locales/fr.json +85 -4
  60. package/lib/locales/it.json +0 -1
  61. package/lib/locales/ko.json +2 -0
  62. package/lib/locales/nl.json +2 -1
  63. package/lib/locales/pt-BR.json +0 -1
  64. package/lib/locales/pt.json +0 -1
  65. package/lib/locales/ru.json +0 -1
  66. package/lib/locales/sk.json +56 -1
  67. package/lib/locales/sv.json +3 -2
  68. package/lib/locales/uk.json +0 -1
  69. package/lib/locales/zh-Hans.json +79 -4
  70. package/lib/packages/form-validation/custom-rules/ip-addresses.rule.ts +15 -0
  71. package/lib/packages/form-validation/custom-rules/ip.regex.ts +26 -0
  72. package/lib/packages/form-validation/custom-rules/ipv4-or-cidr.rule.ts +2 -3
  73. package/lib/packages/form-validation/index.ts +2 -0
  74. package/lib/packages/hide-permanently/README.md +60 -0
  75. package/lib/packages/hide-permanently/types.ts +3 -0
  76. package/lib/packages/hide-permanently/use-hide-permanently.ts +15 -0
  77. package/lib/packages/overlay/OverlayComponent.vue +18 -0
  78. package/lib/packages/overlay/README.md +285 -0
  79. package/lib/packages/overlay/create-event-handler.ts +80 -0
  80. package/lib/packages/overlay/injection-keys.ts +3 -0
  81. package/lib/packages/overlay/is-thenable.ts +3 -0
  82. package/lib/packages/overlay/symbols.ts +5 -0
  83. package/lib/packages/overlay/types.ts +94 -0
  84. package/lib/packages/overlay/use-overlay-escape.ts +34 -0
  85. package/lib/packages/overlay/use-overlay-store.ts +33 -0
  86. package/lib/packages/overlay/use-overlay-trigger.ts +33 -0
  87. package/lib/packages/overlay/use-overlay.ts +99 -0
  88. package/lib/packages/progress/use-progress.ts +12 -2
  89. package/lib/packages/remote-resource/define-remote-resource.ts +64 -29
  90. package/lib/packages/sidebar/index.ts +2 -0
  91. package/lib/packages/sidebar/sidebar.store.ts +55 -0
  92. package/lib/packages/sidebar/types.ts +21 -0
  93. package/lib/packages/sidebar/use-sidebar-resize.ts +47 -0
  94. package/lib/packages/sidebar/use-sidebar-responsive-expand.ts +19 -0
  95. package/lib/packages/tree/types.ts +6 -0
  96. package/lib/packages/tree/use-tree.ts +38 -37
  97. package/lib/stores/panel.store.ts +56 -5
  98. package/lib/tables/column-definitions/action-column.ts +21 -9
  99. package/lib/tables/column-definitions/user-name-column.ts +10 -0
  100. package/lib/tables/column-sets/server-columns.ts +2 -2
  101. package/lib/tables/column-sets/user-columns.ts +16 -0
  102. package/lib/tables/column-sets/vm-columns.ts +2 -3
  103. package/lib/types/object.type.ts +9 -1
  104. package/lib/types/state-hero.type.ts +19 -0
  105. package/lib/types/task.type.ts +5 -12
  106. package/lib/types/vue-virtual-scroller.d.ts +8 -2
  107. package/lib/utils/injection-keys.util.ts +4 -2
  108. package/lib/utils/ip-address.utils.ts +7 -0
  109. package/lib/utils/sr.utils.ts +4 -0
  110. package/package.json +2 -1
  111. package/lib/stores/sidebar.store.ts +0 -79
@@ -4,18 +4,30 @@ import { defineColumn } from '@core/packages/table/define-column'
4
4
  import type { ButtonIconConfig } from '@core/tables/column-definitions/button-icon-column.ts'
5
5
  import { renderHeadCell } from '@core/tables/helpers/render-head-cell'
6
6
  import type { HeaderConfig } from '@core/tables/types.ts'
7
- import { h, toValue } from 'vue'
7
+ import { type Component, h, toValue } from 'vue'
8
8
 
9
9
  export type { ActionItem }
10
10
 
11
11
  export const useActionColumn = defineColumn((config: HeaderConfig & Partial<ButtonIconConfig>) => ({
12
12
  renderHead: () => renderHeadCell(config.headerLabel),
13
- renderBody: (params: { onClick: () => void; actions?: ActionItem[] }) =>
14
- h(VtsActionCell, {
15
- buttonIcon: toValue(config.buttonIcon ?? 'fa:eye'),
16
- buttonAccent: toValue(config.buttonAccent),
17
- buttonSize: toValue(config.buttonSize),
18
- actions: params.actions,
19
- onClick: params.onClick,
20
- }),
13
+ renderBody: (params: {
14
+ onClick: () => void
15
+ actions?: ActionItem[]
16
+ component?: Component
17
+ props?: Record<string, unknown>
18
+ }) => {
19
+ const { component, props } = params
20
+
21
+ return h(
22
+ VtsActionCell,
23
+ {
24
+ buttonIcon: toValue(config.buttonIcon ?? 'fa:eye'),
25
+ buttonAccent: toValue(config.buttonAccent),
26
+ buttonSize: toValue(config.buttonSize),
27
+ actions: params.actions,
28
+ onClick: params.onClick,
29
+ },
30
+ component ? () => h(component, props) : undefined
31
+ )
32
+ },
21
33
  }))
@@ -0,0 +1,10 @@
1
+ import VtsUserNameCell from '@core/components/table/cells/VtsUserNameCell.vue'
2
+ import { defineColumn } from '@core/packages/table/define-column.ts'
3
+ import { renderHeadCell } from '@core/tables/helpers/render-head-cell.ts'
4
+ import type { HeaderConfig } from '@core/tables/types.ts'
5
+ import { h } from 'vue'
6
+
7
+ export const useUserNameColumn = defineColumn((config?: HeaderConfig) => ({
8
+ renderHead: () => renderHeadCell(config?.headerLabel),
9
+ renderBody: (content: string, props?: { href?: string }) => h(VtsUserNameCell, { content, href: props?.href }),
10
+ }))
@@ -1,7 +1,7 @@
1
1
  import { defineColumns } from '@core/packages/table/define-columns.ts'
2
+ import { useActionColumn } from '@core/tables/column-definitions/action-column.ts'
2
3
  import { useAddressColumn } from '@core/tables/column-definitions/address-column.ts'
3
4
  import { useLinkColumn } from '@core/tables/column-definitions/link-column'
4
- import { useSelectItemColumn } from '@core/tables/column-definitions/select-item-column'
5
5
  import { useStatusColumn } from '@core/tables/column-definitions/status-column.ts'
6
6
  import { useI18n } from 'vue-i18n'
7
7
 
@@ -13,6 +13,6 @@ export const useServerColumns = defineColumns(() => {
13
13
  hostIp: useAddressColumn({ headerLabel: () => t('ip-address') }),
14
14
  status: useStatusColumn({ headerLabel: () => t('status') }),
15
15
  primaryHost: useLinkColumn({ headerLabel: () => t('master') }),
16
- selectItem: useSelectItemColumn(),
16
+ actions: useActionColumn({}),
17
17
  }
18
18
  })
@@ -0,0 +1,16 @@
1
+ import { defineColumns } from '@core/packages/table/define-columns.ts'
2
+ import { useSelectItemColumn } from '@core/tables/column-definitions/select-item-column.ts'
3
+ import { useTruncatedTextColumn } from '@core/tables/column-definitions/truncated-text-column.ts'
4
+ import { useUserNameColumn } from '@core/tables/column-definitions/user-name-column.ts'
5
+ import { useI18n } from 'vue-i18n'
6
+
7
+ export const useUserColumns = defineColumns(() => {
8
+ const { t } = useI18n()
9
+
10
+ return {
11
+ username: useUserNameColumn({ headerLabel: () => t('username') }),
12
+ email: useTruncatedTextColumn({ headerLabel: () => t('email') }),
13
+ providers: useTruncatedTextColumn({ headerLabel: () => t('providers') }),
14
+ selectItem: useSelectItemColumn(),
15
+ }
16
+ })
@@ -1,14 +1,13 @@
1
1
  import { defineColumns } from '@core/packages/table/define-columns.ts'
2
+ import { useActionColumn } from '@core/tables/column-definitions/action-column.ts'
2
3
  import { useAddressColumn } from '@core/tables/column-definitions/address-column.ts'
3
4
  import { useLinkColumn } from '@core/tables/column-definitions/link-column'
4
5
  import { useNumberColumn } from '@core/tables/column-definitions/number-column.ts'
5
- import { useSelectItemColumn } from '@core/tables/column-definitions/select-item-column'
6
6
  import { useTagColumn } from '@core/tables/column-definitions/tag-column.ts'
7
7
  import { useI18n } from 'vue-i18n'
8
8
 
9
9
  export const useVmColumns = defineColumns(() => {
10
10
  const { t } = useI18n()
11
-
12
11
  return {
13
12
  vm: useLinkColumn({ headerLabel: () => t('vm') }),
14
13
  ipAddresses: useAddressColumn({ headerLabel: () => t('ip-addresses') }),
@@ -16,6 +15,6 @@ export const useVmColumns = defineColumns(() => {
16
15
  ram: useNumberColumn({ headerLabel: () => t('ram') }),
17
16
  diskSpace: useNumberColumn({ headerLabel: () => t('disk-space') }),
18
17
  tags: useTagColumn({ headerLabel: () => t('tags') }),
19
- selectItem: useSelectItemColumn(),
18
+ actions: useActionColumn({}),
20
19
  }
21
20
  })
@@ -1,5 +1,8 @@
1
- export type ObjectType = 'vm'
1
+ export type ObjectType = 'vm' | 'host'
2
+
2
3
  export type VmActions = 'reboot' | 'shutdown' | 'force-reboot' | 'force-shutdown'
4
+ export type HostActions = 'enable' | 'disable'
5
+
3
6
  export type VmBlockedOperations =
4
7
  | 'clean_shutdown'
5
8
  | 'hard_shutdown'
@@ -8,3 +11,8 @@ export type VmBlockedOperations =
8
11
  | 'hard_reboot'
9
12
  | 'suspend'
10
13
  | 'destroy'
14
+
15
+ export type ActionsByObject = {
16
+ vm: VmActions
17
+ host: HostActions
18
+ }
@@ -0,0 +1,19 @@
1
+ export const STATE_HERO_TYPES = [
2
+ 'busy',
3
+ 'no-result',
4
+ 'under-construction',
5
+ 'no-data',
6
+ 'no-selection',
7
+ 'error',
8
+ 'not-found',
9
+ 'offline',
10
+ 'all-good',
11
+ 'all-done',
12
+ 'creating',
13
+ ] as const
14
+
15
+ export type StateHeroType = (typeof STATE_HERO_TYPES)[number]
16
+
17
+ export type StateHeroFormat = 'page' | 'card' | 'panel' | 'table'
18
+
19
+ export type StateHeroSize = 'extra-small' | 'small' | 'medium' | 'large'
@@ -1,15 +1,8 @@
1
+ import type { RouteLocationRaw } from 'vue-router'
2
+
1
3
  export type TaskStatus = 'failure' | 'interrupted' | 'pending' | 'success'
2
4
 
3
- export type Task = {
4
- id: string
5
- infos?: { data: unknown; message: string }[]
6
- name?: string
7
- progress?: number
8
- tag?: string
9
- userName?: string
10
- start?: number
11
- end?: number
12
- status: TaskStatus
13
- subtasks?: Task[]
14
- warnings?: { data: unknown; message: string }[]
5
+ export type TaskObjectSegment = {
6
+ text: string
7
+ to?: RouteLocationRaw
15
8
  }
@@ -48,10 +48,16 @@ declare module 'vue-virtual-scroller' {
48
48
  after(): unknown
49
49
  }
50
50
 
51
+ interface ScrollToOptions {
52
+ smooth?: boolean
53
+ align?: 'start' | 'center' | 'end' | 'nearest'
54
+ offset?: number
55
+ }
56
+
51
57
  export interface RecycleScrollerInstance {
52
58
  getScroll(): { start: number; end: number }
53
- scrollToItem(index: number): void
54
- scrollToPosition(position: number): void
59
+ scrollToItem(index: number, options?: ScrollToOptions): void
60
+ scrollToPosition(position: number, options?: ScrollToOptions): void
55
61
  }
56
62
 
57
63
  export const RecycleScroller: <T>(
@@ -1,6 +1,6 @@
1
1
  import type { InputWrapperController } from '@core/components/input-wrapper/VtsInputWrapper.vue'
2
2
  import type { ModalAccent } from '@core/components/ui/modal/UiModal.vue'
3
- import type { ValueFormatter } from '@core/types/chart'
3
+ import type { ValueFormatter } from '@core/types/chart.ts'
4
4
  import type { ComputedRef, InjectionKey, Ref } from 'vue'
5
5
 
6
6
  export const IK_CHART_VALUE_FORMATTER = Symbol('IK_CHART_VALUE_FORMATTER') as InjectionKey<ComputedRef<ValueFormatter>>
@@ -11,7 +11,7 @@ export const IK_TREE_ITEM_TOGGLE = Symbol('IK_TREE_ITEM_TOGGLE') as InjectionKey
11
11
 
12
12
  export const IK_TREE_ITEM_EXPANDED = Symbol('IK_TREE_ITEM_EXPANDED') as InjectionKey<Ref<boolean>>
13
13
 
14
- export const IK_TREE_LIST_DEPTH = Symbol('IK_TREE_LIST_DEPTH') as InjectionKey<number>
14
+ export const IK_TREE_LIST_DEPTH = Symbol('IK_TREE_LIST_DEPTH') as InjectionKey<Ref<number>>
15
15
 
16
16
  export const IK_MENU_HORIZONTAL = Symbol('IK_MENU_HORIZONTAL') as InjectionKey<ComputedRef<boolean>>
17
17
 
@@ -24,3 +24,5 @@ export const IK_DISABLED = Symbol('IK_DISABLED') as InjectionKey<ComputedRef<boo
24
24
  export const IK_INPUT_WRAPPER_CONTROLLER = Symbol('IK_INPUT_WRAPPER_CONTROLLER') as InjectionKey<InputWrapperController>
25
25
 
26
26
  export const IK_MODAL_ACCENT = Symbol('IK_MODAL_ACCENT') as InjectionKey<ComputedRef<ModalAccent>>
27
+
28
+ export const IK_OVERLAY_ACCENT = Symbol('IK_OVERLAY_ACCENT') as InjectionKey<ComputedRef<ModalAccent>>
@@ -16,3 +16,10 @@ export const getUniqueIpAddressesForDevice = (
16
16
 
17
17
  return [...new Set(getIpAddressesByDevice(addresses)[device])]
18
18
  }
19
+
20
+ export function parseIpList(value: string, separator = ';'): string[] {
21
+ return value
22
+ .split(separator)
23
+ .map(ip => ip.trim())
24
+ .filter(ip => ip !== '')
25
+ }
@@ -32,3 +32,7 @@ export function getSrModalInfoVariant(scope: SrScope, accessMode: SrAccessMode):
32
32
 
33
33
  return 'pool-shared'
34
34
  }
35
+
36
+ export function shouldShowTargetCount(scope: SrScope, targetCount: number) {
37
+ return targetCount > (scope.type === SR_SCOPE_TYPE.POOL ? 0 : 1)
38
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@xen-orchestra/web-core",
3
3
  "type": "module",
4
- "version": "0.55.0",
4
+ "version": "0.57.0",
5
5
  "private": false,
6
6
  "exports": {
7
7
  "./*": {
@@ -34,6 +34,7 @@
34
34
  "ndjson-readablestream": "^1.3.0",
35
35
  "placement.js": "^1.0.0-beta.5",
36
36
  "simple-icons": "^14.14.0",
37
+ "vue-component-type-helpers": "~3.3.7",
37
38
  "vue-echarts": "^6.6.8",
38
39
  "vue-virtual-scroller": "^2.0.0-beta.8"
39
40
  },
@@ -1,79 +0,0 @@
1
- import { useUiStore } from '@core/stores/ui.store'
2
- import { ifElse } from '@core/utils/if-else.utils'
3
- import { useLocalStorage, useRafFn, useStyleTag, useToggle } from '@vueuse/core'
4
- import { defineStore } from 'pinia'
5
- import { computed, ref, watch } from 'vue'
6
-
7
- export const useSidebarStore = defineStore('layout', () => {
8
- const uiStore = useUiStore()
9
- const isExpanded = useLocalStorage('sidebar.expanded', true)
10
- const isLocked = useLocalStorage('sidebar.locked', true)
11
- const width = useLocalStorage('sidebar.width', 350)
12
- const toggleExpand = useToggle(isExpanded)
13
- const toggleLock = useToggle(isLocked)
14
- const isResizing = ref(false)
15
- let desktopState = false
16
-
17
- let initialX: number
18
- let initialWidth: number
19
- let clientX: number
20
-
21
- function startResize(event: MouseEvent) {
22
- clientX = event.clientX
23
- initialX = clientX
24
- initialWidth = width.value
25
- isResizing.value = true
26
- document.addEventListener('mousemove', handleMouseMove)
27
- document.addEventListener('mouseup', handleMouseUp)
28
- }
29
-
30
- function handleMouseMove(event: MouseEvent) {
31
- clientX = event.clientX
32
- }
33
-
34
- function handleMouseUp() {
35
- isResizing.value = false
36
- document.removeEventListener('mousemove', handleMouseMove)
37
- document.removeEventListener('mouseup', handleMouseUp)
38
- }
39
-
40
- const cssWidth = computed(() => (uiStore.isSmall ? '100%' : `${width.value}px`))
41
-
42
- const cssOffset = computed(() => (isExpanded.value ? 0 : `-${cssWidth.value}`))
43
-
44
- const { load, unload } = useStyleTag('body { cursor: col-resize !important; }', { immediate: false })
45
-
46
- const { pause, resume } = useRafFn(
47
- () => (width.value = Math.min(500, Math.max(200, initialWidth + clientX - initialX))),
48
- { immediate: false }
49
- )
50
-
51
- ifElse(isResizing, [load, resume], [pause, unload])
52
-
53
- watch(
54
- () => ({
55
- isSmall: uiStore.isSmall,
56
- isLarge: uiStore.isLarge,
57
- }),
58
- ({ isSmall, isLarge }) => {
59
- if (isSmall) {
60
- desktopState = isExpanded.value
61
- } else {
62
- isExpanded.value = isLarge ? true : desktopState
63
- }
64
- },
65
- { immediate: true }
66
- )
67
-
68
- return {
69
- isExpanded,
70
- isLocked,
71
- width,
72
- toggleExpand,
73
- toggleLock,
74
- startResize,
75
- isResizing,
76
- cssWidth,
77
- cssOffset,
78
- }
79
- })