generated from VLADIMIR/template_frontend
94 lines
2.7 KiB
Vue
94 lines
2.7 KiB
Vue
<script setup lang="ts">
|
|
import { darkTheme, NButton, NConfigProvider } from 'naive-ui'
|
|
import { nextTick, ref } from 'vue'
|
|
|
|
import ComponentDesignCatalog from '@/components/ComponentDesignCatalog.vue'
|
|
import GameDesignPreview from '@/components/GameDesignPreview.vue'
|
|
import SiteDesignPreview from '@/components/SiteDesignPreview.vue'
|
|
const tabs = [
|
|
{ name: 'site', label: 'Сайт' },
|
|
{ name: 'game', label: 'Игра' },
|
|
{ name: 'components', label: 'Компоненты' },
|
|
] as const
|
|
const activeTab = ref<(typeof tabs)[number]['name']>('site')
|
|
const tabList = ref<HTMLElement | null>(null)
|
|
async function handleTabKey(event: KeyboardEvent) {
|
|
if (!['ArrowLeft', 'ArrowRight', 'Home', 'End'].includes(event.key)) return
|
|
event.preventDefault()
|
|
const index = tabs.findIndex((tab) => tab.name === activeTab.value)
|
|
const nextIndex =
|
|
event.key === 'Home'
|
|
? 0
|
|
: event.key === 'End'
|
|
? tabs.length - 1
|
|
: (index + (event.key === 'ArrowRight' ? 1 : -1) + tabs.length) % tabs.length
|
|
activeTab.value = tabs[nextIndex]!.name
|
|
await nextTick()
|
|
tabList.value?.querySelector<HTMLButtonElement>('[aria-selected="true"]')?.focus()
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<n-config-provider :theme="darkTheme">
|
|
<header class="design-header">Вечерний детектив · Дизайн-система</header>
|
|
<main class="center-block-custom design-scroll">
|
|
<div
|
|
ref="tabList"
|
|
class="design-tabs"
|
|
role="tablist"
|
|
aria-label="Стили оформления"
|
|
@keydown="handleTabKey"
|
|
>
|
|
<n-button
|
|
v-for="tab in tabs"
|
|
:id="`tab-${tab.name}`"
|
|
:key="tab.name"
|
|
role="tab"
|
|
:aria-selected="activeTab === tab.name"
|
|
:aria-controls="`panel-${tab.name}`"
|
|
:tabindex="activeTab === tab.name ? 0 : -1"
|
|
:type="activeTab === tab.name ? 'primary' : 'default'"
|
|
@click="activeTab = tab.name"
|
|
>{{ tab.label }}</n-button
|
|
>
|
|
</div>
|
|
<div
|
|
:id="`panel-${activeTab}`"
|
|
role="tabpanel"
|
|
:aria-labelledby="`tab-${activeTab}`"
|
|
tabindex="0"
|
|
>
|
|
<SiteDesignPreview v-if="activeTab === 'site'" />
|
|
<GameDesignPreview v-else-if="activeTab === 'game'" />
|
|
<ComponentDesignCatalog v-else />
|
|
</div>
|
|
</main>
|
|
</n-config-provider>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.design-header {
|
|
align-items: center;
|
|
background: var(--brand-background);
|
|
color: var(--brand-text);
|
|
display: flex;
|
|
font-size: 16px;
|
|
height: 70px;
|
|
left: 0;
|
|
padding: 0 16px;
|
|
position: fixed;
|
|
right: 0;
|
|
top: 0;
|
|
}
|
|
|
|
.design-scroll {
|
|
background: var(--brand-background);
|
|
}
|
|
|
|
.design-tabs {
|
|
display: flex;
|
|
gap: 12px;
|
|
padding: 16px;
|
|
}
|
|
</style>
|