store/resources/js/components/form/decimal-input/DecimalInput.vue

89 lines
2.3 KiB
Vue

<script setup lang="ts">
import type { HTMLAttributes } from 'vue';
import { computed } from 'vue';
import { Input } from '@/components/ui/input';
const props = defineProps<{
id?: string;
modelValue?: string | number;
class?: HTMLAttributes['class'];
placeholder?: string;
disabled?: boolean;
}>();
const emit = defineEmits<{
(event: 'update:modelValue', value: string): void;
}>();
// Formats JS float string/number (e.g. 1250.75) to ID decimal string (e.g. 1250,75)
function formatDecimal(value: string | number): string {
const str = String(value ?? '').trim();
if (!str) {
return '';
}
// Normalize to dot for processing
const normalized = str.replace(',', '.');
if (isNaN(parseFloat(normalized))) {
return '';
}
// Split into integer and decimal parts
const parts = normalized.split('.');
const integerPart = parts[0];
let decimalPart = parts[1];
if (decimalPart !== undefined) {
decimalPart = decimalPart.slice(0, 2);
return `${integerPart},${decimalPart}`;
}
return integerPart;
}
// Parses ID decimal string (e.g. 1250,75) back to JS float string (e.g. 1250.75)
function parseDecimal(value: string): string {
let normalized = value;
const hasComma = value.includes(',');
const dotCount = (value.match(/\./g) || []).length;
if (!hasComma && dotCount === 1) {
normalized = value.replace('.', ',');
}
// Remove all dots (if any were entered as thousands) and replace comma with dot
let clean = normalized.replace(/\./g, '').replace(',', '.');
const parts = clean.split('.');
if (parts.length > 2) {
clean = parts[0] + '.' + parts.slice(1).join('');
}
clean = clean.replace(/[^0-9.]/g, '');
const cleanParts = clean.split('.');
if (cleanParts[1] !== undefined) {
clean = `${cleanParts[0]}.${cleanParts[1].slice(0, 2)}`;
}
return clean;
}
const displayValue = computed({
get: () => formatDecimal(props.modelValue ?? ''),
set: (value: string) => {
emit('update:modelValue', parseDecimal(value));
},
});
</script>
<template>
<Input :id="id" v-model="displayValue" inputmode="decimal" autocomplete="off" :placeholder="placeholder ?? '0'"
:disabled="disabled" :class="props.class" />
</template>