diff --git a/package.json b/package.json
index 29e2a56c338a..d5245b0ec984 100644
--- a/package.json
+++ b/package.json
@@ -35,7 +35,7 @@
},
"version": "8.0.1",
"requiredAngularVersion": "^8.0.0 || ^9.0.0-0",
- "requiredMDCVersion": "^1.1.0",
+ "requiredMDCVersion": "^2.3.1",
"dependencies": {
"@angular/animations": "^8.0.0",
"@angular/common": "^8.0.0",
@@ -47,7 +47,7 @@
"@bazel/buildifier": "^0.25.1",
"@webcomponents/custom-elements": "^1.1.0",
"core-js": "^2.6.1",
- "material-components-web": "^1.1.0",
+ "material-components-web": "^2.3.1",
"rxjs": "^6.4.0",
"systemjs": "0.19.43",
"tsickle": "^0.35.0",
diff --git a/src/dev-app/mdc-chips/mdc-chips-demo-module.ts b/src/dev-app/mdc-chips/mdc-chips-demo-module.ts
index ca5497af6481..00a0594d851c 100644
--- a/src/dev-app/mdc-chips/mdc-chips-demo-module.ts
+++ b/src/dev-app/mdc-chips/mdc-chips-demo-module.ts
@@ -6,14 +6,31 @@
* found in the LICENSE file at https://angular.io/license
*/
+import {CommonModule} from '@angular/common';
import {NgModule} from '@angular/core';
+import {FormsModule, ReactiveFormsModule} from '@angular/forms';
+import {MatButtonModule} from '@angular/material/button';
+import {MatCardModule} from '@angular/material/card';
+import {MatCheckboxModule} from '@angular/material/checkbox';
+import {MatFormFieldModule} from '@angular/material/form-field';
+import {MatToolbarModule} from '@angular/material/toolbar';
import {MatChipsModule} from '@angular/material-experimental/mdc-chips';
+import {MatIconModule} from '@angular/material/icon';
import {RouterModule} from '@angular/router';
import {MdcChipsDemo} from './mdc-chips-demo';
@NgModule({
imports: [
+ CommonModule,
+ FormsModule,
+ MatButtonModule,
+ MatCardModule,
+ MatCheckboxModule,
MatChipsModule,
+ MatFormFieldModule,
+ MatIconModule,
+ MatToolbarModule,
+ ReactiveFormsModule,
RouterModule.forChild([{path: '', component: MdcChipsDemo}]),
],
declarations: [MdcChipsDemo],
diff --git a/src/dev-app/mdc-chips/mdc-chips-demo.html b/src/dev-app/mdc-chips/mdc-chips-demo.html
index 38dbb08da705..48bc859f7cfc 100644
--- a/src/dev-app/mdc-chips/mdc-chips-demo.html
+++ b/src/dev-app/mdc-chips/mdc-chips-demo.html
@@ -1,2 +1,219 @@
-
-Not yet implemented.
+
+
+ Static Chips
+
+
+ Simple
+
+
+ Chip 1
+ Chip 2
+ Chip 3
+
+
+ Unstyled
+
+
+ Basic Chip 1
+ Basic Chip 2
+ Basic Chip 3
+
+
+ With avatar, icons, and color
+
+
+
+ home
+ Home
+ cancel
+
+
+
+ P
+ Portel
+ cancel
+
+
+
+ M
+ Molly
+
+
+
+ Koby
+ cancel
+
+
+
+ Razzle
+
+
+
+
+ Mal
+
+
+
+
+ Husi
+ cancel
+
+
+
+ Good
+ star
+
+
+
+ Bad
+ star_border
+
+
+
+
+ With Events
+
+
+
+ With Events
+ cancel
+
+
+ {{message}}
+
+
+
+
+
+ Selectable Chips
+
+
+
+ {{disabledListboxes ? "Enable" : "Disable"}}
+
+
+ Single selection
+
+
+ Extra Small
+ Small
+ Medium
+ Large
+
+
+ Multi selection
+
+
+ Open Now
+ Takes Reservations
+ Pet Friendly
+ Good for Brunch
+
+
+
+
+
+
+ Input Chips
+
+
+
+ The <mat-chip-grid> component pairs with the matChipInputFor directive
+ to convert user input text into chips.
+ They can be used inside a <mat-form-field>.
+
+
+
+ {{disableInputs ? "Enable" : "Disable"}}
+
+
+ Input is last child of chip grid
+
+
+
+
+ {{person.name}}
+ cancel
+
+
+
+
+
+ Input is next sibling child of chip grid
+
+
+
+
+ {{person.name}}
+ cancel
+
+
+
+
+
+
+ The example above has overridden the [separatorKeys] input to allow for
+ ENTER, COMMA and SEMICOLON keys.
+
+
+ Options
+
+ Add on Blur
+
+
+
+
+
+
+ Miscellaneous
+
+ Stacked
+
+
+ You can also stack the chips if you want them on top of each other.
+
+
+
+
+ {{aColor.name}}
+
+
+
+ NgModel with multi selection
+
+
+
+ {{aColor.name}}
+
+
+
+ The selected colors are
+
+ {{color}}{{isLast ? '' : ', '}} .
+
+ NgModel with single selection
+
+
+
+ {{aColor.name}}
+
+
+
+ The selected color is {{selectedColor}}.
+
+
+
diff --git a/src/dev-app/mdc-chips/mdc-chips-demo.scss b/src/dev-app/mdc-chips/mdc-chips-demo.scss
index 0b1af89fb3c5..c69505383b38 100644
--- a/src/dev-app/mdc-chips/mdc-chips-demo.scss
+++ b/src/dev-app/mdc-chips/mdc-chips-demo.scss
@@ -1 +1,31 @@
-// TODO: copy in demo styles from existing mat-chips demo.
+.demo-chips {
+ .mat-mdc-chip-set-stacked {
+ display: block;
+ max-width: 200px;
+ }
+
+ .mat-card {
+ padding: 0;
+ margin: 16px;
+
+ & .mat-toolbar {
+ margin: 0;
+ }
+
+ & .mat-card-content {
+ padding: 24px;
+ }
+ }
+
+ mat-basic-chip {
+ margin: auto 10px;
+ }
+
+ mat-chip-grid input {
+ width: 150px;
+ }
+}
+
+.demo-has-chip-list {
+ width: 100%;
+}
diff --git a/src/dev-app/mdc-chips/mdc-chips-demo.ts b/src/dev-app/mdc-chips/mdc-chips-demo.ts
index 3f912d7b5307..a3ea5cf4dc4c 100644
--- a/src/dev-app/mdc-chips/mdc-chips-demo.ts
+++ b/src/dev-app/mdc-chips/mdc-chips-demo.ts
@@ -6,7 +6,19 @@
* found in the LICENSE file at https://angular.io/license
*/
+import {COMMA, ENTER} from '@angular/cdk/keycodes';
import {Component} from '@angular/core';
+import {ThemePalette} from '@angular/material/core';
+import {MatChipInputEvent} from '@angular/material-experimental/mdc-chips';
+
+export interface Person {
+ name: string;
+}
+
+export interface DemoColor {
+ name: string;
+ color: ThemePalette;
+}
@Component({
moduleId: module.id,
@@ -15,4 +27,65 @@ import {Component} from '@angular/core';
styleUrls: ['mdc-chips-demo.css'],
})
export class MdcChipsDemo {
+ visible = true;
+ selectable = true;
+ removable = true;
+ addOnBlur = true;
+ disabledListboxes = false;
+ disableInputs = false;
+ message = '';
+
+ // Enter, comma, semi-colon
+ separatorKeysCodes = [ENTER, COMMA, 186];
+
+ selectedPeople = null;
+
+ people: Person[] = [
+ {name: 'Kara'},
+ {name: 'Jeremy'},
+ {name: 'Topher'},
+ {name: 'Elad'},
+ {name: 'Kristiyan'},
+ {name: 'Paul'}
+ ];
+
+ availableColors: DemoColor[] = [
+ {name: 'none', color: undefined},
+ {name: 'Primary', color: 'primary'},
+ {name: 'Accent', color: 'accent'},
+ {name: 'Warn', color: 'warn'}
+ ];
+
+ displayMessage(message: string): void {
+ this.message = message;
+ }
+
+ add(event: MatChipInputEvent): void {
+ const {input, value} = event;
+
+ // Add our person
+ if ((value || '').trim()) {
+ this.people.push({ name: value.trim() });
+ }
+
+ // Reset the input value
+ if (input) {
+ input.value = '';
+ }
+ }
+
+ remove(person: Person): void {
+ const index = this.people.indexOf(person);
+
+ if (index >= 0) {
+ this.people.splice(index, 1);
+ }
+ }
+
+ toggleVisible(): void {
+ this.visible = false;
+ }
+
+ selectedColors: string[] = ['Primary', 'Warn'];
+ selectedColor = 'Accent';
}
diff --git a/src/material-experimental/mdc-chips/BUILD.bazel b/src/material-experimental/mdc-chips/BUILD.bazel
index 096e077509f2..4331d7b77382 100644
--- a/src/material-experimental/mdc-chips/BUILD.bazel
+++ b/src/material-experimental/mdc-chips/BUILD.bazel
@@ -14,8 +14,10 @@ ng_module(
module_name = "@angular/material-experimental/mdc-chips",
deps = [
"//src/material/core",
+ "//src/material/form-field",
"@npm//@angular/common",
"@npm//@angular/core",
+ "@npm//@angular/forms",
"@npm//material-components-web",
],
)
@@ -37,6 +39,7 @@ sass_binary(
"external/npm/node_modules",
],
deps = [
+ "//src/material-experimental/mdc-helpers:mdc_helpers_scss_lib",
"//src/material-experimental/mdc-helpers:mdc_scss_deps_lib",
"//src/material/core:all_themes",
],
diff --git a/src/material-experimental/mdc-chips/_mdc-chips.scss b/src/material-experimental/mdc-chips/_mdc-chips.scss
index 3a9e81463eb2..795a46c337e7 100644
--- a/src/material-experimental/mdc-chips/_mdc-chips.scss
+++ b/src/material-experimental/mdc-chips/_mdc-chips.scss
@@ -1,13 +1,44 @@
+@import '@material/chips/mixins';
@import '../mdc-helpers/mdc-helpers';
+@import '@material/theme/functions';
@mixin mat-chips-theme-mdc($theme) {
- @include mat-using-mdc-theme($theme) {
- // TODO: MDC theme styles here.
+ @include mdc-chip-set-core-styles($query: $mat-theme-styles-query);
+ @include mdc-chip-without-ripple($query: $mat-theme-styles-query);
+
+ $primary: mat-color(map-get($theme, primary));
+ $accent: mat-color(map-get($theme, accent));
+ $warn: mat-color(map-get($theme, warn));
+ $background: map-get($theme, background);
+
+ $unselected-background: mat-color($background, unselected-chip);
+
+ .mat-mdc-chip {
+ @include mdc-chip-fill-color-accessible($unselected-background);
+
+ &.mat-primary {
+ &.mdc-chip--selected, &.mat-mdc-chip-highlighted {
+ @include mdc-chip-fill-color-accessible($primary);
+ }
+ }
+
+ &.mat-accent {
+ &.mdc-chip--selected, &.mat-mdc-chip-highlighted {
+ @include mdc-chip-fill-color-accessible($accent);
+ }
+ }
+
+ &.mat-warn {
+ &.mdc-chip--selected, &.mat-mdc-chip-highlighted {
+ @include mdc-chip-fill-color-accessible($warn);
+ }
+ }
}
}
@mixin mat-chips-typography-mdc($config) {
+ @include mdc-chip-set-core-styles($query: $mat-typography-styles-query);
@include mat-using-mdc-typography($config) {
- // TODO: MDC typography styles here.
+ @include mdc-chip-without-ripple($query: $mat-typography-styles-query);
}
}
diff --git a/src/material-experimental/mdc-chips/chip-cell.ts b/src/material-experimental/mdc-chips/chip-cell.ts
deleted file mode 100644
index 4745d0726503..000000000000
--- a/src/material-experimental/mdc-chips/chip-cell.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-/**
- * @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 {Directive} from '@angular/core';
-
-@Directive({
- selector: 'mat-chip-cell',
- host: {
- 'class': 'mat-mdc-chip-cell',
- },
-})
-export class MatChipCell {
- // TODO: set up MDC foundation class.
-}
diff --git a/src/material-experimental/mdc-chips/chip-default-options.ts b/src/material-experimental/mdc-chips/chip-default-options.ts
new file mode 100644
index 000000000000..2cd5b2838922
--- /dev/null
+++ b/src/material-experimental/mdc-chips/chip-default-options.ts
@@ -0,0 +1,20 @@
+/**
+ * @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 {InjectionToken} from '@angular/core';
+
+/** Default options, for the chips module, that can be overridden. */
+export interface MatChipsDefaultOptions {
+ /** The list of key codes that will trigger a chipEnd event. */
+ separatorKeyCodes: number[] | Set;
+}
+
+/** Injection token to be used to override the default options for the chips module. */
+export const MAT_CHIPS_DEFAULT_OPTIONS =
+ new InjectionToken('mat-chips-default-options');
+
diff --git a/src/material-experimental/mdc-chips/chip-grid.html b/src/material-experimental/mdc-chips/chip-grid.html
deleted file mode 100644
index 9c75ddb6f477..000000000000
--- a/src/material-experimental/mdc-chips/chip-grid.html
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/src/material-experimental/mdc-chips/chip-grid.ts b/src/material-experimental/mdc-chips/chip-grid.ts
index 1ca5fae2adbf..018673077f2a 100644
--- a/src/material-experimental/mdc-chips/chip-grid.ts
+++ b/src/material-experimental/mdc-chips/chip-grid.ts
@@ -6,19 +6,434 @@
* found in the LICENSE file at https://angular.io/license
*/
-import {ChangeDetectionStrategy, Component, ViewEncapsulation} from '@angular/core';
+import {coerceBooleanProperty} from '@angular/cdk/coercion';
+import {BACKSPACE} from '@angular/cdk/keycodes';
+import {
+ AfterContentInit,
+ AfterViewInit,
+ ChangeDetectionStrategy,
+ ChangeDetectorRef,
+ Component,
+ DoCheck,
+ ElementRef,
+ EventEmitter,
+ Input,
+ OnDestroy,
+ Optional,
+ Output,
+ Self,
+ ViewEncapsulation
+} from '@angular/core';
+import {ControlValueAccessor, FormGroupDirective, NgControl, NgForm} from '@angular/forms';
+import {
+ CanUpdateErrorState,
+ CanUpdateErrorStateCtor,
+ ErrorStateMatcher,
+ mixinErrorState,
+} from '@angular/material/core';
+import {MatFormFieldControl} from '@angular/material/form-field';
+import {MatChipTextControl} from './chip-text-control';
+import {merge, Observable, Subscription} from 'rxjs';
+import {startWith, takeUntil} from 'rxjs/operators';
+import {MatChipEvent} from './chip';
+import {MatChipSet} from './chip-set';
+
+
+/** Change event object that is emitted when the chip grid value has changed. */
+export class MatChipGridChange {
+ constructor(
+ /** Chip grid that emitted the event. */
+ public source: MatChipGrid,
+ /** Value of the chip grid when the event was emitted. */
+ public value: any) { }
+}
+
+/**
+ * Boilerplate for applying mixins to MatChipGrid.
+ * @docs-private
+ */
+class MatChipGridBase extends MatChipSet {
+ constructor(_elementRef: ElementRef,
+ _changeDetectorRef: ChangeDetectorRef,
+ public _defaultErrorStateMatcher: ErrorStateMatcher,
+ public _parentForm: NgForm,
+ public _parentFormGroup: FormGroupDirective,
+ /** @docs-private */
+ public ngControl: NgControl) {
+ super(_elementRef, _changeDetectorRef);
+ }
+}
+const _MatChipGridMixinBase: CanUpdateErrorStateCtor & typeof MatChipGridBase =
+ mixinErrorState(MatChipGridBase);
+
+/**
+ * An extension of the MatChipSet component used with MatChipRow chips and
+ * the matChipInputFor directive.
+ */
@Component({
moduleId: module.id,
selector: 'mat-chip-grid',
- templateUrl: 'chip-grid.html',
+ template: ' ',
styleUrls: ['chips.css'],
host: {
- 'class': 'mat-mdc-chip-grid',
+ 'class': 'mat-mdc-chip-set mat-mdc-chip-grid mdc-chip-set',
+ 'role': 'grid',
+ '[tabIndex]': 'empty ? -1 : 0',
+ // TODO: replace this binding with use of AriaDescriber
+ '[attr.aria-describedby]': '_ariaDescribedby || null',
+ '[attr.aria-required]': 'required.toString()',
+ '[attr.aria-disabled]': 'disabled.toString()',
+ '[attr.aria-invalid]': 'errorState',
+ '[class.mat-mdc-chip-list-disabled]': 'disabled',
+ '[class.mat-mdc-chip-list-invalid]': 'errorState',
+ '[class.mat-mdc-chip-list-required]': 'required',
+ '[id]': '_uid',
},
+ providers: [{provide: MatFormFieldControl, useExisting: MatChipGrid}],
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush,
})
-export class MatChipGrid {
- // TODO: set up MDC foundation class.
+export class MatChipGrid extends _MatChipGridMixinBase implements AfterContentInit, AfterViewInit,
+ CanUpdateErrorState, ControlValueAccessor, DoCheck, MatFormFieldControl, OnDestroy {
+ /**
+ * Implemented as part of MatFormFieldControl.
+ * @docs-private
+ */
+ readonly controlType: string = 'mat-chip-grid';
+
+ /** Subscription to blur changes in the chips. */
+ private _chipBlurSubscription: Subscription | null;
+
+ /** The chip input to add more chips */
+ protected _chipInput: MatChipTextControl;
+
+ /**
+ * Function when touched. Set as part of ControlValueAccessor implementation.
+ * @docs-private
+ */
+ _onTouched = () => {};
+
+ /**
+ * Function when changed. Set as part of ControlValueAccessor implementation.
+ * @docs-private
+ */
+ _onChange: (value: any) => void = () => {};
+
+ /**
+ * Implemented as part of MatFormFieldControl.
+ * @docs-private
+ */
+ @Input()
+ get disabled(): boolean { return this.ngControl ? !!this.ngControl.disabled : this._disabled; }
+ set disabled(value: boolean) {
+ this._disabled = coerceBooleanProperty(value);
+ this._syncChipsState();
+ }
+
+ /**
+ * Implemented as part of MatFormFieldControl.
+ * @docs-private
+ */
+ get id(): string { return this._chipInput.id; }
+
+ /**
+ * Implemented as part of MatFormFieldControl.
+ * @docs-private
+ */
+ get empty(): boolean { return this._chipInput.empty && this._chips.length === 0; }
+
+ /**
+ * Implemented as part of MatFormFieldControl.
+ * @docs-private
+ */
+ @Input()
+ get placeholder(): string { return this._chipInput.placeholder; }
+
+ /** Whether any chips or the matChipInput inside of this chip-grid has focus. */
+ get focused(): boolean { return this._chipInput.focused || this._hasFocusedChip(); }
+
+ /**
+ * Implemented as part of MatFormFieldControl.
+ * @docs-private
+ */
+ @Input()
+ get required(): boolean { return this._required; }
+ set required(value: boolean) {
+ this._required = coerceBooleanProperty(value);
+ this.stateChanges.next();
+ }
+ protected _required: boolean = false;
+
+ /**
+ * Implemented as part of MatFormFieldControl.
+ * @docs-private
+ */
+ get shouldLabelFloat(): boolean { return !this.empty || this.focused; }
+
+ /**
+ * Implemented as part of MatFormFieldControl.
+ * @docs-private
+ */
+ @Input()
+ get value(): any { return this._value; }
+ set value(value: any) {
+ this._value = value;
+ }
+ protected _value: any;
+
+ /** Combined stream of all of the child chips' blur events. */
+ get chipBlurChanges(): Observable {
+ return merge(...this._chips.map(chip => chip._onBlur));
+ }
+
+ /** Emits when the chip grid value has been changed by the user. */
+ @Output() readonly change: EventEmitter =
+ new EventEmitter();
+
+ /**
+ * Emits whenever the raw value of the chip-grid changes. This is here primarily
+ * to facilitate the two-way binding for the `value` input.
+ * @docs-private
+ */
+ @Output() readonly valueChange: EventEmitter = new EventEmitter();
+
+ constructor(_elementRef: ElementRef,
+ _changeDetectorRef: ChangeDetectorRef,
+ @Optional() _parentForm: NgForm,
+ @Optional() _parentFormGroup: FormGroupDirective,
+ _defaultErrorStateMatcher: ErrorStateMatcher,
+ /** @docs-private */
+ @Optional() @Self() public ngControl: NgControl) {
+ super(_elementRef, _changeDetectorRef, _defaultErrorStateMatcher, _parentForm, _parentFormGroup,
+ ngControl);
+ if (this.ngControl) {
+ this.ngControl.valueAccessor = this;
+ }
+ }
+
+ ngAfterContentInit() {
+ super.ngAfterContentInit();
+ this._chips.changes.pipe(startWith(null), takeUntil(this._destroyed)).subscribe(() => {
+ // Check to see if we have a destroyed chip and need to refocus
+ this._updateFocusForDestroyedChips();
+
+ this.stateChanges.next();
+ });
+ }
+
+ ngAfterViewInit() {
+ super.ngAfterViewInit();
+ if (!this._chipInput) {
+ throw Error('mat-chip-grid must be used in combination with matChipInputFor.');
+ }
+ }
+
+ 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();
+ }
+ }
+
+ ngOnDestroy() {
+ super.ngOnDestroy();
+ this.stateChanges.complete();
+ }
+
+ /** Associates an HTML input element with this chip grid. */
+ registerInput(inputElement: MatChipTextControl): void {
+ this._chipInput = inputElement;
+ this._setMdcClass('mdc-chip-set--input', true);
+ }
+
+ /**
+ * Implemented as part of MatFormFieldControl.
+ * @docs-private
+ */
+ onContainerClick(event: MouseEvent) {
+ if (!this._originatesFromChip(event) && !this.disabled) {
+ this.focus();
+ }
+ }
+
+ /**
+ * Focuses the first chip in this chip grid, or the associated input when there
+ * are no eligible chips.
+ */
+ focus(): void {
+ if (this.disabled || this._chipInput.focused) {
+ return;
+ }
+
+ if (this._chips.length > 0) {
+ this._chips.toArray()[0].focus();
+ } else {
+ this._focusInput();
+ }
+
+ this.stateChanges.next();
+ }
+
+ /**
+ * Implemented as part of MatFormFieldControl.
+ * @docs-private
+ */
+ setDescribedByIds(ids: string[]) { this._ariaDescribedby = ids.join(' '); }
+
+ /**
+ * Implemented as part of ControlValueAccessor.
+ * @docs-private
+ */
+ writeValue(value: any): void {
+ // The user is responsible for creating the child chips, so we just store the value.
+ this._value = value;
+ }
+
+ /**
+ * Implemented as part of ControlValueAccessor.
+ * @docs-private
+ */
+ registerOnChange(fn: (value: any) => void): void {
+ this._onChange = fn;
+ }
+
+ /**
+ * Implemented as part of ControlValueAccessor.
+ * @docs-private
+ */
+ registerOnTouched(fn: () => void): void {
+ this._onTouched = fn;
+ }
+
+ /** When blurred, mark the field as touched when focus moved outside the chip grid. */
+ _blur() {
+ if (this.disabled) {
+ return;
+ }
+
+ // 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(() => {
+ if (!this.focused) {
+ this._propagateChanges();
+ this._markAsTouched();
+ }
+ });
+ }
+
+ /**
+ * Removes the `tabindex` from the chip grid and resets it back afterwards, allowing the
+ * user to tab out of it. This prevents the grid from capturing focus and redirecting
+ * it back to the first chip, creating a focus trap, if it user tries to tab away.
+ */
+ _allowFocusEscape() {
+ // TODO
+ }
+
+ /** Handles custom keyboard events. */
+ _keydown(event: KeyboardEvent) {
+ const target = event.target as HTMLElement;
+
+ // If they are on an empty input and hit backspace, focus the last chip
+ if (event.keyCode === BACKSPACE && this._isEmptyInput(target)) {
+ if (this._chips.length) {
+ this._chips.toArray()[this._chips.length - 1].focus();
+ }
+ event.preventDefault();
+ }
+ this.stateChanges.next();
+ }
+
+ /** Unsubscribes from all chip events. */
+ protected _dropSubscriptions() {
+ super._dropSubscriptions();
+ if (this._chipBlurSubscription) {
+ this._chipBlurSubscription.unsubscribe();
+ this._chipBlurSubscription = null;
+ }
+ }
+
+ /** Subscribes to events on the child chips. */
+ protected _subscribeToChipEvents() {
+ super._subscribeToChipEvents();
+ this._listenToChipsBlur();
+ }
+
+ /** Subscribes to chip blur events. */
+ private _listenToChipsBlur(): void {
+ this._chipBlurSubscription = this.chipBlurChanges.subscribe(() => {
+ this._blur();
+ this.stateChanges.next();
+ });
+ }
+
+ /** Emits change event to set the model value. */
+ private _propagateChanges(fallbackValue?: any): void {
+ const valueToEmit = this._chips.length ? this._chips.toArray().map(
+ chip => chip.value) : fallbackValue;
+ this._value = valueToEmit;
+ this.change.emit(new MatChipGridChange(this, valueToEmit));
+ this.valueChange.emit(valueToEmit);
+ this._onChange(valueToEmit);
+ this._changeDetectorRef.markForCheck();
+ }
+
+ /** Mark the field as touched */
+ private _markAsTouched() {
+ this._onTouched();
+ this._changeDetectorRef.markForCheck();
+ this.stateChanges.next();
+ }
+
+ /** Checks whether an event comes from inside a chip element. */
+ private _originatesFromChip(event: Event): boolean {
+ let currentElement = event.target as HTMLElement | null;
+
+ while (currentElement && currentElement !== this._elementRef.nativeElement) {
+ if (currentElement.classList.contains('mdc-chip')) {
+ return true;
+ }
+
+ currentElement = currentElement.parentElement;
+ }
+
+ return false;
+ }
+
+ /**
+ * If the amount of chips changed, we need to focus the next closest chip.
+ */
+ private _updateFocusForDestroyedChips() {
+ // Move focus to the closest chip. If no other chips remain, focus the chip-grid itself.
+ if (this._lastDestroyedChipIndex != null) {
+ if (this._chips.length) {
+ const newChipIndex = Math.min(this._lastDestroyedChipIndex, this._chips.length - 1);
+ this._chips.toArray()[newChipIndex].focus();
+ } else {
+ this.focus();
+ }
+ }
+
+ this._lastDestroyedChipIndex = null;
+ }
+
+ /** Focus input element. */
+ private _focusInput() {
+ this._chipInput.focus();
+ }
+
+ /** Returns true if element is an input with no value. */
+ private _isEmptyInput(element: HTMLElement): boolean {
+ if (element && element.nodeName.toLowerCase() === 'input') {
+ let input = element as HTMLInputElement;
+ return !input.value;
+ }
+
+ return false;
+ }
}
diff --git a/src/material-experimental/mdc-chips/chip-input.ts b/src/material-experimental/mdc-chips/chip-input.ts
new file mode 100644
index 000000000000..cbfc6b3241ea
--- /dev/null
+++ b/src/material-experimental/mdc-chips/chip-input.ts
@@ -0,0 +1,173 @@
+/**
+ * @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 {Directive, ElementRef, EventEmitter, Inject, Input, OnChanges, Output} from '@angular/core';
+import {hasModifierKey, TAB} from '@angular/cdk/keycodes';
+import {MAT_CHIPS_DEFAULT_OPTIONS, MatChipsDefaultOptions} from './chip-default-options';
+import {MatChipGrid} from './chip-grid';
+import {MatChipTextControl} from './chip-text-control';
+
+/** Represents an input event on a `matChipInput`. */
+export interface MatChipInputEvent {
+ /** The native ` ` element that the event is being fired for. */
+ input: HTMLInputElement;
+
+ /** The value of the input. */
+ value: string;
+}
+
+// Increasing integer for generating unique ids.
+let nextUniqueId = 0;
+
+/**
+ * Directive that adds chip-specific behaviors to an input element inside ``.
+ * May be placed inside or outside of a ``.
+ */
+@Directive({
+ 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]': '_chipGrid && _chipGrid.ngControl ? _chipGrid.ngControl.invalid : null',
+ }
+})
+export class MatChipInput implements MatChipTextControl, OnChanges {
+ /** Whether the control is focused. */
+ focused: boolean = false;
+ _chipGrid: MatChipGrid;
+
+ /** Register input for chip list */
+ @Input('matChipInputFor')
+ set chipGrid(value: MatChipGrid) {
+ if (value) {
+ this._chipGrid = value;
+ this._chipGrid.registerInput(this);
+ }
+ }
+
+ /**
+ * Whether or not the chipEnd event will be emitted when the input is blurred.
+ */
+ @Input('matChipInputAddOnBlur')
+ get addOnBlur(): boolean { return this._addOnBlur; }
+ set addOnBlur(value: boolean) { this._addOnBlur = coerceBooleanProperty(value); }
+ _addOnBlur: boolean = false;
+
+ /**
+ * The list of key codes that will trigger a chipEnd event.
+ *
+ * Defaults to `[ENTER]`.
+ */
+ @Input('matChipInputSeparatorKeyCodes')
+ separatorKeyCodes: number[] | Set = this._defaultOptions.separatorKeyCodes;
+
+ /** Emitted when a chip is to be added. */
+ @Output('matChipInputTokenEnd')
+ chipEnd: EventEmitter = new EventEmitter();
+
+ /** The input's placeholder text. */
+ @Input() placeholder: string = '';
+
+ /** Unique id for the input. */
+ @Input() id: string = `mat-chip-list-input-${nextUniqueId++}`;
+
+ /** Whether the input is disabled. */
+ @Input()
+ get disabled(): boolean { return this._disabled || (this._chipGrid && this._chipGrid.disabled); }
+ set disabled(value: boolean) { this._disabled = coerceBooleanProperty(value); }
+ private _disabled: boolean = false;
+
+ /** Whether the input is empty. */
+ get empty(): boolean { return !this._inputElement.value; }
+
+ /** The native input element to which this directive is attached. */
+ protected _inputElement: HTMLInputElement;
+
+ constructor(
+ protected _elementRef: ElementRef,
+ @Inject(MAT_CHIPS_DEFAULT_OPTIONS) private _defaultOptions: MatChipsDefaultOptions) {
+ this._inputElement = this._elementRef.nativeElement as HTMLInputElement;
+ }
+
+ ngOnChanges() {
+ this._chipGrid.stateChanges.next();
+ }
+
+ /** Utility method to make host definition/tests more clear. */
+ _keydown(event?: KeyboardEvent) {
+ // 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._chipGrid._allowFocusEscape();
+ }
+
+ this._emitChipEnd(event);
+ }
+
+ /** Checks to see if the blur should emit the (chipEnd) event. */
+ _blur() {
+ if (this.addOnBlur) {
+ this._emitChipEnd();
+ }
+ this.focused = false;
+ // Blur the chip list if it is not focused
+ if (!this._chipGrid.focused) {
+ this._chipGrid._blur();
+ }
+ this._chipGrid.stateChanges.next();
+ }
+
+ _focus() {
+ this.focused = true;
+ this._chipGrid.stateChanges.next();
+ }
+
+ /** Checks to see if the (chipEnd) event needs to be emitted. */
+ _emitChipEnd(event?: KeyboardEvent) {
+ if (!this._inputElement.value && !!event) {
+ this._chipGrid._keydown(event);
+ }
+ if (!event || this._isSeparatorKey(event)) {
+ this.chipEnd.emit({ input: this._inputElement, value: this._inputElement.value });
+
+ if (event) {
+ event.preventDefault();
+ }
+ }
+ }
+
+ _onInput() {
+ // Let chip list know whenever the value changes.
+ this._chipGrid.stateChanges.next();
+ }
+
+ /** Focuses the input. */
+ focus(): void {
+ this._inputElement.focus();
+ }
+
+ /** Checks whether a keycode is one of the configured separators. */
+ private _isSeparatorKey(event: KeyboardEvent) {
+ if (hasModifierKey(event)) {
+ return false;
+ }
+
+ const separators = this.separatorKeyCodes;
+ const keyCode = event.keyCode;
+ return Array.isArray(separators) ? separators.indexOf(keyCode) > -1 : separators.has(keyCode);
+ }
+}
+
diff --git a/src/material-experimental/mdc-chips/chip-listbox.ts b/src/material-experimental/mdc-chips/chip-listbox.ts
new file mode 100644
index 000000000000..84f5a268909f
--- /dev/null
+++ b/src/material-experimental/mdc-chips/chip-listbox.ts
@@ -0,0 +1,547 @@
+/**
+ * @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 {FocusKeyManager} from '@angular/cdk/a11y';
+import {coerceBooleanProperty} from '@angular/cdk/coercion';
+import {HOME, END} from '@angular/cdk/keycodes';
+import {
+ AfterContentInit,
+ ChangeDetectionStrategy,
+ ChangeDetectorRef,
+ Component,
+ ContentChildren,
+ ElementRef,
+ EventEmitter,
+ forwardRef,
+ Input,
+ Output,
+ QueryList,
+ ViewEncapsulation
+} from '@angular/core';
+import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';
+import {MDCChipSetAdapter, MDCChipSetFoundation} from '@material/chips';
+import {merge, Observable, Subscription} from 'rxjs';
+import {startWith, takeUntil} from 'rxjs/operators';
+
+import {MatChip, MatChipEvent} from './chip';
+import {MatChipOption, MatChipSelectionChange} from './chip-option';
+import {MatChipSet} from './chip-set';
+
+
+/** Change event object that is emitted when the chip listbox value has changed. */
+export class MatChipListboxChange {
+ constructor(
+ /** Chip listbox that emitted the event. */
+ public source: MatChipListbox,
+ /** Value of the chip listbox when the event was emitted. */
+ public value: any) { }
+}
+
+/**
+ * Provider Expression that allows mat-chip-listbox to register as a ControlValueAccessor.
+ * This allows it to support [(ngModel)].
+ * @docs-private
+ */
+export const MAT_CHIP_LISTBOX_CONTROL_VALUE_ACCESSOR: any = {
+ provide: NG_VALUE_ACCESSOR,
+ useExisting: forwardRef(() => MatChipListbox),
+ multi: true
+};
+
+/**
+ * An extension of the MatChipSet component that supports chip selection.
+ * Used with MatChipOption chips.
+ */
+@Component({
+ moduleId: module.id,
+ selector: 'mat-chip-listbox',
+ template: ' ',
+ styleUrls: ['chips.css'],
+ host: {
+ 'class': 'mat-mdc-chip-set mat-mdc-chip-listbox mdc-chip-set',
+ 'role': 'listbox',
+ '[tabIndex]': 'disabled ? null : _tabIndex',
+ // TODO: replace this binding with use of AriaDescriber
+ '[attr.aria-describedby]': '_ariaDescribedby || null',
+ '[attr.aria-required]': 'required.toString()',
+ '[attr.aria-disabled]': 'disabled.toString()',
+ '[attr.aria-multiselectable]': 'multiple',
+ '[class.mat-mdc-chip-list-disabled]': 'disabled',
+ '[class.mat-mdc-chip-list-required]': 'required',
+ '(focus)': 'focus()',
+ '(blur)': '_blur()',
+ '(keydown)': '_keydown($event)',
+ '[id]': '_uid',
+ },
+ providers: [MAT_CHIP_LISTBOX_CONTROL_VALUE_ACCESSOR],
+ encapsulation: ViewEncapsulation.None,
+ changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class MatChipListbox extends MatChipSet implements AfterContentInit, ControlValueAccessor {
+
+ /** Subscription to selection changes in the chips. */
+ private _chipSelectionSubscription: Subscription | null;
+
+ /** Subscription to blur changes in the chips. */
+ private _chipBlurSubscription: Subscription | null;
+
+ /** Subscription to focus changes in the chips. */
+ private _chipFocusSubscription: Subscription | null;
+
+ /**
+ * Implementation of the MDC chip-set adapter interface.
+ * These methods are called by the chip set foundation.
+ *
+ * Overrides the base MatChipSet adapter to provide a setSelected method.
+ */
+ protected _chipSetAdapter: MDCChipSetAdapter = {
+ hasClass: (className: string) => this._hasMdcClass(className),
+ // No-op. We keep track of chips via ContentChildren, which will be updated when a chip is
+ // removed.
+ removeChip: () => {},
+ setSelected: (chipId: string, selected: boolean) => {
+ this._setSelected(chipId, selected);
+ }
+ };
+
+ /** Tab index for the chip list. */
+ _tabIndex = 0;
+
+ /** The FocusKeyManager which handles focus. */
+ _keyManager: FocusKeyManager;
+
+ /**
+ * Function when touched. Set as part of ControlValueAccessor implementation.
+ * @docs-private
+ */
+ _onTouched = () => {};
+
+ /**
+ * Function when changed. Set as part of ControlValueAccessor implementation.
+ * @docs-private
+ */
+ _onChange: (value: any) => void = () => {};
+
+ /** Whether the user should be allowed to select multiple chips. */
+ @Input()
+ get multiple(): boolean { return this._multiple; }
+ set multiple(value: boolean) {
+ this._multiple = coerceBooleanProperty(value);
+ this._updateMdcSelectionClasses();
+ this._syncListboxProperties();
+ }
+ private _multiple: boolean = false;
+
+ /** The array of selected chips inside the chip listbox. */
+ get selected(): MatChipOption[] | MatChipOption {
+ const selectedChips = this._optionChips.toArray().filter(chip => chip.selected);
+ return this.multiple ? selectedChips : selectedChips[0];
+ }
+
+ /**
+ * Whether or not this chip listbox is selectable.
+ *
+ * When a chip listbox is not selectable, the selected states for all
+ * the chips inside the chip listbox are always ignored.
+ */
+ @Input()
+ get selectable(): boolean { return this._selectable; }
+ set selectable(value: boolean) {
+ this._selectable = coerceBooleanProperty(value);
+ this._updateMdcSelectionClasses();
+ this._syncListboxProperties();
+ }
+ protected _selectable: boolean = true;
+
+ /**
+ * 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.
+ */
+ @Input()
+ get compareWith(): (o1: any, o2: any) => boolean { return this._compareWith; }
+ set compareWith(fn: (o1: any, o2: any) => boolean) {
+ this._compareWith = fn;
+ this._initializeSelection();
+ }
+ private _compareWith = (o1: any, o2: any) => o1 === o2;
+
+
+ /** Whether this chip listbox is required. */
+ @Input()
+ get required(): boolean { return this._required; }
+ set required(value: boolean) {
+ this._required = coerceBooleanProperty(value);
+ }
+ protected _required: boolean = false;
+
+ /** Combined stream of all of the child chips' selection change events. */
+ get chipSelectionChanges(): Observable {
+ return merge(...this._optionChips.map(chip => chip.selectionChange));
+ }
+
+ /** Combined stream of all of the child chips' focus events. */
+ get chipFocusChanges(): Observable {
+ return merge(...this._chips.map(chip => chip._onFocus));
+ }
+
+ /** Combined stream of all of the child chips' blur events. */
+ get chipBlurChanges(): Observable {
+ return merge(...this._chips.map(chip => chip._onBlur));
+ }
+
+ /** The value of the listbox, which is the combined value of the selected chips. */
+ get value(): any {
+ if (Array.isArray(this.selected)) {
+ return this.selected.map(chip => chip.value);
+ } else {
+ return this.selected ? this.selected.value : null;
+ }
+ }
+
+ /** Event emitted when the selected chip listbox value has been changed by the user. */
+ @Output() readonly change: EventEmitter =
+ new EventEmitter();
+
+ @ContentChildren(MatChipOption, {
+ // We need to use `descendants: true`, because Ivy will no longer match
+ // indirect descendants if it's left as false.
+ descendants: true
+ })
+ _optionChips: QueryList;
+
+ constructor(protected _elementRef: ElementRef,
+ _changeDetectorRef: ChangeDetectorRef) {
+ super(_elementRef, _changeDetectorRef);
+ // Reinitialize the foundation with our overridden adapter
+ this._chipSetFoundation = new MDCChipSetFoundation(this._chipSetAdapter);
+ this._updateMdcSelectionClasses();
+ }
+
+ ngAfterContentInit() {
+ super.ngAfterContentInit();
+ this._initKeyManager();
+
+ this._optionChips.changes.pipe(startWith(null), takeUntil(this._destroyed)).subscribe(() => {
+ // Update listbox selectable/multiple properties on chips
+ this._syncListboxProperties();
+
+ // 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();
+ });
+ }
+
+ /**
+ * Focuses the first selected chip in this chip listbox, or the first non-disabled chip when there
+ * are no selected chips.
+ */
+ focus(): void {
+ if (this.disabled) {
+ return;
+ }
+
+ const firstSelectedChip = this._getFirstSelectedChip();
+
+ if (firstSelectedChip) {
+ const firstSelectedChipIndex = this._chips.toArray().indexOf(firstSelectedChip);
+ this._keyManager.setActiveItem(firstSelectedChipIndex);
+ } else if (this._chips.length > 0) {
+ this._keyManager.setFirstItemActive();
+ }
+ }
+
+ /**
+ * Implemented as part of ControlValueAccessor.
+ * @docs-private
+ */
+ writeValue(value: any): void {
+ if (this._chips) {
+ this._setSelectionByValue(value, false);
+ }
+ }
+
+ /**
+ * Implemented as part of ControlValueAccessor.
+ * @docs-private
+ */
+ registerOnChange(fn: (value: any) => void): void {
+ this._onChange = fn;
+ }
+
+ /**
+ * Implemented as part of ControlValueAccessor.
+ * @docs-private
+ */
+ registerOnTouched(fn: () => void): void {
+ this._onTouched = fn;
+ }
+
+ /** Selects all chips with value. */
+ _setSelectionByValue(value: any, isUserInput: boolean = true) {
+ this._clearSelection();
+
+ if (Array.isArray(value)) {
+ value.forEach(currentValue => this._selectValue(currentValue, isUserInput));
+ } else {
+ 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);
+ }
+ }
+ }
+ }
+
+ /** Selects or deselects a chip by id. */
+ _setSelected(chipId: string, selected: boolean) {
+ const chip = this._optionChips.find(c => c.id === chipId);
+ if (chip && chip.selected != selected) {
+ chip.toggleSelected(true);
+ }
+ }
+
+ /** When blurred, marks the field as touched when focus moved outside the chip listbox. */
+ _blur() {
+ if (this.disabled) {
+ return;
+ }
+
+ // Wait to see if focus moves to an indivdual chip.
+ setTimeout(() => {
+ if (!this.focused) {
+ this._keyManager.setActiveItem(-1);
+ this._propagateChanges();
+ this._markAsTouched();
+ }
+ });
+ }
+
+ /**
+ * Removes the `tabindex` from the chip listbox and resets it back afterwards, allowing the
+ * user to tab out of it. This prevents the listbox from capturing focus and redirecting
+ * it back to the first chip, creating a focus trap, if it user tries to tab away.
+ */
+ _allowFocusEscape() {
+ if (this._tabIndex !== -1) {
+ this._tabIndex = -1;
+
+ setTimeout(() => {
+ this._tabIndex = 0;
+ this._changeDetectorRef.markForCheck();
+ });
+ }
+ }
+
+ /**
+ * Handles custom keyboard shortcuts, and passes other keyboard events to the keyboard manager.
+ */
+ _keydown(event: KeyboardEvent) {
+ const target = event.target as HTMLElement;
+
+ if (target && target.classList.contains('mdc-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);
+ }
+ }
+ }
+
+ /** Marks the field as touched */
+ private _markAsTouched() {
+ this._onTouched();
+ this._changeDetectorRef.markForCheck();
+ }
+
+ /** Emits change event to set the model value. */
+ private _propagateChanges(fallbackValue?: any): void {
+ let valueToEmit: any = this.value || fallbackValue;
+ this.change.emit(new MatChipListboxChange(this, valueToEmit));
+ this._onChange(valueToEmit);
+ this._changeDetectorRef.markForCheck();
+ }
+
+ /**
+ * Initializes the chip listbox selection state to reflect any chips that were preselected.
+ */
+ private _initializeSelection() {
+ this._optionChips.forEach(chip => {
+ if (chip.selected) {
+ this._chipSetFoundation.select(chip.id);
+ }
+ });
+ }
+
+ /**
+ * Deselects every chip in the listbox.
+ * @param skip Chip that should not be deselected.
+ */
+ private _clearSelection(skip?: MatChip): void {
+ this._optionChips.forEach(chip => {
+ if (chip !== skip) {
+ chip.deselect();
+ }
+ });
+ }
+
+ /**
+ * Finds and selects the chip based on its value.
+ * @returns Chip that has the corresponding value.
+ */
+ private _selectValue(value: any, isUserInput: boolean = true): MatChip | undefined {
+
+ const correspondingChip = this._optionChips.find(chip => {
+ return chip.value != null && this._compareWith(chip.value, value);
+ });
+
+ if (correspondingChip) {
+ isUserInput ? correspondingChip.selectViaInteraction() : correspondingChip.select();
+ }
+
+ return correspondingChip;
+ }
+
+ /** Syncs the chip-listbox selection state with the individual chips. */
+ private _syncListboxProperties() {
+ if (this._optionChips) {
+ // Defer setting the value in order to avoid the "Expression
+ // has changed after it was checked" errors from Angular.
+ Promise.resolve().then(() => {
+ this._optionChips.forEach(chip => {
+ chip._chipListMultiple = this.multiple;
+ chip.chipListSelectable = this._selectable;
+ chip._changeDetectorRef.markForCheck();
+ });
+ });
+ }
+ }
+
+ /** Sets the mdc classes for single vs multi selection. */
+ private _updateMdcSelectionClasses() {
+ this._setMdcClass('mdc-chip-set--filter', this.selectable && this.multiple);
+ this._setMdcClass('mdc-chip-set--choice', this.selectable && !this.multiple);
+ }
+
+ /** Initializes the key manager to manage focus. */
+ private _initKeyManager() {
+ this._keyManager = new FocusKeyManager(this._chips)
+ .withWrap()
+ .withVerticalOrientation()
+ .withHorizontalOrientation('ltr');
+
+ this._keyManager.tabOut.pipe(takeUntil(this._destroyed)).subscribe(() => {
+ this._allowFocusEscape();
+ });
+ }
+
+ /** Returns the first selected chip in this listbox, or undefined if no chips are selected. */
+ private _getFirstSelectedChip(): MatChipOption | undefined {
+ if (Array.isArray(this.selected)) {
+ return this.selected.length ? this.selected[0] : undefined;
+ } else {
+ return this.selected;
+ }
+ }
+
+ /** Unsubscribes from all chip events. */
+ protected _dropSubscriptions() {
+ super._dropSubscriptions();
+ if (this._chipSelectionSubscription) {
+ this._chipSelectionSubscription.unsubscribe();
+ this._chipSelectionSubscription = null;
+ }
+
+ if (this._chipBlurSubscription) {
+ this._chipBlurSubscription.unsubscribe();
+ this._chipBlurSubscription = null;
+ }
+
+ if (this._chipFocusSubscription) {
+ this._chipFocusSubscription.unsubscribe();
+ this._chipFocusSubscription = null;
+ }
+ }
+
+ /** Subscribes to events on the child chips. */
+ protected _subscribeToChipEvents() {
+ super._subscribeToChipEvents();
+ this._listenToChipsSelection();
+ this._listenToChipsFocus();
+ this._listenToChipsBlur();
+ }
+
+ /** Subscribes to chip focus events. */
+ private _listenToChipsFocus(): void {
+ this._chipFocusSubscription = this.chipFocusChanges.subscribe((event: MatChipEvent) => {
+ let chipIndex: number = this._chips.toArray().indexOf(event.chip);
+
+ if (this._isValidIndex(chipIndex)) {
+ this._keyManager.updateActiveItemIndex(chipIndex);
+ }
+ });
+ }
+
+ /** Subscribes to chip blur events. */
+ private _listenToChipsBlur(): void {
+ this._chipBlurSubscription = this.chipBlurChanges.subscribe(() => {
+ this._blur();
+ });
+ }
+
+ /** Subscribes to selection changes in the option chips. */
+ private _listenToChipsSelection(): void {
+ this._chipSelectionSubscription = this.chipSelectionChanges.subscribe(
+ (chipSelectionChange: MatChipSelectionChange) => {
+ this._chipSetFoundation.handleChipSelection(
+ chipSelectionChange.source.id, chipSelectionChange.selected);
+ if (chipSelectionChange.isUserInput) {
+ this._propagateChanges();
+ }
+ });
+ }
+
+ /**
+ * Check the tab index as you should not be allowed to focus an empty list.
+ */
+ protected _updateTabIndex(): void {
+ // If we have 0 chips, we should not allow keyboard focus
+ this._tabIndex = 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.
+ */
+ private _updateFocusForDestroyedChips() {
+ // Move focus to the closest chip. If no other chips remain, focus the chip-listbox itself.
+ if (this._lastDestroyedChipIndex != null) {
+ if (this._chips.length) {
+ const newChipIndex = Math.min(this._lastDestroyedChipIndex, this._chips.length - 1);
+ this._keyManager.setActiveItem(newChipIndex);
+ } else {
+ this.focus();
+ }
+ }
+
+ this._lastDestroyedChipIndex = null;
+ }
+}
+
diff --git a/src/material-experimental/mdc-chips/chip-option.html b/src/material-experimental/mdc-chips/chip-option.html
new file mode 100644
index 000000000000..cae0fd013d27
--- /dev/null
+++ b/src/material-experimental/mdc-chips/chip-option.html
@@ -0,0 +1,7 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/material-experimental/mdc-chips/chip-option.ts b/src/material-experimental/mdc-chips/chip-option.ts
new file mode 100644
index 000000000000..c371544926cd
--- /dev/null
+++ b/src/material-experimental/mdc-chips/chip-option.ts
@@ -0,0 +1,188 @@
+/**
+ * @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 {SPACE} from '@angular/cdk/keycodes';
+import {
+ ChangeDetectionStrategy,
+ Component,
+ EventEmitter,
+ Input,
+ Output,
+ ViewEncapsulation
+} from '@angular/core';
+
+import {MatChip} from './chip';
+
+
+/** Event object emitted by MatChipOption when selected or deselected. */
+export class MatChipSelectionChange {
+ constructor(
+ /** Reference to the chip that emitted the event. */
+ public source: MatChipOption,
+ /** Whether the chip that emitted the event is selected. */
+ public selected: boolean,
+ /** Whether the selection change was a result of a user interaction. */
+ public isUserInput = false) { }
+}
+
+/**
+ * An extension of the MatChip component that supports chip selection.
+ * Used with MatChipListbox.
+ */
+@Component({
+ moduleId: module.id,
+ selector: 'mat-basic-chip-option, mat-chip-option',
+ templateUrl: 'chip-option.html',
+ styleUrls: ['chips.css'],
+ inputs: ['color', 'disabled', 'disableRipple'],
+ host: {
+ 'role': 'option',
+ '[class.mat-mdc-chip-disabled]': 'disabled',
+ '[class.mat-mdc-chip-highlighted]': 'highlighted',
+ '[class.mat-mdc-chip-with-avatar]': 'leadingIcon',
+ '[class.mat-mdc-chip-with-trailing-icon]': 'trailingIcon',
+ '[class.mat-mdc-chip-selected]': 'selected',
+ '[id]': 'id',
+ '[tabIndex]': 'disabled ? null : -1',
+ '[attr.disabled]': 'disabled || null',
+ '[attr.aria-disabled]': 'disabled.toString()',
+ '[attr.aria-selected]': 'ariaSelected',
+ '(click)': '_handleClick($event)',
+ '(keydown)': '_handleKeydown($event)',
+ '(focus)': 'focus()',
+ '(blur)': '_blur()',
+ '(transitionend)': '_chipFoundation.handleTransitionEnd($event)'
+ },
+ providers: [{provide: MatChip, useExisting: MatChipOption}],
+ encapsulation: ViewEncapsulation.None,
+ changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class MatChipOption extends MatChip {
+
+ /** Whether the chip list is selectable. */
+ chipListSelectable: boolean = true;
+
+ /** Whether the chip list is in multi-selection mode. */
+ _chipListMultiple: boolean = false;
+
+ /**
+ * Whether or not the chip is selectable.
+ *
+ * When a chip is not selectable, changes to its selected state are always
+ * ignored. By default an option chip is selectable, and it becomes
+ * non-selectable if its parent chip list is not selectable.
+ */
+ @Input()
+ get selectable(): boolean {
+ return this._selectable && this.chipListSelectable;
+ }
+ set selectable(value: boolean) {
+ this._selectable = coerceBooleanProperty(value);
+ }
+ protected _selectable: boolean = true;
+
+ /** Whether the chip is selected. */
+ @Input()
+ get selected(): boolean {
+ return this._chipFoundation.isSelected();
+ }
+ set selected(value: boolean) {
+ if (!this.selectable) {
+ return;
+ }
+ const coercedValue = coerceBooleanProperty(value);
+ if (coercedValue != this._chipFoundation.isSelected()) {
+ this._chipFoundation.setSelected(coerceBooleanProperty(value));
+ this._dispatchSelectionChange();
+ }
+ }
+
+ /** The ARIA selected applied to the chip. */
+ get ariaSelected(): string | null {
+ // 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;
+ }
+
+ /** The unstyled chip selector for this component. */
+ protected basicChipAttrName = 'mat-basic-chip-option';
+
+ /** Emitted when the chip is selected or deselected. */
+ @Output() readonly selectionChange: EventEmitter =
+ new EventEmitter();
+
+ /** Selects the chip. */
+ select(): void {
+ if (!this.selectable) {
+ return;
+ } else if (!this.selected) {
+ this._chipFoundation.setSelected(true);
+ this._dispatchSelectionChange();
+ }
+ }
+
+ /** Deselects the chip. */
+ deselect(): void {
+ if (!this.selectable) {
+ return;
+ } else if (this.selected) {
+ this._chipFoundation.setSelected(false);
+ this._dispatchSelectionChange();
+ }
+ }
+
+ /** Selects this chip and emits userInputSelection event */
+ selectViaInteraction(): void {
+ if (!this.selectable) {
+ return;
+ } else if (!this.selected) {
+ this._chipFoundation.setSelected(true);
+ this._dispatchSelectionChange(true);
+ }
+ }
+
+ /** Toggles the current selected state of this chip. */
+ toggleSelected(isUserInput: boolean = false): boolean {
+ if (!this.selectable) {
+ return this.selected;
+ }
+
+ this._chipFoundation.setSelected(!this.selected);
+ this._dispatchSelectionChange(isUserInput);
+ return this.selected;
+ }
+
+ /** Emits a selection change event. */
+ private _dispatchSelectionChange(isUserInput = false) {
+ this.selectionChange.emit({
+ source: this,
+ isUserInput,
+ selected: this.selected
+ });
+ }
+
+ /** Handles custom key presses. */
+ _handleKeydown(event: KeyboardEvent): void {
+ if (this.disabled) {
+ return;
+ }
+
+ switch (event.keyCode) {
+ case SPACE:
+ this.toggleSelected(true);
+
+ // Always prevent space from scrolling the page since the list has focus
+ event.preventDefault();
+ break;
+ default:
+ this._handleInteraction(event);
+ }
+ }
+}
diff --git a/src/material-experimental/mdc-chips/chip-row.html b/src/material-experimental/mdc-chips/chip-row.html
new file mode 100644
index 000000000000..4f6bc3bf3987
--- /dev/null
+++ b/src/material-experimental/mdc-chips/chip-row.html
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/material-experimental/mdc-chips/chip-row.ts b/src/material-experimental/mdc-chips/chip-row.ts
new file mode 100644
index 000000000000..b3904c24a643
--- /dev/null
+++ b/src/material-experimental/mdc-chips/chip-row.ts
@@ -0,0 +1,64 @@
+/**
+ * @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 {ChangeDetectionStrategy, Component, ViewEncapsulation} from '@angular/core';
+import {BACKSPACE, DELETE} from '@angular/cdk/keycodes';
+import {MatChip} from './chip';
+
+/**
+ * An extension of the MatChip component used with MatChipGrid and
+ * the matChipInputFor directive.
+ */
+@Component({
+ moduleId: module.id,
+ selector: 'mat-chip-row, mat-basic-chip-row',
+ templateUrl: 'chip-row.html',
+ styleUrls: ['chips.css'],
+ inputs: ['color', 'disabled', 'disableRipple'],
+ host: {
+ 'role': 'row',
+ '[class.mat-mdc-chip-disabled]': 'disabled',
+ '[class.mat-mdc-chip-highlighted]': 'highlighted',
+ '[class.mat-mdc-chip-with-avatar]': 'leadingIcon',
+ '[class.mat-mdc-chip-with-trailing-icon]': 'trailingIcon',
+ '[id]': 'id',
+ '[tabIndex]': 'disabled ? null : -1',
+ '[attr.disabled]': 'disabled || null',
+ '[attr.aria-disabled]': 'disabled.toString()',
+ '(click)': '_handleClick($event)',
+ '(keydown)': '_handleKeydown($event)',
+ '(focus)': 'focus()',
+ '(blur)': '_blur()',
+ '(transitionend)': '_chipFoundation.handleTransitionEnd($event)'
+ },
+ providers: [{provide: MatChip, useExisting: MatChipRow}],
+ encapsulation: ViewEncapsulation.None,
+ changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class MatChipRow extends MatChip {
+ protected basicChipAttrName = 'mat-basic-chip-row';
+
+ /** Handle custom key presses. */
+ _handleKeydown(event: KeyboardEvent): void {
+ if (this.disabled) {
+ return;
+ }
+
+ switch (event.keyCode) {
+ case DELETE:
+ case BACKSPACE:
+ // Remove the focused chip
+ this.remove();
+ // Always prevent so page navigation does not occur
+ event.preventDefault();
+ break;
+ default:
+ this._handleInteraction(event);
+ }
+ }
+}
diff --git a/src/material-experimental/mdc-chips/chip-set.ts b/src/material-experimental/mdc-chips/chip-set.ts
new file mode 100644
index 000000000000..450cfc6373d4
--- /dev/null
+++ b/src/material-experimental/mdc-chips/chip-set.ts
@@ -0,0 +1,254 @@
+/**
+ * @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 {
+ AfterContentInit,
+ AfterViewInit,
+ ChangeDetectionStrategy,
+ ChangeDetectorRef,
+ Component,
+ ContentChildren,
+ ElementRef,
+ Input,
+ OnDestroy,
+ QueryList,
+ ViewEncapsulation
+} from '@angular/core';
+import {MDCChipSetAdapter, MDCChipSetFoundation} from '@material/chips';
+import {MatChip, MatChipEvent} from './chip';
+import {merge, Observable, Subject, Subscription} from 'rxjs';
+import {startWith, takeUntil} from 'rxjs/operators';
+
+let uid = 0;
+
+/**
+ * Basic container component for the MatChip component.
+ *
+ * Extended by MatChipListbox and MatChipGrid for different interaction patterns.
+ */
+@Component({
+ moduleId: module.id,
+ selector: 'mat-chip-set',
+ template: ' ',
+ styleUrls: ['chips.css'],
+ host: {
+ 'class': 'mat-mdc-chip-set mdc-chip-set',
+ 'role': 'presentation',
+ // TODO: replace this binding with use of AriaDescriber
+ '[attr.aria-describedby]': '_ariaDescribedby || null',
+ '[id]': '_uid',
+ },
+ encapsulation: ViewEncapsulation.None,
+ changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class MatChipSet implements AfterContentInit, AfterViewInit, OnDestroy {
+ /** Subscription to remove changes in chips. */
+ private _chipRemoveSubscription: Subscription | null;
+
+ /** Subscription to chip interactions. */
+ private _chipInteractionSubscription: Subscription | null;
+
+ /**
+ * 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.
+ */
+ protected _lastDestroyedChipIndex: number | null = null;
+
+ /** The MDC foundation containing business logic for MDC chip-set. */
+ protected _chipSetFoundation: MDCChipSetFoundation;
+
+ /** Subject that emits when the component has been destroyed. */
+ protected _destroyed = new Subject();
+
+ /**
+ * Implementation of the MDC chip-set adapter interface.
+ * These methods are called by the chip set foundation.
+ */
+ protected _chipSetAdapter: MDCChipSetAdapter = {
+ hasClass: (className) => this._hasMdcClass(className),
+ // No-op. We keep track of chips via ContentChildren, which will be updated when a chip is
+ // removed.
+ removeChip: () => {},
+ // No-op for base chip set. MatChipListbox overrides the adapter to provide this method.
+ setSelected: () => {}
+ };
+
+ /** The aria-describedby attribute on the chip list for improved a11y. */
+ _ariaDescribedby: string;
+
+ /** Uid of the chip set */
+ _uid: string = `mat-mdc-chip-set-${uid++}`;
+
+ /**
+ * Map from class to whether the class is enabled.
+ * Enabled classes are set on the MDC chip-set div.
+ */
+ _mdcClasses: {[key: string]: boolean} = {};
+
+ /** Whether the chip set is disabled. */
+ @Input()
+ get disabled(): boolean { return this._disabled; }
+ set disabled(value: boolean) {
+ this._disabled = coerceBooleanProperty(value);
+ this._syncChipsState();
+ }
+ protected _disabled: boolean = false;
+
+ /** Whether the chip list contains chips or not. */
+ get empty(): boolean { return this._chips.length === 0; }
+
+ /** Whether any of the chips inside of this chip-set has focus. */
+ get focused(): boolean { return this._hasFocusedChip(); }
+
+ /** Combined stream of all of the child chips' remove events. */
+ get chipRemoveChanges(): Observable {
+ return merge(...this._chips.map(chip => chip.removed));
+ }
+
+ /** Combined stream of all of the child chips' interaction events. */
+ get chipInteractionChanges(): Observable {
+ return merge(...this._chips.map(chip => chip.interaction));
+ }
+
+ /** The chips that are part of this chip set. */
+ @ContentChildren(MatChip) _chips: QueryList;
+
+ constructor(protected _elementRef: ElementRef,
+ protected _changeDetectorRef: ChangeDetectorRef) {
+ this._chipSetFoundation = new MDCChipSetFoundation(this._chipSetAdapter);
+ }
+
+ ngAfterViewInit() {
+ this._chipSetFoundation.init();
+ }
+
+ ngAfterContentInit() {
+ this._chips.changes.pipe(startWith(null), takeUntil(this._destroyed)).subscribe(() => {
+ if (this.disabled) {
+ // Since this happens after the content has been
+ // checked, we need to defer it to the next tick.
+ Promise.resolve().then(() => {
+ this._syncChipsState();
+ });
+ }
+
+ this._resetChips();
+ });
+ }
+
+ ngOnDestroy() {
+ this._dropSubscriptions();
+ this._destroyed.next();
+ this._destroyed.complete();
+ this._chipSetFoundation.destroy();
+ }
+
+ /** Checks whether any of the chips is focused. */
+ protected _hasFocusedChip() {
+ return this._chips.some(chip => chip._hasFocus);
+ }
+
+ /** Syncs the chip-set's state with the individual chips. */
+ protected _syncChipsState() {
+ if (this._chips) {
+ this._chips.forEach(chip => {
+ chip.disabled = this._disabled;
+ chip._changeDetectorRef.markForCheck();
+ });
+ }
+ }
+
+ /** Sets whether the given CSS class should be applied to the MDC chip. */
+ protected _setMdcClass(cssClass: string, active: boolean) {
+ const classes = this._elementRef.nativeElement.classList;
+ active ? classes.add(cssClass) : classes.remove(cssClass);
+ this._changeDetectorRef.markForCheck();
+ }
+
+ /** Adapter method that returns true if the chip set has the given MDC class. */
+ protected _hasMdcClass(className: string) {
+ return this._elementRef.nativeElement.classList.contains(className);
+ }
+
+ /** Updates subscriptions to chip events. */
+ private _resetChips() {
+ this._dropSubscriptions();
+ this._subscribeToChipEvents();
+ }
+
+ /** Subscribes to events on the child chips. */
+ protected _subscribeToChipEvents() {
+ this._listenToChipsRemove();
+ this._listenToChipsInteraction();
+ }
+
+ /** Subscribes to chip removal events. */
+ private _listenToChipsRemove() {
+ this._chipRemoveSubscription = this.chipRemoveChanges.subscribe((event: MatChipEvent) => {
+ this._handleChipRemove(event);
+ });
+ }
+
+ /** Subscribes to chip interaction events. */
+ private _listenToChipsInteraction() {
+ this._chipInteractionSubscription = this.chipInteractionChanges.subscribe((id: string) => {
+ this._handleChipInteraction(id);
+ });
+ }
+
+ /**
+ * Called when one of the chips is about to be removed.
+ * If the removed chip has focus, stores its index so we can refocus.
+ */
+ protected _handleChipRemove(event: MatChipEvent) {
+ this._chipSetFoundation.handleChipRemoval(event.chip.id);
+ const chip = event.chip;
+ const chipIndex: number = 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;
+ }
+ }
+
+ /** Notifies the chip set foundation when the user interacts with a chip. */
+ protected _handleChipInteraction(id: string) {
+ this._chipSetFoundation.handleChipInteraction(id);
+ }
+
+ /** Unsubscribes from all chip events. */
+ protected _dropSubscriptions() {
+ if (this._chipRemoveSubscription) {
+ this._chipRemoveSubscription.unsubscribe();
+ this._chipRemoveSubscription = null;
+ }
+
+ if (this._chipInteractionSubscription) {
+ this._chipInteractionSubscription.unsubscribe();
+ this._chipInteractionSubscription = null;
+ }
+ }
+
+ /** Dummy method for subclasses to override. Base chip set cannot be focused. */
+ focus() {}
+
+ /**
+ * Utility to ensure all indexes are valid.
+ *
+ * @param index The index to be checked.
+ * @returns True if the index is valid for our list of chips.
+ */
+ protected _isValidIndex(index: number): boolean {
+ return index >= 0 && index < this._chips.length;
+ }
+}
+
diff --git a/src/material-experimental/mdc-chips/chip-text-control.ts b/src/material-experimental/mdc-chips/chip-text-control.ts
new file mode 100644
index 000000000000..0431a10c1983
--- /dev/null
+++ b/src/material-experimental/mdc-chips/chip-text-control.ts
@@ -0,0 +1,27 @@
+/**
+ * @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
+ */
+
+
+/** Interface for a text control that is used to drive interaction with a mat-chip-list. */
+export interface MatChipTextControl {
+ /** Unique identifier for the text control. */
+ id: string;
+
+ /** The text control's placeholder text. */
+ placeholder: string;
+
+ /** Whether the text control has browser focus. */
+ focused: boolean;
+
+ /** Whether the text control is empty. */
+ empty: boolean;
+
+ /** Focuses the text control. */
+ focus(): void;
+}
+
diff --git a/src/material-experimental/mdc-chips/chip.html b/src/material-experimental/mdc-chips/chip.html
new file mode 100644
index 000000000000..f7df2f5d98e6
--- /dev/null
+++ b/src/material-experimental/mdc-chips/chip.html
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/src/material-experimental/mdc-chips/chip.ts b/src/material-experimental/mdc-chips/chip.ts
new file mode 100644
index 000000000000..45ca8867c194
--- /dev/null
+++ b/src/material-experimental/mdc-chips/chip.ts
@@ -0,0 +1,416 @@
+/**
+ * @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 {Platform} from '@angular/cdk/platform';
+import {
+ AfterContentInit,
+ AfterViewInit,
+ Component,
+ ChangeDetectionStrategy,
+ ChangeDetectorRef,
+ ContentChild,
+ Directive,
+ ElementRef,
+ EventEmitter,
+ Input,
+ NgZone,
+ OnDestroy,
+ Output,
+ ViewEncapsulation
+} from '@angular/core';
+import {
+ CanColor,
+ CanColorCtor,
+ CanDisable,
+ CanDisableCtor,
+ CanDisableRipple,
+ CanDisableRippleCtor,
+ mixinColor,
+ mixinDisabled,
+ mixinDisableRipple,
+ RippleConfig,
+ RippleRenderer,
+ RippleTarget,
+} from '@angular/material/core';
+import {MDCChipAdapter, MDCChipFoundation} from '@material/chips';
+import {Subject} from 'rxjs';
+import {takeUntil} from 'rxjs/operators';
+
+
+let uid = 0;
+
+/** Represents an event fired on an individual `mat-chip`. */
+export interface MatChipEvent {
+ /** The chip the event was fired on. */
+ chip: MatChip;
+}
+
+/**
+ * Directive to add CSS classes to chip leading icon.
+ * @docs-private
+ */
+@Directive({
+ selector: 'mat-chip-avatar, [matChipAvatar]',
+ host: {
+ 'class': 'mat-mdc-chip-avatar mdc-chip__icon mdc-chip__icon--leading',
+ 'role': 'img'
+ }
+})
+export class MatChipAvatar {
+ constructor(private _changeDetectorRef: ChangeDetectorRef,
+ private _elementRef: ElementRef) {}
+
+ /** Sets whether the given CSS class should be applied to the leading icon. */
+ setClass(cssClass: string, active: boolean) {
+ const element = this._elementRef.nativeElement;
+ active ? element.addClass(cssClass) : element.removeClass(cssClass);
+ this._changeDetectorRef.markForCheck();
+ }
+}
+
+/**
+ * Directive to add CSS class to chip trailing icon and notify parent chip
+ * about trailing icon interactions.
+ *
+ * If matChipRemove is used to add this directive, the parent chip will be
+ * removed when the trailing icon is clicked.
+ *
+ * @docs-private
+ */
+@Directive({
+ selector: 'mat-chip-trailing-icon, [matChipTrailingIcon], [matChipRemove]',
+ host: {
+ 'class': 'mat-mdc-chip-trailing-icon mdc-chip__icon mdc-chip__icon--trailing',
+ '[tabIndex]': 'tabIndex',
+ '[attr.aria-hidden]': '!shouldRemove',
+ '[attr.role]': 'shouldRemove ? "button" : null',
+ '(click)': 'interaction.emit($event)',
+ '(keydown)': 'interaction.emit($event)',
+ '(blur)': 'parentChip ? parentChip._blur() : {}',
+ '(focus)': 'parentChip ? parentChip._hasFocus = true : {}'
+ }
+})
+export class MatChipTrailingIcon {
+ /** Whether interaction with this icon should remove the parent chip. */
+ shouldRemove!: boolean;
+
+ /** The MatChip component associated with this icon. */
+ @Input() parentChip?: MatChip;
+
+ /** The tab index for this icon. */
+ get tabIndex(): number|null {
+ if ((this.parentChip && this.parentChip.disabled) || !this.shouldRemove) {
+ return -1;
+ }
+ return 0;
+ }
+
+ /** Emits when the user interacts with the icon. */
+ @Output() interaction = new EventEmitter();
+
+ constructor(private _elementRef: ElementRef) {
+ this.shouldRemove = this._isMatChipRemoveIcon();
+ }
+
+ /** Returns true if the icon was created with the matChipRemove directive. */
+ _isMatChipRemoveIcon(): boolean {
+ return this._elementRef.nativeElement.getAttribute('matChipRemove') !== null;
+ }
+}
+
+/**
+ * Directive to add MDC CSS to non-basic chips.
+ * @docs-private
+ */
+@Directive({
+ selector: 'mat-chip, mat-chip-option, mat-chip-row, [mat-chip], [mat-chip-option], [mat-chip-row]',
+ host: {'class': 'mat-mdc-chip mdc-chip'}
+})
+export class MatChipCssInternalOnly { }
+
+/**
+ * Boilerplate for applying mixins to MatChip.
+ * @docs-private
+ */
+class MatChipBase {
+ constructor(public _elementRef: ElementRef) {}
+}
+
+const _MatChipMixinBase: CanColorCtor & CanDisableRippleCtor & CanDisableCtor & typeof MatChipBase =
+ mixinColor(mixinDisableRipple(mixinDisabled(MatChipBase)), 'primary');
+
+/**
+ * Material design styled Chip base component. Used inside the MatChipSet component.
+ *
+ * Extended by MatChipOption and MatChipRow for different interaction patterns.
+ */
+@Component({
+ moduleId: module.id,
+ selector: 'mat-basic-chip, mat-chip',
+ inputs: ['color', 'disabled', 'disableRipple'],
+ exportAs: 'matChip',
+ templateUrl: 'chip.html',
+ styleUrls: ['chips.css'],
+ host: {
+ '[class.mat-mdc-chip-disabled]': 'disabled',
+ '[class.mat-mdc-chip-highlighted]': 'highlighted',
+ '[class.mat-mdc-chip-with-avatar]': 'leadingIcon',
+ '[class.mat-mdc-chip-with-trailing-icon]': 'trailingIcon',
+ '[id]': 'id',
+ '[attr.disabled]': 'disabled || null',
+ '[attr.aria-disabled]': 'disabled.toString()',
+ '(transitionend)': '_chipFoundation.handleTransitionEnd($event)'
+ },
+ encapsulation: ViewEncapsulation.None,
+ changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class MatChip extends _MatChipMixinBase implements AfterContentInit, AfterViewInit,
+ CanColor, CanDisable, CanDisableRipple, RippleTarget, OnDestroy {
+ /** Emits when the chip is focused. */
+ readonly _onFocus = new Subject();
+
+ /** Emits when the chip is blurred. */
+ readonly _onBlur = new Subject();
+
+ /** Whether the chip has focus. */
+ _hasFocus: boolean = false;
+
+ /** Default unique id for the chip. */
+ private _uniqueId = `mat-mdc-chip-${uid++}`;
+
+ /** A unique id for the chip. If none is supplied, it will be auto-generated. */
+ @Input() id: string = this._uniqueId;
+
+ /** The value of the chip. Defaults to the content inside `` tags. */
+ @Input()
+ get value(): any {
+ return this._value != undefined
+ ? this._value
+ : this._elementRef.nativeElement.textContent;
+ }
+ set value(value: any) { this._value = value; }
+ protected _value: any;
+
+ /**
+ * Determines whether or not the chip displays the remove styling and emits (removed) events.
+ */
+ @Input()
+ get removable(): boolean { return this._removable; }
+ set removable(value: boolean) {
+ this._removable = coerceBooleanProperty(value);
+ }
+ protected _removable: boolean = true;
+
+ /**
+ * Colors the chip for emphasis as if it were selected.
+ */
+ @Input()
+ get highlighted(): boolean { return this._highlighted; }
+ set highlighted(value: boolean) {
+ this._highlighted = coerceBooleanProperty(value);
+ }
+ protected _highlighted: boolean = false;
+
+ /** Emitted when the user interacts with the trailing icon. */
+ @Output() trailingIconInteraction = new EventEmitter();
+
+ /** Emitted when the user interacts with the chip. */
+ @Output() interaction = new EventEmitter();
+
+ /** Emitted when the chip is destroyed. */
+ @Output() readonly destroyed: EventEmitter = new EventEmitter();
+
+ /** Emitted when a chip is to be removed. */
+ @Output() readonly removed: EventEmitter = new EventEmitter();
+
+ /** The MDC foundation containing business logic for MDC chip. */
+ _chipFoundation: MDCChipFoundation;
+
+ /** The unstyled chip selector for this component. */
+ protected basicChipAttrName = 'mat-basic-chip';
+
+ /** Subject that emits when the component has been destroyed. */
+ private _destroyed = new Subject();
+
+ /** The ripple renderer for this chip. */
+ private _rippleRenderer: RippleRenderer;
+
+ /**
+ * Implemented as part of RippleTarget. Configures ripple animation to match MDC Ripple.
+ * @docs-private
+ */
+ rippleConfig: RippleConfig = {
+ animation: {
+ enterDuration: 225 /* MDCRippleFoundation.numbers.DEACTIVATION_TIMEOUT_MS */,
+ exitDuration: 150 /* MDCRippleFoundation.numbers.FG_DEACTIVATION_MS */,
+ }
+ };
+
+ /**
+ * Implemented as part of RippleTarget. Whether ripples are disabled on interaction.
+ * @docs-private
+ */
+ get rippleDisabled(): boolean {
+ return this.disabled || this.disableRipple || this._isBasicChip();
+ }
+
+ /** The chip's leading icon. */
+ @ContentChild(MatChipAvatar, {static: false}) leadingIcon: MatChipAvatar;
+
+ /** The chip's trailing icon. */
+ @ContentChild(MatChipTrailingIcon, {static: false}) trailingIcon: MatChipTrailingIcon;
+
+ /**
+ * Implementation of the MDC chip adapter interface.
+ * These methods are called by the chip foundation.
+ */
+ protected _chipAdapter: MDCChipAdapter = {
+ addClass: (className) => this._setMdcClass(className, true),
+ removeClass: (className) => this._setMdcClass(className, false),
+ hasClass: (className) => this._elementRef.nativeElement.classList.contains(className),
+ addClassToLeadingIcon: (className) => this.leadingIcon.setClass(className, true),
+ removeClassFromLeadingIcon: (className) => this.leadingIcon.setClass(className, false),
+ eventTargetHasClass: (target: EventTarget | null, className: string) => {
+ return target ? (target as Element).classList.contains(className) : false;
+ },
+ notifyInteraction: () => this.interaction.emit(this.id),
+ notifySelection: () => {
+ // No-op. We call dispatchSelectionEvent ourselves in MatChipOption, because we want to
+ // specify whether selection occurred via user input.
+ },
+ notifyTrailingIconInteraction: () => this.trailingIconInteraction.emit(this.id),
+ notifyRemoval: () => this.removed.emit({chip: this}),
+ getComputedStyleValue: (propertyName) => {
+ return window.getComputedStyle(this._elementRef.nativeElement).getPropertyValue(propertyName);
+ },
+ setStyleProperty: (propertyName: string, value: string) => {
+ this._elementRef.nativeElement.style.setProperty(propertyName, value);
+ },
+ hasLeadingIcon: () => { return !!this.leadingIcon; },
+ // The 2 functions below are used by the MDC ripple, which we aren't using,
+ // so they will never be called
+ getRootBoundingClientRect: () => this._elementRef.nativeElement.getBoundingClientRect(),
+ getCheckmarkBoundingClientRect: () => { return null; },
+ };
+
+ constructor(
+ public _changeDetectorRef: ChangeDetectorRef,
+ readonly _elementRef: ElementRef,
+ private _platform: Platform,
+ private _ngZone: NgZone) {
+ super(_elementRef);
+ this._chipFoundation = new MDCChipFoundation(this._chipAdapter);
+ }
+
+ ngAfterContentInit() {
+ this._initTrailingIcon();
+ }
+
+ ngAfterViewInit() {
+ this._initRipple();
+ this._chipFoundation.init();
+ }
+
+ ngOnDestroy() {
+ this.destroyed.emit({chip: this});
+ this._destroyed.next();
+ this._destroyed.complete();
+ this._rippleRenderer._removeTriggerEvents();
+ this._chipFoundation.destroy();
+ }
+
+ /** Allows for programmatic focusing of the chip. */
+ focus(): void {
+ if (this.disabled) {
+ return;
+ }
+
+ if (!this._hasFocus) {
+ this._elementRef.nativeElement.focus();
+ this._onFocus.next({chip: this});
+ }
+ this._hasFocus = true;
+ }
+
+ /** Resets the state of the chip when it loses focus. */
+ _blur(): void {
+ this._hasFocus = false;
+ this._onBlur.next({chip: this});
+ }
+
+ /** Handles click events on the chip. */
+ _handleClick(event: MouseEvent) {
+ if (this.disabled) {
+ event.preventDefault();
+ } else {
+ this._handleInteraction(event);
+ event.stopPropagation();
+ }
+ }
+
+ /** Registers this chip with the trailing icon, and subscribes to trailing icon events. */
+ _initTrailingIcon() {
+ if (this.trailingIcon) {
+ this.trailingIcon.parentChip = this;
+ this._chipFoundation.setShouldRemoveOnTrailingIconClick(this.trailingIcon.shouldRemove);
+ this._listenToTrailingIconInteraction();
+ }
+ }
+
+ /** Handles interaction with the trailing icon. */
+ _listenToTrailingIconInteraction() {
+ this.trailingIcon.interaction
+ .pipe(takeUntil(this._destroyed))
+ .subscribe((event) => {
+ if (!this.disabled) {
+ this._chipFoundation.handleTrailingIconInteraction(event);
+ }
+ });
+ }
+
+ /**
+ * Allows for programmatic removal of the chip. Called when the DELETE or BACKSPACE
+ * keys are pressed.
+ *
+ * Informs any listeners of the removal request. Does not remove the chip from the DOM.
+ */
+ remove(): void {
+ if (this.removable) {
+ this._chipFoundation.beginExit();
+ }
+ }
+
+ /** Whether this chip is a basic (unstyled) chip. */
+ _isBasicChip() {
+ const element = this._elementRef.nativeElement as HTMLElement;
+ return element.hasAttribute(this.basicChipAttrName) ||
+ element.tagName.toLowerCase() === this.basicChipAttrName;
+ }
+
+ /** Sets whether the given CSS class should be applied to the MDC chip. */
+ private _setMdcClass(cssClass: string, active: boolean) {
+ const classes = this._elementRef.nativeElement.classList;
+ active ? classes.add(cssClass) : classes.remove(cssClass);
+ this._changeDetectorRef.markForCheck();
+ }
+
+ /** Initializes the ripple renderer. */
+ private _initRipple() {
+ this._rippleRenderer =
+ new RippleRenderer(this, this._ngZone, this._elementRef, this._platform);
+ this._rippleRenderer.setupTriggerEvents(this._elementRef.nativeElement);
+ }
+
+ /** Forwards interaction events to the MDC chip foundation. */
+ _handleInteraction(event: MouseEvent | KeyboardEvent) {
+ if (!this.disabled) {
+ this._chipFoundation.handleInteraction(event);
+ }
+ }
+}
diff --git a/src/material-experimental/mdc-chips/chips.scss b/src/material-experimental/mdc-chips/chips.scss
index f61450904c9a..ddb53690e650 100644
--- a/src/material-experimental/mdc-chips/chips.scss
+++ b/src/material-experimental/mdc-chips/chips.scss
@@ -1 +1,51 @@
-// TODO: MDC core styles here.
+@import '@material/chips/mixins';
+@import '../mdc-helpers/mdc-helpers';
+
+@include mdc-chip-without-ripple($query: $mat-base-styles-query);
+@include mdc-chip-set-core-styles($query: $mat-base-styles-query);
+
+
+// The MDC chip styles related to hover and focus states are intertwined with the MDC ripple styles.
+// We currently don't use the MDC ripple due to size concerns, therefore we need to add some
+// additional styles to restore these states.
+.mat-mdc-chip:not(.mat-mdc-chip-disabled) {
+ &:hover, &:focus {
+ &::after {
+ content: '';
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ background: rgba(0, 0, 0, 0.2);
+ top: 0;
+ left:0;
+ pointer-events: none;
+ }
+ }
+}
+
+// Angular Material supports disabled chips, which MDC does not.
+// Dim the disabled chips and stop MDC from changing the icon color on click.
+.mat-mdc-chip-disabled.mat-mdc-chip {
+ opacity: 0.4;
+
+ .mat-mdc-chip-trailing-icon {
+ pointer-events: none;
+ }
+}
+
+// Angular Material supports vertically-stacked chips, which MDC does not.
+.mat-mdc-chip-set-stacked {
+ flex-direction: column;
+ align-items: flex-start;
+
+ .mat-mdc-standard-chip {
+ width: 100%;
+ }
+}
+
+// Add styles for the matChipInputFor input element.
+$mat-chip-input-width: 150px;
+
+input.mat-chip-input {
+ flex: 1 0 $mat-chip-input-width;
+}
diff --git a/src/material-experimental/mdc-chips/module.ts b/src/material-experimental/mdc-chips/module.ts
index d556ee664fe1..2bc2eaaa8791 100644
--- a/src/material-experimental/mdc-chips/module.ts
+++ b/src/material-experimental/mdc-chips/module.ts
@@ -6,16 +6,55 @@
* found in the LICENSE file at https://angular.io/license
*/
+import {ENTER} from '@angular/cdk/keycodes';
import {CommonModule} from '@angular/common';
import {NgModule} from '@angular/core';
-import {MatCommonModule} from '@angular/material/core';
-import {MatChipCell} from './chip-cell';
+import {ErrorStateMatcher, MatCommonModule} from '@angular/material/core';
+import {MatChip, MatChipAvatar, MatChipCssInternalOnly, MatChipTrailingIcon} from './chip';
+import {MatChipRow} from './chip-row';
+import {MatChipOption} from './chip-option';
+import {MAT_CHIPS_DEFAULT_OPTIONS, MatChipsDefaultOptions} from './chip-default-options';
+import {MatChipInput} from './chip-input';
+import {MatChipSet} from './chip-set';
import {MatChipGrid} from './chip-grid';
+import {MatChipListbox} from './chip-listbox';
@NgModule({
imports: [MatCommonModule, CommonModule],
- exports: [MatChipCell, MatChipGrid, MatCommonModule],
- declarations: [MatChipCell, MatChipGrid],
+ exports: [
+ MatChip,
+ MatChipAvatar,
+ MatChipCssInternalOnly,
+ MatChipGrid,
+ MatChipInput,
+ MatChipListbox,
+ MatChipOption,
+ MatChipRow,
+ MatChipSet,
+ MatChipTrailingIcon,
+ MatCommonModule
+ ],
+ declarations: [
+ MatChip,
+ MatChipAvatar,
+ MatChipCssInternalOnly,
+ MatChipGrid,
+ MatChipInput,
+ MatChipListbox,
+ MatChipOption,
+ MatChipRow,
+ MatChipSet,
+ MatChipTrailingIcon
+ ],
+ providers: [
+ ErrorStateMatcher,
+ {
+ provide: MAT_CHIPS_DEFAULT_OPTIONS,
+ useValue: {
+ separatorKeyCodes: [ENTER]
+ } as MatChipsDefaultOptions
+ }
+ ]
})
export class MatChipsModule {
}
diff --git a/src/material-experimental/mdc-chips/public-api.ts b/src/material-experimental/mdc-chips/public-api.ts
index 52144a2ff370..dab8fb850ee8 100644
--- a/src/material-experimental/mdc-chips/public-api.ts
+++ b/src/material-experimental/mdc-chips/public-api.ts
@@ -6,6 +6,12 @@
* found in the LICENSE file at https://angular.io/license
*/
-export * from './chip-cell';
+export * from './chip';
+export * from './chip-option';
+export * from './chip-row';
+export * from './chip-set';
+export * from './chip-listbox';
export * from './chip-grid';
export * from './module';
+export * from './chip-input';
+export * from './chip-default-options';
diff --git a/yarn.lock b/yarn.lock
index 681964797bad..19049761f651 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -522,10 +522,10 @@
dependencies:
tslib "^1.9.3"
-"@material/auto-init@^1.0.0":
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/@material/auto-init/-/auto-init-1.0.0.tgz#0431b03f1bd533f57c4532657e236fe47711f3ab"
- integrity sha512-xlWEjjyQquXMi9xz3RH7HW/5dsLGkVYQnm1WCYw2a6JIpIlipkkLEzBNc//agiQongIBbLndL07gO7WQeSohNA==
+"@material/auto-init@^2.3.0":
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/@material/auto-init/-/auto-init-2.3.0.tgz#3d32507675939ab0d7c9f05b075805ed24d22095"
+ integrity sha512-WFlX3xdZjp/gWZiY0+poFjO12fl8t8C9M82kkQLb6WaUkoQ1YHeV2xQSDLY4IvwcKXb70IpcU3Tqgh+aoiHC0w==
dependencies:
"@material/base" "^1.0.0"
tslib "^1.9.3"
@@ -537,76 +537,77 @@
dependencies:
tslib "^1.9.3"
-"@material/button@^1.1.0":
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/@material/button/-/button-1.1.0.tgz#dbb46c953040d3a161346e1d3cd057159b9a3c34"
- integrity sha512-P1oZyyC1ELRe26vdnmax+fO3BWNmftDqHDDlQbJ+gfYMDQsNQtZNJU16ZbnVHsnzEXOpFj729imbmuLfnz8Nbg==
+"@material/button@^2.3.0":
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/@material/button/-/button-2.3.0.tgz#eff1349babf3f929bd99f86742df988185247d43"
+ integrity sha512-xFyZjm0pmisfSU6OHO3Wk2zZX51N9D0DohmaPoXnZt2unfD8bCwRmlmD7TTUQCLnuBoIaqrYShpcRTGp4K5dMw==
dependencies:
"@material/elevation" "^1.1.0"
"@material/feature-targeting" "^0.44.1"
- "@material/ripple" "^1.1.0"
+ "@material/ripple" "^2.3.0"
"@material/rtl" "^0.42.0"
- "@material/shape" "^1.0.0"
+ "@material/shape" "^1.1.1"
"@material/theme" "^1.1.0"
- "@material/typography" "^1.0.0"
+ "@material/typography" "^2.3.0"
-"@material/card@^1.1.0":
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/@material/card/-/card-1.1.0.tgz#d6e884079f6ae256884e06e3e1b5716c2ae14ab2"
- integrity sha512-ZuzMwnFZx0qMTiQK/QHoEClsOQ8TbfIpYm1N5gVvnGgbLF6xxoLD2zZFS9/l3eJxrDOxsrqqgxxfjGlJB5dFRg==
+"@material/card@^2.3.0":
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/@material/card/-/card-2.3.0.tgz#f59ac3b16d21d21af5747a530624f14622286c29"
+ integrity sha512-udVIanyz3KhZ8IfZjX2Yg+YJxU+oYdP9RbCM3hl7l1aqvNr8WSAy/YZ60Ue54fuN+0CpXiNpz1J8GyDktUZKKg==
dependencies:
"@material/elevation" "^1.1.0"
"@material/feature-targeting" "^0.44.1"
- "@material/ripple" "^1.1.0"
+ "@material/ripple" "^2.3.0"
"@material/rtl" "^0.42.0"
- "@material/shape" "^1.0.0"
+ "@material/shape" "^1.1.1"
"@material/theme" "^1.1.0"
-"@material/checkbox@^1.1.0":
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/@material/checkbox/-/checkbox-1.1.0.tgz#f01c4f70e028ac26aa2bbe4ee6de7d7a1384f701"
- integrity sha512-jCrkG8VkN63uH+YRhu7RWIsrYGw2Gu89OEPe+sqGYWsr+BbOuzBBqxrM09rUkXykCyi8gAbxpWZPVGCmcDHtJQ==
+"@material/checkbox@^2.3.0":
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/@material/checkbox/-/checkbox-2.3.0.tgz#3fd2d8ad4d602b8486f841fc561e01107f06b4a3"
+ integrity sha512-ejDn0CyXITF8mKcBZHCLa0fc0a/agej9o4viMLOXXkUIfOY3NJuY11MyZ07MxAgin11NK9HpUX7cIxu3upV+6w==
dependencies:
"@material/animation" "^1.0.0"
"@material/base" "^1.0.0"
"@material/dom" "^1.1.0"
"@material/feature-targeting" "^0.44.1"
- "@material/ripple" "^1.1.0"
+ "@material/ripple" "^2.3.0"
"@material/rtl" "^0.42.0"
"@material/theme" "^1.1.0"
tslib "^1.9.3"
-"@material/chips@^1.1.0":
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/@material/chips/-/chips-1.1.0.tgz#e4b698dfaece9c21bf4205eb65909b17388147e8"
- integrity sha512-+cIscrfKBFhXMaOHgNWx46U7zctXX6toMMG9lxY+uqH9ALo/NeScdZp/kc0C3z0shvRzXjZfvl5rM28rKjnizw==
+"@material/chips@^2.3.0":
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/@material/chips/-/chips-2.3.0.tgz#c778dfd61ab072a9771e69edb8b00917a6faf558"
+ integrity sha512-jIyThwx3Ax8mFQSmNtfMsI66zJINPis1zZ5LBFTEDN2+iEf7KUgnke8UmQzQac4fPkdZQpsXuiUrMNe4PGf8iA==
dependencies:
"@material/animation" "^1.0.0"
"@material/base" "^1.0.0"
- "@material/checkbox" "^1.1.0"
+ "@material/checkbox" "^2.3.0"
"@material/elevation" "^1.1.0"
- "@material/ripple" "^1.1.0"
- "@material/shape" "^1.0.0"
+ "@material/feature-targeting" "^0.44.1"
+ "@material/ripple" "^2.3.0"
+ "@material/shape" "^1.1.1"
"@material/theme" "^1.1.0"
- "@material/typography" "^1.0.0"
+ "@material/typography" "^2.3.0"
tslib "^1.9.3"
-"@material/dialog@^1.1.0":
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/@material/dialog/-/dialog-1.1.0.tgz#62f6bf31a7329ff4948609fb0e377ea336654972"
- integrity sha512-XbFTTdzzRhjClLMDFxz8SR/mwwCBpQL6N7Z7UzX7SgD51UJmd19SsXPItFquRKk3mkNuoFBBcR4KJ6julAW+Fg==
+"@material/dialog@^2.3.0":
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/@material/dialog/-/dialog-2.3.0.tgz#4525daf31e1d65e50c67f1cc944e69184134734a"
+ integrity sha512-T0xEKUW4uY5ZK7S92bdCVjFo9naasTRFNTXccIk+lBkbUlTsCOJMtix+dSJcj8ba8NOZjjq7phfAQqnNKUOhvg==
dependencies:
"@material/animation" "^1.0.0"
"@material/base" "^1.0.0"
"@material/dom" "^1.1.0"
"@material/elevation" "^1.1.0"
"@material/feature-targeting" "^0.44.1"
- "@material/ripple" "^1.1.0"
+ "@material/ripple" "^2.3.0"
"@material/rtl" "^0.42.0"
- "@material/shape" "^1.0.0"
+ "@material/shape" "^1.1.1"
"@material/theme" "^1.1.0"
- "@material/typography" "^1.0.0"
- focus-trap "^4.0.2"
+ "@material/typography" "^2.3.0"
+ focus-trap "^5.0.0"
tslib "^1.9.3"
"@material/dom@^1.1.0":
@@ -616,21 +617,21 @@
dependencies:
tslib "^1.9.3"
-"@material/drawer@^1.1.0":
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/@material/drawer/-/drawer-1.1.0.tgz#624cc794ac94f09a08551d6b0030163d1dc2b4fb"
- integrity sha512-IKQejVv9oSf1sUoAZ7BdV8fhTVDfMfZXfAOQBpw6QyfQujGkNVZatOsvbRcOFPu3naNZFS7brbHRiBH2ArgMQQ==
+"@material/drawer@^2.3.0":
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/@material/drawer/-/drawer-2.3.0.tgz#be81ef24d21f77b2d716dbd8240c02c9dccce2c9"
+ integrity sha512-BIrRLkqUO2SLkSRPWtXNwF4ZFmP1NOYynNelPZOdHZOIa6HcgmXzCL5p7hInJ5MDar/xMTA84CSO6Vf1byQwHA==
dependencies:
"@material/animation" "^1.0.0"
"@material/base" "^1.0.0"
"@material/elevation" "^1.1.0"
- "@material/list" "^1.1.0"
- "@material/ripple" "^1.1.0"
+ "@material/list" "^2.3.0"
+ "@material/ripple" "^2.3.0"
"@material/rtl" "^0.42.0"
- "@material/shape" "^1.0.0"
+ "@material/shape" "^1.1.1"
"@material/theme" "^1.1.0"
- "@material/typography" "^1.0.0"
- focus-trap "^4.0.2"
+ "@material/typography" "^2.3.0"
+ focus-trap "^5.0.0"
tslib "^1.9.3"
"@material/elevation@^1.1.0":
@@ -642,179 +643,179 @@
"@material/feature-targeting" "^0.44.1"
"@material/theme" "^1.1.0"
-"@material/fab@^1.1.0":
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/@material/fab/-/fab-1.1.0.tgz#37d25f1c48283a19ac5ebb0cdc8a51fc972cb1e0"
- integrity sha512-oCvo/4TFri+agTkGvEd5lREuwowInYw6OBbwNmGxpWQs4QGlMGHRS9/h8sfMYNZxs2TrwNUCwf/DK0WZEvniLQ==
+"@material/fab@^2.3.0":
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/@material/fab/-/fab-2.3.0.tgz#d2302b6f21bd84c7fa6d4196079d39bf7abd3cc2"
+ integrity sha512-XsRTbUMsH4pE/E8CkpAahwNywzwiz73nyBC0YxhzAxfHVO4oGwjcUsVlnFMSITj7K8aEyD/uMyAYwgISuAH83g==
dependencies:
"@material/animation" "^1.0.0"
"@material/elevation" "^1.1.0"
"@material/feature-targeting" "^0.44.1"
- "@material/ripple" "^1.1.0"
+ "@material/ripple" "^2.3.0"
"@material/rtl" "^0.42.0"
- "@material/shape" "^1.0.0"
+ "@material/shape" "^1.1.1"
"@material/theme" "^1.1.0"
- "@material/typography" "^1.0.0"
+ "@material/typography" "^2.3.0"
"@material/feature-targeting@^0.44.1":
version "0.44.1"
resolved "https://registry.yarnpkg.com/@material/feature-targeting/-/feature-targeting-0.44.1.tgz#afafc80294e5efab94bee31a187273d43d34979a"
integrity sha512-90cc7njn4aHbH9UxY8qgZth1W5JgOgcEdWdubH1t7sFkwqFxS5g3zgxSBt46TygFBVIXNZNq35Xmg80wgqO7Pg==
-"@material/floating-label@^1.1.0":
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/@material/floating-label/-/floating-label-1.1.0.tgz#505b9f8c7628d0498a18b165c387d4c34840ca60"
- integrity sha512-7q7V+9o9XesgMnK11up9z+BcRFwtLIAIqVTCL3liKRARNHuzw9FGrGMKhTJUKvLZ3z0bM1+FmmVlA3q9FJWehQ==
+"@material/floating-label@^2.3.0":
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/@material/floating-label/-/floating-label-2.3.0.tgz#001aa73bc035b8bf913dabc2926361f8c1458f28"
+ integrity sha512-OYcNmf+mVzW+rphDgVkyWES+SbivA6Y2+0amVDD9E9X6hLjO+L1dtIP3rC7lp0Y2Ey9rkuRORsUoriFvN7xQQw==
dependencies:
"@material/animation" "^1.0.0"
"@material/base" "^1.0.0"
"@material/rtl" "^0.42.0"
"@material/theme" "^1.1.0"
- "@material/typography" "^1.0.0"
+ "@material/typography" "^2.3.0"
tslib "^1.9.3"
-"@material/form-field@^1.1.0":
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/@material/form-field/-/form-field-1.1.0.tgz#4def529d10c416840bdfe5988a320b8c491e7cc1"
- integrity sha512-/m382G90rctenVAbPwTzAVtQGm9Bjnnvde03WPijFE6kirczi6pJWns+cFtN///e5nUc5fhBwNdpEnJmoUYvQg==
+"@material/form-field@^2.3.0":
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/@material/form-field/-/form-field-2.3.0.tgz#0f36b50ad8c96d6aa856fa74084e5e2128218724"
+ integrity sha512-f+RmxxoARS1UV5/yLhZJ9x2b7ueSKZJJCvaUfuppbv/oIkas0dYeW6lIkGo1ienNZyr2ZdW/j2KiL47LahYP/Q==
dependencies:
"@material/base" "^1.0.0"
"@material/feature-targeting" "^0.44.1"
- "@material/ripple" "^1.1.0"
+ "@material/ripple" "^2.3.0"
"@material/rtl" "^0.42.0"
"@material/theme" "^1.1.0"
- "@material/typography" "^1.0.0"
+ "@material/typography" "^2.3.0"
tslib "^1.9.3"
-"@material/grid-list@^1.1.0":
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/@material/grid-list/-/grid-list-1.1.0.tgz#f92dba86ebd26480bc6407d2c6851214b7d21bbc"
- integrity sha512-YukGKO4EzJca5DktfcNKCcrE6B3j/0CznCS0yfAoYagGZxsmmxa1wY4BcObuocIP31/xksRMUCieRbWpKzBhBQ==
+"@material/grid-list@^2.3.0":
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/@material/grid-list/-/grid-list-2.3.0.tgz#159e58321407c49527bc5a02dfbfcb1a8445060a"
+ integrity sha512-7SrUnhXW2iA0voUUGrwYM0PVx+BK9fsgW8GPakIJFIsRO9ES0VafaOl9DQLsjB8q39jL1nYmwIf/CMfeAlN/dQ==
dependencies:
"@material/base" "^1.0.0"
"@material/feature-targeting" "^0.44.1"
"@material/rtl" "^0.42.0"
"@material/theme" "^1.1.0"
- "@material/typography" "^1.0.0"
+ "@material/typography" "^2.3.0"
tslib "^1.9.3"
-"@material/icon-button@^1.1.0":
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/@material/icon-button/-/icon-button-1.1.0.tgz#a7c649b3549e6e9ecd9c82a8ef124757bfcc98b8"
- integrity sha512-3HYOfFhlgpY32HyNt1thQJfTycjy0Cfd4B4IZmt2irfaETdj6JuP0wKs9kdEx1VHTbfWFkJHENArz0lPW+6r2A==
+"@material/icon-button@^2.3.0":
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/@material/icon-button/-/icon-button-2.3.0.tgz#1160ecccd201161a1a38033732beed95a0624497"
+ integrity sha512-EZhdCn9a9livj9rGTUtDQ3UmF2zkAkaysWzgyDaGUih9rAbZThtGE68DhBWjBoe/+FDAOZ/vXnR23FdCsITmmw==
dependencies:
"@material/base" "^1.0.0"
"@material/feature-targeting" "^0.44.1"
- "@material/ripple" "^1.1.0"
+ "@material/ripple" "^2.3.0"
"@material/theme" "^1.1.0"
tslib "^1.9.3"
-"@material/image-list@^1.1.0":
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/@material/image-list/-/image-list-1.1.0.tgz#944f51d2ce1e4415407f7eeaae1405e0523b6c83"
- integrity sha512-X4ZlrzMbtZ80kkZQQHTR6CJEgBbCuQ3bYcJiXeTYS5jvl7frtiqf7CPkkrAZ4ftaaS/qiJgWMZUz3U5YaaAAsQ==
+"@material/image-list@^2.3.0":
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/@material/image-list/-/image-list-2.3.0.tgz#05f1836f93decf77529f7ce42d73ee6e9d31117e"
+ integrity sha512-0hV+G743pUhZbkwppY6J7QujWS7TMmYJ6KGLMabArkBwk/BgFk5HTHR7Wn1n3ZCId18JpEBo9XzLcSOZc6fNAg==
dependencies:
"@material/feature-targeting" "^0.44.1"
- "@material/shape" "^1.0.0"
+ "@material/shape" "^1.1.1"
"@material/theme" "^1.1.0"
- "@material/typography" "^1.0.0"
+ "@material/typography" "^2.3.0"
"@material/layout-grid@^0.41.0":
version "0.41.0"
resolved "https://registry.yarnpkg.com/@material/layout-grid/-/layout-grid-0.41.0.tgz#2e7d3be76313e0684d573b10c2c6a88b3230d251"
integrity sha512-Sa5RNoTGgfIojqJ9E94p7/k11V6q/tGk7HwKi4AQNAPjxield0zcl3G/SbsSb8YSHoK+D+7OXDN+n11x6EqF7g==
-"@material/line-ripple@^1.1.0":
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/@material/line-ripple/-/line-ripple-1.1.0.tgz#0fef69c14ddf42877f10eed05c1a2489808f1cf4"
- integrity sha512-XqCxDNfgkh9zq0IVlTEFVjmQV8hx8m4vxLFM5qwHDDqcKPlX/Lfc8M43fmm9uE1CaJBC6whMGPvOt/dIla+RUg==
+"@material/line-ripple@^2.3.0":
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/@material/line-ripple/-/line-ripple-2.3.0.tgz#e7d876f1dad0a7e418ccfefed875443fc1f303a0"
+ integrity sha512-gDjUlrM6P182ldY4CYiBMcdcUtch1DWd1osgp5STJXxth6ukRCNIdDigrKCHtyJKB8eXeNORwWs39xWZOGihbA==
dependencies:
"@material/animation" "^1.0.0"
"@material/base" "^1.0.0"
"@material/theme" "^1.1.0"
tslib "^1.9.3"
-"@material/linear-progress@^1.1.0":
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/@material/linear-progress/-/linear-progress-1.1.0.tgz#559921d4274fce0131fdf146698582d292855d6a"
- integrity sha512-MIK0cD/o1rTLREtAfTK3v60h7A0/wAu8/3v9dPTMlAPe+Y5gOarAizYWM3r/bp88sGO4gU8nPK0X9/daD28pNw==
+"@material/linear-progress@^2.3.0":
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/@material/linear-progress/-/linear-progress-2.3.0.tgz#07ab97443e04a8075b2ca8d38bc97a7d60a4bd4a"
+ integrity sha512-GouSTtxMRpU2lpjhKXcKFTJOePkZoPZTnXN/o4PxUJFeGp9TJcDQwyz0+WcaLPJ/MmCsXZWXJzd+oBWVOkca+A==
dependencies:
"@material/animation" "^1.0.0"
"@material/base" "^1.0.0"
"@material/theme" "^1.1.0"
tslib "^1.9.3"
-"@material/list@^1.1.0":
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/@material/list/-/list-1.1.0.tgz#e4f0282ceb23c7c8704b9160feab666be5db2ab7"
- integrity sha512-+NqVwqhRX08kjDatwZRO/LiiOw9gl7eq2Ogi8hrWkXqYn9ARfUq3K74MeTIit9f7BGPobs86dXBaQ/mjI5HSXg==
+"@material/list@^2.3.0":
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/@material/list/-/list-2.3.0.tgz#07f8d7194495f487d440bbe9fd09a2d3a68cee06"
+ integrity sha512-uuHWXpaXvPuOaQtQXwrgNc+WTTwBSwU/es65KJJcGrpc/o8Q3mYwMepotMN7E7/L75Wxz2w6uejnoM3zGZfvqg==
dependencies:
"@material/base" "^1.0.0"
"@material/dom" "^1.1.0"
"@material/feature-targeting" "^0.44.1"
- "@material/ripple" "^1.1.0"
+ "@material/ripple" "^2.3.0"
"@material/rtl" "^0.42.0"
- "@material/shape" "^1.0.0"
+ "@material/shape" "^1.1.1"
"@material/theme" "^1.1.0"
- "@material/typography" "^1.0.0"
+ "@material/typography" "^2.3.0"
tslib "^1.9.3"
-"@material/menu-surface@^1.1.0":
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/@material/menu-surface/-/menu-surface-1.1.0.tgz#76bf59fca940f53e4b1f41a3e24db8d6f9b73105"
- integrity sha512-FjCA6TBgeY+mwNcjfeq5/3TCSyhtXxHrT/xQrz1LyrztuHI4Qu7bTb496pXlfPsZHQuwmo9PqW+pwP78noDf9A==
+"@material/menu-surface@^2.3.0":
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/@material/menu-surface/-/menu-surface-2.3.0.tgz#5d82b4ed7b7124000a2e1fe76034e94f0a433b83"
+ integrity sha512-jJ1MyeJnEJUO0Z7kNxvqN0xruWbTT2XKHCiApQcJHHkeibWfbWJdhXcx5aO4FMf/TVcy3ADSxDTdvc6AYrBX0g==
dependencies:
"@material/animation" "^1.0.0"
"@material/base" "^1.0.0"
"@material/elevation" "^1.1.0"
"@material/feature-targeting" "^0.44.1"
"@material/rtl" "^0.42.0"
- "@material/shape" "^1.0.0"
+ "@material/shape" "^1.1.1"
"@material/theme" "^1.1.0"
tslib "^1.9.3"
-"@material/menu@^1.1.0":
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/@material/menu/-/menu-1.1.0.tgz#708110143985a9e98d811277e00f8ad85b277738"
- integrity sha512-oEubeu4h5EWeaOWbDwstmWkzBSq/2qVtu476CLmaAI5joUilXaPBKpryv4F+ZRIDlVQjs5Ey7uBotxDwDELPQA==
+"@material/menu@^2.3.0":
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/@material/menu/-/menu-2.3.0.tgz#29d7b3557efcf627796cfaf532754f5c6f2d120b"
+ integrity sha512-XPI6w4x5c9ACwKBdujcTskBlisWlEgrb09Sa+s0vjhqBJVZVAUJT1j0OpG8tArNUqQiFssXBa/JuJIe6sMAK1A==
dependencies:
"@material/base" "^1.0.0"
"@material/feature-targeting" "^0.44.1"
- "@material/list" "^1.1.0"
- "@material/menu-surface" "^1.1.0"
- "@material/ripple" "^1.1.0"
+ "@material/list" "^2.3.0"
+ "@material/menu-surface" "^2.3.0"
+ "@material/ripple" "^2.3.0"
"@material/rtl" "^0.42.0"
tslib "^1.9.3"
-"@material/notched-outline@^1.1.0":
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/@material/notched-outline/-/notched-outline-1.1.0.tgz#de1326b762e7e0002a90e15a06e34561334d9234"
- integrity sha512-J/MCe3an5lTxzYZPzHxblbVZu51OKeum4BU9PeV9WJW+IBGBAnQmT2uf53rqAzhEchIpv39MYmJLPMJghVxWAg==
+"@material/notched-outline@^2.3.0":
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/@material/notched-outline/-/notched-outline-2.3.0.tgz#707c7329a01fe80b269a1c4689769b812d825eb1"
+ integrity sha512-EDhpCGcMi1gW12qzXsdKHr6aYxa80s94tn9/G1r7eJu/BOVTYt20qjqoYQJE6i8XxcD1t5xcNdQ7StgGpk2MGA==
dependencies:
"@material/animation" "^1.0.0"
"@material/base" "^1.0.0"
- "@material/floating-label" "^1.1.0"
+ "@material/floating-label" "^2.3.0"
"@material/rtl" "^0.42.0"
- "@material/shape" "^1.0.0"
+ "@material/shape" "^1.1.1"
"@material/theme" "^1.1.0"
tslib "^1.9.3"
-"@material/radio@^1.1.0":
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/@material/radio/-/radio-1.1.0.tgz#15154c6c50d39b2b21b079dc2025a822a699e2b7"
- integrity sha512-ySocik+l1fInopaA8hDfByJahv1UywUNtUcG7+hLLOhxQx4XVpUJUIjU/dlvVDYJB0QZQatm2asKi2cYVPlIsQ==
+"@material/radio@^2.3.0":
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/@material/radio/-/radio-2.3.0.tgz#6d990edb88a3aea610895d11591d93814faac44a"
+ integrity sha512-l22cFvA/cZj/tsyDJVk1xayXGUTFFXp8yFe4SuGRW00/hNgGNF44rOSjN5H41iPU3oD1AreQL8r43fWW4eMH8w==
dependencies:
"@material/animation" "^1.0.0"
"@material/base" "^1.0.0"
"@material/feature-targeting" "^0.44.1"
- "@material/ripple" "^1.1.0"
+ "@material/ripple" "^2.3.0"
"@material/theme" "^1.1.0"
tslib "^1.9.3"
-"@material/ripple@^1.1.0":
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/@material/ripple/-/ripple-1.1.0.tgz#236016fb30c8366faf143297df2c38166d84ffbc"
- integrity sha512-mkfDBZAmxjpRG7V9TrfOmLxt1g/wvGHCXtYPgvH7W8ozjf53edqxLOFENEdvHbie27y9nyixzXn0gzU0HnxSeA==
+"@material/ripple@^2.3.0":
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/@material/ripple/-/ripple-2.3.0.tgz#4701b2cfecddc4c83ae62c777ae2bf9607988705"
+ integrity sha512-ejXR0nstERofFhssRyFlwOLgebwm2uGbarHtWZ2/+7QY2Th/Z1wOqNb2h/WRoShsJXK11RUsochb6BJrg30u7w==
dependencies:
"@material/animation" "^1.0.0"
"@material/base" "^1.0.0"
@@ -828,137 +829,137 @@
resolved "https://registry.yarnpkg.com/@material/rtl/-/rtl-0.42.0.tgz#1836e78186c2d8b996f6fbf97adab203535335bc"
integrity sha512-VrnrKJzhmspsN8WXHuxxBZ69yM5IwhCUqWr1t1eNfw3ZEvEj7i1g3P31HGowKThIN1dc1Wh4LE14rCISWCtv5w==
-"@material/select@^1.1.0":
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/@material/select/-/select-1.1.0.tgz#07589c1f8b6c9ac2dc1341a85cb7197e4f489474"
- integrity sha512-sl5dt+sepqfo4HC82085q9sSCaXoK1swmeQ54Yp8KWlit9CtfV4/QZT83NwF9eEbQF3NmtCdh9T2ueaX//uihg==
+"@material/select@^2.3.1":
+ version "2.3.1"
+ resolved "https://registry.yarnpkg.com/@material/select/-/select-2.3.1.tgz#25d23a554a6d04f3873cf857e37ea509c6b7f33b"
+ integrity sha512-0NLXp0G0eEFcXpO8VaqfBPBu8yUcXcgiWQwsusT2/ek9bA8eT/rzkwvcK++K9i9V3wTPcWPLz5tnZbPASHfGOw==
dependencies:
"@material/animation" "^1.0.0"
"@material/base" "^1.0.0"
- "@material/floating-label" "^1.1.0"
- "@material/line-ripple" "^1.1.0"
- "@material/menu" "^1.1.0"
- "@material/menu-surface" "^1.1.0"
- "@material/notched-outline" "^1.1.0"
- "@material/ripple" "^1.1.0"
+ "@material/floating-label" "^2.3.0"
+ "@material/line-ripple" "^2.3.0"
+ "@material/menu" "^2.3.0"
+ "@material/menu-surface" "^2.3.0"
+ "@material/notched-outline" "^2.3.0"
+ "@material/ripple" "^2.3.0"
"@material/rtl" "^0.42.0"
- "@material/shape" "^1.0.0"
+ "@material/shape" "^1.1.1"
"@material/theme" "^1.1.0"
- "@material/typography" "^1.0.0"
+ "@material/typography" "^2.3.0"
tslib "^1.9.3"
-"@material/shape@^1.0.0":
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/@material/shape/-/shape-1.0.0.tgz#bef17de1f282e5c71138338a34078d8402308f65"
- integrity sha512-zfXEacPQZmH+ujVtaFyfAsYiF46j1QCcFzJeZVouG4pznrbA7XD6614Ywg0wbyWX5iB6hD52ld/IN+R/6oxKqA==
+"@material/shape@^1.1.1":
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/@material/shape/-/shape-1.1.1.tgz#7a5368694bc3555e69ea547950904b46fa1024bf"
+ integrity sha512-Jge/h1XBLjdLlam4QMSzVgM99e/N8+elQROPkltqVP7eyLc17BwM3aP5cLVfZDgrJgvsjUxbgAP1H1j8sqmUyg==
dependencies:
"@material/feature-targeting" "^0.44.1"
-"@material/slider@^1.1.0":
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/@material/slider/-/slider-1.1.0.tgz#a4940b345d80e2b08d68ba284b9f724119a0a8e8"
- integrity sha512-JV/Phpt38LUVb2sQCr2pBsf0faw0E5LI7ag3dNF3hDq1f3F9Vcu+a8oNADi2M+TNlyelltAMKnc9guARwhDLKw==
+"@material/slider@^2.3.0":
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/@material/slider/-/slider-2.3.0.tgz#abf8c17b0e51842c807f26eac0519728185f2c9d"
+ integrity sha512-TlSwQ0d8ciD2qsZjrNrj0Qmw6AWlqkNJ3CrXVyqeaaL4JzS/oKKk7Ib83fJbwsSSeK6ipH3zGM7138R8OSj94g==
dependencies:
"@material/animation" "^1.0.0"
"@material/base" "^1.0.0"
"@material/rtl" "^0.42.0"
"@material/theme" "^1.1.0"
- "@material/typography" "^1.0.0"
+ "@material/typography" "^2.3.0"
tslib "^1.9.3"
-"@material/snackbar@^1.1.0":
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/@material/snackbar/-/snackbar-1.1.0.tgz#1a7886f4d76903abbd0f0f16db3374c4a27e7d97"
- integrity sha512-gPTvAg/Djzz2FHVruGNvdvOkNoeZPctc1hasksBmJPHhQ0nQtc3JsRPfNTLAy32k8aF2+JlrEa1YGMAnltkuPA==
+"@material/snackbar@^2.3.0":
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/@material/snackbar/-/snackbar-2.3.0.tgz#c5e3700834373808eb8845c020de38054008447d"
+ integrity sha512-/ac/i6MXhIVvQAwJ20SCBFos2TZ66YUJRUfLH8C7UpH+ukbrZNohLwFzoVFOZtbGMOfS5jLzlVTI8LBNezng6Q==
dependencies:
"@material/animation" "^1.0.0"
"@material/base" "^1.0.0"
- "@material/button" "^1.1.0"
+ "@material/button" "^2.3.0"
"@material/dom" "^1.1.0"
- "@material/icon-button" "^1.1.0"
- "@material/ripple" "^1.1.0"
+ "@material/icon-button" "^2.3.0"
+ "@material/ripple" "^2.3.0"
"@material/rtl" "^0.42.0"
- "@material/shape" "^1.0.0"
+ "@material/shape" "^1.1.1"
"@material/theme" "^1.1.0"
- "@material/typography" "^1.0.0"
+ "@material/typography" "^2.3.0"
tslib "^1.9.3"
-"@material/switch@^1.1.0":
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/@material/switch/-/switch-1.1.0.tgz#c64b19523c4a495de9df022940ce5a5d2c21cbb2"
- integrity sha512-hwgPzMgZksmJB/hOHQqrT7SZ6TpYaBtkW6BDhCojluBzaMDfDZxeXbNAhAYCO+tJI9p+CndPF+InsocCQDAJXA==
+"@material/switch@^2.3.0":
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/@material/switch/-/switch-2.3.0.tgz#3ea7acc35d645d83a718e40d27e72ccca083e271"
+ integrity sha512-c21j5VJFUAoey1fPGZaQaRcFeXZcP6dPYewUgFdGURtHnbVHqVVf5GEAcnFsk2NuBN2mDqKLQ3AKbozIAi65hw==
dependencies:
"@material/animation" "^1.0.0"
"@material/base" "^1.0.0"
"@material/dom" "^1.1.0"
"@material/elevation" "^1.1.0"
"@material/feature-targeting" "^0.44.1"
- "@material/ripple" "^1.1.0"
+ "@material/ripple" "^2.3.0"
"@material/rtl" "^0.42.0"
"@material/theme" "^1.1.0"
tslib "^1.9.3"
-"@material/tab-bar@^1.1.0":
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/@material/tab-bar/-/tab-bar-1.1.0.tgz#5bd278ee26d8a3d4bab151e187c694cf2810bc3d"
- integrity sha512-uhtIRiyOvBVMZ5Wf6QctPfI/amXfWR9sXjHpYj+So6GPRNgmeQFkRJ1yyxU3KthBoyzfLvj57sgWEsBlSkiJzw==
+"@material/tab-bar@^2.3.0":
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/@material/tab-bar/-/tab-bar-2.3.0.tgz#f2d7b714cdec4ed766a4b1b7f3a871e19a4d47a1"
+ integrity sha512-p+uDZrhwolnwsaY42kehEc8mbe5ekzjlZEsoCAJCN9AL8VnbsRSS/qePFllhz7roUGrH6AYmYYoHNw1Qx2XT+Q==
dependencies:
"@material/base" "^1.0.0"
"@material/elevation" "^1.1.0"
- "@material/tab" "^1.1.0"
- "@material/tab-scroller" "^1.1.0"
+ "@material/tab" "^2.3.0"
+ "@material/tab-scroller" "^2.3.0"
tslib "^1.9.3"
-"@material/tab-indicator@^1.1.0":
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/@material/tab-indicator/-/tab-indicator-1.1.0.tgz#2c37e8fe146d84a99482509cbb785f67336f0463"
- integrity sha512-wbZMFCfQo62einr/Ju9PDAeFpRR/Ori5oV2lcsF1uSQXeLxougarwcbk0egWnEtY4Wa6dPgM5P6wQQsYnIeUIQ==
+"@material/tab-indicator@^2.3.0":
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/@material/tab-indicator/-/tab-indicator-2.3.0.tgz#db2eb467303e4b7cd09bc92b23f1d575dd5aee37"
+ integrity sha512-E9jlESIfjTOoo2pZn7KoTeXEW2jI/2RVtu6IEbhfESLuxPGYmxgO+QxIodEW0VoJhogaa6hR/mFnYBVLbh3JZQ==
dependencies:
"@material/animation" "^1.0.0"
"@material/base" "^1.0.0"
"@material/theme" "^1.1.0"
tslib "^1.9.3"
-"@material/tab-scroller@^1.1.0":
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/@material/tab-scroller/-/tab-scroller-1.1.0.tgz#081340c54cba7dbc1c299bd996cc223da3ab85e7"
- integrity sha512-o5AuG8kwZaghuZ4kvYXIsp2tp3plHoGArF5wM7UgERk+8vMeMRmanagnpYvFwrtGgsta8EQa9sKVZ90dMIDZvg==
+"@material/tab-scroller@^2.3.0":
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/@material/tab-scroller/-/tab-scroller-2.3.0.tgz#226b73770bd5b4b03026124e662fd2e4ce15e425"
+ integrity sha512-WLAH6po+3SrMJSsnq056aBcLxNMg4TVGyST+TO/wdWOg6QYFSgrjaYpZQEJIkPO+W8Jt54xieI7GPoU5KYB7AA==
dependencies:
"@material/animation" "^1.0.0"
"@material/base" "^1.0.0"
"@material/dom" "^1.1.0"
- "@material/tab" "^1.1.0"
+ "@material/tab" "^2.3.0"
tslib "^1.9.3"
-"@material/tab@^1.1.0":
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/@material/tab/-/tab-1.1.0.tgz#05191b5db9d41a946c2ea639e1f244c242c2c40c"
- integrity sha512-62uttUJURFM36AmXNSrH5ewxJJv632pryEXllpBB1WCHYv6QqRwgfQ6dTjE5xdgSa4k+1X2BJCgmD/0ghc233A==
+"@material/tab@^2.3.0":
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/@material/tab/-/tab-2.3.0.tgz#539763bb1d50b06f6c36ed19fc83bf76d6cb84a9"
+ integrity sha512-6AdTvTWndPnFC4R/cXo9yZa1M6AGUoeKKaDBaTi2/8cDT0Hmwf5dJTq/T2E0XPtXEW/cELmw7x7yYy3B7IpeQA==
dependencies:
"@material/base" "^1.0.0"
- "@material/ripple" "^1.1.0"
+ "@material/ripple" "^2.3.0"
"@material/rtl" "^0.42.0"
- "@material/tab-indicator" "^1.1.0"
+ "@material/tab-indicator" "^2.3.0"
"@material/theme" "^1.1.0"
- "@material/typography" "^1.0.0"
+ "@material/typography" "^2.3.0"
tslib "^1.9.3"
-"@material/textfield@^1.1.0":
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/@material/textfield/-/textfield-1.1.0.tgz#ad26b3463f42be86540a7305577b9ecf03bb3b01"
- integrity sha512-3+vFrWGVrGxvP4ICsniS8Sn9HHRaBz+/EZgpPnsYRcE0LcE2ABjUlwZQzeHV1h1qc0XmulmLUon8WFGvTxISkA==
+"@material/textfield@^2.3.1":
+ version "2.3.1"
+ resolved "https://registry.yarnpkg.com/@material/textfield/-/textfield-2.3.1.tgz#893183f86b3c6f5913247f56f8516dcbf83a1cd8"
+ integrity sha512-4w/HyJjNUTnYwuLpOaYEDN45bdFkBcZLC3QfCn3I0B3qrbM1zcpmDUxG0DYyGj1oXCM/sDJXMk07u6CcXpJU5w==
dependencies:
"@material/animation" "^1.0.0"
"@material/base" "^1.0.0"
"@material/dom" "^1.1.0"
- "@material/floating-label" "^1.1.0"
- "@material/line-ripple" "^1.1.0"
- "@material/notched-outline" "^1.1.0"
- "@material/ripple" "^1.1.0"
+ "@material/floating-label" "^2.3.0"
+ "@material/line-ripple" "^2.3.0"
+ "@material/notched-outline" "^2.3.0"
+ "@material/ripple" "^2.3.0"
"@material/rtl" "^0.42.0"
- "@material/shape" "^1.0.0"
+ "@material/shape" "^1.1.1"
"@material/theme" "^1.1.0"
- "@material/typography" "^1.0.0"
+ "@material/typography" "^2.3.0"
tslib "^1.9.3"
"@material/theme@^1.1.0":
@@ -968,38 +969,38 @@
dependencies:
"@material/feature-targeting" "^0.44.1"
-"@material/toolbar@^1.1.0":
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/@material/toolbar/-/toolbar-1.1.0.tgz#6304845dc175fe3ebbaaae5245f0f0d12ece7f0a"
- integrity sha512-NrO3Z1YH6AGbLjS91BT4BTaUL1ghXHB0Wrre+rT1mmQ7VXtr+AaTSshu9xc7z3wcVuPotR41OOdZqSCDWjzBrg==
+"@material/toolbar@^2.3.0":
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/@material/toolbar/-/toolbar-2.3.0.tgz#6cc4635909606a1dff3c51619f269aff927ada48"
+ integrity sha512-lRMxWLjThMl8jI9iv0By1ABsbESIclIn3jTugvZUmYQQwPmJQ38YzdA6I27gJKLiljZxteaShIOd1TQHnjI4Fw==
dependencies:
"@material/base" "^1.0.0"
"@material/elevation" "^1.1.0"
- "@material/ripple" "^1.1.0"
+ "@material/ripple" "^2.3.0"
"@material/rtl" "^0.42.0"
"@material/theme" "^1.1.0"
- "@material/typography" "^1.0.0"
+ "@material/typography" "^2.3.0"
tslib "^1.9.3"
-"@material/top-app-bar@^1.1.0":
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/@material/top-app-bar/-/top-app-bar-1.1.0.tgz#2b3412dd5eeba8b24a46ab184e17e68fa0693581"
- integrity sha512-fxVKFolNzPG4+LqH8GUxDKfjRZtQNDYX8sGD6c2pKbk1tigyiAGBO/Py31RQQkbAdCKPdmnanGJK2gewaUdw4g==
+"@material/top-app-bar@^2.3.0":
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/@material/top-app-bar/-/top-app-bar-2.3.0.tgz#59805864b3693cd54e2d96c6f2981221e51f9e0e"
+ integrity sha512-Y62htWToLGABuxHbFYZxHrR99uVAQa7kdlix7tB7h4J5C/SIIKe9plRMh5e0mqhGf006okrAS0h8J+6KM4hh0Q==
dependencies:
"@material/animation" "^1.0.0"
"@material/base" "^1.0.0"
"@material/elevation" "^1.1.0"
- "@material/ripple" "^1.1.0"
+ "@material/ripple" "^2.3.0"
"@material/rtl" "^0.42.0"
- "@material/shape" "^1.0.0"
+ "@material/shape" "^1.1.1"
"@material/theme" "^1.1.0"
- "@material/typography" "^1.0.0"
+ "@material/typography" "^2.3.0"
tslib "^1.9.3"
-"@material/typography@^1.0.0":
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/@material/typography/-/typography-1.0.0.tgz#327ecfcac5ee3af8a3a102f3f125a761202f4189"
- integrity sha512-Oeqbjci1cC7jTE8/n3dwnkqKe9ZeWiaE+rgMtRYtRFw1HvAw14SpGA5EEAS/Li2Hu2KZ50FYCe3HYqShfxtChA==
+"@material/typography@^2.3.0":
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/@material/typography/-/typography-2.3.0.tgz#fe2180c697172227f0745cda684ecafdaba3f8dd"
+ integrity sha512-NtWVVvwG9Te6/kuIl4fEwDcXGCS7mfPgo5CKPyxcK6y0hJHv6yRHpipJT9D4ZlXw0sQx9B33doOK7iYJtwBBZw==
dependencies:
"@material/feature-targeting" "^0.44.1"
@@ -4711,12 +4712,12 @@ flatted@^2.0.0:
resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.0.tgz#55122b6536ea496b4b44893ee2608141d10d9916"
integrity sha512-R+H8IZclI8AAkSBRQJLVOsxwAoHd6WC40b4QTNWIjzAa6BXOBfQcM587MXDTVPeYaopFNWHUFLx7eNmHDSxMWg==
-focus-trap@^4.0.2:
- version "4.0.2"
- resolved "https://registry.yarnpkg.com/focus-trap/-/focus-trap-4.0.2.tgz#4ee2b96547c9ea0e4252a2d4b2cca68944194663"
- integrity sha512-HtLjfAK7Hp2qbBtLS6wEznID1mPT+48ZnP2nkHzgjpL4kroYHg0CdqJ5cTXk+UO5znAxF5fRUkhdyfgrhh8Lzw==
+focus-trap@^5.0.0:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/focus-trap/-/focus-trap-5.0.1.tgz#285f9df2cd9f5ef82dd1abb5d8a70e66cd4f99e3"
+ integrity sha512-vU7zEdL3y+kfkuwBbT9456JH8QfyemdcdZ2gKMfmgLyAs9NQAkSVQBSZmb9nlb1cVMo+iCsddqeGJog00pd2EQ==
dependencies:
- tabbable "^3.1.2"
+ tabbable "^4.0.0"
xtend "^4.0.1"
follow-redirects@^1.0.0, follow-redirects@^1.2.5, follow-redirects@^1.3.0:
@@ -7641,53 +7642,53 @@ matchdep@^2.0.0:
resolve "^1.4.0"
stack-trace "0.0.10"
-material-components-web@^1.1.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/material-components-web/-/material-components-web-1.1.0.tgz#47446115e474c3dda1f21bffa8361903760cc3ed"
- integrity sha512-bTivBr0oZUfw9N32G/PVZ4RRzH8ge7V8CXlGblVB4WE1HJQU5FVgp7cN1z5vYQhEGOCtyT0VlHjzWvOWKMDxFg==
+material-components-web@^2.3.1:
+ version "2.3.1"
+ resolved "https://registry.yarnpkg.com/material-components-web/-/material-components-web-2.3.1.tgz#04595079e6e9784bfab58519d866ef9df35cb246"
+ integrity sha512-jDf4T0h8XiSadvrodpkGBtybRI5bAqtfz6MJyq4Kw/PzUPT6jQ5T8EunJyhEXOkjdfWy5VLRfltICTioCtbq6Q==
dependencies:
"@material/animation" "^1.0.0"
- "@material/auto-init" "^1.0.0"
+ "@material/auto-init" "^2.3.0"
"@material/base" "^1.0.0"
- "@material/button" "^1.1.0"
- "@material/card" "^1.1.0"
- "@material/checkbox" "^1.1.0"
- "@material/chips" "^1.1.0"
- "@material/dialog" "^1.1.0"
+ "@material/button" "^2.3.0"
+ "@material/card" "^2.3.0"
+ "@material/checkbox" "^2.3.0"
+ "@material/chips" "^2.3.0"
+ "@material/dialog" "^2.3.0"
"@material/dom" "^1.1.0"
- "@material/drawer" "^1.1.0"
+ "@material/drawer" "^2.3.0"
"@material/elevation" "^1.1.0"
- "@material/fab" "^1.1.0"
+ "@material/fab" "^2.3.0"
"@material/feature-targeting" "^0.44.1"
- "@material/floating-label" "^1.1.0"
- "@material/form-field" "^1.1.0"
- "@material/grid-list" "^1.1.0"
- "@material/icon-button" "^1.1.0"
- "@material/image-list" "^1.1.0"
+ "@material/floating-label" "^2.3.0"
+ "@material/form-field" "^2.3.0"
+ "@material/grid-list" "^2.3.0"
+ "@material/icon-button" "^2.3.0"
+ "@material/image-list" "^2.3.0"
"@material/layout-grid" "^0.41.0"
- "@material/line-ripple" "^1.1.0"
- "@material/linear-progress" "^1.1.0"
- "@material/list" "^1.1.0"
- "@material/menu" "^1.1.0"
- "@material/menu-surface" "^1.1.0"
- "@material/notched-outline" "^1.1.0"
- "@material/radio" "^1.1.0"
- "@material/ripple" "^1.1.0"
+ "@material/line-ripple" "^2.3.0"
+ "@material/linear-progress" "^2.3.0"
+ "@material/list" "^2.3.0"
+ "@material/menu" "^2.3.0"
+ "@material/menu-surface" "^2.3.0"
+ "@material/notched-outline" "^2.3.0"
+ "@material/radio" "^2.3.0"
+ "@material/ripple" "^2.3.0"
"@material/rtl" "^0.42.0"
- "@material/select" "^1.1.0"
- "@material/shape" "^1.0.0"
- "@material/slider" "^1.1.0"
- "@material/snackbar" "^1.1.0"
- "@material/switch" "^1.1.0"
- "@material/tab" "^1.1.0"
- "@material/tab-bar" "^1.1.0"
- "@material/tab-indicator" "^1.1.0"
- "@material/tab-scroller" "^1.1.0"
- "@material/textfield" "^1.1.0"
+ "@material/select" "^2.3.1"
+ "@material/shape" "^1.1.1"
+ "@material/slider" "^2.3.0"
+ "@material/snackbar" "^2.3.0"
+ "@material/switch" "^2.3.0"
+ "@material/tab" "^2.3.0"
+ "@material/tab-bar" "^2.3.0"
+ "@material/tab-indicator" "^2.3.0"
+ "@material/tab-scroller" "^2.3.0"
+ "@material/textfield" "^2.3.1"
"@material/theme" "^1.1.0"
- "@material/toolbar" "^1.1.0"
- "@material/top-app-bar" "^1.1.0"
- "@material/typography" "^1.0.0"
+ "@material/toolbar" "^2.3.0"
+ "@material/top-app-bar" "^2.3.0"
+ "@material/typography" "^2.3.0"
math-random@^1.0.1:
version "1.0.1"
@@ -10994,10 +10995,10 @@ systemjs@0.19.43:
dependencies:
when "^3.7.5"
-tabbable@^3.1.2:
- version "3.1.2"
- resolved "https://registry.yarnpkg.com/tabbable/-/tabbable-3.1.2.tgz#f2d16cccd01f400e38635c7181adfe0ad965a4a2"
- integrity sha512-wjB6puVXTYO0BSFtCmWQubA/KIn7Xvajw0x0l6eJUudMG/EAiJvIUnyNX6xO4NpGrJ16lbD0eUseB9WxW0vlpQ==
+tabbable@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/tabbable/-/tabbable-4.0.0.tgz#5bff1d1135df1482cf0f0206434f15eadbeb9261"
+ integrity sha512-H1XoH1URcBOa/rZZWxLxHCtOdVUEev+9vo5YdYhC9tCY4wnybX+VQrCYuy9ubkg69fCBxCONJOSLGfw0DWMffQ==
table@^5.0.0:
version "5.1.1"