Skip to content

Commit f8bb2f1

Browse files
asynclizcopybara-github
authored andcommitted
feat(chips): add single select filter chip set
PiperOrigin-RevId: 538842612
1 parent 9c0336a commit f8bb2f1

File tree

5 files changed

+58
-11
lines changed

5 files changed

+58
-11
lines changed

chips/demo/demo.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ const collection =
1717
new Knob('label', {defaultValue: '', ui: textInput()}),
1818
new Knob('elevated', {defaultValue: false, ui: boolInput()}),
1919
new Knob('disabled', {defaultValue: false, ui: boolInput()}),
20+
new Knob('singleSelect', {defaultValue: false, ui: boolInput()}),
2021
new Knob('scrolling', {defaultValue: false, ui: boolInput()}),
2122
]);
2223

chips/demo/stories.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export interface StoryKnobs {
2020
label: string;
2121
elevated: boolean;
2222
disabled: boolean;
23+
singleSelect: boolean;
2324
scrolling: boolean;
2425
}
2526

@@ -89,10 +90,10 @@ const links: MaterialStoryInit<StoryKnobs> = {
8990
const filters: MaterialStoryInit<StoryKnobs> = {
9091
name: 'Filter chips',
9192
styles,
92-
render({label, elevated, disabled, scrolling}) {
93+
render({label, elevated, disabled, scrolling, singleSelect}) {
9394
const classes = {scrolling};
9495
return html`
95-
<md-chip-set class=${classMap(classes)}>
96+
<md-chip-set class=${classMap(classes)} ?single-select=${singleSelect}>
9697
<md-filter-chip
9798
label=${label || 'Filter chip'}
9899
?disabled=${disabled}

chips/lib/chip-set.ts

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
* SPDX-License-Identifier: Apache-2.0
55
*/
66

7-
import {html, isServer, LitElement} from 'lit';
8-
import {queryAssignedElements} from 'lit/decorators.js';
7+
import {html, isServer, LitElement, PropertyValues} from 'lit';
8+
import {property, queryAssignedElements} from 'lit/decorators.js';
99

1010
import {Chip} from './chip.js';
1111

@@ -15,9 +15,11 @@ import {Chip} from './chip.js';
1515
export class ChipSet extends LitElement {
1616
get chips() {
1717
return this.childElements.filter(
18-
(child): child is MaybeMultiActionChip => child instanceof Chip);
18+
(child): child is Chip => child instanceof Chip);
1919
}
2020

21+
@property({type: Boolean, attribute: 'single-select'}) singleSelect = false;
22+
2123
@queryAssignedElements({flatten: true})
2224
private readonly childElements!: HTMLElement[];
2325

@@ -26,6 +28,23 @@ export class ChipSet extends LitElement {
2628
if (!isServer) {
2729
this.addEventListener('focusin', this.updateTabIndices.bind(this));
2830
this.addEventListener('keydown', this.handleKeyDown.bind(this));
31+
this.addEventListener('selected', this.handleSelected.bind(this));
32+
}
33+
}
34+
35+
protected override updated(changed: PropertyValues<this>) {
36+
if (changed.has('singleSelect') && this.singleSelect) {
37+
let hasSelectedChip = false;
38+
for (const chip of this.chips as MaybeSelectableChip[]) {
39+
if (chip.selected === true) {
40+
if (!hasSelectedChip) {
41+
hasSelectedChip = true;
42+
continue;
43+
}
44+
45+
chip.selected = false;
46+
}
47+
}
2948
}
3049
}
3150

@@ -48,7 +67,7 @@ export class ChipSet extends LitElement {
4867
// Prevent default interactions, such as scrolling.
4968
event.preventDefault();
5069

51-
const {chips} = this;
70+
const {chips} = this as {chips: MaybeMultiActionChip[]};
5271
// Don't try to select another chip if there aren't any.
5372
if (chips.length < 2) {
5473
return;
@@ -122,8 +141,26 @@ export class ChipSet extends LitElement {
122141
chips[0]?.removeAttribute('tabindex');
123142
}
124143
}
144+
145+
private handleSelected(event: Event) {
146+
if (!this.singleSelect) {
147+
return;
148+
}
149+
150+
if ((event.target as MaybeSelectableChip).selected === true) {
151+
for (const chip of this.chips as MaybeSelectableChip[]) {
152+
if (chip !== event.target && chip.selected) {
153+
chip.selected = false;
154+
}
155+
}
156+
}
157+
}
125158
}
126159

127160
interface MaybeMultiActionChip extends Chip {
128161
focus(options?: FocusOptions&{trailing?: boolean}): void;
129162
}
163+
164+
interface MaybeSelectableChip extends Chip {
165+
selected?: boolean;
166+
}

chips/lib/filter-chip.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,10 @@ export class FilterChip extends MultiActionChip {
4141
});
4242
}
4343

44-
protected override updated(changedProperties: PropertyValues<FilterChip>) {
45-
if (changedProperties.has('selected') &&
46-
changedProperties.get('selected') !== undefined) {
44+
protected override updated(changed: PropertyValues<this>) {
45+
if (changed.has('selected') && changed.get('selected') !== undefined) {
4746
// Dispatch when `selected` changes, except for the first update.
48-
this.dispatchEvent(new Event('change', {bubbles: true}));
47+
this.dispatchEvent(new Event('selected', {bubbles: true}));
4948
}
5049
}
5150

chips/lib/input-chip.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* SPDX-License-Identifier: Apache-2.0
55
*/
66

7-
import {html, nothing} from 'lit';
7+
import {html, nothing, PropertyValues} from 'lit';
88
import {property, query} from 'lit/decorators.js';
99

1010
import {ARIAMixinStrict} from '../../internal/aria/aria.js';
@@ -52,6 +52,15 @@ export class InputChip extends MultiActionChip {
5252
@query('.trailing.action')
5353
protected readonly trailingAction!: HTMLElement|null;
5454

55+
protected override update(changed: PropertyValues<this>) {
56+
if (changed.has('selected') && changed.get('selected') !== undefined) {
57+
// Dispatch when `selected` changes, except for the first update.
58+
this.dispatchEvent(new Event('selected', {bubbles: true}));
59+
}
60+
61+
super.update(changed);
62+
}
63+
5564
protected override getContainerClasses() {
5665
return {
5766
...super.getContainerClasses(),

0 commit comments

Comments
 (0)