|
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