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:
86
frontend/src/components/ds/feedback/Alert.vue
Normal file
86
frontend/src/components/ds/feedback/Alert.vue
Normal file
@@ -0,0 +1,86 @@
|
||||
<script setup lang="ts">
|
||||
/**
|
||||
* Alert — contextual inline alert strip.
|
||||
* Tones: info | warn | danger | online | accent | neutral.
|
||||
* Pass `title` for a bold heading, default slot for body text, `actions` slot
|
||||
* for inline action buttons. Set `dismissible` to show an × ghost button that
|
||||
* emits `dismiss`.
|
||||
*/
|
||||
import Icon from '@/components/ds/core/Icon.vue'
|
||||
|
||||
type Tone = 'info' | 'warn' | 'danger' | 'online' | 'accent' | 'neutral'
|
||||
|
||||
const ICONS: Record<Tone, string> = {
|
||||
info: 'info',
|
||||
warn: 'triangle-alert',
|
||||
danger: 'octagon-alert',
|
||||
online: 'circle-check',
|
||||
accent: 'sparkles',
|
||||
neutral: 'info',
|
||||
}
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
tone?: Tone
|
||||
title?: string
|
||||
dismissible?: boolean
|
||||
icon?: string
|
||||
}>(),
|
||||
{ tone: 'info', dismissible: false },
|
||||
)
|
||||
|
||||
defineEmits<{ dismiss: [] }>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
:class="['cc-alert', 'cc-alert--' + tone]"
|
||||
role="status"
|
||||
>
|
||||
<span class="cc-alert__icon">
|
||||
<Icon :name="icon ?? ICONS[tone]" :size="17" :stroke-width="2" />
|
||||
</span>
|
||||
<div class="cc-alert__main">
|
||||
<div v-if="title" class="cc-alert__title">{{ title }}</div>
|
||||
<div v-if="$slots.default" class="cc-alert__body"><slot /></div>
|
||||
<div v-if="$slots.actions" class="cc-alert__actions"><slot name="actions" /></div>
|
||||
</div>
|
||||
<button
|
||||
v-if="dismissible"
|
||||
class="cc-alert__dismiss"
|
||||
type="button"
|
||||
aria-label="Dismiss"
|
||||
@click="$emit('dismiss')"
|
||||
>
|
||||
<Icon name="x" :size="15" :stroke-width="2.25" />
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.cc-alert { display:flex; gap:11px; padding:12px 13px; border-radius:var(--radius-md); background:var(--surface-raised); box-shadow:var(--ring-default); }
|
||||
.cc-alert__icon { flex:none; margin-top:1px; }
|
||||
.cc-alert__main { flex:1; min-width:0; display:flex; flex-direction:column; gap:3px; }
|
||||
.cc-alert__title { font-size:var(--text-sm); font-weight:600; color:var(--text-primary); }
|
||||
.cc-alert__body { font-size:var(--text-xs); color:var(--text-secondary); line-height:1.5; }
|
||||
.cc-alert__actions { display:flex; gap:8px; margin-top:8px; }
|
||||
.cc-alert--info { background:var(--status-info-soft); box-shadow: inset 0 0 0 1px var(--status-info-border); }
|
||||
.cc-alert--info .cc-alert__icon { color:var(--status-info); }
|
||||
.cc-alert--warn { background:var(--status-warn-soft); box-shadow: inset 0 0 0 1px var(--status-warn-border); }
|
||||
.cc-alert--warn .cc-alert__icon { color:var(--status-warn); }
|
||||
.cc-alert--danger { background:var(--status-offline-soft); box-shadow: inset 0 0 0 1px var(--status-offline-border); }
|
||||
.cc-alert--danger .cc-alert__icon { color:var(--status-offline); }
|
||||
.cc-alert--online { background:var(--status-online-soft); box-shadow: inset 0 0 0 1px var(--status-online-border); }
|
||||
.cc-alert--online .cc-alert__icon { color:var(--status-online); }
|
||||
.cc-alert--accent { background:var(--accent-soft); box-shadow: inset 0 0 0 1px var(--accent-border); }
|
||||
.cc-alert--accent .cc-alert__icon { color:var(--accent-text); }
|
||||
.cc-alert__dismiss {
|
||||
flex: none; display:inline-flex; align-items:center; justify-content:center;
|
||||
width:26px; height:26px; border-radius:var(--radius-sm); border:none; cursor:pointer;
|
||||
background:transparent; color:var(--text-secondary);
|
||||
transition: var(--transition-colors);
|
||||
margin-top:-3px; margin-right:-3px;
|
||||
}
|
||||
.cc-alert__dismiss:hover { background:var(--surface-hover); color:var(--text-primary); }
|
||||
.cc-alert__dismiss:focus-visible { outline:none; box-shadow:var(--focus-ring); }
|
||||
</style>
|
||||
Reference in New Issue
Block a user