UNPKG

@material/web

Version:
146 lines 5.36 kB
/** * @license * Copyright 2023 Google LLC * SPDX-License-Identifier: Apache-2.0 */ import { Validator } from './validator.js'; /** * A validator that provides constraint validation that emulates `<input>` and * `<textarea>` validation. */ export class TextFieldValidator extends Validator { computeValidity({ state, renderedControl }) { let inputOrTextArea = renderedControl; if (isInputState(state) && !inputOrTextArea) { // Get cached <input> or create it. inputOrTextArea = this.inputControl || document.createElement('input'); // Cache the <input> to re-use it next time. this.inputControl = inputOrTextArea; } else if (!inputOrTextArea) { // Get cached <textarea> or create it. inputOrTextArea = this.textAreaControl || document.createElement('textarea'); // Cache the <textarea> to re-use it next time. this.textAreaControl = inputOrTextArea; } // Set this variable so we can check it for input-specific properties. const input = isInputState(state) ? inputOrTextArea : null; // Set input's "type" first, since this can change the other properties if (input) { input.type = state.type; } if (inputOrTextArea.value !== state.value) { // Only programmatically set the value if there's a difference. When using // the rendered control, the value will always be up to date. Setting the // property (even if it's the same string) will reset the internal <input> // dirty flag, making minlength and maxlength validation reset. inputOrTextArea.value = state.value; } inputOrTextArea.required = state.required; // The following IDLAttribute properties will always hydrate an attribute, // even if set to a the default value ('' or -1). The presence of the // attribute triggers constraint validation, so we must remove the attribute // when empty. if (input) { const inputState = state; if (inputState.pattern) { input.pattern = inputState.pattern; } else { input.removeAttribute('pattern'); } if (inputState.min) { input.min = inputState.min; } else { input.removeAttribute('min'); } if (inputState.max) { input.max = inputState.max; } else { input.removeAttribute('max'); } if (inputState.step) { input.step = inputState.step; } else { input.removeAttribute('step'); } } // Use -1 to represent no minlength and maxlength, which is what the // platform input returns. However, it will throw an error if you try to // manually set it to -1. if (state.minLength > -1) { inputOrTextArea.minLength = state.minLength; } else { inputOrTextArea.removeAttribute('minlength'); } if (state.maxLength > -1) { inputOrTextArea.maxLength = state.maxLength; } else { inputOrTextArea.removeAttribute('maxlength'); } return { validity: inputOrTextArea.validity, validationMessage: inputOrTextArea.validationMessage, }; } equals({ state: prev }, { state: next }) { // Check shared input and textarea properties const inputOrTextAreaEqual = prev.type === next.type && prev.value === next.value && prev.required === next.required && prev.minLength === next.minLength && prev.maxLength === next.maxLength; if (!isInputState(prev) || !isInputState(next)) { // Both are textareas, all relevant properties are equal. return inputOrTextAreaEqual; } // Check additional input-specific properties. return (inputOrTextAreaEqual && prev.pattern === next.pattern && prev.min === next.min && prev.max === next.max && prev.step === next.step); } copy({ state }) { // Don't hold a reference to the rendered control when copying since we // don't use it when checking if the state changed. return { state: isInputState(state) ? this.copyInput(state) : this.copyTextArea(state), renderedControl: null, }; } copyInput(state) { const { type, pattern, min, max, step } = state; return { ...this.copySharedState(state), type, pattern, min, max, step, }; } copyTextArea(state) { return { ...this.copySharedState(state), type: state.type, }; } copySharedState({ value, required, minLength, maxLength, }) { return { value, required, minLength, maxLength }; } } function isInputState(state) { return state.type !== 'textarea'; } //# sourceMappingURL=text-field-validator.js.map