UNPKG

@material/textfield

Version:
584 lines • 24.1 kB
/** * @license * Copyright 2016 Google Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ import { __assign, __extends, __values } from "tslib"; import { MDCFoundation } from '@material/base/foundation'; import { ALWAYS_FLOAT_TYPES, cssClasses, numbers, strings, VALIDATION_ATTR_WHITELIST } from './constants'; var POINTERDOWN_EVENTS = ['mousedown', 'touchstart']; var INTERACTION_EVENTS = ['click', 'keydown']; var MDCTextFieldFoundation = /** @class */ (function (_super) { __extends(MDCTextFieldFoundation, _super); /** * @param adapter * @param foundationMap Map from subcomponent names to their subfoundations. */ function MDCTextFieldFoundation(adapter, foundationMap) { if (foundationMap === void 0) { foundationMap = {}; } var _this = _super.call(this, __assign(__assign({}, MDCTextFieldFoundation.defaultAdapter), adapter)) || this; _this.isFocused = false; _this.receivedUserInput = false; _this.valid = true; _this.useNativeValidation = true; _this.validateOnValueChange = true; _this.helperText = foundationMap.helperText; _this.characterCounter = foundationMap.characterCounter; _this.leadingIcon = foundationMap.leadingIcon; _this.trailingIcon = foundationMap.trailingIcon; _this.inputFocusHandler = function () { _this.activateFocus(); }; _this.inputBlurHandler = function () { _this.deactivateFocus(); }; _this.inputInputHandler = function () { _this.handleInput(); }; _this.setPointerXOffset = function (evt) { _this.setTransformOrigin(evt); }; _this.textFieldInteractionHandler = function () { _this.handleTextFieldInteraction(); }; _this.validationAttributeChangeHandler = function (attributesList) { _this.handleValidationAttributeChange(attributesList); }; return _this; } Object.defineProperty(MDCTextFieldFoundation, "cssClasses", { get: function () { return cssClasses; }, enumerable: false, configurable: true }); Object.defineProperty(MDCTextFieldFoundation, "strings", { get: function () { return strings; }, enumerable: false, configurable: true }); Object.defineProperty(MDCTextFieldFoundation, "numbers", { get: function () { return numbers; }, enumerable: false, configurable: true }); Object.defineProperty(MDCTextFieldFoundation.prototype, "shouldAlwaysFloat", { get: function () { var type = this.getNativeInput().type; return ALWAYS_FLOAT_TYPES.indexOf(type) >= 0; }, enumerable: false, configurable: true }); Object.defineProperty(MDCTextFieldFoundation.prototype, "shouldFloat", { get: function () { return this.shouldAlwaysFloat || this.isFocused || !!this.getValue() || this.isBadInput(); }, enumerable: false, configurable: true }); Object.defineProperty(MDCTextFieldFoundation.prototype, "shouldShake", { get: function () { return !this.isFocused && !this.isValid() && !!this.getValue(); }, enumerable: false, configurable: true }); Object.defineProperty(MDCTextFieldFoundation, "defaultAdapter", { /** * See {@link MDCTextFieldAdapter} for typing information on parameters and * return types. */ get: function () { // tslint:disable:object-literal-sort-keys Methods should be in the same order as the adapter interface. return { addClass: function () { return undefined; }, removeClass: function () { return undefined; }, hasClass: function () { return true; }, setInputAttr: function () { return undefined; }, removeInputAttr: function () { return undefined; }, registerTextFieldInteractionHandler: function () { return undefined; }, deregisterTextFieldInteractionHandler: function () { return undefined; }, registerInputInteractionHandler: function () { return undefined; }, deregisterInputInteractionHandler: function () { return undefined; }, registerValidationAttributeChangeHandler: function () { return new MutationObserver(function () { return undefined; }); }, deregisterValidationAttributeChangeHandler: function () { return undefined; }, getNativeInput: function () { return null; }, isFocused: function () { return false; }, activateLineRipple: function () { return undefined; }, deactivateLineRipple: function () { return undefined; }, setLineRippleTransformOrigin: function () { return undefined; }, shakeLabel: function () { return undefined; }, floatLabel: function () { return undefined; }, setLabelRequired: function () { return undefined; }, hasLabel: function () { return false; }, getLabelWidth: function () { return 0; }, hasOutline: function () { return false; }, notchOutline: function () { return undefined; }, closeOutline: function () { return undefined; }, }; // tslint:enable:object-literal-sort-keys }, enumerable: false, configurable: true }); MDCTextFieldFoundation.prototype.init = function () { var e_1, _a, e_2, _b; if (this.adapter.hasLabel() && this.getNativeInput().required) { this.adapter.setLabelRequired(true); } if (this.adapter.isFocused()) { this.inputFocusHandler(); } else if (this.adapter.hasLabel() && this.shouldFloat) { this.notchOutline(true); this.adapter.floatLabel(true); this.styleFloating(true); } this.adapter.registerInputInteractionHandler('focus', this.inputFocusHandler); this.adapter.registerInputInteractionHandler('blur', this.inputBlurHandler); this.adapter.registerInputInteractionHandler('input', this.inputInputHandler); try { for (var POINTERDOWN_EVENTS_1 = __values(POINTERDOWN_EVENTS), POINTERDOWN_EVENTS_1_1 = POINTERDOWN_EVENTS_1.next(); !POINTERDOWN_EVENTS_1_1.done; POINTERDOWN_EVENTS_1_1 = POINTERDOWN_EVENTS_1.next()) { var evtType = POINTERDOWN_EVENTS_1_1.value; this.adapter.registerInputInteractionHandler(evtType, this.setPointerXOffset); } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (POINTERDOWN_EVENTS_1_1 && !POINTERDOWN_EVENTS_1_1.done && (_a = POINTERDOWN_EVENTS_1.return)) _a.call(POINTERDOWN_EVENTS_1); } finally { if (e_1) throw e_1.error; } } try { for (var INTERACTION_EVENTS_1 = __values(INTERACTION_EVENTS), INTERACTION_EVENTS_1_1 = INTERACTION_EVENTS_1.next(); !INTERACTION_EVENTS_1_1.done; INTERACTION_EVENTS_1_1 = INTERACTION_EVENTS_1.next()) { var evtType = INTERACTION_EVENTS_1_1.value; this.adapter.registerTextFieldInteractionHandler(evtType, this.textFieldInteractionHandler); } } catch (e_2_1) { e_2 = { error: e_2_1 }; } finally { try { if (INTERACTION_EVENTS_1_1 && !INTERACTION_EVENTS_1_1.done && (_b = INTERACTION_EVENTS_1.return)) _b.call(INTERACTION_EVENTS_1); } finally { if (e_2) throw e_2.error; } } this.validationObserver = this.adapter.registerValidationAttributeChangeHandler(this.validationAttributeChangeHandler); this.setcharacterCounter(this.getValue().length); }; MDCTextFieldFoundation.prototype.destroy = function () { var e_3, _a, e_4, _b; this.adapter.deregisterInputInteractionHandler('focus', this.inputFocusHandler); this.adapter.deregisterInputInteractionHandler('blur', this.inputBlurHandler); this.adapter.deregisterInputInteractionHandler('input', this.inputInputHandler); try { for (var POINTERDOWN_EVENTS_2 = __values(POINTERDOWN_EVENTS), POINTERDOWN_EVENTS_2_1 = POINTERDOWN_EVENTS_2.next(); !POINTERDOWN_EVENTS_2_1.done; POINTERDOWN_EVENTS_2_1 = POINTERDOWN_EVENTS_2.next()) { var evtType = POINTERDOWN_EVENTS_2_1.value; this.adapter.deregisterInputInteractionHandler(evtType, this.setPointerXOffset); } } catch (e_3_1) { e_3 = { error: e_3_1 }; } finally { try { if (POINTERDOWN_EVENTS_2_1 && !POINTERDOWN_EVENTS_2_1.done && (_a = POINTERDOWN_EVENTS_2.return)) _a.call(POINTERDOWN_EVENTS_2); } finally { if (e_3) throw e_3.error; } } try { for (var INTERACTION_EVENTS_2 = __values(INTERACTION_EVENTS), INTERACTION_EVENTS_2_1 = INTERACTION_EVENTS_2.next(); !INTERACTION_EVENTS_2_1.done; INTERACTION_EVENTS_2_1 = INTERACTION_EVENTS_2.next()) { var evtType = INTERACTION_EVENTS_2_1.value; this.adapter.deregisterTextFieldInteractionHandler(evtType, this.textFieldInteractionHandler); } } catch (e_4_1) { e_4 = { error: e_4_1 }; } finally { try { if (INTERACTION_EVENTS_2_1 && !INTERACTION_EVENTS_2_1.done && (_b = INTERACTION_EVENTS_2.return)) _b.call(INTERACTION_EVENTS_2); } finally { if (e_4) throw e_4.error; } } this.adapter.deregisterValidationAttributeChangeHandler(this.validationObserver); }; /** * Handles user interactions with the Text Field. */ MDCTextFieldFoundation.prototype.handleTextFieldInteraction = function () { var nativeInput = this.adapter.getNativeInput(); if (nativeInput && nativeInput.disabled) { return; } this.receivedUserInput = true; }; /** * Handles validation attribute changes */ MDCTextFieldFoundation.prototype.handleValidationAttributeChange = function (attributesList) { var _this = this; attributesList.some(function (attributeName) { if (VALIDATION_ATTR_WHITELIST.indexOf(attributeName) > -1) { _this.styleValidity(true); _this.adapter.setLabelRequired(_this.getNativeInput().required); return true; } return false; }); if (attributesList.indexOf('maxlength') > -1) { this.setcharacterCounter(this.getValue().length); } }; /** * Opens/closes the notched outline. */ MDCTextFieldFoundation.prototype.notchOutline = function (openNotch) { if (!this.adapter.hasOutline() || !this.adapter.hasLabel()) { return; } if (openNotch) { var labelWidth = this.adapter.getLabelWidth() * numbers.LABEL_SCALE; this.adapter.notchOutline(labelWidth); } else { this.adapter.closeOutline(); } }; /** * Activates the text field focus state. */ MDCTextFieldFoundation.prototype.activateFocus = function () { this.isFocused = true; this.styleFocused(this.isFocused); this.adapter.activateLineRipple(); if (this.adapter.hasLabel()) { this.notchOutline(this.shouldFloat); this.adapter.floatLabel(this.shouldFloat); this.styleFloating(this.shouldFloat); this.adapter.shakeLabel(this.shouldShake); } if (this.helperText && (this.helperText.isPersistent() || !this.helperText.isValidation() || !this.valid)) { this.helperText.showToScreenReader(); } }; /** * Sets the line ripple's transform origin, so that the line ripple activate * animation will animate out from the user's click location. */ MDCTextFieldFoundation.prototype.setTransformOrigin = function (evt) { if (this.isDisabled() || this.adapter.hasOutline()) { return; } var touches = evt.touches; var targetEvent = touches ? touches[0] : evt; var targetClientRect = targetEvent.target.getBoundingClientRect(); var normalizedX = targetEvent.clientX - targetClientRect.left; this.adapter.setLineRippleTransformOrigin(normalizedX); }; /** * Handles input change of text input and text area. */ MDCTextFieldFoundation.prototype.handleInput = function () { this.autoCompleteFocus(); this.setcharacterCounter(this.getValue().length); }; /** * Activates the Text Field's focus state in cases when the input value * changes without user input (e.g. programmatically). */ MDCTextFieldFoundation.prototype.autoCompleteFocus = function () { if (!this.receivedUserInput) { this.activateFocus(); } }; /** * Deactivates the Text Field's focus state. */ MDCTextFieldFoundation.prototype.deactivateFocus = function () { this.isFocused = false; this.adapter.deactivateLineRipple(); var isValid = this.isValid(); this.styleValidity(isValid); this.styleFocused(this.isFocused); if (this.adapter.hasLabel()) { this.notchOutline(this.shouldFloat); this.adapter.floatLabel(this.shouldFloat); this.styleFloating(this.shouldFloat); this.adapter.shakeLabel(this.shouldShake); } if (!this.shouldFloat) { this.receivedUserInput = false; } }; MDCTextFieldFoundation.prototype.getValue = function () { return this.getNativeInput().value; }; /** * @param value The value to set on the input Element. */ MDCTextFieldFoundation.prototype.setValue = function (value) { // Prevent Safari from moving the caret to the end of the input when the // value has not changed. if (this.getValue() !== value) { this.getNativeInput().value = value; } this.setcharacterCounter(value.length); if (this.validateOnValueChange) { var isValid = this.isValid(); this.styleValidity(isValid); } if (this.adapter.hasLabel()) { this.notchOutline(this.shouldFloat); this.adapter.floatLabel(this.shouldFloat); this.styleFloating(this.shouldFloat); if (this.validateOnValueChange) { this.adapter.shakeLabel(this.shouldShake); } } }; /** * @return The custom validity state, if set; otherwise, the result of a * native validity check. */ MDCTextFieldFoundation.prototype.isValid = function () { return this.useNativeValidation ? this.isNativeInputValid() : this.valid; }; /** * @param isValid Sets the custom validity state of the Text Field. */ MDCTextFieldFoundation.prototype.setValid = function (isValid) { this.valid = isValid; this.styleValidity(isValid); var shouldShake = !isValid && !this.isFocused && !!this.getValue(); if (this.adapter.hasLabel()) { this.adapter.shakeLabel(shouldShake); } }; /** * @param shouldValidate Whether or not validity should be updated on * value change. */ MDCTextFieldFoundation.prototype.setValidateOnValueChange = function (shouldValidate) { this.validateOnValueChange = shouldValidate; }; /** * @return Whether or not validity should be updated on value change. `true` * by default. */ MDCTextFieldFoundation.prototype.getValidateOnValueChange = function () { return this.validateOnValueChange; }; /** * Enables or disables the use of native validation. Use this for custom * validation. * @param useNativeValidation Set this to false to ignore native input * validation. */ MDCTextFieldFoundation.prototype.setUseNativeValidation = function (useNativeValidation) { this.useNativeValidation = useNativeValidation; }; MDCTextFieldFoundation.prototype.isDisabled = function () { return this.getNativeInput().disabled; }; /** * @param disabled Sets the text-field disabled or enabled. */ MDCTextFieldFoundation.prototype.setDisabled = function (disabled) { this.getNativeInput().disabled = disabled; this.styleDisabled(disabled); }; /** * @param content Sets the content of the helper text. */ MDCTextFieldFoundation.prototype.setHelperTextContent = function (content) { if (this.helperText) { this.helperText.setContent(content); } }; /** * Sets the aria label of the leading icon. */ MDCTextFieldFoundation.prototype.setLeadingIconAriaLabel = function (label) { if (this.leadingIcon) { this.leadingIcon.setAriaLabel(label); } }; /** * Sets the text content of the leading icon. */ MDCTextFieldFoundation.prototype.setLeadingIconContent = function (content) { if (this.leadingIcon) { this.leadingIcon.setContent(content); } }; /** * Sets the aria label of the trailing icon. */ MDCTextFieldFoundation.prototype.setTrailingIconAriaLabel = function (label) { if (this.trailingIcon) { this.trailingIcon.setAriaLabel(label); } }; /** * Sets the text content of the trailing icon. */ MDCTextFieldFoundation.prototype.setTrailingIconContent = function (content) { if (this.trailingIcon) { this.trailingIcon.setContent(content); } }; /** * Sets character counter values that shows characters used and the total * character limit. */ MDCTextFieldFoundation.prototype.setcharacterCounter = function (currentLength) { if (!this.characterCounter) { return; } var maxLength = this.getNativeInput().maxLength; if (maxLength === -1) { throw new Error('MDCTextFieldFoundation: Expected maxlength html property on text input or textarea.'); } this.characterCounter.setCounterValue(currentLength, maxLength); }; /** * @return True if the Text Field input fails in converting the user-supplied * value. */ MDCTextFieldFoundation.prototype.isBadInput = function () { // The badInput property is not supported in IE 11 💩. return this.getNativeInput().validity.badInput || false; }; /** * @return The result of native validity checking (ValidityState.valid). */ MDCTextFieldFoundation.prototype.isNativeInputValid = function () { return this.getNativeInput().validity.valid; }; /** * Styles the component based on the validity state. */ MDCTextFieldFoundation.prototype.styleValidity = function (isValid) { var INVALID = MDCTextFieldFoundation.cssClasses.INVALID; if (isValid) { this.adapter.removeClass(INVALID); } else { this.adapter.addClass(INVALID); } if (this.helperText) { this.helperText.setValidity(isValid); // We dynamically set or unset aria-describedby for validation helper text // only, based on whether the field is valid var helperTextValidation = this.helperText.isValidation(); if (!helperTextValidation) { return; } var helperTextVisible = this.helperText.isVisible(); var helperTextId = this.helperText.getId(); if (helperTextVisible && helperTextId) { this.adapter.setInputAttr(strings.ARIA_DESCRIBEDBY, helperTextId); } else { this.adapter.removeInputAttr(strings.ARIA_DESCRIBEDBY); } } }; /** * Styles the component based on the focused state. */ MDCTextFieldFoundation.prototype.styleFocused = function (isFocused) { var FOCUSED = MDCTextFieldFoundation.cssClasses.FOCUSED; if (isFocused) { this.adapter.addClass(FOCUSED); } else { this.adapter.removeClass(FOCUSED); } }; /** * Styles the component based on the disabled state. */ MDCTextFieldFoundation.prototype.styleDisabled = function (isDisabled) { var _a = MDCTextFieldFoundation.cssClasses, DISABLED = _a.DISABLED, INVALID = _a.INVALID; if (isDisabled) { this.adapter.addClass(DISABLED); this.adapter.removeClass(INVALID); } else { this.adapter.removeClass(DISABLED); } if (this.leadingIcon) { this.leadingIcon.setDisabled(isDisabled); } if (this.trailingIcon) { this.trailingIcon.setDisabled(isDisabled); } }; /** * Styles the component based on the label floating state. */ MDCTextFieldFoundation.prototype.styleFloating = function (isFloating) { var LABEL_FLOATING = MDCTextFieldFoundation.cssClasses.LABEL_FLOATING; if (isFloating) { this.adapter.addClass(LABEL_FLOATING); } else { this.adapter.removeClass(LABEL_FLOATING); } }; /** * @return The native text input element from the host environment, or an * object with the same shape for unit tests. */ MDCTextFieldFoundation.prototype.getNativeInput = function () { // this.adapter may be undefined in foundation unit tests. This happens when // testdouble is creating a mock object and invokes the // shouldShake/shouldFloat getters (which in turn call getValue(), which // calls this method) before init() has been called from the MDCTextField // constructor. To work around that issue, we return a dummy object. var nativeInput = this.adapter ? this.adapter.getNativeInput() : null; return nativeInput || { disabled: false, maxLength: -1, required: false, type: 'input', validity: { badInput: false, valid: true, }, value: '', }; }; return MDCTextFieldFoundation; }(MDCFoundation)); export { MDCTextFieldFoundation }; // tslint:disable-next-line:no-default-export Needed for backward compatibility with MDC Web v0.44.0 and earlier. export default MDCTextFieldFoundation; //# sourceMappingURL=foundation.js.map