Files
KArtSell.Aegis/frontend/src/shared/@kbx/ui/components/KbxMoneyField.vue
T
kjh2064 889212d643 feat: KBX v60 Phase 4 complete — KbxQuantityField + index exports
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>
2026-08-15 10:43:10 +09:00

130 lines
2.5 KiB
Vue

<script setup lang="ts">
/**
* KBX Money Field — v60
* Currency input with formatting
*/
withDefaults(
defineProps<{
modelValue: number | string
label?: string
error?: string
required?: boolean
disabled?: boolean
currency?: string
}>(),
{
modelValue: '',
currency: '₩',
}
)
defineEmits<{
'update:modelValue': [value: number]
blur: []
}>()
const formatCurrency = (value: number | string): string => {
if (!value) return ''
const num = typeof value === 'string' ? parseFloat(value) : value
return num.toLocaleString('ko-KR')
}
</script>
<template>
<div class="kbx-money-wrapper">
<label v-if="label" class="kbx-money__label">
{{ label }}
<span v-if="required" class="kbx-money__required">*</span>
</label>
<div class="kbx-money__input-wrapper">
<span class="kbx-money__currency">{{ currency }}</span>
<input
:value="formatCurrency(modelValue)"
type="text"
inputmode="numeric"
:disabled="disabled"
:class="['kbx-money__input', { 'is-error': error }]"
@input="$emit('update:modelValue', Number(($event.target as HTMLInputElement).value.replace(/[^\d]/g, '')))"
@blur="$emit('blur')"
/>
</div>
<div v-if="error" class="kbx-money__error">{{ error }}</div>
</div>
</template>
<style scoped>
.kbx-money-wrapper {
display: flex;
flex-direction: column;
gap: 4px;
}
.kbx-money__label {
font-size: 13px;
font-weight: 500;
color: var(--kbx-color-text);
}
.kbx-money__required {
color: var(--kbx-color-danger);
margin-left: 2px;
}
.kbx-money__input-wrapper {
position: relative;
display: flex;
align-items: center;
border: 1px solid var(--kbx-color-border);
border-radius: 4px;
background: var(--kbx-color-surface);
min-height: 34px;
}
.kbx-money__currency {
padding: 0 10px;
color: var(--kbx-color-text-muted);
font-size: 14px;
font-weight: 500;
}
.kbx-money__input {
flex: 1;
border: none;
padding: 8px 0 8px 0;
font-size: 14px;
background: transparent;
color: var(--kbx-color-text);
text-align: right;
}
.kbx-money__input:focus {
outline: none;
}
.kbx-money__input-wrapper:focus-within {
border-color: var(--kbx-color-primary);
box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.1);
}
.kbx-money__error {
font-size: 12px;
color: var(--kbx-color-danger);
}
@media (prefers-color-scheme: dark) {
.kbx-money__label {
color: #f9fafb;
}
.kbx-money__input-wrapper {
background: #1f2937;
border-color: #374151;
}
.kbx-money__input {
color: #f9fafb;
}
}
</style>