UNPKG

nice-numeric-input

Version:

A Vue input component for numbers with realtime formating and currency support.

640 lines (590 loc) 25.9 kB
'use strict';var Vue=require('vue');function _interopDefaultLegacy(e){return e&&typeof e==='object'&&'default'in e?e:{'default':e}}var Vue__default=/*#__PURE__*/_interopDefaultLegacy(Vue);function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); } function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } function _iterableToArrayLimit(arr, i) { var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]; if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }var script = Vue__default['default'].extend({ props: { value: { type: Number, default: 0 }, id: { type: String, default: "nice-numeric-input" }, name: { type: String, default: "nice-numeric-input" }, label: { type: String, required: true }, placeholder: { type: String, default: "" }, step: { type: Number, default: 1 }, min: { type: Number, default: Number.NEGATIVE_INFINITY }, max: { type: Number, default: Number.POSITIVE_INFINITY }, isValid: { type: Boolean, default: false, required: false }, disabled: { type: Boolean, default: false }, locale: { type: String, default: null }, currency: { type: String, default: null }, minDecimalPlaces: { type: Number, default: 0 }, maxDecimalPlaces: { type: Number, default: 2 }, integerOnly: { type: Boolean, default: false }, noControls: { type: Boolean, default: false }, hideLabel: { type: Boolean, default: false }, decreaseTitle: { type: String, default: "Increase" }, increaseTitle: { type: String, default: "Decrease" }, increaseText: { type: String, default: "+" }, decreaseText: { type: String, default: "-" }, superIncreaseText: { type: String, default: "++" }, superDecreaseText: { type: String, default: "--" }, ultraIncreaseText: { type: String, default: "+++" }, ultraDecreaseText: { type: String, default: "---" }, superStep: { type: Number, default: 10 }, ultraStep: { type: Number, default: 100 }, labelClass: { type: String, default: null }, inputClass: { type: String, default: null }, decreaseButtonClass: { type: String, default: null }, increaseButtonClass: { type: String, default: null }, wrapperClass: { type: String, default: null }, superStepClass: { type: String, default: "" }, ultraStepClass: { type: String, default: "" } }, data: function data() { return { internalValue: null, ctrlActive: false, shiftActive: false, internalLocale: "en-US" }; }, computed: { labelId: function labelId() { return this.id + '-label'; }, canIncrease: function canIncrease() { return this.internalValueIsNotDefined || this.internalValue + this.internalStep <= this.max; }, canDecrease: function canDecrease() { return this.internalValueIsNotDefined || this.internalValue - this.internalStep >= this.min; }, displayString: function displayString() { if (this.internalValueIsNotDefined) { if (this.value) { this.setInternalValue(this.value); } else { this.setToDefaultValue(); } } var minDecimals = 0, maxDecimals = 0; if (!this.integerOnly) { minDecimals = this.minDecimalPlaces; maxDecimals = this.maxDecimalPlaces; } return this.internalValue.toLocaleString(this.internalLocale, { style: this.currency ? "currency" : "decimal", currency: this.currency || undefined, minimumFractionDigits: minDecimals, maximumFractionDigits: maxDecimals }); }, internalValueIsNotDefined: function internalValueIsNotDefined() { return this.internalValue == null || Number.isNaN(this.internalValue); }, isValidComputed: { get: function get() { return this.isValid; }, set: function set(val) { this.$emit('update:isValid', val); } }, isError: function isError() { var error = false; if (this.internalValue == null || this.internalValue > this.max || this.internalValue < this.min) { error = true; } this.isValidComputed = !error; return error; }, isUltraChangeActive: function isUltraChangeActive() { return this.ctrlActive && this.shiftActive; }, isSuperChangeActive: function isSuperChangeActive() { // Equivalent of ctrlActive XOR shiftActive for booleans. return this.ctrlActive != this.shiftActive; }, internalIncreaseText: function internalIncreaseText() { if (this.isUltraChangeActive) { return this.ultraIncreaseText; } else if (this.isSuperChangeActive) { return this.superIncreaseText; } else { return this.increaseText; } }, internalDecreaseText: function internalDecreaseText() { if (this.isUltraChangeActive) { return this.ultraDecreaseText; } else if (this.isSuperChangeActive) { return this.superDecreaseText; } else { return this.decreaseText; } }, changeButtonClass: function changeButtonClass() { if (this.isUltraChangeActive) { return this.ultraStepClass || "much-smaller-padding"; } else if (this.isSuperChangeActive) { return this.superStepClass || "smaller-padding"; } else { return ""; } }, internalStep: function internalStep() { if (this.isUltraChangeActive) { return this.ultraStep; } else if (this.isSuperChangeActive) { return this.superStep; } else { return this.step; } } }, methods: { getDefaultValue: function getDefaultValue() { if (this.min != Number.NEGATIVE_INFINITY) { return this.min; } return 0; }, handlePaste: function handlePaste(e) { var clipboardData = e.clipboardData || window.clipboardData; var pastedData = clipboardData.getData("Text"); if (pastedData) { e.stopPropagation(); e.preventDefault(); this.valueChanged(pastedData, null); } }, handleInput: function handleInput(e) { var target = e.target; this.valueChanged(target.value, e.data); }, handleChange: function handleChange(e) { var target = e.target; this.valueChanged(target.value, null, true); }, valueChanged: function valueChanged(newValue, newInput) { var strictValidation = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; var possibleRecurse = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true; var decimalNumbersRegex = /[+-]?\d+(\.\d+)?/g; var normalisedInput = this.normaliseInput(newValue, this.internalLocale); // Match to find any numbers. var matches = normalisedInput.match(decimalNumbersRegex); var result = null; var isValidNonNumeric = false; if (!strictValidation && (this.isEmptyInput(newValue) || this.isStartingSignedInput(newValue) || this.isAddingDecimalPlaces(newValue))) { isValidNonNumeric = true; } else if (matches != null && matches.length > 0) { // Parse the first match. result = parseFloat(matches[0]); } else { if (possibleRecurse && newValue.length > 0) { this.valueChanged("".concat(newValue[0]), newInput, strictValidation, false); return; } // Manually clear the invalid input to cover edge cases where computed properties don't update because the internal value hasn't changed value. this.$refs.numberInput.value = null; } // Don't reset to 0 when we have a valid non-numeric edge case. if (!isValidNonNumeric) { if (this.integerOnly) { result = Math.round(result); } if (newInput === "-") { result = -result; } else if (newInput === "+") { result = Math.abs(result); } this.setInternalValue(result); } }, normaliseInput: function normaliseInput(value, locale) { var example = Intl.NumberFormat(locale).format(1.1); var cleanRegExp = new RegExp("[^-+0-9".concat(example.charAt(1), "]"), "g"); var cleanValue = value.replace(cleanRegExp, ""); var normalised = cleanValue.replace(example.charAt(1), "."); return normalised; }, isStartingSignedInput: function isStartingSignedInput(input) { return input.length === 1 && (input === "+" || input === "-"); }, isEmptyInput: function isEmptyInput(input) { return input.length === 0; }, isAddingDecimalPlaces: function isAddingDecimalPlaces(input) { return input.endsWith(".") || input.endsWith(",") || input.endsWith(" "); }, increase: function increase() { if (this.canIncrease) { if (this.internalValueIsNotDefined) { this.setToDefaultValue(); } this.setInternalValue(this.internalValue + this.internalStep); } }, decrease: function decrease() { if (this.canDecrease) { if (this.internalValueIsNotDefined) { this.setToDefaultValue(); } this.setInternalValue(this.internalValue - this.internalStep); } }, setToDefaultValue: function setToDefaultValue() { var newVal = 0; if (this.min != Number.NEGATIVE_INFINITY) { newVal = this.min; } this.setInternalValue(newVal); }, setInternalValue: function setInternalValue(val) { // Wipe out the value to force an update even if the value hasn't changed - ensures extra characters that don't affect the parsed value are removed from display. this.internalValue = null; this.internalValue = val; this.$emit('input', this.internalValue); }, keychange: function keychange(e) { this.ctrlActive = e.ctrlKey; this.shiftActive = e.shiftKey; } }, created: function created() { if (this.locale === null) { if (typeof window !== 'undefined' && window) { this.internalLocale = window.navigator.language; document.addEventListener("keydown", this.keychange); document.addEventListener("keyup", this.keychange); } } else { this.internalLocale = this.locale; } if (Vue__default['default'].config.devtools) { // Validate props that depend on each other in development mode. if (this.min > this.max) { console.error("nice-numeric-input Prop Error: Min [".concat(this.min, "] cannot be greater than Max [").concat(this.max, "]")); } if (this.$listeners && !this.$listeners.input) { console.warn("nice-numeric-input Warning: There is no input event listener attached, use v-model or bind one directly to the input event."); } } }, beforeDestroy: function beforeDestroy() { document.removeEventListener('keydown', this.keychange); document.removeEventListener('keyup', this.keychange); }, watch: { value: function value(newVal) { this.internalValue = newVal; } } });function normalizeComponent(template, style, script, scopeId, isFunctionalTemplate, moduleIdentifier /* server only */, shadowMode, createInjector, createInjectorSSR, createInjectorShadow) { if (typeof shadowMode !== 'boolean') { createInjectorSSR = createInjector; createInjector = shadowMode; shadowMode = false; } // Vue.extend constructor export interop. const options = typeof script === 'function' ? script.options : script; // render functions if (template && template.render) { options.render = template.render; options.staticRenderFns = template.staticRenderFns; options._compiled = true; // functional template if (isFunctionalTemplate) { options.functional = true; } } // scopedId if (scopeId) { options._scopeId = scopeId; } let hook; if (moduleIdentifier) { // server build hook = function (context) { // 2.3 injection context = context || // cached call (this.$vnode && this.$vnode.ssrContext) || // stateful (this.parent && this.parent.$vnode && this.parent.$vnode.ssrContext); // functional // 2.2 with runInNewContext: true if (!context && typeof __VUE_SSR_CONTEXT__ !== 'undefined') { context = __VUE_SSR_CONTEXT__; } // inject component styles if (style) { style.call(this, createInjectorSSR(context)); } // register component module identifier for async chunk inference if (context && context._registeredComponents) { context._registeredComponents.add(moduleIdentifier); } }; // used by ssr in case component is cached and beforeCreate // never gets called options._ssrRegister = hook; } else if (style) { hook = shadowMode ? function (context) { style.call(this, createInjectorShadow(context, this.$root.$options.shadowRoot)); } : function (context) { style.call(this, createInjector(context)); }; } if (hook) { if (options.functional) { // register for functional component in vue file const originalRender = options.render; options.render = function renderWithStyleInjection(h, context) { hook.call(context); return originalRender(h, context); }; } else { // inject component registration as beforeCreate hook const existing = options.beforeCreate; options.beforeCreate = existing ? [].concat(existing, hook) : [hook]; } } return script; }function createInjectorSSR(context) { if (!context && typeof __VUE_SSR_CONTEXT__ !== 'undefined') { context = __VUE_SSR_CONTEXT__; } if (!context) return () => { }; if (!('styles' in context)) { context._styles = context._styles || {}; Object.defineProperty(context, 'styles', { enumerable: true, get: () => context._renderStyles(context._styles) }); context._renderStyles = context._renderStyles || renderStyles; } return (id, style) => addStyle(id, style, context); } function addStyle(id, css, context) { const group = css.media || 'default' ; const style = context._styles[group] || (context._styles[group] = { ids: [], css: '' }); if (!style.ids.includes(id)) { style.media = css.media; style.ids.push(id); let code = css.source; style.css += code + '\n'; } } function renderStyles(styles) { let css = ''; for (const key in styles) { const style = styles[key]; css += '<style data-vue-ssr-id="' + Array.from(style.ids).join(' ') + '"' + (style.media ? ' media="' + style.media + '"' : '') + '>' + style.css + '</style>'; } return css; }/* script */ var __vue_script__ = script; /* template */ var __vue_render__ = function __vue_render__() { var _vm = this; var _h = _vm.$createElement; var _c = _vm._self._c || _h; return _c('div', { staticClass: "input-wrapper", class: [_vm.noControls ? '' : 'controls', _vm.isError ? 'error' : '', _vm.wrapperClass] }, [_vm._ssrNode((!_vm.hideLabel ? "<label" + _vm._ssrAttr("id", _vm.labelId) + _vm._ssrAttr("for", _vm.id) + _vm._ssrClass("input-label", _vm.labelClass) + " data-v-89fe9580>" + _vm._ssrEscape("\n " + _vm._s(_vm.label) + "\n ") + "</label>" : "<!---->") + " " + (!_vm.noControls ? "<button" + _vm._ssrAttr("disabled", _vm.disabled || !_vm.canDecrease) + _vm._ssrAttr("title", _vm.decreaseTitle) + _vm._ssrClass("left-control", [_vm.changeButtonClass, _vm.decreaseButtonClass]) + " data-v-89fe9580>" + _vm._ssrEscape("\n " + _vm._s(_vm.internalDecreaseText) + "\n ") + "</button>" : "<!---->") + " <input" + _vm._ssrAttr("id", _vm.id) + _vm._ssrAttr("name", _vm.name) + _vm._ssrAttr("disabled", _vm.disabled) + " type=\"text\"" + _vm._ssrAttr("placeholder", _vm.placeholder) + _vm._ssrAttr("aria-labelledby", !_vm.hideLabel ? _vm.labelId : false) + _vm._ssrAttr("aria-label", _vm.hideLabel ? _vm.label : false) + _vm._ssrAttr("value", _vm.displayString) + _vm._ssrClass(null, [_vm.noControls ? 'no-controls-input' : 'double-controls-input', _vm.inputClass]) + " data-v-89fe9580> " + (!_vm.noControls ? "<button" + _vm._ssrAttr("disabled", _vm.disabled || !_vm.canIncrease) + _vm._ssrAttr("title", _vm.increaseTitle) + _vm._ssrClass("right-control", [_vm.changeButtonClass, _vm.increaseButtonClass]) + " data-v-89fe9580>" + _vm._ssrEscape("\n " + _vm._s(_vm.internalIncreaseText) + "\n ") + "</button>" : "<!---->"))]); }; var __vue_staticRenderFns__ = []; /* style */ var __vue_inject_styles__ = function __vue_inject_styles__(inject) { if (!inject) return; inject("data-v-89fe9580_0", { source: ".input-wrapper[data-v-89fe9580]{position:relative;font-weight:400;font-style:normal;display:-webkit-box;display:-ms-flexbox;display:flex;color:rgba(0,0,0,.9)}.input-wrapper>input[data-v-89fe9580]{width:100%;margin:0;max-width:100%;-webkit-box-flex:1;-ms-flex:1 0 auto;flex:1 0 auto;outline:0;-webkit-tap-highlight-color:transparent;text-align:left;line-height:1.2em;font-family:\"Helvetica Neue\",Arial,Helvetica,sans-serif;padding:.66em 1em;background:#fff;border:1px solid rgba(34,36,38,.2);color:rgba(0,0,0,.9);border-radius:.3rem;-webkit-transition:border-color .1s ease,-webkit-box-shadow .1s ease;transition:border-color .1s ease,-webkit-box-shadow .1s ease;transition:box-shadow .1s ease,border-color .1s ease;transition:box-shadow .1s ease,border-color .1s ease,-webkit-box-shadow .1s ease;-webkit-box-shadow:none;box-shadow:none}.input-wrapper input[disabled][data-v-89fe9580],.input-wrapper.disabled[data-v-89fe9580]{opacity:.4}.input-wrapper.disabled>input[data-v-89fe9580]{pointer-events:none}.input-wrapper>input[data-v-89fe9580]:active{border-color:rgba(0,0,0,.4);background:#fafafa}.input-wrapper>input[data-v-89fe9580]:focus{border-color:#85b7d9;background:#fff;color:rgba(0,0,0,.8)}.input-wrapper.error>input[data-v-89fe9580]{background-color:#ffd7d7;border-color:#dba8a8;color:#9b2d2b}.input-wrapper>input[data-v-89fe9580]::-webkit-input-placeholder{color:rgba(191,191,191,.87)}.input-wrapper>input[data-v-89fe9580]::-moz-placeholder{color:rgba(191,191,191,.87)}.input-wrapper>input[data-v-89fe9580]:-ms-input-placeholder{color:rgba(191,191,191,.87)}.input-wrapper.error>input[data-v-89fe9580]::-webkit-input-placeholder{color:#e7bdbc}.input-wrapper.error>input[data-v-89fe9580]::-moz-placeholder{color:#e7bdbc}.input-wrapper.error>input[data-v-89fe9580]::-ms-input-placeholder{color:#e7bdbc!important}.input-wrapper.error>input[data-v-89fe9580]:focus::-webkit-input-placeholder{color:#da9796}.input-wrapper.error>input[data-v-89fe9580]:focus::-moz-placeholder{color:#da9796}.input-wrapper.error>input[data-v-89fe9580]:focus::-ms-input-placeholder{color:#da9796!important}.input-label[data-v-89fe9580]{-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;margin:0;font-size:1em;padding-right:1em;font-weight:700;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}button[data-v-89fe9580]{cursor:pointer;display:inline-block;min-height:1em;outline:0;border:none;vertical-align:baseline;background:#e0e1e2 none;color:rgba(0,0,0,.7);font-family:\"Helvetica Neue\",Arial,Helvetica,sans-serif;margin:0 .25em 0 0;padding:.75em 1.5em .75em}button.smaller-padding[data-v-89fe9580]{padding:.75em 1.25em .75em}button.much-smaller-padding[data-v-89fe9580]{padding:.75em 1em .75em}button[data-v-89fe9580]:hover{background-color:#cacbcd;-webkit-box-shadow:0 0 0 1px transparent inset,0 0 0 0 rgba(34,36,38,.15) inset;box-shadow:0 0 0 1px transparent inset,0 0 0 0 rgba(34,36,38,.15) inset;color:rgba(0,0,0,.8)}button[data-v-89fe9580]:focus{background-color:#cacbcd;color:rgba(0,0,0,.8);background-image:\"\"!important;-webkit-box-shadow:\"\"!important;box-shadow:\"\"!important}button[data-v-89fe9580]:active{background-color:#babbbc;background-image:\"\";color:rgba(0,0,0,.9);-webkit-box-shadow:0 0 0 1px transparent inset,none;box-shadow:0 0 0 1px transparent inset,none}button[data-v-89fe9580]:disabled{cursor:default;opacity:.4!important;-webkit-box-shadow:none!important;box-shadow:none!important;pointer-events:none!important}.input-wrapper.controls>button[data-v-89fe9580]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;-webkit-box-flex:0;-ms-flex:0 0 auto;flex:0 0 auto;margin:0;text-transform:none;text-shadow:none;font-weight:700;line-height:1em;font-style:normal;text-align:center;text-decoration:none;-webkit-box-shadow:0 0 0 1px transparent inset,0 0 0 0 rgba(34,36,38,.15) inset;box-shadow:0 0 0 1px transparent inset,0 0 0 0 rgba(34,36,38,.15) inset;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-transition:opacity .1s ease,background-color .1s ease,color .1s ease,background .1s ease,-webkit-box-shadow .1s ease;transition:opacity .1s ease,background-color .1s ease,color .1s ease,background .1s ease,-webkit-box-shadow .1s ease;transition:opacity .1s ease,background-color .1s ease,color .1s ease,box-shadow .1s ease,background .1s ease;transition:opacity .1s ease,background-color .1s ease,color .1s ease,box-shadow .1s ease,background .1s ease,-webkit-box-shadow .1s ease;-webkit-tap-highlight-color:transparent}button.left-control[data-v-89fe9580]{border-radius:3px 0 0 3px}button.right-control[data-v-89fe9580]{border-radius:0 3px 3px 0}.double-controls-input[data-v-89fe9580]{border-radius:0!important}.no-controls-input[data-v-89fe9580]{border-radius:3px!important;border-left-color:rgba(34,36,38,.15)!important}", map: undefined, media: undefined }); }; /* scoped */ var __vue_scope_id__ = "data-v-89fe9580"; /* module identifier */ var __vue_module_identifier__ = "data-v-89fe9580"; /* functional template */ var __vue_is_functional_template__ = false; /* style inject shadow dom */ var __vue_component__ = /*#__PURE__*/normalizeComponent({ render: __vue_render__, staticRenderFns: __vue_staticRenderFns__ }, __vue_inject_styles__, __vue_script__, __vue_scope_id__, __vue_is_functional_template__, __vue_module_identifier__, false, undefined, createInjectorSSR, undefined); var component$1 = __vue_component__;// Import vue component // Default export is installable instance of component. // IIFE injects install function into component, allowing component // to be registered via Vue.use() as well as Vue.component(), var component = /*#__PURE__*/(function () { // Assign InstallableComponent type var installable = component$1; // Attach install function executed by Vue.use() installable.install = function (Vue) { Vue.component('NiceNumericInput', installable); }; return installable; })(); // It's possible to expose named exports when writing components that can // also be used as directives, etc. - eg. import { RollupDemoDirective } from 'rollup-demo'; // export const RollupDemoDirective = directive; var namedExports=/*#__PURE__*/Object.freeze({__proto__:null,'default': component});// only expose one global var, with named exports exposed as properties of // that global var (eg. plugin.namedExport) Object.entries(namedExports).forEach(function (_ref) { var _ref2 = _slicedToArray(_ref, 2), exportName = _ref2[0], exported = _ref2[1]; if (exportName !== 'default') component[exportName] = exported; });module.exports=component;