restricted-input
Version:
Restrict inputs to certain valid characters (e.g. formatting phone or card numbers)
218 lines (217 loc) • 9.06 kB
JavaScript
"use strict";
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.BaseStrategy = void 0;
var strategy_interface_1 = require("./strategy-interface");
var key_cannot_mutate_value_1 = require("../key-cannot-mutate-value");
var input_selection_1 = require("../input-selection");
var is_backspace_1 = require("../is-backspace");
var is_delete_1 = require("../is-delete");
var formatter_1 = require("../formatter");
function isSimulatedEvent(event) {
// 1Password sets input.value then fires keyboard events. Dependent on browser
// here might be falsy values (key = '', keyCode = 0) or these keys might be omitted
// Chrome autofill inserts keys all at once and fires a single event without key info
return !event.key && !event.keyCode;
}
var BaseStrategy = /** @class */ (function (_super) {
__extends(BaseStrategy, _super);
function BaseStrategy(options) {
var _this = _super.call(this, options) || this;
_this.hasKeypressEvent = false;
_this.formatter = new formatter_1.PatternFormatter(options.pattern);
_this.onPasteEvent = options.onPasteEvent;
_this.attachListeners();
_this.formatIfNotEmpty();
return _this;
}
BaseStrategy.prototype.getUnformattedValue = function (forceUnformat) {
var value = this.inputElement.value;
if (forceUnformat || this.isFormatted) {
value = this.formatter.unformat({
value: this.inputElement.value,
selection: { start: 0, end: 0 },
}).value;
}
return value;
};
BaseStrategy.prototype.formatIfNotEmpty = function () {
if (this.inputElement.value) {
this.reformatInput();
}
};
BaseStrategy.prototype.setPattern = function (pattern) {
this.unformatInput();
this.formatter = new formatter_1.PatternFormatter(pattern);
this.formatIfNotEmpty();
};
BaseStrategy.prototype.attachListeners = function () {
var _this = this;
this.inputElement.addEventListener("keydown", function (e) {
var event = e;
if (isSimulatedEvent(event)) {
_this.isFormatted = false;
}
if ((0, key_cannot_mutate_value_1.keyCannotMutateValue)(event)) {
return;
}
if (_this.isDeletion(event)) {
_this.unformatInput();
}
});
this.inputElement.addEventListener("keypress", function (e) {
_this.hasKeypressEvent = true;
_this.onKeypress(e);
});
this.inputElement.addEventListener("keyup", function () {
_this.reformatInput();
// if the user changes their keyboard and
// the browser doesn't support the keypress event listener,
// we need to reset the keypress flag to be able to enable the
// fallback for the custom input event listener
// to be able to format the field
_this.hasKeypressEvent = false;
});
this.inputElement.addEventListener("input", function (e) {
var event = e;
// Some input sources on Mac OS prevent
// the keypress event from being fired,
// so if we can't detect that the keypress
// event fired, we simulate the event
// here before the handler for the input
// event
if (!_this.hasKeypressEvent) {
_this.onKeypress(e);
}
// Safari AutoFill fires CustomEvents
// LastPass sends an `isTrusted: false` property
// Since the input is changed all at once, set isFormatted
// to false so that reformatting actually occurs
if (event instanceof CustomEvent || !event.isTrusted) {
_this.isFormatted = false;
}
_this.reformatInput();
});
this.inputElement.addEventListener("paste", function (event) {
_this.pasteEventHandler(event);
});
};
BaseStrategy.prototype.isDeletion = function (event) {
return (0, is_delete_1.isDelete)(event) || (0, is_backspace_1.isBackspace)(event);
};
BaseStrategy.prototype.reformatInput = function () {
if (this.isFormatted) {
return;
}
this.isFormatted = true;
var input = this.inputElement;
var formattedState = this.formatter.format({
selection: (0, input_selection_1.get)(input),
value: input.value,
});
input.value = formattedState.value;
(0, input_selection_1.set)(input, formattedState.selection.start, formattedState.selection.end);
this.afterReformatInput(formattedState);
};
// If a strategy needs to impliment specific behavior
// after reformatting has happend, the strategy just
// overwrites this method on their own prototype
// eslint-disable-next-line @typescript-eslint/no-unused-vars
BaseStrategy.prototype.afterReformatInput = function (formattedState) {
// noop
};
BaseStrategy.prototype.unformatInput = function () {
if (!this.isFormatted) {
return;
}
this.isFormatted = false;
var input = this.inputElement;
var selection = (0, input_selection_1.get)(input);
var unformattedState = this.formatter.unformat({
selection: selection,
value: input.value,
});
input.value = unformattedState.value;
(0, input_selection_1.set)(input, unformattedState.selection.start, unformattedState.selection.end);
};
BaseStrategy.prototype.prePasteEventHandler = function (event) {
// without this, the paste event is called twice
// so if you were pasting abc it would result in
// abcabc
event.preventDefault();
};
BaseStrategy.prototype.postPasteEventHandler = function () {
this.reformatAfterPaste();
};
BaseStrategy.prototype.pasteEventHandler = function (event) {
var splicedEntry;
var entryValue = "";
this.prePasteEventHandler(event);
this.unformatInput();
if (event.clipboardData) {
entryValue = event.clipboardData.getData("Text");
}
else if (window.clipboardData) {
entryValue = window.clipboardData.getData("Text");
}
var selection = (0, input_selection_1.get)(this.inputElement);
splicedEntry = this.inputElement.value.split("");
splicedEntry.splice(selection.start, selection.end - selection.start, entryValue);
splicedEntry = splicedEntry.join("");
if (this.onPasteEvent) {
this.onPasteEvent({
unformattedInputValue: splicedEntry,
});
}
this.inputElement.value = splicedEntry;
(0, input_selection_1.set)(this.inputElement, selection.start + entryValue.length, selection.start + entryValue.length);
this.postPasteEventHandler();
};
BaseStrategy.prototype.reformatAfterPaste = function () {
var event = document.createEvent("Event");
this.reformatInput();
event.initEvent("input", true, true);
this.inputElement.dispatchEvent(event);
};
BaseStrategy.prototype.getStateToFormat = function () {
var input = this.inputElement;
var selection = (0, input_selection_1.get)(input);
var stateToFormat = {
selection: selection,
value: input.value,
};
if (this.stateToFormat) {
stateToFormat = this.stateToFormat;
delete this.stateToFormat;
}
else if (selection.start === input.value.length && this.isFormatted) {
stateToFormat = this.formatter.unformat(stateToFormat);
}
return stateToFormat;
};
BaseStrategy.prototype.onKeypress = function (event) {
if (isSimulatedEvent(event)) {
this.isFormatted = false;
}
if ((0, key_cannot_mutate_value_1.keyCannotMutateValue)(event)) {
return;
}
this.unformatInput();
};
return BaseStrategy;
}(strategy_interface_1.StrategyInterface));
exports.BaseStrategy = BaseStrategy;