parsing.tsx
1 import React from "react"; 2 3 /** 4 * Insert blankspace thousands separator into a number 5 * Supports both period or comma as decimal separator 6 * @param input 7 * @returns 8 */ 9 export function formatNumberWithSpaceSeparator(input: string): string { 10 const isPeriodDecimalSeparator = input.indexOf(".") > -1; 11 12 const parts = input.split(isPeriodDecimalSeparator ? "." : ","); 13 14 const integerPart = parts[0]; 15 const decimalPart = parts[1] || ""; 16 17 // Add the thousands separator (blank space) to the integer part 18 const formattedIntegerPart = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, " "); 19 20 // Combine the formatted integer and decimal parts 21 const suffix = decimalPart ? (isPeriodDecimalSeparator ? "." : ",") + decimalPart : ""; 22 const formattedNumber = formattedIntegerPart + suffix; 23 24 return formattedNumber; 25 } 26 27 /** 28 * Number formatter compatible with Ant Design's InputNumber component, handling UX properly. 29 * @param value 30 * @param info 31 * @returns 32 */ 33 export function formatNumberOnUserInput( 34 value: number | string | undefined, 35 info: { userTyping: boolean; input: string }, 36 ): string { 37 if (info.userTyping) { 38 return info.input; 39 } 40 return numberFormatter(value); 41 } 42 43 /** 44 * Number formatter that nicely formats numbers with correct decimal separator based on locale 45 * Always uses blank space as thousands separator to prevent confusion with the decimal separator 46 * @param value 47 * @returns 48 */ 49 export function numberFormatter(value: number | string | undefined): string { 50 const formattedValue = value 51 ? Number(value).toLocaleString(undefined, { 52 useGrouping: false, // Disable thousands separator and do it manually instead so it's always spaces 53 }) 54 : ""; 55 56 return formatNumberWithSpaceSeparator(formattedValue); 57 } 58 59 /** 60 * Number parser that supports both comma and dot as decimal separator 61 * @param value 62 * @returns 63 */ 64 export function numberParser(value: string | undefined): number { 65 // Convert comma to dot 66 value = value?.replace(",", "."); 67 68 // Remove all non-digit characters 69 value = value?.replace(/[^\d.-]/g, ""); 70 71 // Parse as float 72 return parseFloat(value || "0"); 73 } 74 75 /** 76 * Number parser that supports both comma and dot as decimal separator 77 * Same as numberParser but allows empty values. numberParser will always return a valid number 78 * this one returns an empty string if the value is empty 79 * @param value 80 * @returns 81 */ 82 export function numberParserAllowEmpty(value: string | undefined): number | string { 83 // Convert comma to dot 84 value = value?.replace(",", "."); 85 86 // Remove all non-digit characters 87 value = value?.replace(/[^\d.-]/g, ""); 88 89 if (value === "" || value === undefined) { 90 return ""; 91 } 92 93 // Parse as float 94 return parseFloat(value); 95 } 96 97 /** 98 * Enrich text with links 99 * @param text 100 * @returns 101 */ 102 export function enrichText(text: string | undefined) { 103 // Regular expression to match URLs 104 const urlRegex = 105 /(https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|www\.[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9]+\.[^\s]{2,}|www\.[a-zA-Z0-9]+\.[^\s]{2,})/gi; 106 107 // Split the input text by URLs 108 const parts = (text ?? "").split(urlRegex); 109 110 // Convert URLs to <a> tags 111 const elements = parts.map((part, index) => { 112 if (part.match(urlRegex)) { 113 return ( 114 <a href={part} key={index} target="_blank" rel="noopener noreferrer"> 115 {part} 116 </a> 117 ); 118 } else { 119 return <React.Fragment key={index}>{part}</React.Fragment>; 120 } 121 }); 122 123 return <>{elements}</>; 124 } 125 126 /** 127 * Formats the weight in grams to either kilograms or grams based on the provided precision. 128 * 129 * @param {number} weightInGrams - The weight in grams to be formatted. 130 * @param {number} [precision=2] - The precision of the formatting (number of decimal places). 131 * @return {string} The formatted weight with the appropriate unit (kg or g). 132 */ 133 export function formatWeight(weightInGrams: number, precision: number = 2): string { 134 if (weightInGrams >= 1000) { 135 const kilograms = removeTrailingZeros((weightInGrams / 1000).toFixed(precision)); 136 return `${kilograms} kg`; 137 } else { 138 const grams = removeTrailingZeros(weightInGrams.toFixed(precision)); 139 return `${grams} g`; 140 } 141 } 142 143 /** 144 * Formats the length in millimeters to either meters or millimeters based on the provided precision. 145 * 146 * @param {number} lengthInMillimeter - The length in millimeters to be formatted. 147 * @param {number} [precision=2] - The precision of the formatting (number of decimal places). 148 * @return {string} The formatted length with the appropriate unit (m or mm). 149 */ 150 export function formatLength(lengthInMillimeter: number, precision: number = 2): string { 151 if (lengthInMillimeter >= 1000) { 152 const meters = removeTrailingZeros((lengthInMillimeter / 1000).toFixed(precision)); 153 return `${meters} m`; 154 } else { 155 return `${lengthInMillimeter} mm`; 156 } 157 } 158 159 /** 160 * Removes trailing zeros from a numeric string, including unnecessary decimal points. 161 * 162 * This function takes a string representation of a number and removes any trailing zeros 163 * after the decimal point. If the number ends with a decimal point followed by only zeros, 164 * the entire decimal portion is removed. 165 * 166 * @param num - The numeric string to process. 167 * @returns The numeric string with trailing zeros removed. 168 * 169 * @example 170 * ```typescript 171 * removeTrailingZeros("123.45000"); // Returns "123.45" 172 * removeTrailingZeros("100.000"); // Returns "100" 173 * removeTrailingZeros("0.0000"); // Returns "0" 174 * ``` 175 */ 176 function removeTrailingZeros(num: string): string { 177 return num.replace(/(\.\d*?[1-9])0+|\.0*$/, "$1"); 178 }