UNPKG

@angular/material

Version:
526 lines (516 loc) 18 kB
/** * @license * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ import { CdkTextareaAutosize, AutofillMonitor, TextFieldModule } from '@angular/cdk/text-field'; import { Directive, Input, InjectionToken, ElementRef, Inject, Optional, Self, NgZone, NgModule } from '@angular/core'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { getSupportedInputTypes, Platform } from '@angular/cdk/platform'; import { FormGroupDirective, NgControl, NgForm } from '@angular/forms'; import { ErrorStateMatcher, mixinErrorState } from '@angular/material/core'; import { MatFormFieldControl, MatFormFieldModule } from '@angular/material/form-field'; import { Subject } from 'rxjs'; import { CommonModule } from '@angular/common'; /** * @fileoverview added by tsickle * @suppress {checkTypes} checked by tsc */ /** * Directive to automatically resize a textarea to fit its content. * \@deletion-target 7.0.0 deprecate in favor of `cdkTextareaAutosize`. */ class MatTextareaAutosize extends CdkTextareaAutosize { /** * @return {?} */ get matAutosizeMinRows() { return this.minRows; } /** * @param {?} value * @return {?} */ set matAutosizeMinRows(value) { this.minRows = value; } /** * @return {?} */ get matAutosizeMaxRows() { return this.maxRows; } /** * @param {?} value * @return {?} */ set matAutosizeMaxRows(value) { this.maxRows = value; } } MatTextareaAutosize.decorators = [ { type: Directive, args: [{ selector: 'textarea[mat-autosize], textarea[matTextareaAutosize]', exportAs: 'matTextareaAutosize', inputs: ['cdkAutosizeMinRows', 'cdkAutosizeMaxRows'], host: { 'class': 'cdk-textarea-autosize mat-autosize', // Textarea elements that have the directive applied should have a single row by default. // Browsers normally show two rows by default and therefore this limits the minRows binding. 'rows': '1', '(input)': '_noopInputHandler()', }, },] }, ]; /** @nocollapse */ MatTextareaAutosize.propDecorators = { "matAutosizeMinRows": [{ type: Input },], "matAutosizeMaxRows": [{ type: Input },], }; /** * @fileoverview added by tsickle * @suppress {checkTypes} checked by tsc */ /** * \@docs-private * @param {?} type * @return {?} */ function getMatInputUnsupportedTypeError(type) { return Error(`Input type "${type}" isn't supported by matInput.`); } /** * @fileoverview added by tsickle * @suppress {checkTypes} checked by tsc */ /** * This token is used to inject the object whose value should be set into `MatInput`. If none is * provided, the native `HTMLInputElement` is used. Directives like `MatDatepickerInput` can provide * themselves for this token, in order to make `MatInput` delegate the getting and setting of the * value to them. */ const /** @type {?} */ MAT_INPUT_VALUE_ACCESSOR = new InjectionToken('MAT_INPUT_VALUE_ACCESSOR'); /** * @fileoverview added by tsickle * @suppress {checkTypes} checked by tsc */ // Invalid input type. Using one of these will throw an MatInputUnsupportedTypeError. const /** @type {?} */ MAT_INPUT_INVALID_TYPES = [ 'button', 'checkbox', 'file', 'hidden', 'image', 'radio', 'range', 'reset', 'submit' ]; let /** @type {?} */ nextUniqueId = 0; /** * \@docs-private */ class MatInputBase { /** * @param {?} _defaultErrorStateMatcher * @param {?} _parentForm * @param {?} _parentFormGroup * @param {?} ngControl */ constructor(_defaultErrorStateMatcher, _parentForm, _parentFormGroup, ngControl) { this._defaultErrorStateMatcher = _defaultErrorStateMatcher; this._parentForm = _parentForm; this._parentFormGroup = _parentFormGroup; this.ngControl = ngControl; } } const /** @type {?} */ _MatInputMixinBase = mixinErrorState(MatInputBase); /** * Directive that allows a native input to work inside a `MatFormField`. */ class MatInput extends _MatInputMixinBase { /** * @param {?} _elementRef * @param {?} _platform * @param {?} ngControl * @param {?} _parentForm * @param {?} _parentFormGroup * @param {?} _defaultErrorStateMatcher * @param {?} inputValueAccessor * @param {?} _autofillMonitor * @param {?} ngZone */ constructor(_elementRef, _platform, /** @docs-private */ ngControl, _parentForm, _parentFormGroup, _defaultErrorStateMatcher, inputValueAccessor, _autofillMonitor, ngZone) { super(_defaultErrorStateMatcher, _parentForm, _parentFormGroup, ngControl); this._elementRef = _elementRef; this._platform = _platform; this.ngControl = ngControl; this._autofillMonitor = _autofillMonitor; this._uid = `mat-input-${nextUniqueId++}`; /** * Whether the component is being rendered on the server. */ this._isServer = false; /** * Implemented as part of MatFormFieldControl. * \@docs-private */ this.focused = false; /** * Implemented as part of MatFormFieldControl. * \@docs-private */ this.stateChanges = new Subject(); /** * Implemented as part of MatFormFieldControl. * \@docs-private */ this.controlType = 'mat-input'; /** * Implemented as part of MatFormFieldControl. * \@docs-private */ this.autofilled = false; this._disabled = false; this._required = false; this._type = 'text'; this._readonly = false; this._neverEmptyInputTypes = [ 'date', 'datetime', 'datetime-local', 'month', 'time', 'week' ].filter(t => getSupportedInputTypes().has(t)); // If no input value accessor was explicitly specified, use the element as the input value // accessor. this._inputValueAccessor = inputValueAccessor || this._elementRef.nativeElement; this._previousNativeValue = this.value; // Force setter to be called in case id was not specified. this.id = this.id; // On some versions of iOS the caret gets stuck in the wrong place when holding down the delete // key. In order to get around this we need to "jiggle" the caret loose. Since this bug only // exists on iOS, we only bother to install the listener on iOS. if (_platform.IOS) { ngZone.runOutsideAngular(() => { _elementRef.nativeElement.addEventListener('keyup', (event) => { let /** @type {?} */ el = /** @type {?} */ (event.target); if (!el.value && !el.selectionStart && !el.selectionEnd) { // Note: Just setting `0, 0` doesn't fix the issue. Setting // `1, 1` fixes it for the first time that you type text and // then hold delete. Toggling to `1, 1` and then back to // `0, 0` seems to completely fix it. el.setSelectionRange(1, 1); el.setSelectionRange(0, 0); } }); }); } this._isServer = !this._platform.isBrowser; } /** * Implemented as part of MatFormFieldControl. * \@docs-private * @return {?} */ get disabled() { if (this.ngControl && this.ngControl.disabled !== null) { return this.ngControl.disabled; } return this._disabled; } /** * @param {?} value * @return {?} */ set disabled(value) { this._disabled = coerceBooleanProperty(value); // Browsers may not fire the blur event if the input is disabled too quickly. // Reset from here to ensure that the element doesn't become stuck. if (this.focused) { this.focused = false; this.stateChanges.next(); } } /** * Implemented as part of MatFormFieldControl. * \@docs-private * @return {?} */ get id() { return this._id; } /** * @param {?} value * @return {?} */ set id(value) { this._id = value || this._uid; } /** * Implemented as part of MatFormFieldControl. * \@docs-private * @return {?} */ get required() { return this._required; } /** * @param {?} value * @return {?} */ set required(value) { this._required = coerceBooleanProperty(value); } /** * Input type of the element. * @return {?} */ get type() { return this._type; } /** * @param {?} value * @return {?} */ set type(value) { this._type = value || 'text'; this._validateType(); // When using Angular inputs, developers are no longer able to set the properties on the native // input element. To ensure that bindings for `type` work, we need to sync the setter // with the native property. Textarea elements don't support the type property or attribute. if (!this._isTextarea() && getSupportedInputTypes().has(this._type)) { this._elementRef.nativeElement.type = this._type; } } /** * Implemented as part of MatFormFieldControl. * \@docs-private * @return {?} */ get value() { return this._inputValueAccessor.value; } /** * @param {?} value * @return {?} */ set value(value) { if (value !== this.value) { this._inputValueAccessor.value = value; this.stateChanges.next(); } } /** * Whether the element is readonly. * @return {?} */ get readonly() { return this._readonly; } /** * @param {?} value * @return {?} */ set readonly(value) { this._readonly = coerceBooleanProperty(value); } /** * @return {?} */ ngOnInit() { this._autofillMonitor.monitor(this._elementRef.nativeElement).subscribe(event => { this.autofilled = event.isAutofilled; this.stateChanges.next(); }); } /** * @return {?} */ ngOnChanges() { this.stateChanges.next(); } /** * @return {?} */ ngOnDestroy() { this.stateChanges.complete(); this._autofillMonitor.stopMonitoring(this._elementRef.nativeElement); } /** * @return {?} */ ngDoCheck() { if (this.ngControl) { // We need to re-evaluate this on every change detection cycle, because there are some // error triggers that we can't subscribe to (e.g. parent form submissions). This means // that whatever logic is in here has to be super lean or we risk destroying the performance. this.updateErrorState(); } // We need to dirty-check the native element's value, because there are some cases where // we won't be notified when it changes (e.g. the consumer isn't using forms or they're // updating the value using `emitEvent: false`). this._dirtyCheckNativeValue(); } /** * Focuses the input. * @return {?} */ focus() { this._elementRef.nativeElement.focus(); } /** * Callback for the cases where the focused state of the input changes. * @param {?} isFocused * @return {?} */ _focusChanged(isFocused) { if (isFocused !== this.focused && !this.readonly) { this.focused = isFocused; this.stateChanges.next(); } } /** * @return {?} */ _onInput() { // This is a noop function and is used to let Angular know whenever the value changes. // Angular will run a new change detection each time the `input` event has been dispatched. // It's necessary that Angular recognizes the value change, because when floatingLabel // is set to false and Angular forms aren't used, the placeholder won't recognize the // value changes and will not disappear. // Listening to the input event wouldn't be necessary when the input is using the // FormsModule or ReactiveFormsModule, because Angular forms also listens to input events. } /** * Does some manual dirty checking on the native input `value` property. * @return {?} */ _dirtyCheckNativeValue() { const /** @type {?} */ newValue = this.value; if (this._previousNativeValue !== newValue) { this._previousNativeValue = newValue; this.stateChanges.next(); } } /** * Make sure the input is a supported type. * @return {?} */ _validateType() { if (MAT_INPUT_INVALID_TYPES.indexOf(this._type) > -1) { throw getMatInputUnsupportedTypeError(this._type); } } /** * Checks whether the input type is one of the types that are never empty. * @return {?} */ _isNeverEmpty() { return this._neverEmptyInputTypes.indexOf(this._type) > -1; } /** * Checks whether the input is invalid based on the native validation. * @return {?} */ _isBadInput() { // The `validity` property won't be present on platform-server. let /** @type {?} */ validity = (/** @type {?} */ (this._elementRef.nativeElement)).validity; return validity && validity.badInput; } /** * Determines if the component host is a textarea. * @return {?} */ _isTextarea() { return this._elementRef.nativeElement.nodeName.toLowerCase() === 'textarea'; } /** * Implemented as part of MatFormFieldControl. * \@docs-private * @return {?} */ get empty() { return !this._isNeverEmpty() && !this._elementRef.nativeElement.value && !this._isBadInput() && !this.autofilled; } /** * Implemented as part of MatFormFieldControl. * \@docs-private * @return {?} */ get shouldLabelFloat() { return this.focused || !this.empty; } /** * Implemented as part of MatFormFieldControl. * \@docs-private * @param {?} ids * @return {?} */ setDescribedByIds(ids) { this._ariaDescribedby = ids.join(' '); } /** * Implemented as part of MatFormFieldControl. * \@docs-private * @return {?} */ onContainerClick() { this.focus(); } } MatInput.decorators = [ { type: Directive, args: [{ selector: `input[matInput], textarea[matInput]`, exportAs: 'matInput', host: { /** * @deletion-target 7.0.0 remove .mat-form-field-autofill-control in favor of AutofillMonitor. */ 'class': 'mat-input-element mat-form-field-autofill-control', '[class.mat-input-server]': '_isServer', // Native input properties that are overwritten by Angular inputs need to be synced with // the native input element. Otherwise property bindings for those don't work. '[attr.id]': 'id', '[attr.placeholder]': 'placeholder', '[disabled]': 'disabled', '[required]': 'required', '[readonly]': 'readonly', '[attr.aria-describedby]': '_ariaDescribedby || null', '[attr.aria-invalid]': 'errorState', '[attr.aria-required]': 'required.toString()', '(blur)': '_focusChanged(false)', '(focus)': '_focusChanged(true)', '(input)': '_onInput()', }, providers: [{ provide: MatFormFieldControl, useExisting: MatInput }], },] }, ]; /** @nocollapse */ MatInput.ctorParameters = () => [ { type: ElementRef, }, { type: Platform, }, { type: NgControl, decorators: [{ type: Optional }, { type: Self },] }, { type: NgForm, decorators: [{ type: Optional },] }, { type: FormGroupDirective, decorators: [{ type: Optional },] }, { type: ErrorStateMatcher, }, { type: undefined, decorators: [{ type: Optional }, { type: Self }, { type: Inject, args: [MAT_INPUT_VALUE_ACCESSOR,] },] }, { type: AutofillMonitor, }, { type: NgZone, }, ]; MatInput.propDecorators = { "disabled": [{ type: Input },], "id": [{ type: Input },], "placeholder": [{ type: Input },], "required": [{ type: Input },], "type": [{ type: Input },], "errorStateMatcher": [{ type: Input },], "value": [{ type: Input },], "readonly": [{ type: Input },], }; /** * @fileoverview added by tsickle * @suppress {checkTypes} checked by tsc */ class MatInputModule { } MatInputModule.decorators = [ { type: NgModule, args: [{ declarations: [MatInput, MatTextareaAutosize], imports: [ CommonModule, TextFieldModule, MatFormFieldModule, ], exports: [ TextFieldModule, MatFormFieldModule, MatInput, MatTextareaAutosize, ], providers: [ErrorStateMatcher], },] }, ]; /** * @fileoverview added by tsickle * @suppress {checkTypes} checked by tsc */ /** * @fileoverview added by tsickle * @suppress {checkTypes} checked by tsc */ export { MatTextareaAutosize, MatInputBase, _MatInputMixinBase, MatInput, getMatInputUnsupportedTypeError, MatInputModule, MAT_INPUT_VALUE_ACCESSOR }; //# sourceMappingURL=input.js.map