Added modal to menu

This commit is contained in:
2026-09-11 17:37:24 +03:30
parent 103e9b1c50
commit bd63d2ab76
4 changed files with 723 additions and 151 deletions
+280
View File
@@ -0,0 +1,280 @@
<script setup lang="ts">
import { onMounted, onUnmounted } from 'vue';
import UiIcon from '@/components/UiIcon.vue';
import type { NavHubDef } from '@/lib/nav-hubs';
defineProps<{
hub: NavHubDef;
}>();
const emit = defineEmits<{
close: [];
go: [to: string];
}>();
function onKey(event: KeyboardEvent) {
if (event.key === 'Escape') emit('close');
}
onMounted(() => {
window.addEventListener('keydown', onKey);
});
onUnmounted(() => {
window.removeEventListener('keydown', onKey);
});
</script>
<template>
<section class="hub" role="dialog" aria-modal="true" :aria-label="hub.title">
<header class="head">
<div class="copy">
<h1>{{ hub.title }}</h1>
<p>{{ hub.desc }}</p>
</div>
<span class="head-ico" :class="hub.tint" aria-hidden="true">
<UiIcon :name="hub.icon" :size="20" />
</span>
</header>
<div class="grid">
<button
v-for="card in hub.cards"
:key="card.to + card.title"
class="card"
type="button"
@click="emit('go', card.to)"
>
<span class="card-top">
<span class="card-title">
{{ card.title }}
<UiIcon name="chevron" :size="13" class="go" />
</span>
<span class="card-ico" :class="card.tint" aria-hidden="true">
<UiIcon :name="card.icon" :size="16" />
</span>
</span>
<span class="card-desc">{{ card.desc }}</span>
</button>
</div>
<div v-if="hub.shortcuts.length" class="quick">
<span class="quick-label">دسترسی سریع</span>
<div class="quick-row">
<button
v-for="item in hub.shortcuts"
:key="item.to + item.title"
class="quick-btn"
type="button"
@click="emit('go', item.to)"
>
<UiIcon :name="item.icon" :size="15" />
{{ item.title }}
</button>
</div>
</div>
</section>
</template>
<style scoped>
.hub {
width: min(28rem, calc(100vw - var(--hub-offset, 16rem) - 1.5rem));
max-height: calc(100dvh - 56px);
overflow: hidden;
padding: 0.9rem 0.9rem 0.8rem;
display: grid;
align-content: start;
gap: 0.7rem;
border-radius: 28px;
background: #fff;
box-shadow: 0 18px 40px rgba(15, 23, 42, 0.12);
}
.head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.8rem;
padding: 0 0.15rem;
}
.copy h1 {
margin: 0;
font-size: 1.05rem;
font-weight: 800;
color: #0f172a;
}
.copy p {
margin: 0.15rem 0 0;
color: #94a3b8;
font-size: 0.75rem;
}
.head-ico,
.card-ico {
display: grid;
place-items: center;
flex-shrink: 0;
}
.head-ico {
width: 2.2rem;
height: 2.2rem;
border-radius: 999px;
}
.card-ico {
width: 1.9rem;
height: 1.9rem;
border-radius: 10px;
}
.grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 0.45rem;
}
.card-top {
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.5rem;
width: 100%;
}
.card {
display: grid;
justify-items: stretch;
align-content: start;
gap: 0.28rem;
padding: 0.65rem 0.7rem 0.55rem;
border: 0;
border-radius: 16px;
background: #f7f8fa;
cursor: pointer;
text-align: start;
font: inherit;
color: inherit;
}
.card:hover {
background: #f1f5f9;
}
.card-title {
display: flex;
align-items: center;
gap: 0.25rem;
font-size: 0.88rem;
font-weight: 800;
color: #0f172a;
}
.card-desc {
color: #94a3b8;
font-size: 0.72rem;
line-height: 1.5;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.go {
transform: scaleX(-1);
color: #94a3b8;
flex-shrink: 0;
}
.quick {
display: grid;
gap: 0.4rem;
}
.quick-label {
padding-inline: 0.15rem;
color: #64748b;
font-size: 0.75rem;
font-weight: 700;
}
.quick-row {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 0.45rem;
}
.quick-btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 0.35rem;
min-height: 2.4rem;
padding: 0.45rem 0.7rem;
border: 0;
border-radius: 999px;
background: #ecfdf8;
color: #0f766e;
font: inherit;
font-size: 0.8rem;
font-weight: 800;
cursor: pointer;
}
.quick-btn:hover {
background: #d8faf3;
}
.mint {
background: #d8faf3;
color: #0f766e;
}
.violet {
background: #eee7ff;
color: #6d28d9;
}
.peach {
background: #ffedd5;
color: #c2410c;
}
.sky {
background: #e0f2fe;
color: #0369a1;
}
.rose {
background: #fce7f3;
color: #be185d;
}
.amber {
background: #fef3c7;
color: #b45309;
}
@media (max-width: 900px) {
.hub {
width: 100%;
max-height: min(86dvh, calc(100dvh - 3.5rem));
overflow: auto;
padding: 0.85rem 0.9rem calc(0.9rem + env(safe-area-inset-bottom));
border-radius: 24px 24px 0 0;
box-shadow: 0 -12px 40px rgba(15, 23, 42, 0.16);
}
.hub::before {
content: '';
display: block;
width: 2.6rem;
height: 0.28rem;
margin: 0 auto 0.65rem;
border-radius: 999px;
background: #e2e8f0;
}
}
</style>