Last active
June 1, 2020 22:53
-
-
Save VitorLuizC/0ed8884f4763cd77881f4da2919264ba to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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