react-native-advanced-input-mask
Version:
Text input mask for React Native on iOS, Android and web. Synchronous and easy formatting without hustle
221 lines • 7.26 kB
JavaScript
import CaretString from "../model/CaretString";
import EOLState from "../model/state/EOLState";
import FixedState from "../model/state/FixedState";
import FreeState from "../model/state/FreeState";
import OptionalValueState from "../model/state/OptionalValueState";
import ValueState from "../model/state/ValueState";
import { StateName } from "../model/types";
import AutocompletionStack from "./AutocompletionStack";
import CaretStringIterator from "./CaretStringIterator";
import Compiler from "./Compiler";
export class Mask {
static cache = new Map();
constructor(format, customNotations = []) {
this.initialState = new Compiler(customNotations).compile(format);
}
static getOrCreate(format, customNotations) {
const cachedMask = Mask.cache.get(format);
if (!cachedMask) {
const newMask = new Mask(format, customNotations);
Mask.cache.set(format, newMask);
return newMask;
}
return cachedMask;
}
static isValid(format, customNotations) {
try {
this.getOrCreate(format, customNotations);
return true;
} catch (e) {
return false;
}
}
apply(text) {
const iterator = this.makeIterator(text);
let affinity = 0;
let extractedValue = "";
let modifiedString = "";
let modifiedCaretPosition = text.caretPosition;
let state = this.initialState;
const autocompletionStack = new AutocompletionStack();
let insertionAffectsCaret = iterator.insertionAffectsCaret();
let deletionAffectsCaret = iterator.deletionAffectsCaret();
let character = iterator.next();
while (character !== null) {
const next = state.accept(character);
if (next) {
if (deletionAffectsCaret) {
autocompletionStack.push(state.autocomplete());
}
state = next.state;
if (next.insert) {
modifiedString += next.insert;
}
if (next.value) {
extractedValue += next.value;
}
if (next.pass) {
insertionAffectsCaret = iterator.insertionAffectsCaret();
deletionAffectsCaret = iterator.deletionAffectsCaret();
character = iterator.next();
affinity += 1;
} else {
if (insertionAffectsCaret && next.insert) {
modifiedCaretPosition += 1;
}
affinity -= 1;
}
} else {
if (deletionAffectsCaret) {
modifiedCaretPosition -= 1;
}
insertionAffectsCaret = iterator.insertionAffectsCaret();
deletionAffectsCaret = iterator.deletionAffectsCaret();
character = iterator.next();
affinity -= 1;
}
}
while (text.caretGravity.autocomplete && insertionAffectsCaret) {
const next = state.autocomplete();
if (!next) {
break;
}
state = next.state;
if (next.insert) {
modifiedString += next.insert;
modifiedCaretPosition += 1;
}
if (next.value) {
extractedValue += next.value;
}
}
let tailState = state;
let tail = "";
while (text.caretGravity.autoskip && !autocompletionStack.empty()) {
const skip = autocompletionStack.pop();
if (modifiedString.length === modifiedCaretPosition) {
if (skip.insert && skip.insert === modifiedString.slice(-1)) {
modifiedString = modifiedString.slice(0, -1);
modifiedCaretPosition -= 1;
}
if (skip.value && skip.value === extractedValue.slice(-1)) {
extractedValue = extractedValue.slice(0, -1);
}
} else {
if (skip.insert) {
modifiedCaretPosition -= 1;
}
}
tailState = skip.state;
tail = skip.insert ? skip.insert : tail;
}
const tailPlaceholder = this.appendPlaceholder(tailState, tail);
const result = {
formattedText: new CaretString(modifiedString, modifiedCaretPosition, text.caretGravity),
extractedValue,
affinity,
complete: this.noMandatoryCharactersLeftAfterState(state),
tailPlaceholder,
reversed() {
return {
formattedText: this.formattedText.reversed(),
extractedValue: this.extractedValue.split("").reverse().join(""),
affinity: this.affinity,
complete: this.complete,
tailPlaceholder: this.tailPlaceholder.split("").reverse().join(""),
reversed: this.reversed
};
}
};
return result;
}
makeIterator(text) {
return new CaretStringIterator(text);
}
placeholder = () => this.appendPlaceholder(this.initialState, "");
acceptableTextLength() {
let state = this.initialState;
let length = 0;
while (state && !(state instanceof EOLState)) {
if (state instanceof FixedState || state instanceof FreeState || state instanceof ValueState) {
length += 1;
}
state = state.child;
}
return length;
}
totalTextLength() {
let state = this.initialState;
let length = 0;
while (state && !(state instanceof EOLState)) {
if (state instanceof FixedState || state instanceof FreeState || state instanceof ValueState || state instanceof OptionalValueState) {
length += 1;
}
state = state.child;
}
return length;
}
acceptableValueLength() {
let state = this.initialState;
let length = 0;
while (state && !(state instanceof EOLState)) {
if (state instanceof FixedState || state instanceof ValueState) {
length += 1;
}
state = state.child;
}
return length;
}
totalValueLength() {
let state = this.initialState;
let length = 0;
while (state && !(state instanceof EOLState)) {
if (state instanceof FixedState || state instanceof ValueState || state instanceof OptionalValueState) {
length += 1;
}
state = state.child;
}
return length;
}
appendPlaceholder(state, placeholder) {
if (!state) {
return placeholder;
}
if (state instanceof EOLState) {
return placeholder;
}
if (state instanceof FreeState || state instanceof FixedState) {
return this.appendPlaceholder(state.child, placeholder + state.ownCharacter);
}
if (state instanceof ValueState || state instanceof OptionalValueState) {
if ("name" in state.stateType) {
switch (state.stateType.name) {
case StateName.alphaNumeric:
return this.appendPlaceholder(state.child, placeholder + "-");
case StateName.literal:
return this.appendPlaceholder(state.child, placeholder + "a");
case StateName.numeric:
return this.appendPlaceholder(state.child, placeholder + "0");
case "ellipsis":
return placeholder;
}
}
return this.appendPlaceholder(state.child, placeholder + state.stateType.character);
}
return placeholder;
}
noMandatoryCharactersLeftAfterState(state) {
if (state instanceof EOLState) {
return true;
}
if (state instanceof ValueState) {
return state.isElliptical;
}
if (state instanceof FixedState) {
return false;
}
return this.noMandatoryCharactersLeftAfterState(state.nextState());
}
}
export default Mask;
//# sourceMappingURL=Mask.js.map