UNPKG

imask

Version:

vanilla javascript input mask

342 lines (333 loc) 11.2 kB
import { DIRECTION, objectIncludes } from '../core/utils.js'; import ChangeDetails from '../core/change-details.js'; import createMask, { normalizeOpts } from './factory.js'; import Masked from './base.js'; import IMask from '../core/holder.js'; import '../core/continuous-tail-details.js'; /** Dynamic mask for choosing appropriate mask in run-time */ class MaskedDynamic extends Masked { constructor(opts) { super({ ...MaskedDynamic.DEFAULTS, ...opts }); this.currentMask = undefined; } updateOptions(opts) { super.updateOptions(opts); } _update(opts) { super._update(opts); if ('mask' in opts) { this.exposeMask = undefined; // mask could be totally dynamic with only `dispatch` option this.compiledMasks = Array.isArray(opts.mask) ? opts.mask.map(m => { const { expose, ...maskOpts } = normalizeOpts(m); const masked = createMask({ overwrite: this._overwrite, eager: this._eager, skipInvalid: this._skipInvalid, ...maskOpts }); if (expose) this.exposeMask = masked; return masked; }) : []; // this.currentMask = this.doDispatch(''); // probably not needed but lets see } } _appendCharRaw(ch, flags) { if (flags === void 0) { flags = {}; } const details = this._applyDispatch(ch, flags); if (this.currentMask) { details.aggregate(this.currentMask._appendChar(ch, this.currentMaskFlags(flags))); } return details; } _applyDispatch(appended, flags, tail) { if (appended === void 0) { appended = ''; } if (flags === void 0) { flags = {}; } if (tail === void 0) { tail = ''; } const prevValueBeforeTail = flags.tail && flags._beforeTailState != null ? flags._beforeTailState._value : this.value; const inputValue = this.rawInputValue; const insertValue = flags.tail && flags._beforeTailState != null ? flags._beforeTailState._rawInputValue : inputValue; const tailValue = inputValue.slice(insertValue.length); const prevMask = this.currentMask; const details = new ChangeDetails(); const prevMaskState = prevMask == null ? void 0 : prevMask.state; // clone flags to prevent overwriting `_beforeTailState` this.currentMask = this.doDispatch(appended, { ...flags }, tail); // restore state after dispatch if (this.currentMask) { if (this.currentMask !== prevMask) { // if mask changed reapply input this.currentMask.reset(); if (insertValue) { this.currentMask.append(insertValue, { raw: true }); details.tailShift = this.currentMask.value.length - prevValueBeforeTail.length; } if (tailValue) { details.tailShift += this.currentMask.append(tailValue, { raw: true, tail: true }).tailShift; } } else if (prevMaskState) { // Dispatch can do something bad with state, so // restore prev mask state this.currentMask.state = prevMaskState; } } return details; } _appendPlaceholder() { const details = this._applyDispatch(); if (this.currentMask) { details.aggregate(this.currentMask._appendPlaceholder()); } return details; } _appendEager() { const details = this._applyDispatch(); if (this.currentMask) { details.aggregate(this.currentMask._appendEager()); } return details; } appendTail(tail) { const details = new ChangeDetails(); if (tail) details.aggregate(this._applyDispatch('', {}, tail)); return details.aggregate(this.currentMask ? this.currentMask.appendTail(tail) : super.appendTail(tail)); } currentMaskFlags(flags) { var _flags$_beforeTailSta, _flags$_beforeTailSta2; return { ...flags, _beforeTailState: ((_flags$_beforeTailSta = flags._beforeTailState) == null ? void 0 : _flags$_beforeTailSta.currentMaskRef) === this.currentMask && ((_flags$_beforeTailSta2 = flags._beforeTailState) == null ? void 0 : _flags$_beforeTailSta2.currentMask) || flags._beforeTailState }; } doDispatch(appended, flags, tail) { if (flags === void 0) { flags = {}; } if (tail === void 0) { tail = ''; } return this.dispatch(appended, this, flags, tail); } doValidate(flags) { return super.doValidate(flags) && (!this.currentMask || this.currentMask.doValidate(this.currentMaskFlags(flags))); } doPrepare(str, flags) { if (flags === void 0) { flags = {}; } let [s, details] = super.doPrepare(str, flags); if (this.currentMask) { let currentDetails; [s, currentDetails] = super.doPrepare(s, this.currentMaskFlags(flags)); details = details.aggregate(currentDetails); } return [s, details]; } doPrepareChar(str, flags) { if (flags === void 0) { flags = {}; } let [s, details] = super.doPrepareChar(str, flags); if (this.currentMask) { let currentDetails; [s, currentDetails] = super.doPrepareChar(s, this.currentMaskFlags(flags)); details = details.aggregate(currentDetails); } return [s, details]; } reset() { var _this$currentMask; (_this$currentMask = this.currentMask) == null || _this$currentMask.reset(); this.compiledMasks.forEach(m => m.reset()); } get value() { return this.exposeMask ? this.exposeMask.value : this.currentMask ? this.currentMask.value : ''; } set value(value) { if (this.exposeMask) { this.exposeMask.value = value; this.currentMask = this.exposeMask; this._applyDispatch(); } else super.value = value; } get unmaskedValue() { return this.exposeMask ? this.exposeMask.unmaskedValue : this.currentMask ? this.currentMask.unmaskedValue : ''; } set unmaskedValue(unmaskedValue) { if (this.exposeMask) { this.exposeMask.unmaskedValue = unmaskedValue; this.currentMask = this.exposeMask; this._applyDispatch(); } else super.unmaskedValue = unmaskedValue; } get typedValue() { return this.exposeMask ? this.exposeMask.typedValue : this.currentMask ? this.currentMask.typedValue : ''; } set typedValue(typedValue) { if (this.exposeMask) { this.exposeMask.typedValue = typedValue; this.currentMask = this.exposeMask; this._applyDispatch(); return; } let unmaskedValue = String(typedValue); // double check it if (this.currentMask) { this.currentMask.typedValue = typedValue; unmaskedValue = this.currentMask.unmaskedValue; } this.unmaskedValue = unmaskedValue; } get displayValue() { return this.currentMask ? this.currentMask.displayValue : ''; } get isComplete() { var _this$currentMask2; return Boolean((_this$currentMask2 = this.currentMask) == null ? void 0 : _this$currentMask2.isComplete); } get isFilled() { var _this$currentMask3; return Boolean((_this$currentMask3 = this.currentMask) == null ? void 0 : _this$currentMask3.isFilled); } remove(fromPos, toPos) { const details = new ChangeDetails(); if (this.currentMask) { details.aggregate(this.currentMask.remove(fromPos, toPos)) // update with dispatch .aggregate(this._applyDispatch()); } return details; } get state() { var _this$currentMask4; return { ...super.state, _rawInputValue: this.rawInputValue, compiledMasks: this.compiledMasks.map(m => m.state), currentMaskRef: this.currentMask, currentMask: (_this$currentMask4 = this.currentMask) == null ? void 0 : _this$currentMask4.state }; } set state(state) { const { compiledMasks, currentMaskRef, currentMask, ...maskedState } = state; if (compiledMasks) this.compiledMasks.forEach((m, mi) => m.state = compiledMasks[mi]); if (currentMaskRef != null) { this.currentMask = currentMaskRef; this.currentMask.state = currentMask; } super.state = maskedState; } extractInput(fromPos, toPos, flags) { return this.currentMask ? this.currentMask.extractInput(fromPos, toPos, flags) : ''; } extractTail(fromPos, toPos) { return this.currentMask ? this.currentMask.extractTail(fromPos, toPos) : super.extractTail(fromPos, toPos); } doCommit() { if (this.currentMask) this.currentMask.doCommit(); super.doCommit(); } nearestInputPos(cursorPos, direction) { return this.currentMask ? this.currentMask.nearestInputPos(cursorPos, direction) : super.nearestInputPos(cursorPos, direction); } get overwrite() { return this.currentMask ? this.currentMask.overwrite : this._overwrite; } set overwrite(overwrite) { this._overwrite = overwrite; } get eager() { return this.currentMask ? this.currentMask.eager : this._eager; } set eager(eager) { this._eager = eager; } get skipInvalid() { return this.currentMask ? this.currentMask.skipInvalid : this._skipInvalid; } set skipInvalid(skipInvalid) { this._skipInvalid = skipInvalid; } get autofix() { return this.currentMask ? this.currentMask.autofix : this._autofix; } set autofix(autofix) { this._autofix = autofix; } maskEquals(mask) { return Array.isArray(mask) ? this.compiledMasks.every((m, mi) => { if (!mask[mi]) return; const { mask: oldMask, ...restOpts } = mask[mi]; return objectIncludes(m, restOpts) && m.maskEquals(oldMask); }) : super.maskEquals(mask); } typedValueEquals(value) { var _this$currentMask5; return Boolean((_this$currentMask5 = this.currentMask) == null ? void 0 : _this$currentMask5.typedValueEquals(value)); } } /** Currently chosen mask */ /** Currently chosen mask */ /** Compliled {@link Masked} options */ /** Chooses {@link Masked} depending on input value */ MaskedDynamic.DEFAULTS = { ...Masked.DEFAULTS, dispatch: (appended, masked, flags, tail) => { if (!masked.compiledMasks.length) return; const inputValue = masked.rawInputValue; // simulate input const inputs = masked.compiledMasks.map((m, index) => { const isCurrent = masked.currentMask === m; const startInputPos = isCurrent ? m.displayValue.length : m.nearestInputPos(m.displayValue.length, DIRECTION.FORCE_LEFT); if (m.rawInputValue !== inputValue) { m.reset(); m.append(inputValue, { raw: true }); } else if (!isCurrent) { m.remove(startInputPos); } m.append(appended, masked.currentMaskFlags(flags)); m.appendTail(tail); return { index, weight: m.rawInputValue.length, totalInputPositions: m.totalInputPositions(0, Math.max(startInputPos, m.nearestInputPos(m.displayValue.length, DIRECTION.FORCE_LEFT))) }; }); // pop masks with longer values first inputs.sort((i1, i2) => i2.weight - i1.weight || i2.totalInputPositions - i1.totalInputPositions); return masked.compiledMasks[inputs[0].index]; } }; IMask.MaskedDynamic = MaskedDynamic; export { MaskedDynamic as default };