|
6 | 6 | * found in the LICENSE file at https://angular.io/license
|
7 | 7 | */
|
8 | 8 |
|
9 |
| -import { |
10 |
| - AfterViewInit, |
11 |
| - ChangeDetectorRef, |
12 |
| - Directive, |
13 |
| - ElementRef, |
14 |
| - Inject, |
15 |
| - Input, |
16 |
| - OnChanges, |
17 |
| - OnDestroy, |
18 |
| - SimpleChanges, |
19 |
| -} from '@angular/core'; |
20 |
| -import {DOCUMENT} from '@angular/common'; |
21 |
| -import { |
22 |
| - MDCChipActionAdapter, |
23 |
| - MDCChipActionFoundation, |
24 |
| - MDCChipActionType, |
25 |
| - MDCChipPrimaryActionFoundation, |
26 |
| -} from '@material/chips'; |
27 |
| -import {emitCustomEvent} from './emit-event'; |
28 |
| -import { |
29 |
| - CanDisable, |
30 |
| - HasTabIndex, |
31 |
| - mixinDisabled, |
32 |
| - mixinTabIndex, |
33 |
| -} from '@angular/material-experimental/mdc-core'; |
| 9 | +import {BooleanInput, coerceBooleanProperty} from '@angular/cdk/coercion'; |
| 10 | +import {ENTER, SPACE} from '@angular/cdk/keycodes'; |
| 11 | +import {Directive, ElementRef, Inject, Input} from '@angular/core'; |
| 12 | +import {HasTabIndex, mixinTabIndex} from '@angular/material-experimental/mdc-core'; |
| 13 | +import {MAT_CHIP} from './tokens'; |
34 | 14 |
|
35 |
| -const _MatChipActionMixinBase = mixinTabIndex(mixinDisabled(class {}), -1); |
| 15 | +abstract class _MatChipActionBase { |
| 16 | + abstract disabled: boolean; |
| 17 | +} |
| 18 | + |
| 19 | +const _MatChipActionMixinBase = mixinTabIndex(_MatChipActionBase, -1); |
36 | 20 |
|
37 | 21 | /**
|
38 |
| - * Interactive element within a chip. |
| 22 | + * Section within a chip. |
39 | 23 | * @docs-private
|
40 | 24 | */
|
41 | 25 | @Directive({
|
42 | 26 | selector: '[matChipAction]',
|
43 | 27 | inputs: ['disabled', 'tabIndex'],
|
44 | 28 | host: {
|
45 | 29 | 'class': 'mdc-evolution-chip__action mat-mdc-chip-action',
|
46 |
| - '[class.mdc-evolution-chip__action--primary]': `_getFoundation().actionType() === ${MDCChipActionType.PRIMARY}`, |
| 30 | + '[class.mdc-evolution-chip__action--primary]': '_isPrimary', |
47 | 31 | // Note that while our actions are interactive, we have to add the `--presentational` class,
|
48 | 32 | // in order to avoid some super-specific `:hover` styles from MDC.
|
49 |
| - '[class.mdc-evolution-chip__action--presentational]': `_getFoundation().actionType() === ${MDCChipActionType.PRIMARY}`, |
50 |
| - '[class.mdc-evolution-chip__action--trailing]': `_getFoundation().actionType() === ${MDCChipActionType.TRAILING}`, |
| 33 | + '[class.mdc-evolution-chip__action--presentational]': '_isPrimary', |
| 34 | + '[class.mdc-evolution-chip__action--trailing]': '!_isPrimary', |
51 | 35 | '[attr.tabindex]': '(disabled || !isInteractive) ? null : tabIndex',
|
52 | 36 | '[attr.disabled]': "disabled ? '' : null",
|
53 | 37 | '[attr.aria-disabled]': 'disabled',
|
54 | 38 | '(click)': '_handleClick($event)',
|
55 | 39 | '(keydown)': '_handleKeydown($event)',
|
56 | 40 | },
|
57 | 41 | })
|
58 |
| -export class MatChipAction |
59 |
| - extends _MatChipActionMixinBase |
60 |
| - implements AfterViewInit, OnDestroy, CanDisable, HasTabIndex, OnChanges |
61 |
| -{ |
62 |
| - private _document: Document; |
63 |
| - private _foundation: MDCChipActionFoundation; |
64 |
| - private _adapter: MDCChipActionAdapter = { |
65 |
| - focus: () => this.focus(), |
66 |
| - getAttribute: (name: string) => this._elementRef.nativeElement.getAttribute(name), |
67 |
| - setAttribute: (name: string, value: string) => { |
68 |
| - // MDC tries to update the tabindex directly in the DOM when navigating using the keyboard |
69 |
| - // which overrides our own handling. If we detect such a case, assign it to the same property |
70 |
| - // as the Angular binding in order to maintain consistency. |
71 |
| - if (name === 'tabindex') { |
72 |
| - this._updateTabindex(parseInt(value)); |
73 |
| - } else { |
74 |
| - this._elementRef.nativeElement.setAttribute(name, value); |
75 |
| - } |
76 |
| - }, |
77 |
| - removeAttribute: (name: string) => { |
78 |
| - if (name !== 'tabindex') { |
79 |
| - this._elementRef.nativeElement.removeAttribute(name); |
80 |
| - } |
81 |
| - }, |
82 |
| - getElementID: () => this._elementRef.nativeElement.id, |
83 |
| - emitEvent: <T>(eventName: string, data: T) => { |
84 |
| - emitCustomEvent<T>(this._elementRef.nativeElement, this._document, eventName, data, true); |
85 |
| - }, |
86 |
| - }; |
87 |
| - |
| 42 | +export class MatChipAction extends _MatChipActionMixinBase implements HasTabIndex { |
88 | 43 | /** Whether the action is interactive. */
|
89 | 44 | @Input() isInteractive = true;
|
90 | 45 |
|
91 |
| - _handleClick(event: MouseEvent) { |
92 |
| - // Usually these events can't happen while the chip is disabled since the browser won't |
93 |
| - // allow them which is what MDC seems to rely on, however the event can be faked in tests. |
94 |
| - if (!this.disabled && this.isInteractive) { |
95 |
| - this._foundation.handleClick(); |
96 |
| - event.preventDefault(); |
97 |
| - } |
98 |
| - } |
| 46 | + /** Whether this is the primary action in the chip. */ |
| 47 | + _isPrimary = true; |
99 | 48 |
|
100 |
| - _handleKeydown(event: KeyboardEvent) { |
101 |
| - // Usually these events can't happen while the chip is disabled since the browser won't |
102 |
| - // allow them which is what MDC seems to rely on, however the event can be faked in tests. |
103 |
| - if (!this.disabled && this.isInteractive) { |
104 |
| - this._foundation.handleKeydown(event); |
105 |
| - } |
| 49 | + /** Whether the action is disabled. */ |
| 50 | + @Input() |
| 51 | + get disabled(): boolean { |
| 52 | + return this._disabled || this._parentChip.disabled; |
106 | 53 | }
|
107 |
| - |
108 |
| - protected _createFoundation(adapter: MDCChipActionAdapter): MDCChipActionFoundation { |
109 |
| - return new MDCChipPrimaryActionFoundation(adapter); |
| 54 | + set disabled(value: BooleanInput) { |
| 55 | + this._disabled = coerceBooleanProperty(value); |
110 | 56 | }
|
| 57 | + private _disabled = false; |
111 | 58 |
|
112 | 59 | constructor(
|
113 |
| - public _elementRef: ElementRef, |
114 |
| - @Inject(DOCUMENT) _document: any, |
115 |
| - private _changeDetectorRef: ChangeDetectorRef, |
| 60 | + public _elementRef: ElementRef<HTMLElement>, |
| 61 | + @Inject(MAT_CHIP) |
| 62 | + protected _parentChip: { |
| 63 | + _handlePrimaryActionInteraction(): void; |
| 64 | + remove(): void; |
| 65 | + disabled: boolean; |
| 66 | + }, |
116 | 67 | ) {
|
117 | 68 | super();
|
118 |
| - this._foundation = this._createFoundation(this._adapter); |
119 | 69 |
|
120 | 70 | if (_elementRef.nativeElement.nodeName === 'BUTTON') {
|
121 | 71 | _elementRef.nativeElement.setAttribute('type', 'button');
|
122 | 72 | }
|
123 | 73 | }
|
124 | 74 |
|
125 |
| - ngAfterViewInit() { |
126 |
| - this._foundation.init(); |
127 |
| - this._foundation.setDisabled(this.disabled); |
128 |
| - } |
129 |
| - |
130 |
| - ngOnChanges(changes: SimpleChanges) { |
131 |
| - if (changes['disabled']) { |
132 |
| - this._foundation.setDisabled(this.disabled); |
133 |
| - } |
134 |
| - } |
135 |
| - |
136 |
| - ngOnDestroy() { |
137 |
| - this._foundation.destroy(); |
138 |
| - } |
139 |
| - |
140 | 75 | focus() {
|
141 | 76 | this._elementRef.nativeElement.focus();
|
142 | 77 | }
|
143 | 78 |
|
144 |
| - _getFoundation() { |
145 |
| - return this._foundation; |
| 79 | + _handleClick(event: MouseEvent) { |
| 80 | + if (!this.disabled && this.isInteractive && this._isPrimary) { |
| 81 | + event.preventDefault(); |
| 82 | + this._parentChip._handlePrimaryActionInteraction(); |
| 83 | + } |
146 | 84 | }
|
147 | 85 |
|
148 |
| - _updateTabindex(value: number) { |
149 |
| - this.tabIndex = value; |
150 |
| - this._changeDetectorRef.markForCheck(); |
| 86 | + _handleKeydown(event: KeyboardEvent) { |
| 87 | + if ( |
| 88 | + (event.keyCode === ENTER || event.keyCode === SPACE) && |
| 89 | + !this.disabled && |
| 90 | + this.isInteractive && |
| 91 | + this._isPrimary |
| 92 | + ) { |
| 93 | + event.preventDefault(); |
| 94 | + this._parentChip._handlePrimaryActionInteraction(); |
| 95 | + } |
151 | 96 | }
|
152 | 97 | }
|
0 commit comments