Skip to content

Commit 172ee1d

Browse files
zelliottmmalerba
authored andcommitted
feat(material/chip): Add focus indicator (#18019)
* Add focus indicators to mat-chip. Use a dynamically added element as the ripple target * _document should be an optional param to avoid breaking change. * Updated chips API golden.
1 parent 2aa21c6 commit 172ee1d

File tree

4 files changed

+39
-5
lines changed

4 files changed

+39
-5
lines changed

src/material/chips/chip.ts

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9+
import {DOCUMENT} from '@angular/common';
910
import {FocusableOption} from '@angular/cdk/a11y';
1011
import {coerceBooleanProperty} from '@angular/cdk/coercion';
1112
import {BACKSPACE, DELETE, SPACE} from '@angular/cdk/keycodes';
@@ -100,7 +101,7 @@ export class MatChipTrailingIcon {}
100101
inputs: ['color', 'disabled', 'disableRipple'],
101102
exportAs: 'matChip',
102103
host: {
103-
'class': 'mat-chip',
104+
'class': 'mat-chip mat-focus-indicator',
104105
'[attr.tabindex]': 'disabled ? null : -1',
105106
'role': 'option',
106107
'[class.mat-chip-selected]': 'selected',
@@ -123,6 +124,13 @@ export class MatChip extends _MatChipMixinBase implements FocusableOption, OnDes
123124
/** Reference to the RippleRenderer for the chip. */
124125
private _chipRipple: RippleRenderer;
125126

127+
/**
128+
* Reference to the element that acts as the chip's ripple target. This element is
129+
* dynamically added as a child node of the chip. The chip itself cannot be used as the
130+
* ripple target because it must be the host of the focus indicator.
131+
*/
132+
private _chipRippleTarget: HTMLElement;
133+
126134
/**
127135
* Ripple configuration for ripples that are launched on pointer down. The ripple config
128136
* is set to the global ripple options since we don't have any configurable options for
@@ -238,13 +246,22 @@ export class MatChip extends _MatChipMixinBase implements FocusableOption, OnDes
238246
// @breaking-change 8.0.0 `animationMode` parameter to become required.
239247
@Optional() @Inject(ANIMATION_MODULE_TYPE) animationMode?: string,
240248
// @breaking-change 9.0.0 `_changeDetectorRef` parameter to become required.
241-
private _changeDetectorRef?: ChangeDetectorRef) {
249+
private _changeDetectorRef?: ChangeDetectorRef,
250+
// @breaking-change 11.0.0 `_document` parameter to become required.
251+
@Optional() @Inject(DOCUMENT) _document?: any) {
242252
super(_elementRef);
243253

244254
this._addHostClassName();
245255

246-
this._chipRipple = new RippleRenderer(this, _ngZone, _elementRef, platform);
256+
// Dynamically create the ripple target, append it within the chip, and use it as the
257+
// chip's ripple target. Adding the class '.mat-chip-ripple' ensures that it will have
258+
// the proper styles.
259+
this._chipRippleTarget = (_document || document).createElement('div');
260+
this._chipRippleTarget.classList.add('mat-chip-ripple');
261+
this._elementRef.nativeElement.appendChild(this._chipRippleTarget);
262+
this._chipRipple = new RippleRenderer(this, _ngZone, this._chipRippleTarget, platform);
247263
this._chipRipple.setupTriggerEvents(_elementRef);
264+
248265
this.rippleConfig = globalRippleOptions || {};
249266
this._animationsDisabled = animationMode === 'NoopAnimations';
250267
}

src/material/chips/chips.scss

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ $mat-chip-remove-size: 18px;
2727

2828
.mat-chip {
2929
position: relative;
30-
overflow: hidden;
3130
box-sizing: border-box;
3231
-webkit-tap-highlight-color: transparent;
3332

@@ -171,6 +170,23 @@ $mat-chip-remove-size: 18px;
171170
}
172171
}
173172

173+
// Styles for the chip's dynamically added ripple target.
174+
.mat-chip-ripple {
175+
@include mat-fill;
176+
177+
// Disable pointer events for the ripple container and focus overlay because the container
178+
// will overlay the user content and we don't want to disable mouse events on the user content.
179+
// Pointer events can be safely disabled because the ripple trigger element is the host element.
180+
pointer-events: none;
181+
182+
// Inherit the border radius from the parent so that ripples don't exceed the parent button
183+
// boundaries.
184+
border-radius: inherit;
185+
186+
// Ensures that the ripple effect doesn't overflow the ripple target.
187+
overflow: hidden;
188+
}
189+
174190
.mat-chip-list-wrapper {
175191
display: flex;
176192
flex-direction: row;

src/material/core/focus-indicator/_focus-indicator.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
.mat-focus-indicator.mat-button-base::before,
3232
.mat-focus-indicator.mat-button-base::before,
3333
.mat-focus-indicator.mat-card::before,
34+
.mat-focus-indicator.mat-chip::before,
3435
.mat-focus-indicator.mat-sort-header-button::before {
3536
margin: $border-width * -2;
3637
}

tools/public_api_guard/material/chips.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export declare class MatChip extends _MatChipMixinBase implements FocusableOptio
2525
readonly selectionChange: EventEmitter<MatChipSelectionChange>;
2626
trailingIcon: MatChipTrailingIcon;
2727
value: any;
28-
constructor(_elementRef: ElementRef<HTMLElement>, _ngZone: NgZone, platform: Platform, globalRippleOptions: RippleGlobalOptions | null, animationMode?: string, _changeDetectorRef?: ChangeDetectorRef | undefined);
28+
constructor(_elementRef: ElementRef<HTMLElement>, _ngZone: NgZone, platform: Platform, globalRippleOptions: RippleGlobalOptions | null, animationMode?: string, _changeDetectorRef?: ChangeDetectorRef | undefined, _document?: any);
2929
_addHostClassName(): void;
3030
_blur(): void;
3131
_handleClick(event: Event): void;

0 commit comments

Comments
 (0)