@redvars/peacock 3.3.1 → 3.3.2

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 (88) 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 +909 -25
  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 +22 -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 → snackbar-74YCdMPL.js} +1005 -143
  42. package/dist/snackbar-74YCdMPL.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 +5 -0
  49. package/dist/src/menu/menu-item/menu-item.d.ts +1 -1
  50. package/dist/src/snackbar/index.d.ts +1 -0
  51. package/dist/src/snackbar/snackbar.d.ts +40 -0
  52. package/dist/src/tabs/tab-group.d.ts +1 -1
  53. package/dist/src/tabs/tab-panel.d.ts +1 -0
  54. package/dist/src/tabs/tab.d.ts +2 -1
  55. package/dist/{style-map-CfNHEkQp.js → style-map-DcB52w-l.js} +2 -2
  56. package/dist/{style-map-CfNHEkQp.js.map → style-map-DcB52w-l.js.map} +1 -1
  57. package/dist/test/card.test.d.ts +1 -0
  58. package/dist/test/chart-bar.test.d.ts +1 -0
  59. package/dist/test/snackbar.test.d.ts +1 -0
  60. package/dist/{transform-DRuHEvar.js → transform-DSwFSqzD.js} +13 -558
  61. package/dist/transform-DSwFSqzD.js.map +1 -0
  62. package/dist/tsconfig.tsbuildinfo +1 -1
  63. package/dist/{unsafe-html-CV6Je6HL.js → unsafe-html-C2r3PyzF.js} +2 -2
  64. package/dist/{unsafe-html-CV6Je6HL.js.map → unsafe-html-C2r3PyzF.js.map} +1 -1
  65. package/package.json +1 -1
  66. package/readme.md +2 -2
  67. package/src/card/card.scss +61 -0
  68. package/src/card/card.ts +38 -0
  69. package/src/card/index.ts +1 -0
  70. package/src/chart-bar/chart-bar.scss +58 -0
  71. package/src/chart-bar/chart-bar.ts +306 -0
  72. package/src/chart-bar/chart-stacked-bar.ts +402 -0
  73. package/src/chart-bar/index.ts +2 -0
  74. package/src/index.ts +5 -0
  75. package/src/menu/menu-item/menu-item.ts +1 -1
  76. package/src/peacock-loader.ts +14 -0
  77. package/src/snackbar/demo/index.html +29 -0
  78. package/src/snackbar/index.ts +1 -0
  79. package/src/snackbar/snackbar.scss +73 -0
  80. package/src/snackbar/snackbar.ts +151 -0
  81. package/src/tabs/tab-group.ts +57 -28
  82. package/src/tabs/tab-panel.scss +3 -3
  83. package/src/tabs/tab-panel.ts +2 -0
  84. package/src/tabs/tab.scss +76 -2
  85. package/src/tabs/tab.ts +28 -6
  86. package/src/tabs/tabs.ts +15 -3
  87. package/dist/transform-DRuHEvar.js.map +0 -1
  88. package/dist/tree-view-CLolVlU0.js.map +0 -1
@@ -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
+ }
@@ -38,15 +38,13 @@ export class TabGroup extends LitElement {
38
38
  @property({ reflect: true })
39
39
  variant: 'line' | 'line-secondary' | 'contained' | 'pill' = 'line';
40
40
 
41
- private uid = crypto.randomUUID();
42
-
43
41
  connectedCallback() {
44
42
  super.connectedCallback();
45
- this.addEventListener('tab-click', this.onTabClick as EventListener);
43
+ this.addEventListener('tab-click', this.onTabClick);
46
44
  }
47
45
 
48
46
  disconnectedCallback() {
49
- this.removeEventListener('tab-click', this.onTabClick as EventListener);
47
+ this.removeEventListener('tab-click', this.onTabClick);
50
48
  super.disconnectedCallback();
51
49
  }
52
50
 
@@ -60,15 +58,18 @@ export class TabGroup extends LitElement {
60
58
  const targetValue = custom.detail?.target || custom.detail?.value;
61
59
  if (targetValue) {
62
60
  this.selectTab(targetValue);
61
+ } else if (typeof custom.detail?.index === 'number') {
62
+ this.selectTabByIndex(custom.detail.index);
63
63
  }
64
64
  };
65
65
 
66
66
  selectTab(target: string) {
67
67
  const tabs = this.getTabs();
68
- tabs.forEach((tab: HTMLElement) => {
68
+ for (const tab of tabs) {
69
+ (tab as any).active = false;
69
70
  (tab as any).selected = false;
70
71
  tab.classList.remove('previous-tab', 'next-tab');
71
- });
72
+ }
72
73
 
73
74
  let selectedIndex = -1;
74
75
  tabs.forEach((tab: HTMLElement, index: number) => {
@@ -80,6 +81,7 @@ export class TabGroup extends LitElement {
80
81
 
81
82
  if (selectedIndex >= 0) {
82
83
  const selectedTab = tabs[selectedIndex];
84
+ (selectedTab as any).active = true;
83
85
  (selectedTab as any).selected = true;
84
86
  if (tabs[selectedIndex - 1]) {
85
87
  tabs[selectedIndex - 1].classList.add('previous-tab');
@@ -90,49 +92,76 @@ export class TabGroup extends LitElement {
90
92
  }
91
93
 
92
94
  const panels = this.getTabPanels();
93
- panels.forEach(panel => {
95
+ for (const panel of panels) {
94
96
  const panelValue = panel.getAttribute('value');
95
97
  (panel as any).active = panelValue === target;
96
- });
98
+ }
97
99
  }
98
100
 
99
- private getTabs(): NodeListOf<HTMLElement> {
100
- return this.querySelectorAll(':scope > tabs-list wc-tab');
101
+ selectTabByIndex(index: number) {
102
+ const tabs = this.getTabs();
103
+ for (const tab of tabs) {
104
+ (tab as any).active = false;
105
+ (tab as any).selected = false;
106
+ tab.classList.remove('previous-tab', 'next-tab');
107
+ }
108
+
109
+ if (index >= 0 && index < tabs.length) {
110
+ (tabs[index] as any).active = true;
111
+ (tabs[index] as any).selected = true;
112
+ if (tabs[index - 1]) tabs[index - 1].classList.add('previous-tab');
113
+ if (tabs[index + 1]) tabs[index + 1].classList.add('next-tab');
114
+ }
115
+
116
+ const panels = this.getTabPanels();
117
+ for (let i = 0; i < panels.length; i += 1) {
118
+ const panel = panels[i];
119
+ (panel as any).active = i === index;
120
+ }
101
121
  }
102
122
 
103
- private getTabPanels(): NodeListOf<HTMLElement> {
104
- return this.querySelectorAll(':scope > wc-tab-panel');
123
+ private getTabs(): HTMLElement[] {
124
+ return Array.from(this.querySelectorAll(':scope > wc-tabs wc-tab'));
125
+ }
126
+
127
+ private getTabPanels(): HTMLElement[] {
128
+ return Array.from(this.querySelectorAll(':scope > wc-tab-panel'));
105
129
  }
106
130
 
107
131
  private getTabList(): HTMLElement | null {
108
- return this.querySelector(':scope > tabs-list');
132
+ return this.querySelector(':scope > wc-tabs');
109
133
  }
110
134
 
111
135
  private tabsHaveTarget(): boolean {
112
- return !!this.querySelector(':scope > tabs-list wc-tab[target]');
136
+ return !!this.querySelector(':scope > wc-tabs wc-tab[target]');
113
137
  }
114
138
 
115
139
  private initializeTabs() {
116
140
  const tabs = Array.from(this.getTabs());
117
141
  if (!this.tabsHaveTarget()) {
118
-
119
- this.getTabPanels().forEach((panel, index) => {
120
- if (!panel.getAttribute('value')) {
121
- panel.setAttribute('value', `tab-${this.uid}-${index}`);
122
- }
123
- });
124
-
125
- if (tabs.length > 0) {
126
- const firstTarget = tabs[0].getAttribute('target');
127
- if (firstTarget) {
128
- this.selectTab(firstTarget);
129
- }
130
- }
142
+ // No target/value attributes — use index-based activation
143
+ const selectedIndex = tabs.findIndex(
144
+ tab =>
145
+ tab.hasAttribute('active') ||
146
+ (tab as any).active ||
147
+ tab.hasAttribute('selected') ||
148
+ (tab as any).selected,
149
+ );
150
+ this.selectTabByIndex(selectedIndex >= 0 ? selectedIndex : 0);
131
151
  } else {
132
- const selectedTab = this.querySelector(':scope > tabs-list wc-tab[selected]') as HTMLElement;
152
+ const selectedTab = tabs.find(
153
+ tab =>
154
+ tab.hasAttribute('active') ||
155
+ (tab as any).active ||
156
+ tab.hasAttribute('selected') ||
157
+ (tab as any).selected,
158
+ );
133
159
  if (selectedTab) {
134
160
  const selectedTarget = selectedTab.getAttribute('target');
135
161
  if (selectedTarget) this.selectTab(selectedTarget);
162
+ } else if (tabs.length > 0) {
163
+ const firstTarget = tabs[0].getAttribute('target');
164
+ if (firstTarget) this.selectTab(firstTarget);
136
165
  }
137
166
  }
138
167
  }
@@ -3,10 +3,10 @@
3
3
  @include mixin.base-styles;
4
4
 
5
5
  :host {
6
- display: block;
6
+ display: none;
7
7
  flex: 1;
8
8
  }
9
9
 
10
- :host([hidden]) {
11
- display: none;
10
+ :host([active]) {
11
+ display: block;
12
12
  }
@@ -23,6 +23,8 @@ export class TabPanel extends LitElement {
23
23
 
24
24
  @property({ reflect: true }) value?: string;
25
25
 
26
+ @property({ type: Boolean, reflect: true }) active = false;
27
+
26
28
  render() {
27
29
  return html`<slot></slot>`;
28
30
  }
package/src/tabs/tab.scss CHANGED
@@ -34,7 +34,7 @@
34
34
  display: flex;
35
35
  align-items: center;
36
36
  justify-content: center;
37
- gap: var(--_tab-icon-label-spacing);
37
+ gap: 0;
38
38
  width: 100%;
39
39
  height: 100%;
40
40
  z-index: 0;
@@ -47,6 +47,25 @@
47
47
  .slot-container {
48
48
  display: none;
49
49
  }
50
+
51
+ ::slotted([slot='icon']) {
52
+ flex: none;
53
+ color: var(--_label-text-color);
54
+ --icon-size: var(--tab-icon-size, var(--_tab-icon-size));
55
+ --icon-color: var(--_label-text-color);
56
+ }
57
+
58
+ ::slotted([slot='badge']) {
59
+ flex: none;
60
+ margin-inline-start: var(--_tab-badge-label-spacing, 0);
61
+ --badge-color: var(--_tab-badge-color, var(--color-error));
62
+ }
63
+ }
64
+
65
+ &.has-icon {
66
+ .tab-content {
67
+ gap: var(--_tab-icon-label-spacing);
68
+ }
50
69
  }
51
70
 
52
71
  .indicator {
@@ -112,6 +131,10 @@
112
131
  :host-context([variant='line']) .tab {
113
132
  --_tab-height: 100%;
114
133
  --_container-padding: 1rem;
134
+ --_tab-icon-size: 1.5rem;
135
+ --_tab-icon-label-spacing: 0.5rem;
136
+ --_tab-badge-label-spacing: 0.25rem;
137
+ --_tab-badge-color: var(--color-error);
115
138
 
116
139
  --_label-text-color: var(--color-on-surface);
117
140
  --_container-state-color: var(--_label-text-color);
@@ -121,7 +144,7 @@
121
144
  --_container-shape-end-end: var(--shape-corner-small);
122
145
  --_active-indicator-color: var(--color-primary);
123
146
  --_active-indicator-shape: 3px 3px 0 0;
124
- --_active-indicator-height: 2px;
147
+ --_active-indicator-height: 3px;
125
148
 
126
149
  .focus-ring {
127
150
  inset: 3px 3px 4px 3px;
@@ -146,6 +169,57 @@
146
169
  --_container-state-opacity: 0.12;
147
170
  }
148
171
 
172
+ &.disabled {
173
+ --_label-text-color: var(--color-on-surface);
174
+ --_label-text-opacity: 0.38;
175
+
176
+ .ripple {
177
+ display: none;
178
+ }
179
+ }
180
+ }
181
+
182
+ :host-context([variant='line-secondary']) .tab {
183
+ --_tab-height: 100%;
184
+ --_container-padding: 1rem;
185
+ --_tab-icon-size: 1.25rem;
186
+ --_tab-icon-label-spacing: 0.5rem;
187
+ --_tab-badge-label-spacing: 0.25rem;
188
+ --_tab-badge-color: var(--color-error);
189
+
190
+ --_label-text-color: var(--color-on-surface);
191
+ --_container-state-color: var(--_label-text-color);
192
+ --_container-shape-start-start: var(--shape-corner-small);
193
+ --_container-shape-start-end: var(--shape-corner-small);
194
+ --_container-shape-end-start: var(--shape-corner-small);
195
+ --_container-shape-end-end: var(--shape-corner-small);
196
+ --_active-indicator-color: var(--color-primary);
197
+ --_active-indicator-shape: 0;
198
+ --_active-indicator-height: 2px;
199
+
200
+ .focus-ring {
201
+ inset: 3px 3px 4px 3px;
202
+ }
203
+
204
+ &.active {
205
+
206
+ .indicator.secondary {
207
+ opacity: 1;
208
+ }
209
+
210
+ .focus-ring {
211
+ inset: 3px 3px calc(4px + var(--_active-indicator-height)) 3px;
212
+ }
213
+ }
214
+
215
+ &:hover:not(:where(.disabled)) {
216
+ --_container-state-opacity: 0.08;
217
+ }
218
+
219
+ &.pressed:not(:where(.disabled)) {
220
+ --_container-state-opacity: 0.12;
221
+ }
222
+
149
223
  &.disabled {
150
224
  --_label-text-color: var(--color-on-surface);
151
225
  --_label-text-opacity: 0.38;
package/src/tabs/tab.ts CHANGED
@@ -34,8 +34,6 @@ export class Tab extends LitElement {
34
34
 
35
35
  @property({ type: String }) disabledReason = '';
36
36
 
37
- @property({ type: String }) icon?: string;
38
-
39
37
  @property({ type: String }) label?: string;
40
38
 
41
39
  @property({ type: String }) value?: string;
@@ -59,6 +57,10 @@ export class Tab extends LitElement {
59
57
 
60
58
  @state() slotHasContent = false;
61
59
 
60
+ @state() slotHasIcon = false;
61
+
62
+ @state() slotHasBadge = false;
63
+
62
64
  /**
63
65
  * States
64
66
  */
@@ -95,6 +97,22 @@ export class Tab extends LitElement {
95
97
  this.requestUpdate();
96
98
  },
97
99
  );
100
+
101
+ observerSlotChangesWithCallback(
102
+ this.renderRoot.querySelector('slot[name="icon"]'),
103
+ hasContent => {
104
+ this.slotHasIcon = hasContent;
105
+ this.requestUpdate();
106
+ },
107
+ );
108
+
109
+ observerSlotChangesWithCallback(
110
+ this.renderRoot.querySelector('slot[name="badge"]'),
111
+ hasContent => {
112
+ this.slotHasBadge = hasContent;
113
+ this.requestUpdate();
114
+ },
115
+ );
98
116
  }
99
117
 
100
118
  __dispatchClickWithThrottle: (event: MouseEvent | KeyboardEvent) => void =
@@ -159,7 +177,9 @@ export class Tab extends LitElement {
159
177
  disabled: this.disabled,
160
178
  pressed: this.isPressed,
161
179
  active: this.active,
162
- 'has-content': this.slotHasContent
180
+ 'has-content': this.slotHasContent || !!this.label,
181
+ 'has-icon': this.slotHasIcon,
182
+ 'has-badge': this.slotHasBadge,
163
183
  };
164
184
 
165
185
 
@@ -206,16 +226,18 @@ export class Tab extends LitElement {
206
226
  <wc-ripple class="ripple"></wc-ripple>
207
227
 
208
228
  <div class="tab-content">
229
+ <slot name="icon"></slot>
230
+
209
231
  <div class="slot-container">
210
- <slot></slot>
232
+ <slot>${this.label || nothing}</slot>
211
233
  </div>
212
234
 
213
- <slot name="icon"></slot>
235
+ <slot name="badge"></slot>
214
236
 
215
237
  <div class="indicator"></div>
216
238
  </div>
217
239
 
218
- <div class="indicator"></div>
240
+ <div class="secondary indicator"></div>
219
241
 
220
242
  ${this.__renderDisabledReason()}
221
243
  `;
package/src/tabs/tabs.ts CHANGED
@@ -49,11 +49,23 @@ export class Tabs extends LitElement {
49
49
 
50
50
  if (!clickedTab) return;
51
51
 
52
- const tabs: NodeListOf<Tab> = this.querySelectorAll('wc-tab');
53
- tabs.forEach(tab => {
52
+ const tabs = Array.from(this.querySelectorAll('wc-tab')) as Tab[];
53
+ let clickedIndex = -1;
54
+ for (let index = 0; index < tabs.length; index += 1) {
55
+ const tab = tabs[index];
54
56
  tab.active = false;
55
- });
57
+ if (tab === clickedTab) clickedIndex = index;
58
+ }
56
59
  (clickedTab as Tab).active = true;
60
+
61
+ this.dispatchEvent(new CustomEvent('tab-click', {
62
+ bubbles: true,
63
+ composed: true,
64
+ detail: {
65
+ index: clickedIndex,
66
+ value: (clickedTab as Tab).value,
67
+ },
68
+ }));
57
69
  };
58
70
 
59
71
  render() {