| /** |
| * @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 { coerceBooleanProperty } from '@angular/cdk/coercion'; |
| import { BACKSPACE, DELETE, SPACE, END, HOME, hasModifierKey, TAB, ENTER } from '@angular/cdk/keycodes'; |
| import { Platform } from '@angular/cdk/platform'; |
| import { ContentChild, Directive, ElementRef, EventEmitter, forwardRef, Inject, Input, NgZone, Optional, Output, InjectionToken, ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChildren, Self, ViewEncapsulation, NgModule } from '@angular/core'; |
| import { MAT_RIPPLE_GLOBAL_OPTIONS, mixinColor, mixinDisabled, mixinDisableRipple, RippleRenderer, ErrorStateMatcher, mixinErrorState } from '@angular/material/core'; |
| import { Subject, merge } from 'rxjs'; |
| import { take, startWith, takeUntil } from 'rxjs/operators'; |
| import { ANIMATION_MODULE_TYPE } from '@angular/platform-browser/animations'; |
| import { FocusKeyManager } from '@angular/cdk/a11y'; |
| import { Directionality } from '@angular/cdk/bidi'; |
| import { SelectionModel } from '@angular/cdk/collections'; |
| import { FormGroupDirective, NgControl, NgForm } from '@angular/forms'; |
| import { MatFormFieldControl } from '@angular/material/form-field'; |
| |
| /** |
| * @fileoverview added by tsickle |
| * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc |
| */ |
| /** |
| * Event object emitted by MatChip when selected or deselected. |
| */ |
| class MatChipSelectionChange { |
| /** |
| * @param {?} source |
| * @param {?} selected |
| * @param {?=} isUserInput |
| */ |
| constructor(source, selected, isUserInput = false) { |
| this.source = source; |
| this.selected = selected; |
| this.isUserInput = isUserInput; |
| } |
| } |
| // Boilerplate for applying mixins to MatChip. |
| /** |
| * \@docs-private |
| */ |
| class MatChipBase { |
| /** |
| * @param {?} _elementRef |
| */ |
| constructor(_elementRef) { |
| this._elementRef = _elementRef; |
| } |
| } |
| /** @type {?} */ |
| const _MatChipMixinBase = mixinColor(mixinDisableRipple(mixinDisabled(MatChipBase)), 'primary'); |
| /** |
| * Dummy directive to add CSS class to chip avatar. |
| * \@docs-private |
| */ |
| class MatChipAvatar { |
| } |
| MatChipAvatar.decorators = [ |
| { type: Directive, args: [{ |
| selector: 'mat-chip-avatar, [matChipAvatar]', |
| host: { 'class': 'mat-chip-avatar' } |
| },] }, |
| ]; |
| /** |
| * Dummy directive to add CSS class to chip trailing icon. |
| * \@docs-private |
| */ |
| class MatChipTrailingIcon { |
| } |
| MatChipTrailingIcon.decorators = [ |
| { type: Directive, args: [{ |
| selector: 'mat-chip-trailing-icon, [matChipTrailingIcon]', |
| host: { 'class': 'mat-chip-trailing-icon' } |
| },] }, |
| ]; |
| /** |
| * Material design styled Chip component. Used inside the MatChipList component. |
| */ |
| class MatChip extends _MatChipMixinBase { |
| /** |
| * @param {?} _elementRef |
| * @param {?} _ngZone |
| * @param {?} platform |
| * @param {?} globalRippleOptions |
| * @param {?=} animationMode |
| */ |
| constructor(_elementRef, _ngZone, platform, globalRippleOptions, |
| // @breaking-change 8.0.0 `animationMode` parameter to become required. |
| animationMode) { |
| super(_elementRef); |
| this._elementRef = _elementRef; |
| this._ngZone = _ngZone; |
| /** |
| * Whether the chip has focus. |
| */ |
| this._hasFocus = false; |
| /** |
| * Whether the chip list is selectable |
| */ |
| this.chipListSelectable = true; |
| /** |
| * Whether the chip list is in multi-selection mode. |
| */ |
| this._chipListMultiple = false; |
| this._selected = false; |
| this._selectable = true; |
| this._removable = true; |
| /** |
| * Emits when the chip is focused. |
| */ |
| this._onFocus = new Subject(); |
| /** |
| * Emits when the chip is blured. |
| */ |
| this._onBlur = new Subject(); |
| /** |
| * Emitted when the chip is selected or deselected. |
| */ |
| this.selectionChange = new EventEmitter(); |
| /** |
| * Emitted when the chip is destroyed. |
| */ |
| this.destroyed = new EventEmitter(); |
| /** |
| * Emitted when a chip is to be removed. |
| */ |
| this.removed = new EventEmitter(); |
| this._addHostClassName(); |
| this._chipRipple = new RippleRenderer(this, _ngZone, _elementRef, platform); |
| this._chipRipple.setupTriggerEvents(_elementRef.nativeElement); |
| this.rippleConfig = globalRippleOptions || {}; |
| this._animationsDisabled = animationMode === 'NoopAnimations'; |
| } |
| /** |
| * Whether ripples are disabled on interaction |
| * \@docs-private |
| * @return {?} |
| */ |
| get rippleDisabled() { |
| return this.disabled || this.disableRipple || !!this.rippleConfig.disabled; |
| } |
| /** |
| * Whether the chip is selected. |
| * @return {?} |
| */ |
| get selected() { return this._selected; } |
| /** |
| * @param {?} value |
| * @return {?} |
| */ |
| set selected(value) { |
| /** @type {?} */ |
| const coercedValue = coerceBooleanProperty(value); |
| if (coercedValue !== this._selected) { |
| this._selected = coercedValue; |
| this._dispatchSelectionChange(); |
| } |
| } |
| /** |
| * The value of the chip. Defaults to the content inside `<mat-chip>` tags. |
| * @return {?} |
| */ |
| get value() { |
| return this._value != undefined |
| ? this._value |
| : this._elementRef.nativeElement.textContent; |
| } |
| /** |
| * @param {?} value |
| * @return {?} |
| */ |
| set value(value) { this._value = value; } |
| /** |
| * Whether or not the chip is selectable. When a chip is not selectable, |
| * changes to its selected state are always ignored. By default a chip is |
| * selectable, and it becomes non-selectable if its parent chip list is |
| * not selectable. |
| * @return {?} |
| */ |
| get selectable() { return this._selectable && this.chipListSelectable; } |
| /** |
| * @param {?} value |
| * @return {?} |
| */ |
| set selectable(value) { |
| this._selectable = coerceBooleanProperty(value); |
| } |
| /** |
| * Determines whether or not the chip displays the remove styling and emits (removed) events. |
| * @return {?} |
| */ |
| get removable() { return this._removable; } |
| /** |
| * @param {?} value |
| * @return {?} |
| */ |
| set removable(value) { |
| this._removable = coerceBooleanProperty(value); |
| } |
| /** |
| * The ARIA selected applied to the chip. |
| * @return {?} |
| */ |
| get ariaSelected() { |
| // Remove the `aria-selected` when the chip is deselected in single-selection mode, because |
| // it adds noise to NVDA users where "not selected" will be read out for each chip. |
| return this.selectable && (this._chipListMultiple || this.selected) ? |
| this.selected.toString() : null; |
| } |
| /** |
| * @return {?} |
| */ |
| _addHostClassName() { |
| /** @type {?} */ |
| const basicChipAttrName = 'mat-basic-chip'; |
| /** @type {?} */ |
| const element = (/** @type {?} */ (this._elementRef.nativeElement)); |
| if (element.hasAttribute(basicChipAttrName) || |
| element.tagName.toLowerCase() === basicChipAttrName) { |
| element.classList.add(basicChipAttrName); |
| return; |
| } |
| else { |
| element.classList.add('mat-standard-chip'); |
| } |
| } |
| /** |
| * @return {?} |
| */ |
| ngOnDestroy() { |
| this.destroyed.emit({ chip: this }); |
| this._chipRipple._removeTriggerEvents(); |
| } |
| /** |
| * Selects the chip. |
| * @return {?} |
| */ |
| select() { |
| if (!this._selected) { |
| this._selected = true; |
| this._dispatchSelectionChange(); |
| } |
| } |
| /** |
| * Deselects the chip. |
| * @return {?} |
| */ |
| deselect() { |
| if (this._selected) { |
| this._selected = false; |
| this._dispatchSelectionChange(); |
| } |
| } |
| /** |
| * Select this chip and emit selected event |
| * @return {?} |
| */ |
| selectViaInteraction() { |
| if (!this._selected) { |
| this._selected = true; |
| this._dispatchSelectionChange(true); |
| } |
| } |
| /** |
| * Toggles the current selected state of this chip. |
| * @param {?=} isUserInput |
| * @return {?} |
| */ |
| toggleSelected(isUserInput = false) { |
| this._selected = !this.selected; |
| this._dispatchSelectionChange(isUserInput); |
| return this.selected; |
| } |
| /** |
| * Allows for programmatic focusing of the chip. |
| * @return {?} |
| */ |
| focus() { |
| if (!this._hasFocus) { |
| this._elementRef.nativeElement.focus(); |
| this._onFocus.next({ chip: this }); |
| } |
| this._hasFocus = true; |
| } |
| /** |
| * Allows for programmatic removal of the chip. Called by the MatChipList when the DELETE or |
| * BACKSPACE keys are pressed. |
| * |
| * Informs any listeners of the removal request. Does not remove the chip from the DOM. |
| * @return {?} |
| */ |
| remove() { |
| if (this.removable) { |
| this.removed.emit({ chip: this }); |
| } |
| } |
| /** |
| * Handles click events on the chip. |
| * @param {?} event |
| * @return {?} |
| */ |
| _handleClick(event) { |
| if (this.disabled) { |
| event.preventDefault(); |
| } |
| else { |
| event.stopPropagation(); |
| } |
| } |
| /** |
| * Handle custom key presses. |
| * @param {?} event |
| * @return {?} |
| */ |
| _handleKeydown(event) { |
| if (this.disabled) { |
| return; |
| } |
| switch (event.keyCode) { |
| case DELETE: |
| case BACKSPACE: |
| // If we are removable, remove the focused chip |
| this.remove(); |
| // Always prevent so page navigation does not occur |
| event.preventDefault(); |
| break; |
| case SPACE: |
| // If we are selectable, toggle the focused chip |
| if (this.selectable) { |
| this.toggleSelected(true); |
| } |
| // Always prevent space from scrolling the page since the list has focus |
| event.preventDefault(); |
| break; |
| } |
| } |
| /** |
| * @return {?} |
| */ |
| _blur() { |
| // When animations are enabled, Angular may end up removing the chip from the DOM a little |
| // earlier than usual, causing it to be blurred and throwing off the logic in the chip list |
| // that moves focus not the next item. To work around the issue, we defer marking the chip |
| // as not focused until the next time the zone stabilizes. |
| this._ngZone.onStable |
| .asObservable() |
| .pipe(take(1)) |
| .subscribe((/** |
| * @return {?} |
| */ |
| () => { |
| this._ngZone.run((/** |
| * @return {?} |
| */ |
| () => { |
| this._hasFocus = false; |
| this._onBlur.next({ chip: this }); |
| })); |
| })); |
| } |
| /** |
| * @private |
| * @param {?=} isUserInput |
| * @return {?} |
| */ |
| _dispatchSelectionChange(isUserInput = false) { |
| this.selectionChange.emit({ |
| source: this, |
| isUserInput, |
| selected: this._selected |
| }); |
| } |
| } |
| MatChip.decorators = [ |
| { type: Directive, args: [{ |
| selector: `mat-basic-chip, [mat-basic-chip], mat-chip, [mat-chip]`, |
| inputs: ['color', 'disabled', 'disableRipple'], |
| exportAs: 'matChip', |
| host: { |
| 'class': 'mat-chip', |
| '[attr.tabindex]': 'disabled ? null : -1', |
| 'role': 'option', |
| '[class.mat-chip-selected]': 'selected', |
| '[class.mat-chip-with-avatar]': 'avatar', |
| '[class.mat-chip-with-trailing-icon]': 'trailingIcon || removeIcon', |
| '[class.mat-chip-disabled]': 'disabled', |
| '[class._mat-animation-noopable]': '_animationsDisabled', |
| '[attr.disabled]': 'disabled || null', |
| '[attr.aria-disabled]': 'disabled.toString()', |
| '[attr.aria-selected]': 'ariaSelected', |
| '(click)': '_handleClick($event)', |
| '(keydown)': '_handleKeydown($event)', |
| '(focus)': 'focus()', |
| '(blur)': '_blur()', |
| }, |
| },] }, |
| ]; |
| /** @nocollapse */ |
| MatChip.ctorParameters = () => [ |
| { type: ElementRef }, |
| { type: NgZone }, |
| { type: Platform }, |
| { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [MAT_RIPPLE_GLOBAL_OPTIONS,] }] }, |
| { type: String, decorators: [{ type: Optional }, { type: Inject, args: [ANIMATION_MODULE_TYPE,] }] } |
| ]; |
| MatChip.propDecorators = { |
| avatar: [{ type: ContentChild, args: [MatChipAvatar, { static: false },] }], |
| trailingIcon: [{ type: ContentChild, args: [MatChipTrailingIcon, { static: false },] }], |
| removeIcon: [{ type: ContentChild, args: [forwardRef((/** |
| * @return {?} |
| */ |
| () => MatChipRemove)), { static: false },] }], |
| selected: [{ type: Input }], |
| value: [{ type: Input }], |
| selectable: [{ type: Input }], |
| removable: [{ type: Input }], |
| selectionChange: [{ type: Output }], |
| destroyed: [{ type: Output }], |
| removed: [{ type: Output }] |
| }; |
| /** |
| * Applies proper (click) support and adds styling for use with the Material Design "cancel" icon |
| * available at https://material.io/icons/#ic_cancel. |
| * |
| * Example: |
| * |
| * `<mat-chip> |
| * <mat-icon matChipRemove>cancel</mat-icon> |
| * </mat-chip>` |
| * |
| * You *may* use a custom icon, but you may need to override the `mat-chip-remove` positioning |
| * styles to properly center the icon within the chip. |
| */ |
| class MatChipRemove { |
| /** |
| * @param {?} _parentChip |
| */ |
| constructor(_parentChip) { |
| this._parentChip = _parentChip; |
| } |
| /** |
| * Calls the parent chip's public `remove()` method if applicable. |
| * @param {?} event |
| * @return {?} |
| */ |
| _handleClick(event) { |
| /** @type {?} */ |
| const parentChip = this._parentChip; |
| if (parentChip.removable && !parentChip.disabled) { |
| parentChip.remove(); |
| } |
| // We need to stop event propagation because otherwise the event will bubble up to the |
| // form field and cause the `onContainerClick` method to be invoked. This method would then |
| // reset the focused chip that has been focused after chip removal. Usually the parent |
| // the parent click listener of the `MatChip` would prevent propagation, but it can happen |
| // that the chip is being removed before the event bubbles up. |
| event.stopPropagation(); |
| } |
| } |
| MatChipRemove.decorators = [ |
| { type: Directive, args: [{ |
| selector: '[matChipRemove]', |
| host: { |
| 'class': 'mat-chip-remove mat-chip-trailing-icon', |
| '(click)': '_handleClick($event)', |
| } |
| },] }, |
| ]; |
| /** @nocollapse */ |
| MatChipRemove.ctorParameters = () => [ |
| { type: MatChip } |
| ]; |
| |
| /** |
| * @fileoverview added by tsickle |
| * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc |
| */ |
| /** |
| * Injection token to be used to override the default options for the chips module. |
| * @type {?} |
| */ |
| const MAT_CHIPS_DEFAULT_OPTIONS = new InjectionToken('mat-chips-default-options'); |
| |
| /** |
| * @fileoverview added by tsickle |
| * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc |
| */ |
| // Boilerplate for applying mixins to MatChipList. |
| /** |
| * \@docs-private |
| */ |
| class MatChipListBase { |
| /** |
| * @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; |
| } |
| } |
| /** @type {?} */ |
| const _MatChipListMixinBase = mixinErrorState(MatChipListBase); |
| // Increasing integer for generating unique ids for chip-list components. |
| /** @type {?} */ |
| let nextUniqueId = 0; |
| /** |
| * Change event object that is emitted when the chip list value has changed. |
| */ |
| class MatChipListChange { |
| /** |
| * @param {?} source |
| * @param {?} value |
| */ |
| constructor(source, value) { |
| this.source = source; |
| this.value = value; |
| } |
| } |
| /** |
| * A material design chips component (named ChipList for its similarity to the List component). |
| */ |
| class MatChipList extends _MatChipListMixinBase { |
| /** |
| * @param {?} _elementRef |
| * @param {?} _changeDetectorRef |
| * @param {?} _dir |
| * @param {?} _parentForm |
| * @param {?} _parentFormGroup |
| * @param {?} _defaultErrorStateMatcher |
| * @param {?} ngControl |
| */ |
| constructor(_elementRef, _changeDetectorRef, _dir, _parentForm, _parentFormGroup, _defaultErrorStateMatcher, ngControl) { |
| super(_defaultErrorStateMatcher, _parentForm, _parentFormGroup, ngControl); |
| this._elementRef = _elementRef; |
| this._changeDetectorRef = _changeDetectorRef; |
| this._dir = _dir; |
| this.ngControl = ngControl; |
| /** |
| * Implemented as part of MatFormFieldControl. |
| * \@docs-private |
| */ |
| this.controlType = 'mat-chip-list'; |
| /** |
| * When a chip is destroyed, we store the index of the destroyed chip until the chips |
| * query list notifies about the update. This is necessary because we cannot determine an |
| * appropriate chip that should receive focus until the array of chips updated completely. |
| */ |
| this._lastDestroyedChipIndex = null; |
| /** |
| * Subject that emits when the component has been destroyed. |
| */ |
| this._destroyed = new Subject(); |
| /** |
| * Uid of the chip list |
| */ |
| this._uid = `mat-chip-list-${nextUniqueId++}`; |
| /** |
| * Tab index for the chip list. |
| */ |
| this._tabIndex = 0; |
| /** |
| * User defined tab index. |
| * When it is not null, use user defined tab index. Otherwise use _tabIndex |
| */ |
| this._userTabIndex = null; |
| /** |
| * Function when touched |
| */ |
| this._onTouched = (/** |
| * @return {?} |
| */ |
| () => { }); |
| /** |
| * Function when changed |
| */ |
| this._onChange = (/** |
| * @return {?} |
| */ |
| () => { }); |
| this._multiple = false; |
| this._compareWith = (/** |
| * @param {?} o1 |
| * @param {?} o2 |
| * @return {?} |
| */ |
| (o1, o2) => o1 === o2); |
| this._required = false; |
| this._disabled = false; |
| /** |
| * Orientation of the chip list. |
| */ |
| this.ariaOrientation = 'horizontal'; |
| this._selectable = true; |
| /** |
| * Event emitted when the selected chip list value has been changed by the user. |
| */ |
| this.change = new EventEmitter(); |
| /** |
| * Event that emits whenever the raw value of the chip-list changes. This is here primarily |
| * to facilitate the two-way binding for the `value` input. |
| * \@docs-private |
| */ |
| this.valueChange = new EventEmitter(); |
| if (this.ngControl) { |
| this.ngControl.valueAccessor = this; |
| } |
| } |
| /** |
| * The array of selected chips inside chip list. |
| * @return {?} |
| */ |
| get selected() { |
| return this.multiple ? this._selectionModel.selected : this._selectionModel.selected[0]; |
| } |
| /** |
| * The ARIA role applied to the chip list. |
| * @return {?} |
| */ |
| get role() { return this.empty ? null : 'listbox'; } |
| /** |
| * Whether the user should be allowed to select multiple chips. |
| * @return {?} |
| */ |
| get multiple() { return this._multiple; } |
| /** |
| * @param {?} value |
| * @return {?} |
| */ |
| set multiple(value) { |
| this._multiple = coerceBooleanProperty(value); |
| this._syncChipsState(); |
| } |
| /** |
| * A function to compare the option values with the selected values. The first argument |
| * is a value from an option. The second is a value from the selection. A boolean |
| * should be returned. |
| * @return {?} |
| */ |
| get compareWith() { return this._compareWith; } |
| /** |
| * @param {?} fn |
| * @return {?} |
| */ |
| set compareWith(fn) { |
| this._compareWith = fn; |
| if (this._selectionModel) { |
| // A different comparator means the selection could change. |
| this._initializeSelection(); |
| } |
| } |
| /** |
| * Implemented as part of MatFormFieldControl. |
| * \@docs-private |
| * @return {?} |
| */ |
| get value() { return this._value; } |
| /** |
| * @param {?} value |
| * @return {?} |
| */ |
| set value(value) { |
| this.writeValue(value); |
| this._value = value; |
| } |
| /** |
| * Implemented as part of MatFormFieldControl. |
| * \@docs-private |
| * @return {?} |
| */ |
| get id() { |
| return this._chipInput ? this._chipInput.id : 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); |
| this.stateChanges.next(); |
| } |
| /** |
| * Implemented as part of MatFormFieldControl. |
| * \@docs-private |
| * @return {?} |
| */ |
| get placeholder() { |
| return this._chipInput ? this._chipInput.placeholder : this._placeholder; |
| } |
| /** |
| * @param {?} value |
| * @return {?} |
| */ |
| set placeholder(value) { |
| this._placeholder = value; |
| this.stateChanges.next(); |
| } |
| /** |
| * Whether any chips or the matChipInput inside of this chip-list has focus. |
| * @return {?} |
| */ |
| get focused() { |
| return (this._chipInput && this._chipInput.focused) || this._hasFocusedChip(); |
| } |
| /** |
| * Implemented as part of MatFormFieldControl. |
| * \@docs-private |
| * @return {?} |
| */ |
| get empty() { |
| return (!this._chipInput || this._chipInput.empty) && this.chips.length === 0; |
| } |
| /** |
| * Implemented as part of MatFormFieldControl. |
| * \@docs-private |
| * @return {?} |
| */ |
| get shouldLabelFloat() { return !this.empty || this.focused; } |
| /** |
| * Implemented as part of MatFormFieldControl. |
| * \@docs-private |
| * @return {?} |
| */ |
| get disabled() { return this.ngControl ? !!this.ngControl.disabled : this._disabled; } |
| /** |
| * @param {?} value |
| * @return {?} |
| */ |
| set disabled(value) { |
| this._disabled = coerceBooleanProperty(value); |
| this._syncChipsState(); |
| } |
| /** |
| * Whether or not this chip list is selectable. When a chip list is not selectable, |
| * the selected states for all the chips inside the chip list are always ignored. |
| * @return {?} |
| */ |
| get selectable() { return this._selectable; } |
| /** |
| * @param {?} value |
| * @return {?} |
| */ |
| set selectable(value) { |
| this._selectable = coerceBooleanProperty(value); |
| if (this.chips) { |
| this.chips.forEach((/** |
| * @param {?} chip |
| * @return {?} |
| */ |
| chip => chip.chipListSelectable = this._selectable)); |
| } |
| } |
| /** |
| * @param {?} value |
| * @return {?} |
| */ |
| set tabIndex(value) { |
| this._userTabIndex = value; |
| this._tabIndex = value; |
| } |
| /** |
| * Combined stream of all of the child chips' selection change events. |
| * @return {?} |
| */ |
| get chipSelectionChanges() { |
| return merge(...this.chips.map((/** |
| * @param {?} chip |
| * @return {?} |
| */ |
| chip => chip.selectionChange))); |
| } |
| /** |
| * Combined stream of all of the child chips' focus change events. |
| * @return {?} |
| */ |
| get chipFocusChanges() { |
| return merge(...this.chips.map((/** |
| * @param {?} chip |
| * @return {?} |
| */ |
| chip => chip._onFocus))); |
| } |
| /** |
| * Combined stream of all of the child chips' blur change events. |
| * @return {?} |
| */ |
| get chipBlurChanges() { |
| return merge(...this.chips.map((/** |
| * @param {?} chip |
| * @return {?} |
| */ |
| chip => chip._onBlur))); |
| } |
| /** |
| * Combined stream of all of the child chips' remove change events. |
| * @return {?} |
| */ |
| get chipRemoveChanges() { |
| return merge(...this.chips.map((/** |
| * @param {?} chip |
| * @return {?} |
| */ |
| chip => chip.destroyed))); |
| } |
| /** |
| * @return {?} |
| */ |
| ngAfterContentInit() { |
| this._keyManager = new FocusKeyManager(this.chips) |
| .withWrap() |
| .withVerticalOrientation() |
| .withHorizontalOrientation(this._dir ? this._dir.value : 'ltr'); |
| if (this._dir) { |
| this._dir.change |
| .pipe(takeUntil(this._destroyed)) |
| .subscribe((/** |
| * @param {?} dir |
| * @return {?} |
| */ |
| dir => this._keyManager.withHorizontalOrientation(dir))); |
| } |
| this._keyManager.tabOut.pipe(takeUntil(this._destroyed)).subscribe((/** |
| * @return {?} |
| */ |
| () => { |
| this._allowFocusEscape(); |
| })); |
| // When the list changes, re-subscribe |
| this.chips.changes.pipe(startWith(null), takeUntil(this._destroyed)).subscribe((/** |
| * @return {?} |
| */ |
| () => { |
| if (this.disabled) { |
| // Since this happens after the content has been |
| // checked, we need to defer it to the next tick. |
| Promise.resolve().then((/** |
| * @return {?} |
| */ |
| () => { |
| this._syncChipsState(); |
| })); |
| } |
| this._resetChips(); |
| // Reset chips selected/deselected status |
| this._initializeSelection(); |
| // Check to see if we need to update our tab index |
| this._updateTabIndex(); |
| // Check to see if we have a destroyed chip and need to refocus |
| this._updateFocusForDestroyedChips(); |
| this.stateChanges.next(); |
| })); |
| } |
| /** |
| * @return {?} |
| */ |
| ngOnInit() { |
| this._selectionModel = new SelectionModel(this.multiple, undefined, false); |
| this.stateChanges.next(); |
| } |
| /** |
| * @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(); |
| } |
| } |
| /** |
| * @return {?} |
| */ |
| ngOnDestroy() { |
| this._destroyed.next(); |
| this._destroyed.complete(); |
| this.stateChanges.complete(); |
| this._dropSubscriptions(); |
| } |
| /** |
| * Associates an HTML input element with this chip list. |
| * @param {?} inputElement |
| * @return {?} |
| */ |
| registerInput(inputElement) { |
| this._chipInput = inputElement; |
| } |
| /** |
| * Implemented as part of MatFormFieldControl. |
| * \@docs-private |
| * @param {?} ids |
| * @return {?} |
| */ |
| setDescribedByIds(ids) { this._ariaDescribedby = ids.join(' '); } |
| // Implemented as part of ControlValueAccessor. |
| /** |
| * @param {?} value |
| * @return {?} |
| */ |
| writeValue(value) { |
| if (this.chips) { |
| this._setSelectionByValue(value, false); |
| } |
| } |
| // Implemented as part of ControlValueAccessor. |
| /** |
| * @param {?} fn |
| * @return {?} |
| */ |
| registerOnChange(fn) { |
| this._onChange = fn; |
| } |
| // Implemented as part of ControlValueAccessor. |
| /** |
| * @param {?} fn |
| * @return {?} |
| */ |
| registerOnTouched(fn) { |
| this._onTouched = fn; |
| } |
| // Implemented as part of ControlValueAccessor. |
| /** |
| * @param {?} isDisabled |
| * @return {?} |
| */ |
| setDisabledState(isDisabled) { |
| this.disabled = isDisabled; |
| this.stateChanges.next(); |
| } |
| /** |
| * Implemented as part of MatFormFieldControl. |
| * \@docs-private |
| * @param {?} event |
| * @return {?} |
| */ |
| onContainerClick(event) { |
| if (!this._originatesFromChip(event)) { |
| this.focus(); |
| } |
| } |
| /** |
| * Focuses the first non-disabled chip in this chip list, or the associated input when there |
| * are no eligible chips. |
| * @return {?} |
| */ |
| focus() { |
| if (this.disabled) { |
| return; |
| } |
| // TODO: ARIA says this should focus the first `selected` chip if any are selected. |
| // Focus on first element if there's no chipInput inside chip-list |
| if (this._chipInput && this._chipInput.focused) { |
| // do nothing |
| } |
| else if (this.chips.length > 0) { |
| this._keyManager.setFirstItemActive(); |
| this.stateChanges.next(); |
| } |
| else { |
| this._focusInput(); |
| this.stateChanges.next(); |
| } |
| } |
| /** |
| * Attempt to focus an input if we have one. |
| * @return {?} |
| */ |
| _focusInput() { |
| if (this._chipInput) { |
| this._chipInput.focus(); |
| } |
| } |
| /** |
| * Pass events to the keyboard manager. Available here for tests. |
| * @param {?} event |
| * @return {?} |
| */ |
| _keydown(event) { |
| /** @type {?} */ |
| const target = (/** @type {?} */ (event.target)); |
| // If they are on an empty input and hit backspace, focus the last chip |
| if (event.keyCode === BACKSPACE && this._isInputEmpty(target)) { |
| this._keyManager.setLastItemActive(); |
| event.preventDefault(); |
| } |
| else if (target && target.classList.contains('mat-chip')) { |
| if (event.keyCode === HOME) { |
| this._keyManager.setFirstItemActive(); |
| event.preventDefault(); |
| } |
| else if (event.keyCode === END) { |
| this._keyManager.setLastItemActive(); |
| event.preventDefault(); |
| } |
| else { |
| this._keyManager.onKeydown(event); |
| } |
| this.stateChanges.next(); |
| } |
| } |
| /** |
| * Check the tab index as you should not be allowed to focus an empty list. |
| * @protected |
| * @return {?} |
| */ |
| _updateTabIndex() { |
| // If we have 0 chips, we should not allow keyboard focus |
| this._tabIndex = this._userTabIndex || (this.chips.length === 0 ? -1 : 0); |
| } |
| /** |
| * If the amount of chips changed, we need to update the |
| * key manager state and focus the next closest chip. |
| * @protected |
| * @return {?} |
| */ |
| _updateFocusForDestroyedChips() { |
| // Move focus to the closest chip. If no other chips remain, focus the chip-list itself. |
| if (this._lastDestroyedChipIndex != null) { |
| if (this.chips.length) { |
| /** @type {?} */ |
| const newChipIndex = Math.min(this._lastDestroyedChipIndex, this.chips.length - 1); |
| this._keyManager.setActiveItem(newChipIndex); |
| } |
| else { |
| this.focus(); |
| } |
| } |
| this._lastDestroyedChipIndex = null; |
| } |
| /** |
| * Utility to ensure all indexes are valid. |
| * |
| * @private |
| * @param {?} index The index to be checked. |
| * @return {?} True if the index is valid for our list of chips. |
| */ |
| _isValidIndex(index) { |
| return index >= 0 && index < this.chips.length; |
| } |
| /** |
| * @private |
| * @param {?} element |
| * @return {?} |
| */ |
| _isInputEmpty(element) { |
| if (element && element.nodeName.toLowerCase() === 'input') { |
| /** @type {?} */ |
| let input = (/** @type {?} */ (element)); |
| return !input.value; |
| } |
| return false; |
| } |
| /** |
| * @param {?} value |
| * @param {?=} isUserInput |
| * @return {?} |
| */ |
| _setSelectionByValue(value, isUserInput = true) { |
| this._clearSelection(); |
| this.chips.forEach((/** |
| * @param {?} chip |
| * @return {?} |
| */ |
| chip => chip.deselect())); |
| if (Array.isArray(value)) { |
| value.forEach((/** |
| * @param {?} currentValue |
| * @return {?} |
| */ |
| currentValue => this._selectValue(currentValue, isUserInput))); |
| this._sortValues(); |
| } |
| else { |
| /** @type {?} */ |
| const correspondingChip = this._selectValue(value, isUserInput); |
| // Shift focus to the active item. Note that we shouldn't do this in multiple |
| // mode, because we don't know what chip the user interacted with last. |
| if (correspondingChip) { |
| if (isUserInput) { |
| this._keyManager.setActiveItem(correspondingChip); |
| } |
| } |
| } |
| } |
| /** |
| * Finds and selects the chip based on its value. |
| * @private |
| * @param {?} value |
| * @param {?=} isUserInput |
| * @return {?} Chip that has the corresponding value. |
| */ |
| _selectValue(value, isUserInput = true) { |
| /** @type {?} */ |
| const correspondingChip = this.chips.find((/** |
| * @param {?} chip |
| * @return {?} |
| */ |
| chip => { |
| return chip.value != null && this._compareWith(chip.value, value); |
| })); |
| if (correspondingChip) { |
| isUserInput ? correspondingChip.selectViaInteraction() : correspondingChip.select(); |
| this._selectionModel.select(correspondingChip); |
| } |
| return correspondingChip; |
| } |
| /** |
| * @private |
| * @return {?} |
| */ |
| _initializeSelection() { |
| // Defer setting the value in order to avoid the "Expression |
| // has changed after it was checked" errors from Angular. |
| Promise.resolve().then((/** |
| * @return {?} |
| */ |
| () => { |
| if (this.ngControl || this._value) { |
| this._setSelectionByValue(this.ngControl ? this.ngControl.value : this._value, false); |
| this.stateChanges.next(); |
| } |
| })); |
| } |
| /** |
| * Deselects every chip in the list. |
| * @private |
| * @param {?=} skip Chip that should not be deselected. |
| * @return {?} |
| */ |
| _clearSelection(skip) { |
| this._selectionModel.clear(); |
| this.chips.forEach((/** |
| * @param {?} chip |
| * @return {?} |
| */ |
| chip => { |
| if (chip !== skip) { |
| chip.deselect(); |
| } |
| })); |
| this.stateChanges.next(); |
| } |
| /** |
| * Sorts the model values, ensuring that they keep the same |
| * order that they have in the panel. |
| * @private |
| * @return {?} |
| */ |
| _sortValues() { |
| if (this._multiple) { |
| this._selectionModel.clear(); |
| this.chips.forEach((/** |
| * @param {?} chip |
| * @return {?} |
| */ |
| chip => { |
| if (chip.selected) { |
| this._selectionModel.select(chip); |
| } |
| })); |
| this.stateChanges.next(); |
| } |
| } |
| /** |
| * Emits change event to set the model value. |
| * @private |
| * @param {?=} fallbackValue |
| * @return {?} |
| */ |
| _propagateChanges(fallbackValue) { |
| /** @type {?} */ |
| let valueToEmit = null; |
| if (Array.isArray(this.selected)) { |
| valueToEmit = this.selected.map((/** |
| * @param {?} chip |
| * @return {?} |
| */ |
| chip => chip.value)); |
| } |
| else { |
| valueToEmit = this.selected ? this.selected.value : fallbackValue; |
| } |
| this._value = valueToEmit; |
| this.change.emit(new MatChipListChange(this, valueToEmit)); |
| this.valueChange.emit(valueToEmit); |
| this._onChange(valueToEmit); |
| this._changeDetectorRef.markForCheck(); |
| } |
| /** |
| * When blurred, mark the field as touched when focus moved outside the chip list. |
| * @return {?} |
| */ |
| _blur() { |
| if (!this._hasFocusedChip()) { |
| this._keyManager.setActiveItem(-1); |
| } |
| if (!this.disabled) { |
| if (this._chipInput) { |
| // If there's a chip input, we should check whether the focus moved to chip input. |
| // If the focus is not moved to chip input, mark the field as touched. If the focus moved |
| // to chip input, do nothing. |
| // Timeout is needed to wait for the focus() event trigger on chip input. |
| setTimeout((/** |
| * @return {?} |
| */ |
| () => { |
| if (!this.focused) { |
| this._markAsTouched(); |
| } |
| })); |
| } |
| else { |
| // If there's no chip input, then mark the field as touched. |
| this._markAsTouched(); |
| } |
| } |
| } |
| /** |
| * Mark the field as touched |
| * @return {?} |
| */ |
| _markAsTouched() { |
| this._onTouched(); |
| this._changeDetectorRef.markForCheck(); |
| this.stateChanges.next(); |
| } |
| /** |
| * Removes the `tabindex` from the chip list and resets it back afterwards, allowing the |
| * user to tab out of it. This prevents the list from capturing focus and redirecting |
| * it back to the first chip, creating a focus trap, if it user tries to tab away. |
| * @return {?} |
| */ |
| _allowFocusEscape() { |
| if (this._tabIndex !== -1) { |
| this._tabIndex = -1; |
| setTimeout((/** |
| * @return {?} |
| */ |
| () => { |
| this._tabIndex = this._userTabIndex || 0; |
| this._changeDetectorRef.markForCheck(); |
| })); |
| } |
| } |
| /** |
| * @private |
| * @return {?} |
| */ |
| _resetChips() { |
| this._dropSubscriptions(); |
| this._listenToChipsFocus(); |
| this._listenToChipsSelection(); |
| this._listenToChipsRemoved(); |
| } |
| /** |
| * @private |
| * @return {?} |
| */ |
| _dropSubscriptions() { |
| if (this._chipFocusSubscription) { |
| this._chipFocusSubscription.unsubscribe(); |
| this._chipFocusSubscription = null; |
| } |
| if (this._chipBlurSubscription) { |
| this._chipBlurSubscription.unsubscribe(); |
| this._chipBlurSubscription = null; |
| } |
| if (this._chipSelectionSubscription) { |
| this._chipSelectionSubscription.unsubscribe(); |
| this._chipSelectionSubscription = null; |
| } |
| if (this._chipRemoveSubscription) { |
| this._chipRemoveSubscription.unsubscribe(); |
| this._chipRemoveSubscription = null; |
| } |
| } |
| /** |
| * Listens to user-generated selection events on each chip. |
| * @private |
| * @return {?} |
| */ |
| _listenToChipsSelection() { |
| this._chipSelectionSubscription = this.chipSelectionChanges.subscribe((/** |
| * @param {?} event |
| * @return {?} |
| */ |
| event => { |
| event.source.selected |
| ? this._selectionModel.select(event.source) |
| : this._selectionModel.deselect(event.source); |
| // For single selection chip list, make sure the deselected value is unselected. |
| if (!this.multiple) { |
| this.chips.forEach((/** |
| * @param {?} chip |
| * @return {?} |
| */ |
| chip => { |
| if (!this._selectionModel.isSelected(chip) && chip.selected) { |
| chip.deselect(); |
| } |
| })); |
| } |
| if (event.isUserInput) { |
| this._propagateChanges(); |
| } |
| })); |
| } |
| /** |
| * Listens to user-generated selection events on each chip. |
| * @private |
| * @return {?} |
| */ |
| _listenToChipsFocus() { |
| this._chipFocusSubscription = this.chipFocusChanges.subscribe((/** |
| * @param {?} event |
| * @return {?} |
| */ |
| event => { |
| /** @type {?} */ |
| let chipIndex = this.chips.toArray().indexOf(event.chip); |
| if (this._isValidIndex(chipIndex)) { |
| this._keyManager.updateActiveItemIndex(chipIndex); |
| } |
| this.stateChanges.next(); |
| })); |
| this._chipBlurSubscription = this.chipBlurChanges.subscribe((/** |
| * @return {?} |
| */ |
| () => { |
| this._blur(); |
| this.stateChanges.next(); |
| })); |
| } |
| /** |
| * @private |
| * @return {?} |
| */ |
| _listenToChipsRemoved() { |
| this._chipRemoveSubscription = this.chipRemoveChanges.subscribe((/** |
| * @param {?} event |
| * @return {?} |
| */ |
| event => { |
| /** @type {?} */ |
| const chip = event.chip; |
| /** @type {?} */ |
| const chipIndex = this.chips.toArray().indexOf(event.chip); |
| // In case the chip that will be removed is currently focused, we temporarily store |
| // the index in order to be able to determine an appropriate sibling chip that will |
| // receive focus. |
| if (this._isValidIndex(chipIndex) && chip._hasFocus) { |
| this._lastDestroyedChipIndex = chipIndex; |
| } |
| })); |
| } |
| /** |
| * Checks whether an event comes from inside a chip element. |
| * @private |
| * @param {?} event |
| * @return {?} |
| */ |
| _originatesFromChip(event) { |
| /** @type {?} */ |
| let currentElement = (/** @type {?} */ (event.target)); |
| while (currentElement && currentElement !== this._elementRef.nativeElement) { |
| if (currentElement.classList.contains('mat-chip')) { |
| return true; |
| } |
| currentElement = currentElement.parentElement; |
| } |
| return false; |
| } |
| /** |
| * Checks whether any of the chips is focused. |
| * @private |
| * @return {?} |
| */ |
| _hasFocusedChip() { |
| return this.chips.some((/** |
| * @param {?} chip |
| * @return {?} |
| */ |
| chip => chip._hasFocus)); |
| } |
| /** |
| * Syncs the list's state with the individual chips. |
| * @private |
| * @return {?} |
| */ |
| _syncChipsState() { |
| if (this.chips) { |
| this.chips.forEach((/** |
| * @param {?} chip |
| * @return {?} |
| */ |
| chip => { |
| chip.disabled = this._disabled; |
| chip._chipListMultiple = this.multiple; |
| })); |
| } |
| } |
| } |
| MatChipList.decorators = [ |
| { type: Component, args: [{selector: 'mat-chip-list', |
| template: `<div class="mat-chip-list-wrapper"><ng-content></ng-content></div>`, |
| exportAs: 'matChipList', |
| host: { |
| '[attr.tabindex]': 'disabled ? null : _tabIndex', |
| '[attr.aria-describedby]': '_ariaDescribedby || null', |
| '[attr.aria-required]': 'required.toString()', |
| '[attr.aria-disabled]': 'disabled.toString()', |
| '[attr.aria-invalid]': 'errorState', |
| '[attr.aria-multiselectable]': 'multiple', |
| '[attr.role]': 'role', |
| '[class.mat-chip-list-disabled]': 'disabled', |
| '[class.mat-chip-list-invalid]': 'errorState', |
| '[class.mat-chip-list-required]': 'required', |
| '[attr.aria-orientation]': 'ariaOrientation', |
| 'class': 'mat-chip-list', |
| '(focus)': 'focus()', |
| '(blur)': '_blur()', |
| '(keydown)': '_keydown($event)', |
| '[id]': '_uid', |
| }, |
| providers: [{ provide: MatFormFieldControl, useExisting: MatChipList }], |
| styles: [".mat-chip{position:relative;overflow:hidden;box-sizing:border-box;-webkit-tap-highlight-color:transparent;transform:translateZ(0)}.mat-standard-chip{transition:box-shadow 280ms cubic-bezier(.4,0,.2,1);display:inline-flex;padding:7px 12px;border-radius:16px;align-items:center;cursor:default;min-height:32px;height:1px}._mat-animation-noopable.mat-standard-chip{transition:none;animation:none}.mat-standard-chip .mat-chip-remove.mat-icon{width:18px;height:18px}.mat-standard-chip::after{top:0;left:0;right:0;bottom:0;position:absolute;border-radius:inherit;opacity:0;content:'';pointer-events:none;transition:opacity .2s cubic-bezier(.35,0,.25,1)}.mat-standard-chip:hover::after{opacity:.12}.mat-standard-chip:focus{outline:0}.mat-standard-chip:focus::after{opacity:.16}@media (-ms-high-contrast:active){.mat-standard-chip{outline:solid 1px}.mat-standard-chip:focus{outline:dotted 2px}}.mat-standard-chip.mat-chip-disabled::after{opacity:0}.mat-standard-chip.mat-chip-disabled .mat-chip-remove,.mat-standard-chip.mat-chip-disabled .mat-chip-trailing-icon{cursor:default}.mat-standard-chip.mat-chip-with-avatar,.mat-standard-chip.mat-chip-with-trailing-icon.mat-chip-with-avatar{padding-top:0;padding-bottom:0}.mat-standard-chip.mat-chip-with-trailing-icon.mat-chip-with-avatar{padding-right:8px;padding-left:0}[dir=rtl] .mat-standard-chip.mat-chip-with-trailing-icon.mat-chip-with-avatar{padding-left:8px;padding-right:0}.mat-standard-chip.mat-chip-with-trailing-icon{padding-top:7px;padding-bottom:7px;padding-right:8px;padding-left:12px}[dir=rtl] .mat-standard-chip.mat-chip-with-trailing-icon{padding-left:8px;padding-right:12px}.mat-standard-chip.mat-chip-with-avatar{padding-left:0;padding-right:12px}[dir=rtl] .mat-standard-chip.mat-chip-with-avatar{padding-right:0;padding-left:12px}.mat-standard-chip .mat-chip-avatar{width:24px;height:24px;margin-right:8px;margin-left:4px}[dir=rtl] .mat-standard-chip .mat-chip-avatar{margin-left:8px;margin-right:4px}.mat-standard-chip .mat-chip-remove,.mat-standard-chip .mat-chip-trailing-icon{width:18px;height:18px;cursor:pointer}.mat-standard-chip .mat-chip-remove,.mat-standard-chip .mat-chip-trailing-icon{margin-left:8px;margin-right:0}[dir=rtl] .mat-standard-chip .mat-chip-remove,[dir=rtl] .mat-standard-chip .mat-chip-trailing-icon{margin-right:8px;margin-left:0}.mat-chip-list-wrapper{display:flex;flex-direction:row;flex-wrap:wrap;align-items:center;margin:-4px}.mat-chip-list-wrapper .mat-standard-chip,.mat-chip-list-wrapper input.mat-input-element{margin:4px}.mat-chip-list-stacked .mat-chip-list-wrapper{flex-direction:column;align-items:flex-start}.mat-chip-list-stacked .mat-chip-list-wrapper .mat-standard-chip{width:100%}.mat-chip-avatar{border-radius:50%;justify-content:center;align-items:center;display:flex;overflow:hidden;object-fit:cover}input.mat-chip-input{width:150px;margin:4px;flex:1 0 150px}"], |
| encapsulation: ViewEncapsulation.None, |
| changeDetection: ChangeDetectionStrategy.OnPush |
| },] }, |
| ]; |
| /** @nocollapse */ |
| MatChipList.ctorParameters = () => [ |
| { type: ElementRef }, |
| { type: ChangeDetectorRef }, |
| { type: Directionality, decorators: [{ type: Optional }] }, |
| { type: NgForm, decorators: [{ type: Optional }] }, |
| { type: FormGroupDirective, decorators: [{ type: Optional }] }, |
| { type: ErrorStateMatcher }, |
| { type: NgControl, decorators: [{ type: Optional }, { type: Self }] } |
| ]; |
| MatChipList.propDecorators = { |
| errorStateMatcher: [{ type: Input }], |
| multiple: [{ type: Input }], |
| compareWith: [{ type: Input }], |
| value: [{ type: Input }], |
| required: [{ type: Input }], |
| placeholder: [{ type: Input }], |
| disabled: [{ type: Input }], |
| ariaOrientation: [{ type: Input, args: ['aria-orientation',] }], |
| selectable: [{ type: Input }], |
| tabIndex: [{ type: Input }], |
| change: [{ type: Output }], |
| valueChange: [{ type: Output }], |
| chips: [{ type: ContentChildren, args: [MatChip, { |
| // We need to use `descendants: true`, because Ivy will no longer match |
| // indirect descendants if it's left as false. |
| descendants: true |
| },] }] |
| }; |
| |
| /** |
| * @fileoverview added by tsickle |
| * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc |
| */ |
| // Increasing integer for generating unique ids. |
| /** @type {?} */ |
| let nextUniqueId$1 = 0; |
| /** |
| * Directive that adds chip-specific behaviors to an input element inside `<mat-form-field>`. |
| * May be placed inside or outside of an `<mat-chip-list>`. |
| */ |
| class MatChipInput { |
| /** |
| * @param {?} _elementRef |
| * @param {?} _defaultOptions |
| */ |
| constructor(_elementRef, _defaultOptions) { |
| this._elementRef = _elementRef; |
| this._defaultOptions = _defaultOptions; |
| /** |
| * Whether the control is focused. |
| */ |
| this.focused = false; |
| this._addOnBlur = false; |
| /** |
| * The list of key codes that will trigger a chipEnd event. |
| * |
| * Defaults to `[ENTER]`. |
| */ |
| this.separatorKeyCodes = this._defaultOptions.separatorKeyCodes; |
| /** |
| * Emitted when a chip is to be added. |
| */ |
| this.chipEnd = new EventEmitter(); |
| /** |
| * The input's placeholder text. |
| */ |
| this.placeholder = ''; |
| /** |
| * Unique id for the input. |
| */ |
| this.id = `mat-chip-list-input-${nextUniqueId$1++}`; |
| this._disabled = false; |
| this._inputElement = (/** @type {?} */ (this._elementRef.nativeElement)); |
| } |
| /** |
| * Register input for chip list |
| * @param {?} value |
| * @return {?} |
| */ |
| set chipList(value) { |
| if (value) { |
| this._chipList = value; |
| this._chipList.registerInput(this); |
| } |
| } |
| /** |
| * Whether or not the chipEnd event will be emitted when the input is blurred. |
| * @return {?} |
| */ |
| get addOnBlur() { return this._addOnBlur; } |
| /** |
| * @param {?} value |
| * @return {?} |
| */ |
| set addOnBlur(value) { this._addOnBlur = coerceBooleanProperty(value); } |
| /** |
| * Whether the input is disabled. |
| * @return {?} |
| */ |
| get disabled() { return this._disabled || (this._chipList && this._chipList.disabled); } |
| /** |
| * @param {?} value |
| * @return {?} |
| */ |
| set disabled(value) { this._disabled = coerceBooleanProperty(value); } |
| /** |
| * Whether the input is empty. |
| * @return {?} |
| */ |
| get empty() { return !this._inputElement.value; } |
| /** |
| * @return {?} |
| */ |
| ngOnChanges() { |
| this._chipList.stateChanges.next(); |
| } |
| /** |
| * Utility method to make host definition/tests more clear. |
| * @param {?=} event |
| * @return {?} |
| */ |
| _keydown(event) { |
| // Allow the user's focus to escape when they're tabbing forward. Note that we don't |
| // want to do this when going backwards, because focus should go back to the first chip. |
| if (event && event.keyCode === TAB && !hasModifierKey(event, 'shiftKey')) { |
| this._chipList._allowFocusEscape(); |
| } |
| this._emitChipEnd(event); |
| } |
| /** |
| * Checks to see if the blur should emit the (chipEnd) event. |
| * @return {?} |
| */ |
| _blur() { |
| if (this.addOnBlur) { |
| this._emitChipEnd(); |
| } |
| this.focused = false; |
| // Blur the chip list if it is not focused |
| if (!this._chipList.focused) { |
| this._chipList._blur(); |
| } |
| this._chipList.stateChanges.next(); |
| } |
| /** |
| * @return {?} |
| */ |
| _focus() { |
| this.focused = true; |
| this._chipList.stateChanges.next(); |
| } |
| /** |
| * Checks to see if the (chipEnd) event needs to be emitted. |
| * @param {?=} event |
| * @return {?} |
| */ |
| _emitChipEnd(event) { |
| if (!this._inputElement.value && !!event) { |
| this._chipList._keydown(event); |
| } |
| if (!event || this._isSeparatorKey(event)) { |
| this.chipEnd.emit({ input: this._inputElement, value: this._inputElement.value }); |
| if (event) { |
| event.preventDefault(); |
| } |
| } |
| } |
| /** |
| * @return {?} |
| */ |
| _onInput() { |
| // Let chip list know whenever the value changes. |
| this._chipList.stateChanges.next(); |
| } |
| /** |
| * Focuses the input. |
| * @return {?} |
| */ |
| focus() { |
| this._inputElement.focus(); |
| } |
| /** |
| * Checks whether a keycode is one of the configured separators. |
| * @private |
| * @param {?} event |
| * @return {?} |
| */ |
| _isSeparatorKey(event) { |
| if (hasModifierKey(event)) { |
| return false; |
| } |
| /** @type {?} */ |
| const separators = this.separatorKeyCodes; |
| /** @type {?} */ |
| const keyCode = event.keyCode; |
| return Array.isArray(separators) ? separators.indexOf(keyCode) > -1 : separators.has(keyCode); |
| } |
| } |
| MatChipInput.decorators = [ |
| { type: Directive, args: [{ |
| selector: 'input[matChipInputFor]', |
| exportAs: 'matChipInput, matChipInputFor', |
| host: { |
| 'class': 'mat-chip-input mat-input-element', |
| '(keydown)': '_keydown($event)', |
| '(blur)': '_blur()', |
| '(focus)': '_focus()', |
| '(input)': '_onInput()', |
| '[id]': 'id', |
| '[attr.disabled]': 'disabled || null', |
| '[attr.placeholder]': 'placeholder || null', |
| '[attr.aria-invalid]': '_chipList && _chipList.ngControl ? _chipList.ngControl.invalid : null', |
| } |
| },] }, |
| ]; |
| /** @nocollapse */ |
| MatChipInput.ctorParameters = () => [ |
| { type: ElementRef }, |
| { type: undefined, decorators: [{ type: Inject, args: [MAT_CHIPS_DEFAULT_OPTIONS,] }] } |
| ]; |
| MatChipInput.propDecorators = { |
| chipList: [{ type: Input, args: ['matChipInputFor',] }], |
| addOnBlur: [{ type: Input, args: ['matChipInputAddOnBlur',] }], |
| separatorKeyCodes: [{ type: Input, args: ['matChipInputSeparatorKeyCodes',] }], |
| chipEnd: [{ type: Output, args: ['matChipInputTokenEnd',] }], |
| placeholder: [{ type: Input }], |
| id: [{ type: Input }], |
| disabled: [{ type: Input }] |
| }; |
| |
| /** |
| * @fileoverview added by tsickle |
| * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc |
| */ |
| /** @type {?} */ |
| const CHIP_DECLARATIONS = [ |
| MatChipList, |
| MatChip, |
| MatChipInput, |
| MatChipRemove, |
| MatChipAvatar, |
| MatChipTrailingIcon, |
| ]; |
| const ɵ0 = ({ |
| separatorKeyCodes: [ENTER] |
| }); |
| class MatChipsModule { |
| } |
| MatChipsModule.decorators = [ |
| { type: NgModule, args: [{ |
| exports: CHIP_DECLARATIONS, |
| declarations: CHIP_DECLARATIONS, |
| providers: [ |
| ErrorStateMatcher, |
| { |
| provide: MAT_CHIPS_DEFAULT_OPTIONS, |
| useValue: (/** @type {?} */ (ɵ0)) |
| } |
| ] |
| },] }, |
| ]; |
| |
| /** |
| * @fileoverview added by tsickle |
| * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc |
| */ |
| |
| /** |
| * @fileoverview added by tsickle |
| * @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc |
| */ |
| |
| export { MatChipsModule, MatChipListChange, MatChipList, MatChipSelectionChange, MatChipAvatar, MatChipTrailingIcon, MatChip, MatChipRemove, MatChipInput, MAT_CHIPS_DEFAULT_OPTIONS }; |
| //# sourceMappingURL=chips.js.map |