264 lines
7.3 KiB
TypeScript
264 lines
7.3 KiB
TypeScript
/**
|
|
* Format number with thousand separators
|
|
* @param value - Number to format
|
|
* @param decimals - Number of decimal places (default: 0)
|
|
* @param decimalSeparator - Decimal separator (default: ',')
|
|
* @param thousandSeparator - Thousand separator (default: '.')
|
|
* @returns Formatted number string
|
|
*
|
|
* @example
|
|
* numberFormat(1234567.89) // "1.234.567"
|
|
* numberFormat(1234567.89, 2) // "1.234.567,89"
|
|
* numberFormat(1234.5, 2, '.', ',') // "1,234.50"
|
|
*/
|
|
export function numberFormat(
|
|
value: number | string | null | undefined,
|
|
decimals: number = 0,
|
|
decimalSeparator: string = ',',
|
|
thousandSeparator: string = '.'
|
|
): string {
|
|
if (value === null || value === undefined || value === '') {
|
|
return '0';
|
|
}
|
|
|
|
const num = typeof value === 'string' ? parseFloat(value) : value;
|
|
|
|
if (isNaN(num)) {
|
|
return '0';
|
|
}
|
|
|
|
const fixedNum = num.toFixed(decimals);
|
|
const parts = fixedNum.split('.');
|
|
|
|
// Format integer part with thousand separator
|
|
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, thousandSeparator);
|
|
|
|
// Join with decimal separator if decimals exist
|
|
if (decimals > 0 && parts[1]) {
|
|
return parts.join(decimalSeparator);
|
|
}
|
|
|
|
return parts[0];
|
|
}
|
|
|
|
/**
|
|
* Format number as currency (Indonesian Rupiah)
|
|
* @param value - Number to format
|
|
* @param decimals - Number of decimal places (default: 0)
|
|
* @param prefix - Currency prefix (default: 'Rp ')
|
|
* @returns Formatted currency string
|
|
*
|
|
* @example
|
|
* currencyFormat(1234567) // "Rp 1.234.567"
|
|
* currencyFormat(1234567.89, 2) // "Rp 1.234.567,89"
|
|
*/
|
|
export function currencyFormat(
|
|
value: number | string | null | undefined,
|
|
decimals: number = 0,
|
|
prefix: string = 'Rp '
|
|
): string {
|
|
return prefix + numberFormat(value, decimals);
|
|
}
|
|
|
|
/**
|
|
* Format number as percentage
|
|
* @param value - Number to format (0-100 or 0-1)
|
|
* @param decimals - Number of decimal places (default: 2)
|
|
* @param isDecimal - Whether value is in decimal form (0-1) or percentage form (0-100)
|
|
* @returns Formatted percentage string
|
|
*
|
|
* @example
|
|
* percentageFormat(45.5) // "45,50%"
|
|
* percentageFormat(0.455, 2, true) // "45,50%"
|
|
*/
|
|
export function percentageFormat(
|
|
value: number | string | null | undefined,
|
|
decimals: number = 2,
|
|
isDecimal: boolean = false
|
|
): string {
|
|
if (value === null || value === undefined || value === '') {
|
|
return '0%';
|
|
}
|
|
|
|
const num = typeof value === 'string' ? parseFloat(value) : value;
|
|
|
|
if (isNaN(num)) {
|
|
return '0%';
|
|
}
|
|
|
|
const percentage = isDecimal ? num * 100 : num;
|
|
return numberFormat(percentage, decimals) + '%';
|
|
}
|
|
|
|
/**
|
|
* Format file size
|
|
* @param bytes - File size in bytes
|
|
* @param decimals - Number of decimal places (default: 2)
|
|
* @returns Formatted file size string
|
|
*
|
|
* @example
|
|
* fileSizeFormat(1024) // "1,00 KB"
|
|
* fileSizeFormat(1048576) // "1,00 MB"
|
|
*/
|
|
export function fileSizeFormat(
|
|
bytes: number | string | null | undefined,
|
|
decimals: number = 2
|
|
): string {
|
|
if (bytes === null || bytes === undefined || bytes === '') {
|
|
return '0 Bytes';
|
|
}
|
|
|
|
const num = typeof bytes === 'string' ? parseFloat(bytes) : bytes;
|
|
|
|
if (num === 0) return '0 Bytes';
|
|
if (isNaN(num)) return '0 Bytes';
|
|
|
|
const k = 1024;
|
|
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB'];
|
|
const i = Math.floor(Math.log(Math.abs(num)) / Math.log(k));
|
|
|
|
return numberFormat(num / Math.pow(k, i), decimals) + ' ' + sizes[i];
|
|
}
|
|
|
|
/**
|
|
* Abbreviate large numbers
|
|
* @param value - Number to abbreviate
|
|
* @param decimals - Number of decimal places (default: 1)
|
|
* @returns Abbreviated number string
|
|
*
|
|
* @example
|
|
* abbreviateNumber(1234) // "1,2 K"
|
|
* abbreviateNumber(1234567) // "1,2 M"
|
|
* abbreviateNumber(1234567890) // "1,2 B"
|
|
*/
|
|
export function abbreviateNumber(
|
|
value: number | string | null | undefined,
|
|
decimals: number = 1
|
|
): string {
|
|
if (value === null || value === undefined || value === '') {
|
|
return '0';
|
|
}
|
|
|
|
const num = typeof value === 'string' ? parseFloat(value) : value;
|
|
|
|
if (isNaN(num)) {
|
|
return '0';
|
|
}
|
|
|
|
if (Math.abs(num) < 1000) {
|
|
return numberFormat(num, 0);
|
|
}
|
|
|
|
const units = ['K', 'M', 'B', 'T'];
|
|
const unit = Math.floor((Math.abs(num).toString().length - 1) / 3);
|
|
const unitValue = Math.pow(1000, unit);
|
|
const shortValue = num / unitValue;
|
|
|
|
return numberFormat(shortValue, decimals) + ' ' + units[unit - 1];
|
|
}
|
|
|
|
/**
|
|
* Parse formatted number string back to number
|
|
* @param value - Formatted number string
|
|
* @param decimalSeparator - Decimal separator used in the string
|
|
* @param thousandSeparator - Thousand separator used in the string
|
|
* @returns Parsed number
|
|
*
|
|
* @example
|
|
* parseNumber("1.234.567,89") // 1234567.89
|
|
* parseNumber("1,234.50", '.', ',') // 1234.50
|
|
*/
|
|
export function parseNumber(
|
|
value: string | null | undefined,
|
|
decimalSeparator: string = ',',
|
|
thousandSeparator: string = '.'
|
|
): number {
|
|
if (!value) return 0;
|
|
|
|
// Remove thousand separators and replace decimal separator with dot
|
|
const cleanValue = value
|
|
.replace(new RegExp('\\' + thousandSeparator, 'g'), '')
|
|
.replace(decimalSeparator, '.');
|
|
|
|
const num = parseFloat(cleanValue);
|
|
return isNaN(num) ? 0 : num;
|
|
}
|
|
|
|
/**
|
|
* Format phone number to Indonesian format
|
|
* @param value - Phone number to format
|
|
* @returns Formatted phone number
|
|
*
|
|
* @example
|
|
* phoneFormat("081234567890") // "0812-3456-7890"
|
|
* phoneFormat("628123456789") // "62-812-3456-7890"
|
|
*/
|
|
export function phoneFormat(value: string | null | undefined): string {
|
|
if (!value) return '';
|
|
|
|
const cleaned = value.replace(/\D/g, '');
|
|
|
|
if (cleaned.startsWith('62')) {
|
|
// International format
|
|
const match = cleaned.match(/^(\d{2})(\d{3})(\d{4})(\d+)$/);
|
|
if (match) {
|
|
return `${match[1]}-${match[2]}-${match[3]}-${match[4]}`;
|
|
}
|
|
} else if (cleaned.startsWith('0')) {
|
|
// Local format
|
|
const match = cleaned.match(/^(\d{4})(\d{4})(\d+)$/);
|
|
if (match) {
|
|
return `${match[1]}-${match[2]}-${match[3]}`;
|
|
}
|
|
}
|
|
|
|
return cleaned;
|
|
}
|
|
|
|
/**
|
|
* Capitalize first letter of each word
|
|
* @param value - String to capitalize
|
|
* @returns Capitalized string
|
|
*
|
|
* @example
|
|
* capitalize("hello world") // "Hello World"
|
|
*/
|
|
export function capitalize(value: string | null | undefined): string {
|
|
if (!value) return '';
|
|
|
|
return value
|
|
.toLowerCase()
|
|
.split(' ')
|
|
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
|
.join(' ');
|
|
}
|
|
|
|
/**
|
|
* Truncate string with ellipsis
|
|
* @param value - String to truncate
|
|
* @param length - Maximum length
|
|
* @param suffix - Suffix to add (default: '...')
|
|
* @returns Truncated string
|
|
*
|
|
* @example
|
|
* truncate("Hello World", 8) // "Hello..."
|
|
*/
|
|
export function truncate(
|
|
value: string | null | undefined,
|
|
length: number,
|
|
suffix: string = '...'
|
|
): string {
|
|
if (!value) return '';
|
|
if (value.length <= length) return value;
|
|
|
|
return value.substring(0, length) + suffix;
|
|
}
|
|
|
|
export function formatDate(date: string | Date | null | undefined, options?: Intl.DateTimeFormatOptions): string {
|
|
if (!date) return '-';
|
|
return new Date(date).toLocaleDateString('id-ID', {
|
|
year: 'numeric',
|
|
month: 'short',
|
|
day: 'numeric'
|
|
});
|
|
}; |