-
Notifications
You must be signed in to change notification settings - Fork 907
/
Copy pathchip.ts
142 lines (120 loc) · 3.77 KB
/
chip.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
/**
* @license
* Copyright 2023 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import '../../focus/md-focus-ring.js';
import '../../ripple/ripple.js';
import {html, LitElement, PropertyValues, TemplateResult} from 'lit';
import {property} from 'lit/decorators.js';
import {ClassInfo, classMap} from 'lit/directives/class-map.js';
import {mixinDelegatesAria} from '../../internal/aria/delegate.js';
// Separate variable needed for closure.
const chipBaseClass = mixinDelegatesAria(LitElement);
/**
* A chip component.
*
* @fires update-focus {Event} Dispatched when `disabled` is toggled. --bubbles
*/
export abstract class Chip extends chipBaseClass {
/** @nocollapse */
static override shadowRootOptions = {
...LitElement.shadowRootOptions,
delegatesFocus: true,
};
/**
* Whether or not the chip is disabled.
*
* Disabled chips are not focusable, unless `always-focusable` is set.
*/
@property({type: Boolean, reflect: true}) disabled = false;
/**
* When true, allow disabled chips to be focused with arrow keys.
*
* Add this when a chip needs increased visibility when disabled. See
* https://www.w3.org/WAI/ARIA/apg/practices/keyboard-interface/#kbd_disabled_controls
* for more guidance on when this is needed.
*/
@property({type: Boolean, attribute: 'always-focusable'})
alwaysFocusable = false;
/**
* The label of the chip.
*/
@property() label = '';
/**
* Only needed for SSR.
*
* Add this attribute when a chip has a `slot="icon"` to avoid a Flash Of
* Unstyled Content.
*/
@property({type: Boolean, reflect: true, attribute: 'has-icon'}) hasIcon =
false;
/**
* The `id` of the action the primary focus ring and ripple are for.
* TODO(b/310046938): use the same id for both elements
*/
protected abstract readonly primaryId: string;
/**
* Whether or not the primary ripple is disabled (defaults to `disabled`).
* Some chip actions such as links cannot be disabled.
*/
protected get rippleDisabled() {
return this.disabled;
}
override focus(options?: FocusOptions) {
if (this.disabled && !this.alwaysFocusable) {
return;
}
super.focus(options);
}
protected override render() {
return html`
<div class="container ${classMap(this.getContainerClasses())}">
${this.renderContainerContent()}
</div>
`;
}
protected override updated(changed: PropertyValues<Chip>) {
if (changed.has('disabled') && changed.get('disabled') !== undefined) {
this.dispatchEvent(new Event('update-focus', {bubbles: true}));
}
}
protected getContainerClasses(): ClassInfo {
return {
'disabled': this.disabled,
'has-icon': this.hasIcon,
};
}
protected renderContainerContent() {
return html`
${this.renderOutline()}
<md-focus-ring part="focus-ring" for=${this.primaryId}></md-focus-ring>
<md-ripple
for=${this.primaryId}
?disabled=${this.rippleDisabled}></md-ripple>
${this.renderPrimaryAction(this.renderPrimaryContent())}
`;
}
protected renderOutline() {
return html`<span class="outline"></span>`;
}
protected renderLeadingIcon(): TemplateResult {
return html`<slot name="icon" @slotchange=${this.handleIconChange}></slot>`;
}
protected abstract renderPrimaryAction(content: unknown): unknown;
private renderPrimaryContent() {
return html`
<span class="leading icon" aria-hidden="true">
${this.renderLeadingIcon()}
</span>
<span class="label">
<span class="label-text">${this.label}</span>
</span>
<span class="touch"></span>
`;
}
private handleIconChange(event: Event) {
const slot = event.target as HTMLSlotElement;
this.hasIcon = slot.assignedElements({flatten: true}).length > 0;
}
}