src/components/currency/Currency.js
import { createNumberMask } from '@formio/text-mask-addons';
import { maskInput } from '@formio/vanilla-text-mask';
import _ from 'lodash';
import { getCurrencyAffixes } from '../../utils/utils';
import NumberComponent from '../number/Number';
export default class CurrencyComponent extends NumberComponent {
static schema(...extend) {
return NumberComponent.schema({
type: 'currency',
label: 'Currency',
key: 'currency'
}, ...extend);
}
static get builderInfo() {
return {
title: 'Currency',
group: 'advanced',
icon: 'usd',
documentation: '/userguide/form-building/advanced-components#currency',
weight: 70,
schema: CurrencyComponent.schema()
};
}
constructor(component, options, data) {
// Currency should default to have a delimiter unless otherwise specified.
if (component && !component.hasOwnProperty('delimiter')) {
component.delimiter = true;
}
super(component, options, data);
}
/**
* Creates the number mask for currency numbers.
*
* @return {*}
*/
createNumberMask() {
const decimalLimit = _.get(this.component, 'decimalLimit', 2);
const affixes = getCurrencyAffixes({
currency: this.component.currency,
decimalLimit: decimalLimit,
decimalSeparator: this.decimalSeparator,
lang: this.options.language
});
this.currencyPrefix = this.options.prefix || affixes.prefix;
this.currencySuffix = this.options.suffix || affixes.suffix;
return createNumberMask({
prefix: this.currencyPrefix,
suffix: this.currencySuffix,
thousandsSeparatorSymbol: _.get(this.component, 'thousandsSeparator', this.delimiter),
decimalSymbol: _.get(this.component, 'decimalSymbol', this.decimalSeparator),
decimalLimit: decimalLimit,
allowNegative: _.get(this.component, 'allowNegative', true),
allowDecimal: this.isDecimalAllowed(),
});
}
isDecimalAllowed() {
return _.get(this.component, 'allowDecimal', true);
}
setInputMask(input) {
const affixes = getCurrencyAffixes({
currency: this.component.currency,
decimalSeparator: this.decimalSeparator,
lang: this.options.language,
});
let numberPattern = `${affixes.prefix}[0-9`;
numberPattern += this.decimalSeparator || '';
numberPattern += this.delimiter || '';
numberPattern += ']*';
input.setAttribute('pattern', numberPattern);
input.mask = maskInput({
inputElement: input,
mask: this.numberMask || '',
pipe: (conformedValue) => {
if (conformedValue === '$0._') {
// Delay to allow mask to update first.
setTimeout(() => {
const caretPosition = input.value.length - 1;
input.setSelectionRange(caretPosition, caretPosition);
});
}
return conformedValue;
},
shadowRoot: this.root ? this.root.shadowRoot : null
});
}
get defaultSchema() {
return CurrencyComponent.schema();
}
parseNumber(value) {
return super.parseNumber(this.stripPrefixSuffix(value));
}
parseValue(value) {
return super.parseValue(this.stripPrefixSuffix(value));
}
addZerosAndFormatValue(value) {
if (!value && value !== 0) return;
const decimalLimit = _.get(this.component, 'decimalLimit', 2);
let integerPart;
let decimalPart = '';
let decimalPartNumbers = [];
const negativeValueSymbol = '-';
const hasPrefix = this.currencyPrefix ? value.includes(this.currencyPrefix) : false;
const hasSuffix = this.currencySuffix ? value.includes(this.currencySuffix) : false;
const isNegative = value.includes(negativeValueSymbol) || false;
value = this.stripPrefixSuffix(isNegative ? value.replace(negativeValueSymbol,'') : value);
if (value.includes(this.decimalSeparator)) {
[integerPart, decimalPart] = value.split(this.decimalSeparator);
decimalPartNumbers =[...decimalPart.split('')] ;
}
else {
integerPart = value;
}
if (decimalPart.length < decimalLimit) {
while (decimalPartNumbers.length < decimalLimit) {
decimalPartNumbers.push('0');
}
}
const formattedValue = `${isNegative ? negativeValueSymbol:''}${hasPrefix ? this.currencyPrefix : ''}${integerPart}${this.decimalSeparator}${decimalPartNumbers.join('')}${hasSuffix ? this.currencySuffix : ''}`;
return super.formatValue(formattedValue);
}
getValueAsString(value, options) {
const stringValue = super.getValueAsString(value, options);
// eslint-disable-next-line eqeqeq
if (value || value == '0') {
if (Array.isArray(value)) {
return value.map((val) => this.addZerosAndFormatValue(super.getValueAsString(val, options))).join(', ');
}
return this.addZerosAndFormatValue(stringValue);
}
return stringValue;
}
formatValue(value) {
if (value || value === '0') {
return this.addZerosAndFormatValue(value);
}
return super.formatValue(value);
}
stripPrefixSuffix(value) {
if (typeof value === 'string') {
try {
const hasPrefix = this.currencyPrefix ? value.includes(this.currencyPrefix) : false;
const hasSuffix = this.currencySuffix ? value.includes(this.currencySuffix) : false;
const hasDelimiter = value.includes(this.delimiter);
const hasDecimalSeparator = value.includes(this.decimalSeparator);
if (this.currencyPrefix) {
value = value.replace(this.currencyPrefix, '');
}
if (this.currencySuffix) {
value = value.replace(this.currencySuffix, '');
}
//when we enter $ in the field using dashboard, it contains '_' that is NaN
if ((hasPrefix || hasSuffix) && !hasDelimiter && !hasDecimalSeparator && (Number.isNaN(+value) || !value)) {
value ='0';
}
}
catch (err) {
// If value doesn't have a replace method, continue on as before.
}
}
return value;
}
addFocusBlurEvents(element) {
super.addFocusBlurEvents(element);
this.addEventListener(element, 'focus', () => {
if (element.defaultValue === element.value) {
element.setSelectionRange(0, element.defaultValue.length);
}
});
this.addEventListener(element, 'blur', () => {
element.value = this.getValueAsString(this.addZerosAndFormatValue(this.parseValue(element.value)));
});
}
}