UNPKG

@coders-tm/vue-number-format

Version:

Easy formatted numbers, currency and percentage with input/directive mask for Vue.js

7 lines (6 loc) 8.76 kB
/** * Vue Number Format 3.34.1 * (c) 2021-2025 Dipak Sarkar <hello@dipaksarkar.in> (https://dipaksarkar.in/) * @license MIT */ import{defineComponent as e,ref as t,computed as i,watch as n,resolveDirective as r,withDirectives as u,openBlock as s,createElementBlock as l}from"vue";var a={prefix:"",suffix:"",separator:",",decimal:".",inputmode:"numeric",precision:2,minimumFractionDigits:null,prefill:!0,reverseFill:!1,min:null,max:null,nullValue:""};function o(e){return t=0,i=e,n=20,Math.max(t,Math.min(i,n));var t,i,n}class p{options;input;number;isClean;isCustomDecimal;noPreSuffix;hasPreOrSuffix;prefix;preSufRegExp;prefixRegExp;suffixRegExp;numberRegExp;cleanRegExp;negativeRegExp;constructor(e){this.options=Object.assign(c(a),e);const{prefix:t,suffix:i,decimal:n,reverseFill:r}=this.options;this.input="",this.number="",this.isClean=!r;const u=t,s=i,l=u.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&"),o=s.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&");l&&(this.prefixRegExp=new RegExp(`^${l}`)),o&&(this.suffixRegExp=new RegExp(`${o}$`)),this.prefix=t,this.numberRegExp=new RegExp(`[^0-9\\${n||"."}]+`,"gi"),this.cleanRegExp=new RegExp("[^0-9]+","gi"),this.negativeRegExp=new RegExp("[^0-9\\-]+","gi"),this.isCustomDecimal="."!==n,this.noPreSuffix=!u&&!s,this.hasPreOrSuffix=!this.noPreSuffix}isNull(){return!this.numberOnly(this.isClean?this.cleanRegExp:this.negativeRegExp)}clean(e=!1){return this.isClean=e,this}sign(){if(null===this.input||void 0===this.input)return"";const e=this.input.toString().indexOf(m)>=0;return this.isClean?e&&this.realNumber()>0?m:"":e?m:""}toFixed(){const e=Math.pow(10,this.options.precision);return(parseFloat(this.numberOnly(/\D+/g))/e||0).toFixed(o(this.options.precision))}toNumber(e){return Number(e)}numberOnly(e){let t=this.input?.toString();return this.prefixRegExp&&(t=t.replace(this.prefixRegExp,"")),this.suffixRegExp&&(t=t.replace(this.suffixRegExp,"")),t.replace(e||this.numberRegExp,"")}inputWithPreOrSuffix(){return this.input&&this.prefixRegExp?this.prefixRegExp.test(this.input.toString()):!this.input||!this.suffixRegExp||this.suffixRegExp.test(this.input.toString())}isNegative(){return this.sign()===m}isNumber(e){return!isNaN(this.toNumber(e||this.input))}numbers(){const{reverseFill:e,decimal:t,separator:i}=this.options;if(e)this.number=this.toFixed().replace(".",t);else{const e=this.input?.toString().replace(this.prefixRegExp??"","").replace(this.suffixRegExp??"","").replace(new RegExp(m,"g"),""),n=this.input.toString().indexOf(t)>=0&&this.isCustomDecimal;let r=e;i&&(r=r.replace(new RegExp(`\\${i}`,"g"),"")),r=r.replace(t,"."),"number"==typeof this.input||this.isNumber()&&!n&&!this.inputWithPreOrSuffix()&&this.hasPreOrSuffix?this.number=this.parts(e,".").join(t):this.isNumber(r)&&!n&&this.inputWithPreOrSuffix()&&this.hasPreOrSuffix?this.number=this.parts(r,".").join(t):this.number=this.parts(this.numberOnly()).join(t)}return this.number}unformatNumber(){return this.numbers().toString().replace(this.options.decimal,".")}realNumber(){return parseFloat(this.unformatNumber())}parts(e,t){const{precision:i,minimumFractionDigits:n,decimal:r}=this.options;let u=e.toString().split(t||r);if(u.length>1&&(u[0]=this.toNumber(u[0])||0,u[1]=u.slice(1,u.length).join(""),u=u.slice(0,2)),this.isClean){const e=this.toNumber(u.join(".")).toFixed(i),t=this.toNumber(e),r=t.toFixed(n);u=n>=0&&t.toString().length<r.length?r.toString().split("."):t.toString().split(".")}return u.slice(0,2)}addSeparator(){const{decimal:e,separator:t}=this.options,i=this.numbers().split(e);return i[0]=i[0].toString().replace(/(\d)(?=(?:\d{3})+\b)/gm,`$1${t}`),i.join(e)}format(e){this.input=e;const{reverseFill:t,nullValue:i,prefix:n,suffix:r}=this.options;return this.isNull()&&!t?i:this.sign()+n+this.addSeparator()+r}unformat(e){this.input=e;const{reverseFill:t,nullValue:i}=this.options,n=this.realNumber(),r=this.unformatNumber();return this.isNull()||t&&0===n?i:this.sign()+r}}const m="-";function c(e){return JSON.parse(JSON.stringify(e))}function f(e){return new CustomEvent(e,{bubbles:!0,cancelable:!0,detail:{facade:!0}})}function h(e){const t=e instanceof HTMLInputElement?e:e.querySelector("input");if(!t)throw new Error("number directive requires an input element");return t}function d(e,t){const i=()=>{e.setSelectionRange(t,t)};i(),setTimeout(i,1)}function g(e,t,{emit:i=!0,force:n=!1,clean:r=!1}={}){const{options:u,oldValue:s}=e,{reverseFill:l,max:a,min:o}=u,m=t?.props?.value||e.value;if(n||s!==m){const t=new p(u).clean(r&&!l);let i=t.format(m),n=t.clean(!l).unformat(m);r&&(Number(a)===a&&Number(n)>a?(i=t.format(a),n=a.toString()):Number(o)===o&&Number(n)<o&&(i=t.format(o),n=o.toString())),e.oldValue=i,e.unmasked=n,e.value!==i&&(e.value=i)}return i&&e.dispatchEvent(f("input"))}var v={beforeMount:(e,{value:t,modifiers:i},n)=>{e=h(e);const r=Object.assign(c(a),t,i),{reverseFill:u,precision:s,decimal:l,inputmode:o}=r;e.options=r,e.setAttribute("inputmode",o),u&&e.value?(e.value=parseFloat(new p({...r,reverseFill:!1}).unformat(e.value)).toFixed(s),n?.props?.value&&(n.props.value=e.value)):e.value&&!isNaN(Number(e.value))&&(e.value=e.value.replace(".",l)),g(e,n,{force:r.prefill,clean:!0,emit:!1})},mounted:e=>{const t=(e=h(e)).parentElement||e,i=t=>{t.target===e&&function(e){const{target:t,detail:i}=e;if(i?.facade)return!1;e.stopPropagation();let n=t.value.length;const{oldValue:r,options:u}=t;t.selectionEnd&&(n=t.value.length-t.selectionEnd),g(t,null,{clean:!u.precision,emit:!1}),u.suffix&&(n=Math.max(n,u.suffix.length)),n=t.value.length-n,u.prefix&&(n=Math.max(n,u.prefix.length)),d(t,n),r!==t.value&&t.dispatchEvent(f("input"))}(t)},n=t=>{t.target===e&&function(e){const{target:t}=e,{oldValue:i}=t;g(t,null,{force:!0,clean:!0,emit:!1}),i!==t.value&&t.dispatchEvent(f("input"))}(t)},r=t=>{t.target===e&&function(e,t){const{options:i}=t,{prefix:n,suffix:r,decimal:u,min:s,separator:l}=i,{key:a}=e,o=new RegExp(`${n}|${r}`,"g"),p=t.value.replace(o,""),c=void 0===s||Number(s)<0||Number(s)!==s;if(a===u)p.includes(u)?e.preventDefault():p||(t.value="0",t.dispatchEvent(new Event("input")));else if(a!==m||c){if("Backspace"===a){const i=t.selectionEnd||0,u=t.value.slice(i-1,i),s=t.value.slice(i-2,i);let a=t.value.length-i;[n,m,l].includes(u)&&(e.preventDefault(),t.value=u===l?t.value.replace(s,""):t.value.replace(new RegExp(`[${n}${m}]`,"g"),""),a=Math.max(a,r.length),a=t.value.length-a,a=Math.max(a,n.length),d(t,a),t.dispatchEvent(new Event("input")))}}else e.preventDefault()}(t,e)};t.addEventListener("input",i,!0),t.addEventListener("blur",n,!0),t.addEventListener("keydown",r,!0),e.cleanup=()=>{t.removeEventListener("input",i,!0),t.removeEventListener("blur",n,!0),t.removeEventListener("keydown",r,!0)}},updated:(e,{value:t,oldValue:i,modifiers:n},r)=>{if(e=h(e),t!==i){const i=e.options;e.options=Object.assign(i,t,n),g(e,r,{force:!0,clean:!1,emit:!1})}else g(e,r,{emit:!1})},unmounted:e=>{h(e)?.cleanup()}};const x=c(a);var b=e({name:"VueNumber",directives:{number:v},props:{modelValue:{type:[String,Number],required:!0},nullValue:{type:[Number,String],default:x.nullValue},masked:Boolean,readonly:Boolean,disabled:Boolean,reverseFill:{type:Boolean,default:x.reverseFill},prefill:{type:Boolean,default:x.prefill},precision:{type:Number,default:()=>x.precision},minimumFractionDigits:{type:Number,default:()=>x.minimumFractionDigits},decimal:{type:String,default:()=>x.decimal},min:{type:Number,default:()=>x.min},max:{type:Number,default:()=>x.max},separator:{type:String,default:()=>x.separator},prefix:{type:String,default:()=>x.prefix},suffix:{type:String,default:()=>x.suffix},inputmode:{type:String,default:()=>x.inputmode}},emits:["update:model-value","input:model-value"],setup(e,{emit:r}){const u=t(e.modelValue),s=t(!1),l=t(""),a=i((()=>({...e}))),o=new p(a.value),m=i((()=>e.masked?o.format(u.value):l.value)),c=()=>{r("update:model-value",m.value)};return n((()=>e.modelValue),(e=>{m.value!==e&&(u.value=o.format(e))})),{config:a,maskedValue:u,unmaskedValue:l,input:e=>{const{target:t}=e;u.value=t.value,l.value=t.unmasked,s.value=!0,r("input:model-value",m.value)},blur:()=>{s.value&&m.value!==e.modelValue&&c()},change:c}}});const E=["value","readonly","disabled"];b.render=function(e,t,i,n,a,o){const p=r("number");return u((s(),l("input",{type:"text",autocomplete:"off",value:e.maskedValue,readonly:e.readonly,disabled:e.disabled,class:"v-number vue-number-format",onChange:t[0]||(t[0]=(...t)=>e.change&&e.change(...t)),onInput:t[1]||(t[1]=(...t)=>e.input&&e.input(...t)),onBlur:t[2]||(t[2]=(...t)=>e.blur&&e.blur(...t))},null,40,E)),[[p,e.config]])},b.__file="src/component.vue";const N={install(e,t){t&&Object.assign(a,t),e.directive("number",v),e.component("VueNumber",b)}};export{p as NumberFormat,b as component,N as default,v as directive,a as options};