Skip to content

Commit 78cfd3c

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 d3efac6 commit 78cfd3c

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 {BooleanInput, coerceBooleanProperty} from '@angular/cdk/coercion';
1112
import {BACKSPACE, DELETE, SPACE} from '@angular/cdk/keycodes';
@@ -105,7 +106,7 @@ export class MatChipTrailingIcon {}
105106
inputs: ['color', 'disabled', 'disableRipple', 'tabIndex'],
106107
exportAs: 'matChip',
107108
host: {
108-
'class': 'mat-chip',
109+
'class': 'mat-chip mat-focus-indicator',
109110
'[attr.tabindex]': 'disabled ? null : tabIndex',
110111
'role': 'option',
111112
'[class.mat-chip-selected]': 'selected',
@@ -128,6 +129,13 @@ export class MatChip extends _MatChipMixinBase implements FocusableOption, OnDes
128129
/** Reference to the RippleRenderer for the chip. */
129130
private _chipRipple: RippleRenderer;
130131

132+
/**
133+
* Reference to the element that acts as the chip's ripple target. This element is
134+
* dynamically added as a child node of the chip. The chip itself cannot be used as the
135+
* ripple target because it must be the host of the focus indicator.
136+
*/
137+
private _chipRippleTarget: HTMLElement;
138+
131139
/**
132140
* Ripple configuration for ripples that are launched on pointer down. The ripple config
133141
* is set to the global ripple options since we don't have any configurable options for
@@ -244,13 +252,22 @@ export class MatChip extends _MatChipMixinBase implements FocusableOption, OnDes
244252
@Optional() @Inject(ANIMATION_MODULE_TYPE) animationMode?: string,
245253
// @breaking-change 9.0.0 `_changeDetectorRef` parameter to become required.
246254
private _changeDetectorRef?: ChangeDetectorRef,
247-
@Attribute('tabindex') tabIndex?: string) {
255+
@Attribute('tabindex') tabIndex?: string,
256+
// @breaking-change 11.0.0 `_document` parameter to become required.
257+
@Optional() @Inject(DOCUMENT) _document?: any) {
248258
super(_elementRef);
249259

250260
this._addHostClassName();
251261

252-
this._chipRipple = new RippleRenderer(this, _ngZone, _elementRef, platform);
262+
// Dynamically create the ripple target, append it within the chip, and use it as the
263+
// chip's ripple target. Adding the class '.mat-chip-ripple' ensures that it will have
264+
// the proper styles.
265+
this._chipRippleTarget = (_document || document).createElement('div');
266+
this._chipRippleTarget.classList.add('mat-chip-ripple');
267+
this._elementRef.nativeElement.appendChild(this._chipRippleTarget);
268+
this._chipRipple = new RippleRenderer(this, _ngZone, this._chipRippleTarget, platform);
253269
this._chipRipple.setupTriggerEvents(_elementRef);
270+
254271
this.rippleConfig = globalRippleOptions || {};
255272
this._animationsDisabled = animationMode === 'NoopAnimations';
256273
this.tabIndex = tabIndex != null ? (parseInt(tabIndex) || -1) : -1;

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
@@ -29,7 +29,7 @@ export declare class MatChip extends _MatChipMixinBase implements FocusableOptio
2929
trailingIcon: MatChipTrailingIcon;
3030
get value(): any;
3131
set value(value: any);
32-
constructor(_elementRef: ElementRef<HTMLElement>, _ngZone: NgZone, platform: Platform, globalRippleOptions: RippleGlobalOptions | null, animationMode?: string, _changeDetectorRef?: ChangeDetectorRef | undefined, tabIndex?: string);
32+
constructor(_elementRef: ElementRef<HTMLElement>, _ngZone: NgZone, platform: Platform, globalRippleOptions: RippleGlobalOptions | null, animationMode?: string, _changeDetectorRef?: ChangeDetectorRef | undefined, tabIndex?: string, _document?: any);
3333
_addHostClassName(): void;
3434
_blur(): void;
3535
_handleClick(event: Event): void;

0 commit comments

Comments
 (0)