All files / js/composables useNumberFormat.ts

92.3% Statements 24/26
70.83% Branches 17/24
100% Functions 7/7
92.3% Lines 24/26

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86                        5x     5x                         5x 5x 5x 5x   5x 5x 4x 4x 3x       5x   5x       5x       2x   2x 2x   2x 2x       3x                   1x 1x 1x         2x              
import { computed } from 'vue';
import { usePage } from '@inertiajs/vue3';
 
export interface NumberFormatProps {
    decimal_separator: string;
    thousands_separator: string;
}
 
/**
 * Group integer part of a digit string with thousands separators (ASCII digits only).
 */
function addThousandsGroups(intDigits: string, thousandsSep: string): string {
    Iif (!thousandsSep) {
        return intDigits;
    }
    return intDigits.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsSep);
}
 
/**
 * Format a number with min/max fraction digits using company separators (no currency symbol).
 */
export function formatDecimalWithSeparators(
    amount: number,
    minFractionDigits: number,
    maxFractionDigits: number,
    decimalSep: string,
    thousandsSep: string,
): string {
    const negative = amount < 0;
    const n = Math.abs(amount);
    const fixed = n.toFixed(maxFractionDigits);
    const [intPart, fracPart = ''] = fixed.split('.');
 
    let frac = fracPart;
    if (maxFractionDigits > 0) {
        frac = frac.replace(/0+$/, '');
        while (frac.length < minFractionDigits) {
            frac += '0';
        }
    }
 
    const grouped = addThousandsGroups(intPart, thousandsSep);
    const num =
        maxFractionDigits === 0 || frac.length === 0
            ? grouped
            : `${grouped}${decimalSep}${frac}`;
 
    return negative ? `-${num}` : num;
}
 
export function useNumberFormat() {
    const page = usePage();
 
    const decimalSeparator = computed(
        () => (page.props.numberFormat as NumberFormatProps | undefined)?.decimal_separator ?? '.',
    );
    const thousandsSeparator = computed(
        () => (page.props.numberFormat as NumberFormatProps | undefined)?.thousands_separator ?? ',',
    );
 
    function formatDecimal(amount: number, minFd = 0, maxFd = 2): string {
        return formatDecimalWithSeparators(
            amount,
            minFd,
            maxFd,
            decimalSeparator.value,
            thousandsSeparator.value,
        );
    }
 
    function formatCurrency(amount: number, currencyCode = 'ZAR'): string {
        const code = (currencyCode || 'ZAR').toUpperCase();
        Eif (code === 'ZAR') {
            return 'R' + formatDecimal(amount, 2, 2);
        }
        return new Intl.NumberFormat('en-ZA', { style: 'currency', currency: code }).format(amount ?? 0);
    }
 
    return {
        decimalSeparator,
        thousandsSeparator,
        formatDecimal,
        formatCurrency,
    };
}