2
0
mirror of https://github.com/tenrok/maska.git synced 2026-05-24 14:04:08 +03:00

feat: number mask

This commit is contained in:
Alexander Shabunevich
2024-05-12 20:11:08 +03:00
parent 7cdb78cfbf
commit ff06ea6703
3 changed files with 67 additions and 0 deletions
+10
View File
@@ -1,13 +1,21 @@
import { MaskTokens, tokens } from './tokens'
import { processNumber } from './number'
export type MaskType = string | string[] | ((input: string) => string) | null
interface MaskNumber {
locale?: string
fraction?: number
unsigned?: boolean
}
export interface MaskOptions {
mask?: MaskType
tokens?: MaskTokens
tokensReplace?: boolean
eager?: boolean
reversed?: boolean
number?: MaskNumber
}
export class Mask {
@@ -109,6 +117,8 @@ export class Mask {
}
private process (value: string, maskRaw: string | null, masked = true): string {
if (this.opts.number != null) return processNumber(value, masked, this.opts)
if (maskRaw == null) return value
const memoKey = `v=${value},mr=${maskRaw},m=${masked ? 1 : 0}`
+43
View File
@@ -0,0 +1,43 @@
import { MaskOptions } from './mask'
const prepare = (input: string, group: string, decimal: string): string =>
input.replaceAll(group, '').replace(decimal, '.').replace('..', '.').replace(/[^.\d]/g, '')
const createFormatter = (min: number, max: number, opts: MaskOptions): Intl.NumberFormat =>
new Intl.NumberFormat(opts.number?.locale ?? 'en', {
minimumFractionDigits: min,
maximumFractionDigits: max,
// @ts-expect-error
roundingMode: 'trunc'
})
export const processNumber = (value: string, masked = true, opts: MaskOptions): string => {
const sign = opts.number?.unsigned == null && value.startsWith('-') ? '-' : ''
const fraction = opts.number?.fraction ?? 0
let formatter = createFormatter(0, fraction, opts)
const parts = formatter.formatToParts(1000.12)
const group = parts.find((part) => part.type === 'group')?.value ?? ' '
const decimal = parts.find((part) => part.type === 'decimal')?.value ?? '.'
const float = prepare(value, group, decimal)
if (float === '' || Number.isNaN(float)) return sign
// allow zero at the end
const floatParts = float.split('.')
if (floatParts[1] != null && floatParts[1].length >= 1) {
const min = floatParts[1].length <= fraction ? floatParts[1].length : fraction
formatter = createFormatter(min, fraction, opts)
}
let result = formatter.format(parseFloat(float))
if (!masked) {
result = prepare(result, group, decimal)
} else if (fraction > 0 && float.endsWith('.') && !float.slice(0, -1).includes('.')) {
// if ends with decimal separator
result += decimal
}
return sign + result
}
+14
View File
@@ -25,6 +25,20 @@ export const parseInput = (
opts.tokens = parseTokens(input.dataset.maskaTokens)
}
const number: MaskOptions['number'] = {}
if (input.dataset.maskaNumberLocale != null) {
number.locale = input.dataset.maskaNumberLocale
}
if (input.dataset.maskaNumberFraction != null) {
number.fraction = parseInt(input.dataset.maskaNumberFraction)
}
if (input.dataset.maskaNumberUnsigned != null) {
number.unsigned = parseBool(input.dataset.maskaNumberUnsigned)
}
if (Object.values(number).length > 0) {
opts.number = number
}
return opts
}