imask
Version:
vanilla javascript input mask
121 lines (114 loc) • 4.1 kB
JavaScript
import ChangeDetails from '../core/change-details.js';
import IMask from '../core/holder.js';
import MaskedPattern from './pattern.js';
import '../core/utils.js';
import './base.js';
import '../core/continuous-tail-details.js';
import './factory.js';
import './pattern/chunk-tail-details.js';
import './pattern/cursor.js';
import './pattern/fixed-definition.js';
import './pattern/input-definition.js';
import './regexp.js';
/** Pattern which accepts ranges */
class MaskedRange extends MaskedPattern {
/**
Optionally sets max length of pattern.
Used when pattern length is longer then `to` param length. Pads zeros at start in this case.
*/
/** Min bound */
/** Max bound */
get _matchFrom() {
return this.maxLength - String(this.from).length;
}
constructor(opts) {
super(opts); // mask will be created in _update
}
updateOptions(opts) {
super.updateOptions(opts);
}
_update(opts) {
const {
to = this.to || 0,
from = this.from || 0,
maxLength = this.maxLength || 0,
autofix = this.autofix,
...patternOpts
} = opts;
this.to = to;
this.from = from;
this.maxLength = Math.max(String(to).length, maxLength);
this.autofix = autofix;
const fromStr = String(this.from).padStart(this.maxLength, '0');
const toStr = String(this.to).padStart(this.maxLength, '0');
let sameCharsCount = 0;
while (sameCharsCount < toStr.length && toStr[sameCharsCount] === fromStr[sameCharsCount]) ++sameCharsCount;
patternOpts.mask = toStr.slice(0, sameCharsCount).replace(/0/g, '\\0') + '0'.repeat(this.maxLength - sameCharsCount);
super._update(patternOpts);
}
get isComplete() {
return super.isComplete && Boolean(this.value);
}
boundaries(str) {
let minstr = '';
let maxstr = '';
const [, placeholder, num] = str.match(/^(\D*)(\d*)(\D*)/) || [];
if (num) {
minstr = '0'.repeat(placeholder.length) + num;
maxstr = '9'.repeat(placeholder.length) + num;
}
minstr = minstr.padEnd(this.maxLength, '0');
maxstr = maxstr.padEnd(this.maxLength, '9');
return [minstr, maxstr];
}
doPrepareChar(ch, flags) {
if (flags === void 0) {
flags = {};
}
let details;
[ch, details] = super.doPrepareChar(ch.replace(/\D/g, ''), flags);
if (!ch) details.skip = !this.isComplete;
return [ch, details];
}
_appendCharRaw(ch, flags) {
if (flags === void 0) {
flags = {};
}
if (!this.autofix || this.value.length + 1 > this.maxLength) return super._appendCharRaw(ch, flags);
const fromStr = String(this.from).padStart(this.maxLength, '0');
const toStr = String(this.to).padStart(this.maxLength, '0');
const [minstr, maxstr] = this.boundaries(this.value + ch);
if (Number(maxstr) < this.from) return super._appendCharRaw(fromStr[this.value.length], flags);
if (Number(minstr) > this.to) {
if (!flags.tail && this.autofix === 'pad' && this.value.length + 1 < this.maxLength) {
return super._appendCharRaw(fromStr[this.value.length], flags).aggregate(this._appendCharRaw(ch, flags));
}
return super._appendCharRaw(toStr[this.value.length], flags);
}
return super._appendCharRaw(ch, flags);
}
doValidate(flags) {
const str = this.value;
const firstNonZero = str.search(/[^0]/);
if (firstNonZero === -1 && str.length <= this._matchFrom) return true;
const [minstr, maxstr] = this.boundaries(str);
return this.from <= Number(maxstr) && Number(minstr) <= this.to && super.doValidate(flags);
}
pad(flags) {
const details = new ChangeDetails();
if (this.value.length === this.maxLength) return details;
const value = this.value;
const padLength = this.maxLength - this.value.length;
if (padLength) {
this.reset();
for (let i = 0; i < padLength; ++i) {
details.aggregate(super._appendCharRaw('0', flags));
}
// append tail
value.split('').forEach(ch => this._appendCharRaw(ch));
}
return details;
}
}
IMask.MaskedRange = MaskedRange;
export { MaskedRange as default };