Files
KArtSell.Aegis/frontend/src/shared/shell/KsAppShell.vue
T
kjh2064 b12fc7411d feat(fe): Add form field navigation - Enter key moves to next field
- Create useFormFieldNavigation composable for Tab-like Enter behavior
- KsTextField: Enter -> next field
- KsTextArea: Ctrl+Enter for newline, Enter -> next field
- KsSelect: Enter -> next field after selection
- Implements standard form navigation pattern across input components
2026-08-16 21:50:50 +09:00

167 lines
3.5 KiB
Vue

<script setup lang="ts">
import { onMounted, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { useLayoutStore } from './layoutStore'
import KsHeader from './KsHeader.vue'
import KsSidebar from './KsSidebar.vue'
import KsFooter from './KsFooter.vue'
import KsNotifications from './KsNotifications.vue'
import KsTabs from './KsTabs.vue'
const route = useRoute()
const router = useRouter()
const layoutStore = useLayoutStore()
const syncRouteTab = (to: { path: string; meta?: Record<string, unknown> }) => {
if (to.path !== '/' && to.path !== '/home') {
const title = (to.meta?.title as string) || to.path
const icon = (to.meta?.icon as string) || '📄'
const id = `tab-${to.path}`
layoutStore.addTab({
id,
title,
path: to.path,
icon,
})
}
}
watch(() => route.path, () => {
syncRouteTab(route)
}, { immediate: true })
onMounted(() => {
syncRouteTab(route)
})
</script>
<template>
<div class="ks-app-shell">
<!-- Skip to main content link (accessibility) -->
<a href="#ks-main" class="ks-skip ks-skip-link">본문으로 이동</a>
<!-- Header -->
<KsHeader
@toggle-sidebar="layoutStore.toggleSidebar()"
@toggle-mobile-menu="layoutStore.mobileMenuOpen = !layoutStore.mobileMenuOpen"
/>
<!-- Tabs (if any tabs are open) -->
<KsTabs v-if="layoutStore.hasOpenedTabs" />
<div class="ks-app-shell__container">
<!-- Sidebar -->
<KsSidebar
:collapsed="layoutStore.sidebarCollapsed"
:mobile-open="layoutStore.mobileMenuOpen"
@close-mobile="layoutStore.mobileMenuOpen = false"
@update:collapsed="layoutStore.sidebarCollapsed = $event"
/>
<!-- Mobile backdrop -->
<div
v-if="layoutStore.mobileMenuOpen"
class="ks-mobile-backdrop"
@click="layoutStore.mobileMenuOpen = false"
/>
<!-- Main content with page transition -->
<main
id="ks-main"
tabindex="-1"
:class="['ks-app-shell__main', { 'ks-app-shell__main--collapsed': layoutStore.sidebarCollapsed }]"
>
<slot />
</main>
</div>
<!-- Notifications (toasts) -->
<KsNotifications />
</div>
</template>
<style scoped>
.ks-app-shell {
display: flex;
flex-direction: column;
height: 100vh;
background-color: var(--color-background-primary);
color: var(--color-text-primary);
}
.ks-skip-link {
position: absolute;
top: -40px;
left: 0;
background-color: var(--color-primary-500);
color: white;
padding: var(--spacing-2) var(--spacing-3);
text-decoration: none;
z-index: 10000;
border-radius: 0 0 var(--border-radius-base) 0;
}
.ks-skip-link:focus {
top: 0;
}
.ks-app-shell__container {
display: flex;
flex: 1;
overflow: hidden;
position: relative;
min-height: 0;
}
.ks-app-shell__main {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
padding: var(--ks-space-3);
max-width: 100%;
min-height: 0;
}
.ks-app-shell__main--collapsed {
/* Sidebar is collapsed, main takes full width */
}
.ks-mobile-backdrop {
position: fixed;
inset: 0;
background-color: rgba(0, 0, 0, 0.5);
backdrop-filter: blur(4px);
z-index: 99;
animation: fadeIn var(--transition-base);
}
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
/* Responsive design */
@media (max-width: 768px) {
.ks-app-shell__main {
padding: var(--spacing-4);
}
}
@media (prefers-reduced-motion: reduce) {
.ks-app-shell__main {
scroll-behavior: auto;
}
.ks-mobile-backdrop {
animation: none;
}
}
</style>