vue-currency-input
Advanced tools
Comparing version 1.21.0 to 1.22.0
/** | ||
* Vue Currency Input 1.21.0 | ||
* Vue Currency Input 1.22.0 | ||
* (c) 2018-2020 Matthias Stiller | ||
* @license MIT | ||
*/ | ||
function dispatchEvent (el, eventName, data) { | ||
var event = document.createEvent('CustomEvent'); | ||
event.initCustomEvent(eventName, true, true, data); | ||
el.dispatchEvent(event); | ||
} | ||
var toExternalNumberModel = function (number, valueAsInteger, fractionDigits) { | ||
return valueAsInteger && number != null ? Number(number.toFixed(fractionDigits).split('.').join('')) : number | ||
}; | ||
var DEFAULT_OPTIONS = { | ||
locale: undefined, | ||
currency: 'EUR', | ||
valueAsInteger: false, | ||
distractionFree: true, | ||
precision: undefined, | ||
autoDecimalMode: false, | ||
valueRange: undefined, | ||
allowNegative: true | ||
}; | ||
var getValue = function (el) { | ||
var ref = el.$ci; | ||
var numberValue = ref.numberValue; | ||
var currencyFormat = ref.currencyFormat; | ||
var options = ref.options; | ||
return toExternalNumberModel(numberValue, options.valueAsInteger, currencyFormat.maximumFractionDigits) | ||
}; | ||
var setValue = function (el, value) { return dispatchEvent(el, 'format', { value: value }); }; | ||
var escapeRegExp = function (str) { return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); }; | ||
@@ -41,54 +12,2 @@ var removeLeadingZeros = function (str) { return str.replace(/^0+(0$|[^0])/, '$1'); }; | ||
var setCaretPosition = function (el, position) { return el.setSelectionRange(position, position); }; | ||
var getCaretPositionAfterFormat = function (newValue, inputtedValue, caretPosition, numberFormat, options) { | ||
var prefix = numberFormat.prefix; | ||
var suffix = numberFormat.suffix; | ||
var decimalSymbol = numberFormat.decimalSymbol; | ||
var maximumFractionDigits = numberFormat.maximumFractionDigits; | ||
var groupingSymbol = numberFormat.groupingSymbol; | ||
var decimalSymbolPosition = inputtedValue.indexOf(decimalSymbol) + 1; | ||
var caretPositionFromLeft = inputtedValue.length - caretPosition; | ||
if (Math.abs(newValue.length - inputtedValue.length) > 1 && caretPosition <= decimalSymbolPosition) { | ||
return newValue.indexOf(decimalSymbol) + 1 | ||
} else if (newValue.substr(caretPosition, 1) === groupingSymbol && count(newValue, groupingSymbol) === count(inputtedValue, groupingSymbol) + 1) { | ||
return newValue.length - caretPositionFromLeft - 1 | ||
} else { | ||
if (!options.autoDecimalMode && decimalSymbolPosition !== 0 && caretPosition > decimalSymbolPosition) { | ||
if (numberFormat.onlyDigits(inputtedValue.substr(decimalSymbolPosition)).length - 1 === maximumFractionDigits) { | ||
caretPositionFromLeft -= 1; | ||
} | ||
} | ||
return options.distractionFree.hideCurrencySymbol | ||
? newValue.length - caretPositionFromLeft | ||
: Math.max(newValue.length - Math.max(caretPositionFromLeft, suffix.length), prefix.length === 0 ? 0 : prefix.length + 1) | ||
} | ||
}; | ||
var getDistractionFreeCaretPosition = function (numberFormat, options, value, caretPosition) { | ||
var result = caretPosition; | ||
if (options.distractionFree.hideCurrencySymbol) { | ||
result -= numberFormat.prefix.length; | ||
} | ||
if (options.distractionFree.hideGroupingSymbol) { | ||
result -= count(value.substring(0, caretPosition), numberFormat.groupingSymbol); | ||
} | ||
return Math.max(0, result) | ||
}; | ||
var equal = function (a, b) { | ||
if (a === b) { | ||
return true | ||
} | ||
if (!a || !b || typeof a !== 'object' || typeof b !== 'object') { | ||
return false | ||
} | ||
var keys = Object.keys(a); | ||
if (keys.length !== Object.keys(b).length) { | ||
return false | ||
} | ||
if (!keys.every(Object.prototype.hasOwnProperty.bind(b))) { | ||
return false | ||
} | ||
return keys.every(function (key) { return equal(a[key], b[key]); }) | ||
}; | ||
var DECIMAL_SYMBOLS = [',', '.', '٫']; | ||
@@ -132,11 +51,15 @@ var NumberFormat = function NumberFormat (options) { | ||
}; | ||
NumberFormat.prototype.parse = function parse (str) { | ||
var negative = this.isNegative(str); | ||
str = this.normalizeDigits(str); | ||
str = this.stripCurrencySymbol(str); | ||
str = this.stripMinusSymbol(str); | ||
var fraction = this.decimalSymbol ? ("(" + (escapeRegExp(this.decimalSymbol)) + "\\d*)?") : ''; | ||
var match = str.match(new RegExp(("^" + (this.integerPattern()) + fraction + "$"))); | ||
if (match) { | ||
return Number(("" + (negative ? '-' : '') + ((this.onlyDigits(match[1]))) + "." + ((this.onlyDigits(match[3] || ''))))) | ||
NumberFormat.prototype.parse = function parse (str, valueAsInteger) { | ||
if ( valueAsInteger === void 0 ) valueAsInteger = false; | ||
if (str) { | ||
var negative = this.isNegative(str); | ||
str = this.normalizeDigits(str); | ||
str = this.stripCurrencySymbol(str); | ||
str = this.stripMinusSymbol(str); | ||
var fraction = this.decimalSymbol ? ("(" + (escapeRegExp(this.decimalSymbol)) + "\\d*)?") : ''; | ||
var match = str.match(new RegExp(("^" + (this.integerPattern()) + fraction + "$"))); | ||
if (match) { | ||
var number = Number(("" + (negative ? '-' : '') + ((this.onlyDigits(match[1]))) + "." + ((this.onlyDigits(match[3] || ''))))); | ||
return valueAsInteger ? Number(number.toFixed(this.maximumFractionDigits).split('.').join('')) : number | ||
} | ||
} | ||
@@ -201,2 +124,38 @@ return null | ||
var DEFAULT_OPTIONS = { | ||
locale: undefined, | ||
currency: 'EUR', | ||
valueAsInteger: false, | ||
distractionFree: true, | ||
precision: undefined, | ||
autoDecimalMode: false, | ||
valueRange: undefined, | ||
allowNegative: true | ||
}; | ||
var parse = function (formattedValue, options) { | ||
var mergedOptions = Object.assign({}, DEFAULT_OPTIONS, options); | ||
return new NumberFormat(mergedOptions).parse(formattedValue, mergedOptions.valueAsInteger) | ||
}; | ||
var getValue = function (ref) { return (ref.$el || ref).$ci.getValue(); }; | ||
var setValue = function (ref, value) { | ||
(ref.$el || ref).$ci.setValue(value); | ||
}; | ||
var equal = function (a, b) { | ||
if (a === b) { | ||
return true | ||
} | ||
if (!a || !b || typeof a !== 'object' || typeof b !== 'object') { | ||
return false | ||
} | ||
var keys = Object.keys(a); | ||
if (keys.length !== Object.keys(b).length) { | ||
return false | ||
} | ||
if (!keys.every(Object.prototype.hasOwnProperty.bind(b))) { | ||
return false | ||
} | ||
return keys.every(function (key) { return equal(a[key], b[key]); }) | ||
}; | ||
var DefaultNumberMask = function DefaultNumberMask (numberFormat) { | ||
@@ -263,13 +222,50 @@ this.numberFormat = numberFormat; | ||
var getCaretPositionAfterFormat = function (newValue, inputtedValue, caretPosition, numberFormat, options) { | ||
var prefix = numberFormat.prefix; | ||
var suffix = numberFormat.suffix; | ||
var decimalSymbol = numberFormat.decimalSymbol; | ||
var maximumFractionDigits = numberFormat.maximumFractionDigits; | ||
var groupingSymbol = numberFormat.groupingSymbol; | ||
var decimalSymbolPosition = inputtedValue.indexOf(decimalSymbol) + 1; | ||
var caretPositionFromLeft = inputtedValue.length - caretPosition; | ||
if (Math.abs(newValue.length - inputtedValue.length) > 1 && caretPosition <= decimalSymbolPosition) { | ||
return newValue.indexOf(decimalSymbol) + 1 | ||
} else if (newValue.substr(caretPosition, 1) === groupingSymbol && count(newValue, groupingSymbol) === count(inputtedValue, groupingSymbol) + 1) { | ||
return newValue.length - caretPositionFromLeft - 1 | ||
} else { | ||
if (!options.autoDecimalMode && decimalSymbolPosition !== 0 && caretPosition > decimalSymbolPosition) { | ||
if (numberFormat.onlyDigits(inputtedValue.substr(decimalSymbolPosition)).length - 1 === maximumFractionDigits) { | ||
caretPositionFromLeft -= 1; | ||
} | ||
} | ||
return options.distractionFree.hideCurrencySymbol | ||
? newValue.length - caretPositionFromLeft | ||
: Math.max(newValue.length - Math.max(caretPositionFromLeft, suffix.length), prefix.length === 0 ? 0 : prefix.length + 1) | ||
} | ||
}; | ||
var getDistractionFreeCaretPosition = function (numberFormat, options, value, caretPosition) { | ||
var result = caretPosition; | ||
if (options.distractionFree.hideCurrencySymbol) { | ||
result -= numberFormat.prefix.length; | ||
} | ||
if (options.distractionFree.hideGroupingSymbol) { | ||
result -= count(value.substring(0, caretPosition), numberFormat.groupingSymbol); | ||
} | ||
return Math.max(0, result) | ||
}; | ||
var MAX_SAFE_INTEGER = Math.pow(2, 53) - 1; | ||
var init = function (el, optionsFromBinding, ref) { | ||
var $ci = ref.$ci; | ||
var inputElement = el.tagName.toLowerCase() === 'input' ? el : el.querySelector('input'); | ||
if (!inputElement) { | ||
throw new Error('No input element found') | ||
} | ||
var options = Object.assign({}, ($ci ? $ci.GLOBAL_OPTIONS : DEFAULT_OPTIONS), optionsFromBinding); | ||
var NumberInput = function NumberInput (el, options, callbackFns) { | ||
this.el = el; | ||
this.callbackFns = callbackFns; | ||
this.numberValue = null; | ||
this.addEventListener(); | ||
this.init(options); | ||
this.setValue(this.currencyFormat.parse(this.el.value)); | ||
}; | ||
NumberInput.prototype.init = function init (newOptions) { | ||
var options = Object.assign({}, newOptions); | ||
var distractionFree = options.distractionFree; | ||
var autoDecimalMode = options.autoDecimalMode; | ||
var valueRange = options.valueRange; | ||
var autoDecimalMode = options.autoDecimalMode; | ||
var valueRange = options.valueRange; | ||
if (typeof distractionFree === 'boolean') { | ||
@@ -295,58 +291,51 @@ options.distractionFree = { | ||
options.distractionFree.hideNegligibleDecimalDigits = false; | ||
inputElement.setAttribute('inputmode', 'numeric'); | ||
this.el.setAttribute('inputmode', 'numeric'); | ||
} else { | ||
inputElement.setAttribute('inputmode', 'decimal'); | ||
this.el.setAttribute('inputmode', 'decimal'); | ||
} | ||
var currencyFormat = new NumberFormat(options); | ||
inputElement.$ci = Object.assign({}, inputElement.$ci || { numberValue: null }, | ||
{options: options, | ||
numberMask: options.autoDecimalMode ? new AutoDecimalModeNumberMask(currencyFormat) : new DefaultNumberMask(currencyFormat), | ||
currencyFormat: currencyFormat}); | ||
return inputElement | ||
this.options = options; | ||
this.currencyFormat = new NumberFormat(this.options); | ||
this.numberMask = options.autoDecimalMode ? new AutoDecimalModeNumberMask(this.currencyFormat) : new DefaultNumberMask(this.currencyFormat); | ||
}; | ||
var triggerEvent = function (el, eventName) { | ||
var ref = el.$ci; | ||
var numberValue = ref.numberValue; | ||
var currencyFormat = ref.currencyFormat; | ||
var options = ref.options; | ||
numberValue = toExternalNumberModel(numberValue, options.valueAsInteger, currencyFormat.maximumFractionDigits); | ||
dispatchEvent(el, eventName, { numberValue: numberValue }); | ||
NumberInput.prototype.setOptions = function setOptions (options) { | ||
this.init(options); | ||
this.applyFixedFractionFormat(this.numberValue, true); | ||
}; | ||
var applyFixedFractionFormat = function (el, value, forcedChange) { | ||
if ( forcedChange === void 0 ) forcedChange = false; | ||
var ref = el.$ci; | ||
var currencyFormat = ref.currencyFormat; | ||
var options = ref.options; | ||
var ref$1 = options.valueRange; | ||
var min = ref$1.min; | ||
var max = ref$1.max; | ||
var validateValueRange = function () { return Math.min(Math.max(value, min), max); }; | ||
format(el, value != null ? currencyFormat.format(validateValueRange()) : null); | ||
if (value !== el.$ci.numberValue || forcedChange) { | ||
triggerEvent(el, 'change'); | ||
NumberInput.prototype.applyFixedFractionFormat = function applyFixedFractionFormat (number, forcedChange) { | ||
this.format(number != null ? this.currencyFormat.format(this.validateValueRange(number)) : null); | ||
if (number !== this.numberValue || forcedChange) { | ||
this.callbackFns.onChange(this.getValue()); | ||
} | ||
}; | ||
var updateInputValue = function (el, value, hideNegligibleDecimalDigits) { | ||
NumberInput.prototype.getValue = function getValue () { | ||
return this.currencyFormat.parse(this.formattedValue, this.options.valueAsInteger) | ||
}; | ||
NumberInput.prototype.setValue = function setValue (value) { | ||
var newValue = this.options.valueAsInteger && value != null ? value / Math.pow(10, this.currencyFormat.maximumFractionDigits) : value; | ||
if (newValue !== this.numberValue) { | ||
this.applyFixedFractionFormat(newValue); | ||
} | ||
}; | ||
NumberInput.prototype.validateValueRange = function validateValueRange (value) { | ||
var ref = this.options.valueRange; | ||
var min = ref.min; | ||
var max = ref.max; | ||
return Math.min(Math.max(value, min), max) | ||
}; | ||
NumberInput.prototype.updateInputValue = function updateInputValue (value, hideNegligibleDecimalDigits) { | ||
if ( hideNegligibleDecimalDigits === void 0 ) hideNegligibleDecimalDigits = false; | ||
if (value != null) { | ||
var ref = el.$ci; | ||
var focus = ref.focus; | ||
var decimalSymbolInsertedAt = ref.decimalSymbolInsertedAt; | ||
var options = ref.options; | ||
var numberMask = ref.numberMask; | ||
var currencyFormat = ref.currencyFormat; | ||
var previousConformedValue = ref.previousConformedValue; | ||
var allowNegative = options.allowNegative; | ||
var distractionFree = options.distractionFree; | ||
if (decimalSymbolInsertedAt !== undefined) { | ||
value = currencyFormat.normalizeDecimalSymbol(value, decimalSymbolInsertedAt); | ||
el.$ci.decimalSymbolInsertedAt = undefined; | ||
if (this.decimalSymbolInsertedAt !== undefined) { | ||
value = this.currencyFormat.normalizeDecimalSymbol(value, this.decimalSymbolInsertedAt); | ||
this.decimalSymbolInsertedAt = undefined; | ||
} | ||
var conformedValue = numberMask.conformToMask(value, previousConformedValue); | ||
var conformedValue = this.numberMask.conformToMask(value, this.formattedValue); | ||
var formattedValue; | ||
if (typeof conformedValue === 'object') { | ||
var numberValue = conformedValue.numberValue; | ||
var fractionDigits = conformedValue.fractionDigits; | ||
var maximumFractionDigits = currencyFormat.maximumFractionDigits; | ||
var minimumFractionDigits = currencyFormat.minimumFractionDigits; | ||
if (focus) { | ||
var fractionDigits = conformedValue.fractionDigits; | ||
var ref = this.currencyFormat; | ||
var maximumFractionDigits = ref.maximumFractionDigits; | ||
var minimumFractionDigits = ref.minimumFractionDigits; | ||
if (this.focus) { | ||
minimumFractionDigits = maximumFractionDigits; | ||
@@ -358,5 +347,5 @@ } | ||
formattedValue = numberValue > MAX_SAFE_INTEGER | ||
? previousConformedValue | ||
: currencyFormat.format(numberValue, { | ||
useGrouping: !(focus && distractionFree.hideGroupingSymbol), | ||
? this.formattedValue | ||
: this.currencyFormat.format(numberValue, { | ||
useGrouping: !(this.focus && this.options.distractionFree.hideGroupingSymbol), | ||
minimumFractionDigits: minimumFractionDigits, | ||
@@ -368,71 +357,52 @@ maximumFractionDigits: maximumFractionDigits | ||
} | ||
if (!allowNegative) { | ||
formattedValue = formattedValue.replace(currencyFormat.negativePrefix, currencyFormat.prefix); | ||
if (!this.options.allowNegative) { | ||
formattedValue = formattedValue.replace(this.currencyFormat.negativePrefix, this.currencyFormat.prefix); | ||
} | ||
if (focus && distractionFree.hideCurrencySymbol) { | ||
if (this.focus && this.options.distractionFree.hideCurrencySymbol) { | ||
formattedValue = formattedValue | ||
.replace(currencyFormat.negativePrefix, currencyFormat.minusSymbol) | ||
.replace(currencyFormat.prefix, '') | ||
.replace(currencyFormat.suffix, ''); | ||
.replace(this.currencyFormat.negativePrefix, this.currencyFormat.minusSymbol) | ||
.replace(this.currencyFormat.prefix, '') | ||
.replace(this.currencyFormat.suffix, ''); | ||
} | ||
el.value = formattedValue; | ||
el.$ci.numberValue = currencyFormat.parse(el.value); | ||
this.el.value = formattedValue; | ||
this.numberValue = this.currencyFormat.parse(formattedValue); | ||
} else { | ||
el.value = el.$ci.numberValue = null; | ||
this.el.value = this.numberValue = null; | ||
} | ||
el.$ci.previousConformedValue = el.value; | ||
this.formattedValue = this.el.value; | ||
}; | ||
var format = function (el, value, hideNegligibleDecimalDigits) { | ||
if ( hideNegligibleDecimalDigits === void 0 ) hideNegligibleDecimalDigits = false; | ||
updateInputValue(el, value, hideNegligibleDecimalDigits); | ||
triggerEvent(el, 'input'); | ||
NumberInput.prototype.format = function format (value) { | ||
this.updateInputValue(value); | ||
this.callbackFns.onInput(this.getValue()); | ||
}; | ||
var addEventListener = function (el) { | ||
el.addEventListener('input', function (e) { | ||
if (!e.detail) { | ||
var value = el.value; | ||
var selectionStart = el.selectionStart; | ||
var el_$ci = el.$ci; | ||
var currencyFormat = el_$ci.currencyFormat; | ||
var options = el_$ci.options; | ||
format(el, value); | ||
if (el.$ci.focus) { | ||
setCaretPosition(el, getCaretPositionAfterFormat(el.value, value, selectionStart, currencyFormat, options)); | ||
} | ||
NumberInput.prototype.addEventListener = function addEventListener () { | ||
var this$1 = this; | ||
this.el.addEventListener('input', function () { | ||
var ref = this$1.el; | ||
var value = ref.value; | ||
var selectionStart = ref.selectionStart; | ||
this$1.format(value); | ||
if (this$1.focus) { | ||
this$1.setCaretPosition(getCaretPositionAfterFormat(this$1.formattedValue, value, selectionStart, this$1.currencyFormat, this$1.options)); | ||
} | ||
}, { capture: true }); | ||
el.addEventListener('keypress', function (e) { | ||
if (DECIMAL_SYMBOLS.includes(e.key)) { | ||
el.$ci.decimalSymbolInsertedAt = el.selectionStart; | ||
} | ||
}); | ||
el.addEventListener('format', function (e) { | ||
var ref = el.$ci; | ||
var currencyFormat = ref.currencyFormat; | ||
var options = ref.options; | ||
var numberValue = ref.numberValue; | ||
var toInternalNumberModel = function (n) { return options.valueAsInteger && n != null ? n / Math.pow(10, currencyFormat.maximumFractionDigits) : n; }; | ||
var newValue = toInternalNumberModel(e.detail.value); | ||
if (numberValue !== newValue) { | ||
applyFixedFractionFormat(el, newValue); | ||
} | ||
}); | ||
el.addEventListener('focus', function () { | ||
el.$ci.focus = true; | ||
var ref = el.$ci.options.distractionFree; | ||
var hideCurrencySymbol = ref.hideCurrencySymbol; | ||
var hideGroupingSymbol = ref.hideGroupingSymbol; | ||
var hideNegligibleDecimalDigits = ref.hideNegligibleDecimalDigits; | ||
this.el.addEventListener('focus', function () { | ||
this$1.focus = true; | ||
var ref = this$1.options.distractionFree; | ||
var hideCurrencySymbol = ref.hideCurrencySymbol; | ||
var hideGroupingSymbol = ref.hideGroupingSymbol; | ||
var hideNegligibleDecimalDigits = ref.hideNegligibleDecimalDigits; | ||
if (hideCurrencySymbol || hideGroupingSymbol || hideNegligibleDecimalDigits) { | ||
setTimeout(function () { | ||
var value = el.value; | ||
var selectionStart = el.selectionStart; | ||
var selectionEnd = el.selectionEnd; | ||
var ref = this$1.el; | ||
var value = ref.value; | ||
var selectionStart = ref.selectionStart; | ||
var selectionEnd = ref.selectionEnd; | ||
if (value) { | ||
format(el, value, hideNegligibleDecimalDigits); | ||
this$1.updateInputValue(this$1.el.value, hideNegligibleDecimalDigits); | ||
} | ||
if (Math.abs(selectionStart - selectionEnd) > 0) { | ||
el.setSelectionRange(0, el.value.length); | ||
this$1.setCaretPosition(0, this$1.el.value.length); | ||
} else { | ||
setCaretPosition(el, getDistractionFreeCaretPosition(el.$ci.currencyFormat, el.$ci.options, value, selectionStart)); | ||
this$1.setCaretPosition(getDistractionFreeCaretPosition(this$1.currencyFormat, this$1.options, value, selectionStart)); | ||
} | ||
@@ -442,29 +412,47 @@ }); | ||
}); | ||
el.addEventListener('blur', function () { | ||
el.$ci.focus = false; | ||
if (el.$ci.numberValue != null) { | ||
applyFixedFractionFormat(el, el.$ci.numberValue); | ||
this.el.addEventListener('keypress', function (e) { | ||
if (DECIMAL_SYMBOLS.includes(e.key)) { | ||
this$1.decimalSymbolInsertedAt = this$1.el.selectionStart; | ||
} | ||
}); | ||
el.addEventListener('change', function (e) { | ||
if (!e.detail) { | ||
triggerEvent(el, 'change'); | ||
this.el.addEventListener('blur', function () { | ||
this$1.focus = false; | ||
if (this$1.numberValue != null) { | ||
this$1.applyFixedFractionFormat(this$1.numberValue); | ||
} | ||
}); | ||
this.el.addEventListener('change', function () { | ||
this$1.callbackFns.onChange(this$1.getValue()); | ||
}); | ||
}; | ||
NumberInput.prototype.setCaretPosition = function setCaretPosition (start, end) { | ||
if ( end === void 0 ) end = start; | ||
this.el.setSelectionRange(start, end); }; | ||
var directive = { | ||
bind: function bind (el, ref, ref$1) { | ||
var value = ref.value; | ||
var context = ref$1.context; | ||
var inputElement = init(el, value, context); | ||
addEventListener(inputElement); | ||
setValue(inputElement, inputElement.$ci.currencyFormat.parse(inputElement.value)); | ||
bind: function bind (el, ref, vnode) { | ||
var optionsFromBinding = ref.value; | ||
var inputElement = el.tagName.toLowerCase() === 'input' ? el : el.querySelector('input'); | ||
if (!inputElement) { | ||
throw new Error('No input element found') | ||
} | ||
var options = Object.assign({}, DEFAULT_OPTIONS, | ||
(vnode.context.$ci || {}).globalOptions, | ||
optionsFromBinding); | ||
var listeners = (vnode.data && vnode.data.on) || (vnode.componentOptions && vnode.componentOptions.listeners); | ||
var emit = function (event, data) { | ||
if (listeners[event]) { | ||
listeners[event].fns(vnode.componentOptions ? data : { target: { value: data } }); | ||
} | ||
}; | ||
el.$ci = new NumberInput(inputElement, options, { | ||
onChange: function () { return emit('change', inputElement.value); }, | ||
onInput: function () { emit('input', inputElement.value); } | ||
}); | ||
}, | ||
componentUpdated: function componentUpdated (el, ref, ref$1) { | ||
componentUpdated: function componentUpdated (el, ref) { | ||
var value = ref.value; | ||
var oldValue = ref.oldValue; | ||
var context = ref$1.context; | ||
if (!equal(value, oldValue)) { | ||
var inputElement = init(el, value, context); | ||
applyFixedFractionFormat(inputElement, inputElement.$ci.numberValue, true); | ||
el.$ci.setOptions(value); | ||
} | ||
@@ -483,10 +471,9 @@ } | ||
on: Object.assign({}, this.$listeners, | ||
{change: function (e) { | ||
if (e.detail) { | ||
this$1.$emit('change', e.detail.numberValue); | ||
} | ||
{change: function () { | ||
this$1.$emit('change', getValue(this$1.$el)); | ||
}, | ||
input: function (e) { | ||
if (e.detail && this$1.value !== e.detail.numberValue) { | ||
this$1.$emit('input', e.detail.numberValue); | ||
input: function () { | ||
var numberValue = getValue(this$1.$el); | ||
if (this$1.value !== numberValue) { | ||
this$1.$emit('input', numberValue); | ||
} | ||
@@ -544,3 +531,4 @@ }}) | ||
var this$1 = this; | ||
var options = Object.assign({}, this.$ci ? this.$ci.GLOBAL_OPTIONS : DEFAULT_OPTIONS); | ||
var options = Object.assign({}, DEFAULT_OPTIONS, | ||
(this.$ci || {}).globalOptions); | ||
Object.keys(DEFAULT_OPTIONS).forEach(function (key) { | ||
@@ -573,5 +561,6 @@ if (this$1[key] !== undefined) { | ||
Vue.prototype.$ci = { | ||
parse: function (formattedValue, options) { return parse(formattedValue, Object.assign({}, globalOptions, options)); }, | ||
getValue: getValue, | ||
setValue: setValue, | ||
GLOBAL_OPTIONS: Object.assign({}, DEFAULT_OPTIONS, globalOptions) | ||
globalOptions: globalOptions | ||
}; | ||
@@ -578,0 +567,0 @@ } |
/** | ||
* Vue Currency Input 1.21.0 | ||
* Vue Currency Input 1.22.0 | ||
* (c) 2018-2020 Matthias Stiller | ||
@@ -12,31 +12,2 @@ * @license MIT | ||
function dispatchEvent (el, eventName, data) { | ||
var event = document.createEvent('CustomEvent'); | ||
event.initCustomEvent(eventName, true, true, data); | ||
el.dispatchEvent(event); | ||
} | ||
var toExternalNumberModel = function (number, valueAsInteger, fractionDigits) { | ||
return valueAsInteger && number != null ? Number(number.toFixed(fractionDigits).split('.').join('')) : number | ||
}; | ||
var DEFAULT_OPTIONS = { | ||
locale: undefined, | ||
currency: 'EUR', | ||
valueAsInteger: false, | ||
distractionFree: true, | ||
precision: undefined, | ||
autoDecimalMode: false, | ||
valueRange: undefined, | ||
allowNegative: true | ||
}; | ||
var getValue = function (el) { | ||
var ref = el.$ci; | ||
var numberValue = ref.numberValue; | ||
var currencyFormat = ref.currencyFormat; | ||
var options = ref.options; | ||
return toExternalNumberModel(numberValue, options.valueAsInteger, currencyFormat.maximumFractionDigits) | ||
}; | ||
var setValue = function (el, value) { return dispatchEvent(el, 'format', { value: value }); }; | ||
var escapeRegExp = function (str) { return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); }; | ||
@@ -48,54 +19,2 @@ var removeLeadingZeros = function (str) { return str.replace(/^0+(0$|[^0])/, '$1'); }; | ||
var setCaretPosition = function (el, position) { return el.setSelectionRange(position, position); }; | ||
var getCaretPositionAfterFormat = function (newValue, inputtedValue, caretPosition, numberFormat, options) { | ||
var prefix = numberFormat.prefix; | ||
var suffix = numberFormat.suffix; | ||
var decimalSymbol = numberFormat.decimalSymbol; | ||
var maximumFractionDigits = numberFormat.maximumFractionDigits; | ||
var groupingSymbol = numberFormat.groupingSymbol; | ||
var decimalSymbolPosition = inputtedValue.indexOf(decimalSymbol) + 1; | ||
var caretPositionFromLeft = inputtedValue.length - caretPosition; | ||
if (Math.abs(newValue.length - inputtedValue.length) > 1 && caretPosition <= decimalSymbolPosition) { | ||
return newValue.indexOf(decimalSymbol) + 1 | ||
} else if (newValue.substr(caretPosition, 1) === groupingSymbol && count(newValue, groupingSymbol) === count(inputtedValue, groupingSymbol) + 1) { | ||
return newValue.length - caretPositionFromLeft - 1 | ||
} else { | ||
if (!options.autoDecimalMode && decimalSymbolPosition !== 0 && caretPosition > decimalSymbolPosition) { | ||
if (numberFormat.onlyDigits(inputtedValue.substr(decimalSymbolPosition)).length - 1 === maximumFractionDigits) { | ||
caretPositionFromLeft -= 1; | ||
} | ||
} | ||
return options.distractionFree.hideCurrencySymbol | ||
? newValue.length - caretPositionFromLeft | ||
: Math.max(newValue.length - Math.max(caretPositionFromLeft, suffix.length), prefix.length === 0 ? 0 : prefix.length + 1) | ||
} | ||
}; | ||
var getDistractionFreeCaretPosition = function (numberFormat, options, value, caretPosition) { | ||
var result = caretPosition; | ||
if (options.distractionFree.hideCurrencySymbol) { | ||
result -= numberFormat.prefix.length; | ||
} | ||
if (options.distractionFree.hideGroupingSymbol) { | ||
result -= count(value.substring(0, caretPosition), numberFormat.groupingSymbol); | ||
} | ||
return Math.max(0, result) | ||
}; | ||
var equal = function (a, b) { | ||
if (a === b) { | ||
return true | ||
} | ||
if (!a || !b || typeof a !== 'object' || typeof b !== 'object') { | ||
return false | ||
} | ||
var keys = Object.keys(a); | ||
if (keys.length !== Object.keys(b).length) { | ||
return false | ||
} | ||
if (!keys.every(Object.prototype.hasOwnProperty.bind(b))) { | ||
return false | ||
} | ||
return keys.every(function (key) { return equal(a[key], b[key]); }) | ||
}; | ||
var DECIMAL_SYMBOLS = [',', '.', '٫']; | ||
@@ -139,11 +58,15 @@ var NumberFormat = function NumberFormat (options) { | ||
}; | ||
NumberFormat.prototype.parse = function parse (str) { | ||
var negative = this.isNegative(str); | ||
str = this.normalizeDigits(str); | ||
str = this.stripCurrencySymbol(str); | ||
str = this.stripMinusSymbol(str); | ||
var fraction = this.decimalSymbol ? ("(" + (escapeRegExp(this.decimalSymbol)) + "\\d*)?") : ''; | ||
var match = str.match(new RegExp(("^" + (this.integerPattern()) + fraction + "$"))); | ||
if (match) { | ||
return Number(("" + (negative ? '-' : '') + ((this.onlyDigits(match[1]))) + "." + ((this.onlyDigits(match[3] || ''))))) | ||
NumberFormat.prototype.parse = function parse (str, valueAsInteger) { | ||
if ( valueAsInteger === void 0 ) valueAsInteger = false; | ||
if (str) { | ||
var negative = this.isNegative(str); | ||
str = this.normalizeDigits(str); | ||
str = this.stripCurrencySymbol(str); | ||
str = this.stripMinusSymbol(str); | ||
var fraction = this.decimalSymbol ? ("(" + (escapeRegExp(this.decimalSymbol)) + "\\d*)?") : ''; | ||
var match = str.match(new RegExp(("^" + (this.integerPattern()) + fraction + "$"))); | ||
if (match) { | ||
var number = Number(("" + (negative ? '-' : '') + ((this.onlyDigits(match[1]))) + "." + ((this.onlyDigits(match[3] || ''))))); | ||
return valueAsInteger ? Number(number.toFixed(this.maximumFractionDigits).split('.').join('')) : number | ||
} | ||
} | ||
@@ -208,2 +131,38 @@ return null | ||
var DEFAULT_OPTIONS = { | ||
locale: undefined, | ||
currency: 'EUR', | ||
valueAsInteger: false, | ||
distractionFree: true, | ||
precision: undefined, | ||
autoDecimalMode: false, | ||
valueRange: undefined, | ||
allowNegative: true | ||
}; | ||
var parse = function (formattedValue, options) { | ||
var mergedOptions = Object.assign({}, DEFAULT_OPTIONS, options); | ||
return new NumberFormat(mergedOptions).parse(formattedValue, mergedOptions.valueAsInteger) | ||
}; | ||
var getValue = function (ref) { return (ref.$el || ref).$ci.getValue(); }; | ||
var setValue = function (ref, value) { | ||
(ref.$el || ref).$ci.setValue(value); | ||
}; | ||
var equal = function (a, b) { | ||
if (a === b) { | ||
return true | ||
} | ||
if (!a || !b || typeof a !== 'object' || typeof b !== 'object') { | ||
return false | ||
} | ||
var keys = Object.keys(a); | ||
if (keys.length !== Object.keys(b).length) { | ||
return false | ||
} | ||
if (!keys.every(Object.prototype.hasOwnProperty.bind(b))) { | ||
return false | ||
} | ||
return keys.every(function (key) { return equal(a[key], b[key]); }) | ||
}; | ||
var DefaultNumberMask = function DefaultNumberMask (numberFormat) { | ||
@@ -270,13 +229,50 @@ this.numberFormat = numberFormat; | ||
var getCaretPositionAfterFormat = function (newValue, inputtedValue, caretPosition, numberFormat, options) { | ||
var prefix = numberFormat.prefix; | ||
var suffix = numberFormat.suffix; | ||
var decimalSymbol = numberFormat.decimalSymbol; | ||
var maximumFractionDigits = numberFormat.maximumFractionDigits; | ||
var groupingSymbol = numberFormat.groupingSymbol; | ||
var decimalSymbolPosition = inputtedValue.indexOf(decimalSymbol) + 1; | ||
var caretPositionFromLeft = inputtedValue.length - caretPosition; | ||
if (Math.abs(newValue.length - inputtedValue.length) > 1 && caretPosition <= decimalSymbolPosition) { | ||
return newValue.indexOf(decimalSymbol) + 1 | ||
} else if (newValue.substr(caretPosition, 1) === groupingSymbol && count(newValue, groupingSymbol) === count(inputtedValue, groupingSymbol) + 1) { | ||
return newValue.length - caretPositionFromLeft - 1 | ||
} else { | ||
if (!options.autoDecimalMode && decimalSymbolPosition !== 0 && caretPosition > decimalSymbolPosition) { | ||
if (numberFormat.onlyDigits(inputtedValue.substr(decimalSymbolPosition)).length - 1 === maximumFractionDigits) { | ||
caretPositionFromLeft -= 1; | ||
} | ||
} | ||
return options.distractionFree.hideCurrencySymbol | ||
? newValue.length - caretPositionFromLeft | ||
: Math.max(newValue.length - Math.max(caretPositionFromLeft, suffix.length), prefix.length === 0 ? 0 : prefix.length + 1) | ||
} | ||
}; | ||
var getDistractionFreeCaretPosition = function (numberFormat, options, value, caretPosition) { | ||
var result = caretPosition; | ||
if (options.distractionFree.hideCurrencySymbol) { | ||
result -= numberFormat.prefix.length; | ||
} | ||
if (options.distractionFree.hideGroupingSymbol) { | ||
result -= count(value.substring(0, caretPosition), numberFormat.groupingSymbol); | ||
} | ||
return Math.max(0, result) | ||
}; | ||
var MAX_SAFE_INTEGER = Math.pow(2, 53) - 1; | ||
var init = function (el, optionsFromBinding, ref) { | ||
var $ci = ref.$ci; | ||
var inputElement = el.tagName.toLowerCase() === 'input' ? el : el.querySelector('input'); | ||
if (!inputElement) { | ||
throw new Error('No input element found') | ||
} | ||
var options = Object.assign({}, ($ci ? $ci.GLOBAL_OPTIONS : DEFAULT_OPTIONS), optionsFromBinding); | ||
var NumberInput = function NumberInput (el, options, callbackFns) { | ||
this.el = el; | ||
this.callbackFns = callbackFns; | ||
this.numberValue = null; | ||
this.addEventListener(); | ||
this.init(options); | ||
this.setValue(this.currencyFormat.parse(this.el.value)); | ||
}; | ||
NumberInput.prototype.init = function init (newOptions) { | ||
var options = Object.assign({}, newOptions); | ||
var distractionFree = options.distractionFree; | ||
var autoDecimalMode = options.autoDecimalMode; | ||
var valueRange = options.valueRange; | ||
var autoDecimalMode = options.autoDecimalMode; | ||
var valueRange = options.valueRange; | ||
if (typeof distractionFree === 'boolean') { | ||
@@ -302,58 +298,51 @@ options.distractionFree = { | ||
options.distractionFree.hideNegligibleDecimalDigits = false; | ||
inputElement.setAttribute('inputmode', 'numeric'); | ||
this.el.setAttribute('inputmode', 'numeric'); | ||
} else { | ||
inputElement.setAttribute('inputmode', 'decimal'); | ||
this.el.setAttribute('inputmode', 'decimal'); | ||
} | ||
var currencyFormat = new NumberFormat(options); | ||
inputElement.$ci = Object.assign({}, inputElement.$ci || { numberValue: null }, | ||
{options: options, | ||
numberMask: options.autoDecimalMode ? new AutoDecimalModeNumberMask(currencyFormat) : new DefaultNumberMask(currencyFormat), | ||
currencyFormat: currencyFormat}); | ||
return inputElement | ||
this.options = options; | ||
this.currencyFormat = new NumberFormat(this.options); | ||
this.numberMask = options.autoDecimalMode ? new AutoDecimalModeNumberMask(this.currencyFormat) : new DefaultNumberMask(this.currencyFormat); | ||
}; | ||
var triggerEvent = function (el, eventName) { | ||
var ref = el.$ci; | ||
var numberValue = ref.numberValue; | ||
var currencyFormat = ref.currencyFormat; | ||
var options = ref.options; | ||
numberValue = toExternalNumberModel(numberValue, options.valueAsInteger, currencyFormat.maximumFractionDigits); | ||
dispatchEvent(el, eventName, { numberValue: numberValue }); | ||
NumberInput.prototype.setOptions = function setOptions (options) { | ||
this.init(options); | ||
this.applyFixedFractionFormat(this.numberValue, true); | ||
}; | ||
var applyFixedFractionFormat = function (el, value, forcedChange) { | ||
if ( forcedChange === void 0 ) forcedChange = false; | ||
var ref = el.$ci; | ||
var currencyFormat = ref.currencyFormat; | ||
var options = ref.options; | ||
var ref$1 = options.valueRange; | ||
var min = ref$1.min; | ||
var max = ref$1.max; | ||
var validateValueRange = function () { return Math.min(Math.max(value, min), max); }; | ||
format(el, value != null ? currencyFormat.format(validateValueRange()) : null); | ||
if (value !== el.$ci.numberValue || forcedChange) { | ||
triggerEvent(el, 'change'); | ||
NumberInput.prototype.applyFixedFractionFormat = function applyFixedFractionFormat (number, forcedChange) { | ||
this.format(number != null ? this.currencyFormat.format(this.validateValueRange(number)) : null); | ||
if (number !== this.numberValue || forcedChange) { | ||
this.callbackFns.onChange(this.getValue()); | ||
} | ||
}; | ||
var updateInputValue = function (el, value, hideNegligibleDecimalDigits) { | ||
NumberInput.prototype.getValue = function getValue () { | ||
return this.currencyFormat.parse(this.formattedValue, this.options.valueAsInteger) | ||
}; | ||
NumberInput.prototype.setValue = function setValue (value) { | ||
var newValue = this.options.valueAsInteger && value != null ? value / Math.pow(10, this.currencyFormat.maximumFractionDigits) : value; | ||
if (newValue !== this.numberValue) { | ||
this.applyFixedFractionFormat(newValue); | ||
} | ||
}; | ||
NumberInput.prototype.validateValueRange = function validateValueRange (value) { | ||
var ref = this.options.valueRange; | ||
var min = ref.min; | ||
var max = ref.max; | ||
return Math.min(Math.max(value, min), max) | ||
}; | ||
NumberInput.prototype.updateInputValue = function updateInputValue (value, hideNegligibleDecimalDigits) { | ||
if ( hideNegligibleDecimalDigits === void 0 ) hideNegligibleDecimalDigits = false; | ||
if (value != null) { | ||
var ref = el.$ci; | ||
var focus = ref.focus; | ||
var decimalSymbolInsertedAt = ref.decimalSymbolInsertedAt; | ||
var options = ref.options; | ||
var numberMask = ref.numberMask; | ||
var currencyFormat = ref.currencyFormat; | ||
var previousConformedValue = ref.previousConformedValue; | ||
var allowNegative = options.allowNegative; | ||
var distractionFree = options.distractionFree; | ||
if (decimalSymbolInsertedAt !== undefined) { | ||
value = currencyFormat.normalizeDecimalSymbol(value, decimalSymbolInsertedAt); | ||
el.$ci.decimalSymbolInsertedAt = undefined; | ||
if (this.decimalSymbolInsertedAt !== undefined) { | ||
value = this.currencyFormat.normalizeDecimalSymbol(value, this.decimalSymbolInsertedAt); | ||
this.decimalSymbolInsertedAt = undefined; | ||
} | ||
var conformedValue = numberMask.conformToMask(value, previousConformedValue); | ||
var conformedValue = this.numberMask.conformToMask(value, this.formattedValue); | ||
var formattedValue; | ||
if (typeof conformedValue === 'object') { | ||
var numberValue = conformedValue.numberValue; | ||
var fractionDigits = conformedValue.fractionDigits; | ||
var maximumFractionDigits = currencyFormat.maximumFractionDigits; | ||
var minimumFractionDigits = currencyFormat.minimumFractionDigits; | ||
if (focus) { | ||
var fractionDigits = conformedValue.fractionDigits; | ||
var ref = this.currencyFormat; | ||
var maximumFractionDigits = ref.maximumFractionDigits; | ||
var minimumFractionDigits = ref.minimumFractionDigits; | ||
if (this.focus) { | ||
minimumFractionDigits = maximumFractionDigits; | ||
@@ -365,5 +354,5 @@ } | ||
formattedValue = numberValue > MAX_SAFE_INTEGER | ||
? previousConformedValue | ||
: currencyFormat.format(numberValue, { | ||
useGrouping: !(focus && distractionFree.hideGroupingSymbol), | ||
? this.formattedValue | ||
: this.currencyFormat.format(numberValue, { | ||
useGrouping: !(this.focus && this.options.distractionFree.hideGroupingSymbol), | ||
minimumFractionDigits: minimumFractionDigits, | ||
@@ -375,71 +364,52 @@ maximumFractionDigits: maximumFractionDigits | ||
} | ||
if (!allowNegative) { | ||
formattedValue = formattedValue.replace(currencyFormat.negativePrefix, currencyFormat.prefix); | ||
if (!this.options.allowNegative) { | ||
formattedValue = formattedValue.replace(this.currencyFormat.negativePrefix, this.currencyFormat.prefix); | ||
} | ||
if (focus && distractionFree.hideCurrencySymbol) { | ||
if (this.focus && this.options.distractionFree.hideCurrencySymbol) { | ||
formattedValue = formattedValue | ||
.replace(currencyFormat.negativePrefix, currencyFormat.minusSymbol) | ||
.replace(currencyFormat.prefix, '') | ||
.replace(currencyFormat.suffix, ''); | ||
.replace(this.currencyFormat.negativePrefix, this.currencyFormat.minusSymbol) | ||
.replace(this.currencyFormat.prefix, '') | ||
.replace(this.currencyFormat.suffix, ''); | ||
} | ||
el.value = formattedValue; | ||
el.$ci.numberValue = currencyFormat.parse(el.value); | ||
this.el.value = formattedValue; | ||
this.numberValue = this.currencyFormat.parse(formattedValue); | ||
} else { | ||
el.value = el.$ci.numberValue = null; | ||
this.el.value = this.numberValue = null; | ||
} | ||
el.$ci.previousConformedValue = el.value; | ||
this.formattedValue = this.el.value; | ||
}; | ||
var format = function (el, value, hideNegligibleDecimalDigits) { | ||
if ( hideNegligibleDecimalDigits === void 0 ) hideNegligibleDecimalDigits = false; | ||
updateInputValue(el, value, hideNegligibleDecimalDigits); | ||
triggerEvent(el, 'input'); | ||
NumberInput.prototype.format = function format (value) { | ||
this.updateInputValue(value); | ||
this.callbackFns.onInput(this.getValue()); | ||
}; | ||
var addEventListener = function (el) { | ||
el.addEventListener('input', function (e) { | ||
if (!e.detail) { | ||
var value = el.value; | ||
var selectionStart = el.selectionStart; | ||
var el_$ci = el.$ci; | ||
var currencyFormat = el_$ci.currencyFormat; | ||
var options = el_$ci.options; | ||
format(el, value); | ||
if (el.$ci.focus) { | ||
setCaretPosition(el, getCaretPositionAfterFormat(el.value, value, selectionStart, currencyFormat, options)); | ||
} | ||
NumberInput.prototype.addEventListener = function addEventListener () { | ||
var this$1 = this; | ||
this.el.addEventListener('input', function () { | ||
var ref = this$1.el; | ||
var value = ref.value; | ||
var selectionStart = ref.selectionStart; | ||
this$1.format(value); | ||
if (this$1.focus) { | ||
this$1.setCaretPosition(getCaretPositionAfterFormat(this$1.formattedValue, value, selectionStart, this$1.currencyFormat, this$1.options)); | ||
} | ||
}, { capture: true }); | ||
el.addEventListener('keypress', function (e) { | ||
if (DECIMAL_SYMBOLS.includes(e.key)) { | ||
el.$ci.decimalSymbolInsertedAt = el.selectionStart; | ||
} | ||
}); | ||
el.addEventListener('format', function (e) { | ||
var ref = el.$ci; | ||
var currencyFormat = ref.currencyFormat; | ||
var options = ref.options; | ||
var numberValue = ref.numberValue; | ||
var toInternalNumberModel = function (n) { return options.valueAsInteger && n != null ? n / Math.pow(10, currencyFormat.maximumFractionDigits) : n; }; | ||
var newValue = toInternalNumberModel(e.detail.value); | ||
if (numberValue !== newValue) { | ||
applyFixedFractionFormat(el, newValue); | ||
} | ||
}); | ||
el.addEventListener('focus', function () { | ||
el.$ci.focus = true; | ||
var ref = el.$ci.options.distractionFree; | ||
var hideCurrencySymbol = ref.hideCurrencySymbol; | ||
var hideGroupingSymbol = ref.hideGroupingSymbol; | ||
var hideNegligibleDecimalDigits = ref.hideNegligibleDecimalDigits; | ||
this.el.addEventListener('focus', function () { | ||
this$1.focus = true; | ||
var ref = this$1.options.distractionFree; | ||
var hideCurrencySymbol = ref.hideCurrencySymbol; | ||
var hideGroupingSymbol = ref.hideGroupingSymbol; | ||
var hideNegligibleDecimalDigits = ref.hideNegligibleDecimalDigits; | ||
if (hideCurrencySymbol || hideGroupingSymbol || hideNegligibleDecimalDigits) { | ||
setTimeout(function () { | ||
var value = el.value; | ||
var selectionStart = el.selectionStart; | ||
var selectionEnd = el.selectionEnd; | ||
var ref = this$1.el; | ||
var value = ref.value; | ||
var selectionStart = ref.selectionStart; | ||
var selectionEnd = ref.selectionEnd; | ||
if (value) { | ||
format(el, value, hideNegligibleDecimalDigits); | ||
this$1.updateInputValue(this$1.el.value, hideNegligibleDecimalDigits); | ||
} | ||
if (Math.abs(selectionStart - selectionEnd) > 0) { | ||
el.setSelectionRange(0, el.value.length); | ||
this$1.setCaretPosition(0, this$1.el.value.length); | ||
} else { | ||
setCaretPosition(el, getDistractionFreeCaretPosition(el.$ci.currencyFormat, el.$ci.options, value, selectionStart)); | ||
this$1.setCaretPosition(getDistractionFreeCaretPosition(this$1.currencyFormat, this$1.options, value, selectionStart)); | ||
} | ||
@@ -449,29 +419,47 @@ }); | ||
}); | ||
el.addEventListener('blur', function () { | ||
el.$ci.focus = false; | ||
if (el.$ci.numberValue != null) { | ||
applyFixedFractionFormat(el, el.$ci.numberValue); | ||
this.el.addEventListener('keypress', function (e) { | ||
if (DECIMAL_SYMBOLS.includes(e.key)) { | ||
this$1.decimalSymbolInsertedAt = this$1.el.selectionStart; | ||
} | ||
}); | ||
el.addEventListener('change', function (e) { | ||
if (!e.detail) { | ||
triggerEvent(el, 'change'); | ||
this.el.addEventListener('blur', function () { | ||
this$1.focus = false; | ||
if (this$1.numberValue != null) { | ||
this$1.applyFixedFractionFormat(this$1.numberValue); | ||
} | ||
}); | ||
this.el.addEventListener('change', function () { | ||
this$1.callbackFns.onChange(this$1.getValue()); | ||
}); | ||
}; | ||
NumberInput.prototype.setCaretPosition = function setCaretPosition (start, end) { | ||
if ( end === void 0 ) end = start; | ||
this.el.setSelectionRange(start, end); }; | ||
var directive = { | ||
bind: function bind (el, ref, ref$1) { | ||
var value = ref.value; | ||
var context = ref$1.context; | ||
var inputElement = init(el, value, context); | ||
addEventListener(inputElement); | ||
setValue(inputElement, inputElement.$ci.currencyFormat.parse(inputElement.value)); | ||
bind: function bind (el, ref, vnode) { | ||
var optionsFromBinding = ref.value; | ||
var inputElement = el.tagName.toLowerCase() === 'input' ? el : el.querySelector('input'); | ||
if (!inputElement) { | ||
throw new Error('No input element found') | ||
} | ||
var options = Object.assign({}, DEFAULT_OPTIONS, | ||
(vnode.context.$ci || {}).globalOptions, | ||
optionsFromBinding); | ||
var listeners = (vnode.data && vnode.data.on) || (vnode.componentOptions && vnode.componentOptions.listeners); | ||
var emit = function (event, data) { | ||
if (listeners[event]) { | ||
listeners[event].fns(vnode.componentOptions ? data : { target: { value: data } }); | ||
} | ||
}; | ||
el.$ci = new NumberInput(inputElement, options, { | ||
onChange: function () { return emit('change', inputElement.value); }, | ||
onInput: function () { emit('input', inputElement.value); } | ||
}); | ||
}, | ||
componentUpdated: function componentUpdated (el, ref, ref$1) { | ||
componentUpdated: function componentUpdated (el, ref) { | ||
var value = ref.value; | ||
var oldValue = ref.oldValue; | ||
var context = ref$1.context; | ||
if (!equal(value, oldValue)) { | ||
var inputElement = init(el, value, context); | ||
applyFixedFractionFormat(inputElement, inputElement.$ci.numberValue, true); | ||
el.$ci.setOptions(value); | ||
} | ||
@@ -490,10 +478,9 @@ } | ||
on: Object.assign({}, this.$listeners, | ||
{change: function (e) { | ||
if (e.detail) { | ||
this$1.$emit('change', e.detail.numberValue); | ||
} | ||
{change: function () { | ||
this$1.$emit('change', getValue(this$1.$el)); | ||
}, | ||
input: function (e) { | ||
if (e.detail && this$1.value !== e.detail.numberValue) { | ||
this$1.$emit('input', e.detail.numberValue); | ||
input: function () { | ||
var numberValue = getValue(this$1.$el); | ||
if (this$1.value !== numberValue) { | ||
this$1.$emit('input', numberValue); | ||
} | ||
@@ -551,3 +538,4 @@ }}) | ||
var this$1 = this; | ||
var options = Object.assign({}, this.$ci ? this.$ci.GLOBAL_OPTIONS : DEFAULT_OPTIONS); | ||
var options = Object.assign({}, DEFAULT_OPTIONS, | ||
(this.$ci || {}).globalOptions); | ||
Object.keys(DEFAULT_OPTIONS).forEach(function (key) { | ||
@@ -580,5 +568,6 @@ if (this$1[key] !== undefined) { | ||
Vue.prototype.$ci = { | ||
parse: function (formattedValue, options) { return parse(formattedValue, Object.assign({}, globalOptions, options)); }, | ||
getValue: getValue, | ||
setValue: setValue, | ||
GLOBAL_OPTIONS: Object.assign({}, DEFAULT_OPTIONS, globalOptions) | ||
globalOptions: globalOptions | ||
}; | ||
@@ -585,0 +574,0 @@ } |
{ | ||
"name": "vue-currency-input", | ||
"description": "Easy input of currency formatted numbers for Vue.js.", | ||
"version": "1.21.0", | ||
"version": "1.22.0", | ||
"license": "MIT", | ||
@@ -60,3 +60,2 @@ "unpkg": "dist/vue-currency-input.umd.js", | ||
"eslint-plugin-vue": "^6.2.2", | ||
"node-sass": "^4.13.0", | ||
"rollup": "^2.15.0", | ||
@@ -66,3 +65,2 @@ "rollup-plugin-buble": "^0.19.8", | ||
"rollup-plugin-filesize": "^9.0.0", | ||
"sass-loader": "^8.0.0", | ||
"vue": "^2.6.11", | ||
@@ -69,0 +67,0 @@ "vue-template-compiler": "^2.6.11", |
@@ -1,3 +0,2 @@ | ||
import dispatchEvent from './utils/dispatchEvent' | ||
import { toExternalNumberModel } from './utils/numberUtils' | ||
import NumberFormat from './numberFormat' | ||
@@ -16,11 +15,20 @@ export const DEFAULT_OPTIONS = { | ||
/** | ||
* Parses a number from a currency formatted string. | ||
* | ||
* @param {String} formattedValue The currency formatted string to be parsed, for example `$1,234.50`. | ||
* @param {Object} options The configured options of the respective `v-currency` directive. | ||
* @returns {Number | null} The parsed number or `null` if the formatted string does not match. | ||
*/ | ||
export const parse = (formattedValue, options) => { | ||
const mergedOptions = { ...DEFAULT_OPTIONS, ...options } | ||
return new NumberFormat(mergedOptions).parse(formattedValue, mergedOptions.valueAsInteger) | ||
} | ||
/** | ||
* Returns the current number value of an input. | ||
* | ||
* @param {HTMLInputElement} el The input element the `v-currency` directive is bound to. | ||
* @param ref {Element | VueConstructor} The element or Vue component the `v-currency` directive is bound to. | ||
* @returns {Number | null} The current number value or `null` if empty. | ||
*/ | ||
export const getValue = el => { | ||
const { numberValue, currencyFormat, options } = el.$ci | ||
return toExternalNumberModel(numberValue, options.valueAsInteger, currencyFormat.maximumFractionDigits) | ||
} | ||
export const getValue = (ref) => (ref.$el || ref).$ci.getValue() | ||
@@ -30,5 +38,7 @@ /** | ||
* | ||
* @param {HTMLInputElement} el The input element the `v-currency` directive is bound to. | ||
* @param ref {Element | VueConstructor} The element or Vue component the `v-currency` directive is bound to. | ||
* @param {Number} value The number to be set. | ||
*/ | ||
export const setValue = (el, value) => dispatchEvent(el, 'format', { value }) | ||
export const setValue = (ref, value) => { | ||
(ref.$el || ref).$ci.setValue(value) | ||
} |
@@ -1,2 +0,2 @@ | ||
import { DEFAULT_OPTIONS, setValue } from './api' | ||
import { DEFAULT_OPTIONS, getValue, setValue } from './api' | ||
import currencyDirective from './directive' | ||
@@ -13,10 +13,9 @@ | ||
...this.$listeners, | ||
change: e => { | ||
if (e.detail) { | ||
this.$emit('change', e.detail.numberValue) | ||
} | ||
change: () => { | ||
this.$emit('change', getValue(this.$el)) | ||
}, | ||
input: e => { | ||
if (e.detail && this.value !== e.detail.numberValue) { | ||
this.$emit('input', e.detail.numberValue) | ||
input: () => { | ||
const numberValue = getValue(this.$el) | ||
if (this.value !== numberValue) { | ||
this.$emit('input', numberValue) | ||
} | ||
@@ -74,4 +73,7 @@ } | ||
options () { | ||
const options = { ...this.$ci ? this.$ci.GLOBAL_OPTIONS : DEFAULT_OPTIONS } | ||
Object.keys(DEFAULT_OPTIONS).forEach(key => { | ||
const options = { | ||
...DEFAULT_OPTIONS, | ||
...(this.$ci || {}).globalOptions | ||
} | ||
Object.keys(DEFAULT_OPTIONS).forEach((key) => { | ||
if (this[key] !== undefined) { | ||
@@ -78,0 +80,0 @@ options[key] = this[key] |
@@ -1,191 +0,34 @@ | ||
import { DEFAULT_OPTIONS, setValue } from './api' | ||
import { getCaretPositionAfterFormat, getDistractionFreeCaretPosition, setCaretPosition } from './utils/caretPosition' | ||
import dispatchEvent from './utils/dispatchEvent' | ||
import { DEFAULT_OPTIONS } from './api' | ||
import equal from './utils/equal' | ||
import { toExternalNumberModel } from './utils/numberUtils' | ||
import NumberFormat, { DECIMAL_SYMBOLS } from './numberFormat' | ||
import { AutoDecimalModeNumberMask, DefaultNumberMask } from './numberMask' | ||
import { NumberInput } from './numberInput' | ||
const MAX_SAFE_INTEGER = Math.pow(2, 53) - 1 | ||
const init = (el, optionsFromBinding, { $ci }) => { | ||
const inputElement = el.tagName.toLowerCase() === 'input' ? el : el.querySelector('input') | ||
if (!inputElement) { | ||
throw new Error('No input element found') | ||
} | ||
const options = { ...($ci ? $ci.GLOBAL_OPTIONS : DEFAULT_OPTIONS), ...optionsFromBinding } | ||
const { distractionFree, autoDecimalMode, valueRange } = options | ||
if (typeof distractionFree === 'boolean') { | ||
options.distractionFree = { | ||
hideCurrencySymbol: distractionFree, | ||
hideNegligibleDecimalDigits: distractionFree, | ||
hideGroupingSymbol: distractionFree | ||
export default { | ||
bind (el, { value: optionsFromBinding }, vnode) { | ||
const inputElement = el.tagName.toLowerCase() === 'input' ? el : el.querySelector('input') | ||
if (!inputElement) { | ||
throw new Error('No input element found') | ||
} | ||
} | ||
if (valueRange) { | ||
options.valueRange = { | ||
min: valueRange.min !== undefined ? Math.max(valueRange.min, -MAX_SAFE_INTEGER) : -MAX_SAFE_INTEGER, | ||
max: valueRange.max !== undefined ? Math.min(valueRange.max, MAX_SAFE_INTEGER) : MAX_SAFE_INTEGER | ||
const options = { | ||
...DEFAULT_OPTIONS, | ||
...(vnode.context.$ci || {}).globalOptions, | ||
...optionsFromBinding | ||
} | ||
} else { | ||
options.valueRange = { | ||
min: -MAX_SAFE_INTEGER, | ||
max: MAX_SAFE_INTEGER | ||
} | ||
} | ||
const listeners = (vnode.data && vnode.data.on) || (vnode.componentOptions && vnode.componentOptions.listeners) | ||
if (autoDecimalMode) { | ||
options.distractionFree.hideNegligibleDecimalDigits = false | ||
inputElement.setAttribute('inputmode', 'numeric') | ||
} else { | ||
inputElement.setAttribute('inputmode', 'decimal') | ||
} | ||
const currencyFormat = new NumberFormat(options) | ||
inputElement.$ci = { | ||
...inputElement.$ci || { numberValue: null }, | ||
options, | ||
numberMask: options.autoDecimalMode ? new AutoDecimalModeNumberMask(currencyFormat) : new DefaultNumberMask(currencyFormat), | ||
currencyFormat | ||
} | ||
return inputElement | ||
} | ||
const triggerEvent = (el, eventName) => { | ||
let { numberValue, currencyFormat, options } = el.$ci | ||
numberValue = toExternalNumberModel(numberValue, options.valueAsInteger, currencyFormat.maximumFractionDigits) | ||
dispatchEvent(el, eventName, { numberValue }) | ||
} | ||
const applyFixedFractionFormat = (el, value, forcedChange = false) => { | ||
const { currencyFormat, options } = el.$ci | ||
const { min, max } = options.valueRange | ||
const validateValueRange = () => Math.min(Math.max(value, min), max) | ||
format(el, value != null ? currencyFormat.format(validateValueRange()) : null) | ||
if (value !== el.$ci.numberValue || forcedChange) { | ||
triggerEvent(el, 'change') | ||
} | ||
} | ||
const updateInputValue = (el, value, hideNegligibleDecimalDigits) => { | ||
if (value != null) { | ||
const { focus, decimalSymbolInsertedAt, options, numberMask, currencyFormat, previousConformedValue } = el.$ci | ||
const { allowNegative, distractionFree } = options | ||
if (decimalSymbolInsertedAt !== undefined) { | ||
value = currencyFormat.normalizeDecimalSymbol(value, decimalSymbolInsertedAt) | ||
el.$ci.decimalSymbolInsertedAt = undefined | ||
} | ||
const conformedValue = numberMask.conformToMask(value, previousConformedValue) | ||
let formattedValue | ||
if (typeof conformedValue === 'object') { | ||
const { numberValue, fractionDigits } = conformedValue | ||
let { maximumFractionDigits, minimumFractionDigits } = currencyFormat | ||
if (focus) { | ||
minimumFractionDigits = maximumFractionDigits | ||
const emit = (event, data) => { | ||
if (listeners[event]) { | ||
listeners[event].fns(vnode.componentOptions ? data : { target: { value: data } }) | ||
} | ||
minimumFractionDigits = hideNegligibleDecimalDigits | ||
? fractionDigits.replace(/0+$/, '').length | ||
: Math.min(minimumFractionDigits, fractionDigits.length) | ||
formattedValue = numberValue > MAX_SAFE_INTEGER | ||
? previousConformedValue | ||
: currencyFormat.format(numberValue, { | ||
useGrouping: !(focus && distractionFree.hideGroupingSymbol), | ||
minimumFractionDigits, | ||
maximumFractionDigits | ||
}) | ||
} else { | ||
formattedValue = conformedValue | ||
} | ||
if (!allowNegative) { | ||
formattedValue = formattedValue.replace(currencyFormat.negativePrefix, currencyFormat.prefix) | ||
} | ||
if (focus && distractionFree.hideCurrencySymbol) { | ||
formattedValue = formattedValue | ||
.replace(currencyFormat.negativePrefix, currencyFormat.minusSymbol) | ||
.replace(currencyFormat.prefix, '') | ||
.replace(currencyFormat.suffix, '') | ||
} | ||
el.value = formattedValue | ||
el.$ci.numberValue = currencyFormat.parse(el.value) | ||
} else { | ||
el.value = el.$ci.numberValue = null | ||
} | ||
el.$ci.previousConformedValue = el.value | ||
} | ||
const format = (el, value, hideNegligibleDecimalDigits = false) => { | ||
updateInputValue(el, value, hideNegligibleDecimalDigits) | ||
triggerEvent(el, 'input') | ||
} | ||
const addEventListener = el => { | ||
el.addEventListener('input', e => { | ||
if (!e.detail) { | ||
const { value, selectionStart, $ci: { currencyFormat, options } } = el | ||
format(el, value) | ||
if (el.$ci.focus) { | ||
setCaretPosition(el, getCaretPositionAfterFormat(el.value, value, selectionStart, currencyFormat, options)) | ||
} | ||
} | ||
}, { capture: true }) | ||
el.addEventListener('keypress', e => { | ||
if (DECIMAL_SYMBOLS.includes(e.key)) { | ||
el.$ci.decimalSymbolInsertedAt = el.selectionStart | ||
} | ||
}) | ||
el.addEventListener('format', e => { | ||
const { currencyFormat, options, numberValue } = el.$ci | ||
const toInternalNumberModel = n => options.valueAsInteger && n != null ? n / Math.pow(10, currencyFormat.maximumFractionDigits) : n | ||
const newValue = toInternalNumberModel(e.detail.value) | ||
if (numberValue !== newValue) { | ||
applyFixedFractionFormat(el, newValue) | ||
} | ||
}) | ||
el.addEventListener('focus', () => { | ||
el.$ci.focus = true | ||
const { hideCurrencySymbol, hideGroupingSymbol, hideNegligibleDecimalDigits } = el.$ci.options.distractionFree | ||
if (hideCurrencySymbol || hideGroupingSymbol || hideNegligibleDecimalDigits) { | ||
setTimeout(() => { | ||
const { value, selectionStart, selectionEnd } = el | ||
if (value) { | ||
format(el, value, hideNegligibleDecimalDigits) | ||
} | ||
if (Math.abs(selectionStart - selectionEnd) > 0) { | ||
el.setSelectionRange(0, el.value.length) | ||
} else { | ||
setCaretPosition(el, getDistractionFreeCaretPosition(el.$ci.currencyFormat, el.$ci.options, value, selectionStart)) | ||
} | ||
}) | ||
} | ||
}) | ||
el.addEventListener('blur', () => { | ||
el.$ci.focus = false | ||
if (el.$ci.numberValue != null) { | ||
applyFixedFractionFormat(el, el.$ci.numberValue) | ||
} | ||
}) | ||
el.addEventListener('change', e => { | ||
if (!e.detail) { | ||
triggerEvent(el, 'change') | ||
} | ||
}) | ||
} | ||
export default { | ||
bind (el, { value }, { context }) { | ||
const inputElement = init(el, value, context) | ||
addEventListener(inputElement) | ||
setValue(inputElement, inputElement.$ci.currencyFormat.parse(inputElement.value)) | ||
el.$ci = new NumberInput(inputElement, options, { | ||
onChange: () => emit('change', inputElement.value), | ||
onInput: () => { emit('input', inputElement.value) } | ||
}) | ||
}, | ||
componentUpdated (el, { value, oldValue }, { context }) { | ||
componentUpdated (el, { value, oldValue }) { | ||
if (!equal(value, oldValue)) { | ||
const inputElement = init(el, value, context) | ||
applyFixedFractionFormat(inputElement, inputElement.$ci.numberValue, true) | ||
el.$ci.setOptions(value) | ||
} | ||
} | ||
} |
@@ -48,2 +48,4 @@ import { Component, DirectiveOptions, PluginFunction } from 'vue' | ||
export function parse (formattedValue: string, options: CurrencyInputOptions): number | null | ||
export function getValue (el: HTMLInputElement): number | null | ||
@@ -55,8 +57,12 @@ | ||
interface Vue { | ||
GLOBAL_OPTIONS: CurrencyInputOptions | ||
$ci: { | ||
globalOptions: CurrencyInputOptions | ||
$getValue (el: HTMLInputElement): number | null | ||
parse (formattedValue: string, options: CurrencyInputOptions): number | null | ||
$setValue (el: HTMLInputElement, value: Number): void | ||
getValue (el: HTMLInputElement): number | null | ||
setValue (el: HTMLInputElement, value: Number): void | ||
} | ||
} | ||
} |
@@ -13,3 +13,3 @@ import { count, escapeRegExp, startsWith, substringBefore } from './utils/stringUtils' | ||
this.currency = currency | ||
this.digits = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(i => i.toLocaleString(locale)) | ||
this.digits = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map((i) => i.toLocaleString(locale)) | ||
this.decimalSymbol = count(ps, this.digits[0]) ? ps.substr(ps.indexOf(this.digits[6]) + 1, 1) : undefined | ||
@@ -44,11 +44,14 @@ this.groupingSymbol = ps.substr(ps.indexOf(this.digits[3]) + 1, 1) | ||
parse (str) { | ||
const negative = this.isNegative(str) | ||
str = this.normalizeDigits(str) | ||
str = this.stripCurrencySymbol(str) | ||
str = this.stripMinusSymbol(str) | ||
const fraction = this.decimalSymbol ? `(${escapeRegExp(this.decimalSymbol)}\\d*)?` : '' | ||
const match = str.match(new RegExp(`^${this.integerPattern()}${fraction}$`)) | ||
if (match) { | ||
return Number(`${negative ? '-' : ''}${(this.onlyDigits(match[1]))}.${(this.onlyDigits(match[3] || ''))}`) | ||
parse (str, valueAsInteger = false) { | ||
if (str) { | ||
const negative = this.isNegative(str) | ||
str = this.normalizeDigits(str) | ||
str = this.stripCurrencySymbol(str) | ||
str = this.stripMinusSymbol(str) | ||
const fraction = this.decimalSymbol ? `(${escapeRegExp(this.decimalSymbol)}\\d*)?` : '' | ||
const match = str.match(new RegExp(`^${this.integerPattern()}${fraction}$`)) | ||
if (match) { | ||
const number = Number(`${negative ? '-' : ''}${(this.onlyDigits(match[1]))}.${(this.onlyDigits(match[3] || ''))}`) | ||
return valueAsInteger ? Number(number.toFixed(this.maximumFractionDigits).split('.').join('')) : number | ||
} | ||
} | ||
@@ -102,3 +105,3 @@ return null | ||
normalizeDecimalSymbol (str, from) { | ||
DECIMAL_SYMBOLS.forEach(s => { | ||
DECIMAL_SYMBOLS.forEach((s) => { | ||
str = str.substr(0, from) + str.substr(from).replace(s, this.decimalSymbol) | ||
@@ -105,0 +108,0 @@ }) |
@@ -1,2 +0,2 @@ | ||
import { DEFAULT_OPTIONS, getValue, setValue } from './api' | ||
import { getValue, parse, setValue } from './api' | ||
import component from './component' | ||
@@ -14,7 +14,8 @@ import directive from './directive' | ||
Vue.prototype.$ci = { | ||
parse: (formattedValue, options) => parse(formattedValue, { ...globalOptions, ...options }), | ||
getValue, | ||
setValue, | ||
GLOBAL_OPTIONS: { ...DEFAULT_OPTIONS, ...globalOptions } | ||
globalOptions | ||
} | ||
} | ||
} |
@@ -15,5 +15,5 @@ const equal = (a, b) => { | ||
} | ||
return keys.every(key => equal(a[key], b[key])) | ||
return keys.every((key) => equal(a[key], b[key])) | ||
} | ||
export default equal |
75934
23
1747
19