EngineTabs.vue 1.97 KB
<script setup lang="ts">
import type { AppName, AppStatusInfo } from '@/types'
import { appLabel, statusTone } from '@/utils/format'

defineProps<{
  appNames: AppName[]
  activeApp: AppName
  appStatus: Record<AppName, AppStatusInfo>
}>()

defineEmits<{
  'update:activeApp': [value: AppName]
}>()
</script>

<template>
  <div class="engine-tabs">
    <button
      v-for="name in appNames"
      :key="name"
      class="engine-tab"
      :class="{ 'engine-tab--active': activeApp === name }"
      type="button"
      @click="$emit('update:activeApp', name)"
    >
      <span class="tab-dot" :data-tone="statusTone(appStatus[name]?.status || 'stopped')" />
      <strong>{{ appLabel(name) }}</strong>
      <small>{{ appStatus[name]?.status || 'stopped' }}</small>
    </button>
  </div>
</template>

<style scoped>
.engine-tabs {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 12px;
}

.engine-tab {
  display: grid;
  gap: 8px;
  padding: 16px 18px;
  border-radius: 22px;
  border: 1px solid var(--border-soft);
  background: rgba(255, 255, 255, 0.7);
  cursor: pointer;
  text-align: left;
  transition: transform 0.18s ease, border-color 0.18s ease, background-color 0.18s ease;
}

.engine-tab strong {
  font-size: 15px;
}

.engine-tab small {
  font-family: var(--font-mono);
  color: var(--text-muted);
}

.engine-tab:hover {
  transform: translateY(-1px);
  border-color: var(--border-strong);
}

.engine-tab--active {
  border-color: rgba(31, 92, 76, 0.3);
  background: rgba(31, 92, 76, 0.08);
}

.tab-dot {
  width: 10px;
  height: 10px;
  border-radius: 999px;
  background: rgba(23, 27, 24, 0.18);
}

.tab-dot[data-tone='success'] {
  background: #2f785f;
}

.tab-dot[data-tone='running'] {
  background: #a46c39;
}

.tab-dot[data-tone='danger'] {
  background: #8b3d35;
}

@media (max-width: 960px) {
  .engine-tabs {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 680px) {
  .engine-tabs {
    grid-template-columns: 1fr;
  }
}
</style>