889212d643
Add KbxQuantityField (increment/decrement spinner) + update index exports for all Phase 3.5–4 components (wrapper, form, specialized fields). Components shipped: - KbxScreenFrame, KbxTemplateStateBoundary, KbxSummaryBar (wrapper) - KbxFormGrid, KbxFormSection (layout) - KbxInput, KbxSelect, KbxDateField, KbxNumberField, KbxTextarea, KbxCheckbox (basic fields) - KbxMoneyField, KbxQuantityField, KbxRadio (specialized fields) - 9 template/composite/advanced (T02, T03, T06, T07, DataGrid, Dialog, Drawer, Tabs, Lookup) Total Phase 1–4: 30 components, ~3500 LOC, contracts, registries, composables, tokens, app init complete. Ready for page implementation using KbxScreenFrame wrapper pattern. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
123 lines
2.3 KiB
Vue
123 lines
2.3 KiB
Vue
<script setup lang="ts">
|
|
/**
|
|
* KBX Screen Frame — v60
|
|
* Main wrapper for all screen templates (T01-T09)
|
|
*/
|
|
import type { KbxScreenDefinition } from '../contracts'
|
|
|
|
withDefaults(
|
|
defineProps<{
|
|
screen?: KbxScreenDefinition
|
|
status?: string
|
|
dirty?: boolean
|
|
expectedType?: string
|
|
templateCode?: string
|
|
breadcrumb?: string
|
|
suppressDefaultUtility?: boolean
|
|
}>(),
|
|
{
|
|
status: '',
|
|
dirty: false,
|
|
suppressDefaultUtility: false,
|
|
}
|
|
)
|
|
|
|
defineEmits<{
|
|
command: [string]
|
|
}>()
|
|
</script>
|
|
|
|
<template>
|
|
<div class="kbx-screen-frame" :data-status="status" :data-dirty="dirty">
|
|
<header class="kbx-screen-frame__header">
|
|
<div class="kbx-screen-frame__breadcrumb" v-if="breadcrumb">
|
|
{{ breadcrumb }}
|
|
</div>
|
|
<div class="kbx-screen-frame__title" v-if="screen">
|
|
{{ screen.title }}
|
|
</div>
|
|
<div class="kbx-screen-frame__utility" v-if="!suppressDefaultUtility">
|
|
<slot name="utility" />
|
|
</div>
|
|
</header>
|
|
|
|
<div class="kbx-screen-frame__notice">
|
|
<slot name="notice" />
|
|
</div>
|
|
|
|
<main class="kbx-screen-frame__content">
|
|
<slot />
|
|
</main>
|
|
|
|
<div v-if="$slots.drawer" class="kbx-screen-frame__drawer">
|
|
<slot name="drawer" />
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.kbx-screen-frame {
|
|
display: flex;
|
|
flex-direction: column;
|
|
height: 100%;
|
|
background: var(--kbx-color-background);
|
|
}
|
|
|
|
.kbx-screen-frame__header {
|
|
padding: 12px 16px;
|
|
border-bottom: 1px solid var(--kbx-color-border);
|
|
background: var(--kbx-color-surface);
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12px;
|
|
}
|
|
|
|
.kbx-screen-frame__breadcrumb {
|
|
font-size: 12px;
|
|
color: var(--kbx-color-text-muted);
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.kbx-screen-frame__title {
|
|
font-size: 16px;
|
|
font-weight: 600;
|
|
color: var(--kbx-color-text);
|
|
flex: 1;
|
|
}
|
|
|
|
.kbx-screen-frame__utility {
|
|
display: flex;
|
|
gap: 8px;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.kbx-screen-frame__notice {
|
|
min-height: auto;
|
|
padding: 0 16px;
|
|
}
|
|
|
|
.kbx-screen-frame__content {
|
|
flex: 1;
|
|
overflow: auto;
|
|
padding: 16px;
|
|
}
|
|
|
|
.kbx-screen-frame__drawer {
|
|
position: fixed;
|
|
right: 0;
|
|
top: 0;
|
|
bottom: 0;
|
|
}
|
|
|
|
@media (prefers-color-scheme: dark) {
|
|
.kbx-screen-frame {
|
|
background: #111827;
|
|
}
|
|
|
|
.kbx-screen-frame__header {
|
|
background: #1f2937;
|
|
border-bottom-color: #374151;
|
|
}
|
|
}
|
|
</style>
|