Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 70 additions & 56 deletions src/lib/strategies/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,64 +71,78 @@ export class BaseStrategy extends StrategyInterface {
}

protected attachListeners(): void {
this.inputElement.addEventListener("keydown", (e) => {
const event = e as KeyboardEvent;

if (isSimulatedEvent(event)) {
this.isFormatted = false;
}

if (keyCannotMutateValue(event)) {
return;
}

if (this.isDeletion(event)) {
this.unformatInput();
}
});

this.inputElement.addEventListener("keypress", (e) => {
this.hasKeypressEvent = true;
this.onKeypress(e as KeyboardEvent);
});

this.inputElement.addEventListener("keyup", () => {
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", (e) => {
const event = e as KeyboardEvent;

// 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 as KeyboardEvent);
}

// 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.inputElement.addEventListener("keydown", this.handleKeyDown);
this.inputElement.addEventListener("keypress", this.handleKeyPress);
this.inputElement.addEventListener("keyup", this.handleKeyUp);
this.inputElement.addEventListener("input", this.handleInput);
this.inputElement.addEventListener("paste", this.handlePaste);
}

destroy(): void {
this.inputElement.removeEventListener("keydown", this.handleKeyDown);
this.inputElement.removeEventListener("keypress", this.handleKeyPress);
this.inputElement.removeEventListener("keyup", this.handleKeyUp);
this.inputElement.removeEventListener("input", this.handleInput);
this.inputElement.removeEventListener("paste", this.handlePaste);
}

this.reformatInput();
});
private handleKeyDown(e: Event): void {
const event = e as KeyboardEvent;

if (isSimulatedEvent(event)) {
this.isFormatted = false;
}

if (keyCannotMutateValue(event)) {
return;
}

if (this.isDeletion(event)) {
this.unformatInput();
}
}

this.inputElement.addEventListener("paste", (event) => {
this.pasteEventHandler(event as ClipboardEvent);
});
private handleKeyPress(e: Event): void {
this.hasKeypressEvent = true;
this.onKeypress(e as KeyboardEvent);
}

private handleKeyUp(): void {
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;
}

private handleInput(e: Event) {
const event = e as KeyboardEvent;

// 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 as KeyboardEvent);
}

// 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();
}

private handlePaste(event: Event): void {
this.pasteEventHandler(event as ClipboardEvent);
}

protected isDeletion(event: KeyboardEvent): boolean {
Expand Down
30 changes: 21 additions & 9 deletions src/lib/strategies/ie9.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,27 @@ export class IE9Strategy extends BaseStrategy {
}

protected attachListeners(): void {
this.inputElement.addEventListener("keydown", (event) => {
this.keydownListener(event as KeyboardEvent);
});
this.inputElement.addEventListener("focus", () => {
this.format();
});
this.inputElement.addEventListener("paste", (event) => {
this.pasteEventHandler(event as ClipboardEvent);
});
this.inputElement.addEventListener("keydown", this.handleKeyDownIe9);
this.inputElement.addEventListener("focus", this.handleFocusIe9);
this.inputElement.addEventListener("paste", this.handlePasteIe9);
}

destroy(): void {
this.inputElement.removeEventListener("keydown", this.handleKeyDownIe9);
this.inputElement.removeEventListener("focus", this.handleFocusIe9);
this.inputElement.removeEventListener("paste", this.handlePasteIe9);
}

private handleKeyDownIe9(event: Event): void {
this.keydownListener(event as KeyboardEvent);
}

private handleFocusIe9(): void {
this.format();
}

private handlePasteIe9(event: Event): void {
this.pasteEventHandler(event as ClipboardEvent);
}

private format(): void {
Expand Down
60 changes: 36 additions & 24 deletions src/lib/strategies/ios.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,32 +8,44 @@ export class IosStrategy extends BaseStrategy {
}

protected attachListeners(): void {
this.inputElement.addEventListener("keydown", (event) => {
this.keydownListener(event as KeyboardEvent);
});
this.inputElement.addEventListener("input", (event) => {
const isCustomEvent = event instanceof CustomEvent;

// Safari AutoFill fires CustomEvents
// Set state to format before calling format listener
if (isCustomEvent) {
this.stateToFormat = {
selection: { start: 0, end: 0 },
value: this.inputElement.value,
};
}

this.formatListener();

if (!isCustomEvent) {
this.fixLeadingBlankSpaceOnIos();
}
});
this.inputElement.addEventListener("paste", (event) => {
this.pasteEventHandler(event as ClipboardEvent);
});
this.inputElement.addEventListener("keydown", this.handleKeyDownIos);
this.inputElement.addEventListener("input", this.handleInputIos);
this.inputElement.addEventListener("paste", this.handlePasteIos);
}

destroy(): void {
this.inputElement.removeEventListener("keydown", this.handleKeyDownIos);
this.inputElement.removeEventListener("input", this.handleInputIos);
this.inputElement.removeEventListener("paste", this.handlePasteIos);
}

private handleKeyDownIos(event: Event): void {
this.keydownListener(event as KeyboardEvent);
}

private handleInputIos(event: Event): void {
const isCustomEvent = event instanceof CustomEvent;

// Safari AutoFill fires CustomEvents
// Set state to format before calling format listener
if (isCustomEvent) {
this.stateToFormat = {
selection: { start: 0, end: 0 },
value: this.inputElement.value,
};
}

this.formatListener();

if (!isCustomEvent) {
this.fixLeadingBlankSpaceOnIos();
}
}

private handlePasteIos(event: Event): void {
this.pasteEventHandler(event as ClipboardEvent);
}

// When deleting the last character on iOS, the cursor
// is positioned as if there is a blank space when there
// is not, setting it to '' in a setTimeout fixes it ¯\_(ツ)_/¯
Expand Down
4 changes: 4 additions & 0 deletions src/lib/strategies/noop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,8 @@ export class NoopKeyboardStrategy extends StrategyInterface {
setPattern(): void {
// noop
}

destroy(): void {
// noop
}
}
1 change: 1 addition & 0 deletions src/lib/strategies/strategy-interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ export abstract class StrategyInterface {

abstract getUnformattedValue(): string;
abstract setPattern(pattern: string): void;
abstract destroy(): void;
}