Compare commits

...

2 Commits

Author SHA1 Message Date
Thierry
d239d4b5af Fix CSS class 2025-08-12 15:04:49 +02:00
Thierry
67d03991b4 feat(web-core): add VtsMenu* components 2025-08-12 14:53:07 +02:00
6 changed files with 161 additions and 19 deletions

View File

@@ -0,0 +1,19 @@
<template>
<li>
<VtsMenuTrigger v-bind="{ ...props, ...attrs }">
<slot />
</VtsMenuTrigger>
</li>
</template>
<script lang="ts" setup>
import VtsMenuTrigger, { type MenuTriggerProps } from '@core/components/menu/VtsMenuTrigger.vue'
import { useAttrs } from 'vue'
defineOptions({
inheritAttrs: false,
})
const props = defineProps<MenuTriggerProps>()
const attrs = useAttrs()
</script>

View File

@@ -0,0 +1,42 @@
<template>
<ul :class="{ horizontal, border }" class="menu-list">
<slot />
</ul>
</template>
<script lang="ts" setup>
import { IK_MENU_HORIZONTAL } from '@core/utils/injection-keys.util'
import { computed, provide } from 'vue'
const { horizontal, border } = defineProps<{
horizontal?: boolean
border?: boolean
}>()
provide(
IK_MENU_HORIZONTAL,
computed(() => horizontal ?? false)
)
</script>
<style lang="postcss" scoped>
.menu-list {
z-index: 1;
display: flex;
flex-direction: column;
padding: 0.4rem;
cursor: default;
color: var(--color-neutral-txt-primary);
border-radius: 0.4rem;
background-color: var(--color-neutral-background-primary);
gap: 0.2rem;
&.horizontal {
flex-direction: row;
}
&.border {
border: 0.1rem solid var(--color-neutral-border);
}
}
</style>

View File

@@ -0,0 +1,83 @@
<!-- v1.0 -->
<template>
<component
:is="as"
v-tooltip="tooltip ?? false"
:class="{ selected, disabled }"
class="vts-menu-trigger typo-body-regular"
>
<VtsIcon :busy :icon accent="current" />
<slot />
<VtsIcon v-if="submenu" :fixed-width="false" :icon="submenuIcon" accent="current" class="submenu-icon" />
</component>
</template>
<script lang="ts" setup>
import VtsIcon from '@core/components/icon/VtsIcon.vue'
import { type TooltipDirectiveContent, vTooltip } from '@core/directives/tooltip.directive'
import { IK_MENU_HORIZONTAL } from '@core/utils/injection-keys.util'
import type { IconDefinition } from '@fortawesome/fontawesome-common-types'
import { faAngleDown, faAngleRight } from '@fortawesome/free-solid-svg-icons'
import { type Component, computed, inject } from 'vue'
export type MenuTriggerProps = {
as: string | Component
tooltip?: TooltipDirectiveContent
selected?: boolean
busy?: boolean
disabled?: boolean
icon?: IconDefinition
submenu?: boolean
}
defineProps<MenuTriggerProps>()
defineSlots<{
default(): any
}>()
const isParentHorizontal = inject(IK_MENU_HORIZONTAL, undefined)
const submenuIcon = computed(() => (isParentHorizontal?.value ? faAngleDown : faAngleRight))
</script>
<style lang="postcss" scoped>
.vts-menu-trigger {
display: flex;
width: 100%;
align-items: center;
height: 4.4rem;
padding-right: 1.5rem;
padding-left: 1.5rem;
white-space: nowrap;
border-radius: 0.8rem;
gap: 1rem;
color: var(--color-neutral-txt-primary);
background-color: var(--color-neutral-background-primary);
border: none;
text-decoration: none;
&.disabled {
color: var(--color-neutral-txt-secondary);
}
&:not(.disabled) {
cursor: pointer;
&:hover {
color: var(--color-neutral-txt-primary);
background-color: var(--color-info-background-selected);
}
&:active,
&.selected {
color: var(--color-neutral-txt-primary);
background-color: var(--color-info-background-hover);
}
}
.submenu-icon {
margin-left: auto;
}
}
</style>

View File

@@ -6,18 +6,18 @@ A menu system for Vue applications supporting props binding for actions, links,
```vue
<template>
<MenuList>
<li><MenuTrigger v-bind="menu.save">Save</MenuTrigger></li>
<li><MenuTrigger v-bind="menu.doc">Documentation</MenuTrigger></li>
<li><MenuTrigger v-bind="menu.profile">Profile</MenuTrigger></li>
<VtsMenuList>
<VtsMenuItem v-bind="menu.save">Save</VtsMenuItem>
<VtsMenuItem v-bind="menu.doc">Documentation</VtsMenuItem>
<VtsMenuItem v-bind="menu.profile">Profile</VtsMenuItem>
<li>
<MenuTrigger v-bind="menu.more.$trigger">More...</MenuTrigger>
<MenuList v-bind="menu.more.$target">
<MenuTrigger v-bind="menu.more.settings">Settings</MenuTrigger>
<MenuTrigger v-bind="menu.more.logout">Logout</MenuTrigger>
</MenuList>
<VtsMenuTrigger v-bind="menu.more.$trigger">More...</VtsMenuTrigger>
<VtsMenuList v-bind="menu.more.$target">
<VtsMenuItem v-bind="menu.more.settings">Settings</VtsMenuItem>
<VtsMenuItem v-bind="menu.more.logout">Logout</VtsMenuItem>
</VtsMenuList>
</li>
</MenuList>
</VtsMenuList>
</template>
<script lang="ts" setup>
@@ -166,15 +166,15 @@ type Props = {
```vue
<template>
<MenuTrigger v-bind="menu.save">Save</MenuTrigger>
<MenuTrigger v-bind="menu.docs">Documentation</MenuTrigger>
<MenuTrigger v-bind="menu.profile">Profile</MenuTrigger>
<VtsMenuTrigger v-bind="menu.save">Save</VtsMenuTrigger>
<VtsMenuTrigger v-bind="menu.docs">Documentation</VtsMenuTrigger>
<VtsMenuTrigger v-bind="menu.profile">Profile</VtsMenuTrigger>
<!-- Toggle/Dropdown menu -->
<MenuTrigger v-bind="menu.more.$trigger">More</MenuTrigger>
<VtsMenuTrigger v-bind="menu.more.$trigger">More</VtsMenuTrigger>
<div v-bind="menu.more.$target">
<MenuTrigger v-bind="menu.more.settings">Settings</MenuTrigger>
<MenuTrigger v-bind="menu.more.logout">Logout</MenuTrigger>
<VtsMenuTrigger v-bind="menu.more.settings">Settings</VtsMenuTrigger>
<VtsMenuTrigger v-bind="menu.more.logout">Logout</VtsMenuTrigger>
</div>
</template>
```

View File

@@ -69,7 +69,7 @@ export function parseConfigHolder<TConfigHolder extends ConfigHolder>(
$target: target.props,
$isOpen: trigger.isOpen,
[MENU_SYMBOL]: trigger.subMenu,
...parseStructure(trigger.subMenu, configHolder.config.items),
...parseStructure(trigger.subMenu, configHolder.config.items ?? {}),
} as ParseConfigHolder<TConfigHolder>
}

View File

@@ -37,8 +37,6 @@ export class MenuToggleTarget {
get props(): MenuToggleTargetProps {
return reactive({
as: 'button',
type: 'button',
ref: (element: HTMLElement | null | undefined) => {
const newElement = unrefElement(element)