96 lines
2.3 KiB
Vue
96 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>
|