UNPKG

custom-card-helpers

Version:

Set of helpful functions and types for Custom Card creators

114 lines (102 loc) 3.65 kB
//REF: https://github.com/home-assistant/frontend/blob/dev/src/common/number/format_number.ts import { HassEntity } from "home-assistant-js-websocket"; import { FrontendLocaleData, NumberFormat } from "./types"; /** * Returns true if the entity is considered numeric based on the attributes it has * @param stateObj The entity state object */ export const isNumericState = (stateObj: HassEntity): boolean => !!stateObj.attributes.unit_of_measurement || !!stateObj.attributes.state_class; export const numberFormatToLocale = ( localeOptions: FrontendLocaleData ): string | string[] | undefined => { switch (localeOptions.number_format) { case NumberFormat.comma_decimal: return ["en-US", "en"]; // Use United States with fallback to English formatting 1,234,567.89 case NumberFormat.decimal_comma: return ["de", "es", "it"]; // Use German with fallback to Spanish then Italian formatting 1.234.567,89 case NumberFormat.space_comma: return ["fr", "sv", "cs"]; // Use French with fallback to Swedish and Czech formatting 1 234 567,89 case NumberFormat.system: return undefined; default: return localeOptions.language; } }; export const round = (value: number, precision = 2): number => Math.round(value * 10 ** precision) / 10 ** precision; /** * Formats a number based on the specified language with thousands separator(s) and decimal character for better legibility. * @param num The number to format * @param locale The user-selected language and number format, from `hass.locale` * @param options Intl.NumberFormatOptions to use */ export const formatNumber = ( num: string | number, localeOptions?: FrontendLocaleData, options?: Intl.NumberFormatOptions ): string => { const locale = localeOptions ? numberFormatToLocale(localeOptions) : undefined; // Polyfill for Number.isNaN, which is more reliable than the global isNaN() Number.isNaN = Number.isNaN || function isNaN(input) { return typeof input === "number" && isNaN(input); }; if ( localeOptions?.number_format !== NumberFormat.none && !Number.isNaN(Number(num)) && Intl ) { try { return new Intl.NumberFormat( locale, getDefaultFormatOptions(num, options) ).format(Number(num)); } catch (err: any) { // Don't fail when using "TEST" language // eslint-disable-next-line no-console console.error(err); return new Intl.NumberFormat( undefined, getDefaultFormatOptions(num, options) ).format(Number(num)); } } if (typeof num === "string") { return num; } return `${round(num, options?.maximumFractionDigits).toString()}${ options?.style === "currency" ? ` ${options.currency}` : "" }`; }; /** * Generates default options for Intl.NumberFormat * @param num The number to be formatted * @param options The Intl.NumberFormatOptions that should be included in the returned options */ const getDefaultFormatOptions = ( num: string | number, options?: Intl.NumberFormatOptions ): Intl.NumberFormatOptions => { const defaultOptions: Intl.NumberFormatOptions = { maximumFractionDigits: 2, ...options, }; if (typeof num !== "string") { return defaultOptions; } // Keep decimal trailing zeros if they are present in a string numeric value if ( !options || (!options.minimumFractionDigits && !options.maximumFractionDigits) ) { const digits = num.indexOf(".") > -1 ? num.split(".")[1].length : 0; defaultOptions.minimumFractionDigits = digits; defaultOptions.maximumFractionDigits = digits; } return defaultOptions; };