input-format
Version:
Formatting user's text input on-the-fly
139 lines (114 loc) • 5.1 kB
JavaScript
import edit from './edit.js';
import parse from './parse.js';
import format from './format.js';
import { isReadOnly, getOperation, getSelection, getCaretPosition, setCaretPosition } from './dom.js'; // Deprecated.
// I don't know why this function exists.
export function onCut(event, input, _parse, _format, on_change) {
if (isReadOnly(input)) {
return;
} // The actual cut hasn't happened just yet hence the timeout.
setTimeout(function () {
return formatInputText(input, _parse, _format, undefined, on_change);
}, 0);
} // Deprecated.
// I don't know why this function exists.
export function onPaste(event, input, _parse, _format, on_change) {
if (isReadOnly(input)) {
return;
}
var selection = getSelection(input); // If selection is made,
// just erase the selected text
// prior to pasting
if (selection) {
eraseSelection(input, selection);
}
formatInputText(input, _parse, _format, undefined, on_change);
}
export function onChange(event, input, _parse, _format, on_change) {
formatInputText(input, _parse, _format, undefined, on_change);
} // "Delete" and "Backspace" keys are special
// in a way that they're not handled by the regular `onChange()` handler
// and instead are intercepted and re-applied manually.
// The reason is that normally hitting "Backspace" or "Delete"
// results in erasing a character, but that character might be any character,
// while it would be a better "user experience" if it erased not just any character
// but the closest "meaningful" character.
// For example, if a template is `(xxx) xxx-xxxx`,
// and the `<input/>` value is `(111) 222-3333`,
// then, if a user begins erasing the `3333` part via "Backspace"
// and reaches the "-" character, then it would just erase the "-" character.
// Nothing wrong with that, but it would be a better "user experience"
// if hitting "Backspace" at that position would erase the closest "meaningful"
// character, which would be the rightmost `2`.
// So, what this `onKeyDown()` handler does is it intercepts
// "Backspace" and "Delete" keys and re-applies those operations manually
// following the logic described above.
export function onKeyDown(event, input, _parse, _format, on_change) {
if (isReadOnly(input)) {
return;
}
var operation = getOperation(event);
switch (operation) {
case 'Delete':
case 'Backspace':
// Intercept this operation and perform it manually.
event.preventDefault();
var selection = getSelection(input); // If a selection is made, just erase the selected text.
if (selection) {
eraseSelection(input, selection);
return formatInputText(input, _parse, _format, undefined, on_change);
} // Else, perform the (character erasing) operation manually.
return formatInputText(input, _parse, _format, operation, on_change);
default: // Will be handled normally as part of the `onChange` handler.
}
}
/**
* Erases the selected text inside an `<input/>`.
* @param {DOMElement} input
* @param {Selection} selection
*/
function eraseSelection(input, selection) {
var text = input.value;
text = text.slice(0, selection.start) + text.slice(selection.end);
input.value = text;
setCaretPosition(input, selection.start);
}
/**
* Parses and re-formats `<input/>` textual value.
* E.g. when a user enters something into the `<input/>`
* that raw input must first be parsed and the re-formatted properly.
* Is called either after some user input (e.g. entered a character, pasted something)
* or after the user performed an `operation` (e.g. "Backspace", "Delete").
* @param {DOMElement} input
* @param {Function} parse
* @param {Function} format
* @param {string} [operation] - The operation that triggered `<input/>` textual value change. E.g. "Backspace", "Delete".
* @param {Function} onChange
*/
function formatInputText(input, _parse, _format, operation, on_change) {
// Parse `<input/>` textual value.
// Get the `value` and `caret` position.
var _parse2 = parse(input.value, getCaretPosition(input), _parse),
value = _parse2.value,
caret = _parse2.caret; // If a user performed an operation ("Backspace", "Delete")
// then apply that operation and get the new `value` and `caret` position.
if (operation) {
var newValueAndCaret = edit(value, caret, operation);
value = newValueAndCaret.value;
caret = newValueAndCaret.caret;
} // Format the `value`.
// (and reposition the caret accordingly)
var formatted = format(value, caret, _format);
var text = formatted.text;
caret = formatted.caret; // Set `<input/>` textual value manually
// to prevent React from resetting the caret position
// later inside a subsequent `render()`.
// Doesn't work for custom `inputComponent`s for some reason.
input.value = text; // Position the caret properly.
setCaretPosition(input, caret); // If the `<input/>` textual value did change,
// then the parsed `value` may have changed too.
if (on_change) {
on_change(value);
}
}
//# sourceMappingURL=inputControl.js.map