UNPKG

vira

Version:

A simple and highly versatile design system using element-vir.

102 lines (101 loc) 3.26 kB
import { check, checkWrap } from '@augment-vir/assert'; import { extractEventTarget } from '@augment-vir/web'; function doesMatch({ input, matcher }) { if (!input || !matcher) { return true; } if (input.length > 1) { return input.split('').every((singleInput) => doesMatch({ input: singleInput, matcher })); } if (matcher instanceof RegExp) { return !!input.match(matcher); } else { return matcher.includes(input); } } function isAllowed({ value, allowed, blocked }) { const isAllowedCharacter = allowed ? doesMatch({ input: value, matcher: allowed, }) : true; const isBlockedCharacter = blocked ? doesMatch({ input: value, matcher: blocked, }) : false; return isAllowedCharacter && !isBlockedCharacter; } /** * Filters out blocked text from an input element's value. * * @category Internal */ export function filterTextInputValue(inputs) { if (!inputs.value) { return { filtered: inputs.value, blocked: '' }; } const { filtered, blocked } = inputs.value.split('').reduce((accum, letter) => { const allowed = isAllowed({ ...inputs, value: letter }); if (allowed) { accum.filtered.push(letter); } else { accum.blocked.push(letter); } return accum; }, { filtered: [], blocked: [], }); return { filtered: filtered.join(''), blocked: blocked.join(''), }; } /** * A function to be called when an input element's value changes. * * @category Internal */ export function textInputListener({ inputs, previousValue, event, inputBlockedCallback, newValueCallback, }) { const inputElement = extractEventTarget(event, HTMLInputElement); /** * This is usually a single character, but can be a bunch of characters in some circumstances. * For example, when a bunch of characters are pasted, this will be the entire pasted contents. * * When a password manager auto fills the password, at least for Safari + iCloud Keychain, it'll * fire a `CustomEvent` (rather than the typical `InputEvent`) and `event.data` won't be * populated. */ const changedText = (check.hasKey(event, 'data') && checkWrap.isString(event.data)) || ''; /** * When changedText is falsy, that means an operation other than inserting characters happened. * Such as: deleting, cutting the text, etc. */ if (changedText) { const { blocked } = filterTextInputValue({ value: changedText, allowed: inputs.allowedInputs, blocked: inputs.blockedInputs, }); if (blocked.length) { inputBlockedCallback(blocked); } } const finalValue = filterTextInputValue({ value: inputElement.value, allowed: inputs.allowedInputs, blocked: inputs.blockedInputs, }).filtered; if (inputElement.value !== finalValue) { // this prevents blocked inputs by simply overwriting them inputElement.value = finalValue; } if (previousValue !== finalValue) { newValueCallback(finalValue); } }