mirror of
https://github.com/tenrok/maska.git
synced 2026-06-20 20:00:34 +03:00
feat: number mask
This commit is contained in:
@@ -1,13 +1,21 @@
|
|||||||
import { MaskTokens, tokens } from './tokens'
|
import { MaskTokens, tokens } from './tokens'
|
||||||
|
import { processNumber } from './number'
|
||||||
|
|
||||||
export type MaskType = string | string[] | ((input: string) => string) | null
|
export type MaskType = string | string[] | ((input: string) => string) | null
|
||||||
|
|
||||||
|
interface MaskNumber {
|
||||||
|
locale?: string
|
||||||
|
fraction?: number
|
||||||
|
unsigned?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
export interface MaskOptions {
|
export interface MaskOptions {
|
||||||
mask?: MaskType
|
mask?: MaskType
|
||||||
tokens?: MaskTokens
|
tokens?: MaskTokens
|
||||||
tokensReplace?: boolean
|
tokensReplace?: boolean
|
||||||
eager?: boolean
|
eager?: boolean
|
||||||
reversed?: boolean
|
reversed?: boolean
|
||||||
|
number?: MaskNumber
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Mask {
|
export class Mask {
|
||||||
@@ -109,6 +117,8 @@ export class Mask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private process (value: string, maskRaw: string | null, masked = true): string {
|
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
|
if (maskRaw == null) return value
|
||||||
|
|
||||||
const memoKey = `v=${value},mr=${maskRaw},m=${masked ? 1 : 0}`
|
const memoKey = `v=${value},mr=${maskRaw},m=${masked ? 1 : 0}`
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -25,6 +25,20 @@ export const parseInput = (
|
|||||||
opts.tokens = parseTokens(input.dataset.maskaTokens)
|
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
|
return opts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user