web_lib

Common web application libraries
git clone https://radroots.dev/git/web_lib.git
Log | Files | Refs | LICENSE

commit 642c1def6e899f383293045423009ddb471a4e20
parent 3370defc76916b0c86ce0bea2a38a5f97e5d367c
Author: triesap <137732411+triesap@users.noreply.github.com>
Date:   Wed, 13 Nov 2024 08:19:32 +0000

utils: add/edit utils

Diffstat:
Mutils/src/currency.ts | 125++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------
Mutils/src/file.ts | 14+++++++++++++-
Mutils/src/list.ts | 6++++++
Mutils/src/math.ts | 3+++
Mutils/src/regex.ts | 13+++++++++++--
Mutils/src/time.ts | 12+++++-------
Mutils/src/types.ts | 7+++++--
Mutils/src/units.ts | 13+++++++++++++
8 files changed, 157 insertions(+), 36 deletions(-)

diff --git a/utils/src/currency.ts b/utils/src/currency.ts @@ -1,28 +1,17 @@ +import { regex } from "./regex"; + export type FiatCurrency = `usd` | `eur`; export const fiat_currencies: FiatCurrency[] = [`usd`, `eur`] as const; export type FiatCurrencyGlyphs = `dollar` | `eur`; -export type CurrencyPrice = { - symbol: string; - currency: FiatCurrency; - /** - * integer value - */ - val_i: number; - /** - * fractional value - */ - val_f: number; -}; - export type CurrencyPriceFmt = [string, FiatCurrency, number, number] export function parse_currency(val?: string): FiatCurrency { const _val = val?.trim().toLowerCase() switch (_val) { - case "usd": - case "eur": + case `usd`: + case `eur`: return _val; default: return `usd`; @@ -40,23 +29,111 @@ export function parse_currency_glyph_key(val?: string): | `currency-${FiatCurren }; }; -export const parse_currency_price = (locale: string, _currency: string, amount: number): CurrencyPrice => { - const currency = parse_currency(_currency); +export type CurrencyDecimalSeparator = `,` | `.`; + +export type CurrencyMetadata = { + cur: FiatCurrency; + /** + * currency symbol + */ + cur_s: string; + /** + * currency marker + */ + cur_m: string; + /** + * true if symbol is at the start + */ + cur_pos: boolean; + dec_s: CurrencyDecimalSeparator; +} + + +export type CurrencyPrice = CurrencyMetadata & { + /** + * integer num + */ + num_i: number; + /** + * fractional num + */ + num_f: number; + /** + * integer value + */ + val_i: string; + /** + * fractional value + */ + val_f: string; +}; + +export const locale_fractional_decimal = (locale: string): CurrencyDecimalSeparator => { + const formatter = new Intl.NumberFormat(locale); + const formatted = formatter.format(1.1); + return formatted.includes(',') ? `,` : `.`; +}; + +export const parse_currency_marker = (locale: string, currency: string): string => { + const cur = parse_currency(currency); + const fmt = new Intl.NumberFormat(`en-US`, { + style: 'currency', + currency: cur.toUpperCase(), + minimumFractionDigits: 2, + }); + const fmt_basis = fmt.format(1); + let fmt_res: string | undefined = undefined; + fmt_res = fmt_basis.match(regex.currency_marker)?.[0]; + if (fmt_res) return fmt_res; + fmt_res = fmt_basis.match(regex.currency_symbol)?.[0]; + if (fmt_res) return fmt_res; + fmt_res = fmt_basis.match(new RegExp(cur, `i`))?.[0]; + if (fmt_res) return fmt_res; + return cur.toUpperCase(); +} + +export const parse_currency_price = (locale: string, currency: string, price: number | string): CurrencyPrice | undefined => { + const num_amt = Math.max(typeof price === `number` ? price : Number(price.replace(/[^0-9.]/g, ``)), 0); + const cur = parse_currency(currency); const fmt = new Intl.NumberFormat(locale, { style: 'currency', - currency: currency.toUpperCase(), + currency: cur.toUpperCase(), minimumFractionDigits: 2, }); - const fmt_amt = fmt.format(amount); - const [symbol_val_i, val_f] = fmt_amt.split('.'); + const fmt_amt = fmt.format(num_amt); + const fmt_num = fmt_amt.replace(/[^0-9.,\s\u200F\u00A0]+/g, ``).trim() + const cur_s = fmt_amt.match(regex.currency_symbol)?.[0] || fmt_amt.match(new RegExp(cur, `i`))?.[0]; + if (!cur_s) return undefined; + const cur_m = fmt_amt.match(regex.currency_marker)?.[0] || cur_s; + const cur_pos = fmt_amt.startsWith(cur_m); + const dec_s = locale_fractional_decimal(locale); + const [_val_i, _val_f] = fmt_num.split(dec_s); + const val_i = _val_i.replace(regex.nbsp_rp, ``).replace(regex.rtlm_rp, ``); + const val_f = _val_f.replace(regex.nbsp_rp, ``).replace(regex.rtlm_rp, ``) + const num_i = parseInt(val_i.replace(dec_s === `,` ? regex.periods : regex.commas, ``)); + const num_f = parseInt(val_f); return { - symbol: symbol_val_i.charAt(0), - currency, - val_i: Number(symbol_val_i.replaceAll(`,`, ``).slice(1)), - val_f: Number(val_f), + cur, + cur_s, + cur_m, + cur_pos, + dec_s, + num_i, + num_f, + val_i, + val_f, }; }; +export const fmt_currency_price = (currency_price: CurrencyPrice, hide_currency_marker?: boolean): string => { + const cur_val = hide_currency_marker ? currency_price.cur_s : currency_price.cur_m; + return `${currency_price.cur_pos ? `${cur_val} ` : ``}${currency_price.val_i}${currency_price.dec_s}${currency_price.val_f}${currency_price.cur_pos ? `` : ` ${cur_val}`}` +}; + +export const sum_currency_price = (currency_price: CurrencyPrice): number => { + return currency_price.num_i + (currency_price.num_f / 100); +}; + export const parse_currency_price_fmt = (locale: string, _currency: string, amount: number): CurrencyPriceFmt => { const currency = parse_currency(_currency); const fmt = new Intl.NumberFormat(locale, { diff --git a/utils/src/file.ts b/utils/src/file.ts @@ -1,4 +1,4 @@ -import type { FileBytesFormat } from "./types"; +import type { FileBytesFormat, FilePath } from "./types"; export const parse_file_name = (file_path: string): string => { const file_path_dirs = file_path.split(`/`); @@ -23,3 +23,14 @@ export const format_file_bytes = (num_bytes: number, format: FileBytesFormat): s const result = num_bytes / factor; return `${result.toFixed(2)} ${format.toUpperCase()}`; }; + +export const parse_file_path = (file_path: string): FilePath | undefined => { + const file_path_spl = file_path.split(`/`); + const file_path_file = file_path_spl[file_path_spl.length - 1]; + const [file_name, mime_type] = file_path_file.split(`.`); + if (!file_name || !mime_type) return undefined; + return { + file_name, + mime_type + }; +}; +\ No newline at end of file diff --git a/utils/src/list.ts b/utils/src/list.ts @@ -14,3 +14,9 @@ export const list_move_index = <T>(array: T[], index_start: number, index_end: n ...newArray.slice(adjustedIndexEnd) ]; }; + +export const list_assign = (list_curr: string[], list_new: string[]): string[] => { + return Array.from( + new Set([...list_curr, ...list_new]), + ).filter((i) => !!i); +}; diff --git a/utils/src/math.ts b/utils/src/math.ts @@ -1,3 +1,5 @@ export const round_to_5 = (num: number): number => { return Math.round(num / 5) * 5; }; + +export const num_str = (num: number): string => num.toString(); +\ No newline at end of file diff --git a/utils/src/regex.ts b/utils/src/regex.ts @@ -1,4 +1,10 @@ -export const regex: Record<string, RegExp> = { +export const regex = { + nbsp: /[\u00A0]/g, + nbsp_rp: /[\u00A0]+/g, + rtlm: /[\u200F]/g, + rtlm_rp: /[\u200F]+/g, + commas: /[,]+/g, + periods: /[.]+/g, word_only: /^[a-zA-Z]+$/, alpha: /[a-zA-Z ]$/, alpha_ch: /[a-zA-Z ]$/, @@ -9,5 +15,8 @@ export const regex: Record<string, RegExp> = { price_ch: /[0-9.]$/, profile_name: /^[a-zA-Z0-9._]{3,30}$/, profile_name_ch: /[a-zA-Z0-9._]/, - trade_product_key: /^(?:[a-zA-Z0-9]+(?:\s+[a-zA-Z0-9]+){0,2})$/ + trade_product_key: /^(?:[a-zA-Z0-9]+(?:\s+[a-zA-Z0-9]+){0,2})$/, + currency_symbol: /(?:[A-Za-z]{3,5}\$|\p{Sc})/u, + currency_marker: /(?:[A-Za-z]{2,4}[^\d\s]+|[^\d\s]{1,3}[A-Za-z]{2,4})/ + ///(?:[A-Za-z]{2,4}\$|\$(?=[A-Za-z]{2,4}$))/ }; diff --git a/utils/src/time.ts b/utils/src/time.ts @@ -1,7 +1,5 @@ -export function time_now_ms(): number { - return Math.floor(new Date().getTime() / 1000); -}; +export const time_now_ms = (): number => Math.floor(new Date().getTime() / 1000); -export function time_created_on(): string { - return new Date().toISOString(); -}; -\ No newline at end of file +export const time_created_on = (): string => new Date().toISOString(); + +export const year_curr = (): string => new Date().getFullYear().toString(); +\ No newline at end of file diff --git a/utils/src/types.ts b/utils/src/types.ts @@ -17,4 +17,7 @@ export type LocationPoint = GeolocationCoordinatesPoint & { } export type NumberTuple = [number, number]; -export type FileBytesFormat = `kb` | `mb` | `gb`; -\ No newline at end of file + +export type FileBytesFormat = `kb` | `mb` | `gb`; +export type FileMimeType = string; +export type FilePath = { file_name: string; mime_type: FileMimeType; } +\ No newline at end of file diff --git a/utils/src/units.ts b/utils/src/units.ts @@ -39,4 +39,17 @@ export const mass_g = (units: MassUnit, amt: number): number => { export const mass_tf = (units_from: MassUnit, units_to: MassUnit, amt: number): number => { return convert(amt, units_from).to(units_to); +}; + +export const mass_tf_u = (units_from: MassUnit, units_to: string, amt: number): number => { + const _units_to = parse_mass_unit_u(units_to); + if (!_units_to) throw new Error(`Malformed units.`) + return convert(amt, units_from).to(_units_to); +}; + +export const mass_tf_str = (units_from: string, units_to: string, amt: number): number => { + const _units_from = parse_mass_unit_u(units_from); + const _units_to = parse_mass_unit_u(units_to); + if (!_units_from || !_units_to) throw new Error(`Malformed units.`) + return convert(amt, _units_from).to(_units_to); }; \ No newline at end of file