feat(redesign): design-system tokens, 23 Vue components, game-aware shell + Fleet/Solo dashboard
All checks were successful
Test Asgard Runner / test (push) Successful in 4s
All checks were successful
Test Asgard Runner / test (push) Successful in 4s
Tokens ported 1:1 from the Claude Design bundle (colors/game-themes/type/spacing/elevation/motion/fonts) with the data-theme/data-game theming contract via useThemeGame (+ cc-skin-swap repaint guard). 23 design-system components reimplemented as Vue SFCs (core/forms/data/navigation/feedback/brand). DashboardLayout rebuilt as the game-aware shell (GameSwitcher, grouped nav with permission gating preserved, agent-health footer, topbar). DashboardView: Fleet + Solo with per-game GAME_FIELDS rows and the themed ECharts PlayersChart; Solo wired to the real server store, Fleet on representative data pending the multi-instance backend. All four game skins (Rust/Dune/Conan/Soulmask). vue-tsc + vite build green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
50
frontend/src/components/ds/navigation/NavItem.vue
Normal file
50
frontend/src/components/ds/navigation/NavItem.vue
Normal file
@@ -0,0 +1,50 @@
|
||||
<script setup lang="ts">
|
||||
/**
|
||||
* NavItem — sidebar navigation row: icon + label, active state with accent rail,
|
||||
* optional trailing count. Collapsed mode renders icon-only at 40 px wide.
|
||||
*/
|
||||
import Icon from '@/components/ds/core/Icon.vue'
|
||||
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
icon: string
|
||||
label: string
|
||||
active?: boolean
|
||||
count?: number | string
|
||||
collapsed?: boolean
|
||||
}>(),
|
||||
{ active: false, collapsed: false },
|
||||
)
|
||||
|
||||
const emit = defineEmits<{ click: [] }>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
:class="['cc-nav', active && 'cc-nav--active', collapsed && 'cc-nav--collapsed']"
|
||||
role="button"
|
||||
:aria-current="active ? 'page' : undefined"
|
||||
:title="collapsed ? label : undefined"
|
||||
@click="emit('click')"
|
||||
>
|
||||
<span class="cc-nav__icon">
|
||||
<Icon :name="icon" :size="17" :stroke-width="2" />
|
||||
</span>
|
||||
<span v-if="!collapsed" class="cc-nav__label">{{ label }}</span>
|
||||
<span v-if="!collapsed && count != null" class="cc-nav__count">{{ count }}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.cc-nav { display:flex; align-items:center; gap:10px; height:34px; padding:0 10px; border-radius:var(--radius-md);
|
||||
color:var(--text-secondary); cursor:pointer; transition:var(--transition-colors); position:relative; user-select:none; }
|
||||
.cc-nav:hover { background:var(--surface-hover); color:var(--text-primary); }
|
||||
.cc-nav__icon { flex:none; color:var(--text-tertiary); display:flex; transition:var(--transition-colors); }
|
||||
.cc-nav__label { flex:1; font-size:var(--text-sm); font-weight:500; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
|
||||
.cc-nav__count { font-family:var(--font-mono); font-size:11px; color:var(--text-tertiary); padding:1px 6px; border-radius:var(--radius-pill); background:var(--surface-active); }
|
||||
.cc-nav--active { background:var(--accent-soft); color:var(--accent-text); }
|
||||
.cc-nav--active .cc-nav__icon { color:var(--accent-text); }
|
||||
.cc-nav--active::before { content:''; position:absolute; left:-10px; top:7px; bottom:7px; width:3px; border-radius:var(--radius-pill); background:var(--accent); }
|
||||
.cc-nav--active .cc-nav__count { background:var(--accent-soft-strong); color:var(--accent-text); }
|
||||
.cc-nav--collapsed { justify-content:center; padding:0; width:40px; }
|
||||
</style>
|
||||
Reference in New Issue
Block a user