@redvars/peacock 3.3.1 → 3.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/dist/{IndividualComponent-tDnXrOLV.js → IndividualComponent-Dt5xirYG.js} +2 -2
  2. package/dist/{IndividualComponent-tDnXrOLV.js.map → IndividualComponent-Dt5xirYG.js.map} +1 -1
  3. package/dist/array-D5vjT2Xm.js +14 -0
  4. package/dist/array-D5vjT2Xm.js.map +1 -0
  5. package/dist/{button-trIfcqC7.js → button-ClzS8JLq.js} +3 -3
  6. package/dist/{button-trIfcqC7.js.map → button-ClzS8JLq.js.map} +1 -1
  7. package/dist/{button-group-DA7xoziD.js → button-group-BMS5WvaF.js} +4 -4
  8. package/dist/{button-group-DA7xoziD.js.map → button-group-BMS5WvaF.js.map} +1 -1
  9. package/dist/button-group.js +4 -4
  10. package/dist/button.js +3 -3
  11. package/dist/card.js +104 -0
  12. package/dist/card.js.map +1 -0
  13. package/dist/chart-bar-DbnXQgvS.js +1121 -0
  14. package/dist/chart-bar-DbnXQgvS.js.map +1 -0
  15. package/dist/chart-bar.js +259 -0
  16. package/dist/chart-bar.js.map +1 -0
  17. package/dist/chart-donut.js +4 -2
  18. package/dist/chart-donut.js.map +1 -1
  19. package/dist/chart-doughnut.js +4 -2
  20. package/dist/chart-doughnut.js.map +1 -1
  21. package/dist/chart-pie.js +4 -2
  22. package/dist/chart-pie.js.map +1 -1
  23. package/dist/chart-stacked-bar.js +401 -0
  24. package/dist/chart-stacked-bar.js.map +1 -0
  25. package/dist/{class-map-hJdvjl-W.js → class-map-59YGWLnx.js} +2 -2
  26. package/dist/{class-map-hJdvjl-W.js.map → class-map-59YGWLnx.js.map} +1 -1
  27. package/dist/clock.js +1 -1
  28. package/dist/code-editor.js +3 -3
  29. package/dist/code-highlighter.js +3 -3
  30. package/dist/custom-elements-jsdocs.json +2308 -766
  31. package/dist/custom-elements.json +3908 -2601
  32. package/dist/index.js +16 -9
  33. package/dist/index.js.map +1 -1
  34. package/dist/number-counter.js +2 -2
  35. package/dist/{observe-theme-change-BISF-Gl5.js → observe-theme-change-pALI5fmV.js} +2 -2
  36. package/dist/{observe-theme-change-BISF-Gl5.js.map → observe-theme-change-pALI5fmV.js.map} +1 -1
  37. package/dist/peacock-loader.js +25 -526
  38. package/dist/peacock-loader.js.map +1 -1
  39. package/dist/pie-Dz0IDiPt.js +537 -0
  40. package/dist/pie-Dz0IDiPt.js.map +1 -0
  41. package/dist/{tree-view-CLolVlU0.js → radio-b70_Ie9n.js} +2216 -901
  42. package/dist/radio-b70_Ie9n.js.map +1 -0
  43. package/dist/src/card/card.d.ts +27 -0
  44. package/dist/src/card/index.d.ts +1 -0
  45. package/dist/src/chart-bar/chart-bar.d.ts +53 -0
  46. package/dist/src/chart-bar/chart-stacked-bar.d.ts +78 -0
  47. package/dist/src/chart-bar/index.d.ts +2 -0
  48. package/dist/src/index.d.ts +6 -0
  49. package/dist/src/menu/menu-item/menu-item.d.ts +1 -1
  50. package/dist/src/radio/index.d.ts +1 -0
  51. package/dist/src/radio/radio.d.ts +73 -0
  52. package/dist/src/snackbar/index.d.ts +1 -0
  53. package/dist/src/snackbar/snackbar.d.ts +40 -0
  54. package/dist/src/tabs/tab-group.d.ts +1 -1
  55. package/dist/src/tabs/tab-panel.d.ts +1 -0
  56. package/dist/src/tabs/tab.d.ts +2 -1
  57. package/dist/{style-map-CfNHEkQp.js → style-map-DcB52w-l.js} +2 -2
  58. package/dist/{style-map-CfNHEkQp.js.map → style-map-DcB52w-l.js.map} +1 -1
  59. package/dist/test/card.test.d.ts +1 -0
  60. package/dist/test/chart-bar.test.d.ts +1 -0
  61. package/dist/test/snackbar.test.d.ts +1 -0
  62. package/dist/{transform-DRuHEvar.js → transform-DSwFSqzD.js} +13 -558
  63. package/dist/transform-DSwFSqzD.js.map +1 -0
  64. package/dist/tsconfig.tsbuildinfo +1 -1
  65. package/dist/{unsafe-html-CV6Je6HL.js → unsafe-html-C2r3PyzF.js} +2 -2
  66. package/dist/{unsafe-html-CV6Je6HL.js.map → unsafe-html-C2r3PyzF.js.map} +1 -1
  67. package/package.json +1 -1
  68. package/readme.md +2 -2
  69. package/src/card/card.scss +61 -0
  70. package/src/card/card.ts +38 -0
  71. package/src/card/index.ts +1 -0
  72. package/src/chart-bar/chart-bar.scss +58 -0
  73. package/src/chart-bar/chart-bar.ts +306 -0
  74. package/src/chart-bar/chart-stacked-bar.ts +402 -0
  75. package/src/chart-bar/index.ts +2 -0
  76. package/src/index.ts +6 -0
  77. package/src/menu/menu-item/menu-item.ts +1 -1
  78. package/src/peacock-loader.ts +18 -0
  79. package/src/radio/index.ts +1 -0
  80. package/src/radio/radio.scss +181 -0
  81. package/src/radio/radio.ts +362 -0
  82. package/src/snackbar/demo/index.html +29 -0
  83. package/src/snackbar/index.ts +1 -0
  84. package/src/snackbar/snackbar.scss +73 -0
  85. package/src/snackbar/snackbar.ts +151 -0
  86. package/src/tabs/tab-group.ts +57 -28
  87. package/src/tabs/tab-panel.scss +3 -3
  88. package/src/tabs/tab-panel.ts +2 -0
  89. package/src/tabs/tab.scss +76 -2
  90. package/src/tabs/tab.ts +29 -7
  91. package/src/tabs/tabs.ts +15 -3
  92. package/dist/transform-DRuHEvar.js.map +0 -1
  93. package/dist/tree-view-CLolVlU0.js.map +0 -1
@@ -0,0 +1,362 @@
1
+ import { html, LitElement } from 'lit';
2
+ import { property, query, state } from 'lit/decorators.js';
3
+ import { classMap } from 'lit/directives/class-map.js';
4
+ import { spread } from '../spread.js';
5
+ import styles from './radio.scss';
6
+
7
+ /**
8
+ * @label Radio
9
+ * @tag wc-radio
10
+ * @rawTag radio
11
+ * @summary Allows selection of a single option from a set.
12
+ * @overview
13
+ * <p>Radio buttons follow the Material Design 3 specifications with clear focus, hover, and selected states.</p>
14
+ *
15
+ * @cssprop --radio-size: Size of the outer radio circle.
16
+ * @cssprop --radio-dot-size: Size of the inner dot when selected.
17
+ * @cssprop --radio-selected-color: Color of the radio when selected.
18
+ * @cssprop --radio-unselected-color: Color of the radio outline when unselected.
19
+ * @cssprop --radio-state-layer-size: Size of the state layer for touch target.
20
+ * @cssprop --radio-disabled-opacity: Opacity applied when the radio is disabled.
21
+ *
22
+ * @fires {CustomEvent} change - Dispatched when the radio selection changes.
23
+ * @fires {CustomEvent} blur - Dispatched when the radio loses focus.
24
+ * @fires {CustomEvent} focus - Dispatched when the radio receives focus.
25
+ *
26
+ * @example
27
+ * ```html
28
+ * <wc-radio name="fruits" value="apple" label="Apple"></wc-radio>
29
+ * ```
30
+ * @tags input, form
31
+ */
32
+ type RadioDirection = 1 | -1;
33
+
34
+ export class Radio extends LitElement {
35
+ private static readonly DIRECTION_NEXT: RadioDirection = 1;
36
+ private static readonly DIRECTION_PREVIOUS: RadioDirection = -1;
37
+
38
+ static styles = [styles];
39
+
40
+ /**
41
+ * The input field name for grouping radios.
42
+ */
43
+ @property({ type: String })
44
+ name: string = '';
45
+
46
+ /**
47
+ * The submitted value when this radio is selected.
48
+ */
49
+ @property({ type: String })
50
+ value: string = '';
51
+
52
+ /**
53
+ * The radio label.
54
+ */
55
+ @property({ type: String })
56
+ label: string = '';
57
+
58
+ /**
59
+ * Whether the radio is selected.
60
+ */
61
+ @property({ type: Boolean, reflect: true })
62
+ checked: boolean = false;
63
+
64
+ /**
65
+ * If true, required icon is shown. Defaults to `false`.
66
+ */
67
+ @property({ type: Boolean, reflect: true })
68
+ required: boolean = false;
69
+
70
+ /**
71
+ * If true, the radio is readonly. Defaults to `false`.
72
+ */
73
+ @property({ type: Boolean, reflect: true })
74
+ readonly: boolean = false;
75
+
76
+ /**
77
+ * If true, the user cannot interact with the radio. Defaults to `false`.
78
+ */
79
+ @property({ type: Boolean, reflect: true })
80
+ disabled: boolean = false;
81
+
82
+ /**
83
+ * Configuration object for aria attributes.
84
+ */
85
+ @property({ type: Object })
86
+ configAria: Record<string, string> = {};
87
+
88
+ @state()
89
+ private hasFocus = false;
90
+
91
+ @state()
92
+ private isActive = false;
93
+
94
+ @state()
95
+ private slotHasContent = false;
96
+
97
+ @state()
98
+ private isGroupFocusTarget = false;
99
+
100
+ @query('.container')
101
+ private containerElement?: HTMLElement;
102
+
103
+ @query('.input-native')
104
+ private nativeElement?: HTMLInputElement;
105
+
106
+ private tabindex?: number;
107
+
108
+ connectedCallback() {
109
+ super.connectedCallback();
110
+ this.handleInitialAttributes();
111
+ window.addEventListener('mouseup', this.windowMouseUp);
112
+ window.addEventListener('keyup', this.windowKeyUp);
113
+ }
114
+
115
+ disconnectedCallback() {
116
+ super.disconnectedCallback();
117
+ window.removeEventListener('mouseup', this.windowMouseUp);
118
+ window.removeEventListener('keyup', this.windowKeyUp);
119
+ }
120
+
121
+ firstUpdated() {
122
+ this.slotHasContent = this.hasChildNodes() || !!this.label;
123
+ this.updateGroupFocusTarget();
124
+ }
125
+
126
+ updated(changedProps: Map<string, unknown>) {
127
+ if (changedProps.has('label')) {
128
+ this.slotHasContent = this.hasChildNodes() || !!this.label;
129
+ }
130
+
131
+ if (changedProps.has('checked') && this.checked) {
132
+ this.uncheckSiblings();
133
+ }
134
+
135
+ if (changedProps.has('checked') || changedProps.has('name')) {
136
+ this.updateGroupFocusTarget();
137
+ }
138
+ }
139
+
140
+ private handleInitialAttributes() {
141
+ if (this.hasAttribute('tabindex')) {
142
+ const attrValue = this.getAttribute('tabindex');
143
+ if (attrValue !== null) {
144
+ this.tabindex = parseInt(attrValue, 10);
145
+ }
146
+ this.removeAttribute('tabindex');
147
+ }
148
+
149
+ Array.from(this.attributes).forEach(attr => {
150
+ if (attr.name.startsWith('aria-')) {
151
+ this.configAria[attr.name] = attr.value;
152
+ this.removeAttribute(attr.name);
153
+ }
154
+ });
155
+ }
156
+
157
+ private windowMouseUp = () => {
158
+ if (this.isActive) {
159
+ this.isActive = false;
160
+ }
161
+ };
162
+
163
+ private windowKeyUp = (evt: KeyboardEvent) => {
164
+ if (this.isActive && evt.key === ' ') {
165
+ evt.preventDefault();
166
+ this.isActive = false;
167
+ }
168
+ };
169
+
170
+ private mouseDownHandler = () => {
171
+ this.isActive = true;
172
+ };
173
+
174
+ private keyDownHandler = (evt: KeyboardEvent) => {
175
+ if (evt.key === ' ') {
176
+ evt.preventDefault();
177
+ this.isActive = true;
178
+ this.selectRadio(evt);
179
+ } else if (evt.key === 'ArrowRight' || evt.key === 'ArrowDown') {
180
+ evt.preventDefault();
181
+ this.navigateGroup(Radio.DIRECTION_NEXT);
182
+ } else if (evt.key === 'ArrowLeft' || evt.key === 'ArrowUp') {
183
+ evt.preventDefault();
184
+ this.navigateGroup(Radio.DIRECTION_PREVIOUS);
185
+ }
186
+ };
187
+
188
+ private clickHandler = (ev: MouseEvent | KeyboardEvent) => {
189
+ this.selectRadio(ev);
190
+ };
191
+
192
+ private blurHandler = (ev: FocusEvent) => {
193
+ this.hasFocus = false;
194
+ this.dispatchEvent(
195
+ new CustomEvent('blur', {
196
+ detail: ev,
197
+ bubbles: true,
198
+ composed: true,
199
+ }),
200
+ );
201
+ };
202
+
203
+ private focusHandler = (ev: FocusEvent) => {
204
+ this.hasFocus = true;
205
+ this.dispatchEvent(
206
+ new CustomEvent('focus', {
207
+ detail: ev,
208
+ bubbles: true,
209
+ composed: true,
210
+ }),
211
+ );
212
+ };
213
+
214
+ private selectRadio(ev?: Event) {
215
+ if (this.disabled || this.readonly) return;
216
+
217
+ if (!this.checked) {
218
+ this.checked = true;
219
+ this.uncheckSiblings();
220
+ this.dispatchChange(ev);
221
+ }
222
+
223
+ this.containerElement?.focus();
224
+ }
225
+
226
+ private dispatchChange(ev?: Event) {
227
+ this.dispatchEvent(
228
+ new CustomEvent('change', {
229
+ detail: { value: this.value, checked: this.checked, originalEvent: ev },
230
+ bubbles: true,
231
+ composed: true,
232
+ }),
233
+ );
234
+ }
235
+
236
+ private uncheckSiblings() {
237
+ if (!this.name || !this.checked) return;
238
+ const radios = this.getGroupRadios();
239
+
240
+ radios.forEach(radio => {
241
+ if (radio === this) return;
242
+ radio.checked = false;
243
+ });
244
+ }
245
+
246
+ private getGroupRadios(): Radio[] {
247
+ if (!this.name) return [this];
248
+ const scopeRoot = this.closest('form') ?? document;
249
+ return Array.from(
250
+ scopeRoot.querySelectorAll<Radio>(`wc-radio[name="${this.name}"]`),
251
+ );
252
+ }
253
+
254
+ private isRadioEnabled(radio: Radio) {
255
+ return !radio.disabled && !radio.readonly;
256
+ }
257
+
258
+ private updateGroupFocusTarget() {
259
+ const group = this.getGroupRadios();
260
+ if (!group.length) return;
261
+
262
+ const enabledGroup = group.filter(radio => this.isRadioEnabled(radio));
263
+ if (!enabledGroup.length) {
264
+ group.forEach(radio => {
265
+ radio.isGroupFocusTarget = false;
266
+ });
267
+ return;
268
+ }
269
+
270
+ const target =
271
+ enabledGroup.find(radio => radio.checked) || enabledGroup[0];
272
+
273
+ group.forEach(radio => {
274
+ radio.isGroupFocusTarget = radio === target;
275
+ });
276
+ }
277
+
278
+ private navigateGroup(direction: RadioDirection) {
279
+ const group = this.getGroupRadios().filter(radio =>
280
+ this.isRadioEnabled(radio),
281
+ );
282
+ if (!group.length) return;
283
+
284
+ const currentIndex = group.indexOf(this);
285
+ const startIndex = currentIndex >= 0 ? currentIndex : 0;
286
+ const nextIndex = (startIndex + direction + group.length) % group.length;
287
+ const target = group[nextIndex];
288
+
289
+ target.selectRadio();
290
+ target.containerElement?.focus();
291
+ }
292
+
293
+ /**
294
+ * Sets focus on the radio.
295
+ */
296
+ focus() {
297
+ this.containerElement?.focus();
298
+ }
299
+
300
+ /**
301
+ * Removes focus from the radio.
302
+ */
303
+ blur() {
304
+ this.containerElement?.blur();
305
+ }
306
+
307
+ render() {
308
+ const cssClasses = {
309
+ radio: true,
310
+ 'state-checked': this.checked,
311
+ 'has-focus': this.hasFocus,
312
+ active: this.isActive,
313
+ disabled: this.disabled,
314
+ readonly: this.readonly,
315
+ required: this.required,
316
+ 'has-content': this.slotHasContent,
317
+ };
318
+
319
+ return html`
320
+ <label class=${classMap(cssClasses)}>
321
+ <div
322
+ class="container"
323
+ tabindex=${this.isGroupFocusTarget
324
+ ? this.tabindex !== undefined
325
+ ? this.tabindex
326
+ : 0
327
+ : -1}
328
+ role="radio"
329
+ aria-disabled=${this.disabled}
330
+ aria-required=${this.required}
331
+ aria-checked=${this.checked}
332
+ @click=${this.clickHandler}
333
+ @mousedown=${this.mouseDownHandler}
334
+ @keydown=${this.keyDownHandler}
335
+ @blur=${this.blurHandler}
336
+ @focus=${this.focusHandler}
337
+ ${spread(this.configAria)}
338
+ >
339
+ <div class="state-layer"></div>
340
+ <div class="outer-circle"></div>
341
+ <div class="inner-circle"></div>
342
+ </div>
343
+
344
+ <input
345
+ type="radio"
346
+ class="input-native"
347
+ name=${this.name}
348
+ .value=${this.value}
349
+ .checked=${this.checked}
350
+ aria-hidden="true"
351
+ ?required=${this.required}
352
+ ?disabled=${this.disabled}
353
+ tabindex="-1"
354
+ />
355
+
356
+ ${this.label
357
+ ? html`<div class="label">${this.label}</div>`
358
+ : html`<div class="label slot-container"><slot></slot></div>`}
359
+ </label>
360
+ `;
361
+ }
362
+ }
@@ -0,0 +1,29 @@
1
+ <!doctype html>
2
+ <html lang='en-GB'>
3
+ <head>
4
+ <meta charset='utf-8'>
5
+ <meta name='viewport' content='width=device-width, initial-scale=1.0, viewport-fit=cover' />
6
+ <link rel='stylesheet' href='/dist/assets/styles/tokens.css' />
7
+ <style>
8
+ body {
9
+ background: #fafafa;
10
+ padding: 2rem;
11
+ display: grid;
12
+ gap: 1rem;
13
+ }
14
+ </style>
15
+ </head>
16
+ <body>
17
+ <wc-snackbar open message="File archived" action-label="Undo"></wc-snackbar>
18
+ <wc-snackbar open multiline show-close-icon>
19
+ Network connection was lost. Please try again when you are back online.
20
+ </wc-snackbar>
21
+
22
+ <script type='module'>
23
+ import { Snackbar, Icon } from '/dist/index.js';
24
+
25
+ window.customElements.define('wc-snackbar', Snackbar);
26
+ window.customElements.define('wc-icon', Icon);
27
+ </script>
28
+ </body>
29
+ </html>
@@ -0,0 +1 @@
1
+ export { Snackbar } from './snackbar.js';
@@ -0,0 +1,73 @@
1
+ @use "../../scss/mixin";
2
+
3
+ @include mixin.base-styles;
4
+
5
+ :host {
6
+ display: inline-flex;
7
+ max-width: 42rem;
8
+ min-width: 21.5rem;
9
+
10
+ --snackbar-container-color: var(--color-inverse-surface);
11
+ --snackbar-label-text-color: var(--color-inverse-on-surface);
12
+ --snackbar-action-text-color: var(--color-inverse-primary);
13
+ --snackbar-close-icon-color: var(--color-inverse-on-surface);
14
+ --snackbar-border-radius: var(--shape-corner-extra-small);
15
+ }
16
+
17
+ .snackbar {
18
+ @include mixin.get-typography(body-medium);
19
+ align-items: center;
20
+ background-color: var(--snackbar-container-color);
21
+ border-radius: var(--snackbar-border-radius);
22
+ color: var(--snackbar-label-text-color);
23
+ display: inline-flex;
24
+ gap: var(--spacing-100);
25
+ min-height: 3rem;
26
+ opacity: 0;
27
+ padding: var(--spacing-100) var(--spacing-200);
28
+ pointer-events: none;
29
+ transform: translateY(0.5rem);
30
+ transition: opacity var(--duration-short2) var(--easing-standard),
31
+ transform var(--duration-short2) var(--easing-standard);
32
+ }
33
+
34
+ .snackbar.open {
35
+ opacity: 1;
36
+ pointer-events: auto;
37
+ transform: translateY(0);
38
+ }
39
+
40
+ .label {
41
+ flex: 1 1 auto;
42
+ color: inherit;
43
+ }
44
+
45
+ .action,
46
+ .close {
47
+ @include mixin.get-typography(label-large);
48
+ align-items: center;
49
+ background: transparent;
50
+ border: none;
51
+ color: var(--snackbar-action-text-color);
52
+ cursor: pointer;
53
+ display: inline-flex;
54
+ justify-content: center;
55
+ margin: 0;
56
+ min-height: 2rem;
57
+ min-width: 2rem;
58
+ padding: 0 var(--spacing-100);
59
+ }
60
+
61
+ .close {
62
+ color: var(--snackbar-close-icon-color);
63
+ padding: 0;
64
+ }
65
+
66
+ .close wc-icon {
67
+ --icon-size: 1.125rem;
68
+ color: inherit;
69
+ }
70
+
71
+ .snackbar.multiline {
72
+ align-items: flex-start;
73
+ }
@@ -0,0 +1,151 @@
1
+ import { LitElement, html, nothing } from 'lit';
2
+ import { property } from 'lit/decorators.js';
3
+ import { classMap } from 'lit/directives/class-map.js';
4
+ import styles from './snackbar.scss';
5
+
6
+ type SnackbarCloseReason = 'timeout' | 'dismiss' | 'action' | 'programmatic';
7
+
8
+ /**
9
+ * @label Snackbar
10
+ * @tag wc-snackbar
11
+ * @rawTag snackbar
12
+ * @summary Snackbars provide brief messages about app processes at the bottom of the screen.
13
+ *
14
+ * @cssprop --snackbar-container-color - Container color for the snackbar.
15
+ * @cssprop --snackbar-label-text-color - Label text color for the snackbar.
16
+ * @cssprop --snackbar-action-text-color - Action text color.
17
+ * @cssprop --snackbar-close-icon-color - Close icon color.
18
+ * @cssprop --snackbar-border-radius - Border radius of the snackbar surface.
19
+ *
20
+ * @example
21
+ * ```html
22
+ * <wc-snackbar open message="Message archived" action-label="Undo"></wc-snackbar>
23
+ * ```
24
+ * @tags display, feedback
25
+ */
26
+ export class Snackbar extends LitElement {
27
+ static styles = [styles];
28
+
29
+ @property({ type: Boolean, reflect: true }) open = false;
30
+
31
+ @property({ type: String }) message = '';
32
+
33
+ @property({ type: String, attribute: 'action-label' }) actionLabel = '';
34
+
35
+ @property({ type: Boolean, attribute: 'show-close-icon' })
36
+ showCloseIcon = false;
37
+
38
+ @property({ type: Number }) duration = 4000;
39
+
40
+ @property({ type: Boolean, reflect: true }) multiline = false;
41
+
42
+ private hideTimer: ReturnType<typeof setTimeout> | null = null;
43
+
44
+ show() {
45
+ this.open = true;
46
+ }
47
+
48
+ hide() {
49
+ this.close('programmatic');
50
+ }
51
+
52
+ private close(reason: SnackbarCloseReason) {
53
+ if (!this.open) {
54
+ return;
55
+ }
56
+
57
+ this.open = false;
58
+ this.dispatchEvent(
59
+ new CustomEvent('snackbar-close', {
60
+ detail: { reason },
61
+ bubbles: true,
62
+ composed: true,
63
+ }),
64
+ );
65
+ }
66
+
67
+ private dispatchActionEvent() {
68
+ this.dispatchEvent(
69
+ new CustomEvent('snackbar-action', {
70
+ bubbles: true,
71
+ composed: true,
72
+ }),
73
+ );
74
+ }
75
+
76
+ private handleActionClick() {
77
+ this.dispatchActionEvent();
78
+ this.close('action');
79
+ }
80
+
81
+ private handleCloseClick() {
82
+ this.close('dismiss');
83
+ }
84
+
85
+ private clearTimer() {
86
+ if (this.hideTimer !== null) {
87
+ clearTimeout(this.hideTimer);
88
+ this.hideTimer = null;
89
+ }
90
+ }
91
+
92
+ private scheduleAutoHide() {
93
+ this.clearTimer();
94
+ if (!this.open || this.duration <= 0) {
95
+ return;
96
+ }
97
+
98
+ this.hideTimer = setTimeout(() => {
99
+ this.close('timeout');
100
+ }, this.duration);
101
+ }
102
+
103
+ protected updated(changedProperties: Map<string, unknown>) {
104
+ if (changedProperties.has('open')) {
105
+ this.scheduleAutoHide();
106
+ }
107
+ }
108
+
109
+ disconnectedCallback() {
110
+ this.clearTimer();
111
+ super.disconnectedCallback();
112
+ }
113
+
114
+ render() {
115
+ const liveRole =
116
+ this.actionLabel || this.showCloseIcon ? 'alert' : 'status';
117
+
118
+ return html`
119
+ <div
120
+ class=${classMap({
121
+ snackbar: true,
122
+ open: this.open,
123
+ multiline: this.multiline,
124
+ })}
125
+ role=${liveRole}
126
+ aria-live="polite"
127
+ >
128
+ <div class="label">
129
+ <slot>${this.message}</slot>
130
+ </div>
131
+
132
+ ${this.actionLabel
133
+ ? html`<button class="action" type="button" @click=${this.handleActionClick}>
134
+ ${this.actionLabel}
135
+ </button>`
136
+ : nothing}
137
+
138
+ ${this.showCloseIcon
139
+ ? html`<button
140
+ class="close"
141
+ type="button"
142
+ aria-label="Dismiss notification"
143
+ @click=${this.handleCloseClick}
144
+ >
145
+ <wc-icon name="close"></wc-icon>
146
+ </button>`
147
+ : nothing}
148
+ </div>
149
+ `;
150
+ }
151
+ }