Skip to content

Instantly share code, notes, and snippets.

@VitorLuizC
Last active June 1, 2020 22:53
Show Gist options
  • Save VitorLuizC/0ed8884f4763cd77881f4da2919264ba to your computer and use it in GitHub Desktop.
Save VitorLuizC/0ed8884f4763cd77881f4da2919264ba to your computer and use it in GitHub Desktop.
import { ChangeEvent } from 'react';
/**
* Update selection position simply removing diff between current and formatted
* values and handling negative results.
* @param currentPosition - Current selection position.
* @param currentValue - Current value.
* @param formattedValue - Formatted value.
*/
const updateSelection = (
currentPosition: number,
currentValue: string,
formattedValue: string
) => {
const difference = currentValue.length - formattedValue.length;
if (currentPosition - difference < 0) return 0;
return currentPosition - difference;
};
/**
* Applies formatter to `ChangeEvent`'s value without breaking its cursor.
* ⚠️ Only works for input type "text", "tel", "search", "url" and "password".
* @param formatter - A function that receives value and format it.
*/
const withFormat = (
formatter: (value: string) => string,
onChange?: (event: ChangeEvent<HTMLInputElement>) => void
) => {
/**
* Receives an event, apply formatter and update selection position.
* @param event - An instance of `ChangeEvent`.
*/
return (event: ChangeEvent<HTMLInputElement>) => {
event.persist();
const element = event.currentTarget;
// Save current value.
const currentValue = element.value;
// Save current selection start, end and direction.
const currentSelectionStart = element.selectionStart ?? 0;
const currentSelectionEnd = element.selectionEnd ?? 0;
const currentSelectionDirection = element.selectionDirection ?? undefined;
const formattedValue = formatter(currentValue);
// eslint-disable-next-line no-param-reassign
element.value = formattedValue;
if (document.activeElement === element) {
element.setSelectionRange(
updateSelection(currentSelectionStart, currentValue, formattedValue),
updateSelection(currentSelectionEnd, currentValue, formattedValue),
currentSelectionDirection
);
}
if (onChange) onChange(event);
};
};
export default withFormat;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment