@xen-orchestra/web-core 0.7.0 → 0.8.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 (36) hide show
  1. package/lib/assets/css/typography/_style.pcss +1 -0
  2. package/lib/components/layout/VtsLayoutSidebar.vue +1 -1
  3. package/lib/components/state-hero/VtsErrorNoDataHero.vue +11 -0
  4. package/lib/components/state-hero/VtsNoSelectionHero.vue +13 -0
  5. package/lib/components/state-hero/VtsObjectNotFoundHero.vue +3 -2
  6. package/lib/components/state-hero/VtsStateHero.vue +30 -2
  7. package/lib/components/ui/donut-chart/UiDonutChart.vue +2 -2
  8. package/lib/components/ui/dropdown-button/UiDropdownButton.vue +81 -0
  9. package/lib/components/ui/link/UiLink.vue +75 -0
  10. package/lib/components/ui/tag/UiTagsList.vue +14 -0
  11. package/lib/composables/link-component.composable.ts +53 -0
  12. package/lib/locales/cs.json +1 -0
  13. package/lib/locales/de.json +2 -0
  14. package/lib/locales/en.json +4 -0
  15. package/lib/locales/fa.json +2 -0
  16. package/lib/locales/fr.json +4 -0
  17. package/lib/packages/job/README.md +130 -0
  18. package/lib/packages/job/define-job-arg.ts +12 -0
  19. package/lib/packages/job/define-job.ts +130 -0
  20. package/lib/packages/job/index.ts +4 -0
  21. package/lib/packages/job/job-error.ts +14 -0
  22. package/lib/packages/job/use-job-store.ts +44 -0
  23. package/lib/packages/menu/README.md +194 -0
  24. package/lib/packages/menu/action.ts +101 -0
  25. package/lib/packages/menu/base.ts +26 -0
  26. package/lib/packages/menu/context.ts +27 -0
  27. package/lib/packages/menu/index.ts +10 -0
  28. package/lib/packages/menu/job.ts +15 -0
  29. package/lib/packages/menu/link.ts +56 -0
  30. package/lib/packages/menu/menu.ts +50 -0
  31. package/lib/packages/menu/router-link.ts +51 -0
  32. package/lib/packages/menu/structure.ts +88 -0
  33. package/lib/packages/menu/toggle-target.ts +59 -0
  34. package/lib/packages/menu/toggle-trigger.ts +72 -0
  35. package/lib/packages/menu/toggle.ts +43 -0
  36. package/package.json +2 -1
@@ -0,0 +1,51 @@
1
+ import { BaseItem, type Menu, type MenuLike, parseConfigHolder } from '@core/packages/menu'
2
+ import { computed, markRaw, type MaybeRefOrGetter, reactive, toValue } from 'vue'
3
+ import { type RouteLocationRaw, RouterLink } from 'vue-router'
4
+
5
+ export interface MenuRouterLinkConfig {
6
+ to: MaybeRefOrGetter<RouteLocationRaw>
7
+ }
8
+
9
+ export class MenuRouterLinkConfigHolder {
10
+ constructor(public config: MenuRouterLinkConfig) {}
11
+ }
12
+
13
+ export interface MenuRouterLinkProps {
14
+ as: typeof RouterLink
15
+ to: RouteLocationRaw
16
+ onClick: () => void
17
+ onMouseenter: () => void
18
+ 'data-menu-id': string
19
+ }
20
+
21
+ export class MenuRouterLink extends BaseItem {
22
+ constructor(
23
+ public menu: Menu,
24
+ public config: MenuRouterLinkConfig
25
+ ) {
26
+ super(menu)
27
+ }
28
+
29
+ get props(): MenuRouterLinkProps {
30
+ return reactive({
31
+ as: markRaw(RouterLink),
32
+ onMouseenter: () => this.activate(),
33
+ onClick: () => this.deactivate(),
34
+ to: computed(() => toValue(this.config.to)),
35
+ 'data-menu-id': this.menu.context.id,
36
+ })
37
+ }
38
+ }
39
+
40
+ export function routerLink(to: MaybeRefOrGetter<RouteLocationRaw>, config: Omit<MenuRouterLinkConfig, 'to'> = {}) {
41
+ return new MenuRouterLinkConfigHolder({
42
+ ...config,
43
+ to,
44
+ })
45
+ }
46
+
47
+ export function useMenuRouterLink(config: MenuRouterLinkConfig & { parent: MenuLike }) {
48
+ const { parent, to, ...configRest } = config
49
+
50
+ return parseConfigHolder(parent, routerLink(to, configRest))
51
+ }
@@ -0,0 +1,88 @@
1
+ import {
2
+ Menu,
3
+ MENU_SYMBOL,
4
+ MenuAction,
5
+ type MenuActionProps,
6
+ MenuActionConfigHolder,
7
+ type MenuLike,
8
+ MenuLink,
9
+ type MenuLinkProps,
10
+ MenuLinkConfigHolder,
11
+ MenuRouterLink,
12
+ type MenuRouterLinkProps,
13
+ MenuRouterLinkConfigHolder,
14
+ type MenuToggleProps,
15
+ MenuToggleConfigHolder,
16
+ MenuToggleTarget,
17
+ MenuToggleTrigger,
18
+ } from '@core/packages/menu'
19
+ import { type MaybeRefOrGetter, toValue } from 'vue'
20
+
21
+ export type MenuStructure = {
22
+ [K: string]: ConfigHolder
23
+ }
24
+
25
+ export type ConfigHolder =
26
+ | MenuActionConfigHolder
27
+ | MenuLinkConfigHolder
28
+ | MenuRouterLinkConfigHolder
29
+ | MenuToggleConfigHolder<any>
30
+
31
+ export type ParseConfigHolder<TConfigHolder extends ConfigHolder> = TConfigHolder extends MenuActionConfigHolder
32
+ ? MenuActionProps
33
+ : TConfigHolder extends MenuLinkConfigHolder
34
+ ? MenuLinkProps
35
+ : TConfigHolder extends MenuRouterLinkConfigHolder
36
+ ? MenuRouterLinkProps
37
+ : TConfigHolder extends MenuToggleConfigHolder<infer TItems>
38
+ ? MenuToggleProps<TItems>
39
+ : never
40
+
41
+ export type ParseStructure<TStructure extends MenuStructure> = {
42
+ [K in keyof TStructure]: ParseConfigHolder<TStructure[K]>
43
+ }
44
+
45
+ export function parseConfigHolder<TConfigHolder extends ConfigHolder>(
46
+ menuLike: MenuLike,
47
+ configHolder: TConfigHolder
48
+ ): ParseConfigHolder<TConfigHolder> {
49
+ const menu = menuLike instanceof Menu ? menuLike : menuLike[MENU_SYMBOL]
50
+
51
+ if (configHolder instanceof MenuActionConfigHolder) {
52
+ return new MenuAction(menu, configHolder.config).props as ParseConfigHolder<TConfigHolder>
53
+ }
54
+
55
+ if (configHolder instanceof MenuLinkConfigHolder) {
56
+ return new MenuLink(menu, configHolder.config).props as ParseConfigHolder<TConfigHolder>
57
+ }
58
+
59
+ if (configHolder instanceof MenuRouterLinkConfigHolder) {
60
+ return new MenuRouterLink(menu, configHolder.config).props as ParseConfigHolder<TConfigHolder>
61
+ }
62
+
63
+ if (configHolder instanceof MenuToggleConfigHolder) {
64
+ const trigger = new MenuToggleTrigger(menu, configHolder.config)
65
+ const target = new MenuToggleTarget(trigger, configHolder.config)
66
+
67
+ return {
68
+ $trigger: trigger.props,
69
+ $target: target.props,
70
+ $isOpen: trigger.isOpen,
71
+ [MENU_SYMBOL]: trigger.subMenu,
72
+ ...parseStructure(trigger.subMenu, configHolder.config.items),
73
+ } as ParseConfigHolder<TConfigHolder>
74
+ }
75
+
76
+ throw new Error('Unsupported config')
77
+ }
78
+
79
+ export function parseStructure<TStructure extends MenuStructure>(
80
+ menu: Menu,
81
+ structure: MaybeRefOrGetter<TStructure>
82
+ ): ParseStructure<TStructure> {
83
+ return Object.fromEntries(
84
+ Object.entries(toValue(structure)).map(([key, configHolder]) => {
85
+ return [key, parseConfigHolder(menu, configHolder)]
86
+ })
87
+ ) as ParseStructure<TStructure>
88
+ }
@@ -0,0 +1,59 @@
1
+ import type { MenuToggleTrigger } from '@core/packages/menu'
2
+ import { autoUpdate, flip, type Placement, shift, useFloating, type UseFloatingReturn } from '@floating-ui/vue'
3
+ import { unrefElement } from '@vueuse/core'
4
+ import { computed, reactive, ref, type Ref, toValue, type UnwrapRef } from 'vue'
5
+
6
+ export interface MenuToggleTargetConfig {
7
+ placement?: Placement
8
+ }
9
+
10
+ export interface MenuToggleTargetProps {
11
+ ref: (el: any) => void
12
+ style: {
13
+ display: string | undefined
14
+ zIndex: number
15
+ } & UnwrapRef<UseFloatingReturn['floatingStyles']>
16
+ 'data-menu-id': string
17
+ }
18
+
19
+ export class MenuToggleTarget {
20
+ element: Ref<HTMLElement | null> = ref(null)
21
+
22
+ styles: UseFloatingReturn['floatingStyles']
23
+
24
+ constructor(
25
+ public trigger: MenuToggleTrigger,
26
+ public config: MenuToggleTargetConfig
27
+ ) {
28
+ const { floatingStyles } = useFloating(trigger.element, this.element, {
29
+ placement: computed(() => toValue(config.placement) ?? 'bottom-start'),
30
+ open: trigger.isOpen,
31
+ whileElementsMounted: autoUpdate,
32
+ middleware: [shift(), flip()],
33
+ })
34
+
35
+ this.styles = floatingStyles
36
+ }
37
+
38
+ get props(): MenuToggleTargetProps {
39
+ return reactive({
40
+ as: 'button',
41
+ type: 'button',
42
+ ref: (element: HTMLElement | null | undefined) => {
43
+ const newElement = unrefElement(element)
44
+
45
+ if (newElement !== this.element.value) {
46
+ this.element.value = newElement ?? null
47
+ }
48
+ },
49
+ style: computed(() => {
50
+ return {
51
+ display: this.trigger.isOpen.value ? undefined : 'none',
52
+ zIndex: 9999,
53
+ ...this.styles.value,
54
+ }
55
+ }),
56
+ 'data-menu-id': this.trigger.menu.context.id,
57
+ })
58
+ }
59
+ }
@@ -0,0 +1,72 @@
1
+ import { BaseItem, Menu } from '@core/packages/menu'
2
+ import { unrefElement, whenever } from '@vueuse/core'
3
+ import { computed, type ComputedRef, type MaybeRefOrGetter, reactive, ref, type Ref, toValue } from 'vue'
4
+
5
+ export interface MenuToggleTriggerConfig {
6
+ behavior?: MaybeRefOrGetter<'click' | 'mouseenter'>
7
+ }
8
+
9
+ export interface MenuToggleTriggerProps {
10
+ ref: (el: any) => void
11
+ as: 'button'
12
+ type: 'button'
13
+ submenu: true
14
+ onClick: () => void
15
+ onMouseenter: () => void
16
+ selected: boolean
17
+ 'data-menu-id': string
18
+ }
19
+
20
+ export class MenuToggleTrigger extends BaseItem {
21
+ element: Ref<HTMLElement | null> = ref(null)
22
+
23
+ subMenu: Menu
24
+
25
+ isSelfOpen = ref(false)
26
+
27
+ isOpen = computed(() => this.isSelfOpen.value || this.subMenu.isActive.value)
28
+
29
+ constructor(
30
+ public menu: Menu,
31
+ public config: MenuToggleTriggerConfig
32
+ ) {
33
+ super(menu)
34
+ this.subMenu = new Menu(menu.context)
35
+
36
+ whenever(
37
+ () => !this.isActive.value && !this.subMenu.isActive.value,
38
+ () => {
39
+ this.isSelfOpen.value = false
40
+ }
41
+ )
42
+ }
43
+
44
+ get isActive(): ComputedRef<boolean> {
45
+ return computed(() => super.isActive.value || this.subMenu?.isActive.value)
46
+ }
47
+
48
+ get props(): MenuToggleTriggerProps {
49
+ return reactive({
50
+ ref: (element: HTMLElement | null | undefined) => {
51
+ const newElement = unrefElement(element)
52
+
53
+ if (newElement !== this.element.value) {
54
+ this.element.value = newElement ?? null
55
+ }
56
+ },
57
+ as: 'button',
58
+ type: 'button',
59
+ submenu: true,
60
+ onClick: () => (this.isSelfOpen.value = !this.isOpen.value),
61
+ onMouseenter: () => {
62
+ this.activate()
63
+
64
+ if (toValue(this.config.behavior) === 'mouseenter') {
65
+ this.isSelfOpen.value = true
66
+ }
67
+ },
68
+ selected: computed(() => this.isOpen.value),
69
+ 'data-menu-id': this.menu.context.id,
70
+ })
71
+ }
72
+ }
@@ -0,0 +1,43 @@
1
+ import {
2
+ Menu,
3
+ MenuContext,
4
+ type MenuLike,
5
+ type MenuStructure,
6
+ type MenuToggleTargetConfig,
7
+ type MenuToggleTargetProps,
8
+ type MenuToggleTriggerConfig,
9
+ type MenuToggleTriggerProps,
10
+ parseConfigHolder,
11
+ type ParseStructure,
12
+ type WithMenu,
13
+ } from '@core/packages/menu'
14
+ import { computed, type ComputedRef, type MaybeRefOrGetter } from 'vue'
15
+
16
+ export type MenuToggleConfig<TStructure extends MenuStructure> = MenuToggleTriggerConfig &
17
+ MenuToggleTargetConfig & {
18
+ items?: MaybeRefOrGetter<TStructure>
19
+ }
20
+
21
+ export class MenuToggleConfigHolder<TStructure extends MenuStructure> {
22
+ constructor(public config: MenuToggleConfig<TStructure>) {}
23
+ }
24
+
25
+ export type MenuToggleProps<TStructure extends MenuStructure> = WithMenu &
26
+ ParseStructure<TStructure> & {
27
+ $trigger: MenuToggleTriggerProps
28
+ $target: MenuToggleTargetProps
29
+ $isOpen: ComputedRef<boolean>
30
+ }
31
+
32
+ export function toggle<const TStructure extends MenuStructure>(config: MenuToggleConfig<TStructure>) {
33
+ return new MenuToggleConfigHolder(config)
34
+ }
35
+
36
+ export function useMenuToggle<const TStructure extends MenuStructure>(
37
+ config: MenuToggleConfig<TStructure> & { parent?: MenuLike } = {}
38
+ ) {
39
+ const { parent, ...toggleConfig } = config
40
+ const menu = parent ?? new Menu(new MenuContext())
41
+
42
+ return computed(() => parseConfigHolder(menu, toggle<TStructure>(toggleConfig)))
43
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@xen-orchestra/web-core",
3
3
  "type": "module",
4
- "version": "0.7.0",
4
+ "version": "0.8.0",
5
5
  "private": false,
6
6
  "exports": {
7
7
  "./*": {
@@ -10,6 +10,7 @@
10
10
  }
11
11
  },
12
12
  "dependencies": {
13
+ "@floating-ui/vue": "^1.1.5",
13
14
  "@fontsource/poppins": "^5.0.14",
14
15
  "@fortawesome/fontawesome-common-types": "^6.5.1",
15
16
  "@fortawesome/free-regular-svg-icons": "^6.5.1",