V13-FE-011: finalize search list layout slice

This commit is contained in:
2026-08-09 02:57:26 +09:00
parent 9efd202e76
commit 6422cb2b13
984 changed files with 120811 additions and 1498 deletions
@@ -0,0 +1,36 @@
<script setup lang="ts">
import { computed, ref } from 'vue'
import type { KbxFieldState } from '@kbx/contracts'
const props=withDefaults(defineProps<{from:string|null;to:string|null;label:string;disabled?:boolean;readonly?:boolean;error?:string;warning?:string;helpText?:string;state?:KbxFieldState;presets?:boolean}>(),{state:'default',presets:true})
const emit=defineEmits<{ 'update:from':[string|null]; 'update:to':[string|null] }>()
const uid=`kbx-range-${Math.random().toString(36).slice(2)}`
const messageId=`${uid}-message`;const preset=ref('custom')
const effectiveState=computed(()=>props.error?'error':props.warning?'warning':props.state)
const message=computed(()=>props.error||props.warning||props.helpText||'')
function fmt(d:Date){const y=d.getFullYear();const m=String(d.getMonth()+1).padStart(2,'0');const day=String(d.getDate()).padStart(2,'0');return`${y}-${m}-${day}`}
function setRange(from:Date,to:Date){emit('update:from',fmt(from));emit('update:to',fmt(to))}
function applyPreset(value:string){
preset.value=value;if(value==='custom')return
const today=new Date();today.setHours(0,0,0,0);const from=new Date(today);const to=new Date(today)
if(value==='yesterday'){from.setDate(from.getDate()-1);to.setDate(to.getDate()-1)}
if(value==='last7'){from.setDate(from.getDate()-6)}
if(value==='thisMonth'){from.setDate(1)}
if(value==='lastMonth'){from.setDate(1);from.setMonth(from.getMonth()-1);to.setDate(0)}
setRange(from,to)
}
</script>
<template>
<div class="kbx-range-field" :data-state="effectiveState">
<span :id="`${uid}-label`" class="kbx-range__label">{{label}}</span>
<div class="kbx-range" role="group" :aria-labelledby="`${uid}-label`" :aria-describedby="message?messageId:undefined">
<input type="date" :value="from??''" :disabled="disabled" :readonly="readonly" aria-label="시작일" @input="emit('update:from',($event.target as HTMLInputElement).value||null);preset='custom'">
<span aria-hidden="true">~</span>
<input type="date" :value="to??''" :disabled="disabled" :readonly="readonly" aria-label="종료일" @input="emit('update:to',($event.target as HTMLInputElement).value||null);preset='custom'">
<select v-if="presets" :value="preset" :disabled="disabled||readonly" aria-label="빠른 기간 선택" @change="applyPreset(($event.target as HTMLSelectElement).value)">
<option value="today">오늘</option><option value="yesterday">어제</option><option value="last7">최근 7</option><option value="thisMonth">이번달</option><option value="lastMonth">지난달</option><option value="custom">직접선택</option>
</select>
</div>
<span v-if="message" :id="messageId" class="kbx-range__message" :role="error?'alert':undefined">{{message}}</span>
</div>
</template>
<style scoped>.kbx-range-field{display:grid;grid-template-columns:var(--kbx-label-width) minmax(0,1fr);gap:var(--kbx-space-2);align-items:start;font-size:var(--kbx-font-md)}.kbx-range__label{padding-top:var(--kbx-space-2);font-weight:500}.kbx-range{display:flex;align-items:center;gap:var(--kbx-space-1)}.kbx-range input,.kbx-range select{height:var(--kbx-control-height);border:var(--kbx-border-width) solid var(--kbx-color-border-strong);border-radius:var(--kbx-radius-sm);padding:0 var(--kbx-space-2);font:inherit;background:var(--kbx-color-surface);color:var(--kbx-color-text)}.kbx-range-field[data-state="changed"] input{border-color:var(--kbx-color-changed-border);background:var(--kbx-color-changed-surface)}.kbx-range-field[data-state="warning"] input{border-color:var(--kbx-color-warning-border);background:var(--kbx-color-warning-surface)}.kbx-range-field[data-state="ai-suggested"] input{border-color:var(--kbx-color-ai-border);background:var(--kbx-color-ai-surface)}.kbx-range-field[data-state="error"] input{border-color:var(--kbx-color-danger);background:var(--kbx-color-danger-surface)}.kbx-range__message{grid-column:2;font-size:var(--kbx-font-xs);color:var(--kbx-color-text-muted)}.kbx-range-field[data-state="warning"] .kbx-range__message{color:var(--kbx-color-warning-text)}.kbx-range-field[data-state="error"] .kbx-range__message{color:var(--kbx-color-danger)}.kbx-range-field[data-state="ai-suggested"] .kbx-range__message{color:var(--kbx-color-ai-text)}</style>