88 lines
5.0 KiB
Vue
88 lines
5.0 KiB
Vue
<script setup lang="ts">
|
||
import { ref } from 'vue'
|
||
defineProps<{ title: string; subtitle?: string; status?: string; asOf?: string; version?: string; asideWidth?: string }>()
|
||
const showHelp = ref(false)
|
||
</script>
|
||
<template>
|
||
<section class="ks-page" :style="{ '--ks-aside-width': asideWidth ?? '22rem' }">
|
||
<header class="ks-page__header">
|
||
<div class="ks-page__title-box">
|
||
<div class="ks-page__breadcrumb">K-ArtSell > Shadow Run Jobs</div>
|
||
<div class="ks-page__title-row">
|
||
<h1>{{ title }}</h1>
|
||
<button v-if="subtitle" class="ks-help-btn" :class="{ active: showHelp }" title="화면 설명 보기/닫기" aria-label="화면 설명 보기" @click="showHelp = !showHelp">
|
||
ℹ️ 도움말
|
||
</button>
|
||
</div>
|
||
<p v-if="subtitle && showHelp" class="ks-page__subtitle">{{ subtitle }}</p>
|
||
<div class="ks-page__meta"><span v-if="status">상태: {{ status }}</span><span v-if="asOf">As-of: {{ asOf }}</span><span v-if="version">Version: {{ version }}</span></div>
|
||
</div>
|
||
</header>
|
||
<!-- Dedicated KBX Command Bar Row (§3, §5) -->
|
||
<div v-if="$slots.commandBar || $slots.actions" class="ks-page__command-bar" role="toolbar" aria-label="화면 주요 명령어">
|
||
<div class="ks-page__command-bar-group">
|
||
<slot name="commandBar" />
|
||
<slot name="actions" />
|
||
</div>
|
||
</div>
|
||
<div v-if="$slots.summary" class="ks-page__summary"><slot name="summary" /></div>
|
||
<div v-if="$slots.filters" class="ks-page__filters"><slot name="filters" /></div>
|
||
<div class="ks-page__workspace" :class="{ 'has-aside': $slots.aside }">
|
||
<div class="ks-page__content"><slot /></div>
|
||
<aside v-if="$slots.aside" class="ks-page__aside"><slot name="aside" /></aside>
|
||
</div>
|
||
<footer v-if="$slots.footer" class="ks-page__footer"><slot name="footer" /></footer>
|
||
</section>
|
||
</template>
|
||
<style scoped>
|
||
.ks-page { width: 100%; max-width: var(--ks-content-max); margin: 0 auto; height: 100%; flex: 1; display: flex; flex-direction: column; gap: var(--ks-space-2); }
|
||
.ks-page__header { display: flex; align-items: center; justify-content: space-between; gap: var(--ks-space-4); flex-shrink: 0; min-height: 32px; border-bottom: 1px solid var(--ks-color-neutral-200); padding-bottom: var(--ks-space-2); }
|
||
.ks-page__title-box { display: flex; flex-direction: column; gap: 2px; }
|
||
.ks-page__breadcrumb { font-size: 11px; color: var(--ks-color-neutral-500); line-height: 14px; }
|
||
.ks-page__title-row { display: flex; align-items: center; gap: var(--ks-space-2); }
|
||
h1 { margin: 0; font-size: var(--ks-font-page); line-height: var(--ks-line-page); font-weight: 700; color: var(--ks-color-neutral-900); }
|
||
.ks-page__subtitle { margin: var(--ks-space-1) 0 0; color: var(--ks-color-neutral-600); font-size: var(--ks-font-body); padding: var(--ks-space-2); background: var(--ks-color-neutral-100); border-radius: var(--ks-radius-sm); border: 1px solid var(--ks-color-neutral-200); }
|
||
.ks-help-btn {
|
||
border: 1px solid var(--ks-color-action);
|
||
background: #eff6ff;
|
||
border-radius: var(--ks-radius-sm);
|
||
padding: 2px 8px;
|
||
font-size: 11px;
|
||
font-weight: 600;
|
||
color: var(--ks-color-action);
|
||
cursor: pointer;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 4px;
|
||
transition: all 0.15s ease;
|
||
}
|
||
.ks-help-btn:hover, .ks-help-btn.active {
|
||
background: var(--ks-color-action);
|
||
color: #ffffff;
|
||
border-color: var(--ks-color-action);
|
||
box-shadow: 0 1px 3px rgba(37, 99, 235, 0.25);
|
||
}
|
||
.ks-page__meta { display: flex; gap: var(--ks-space-3); flex-wrap: wrap; margin-top: 2px; color: var(--ks-color-neutral-600); font-size: var(--ks-font-caption); }
|
||
.ks-page__command-bar {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: flex-start;
|
||
padding: 4px 8px;
|
||
background: #f1f5f9;
|
||
border: 1px solid var(--ks-color-border);
|
||
border-radius: var(--ks-radius-sm);
|
||
flex-shrink: 0;
|
||
min-height: 34px;
|
||
box-sizing: border-box;
|
||
}
|
||
.ks-page__command-bar-group { display: flex; align-items: center; gap: var(--ks-space-2); flex-wrap: wrap; }
|
||
.ks-page__actions { display: flex; align-items: center; gap: var(--ks-space-2); flex-shrink: 0; }
|
||
.ks-page__summary { display: grid; grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr)); gap: var(--ks-space-3); flex-shrink: 0; }
|
||
.ks-page__filters { padding: var(--ks-space-2) var(--ks-space-3); flex-shrink: 0; background: var(--ks-color-surface); border: 1px solid var(--ks-color-border); border-radius: var(--ks-radius-sm); }
|
||
.ks-page__workspace { min-width: 0; flex: 1; display: grid; min-height: 0; gap: var(--ks-space-4); }
|
||
.ks-page__workspace.has-aside { grid-template-columns: minmax(0, 1fr) var(--ks-aside-width); }
|
||
.ks-page__content, .ks-page__aside { min-width: 0; min-height: 0; height: 100%; display: flex; flex-direction: column; }
|
||
.ks-page__footer { position: sticky; bottom: 0; z-index: 2; display: flex; justify-content: flex-end; gap: var(--ks-space-2); padding: var(--ks-space-3); border: 1px solid var(--ks-color-neutral-200); background: rgb(255 255 255 / 96%); }
|
||
@media (max-width: 1100px) { .ks-page__workspace.has-aside { grid-template-columns: 1fr; } }
|
||
</style>
|