Compare commits

...

5 Commits

4 changed files with 94 additions and 9 deletions

View File

@@ -12,10 +12,10 @@
<slot />
</MenuTrigger>
<MenuList v-else :disabled="isDisabled" shadow>
<template #trigger="{ open, isOpen }">
<template #trigger="{ open, isOpen, isHeader }">
<MenuTrigger :active="isOpen" :busy="isBusy" :disabled="isDisabled" :icon @click="open">
<slot />
<UiIcon :fixed-width="false" :icon="submenuIcon" class="submenu-icon" />
<UiIcon v-if="!isHeader" :fixed-width="false" :icon="submenuIcon" class="submenu-icon" />
</MenuTrigger>
</template>
<slot name="submenu" />
@@ -74,6 +74,7 @@ const handleClick = async () => {
<style lang="postcss" scoped>
.menu-item {
color: var(--color-grey-000);
list-style-type: none;
}
.submenu-icon {

View File

@@ -1,11 +1,20 @@
<!-- v1.0 -->
<template>
<slot :is-open="isOpen" :open="open" name="trigger" />
<Teleport :disabled="!shouldTeleport" to="body">
<ul v-if="!hasTrigger || isOpen" ref="menu" :class="{ horizontal, shadow }" class="menu-list" v-bind="$attrs">
<slot :is-open="isOpen" :open name="trigger" :is-header="false" />
<MobileFullscreen :disabled="!hasTrigger" :is-open="isOpen" @close="isOpen = false">
<template #header>
<slot name="trigger" :is-header="true" :is-open="false" :open="noop" />
</template>
<ul
v-if="!hasTrigger || isOpen"
ref="menu"
:class="{ horizontal, shadow, mobile: isMobile }"
class="menu-list"
v-bind="$attrs"
>
<slot />
</ul>
</Teleport>
</MobileFullscreen>
</template>
<script lang="ts" setup>
@@ -14,7 +23,10 @@ import { DisabledContext } from '@core/context'
import { IK_CLOSE_MENU, IK_MENU_HORIZONTAL, IK_MENU_TELEPORTED } from '@core/utils/injection-keys.util'
import placementJs, { type Options } from 'placement.js'
import { computed, inject, nextTick, provide, ref, useSlots } from 'vue'
import { onClickOutside, unrefElement, whenever } from '@vueuse/core'
import { noop, onClickOutside, unrefElement, whenever } from '@vueuse/core'
import { storeToRefs } from 'pinia'
import { useUiStore } from '@core/stores/ui.store'
import MobileFullscreen from '@core/components/mobile-fullscreen/MobileFullscreen.vue'
const props = withDefaults(
defineProps<{
@@ -31,6 +43,7 @@ defineOptions({
})
const slots = useSlots()
const { isMobile } = storeToRefs(useUiStore())
const isOpen = ref(false)
const menu = ref()
const isParentHorizontal = inject(
@@ -41,14 +54,13 @@ provide(
IK_MENU_HORIZONTAL,
computed(() => props.horizontal ?? false)
)
useContext(DisabledContext, () => props.disabled)
let clearClickOutsideEvent: (() => void) | undefined
const hasTrigger = useSlots().trigger !== undefined
const shouldTeleport = hasTrigger && !inject(IK_MENU_TELEPORTED, false)
const shouldTeleport = hasTrigger && !inject(IK_MENU_TELEPORTED, false) && !isMobile.value
if (shouldTeleport) {
provide(IK_MENU_TELEPORTED, true)
@@ -69,6 +81,9 @@ const open = (event: MouseEvent) => {
isOpen.value = true
if (isMobile.value) {
return
}
nextTick(() => {
clearClickOutsideEvent = onClickOutside(menu, () => (isOpen.value = false), {
ignore: [event.currentTarget as HTMLElement],
@@ -100,5 +115,9 @@ const open = (event: MouseEvent) => {
&.shadow {
box-shadow: var(--shadow-300);
}
&:is(.mobile) {
width: 100%;
}
}
</style>

View File

@@ -0,0 +1,36 @@
<template>
<slot v-if="uiStore.isDesktop || disabled" />
<Teleport v-else-if="isOpen" to="body">
<div class="mobile-fullscreen">
<MobileHeadBar @close="emit('close')">
<slot name="header" />
</MobileHeadBar>
<slot />
</div>
</Teleport>
</template>
<script lang="ts" setup>
import MobileHeadBar from '@core/components/mobile-fullscreen/MobileHeadBar.vue'
import { useUiStore } from '@core/stores/ui.store'
defineProps<{
disabled?: boolean
isOpen?: boolean
}>()
const emit = defineEmits<{
close: []
}>()
const uiStore = useUiStore()
</script>
<style lang="postcss" scoped>
.mobile-fullscreen {
background-color: var(--background-color-secondary);
position: fixed;
inset: 0;
z-index: 1002;
}
</style>

View File

@@ -0,0 +1,29 @@
<!-- v1.0 -->
<template>
<header class="mobile-head-bar typo p2-medium">
<ButtonIcon :icon="faAngleLeft" color="info" @click="emit('close')" />
<slot />
</header>
</template>
<script lang="ts" setup>
import ButtonIcon from '@core/components/button/ButtonIcon.vue'
import { faAngleLeft } from '@fortawesome/free-solid-svg-icons'
const emit = defineEmits<{
close: []
}>()
</script>
<style lang="postcss" scoped>
.mobile-head-bar {
align-items: center;
display: flex;
background-color: var(--background-color-primary);
border-bottom: 0.1rem solid var(--color-grey-500);
height: 4rem;
padding: 0.8rem 1.6rem;
gap: 0.8rem;
}
</style>