Skip to content

Commit fab0e93

Browse files
committed
fix: fix pluralization when using full currency/unit names
1 parent 291d841 commit fab0e93

4 files changed

Lines changed: 178 additions & 44 deletions

File tree

src/numberFormat.ts

Lines changed: 46 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,20 @@ import { escapeRegExp, substringBefore } from './utils'
22
import { CurrencyDisplay, NumberFormatStyle, NumberInputOptions, UnitDisplay } from './api'
33
import NumberFormatOptions = Intl.NumberFormatOptions
44

5+
const getPrefix = (parts: Intl.NumberFormatPart[]) =>
6+
parts
7+
.slice(0, parts.map((p) => p.type).indexOf('integer'))
8+
.map((p) => p.value)
9+
.join('')
10+
11+
const getSuffix = (parts: Intl.NumberFormatPart[]) => {
12+
const types = parts.map((p) => p.type)
13+
return parts
14+
.slice(Math.max(types.lastIndexOf('integer'), types.indexOf('fraction')) + 1)
15+
.map((p) => p.value)
16+
.join('')
17+
}
18+
519
export const DECIMAL_SEPARATORS = [',', '.', '٫']
620
export const INTEGER_PATTERN = '(0|[1-9]\\d*)'
721

@@ -20,18 +34,20 @@ export class NumberFormat {
2034
maximumFractionDigits: number
2135
prefix: string
2236
negativePrefix: string
23-
suffix: string
37+
suffix: string[]
2438

2539
constructor(options: NumberInputOptions) {
40+
const createNumberFormat = (options: Intl.NumberFormatOptions) =>
41+
new Intl.NumberFormat(locale, {
42+
currency,
43+
currencyDisplay,
44+
unit,
45+
unitDisplay,
46+
style,
47+
...options
48+
})
2649
const { formatStyle: style, currency, currencyDisplay, unit, unitDisplay, locale, precision } = options
27-
const numberFormat = new Intl.NumberFormat(locale, {
28-
currency,
29-
currencyDisplay,
30-
unit,
31-
unitDisplay,
32-
style,
33-
minimumFractionDigits: style !== NumberFormatStyle.Currency ? 1 : undefined
34-
})
50+
const numberFormat = createNumberFormat({ minimumFractionDigits: style !== NumberFormatStyle.Currency ? 1 : undefined })
3551
const formatParts = numberFormat.formatToParts(style === NumberFormatStyle.Percent ? 1234.56 : 123456)
3652

3753
this.locale = locale
@@ -58,12 +74,9 @@ export class NumberFormat {
5874
this.maximumFractionDigits = maximumFractionDigits
5975
}
6076

61-
const getPrefix = (str: string) => substringBefore(str, this.digits[1])
62-
const getSuffix = (str: string) => str.substring(str.lastIndexOf(this.decimalSymbol ? this.digits[0] : this.digits[1]) + 1)
63-
64-
this.prefix = getPrefix(numberFormat.format(1))
65-
this.suffix = getSuffix(numberFormat.format(1))
66-
this.negativePrefix = getPrefix(numberFormat.format(-1))
77+
this.prefix = getPrefix(numberFormat.formatToParts(1))
78+
this.suffix = [getSuffix(createNumberFormat({ minimumFractionDigits: 0 }).formatToParts(1)), getSuffix(numberFormat.formatToParts(2))]
79+
this.negativePrefix = getPrefix(numberFormat.formatToParts(-1))
6780
}
6881

6982
parse(str: string | null): number | null {
@@ -99,8 +112,22 @@ export class NumberFormat {
99112
integerNumber /= 100
100113
}
101114
return [
102-
this.stripPrefixOrSuffix(this.normalizeDigits(integerNumber.toLocaleString(this.locale, { ...options, useGrouping: true }))),
103-
this.stripPrefixOrSuffix(this.normalizeDigits(integerNumber.toLocaleString(this.locale, { ...options, useGrouping: false })))
115+
this.stripPrefixOrSuffix(
116+
this.normalizeDigits(
117+
integerNumber.toLocaleString(this.locale, {
118+
...options,
119+
useGrouping: true
120+
})
121+
)
122+
),
123+
this.stripPrefixOrSuffix(
124+
this.normalizeDigits(
125+
integerNumber.toLocaleString(this.locale, {
126+
...options,
127+
useGrouping: false
128+
})
129+
)
130+
)
104131
].includes(formattedNumber)
105132
}
106133

@@ -136,7 +163,7 @@ export class NumberFormat {
136163
}
137164

138165
insertPrefixOrSuffix(str: string, negative: boolean): string {
139-
return `${negative ? this.negativePrefix : this.prefix}${str}${this.suffix}`
166+
return `${negative ? this.negativePrefix : this.prefix}${str}${this.suffix[1]}`
140167
}
141168

142169
stripGroupingSeparator(str: string): string {
@@ -148,7 +175,7 @@ export class NumberFormat {
148175
}
149176

150177
stripPrefixOrSuffix(str: string): string {
151-
return str.replace(this.negativePrefix, '').replace(this.prefix, '').replace(this.suffix, '')
178+
return str.replace(this.negativePrefix, '').replace(this.prefix, '').replace(this.suffix[1], '').replace(this.suffix[0], '')
152179
}
153180

154181
normalizeDecimalSeparator(str: string, from: number): string {

src/numberInput.ts

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,8 @@ export class NumberInput {
170170
formattedValue = formattedValue
171171
.replace(this.numberFormat.negativePrefix, this.numberFormat.minusSymbol)
172172
.replace(this.numberFormat.prefix, '')
173-
.replace(this.numberFormat.suffix, '')
173+
.replace(this.numberFormat.suffix[1], '')
174+
.replace(this.numberFormat.suffix[0], '')
174175
}
175176

176177
this.el.value = formattedValue
@@ -222,9 +223,15 @@ export class NumberInput {
222223
}
223224
}
224225
}
225-
return this.options.hidePrefixOrSuffixOnFocus
226-
? newValueLength - caretPositionFromLeft
227-
: Math.max(newValueLength - Math.max(caretPositionFromLeft, suffix.length), prefix.length)
226+
if (this.options.hidePrefixOrSuffixOnFocus) {
227+
return newValueLength - caretPositionFromLeft
228+
} else {
229+
const getSuffixLength = (str: string) => (str.includes(suffix[1]) ? suffix[1] : str.includes(suffix[0]) ? suffix[0] : '').length
230+
const oldSuffixLength = getSuffixLength(value)
231+
const newSuffixLength = getSuffixLength(this.formattedValue)
232+
const suffixLengthDifference = Math.abs(newSuffixLength - oldSuffixLength)
233+
return Math.max(newValueLength - Math.max(caretPositionFromLeft - suffixLengthDifference, newSuffixLength), prefix.length)
234+
}
228235
}
229236
this.setCaretPosition(getCaretPositionAfterFormat())
230237
}
@@ -243,8 +250,9 @@ export class NumberInput {
243250
const getCaretPositionOnFocus = () => {
244251
const { prefix, suffix, groupingSymbol } = this.numberFormat
245252
if (!this.options.hidePrefixOrSuffixOnFocus) {
246-
if (selectionStart >= value.length - suffix.length) {
247-
return this.formattedValue.length - suffix.length
253+
const suffixLength = suffix[this.numberValue === 1 ? 0 : 1].length
254+
if (selectionStart >= value.length - suffixLength) {
255+
return this.formattedValue.length - suffixLength
248256
} else if (selectionStart < prefix.length) {
249257
return prefix.length
250258
}

0 commit comments

Comments
 (0)