Files
web/src/components/MoneyInput.vue
T

77 lines
1.6 KiB
Vue
Raw Normal View History

<script setup lang="ts">
import { computed } from 'vue'
import {
fromGroupedInput,
rialToToman,
toGroupedDisplay,
tomanToRial,
} from '@/utils/money'
/**
* Money field bound to **rial** storage values (API/DB).
* User types and sees **toman** (÷10 / ×10 at the edges).
* With `nullable`, empty input emits `null` (for optional thresholds).
*/
const props = withDefaults(
defineProps<{
value: number | null | undefined
size?: 'large' | 'middle' | 'small'
placeholder?: string
allowNegative?: boolean
disabled?: boolean
nullable?: boolean
}>(),
{
size: 'large',
placeholder: '',
allowNegative: false,
disabled: false,
nullable: false,
},
)
const emit = defineEmits<{
(e: 'update:value', value: number | null): void
}>()
const display = computed(() => {
if (props.nullable && (props.value == null || props.value === undefined)) return ''
return toGroupedDisplay(rialToToman(props.value ?? 0))
})
function onInput(raw: string | number) {
const text = String(raw ?? '').trim()
if (props.nullable && !text) {
emit('update:value', null)
return
}
const toman = fromGroupedInput(text, props.allowNegative)
emit('update:value', tomanToRial(toman))
}
</script>
<template>
<a-input
class="money-input"
dir="ltr"
:size="size"
:placeholder="placeholder"
:disabled="disabled"
:value="display"
allow-clear
@update:value="onInput"
/>
</template>
<style scoped>
.money-input {
width: 100%;
}
.money-input :deep(input) {
font-variant-numeric: tabular-nums;
text-align: left;
direction: ltr;
}
</style>