mtrl 0.2.4 → 0.2.6

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 (76) hide show
  1. package/package.json +6 -3
  2. package/src/components/badge/_styles.scss +9 -9
  3. package/src/components/button/_styles.scss +0 -56
  4. package/src/components/button/button.ts +0 -2
  5. package/src/components/button/constants.ts +0 -6
  6. package/src/components/button/index.ts +2 -2
  7. package/src/components/button/types.ts +1 -7
  8. package/src/components/card/_styles.scss +67 -25
  9. package/src/components/card/api.ts +54 -3
  10. package/src/components/card/card.ts +33 -2
  11. package/src/components/card/config.ts +143 -21
  12. package/src/components/card/constants.ts +20 -19
  13. package/src/components/card/content.ts +299 -2
  14. package/src/components/card/features.ts +155 -4
  15. package/src/components/card/index.ts +31 -9
  16. package/src/components/card/types.ts +138 -15
  17. package/src/components/chip/chip.ts +1 -9
  18. package/src/components/chip/constants.ts +0 -10
  19. package/src/components/chip/index.ts +1 -1
  20. package/src/components/chip/types.ts +1 -4
  21. package/src/components/progress/_styles.scss +0 -65
  22. package/src/components/progress/config.ts +1 -2
  23. package/src/components/progress/constants.ts +0 -14
  24. package/src/components/progress/index.ts +1 -1
  25. package/src/components/progress/progress.ts +1 -4
  26. package/src/components/progress/types.ts +1 -4
  27. package/src/components/radios/_styles.scss +0 -45
  28. package/src/components/radios/api.ts +85 -60
  29. package/src/components/radios/config.ts +1 -2
  30. package/src/components/radios/constants.ts +0 -9
  31. package/src/components/radios/index.ts +1 -1
  32. package/src/components/radios/radio.ts +34 -11
  33. package/src/components/radios/radios.ts +2 -1
  34. package/src/components/radios/types.ts +1 -7
  35. package/src/components/slider/_styles.scss +193 -281
  36. package/src/components/slider/accessibility.md +59 -0
  37. package/src/components/slider/api.ts +36 -101
  38. package/src/components/slider/config.ts +29 -78
  39. package/src/components/slider/constants.ts +12 -8
  40. package/src/components/slider/features/appearance.ts +1 -47
  41. package/src/components/slider/features/disabled.ts +41 -16
  42. package/src/components/slider/features/interactions.ts +166 -26
  43. package/src/components/slider/features/keyboard.ts +125 -6
  44. package/src/components/slider/features/structure.ts +182 -195
  45. package/src/components/slider/features/ui.ts +234 -303
  46. package/src/components/slider/index.ts +11 -1
  47. package/src/components/slider/slider.ts +1 -1
  48. package/src/components/slider/types.ts +10 -25
  49. package/src/components/tabs/_styles.scss +285 -155
  50. package/src/components/tabs/api.ts +178 -400
  51. package/src/components/tabs/config.ts +46 -52
  52. package/src/components/tabs/constants.ts +85 -8
  53. package/src/components/tabs/features.ts +401 -0
  54. package/src/components/tabs/index.ts +60 -3
  55. package/src/components/tabs/indicator.ts +225 -0
  56. package/src/components/tabs/responsive.ts +144 -0
  57. package/src/components/tabs/scroll-indicators.ts +149 -0
  58. package/src/components/tabs/state.ts +186 -0
  59. package/src/components/tabs/tab-api.ts +258 -0
  60. package/src/components/tabs/tab.ts +255 -0
  61. package/src/components/tabs/tabs.ts +50 -31
  62. package/src/components/tabs/types.ts +324 -128
  63. package/src/components/tabs/utils.ts +107 -0
  64. package/src/components/textfield/_styles.scss +0 -98
  65. package/src/components/textfield/config.ts +2 -3
  66. package/src/components/textfield/constants.ts +0 -14
  67. package/src/components/textfield/index.ts +2 -2
  68. package/src/components/textfield/textfield.ts +0 -2
  69. package/src/components/textfield/types.ts +1 -4
  70. package/src/core/compose/component.ts +1 -1
  71. package/src/core/compose/features/badge.ts +79 -0
  72. package/src/core/compose/features/index.ts +3 -1
  73. package/src/styles/abstract/_theme.scss +106 -2
  74. package/src/components/card/actions.ts +0 -48
  75. package/src/components/card/header.ts +0 -88
  76. package/src/components/card/media.ts +0 -52
@@ -1,36 +1,140 @@
1
1
  // src/components/tabs/types.ts
2
- import { TABS_VARIANTS } from './constants';
2
+ import { TABS_VARIANTS, TAB_STATES, TAB_LAYOUT } from './constants';
3
+ import { BadgeComponent } from '../badge/types';
4
+ import { TabIndicator } from './indicator';
3
5
 
4
6
  /**
5
- * Tab item configuration
7
+ * Configuration for the tab indicator
6
8
  * @category Components
7
9
  */
8
- export interface TabItem {
10
+ export interface IndicatorConfig {
11
+ /** Height of the indicator in pixels */
12
+ height?: number;
13
+ /** Width strategy for the indicator */
14
+ widthStrategy?: 'fixed' | 'dynamic' | 'content';
15
+ /** Fixed width in pixels (when using fixed strategy) */
16
+ fixedWidth?: number;
17
+ /** Animation duration in milliseconds */
18
+ animationDuration?: number;
19
+ /** Animation timing function */
20
+ animationTiming?: string;
21
+ /** Custom color for the indicator */
22
+ color?: string;
23
+ }
24
+
25
+ /**
26
+ * Tab change event data interface
27
+ * @category Events
28
+ */
29
+ export interface TabChangeEventData {
9
30
  /**
10
- * Unique identifier for the tab
31
+ * The tab component that was activated
11
32
  */
12
- id: string;
33
+ tab: TabComponent;
13
34
 
14
35
  /**
15
- * Display label for the tab
36
+ * The value of the activated tab
16
37
  */
17
- label: string;
38
+ value: string;
39
+ }
40
+
41
+ /**
42
+ * Configuration interface for a single Tab
43
+ * @category Components
44
+ */
45
+ export interface TabConfig {
46
+ /**
47
+ * Tab state that determines if it's the active destination
48
+ * @default 'inactive'
49
+ */
50
+ state?: keyof typeof TAB_STATES | string;
18
51
 
19
- /**
20
- * Optional icon HTML content
52
+ /**
53
+ * Whether the tab is initially disabled
54
+ * @default false
55
+ */
56
+ disabled?: boolean;
57
+
58
+ /**
59
+ * Initial tab text content (label)
60
+ * @example 'Home'
61
+ */
62
+ text?: string;
63
+
64
+ /**
65
+ * Initial tab icon HTML content
66
+ * @example '<svg>...</svg>'
21
67
  */
22
68
  icon?: string;
23
69
 
70
+ /**
71
+ * Badge text or value to display (if applicable)
72
+ * @example '5'
73
+ */
74
+ badge?: string | number;
75
+
24
76
  /**
25
- * Whether the tab is disabled
26
- * @default false
77
+ * Badge configuration object
78
+ * Pass additional options for the badge component
27
79
  */
28
- disabled?: boolean;
80
+ badgeConfig?: {
81
+ variant?: string;
82
+ color?: string;
83
+ size?: string;
84
+ position?: string;
85
+ max?: number;
86
+ };
87
+
88
+ /**
89
+ * Icon size in pixels or other CSS units
90
+ * @default '24px'
91
+ */
92
+ iconSize?: string;
93
+
94
+ /**
95
+ * Additional CSS classes to add to the tab
96
+ * @example 'home-tab main-navigation'
97
+ */
98
+ class?: string;
99
+
100
+ /**
101
+ * Tab value attribute for identifying the selected tab
102
+ */
103
+ value?: string;
104
+
105
+ /**
106
+ * Whether to enable ripple effect
107
+ * @default true
108
+ */
109
+ ripple?: boolean;
110
+
111
+ /**
112
+ * Component prefix for class names
113
+ * @default 'mtrl'
114
+ */
115
+ prefix?: string;
116
+
117
+ /**
118
+ * Component name used in class generation
119
+ */
120
+ componentName?: string;
121
+
122
+ /**
123
+ * Ripple effect configuration
124
+ */
125
+ rippleConfig?: {
126
+ /** Duration of the ripple animation in milliseconds */
127
+ duration?: number;
128
+ /** Timing function for the ripple animation */
129
+ timing?: string;
130
+ /** Opacity values for ripple start and end [start, end] */
131
+ opacity?: [string, string];
132
+ };
29
133
 
30
134
  /**
31
- * Additional data to associate with this tab
135
+ * Variant of the tab
32
136
  */
33
- data?: any;
137
+ variant?: string;
34
138
  }
35
139
 
36
140
  /**
@@ -39,209 +143,301 @@ export interface TabItem {
39
143
  */
40
144
  export interface TabsConfig {
41
145
  /**
42
- * Tabs variant that determines visual styling
146
+ * Tabs variant (primary or secondary)
43
147
  * @default 'primary'
44
148
  */
45
149
  variant?: keyof typeof TABS_VARIANTS | string;
46
150
 
47
151
  /**
48
- * Initial tab items
152
+ * Initial tabs to create
49
153
  */
50
- items?: TabItem[];
154
+ tabs?: TabConfig[];
51
155
 
52
156
  /**
53
- * Index of the initially active tab
54
- * @default 0
157
+ * Whether to show the divider
158
+ * @default true
55
159
  */
56
- activeIndex?: number;
160
+ showDivider?: boolean;
57
161
 
58
162
  /**
59
- * Whether the tabs component is initially disabled
60
- * @default false
163
+ * Whether to enable horizontal scrolling
164
+ * @default true
61
165
  */
62
- disabled?: boolean;
166
+ scrollable?: boolean;
63
167
 
64
168
  /**
65
- * Whether to show tab indicator
66
- * @default true
169
+ * Additional CSS classes for the container
67
170
  */
68
- showIndicator?: boolean;
171
+ class?: string;
69
172
 
70
173
  /**
71
- * Whether to enable animated transitions
72
- * @default true
174
+ * Component prefix for class names
175
+ * @default 'mtrl'
73
176
  */
74
- animated?: boolean;
177
+ prefix?: string;
75
178
 
76
179
  /**
77
- * Whether tabs should be scrollable when they overflow
78
- * @default true
180
+ * Event handlers configuration
79
181
  */
80
- scrollable?: boolean;
182
+ on?: {
183
+ /**
184
+ * Tab change event handler
185
+ */
186
+ change?: (event: TabChangeEventData) => void;
187
+
188
+ /**
189
+ * Event handlers for other events
190
+ */
191
+ [key: string]: Function | undefined;
192
+ };
81
193
 
82
194
  /**
83
- * Additional CSS classes to add to the tabs component
195
+ * Tab indicator configuration
84
196
  */
85
- class?: string;
197
+ indicator?: IndicatorConfig;
86
198
 
87
199
  /**
88
- * Component prefix for class names
89
- * @default 'mtrl'
200
+ * Tab indicator height in pixels
201
+ * @deprecated Use indicator.height instead
90
202
  */
91
- prefix?: string;
203
+ indicatorHeight?: number;
92
204
 
93
205
  /**
94
- * Component name used in class generation
206
+ * Tab indicator width strategy
207
+ * @deprecated Use indicator.widthStrategy instead
95
208
  */
96
- componentName?: string;
209
+ indicatorWidthStrategy?: 'fixed' | 'dynamic' | 'content';
97
210
  }
98
211
 
99
212
  /**
100
- * Tabs component interface
213
+ * Icon API interface for managing tab icons
101
214
  * @category Components
102
215
  */
103
- export interface TabsComponent {
104
- /** The tabs container DOM element */
105
- element: HTMLElement;
106
-
107
- /** The tabs list DOM element */
108
- tabsListElement: HTMLElement;
109
-
110
- /** The tab indicator DOM element */
111
- indicatorElement: HTMLElement;
112
-
113
- /** API for managing disabled state */
114
- disabled: {
115
- /** Enables the tabs component */
116
- enable: () => void;
117
- /** Disables the tabs component */
118
- disable: () => void;
119
- /** Checks if the tabs component is disabled */
120
- isDisabled: () => boolean;
121
- };
122
-
123
- /** API for managing component lifecycle */
124
- lifecycle: {
125
- /** Destroys the component and cleans up resources */
126
- destroy: () => void;
127
- };
216
+ export interface IconAPI {
217
+ /**
218
+ * Sets the icon HTML content
219
+ * @param html - HTML string for the icon
220
+ * @returns The icon API for chaining
221
+ */
222
+ setIcon: (html: string) => IconAPI;
128
223
 
129
224
  /**
130
- * Gets a class name with the component's prefix
131
- * @param name - Base class name
132
- * @returns Prefixed class name
225
+ * Gets the current icon HTML content
226
+ * @returns HTML string for the icon
133
227
  */
134
- getClass: (name: string) => string;
228
+ getIcon: () => string;
135
229
 
136
230
  /**
137
- * Enables the tabs component
138
- * @returns The tabs component for chaining
231
+ * Gets the icon DOM element
232
+ * @returns The icon element or null if not present
139
233
  */
140
- enable: () => TabsComponent;
234
+ getElement: () => HTMLElement | null;
235
+ }
236
+
237
+ /**
238
+ * Text API interface for managing tab text
239
+ * @category Components
240
+ */
241
+ export interface TextAPI {
242
+ /**
243
+ * Sets the text content
244
+ * @param content - Text content
245
+ * @returns The text API for chaining
246
+ */
247
+ setText: (content: string) => TextAPI;
141
248
 
142
249
  /**
143
- * Disables the tabs component
144
- * @returns The tabs component for chaining
250
+ * Gets the current text content
251
+ * @returns Tab text content
145
252
  */
146
- disable: () => TabsComponent;
253
+ getText: () => string;
147
254
 
148
255
  /**
149
- * Gets all tab items
150
- * @returns Array of tab items
256
+ * Gets the text DOM element
257
+ * @returns The text element or null if not present
151
258
  */
152
- getItems: () => TabItem[];
259
+ getElement: () => HTMLElement | null;
260
+ }
261
+
262
+ /**
263
+ * Tab component interface
264
+ * @category Components
265
+ */
266
+ export interface TabComponent {
267
+ /** The tab's DOM element */
268
+ element: HTMLElement;
269
+
270
+ /** The tab's badge component (if any) */
271
+ badge?: BadgeComponent;
272
+
273
+ /** Gets a class name with the component's prefix */
274
+ getClass: (name: string) => string;
275
+
276
+ /** Gets the tab's value attribute */
277
+ getValue: () => string;
278
+
279
+ /** Sets the tab's value attribute */
280
+ setValue: (value: string) => TabComponent;
281
+
282
+ /** Activates the tab (sets active state) */
283
+ activate: () => TabComponent;
284
+
285
+ /** Deactivates the tab (sets inactive state) */
286
+ deactivate: () => TabComponent;
287
+
288
+ /** Checks if the tab is active */
289
+ isActive: () => boolean;
290
+
291
+ /** Enables the tab (removes disabled attribute) */
292
+ enable: () => TabComponent;
293
+
294
+ /** Disables the tab (adds disabled attribute) */
295
+ disable: () => TabComponent;
296
+
297
+ /** Sets the tab's text content */
298
+ setText: (content: string) => TabComponent;
299
+
300
+ /** Gets the tab's text content */
301
+ getText: () => string;
302
+
303
+ /** Sets the tab's icon */
304
+ setIcon: (icon: string) => TabComponent;
305
+
306
+ /** Gets the tab's icon HTML content */
307
+ getIcon: () => string;
153
308
 
309
+ /** Sets the tab's badge */
310
+ setBadge: (content: string | number) => TabComponent;
311
+
312
+ /** Gets the tab's badge content */
313
+ getBadge: () => string;
314
+
315
+ /** Shows the tab's badge */
316
+ showBadge: () => TabComponent;
317
+
318
+ /** Hides the tab's badge */
319
+ hideBadge: () => TabComponent;
320
+
321
+ /** Gets the badge component instance */
322
+ getBadgeComponent: () => BadgeComponent | undefined;
323
+
324
+ /** Updates the tab's layout style based on content */
325
+ updateLayoutStyle: () => void;
326
+
327
+ /** Adds an event listener to the tab */
328
+ on: (event: string, handler: Function) => TabComponent;
329
+
330
+ /** Removes an event listener from the tab */
331
+ off: (event: string, handler: Function) => TabComponent;
332
+
333
+ /** Destroys the tab component and cleans up resources */
334
+ destroy: () => void;
335
+
336
+ /** API for managing disabled state */
337
+ disabled?: {
338
+ enable: () => void;
339
+ disable: () => void;
340
+ isDisabled: () => boolean;
341
+ };
342
+
343
+ /** API for managing component lifecycle */
344
+ lifecycle?: {
345
+ destroy: () => void;
346
+ };
347
+ }
348
+
349
+ /**
350
+ * Tabs component interface
351
+ * @category Components
352
+ */
353
+ export interface TabsComponent {
154
354
  /**
155
- * Sets tab items, replacing any existing tabs
156
- * @param items - Array of tab items
157
- * @returns The tabs component for chaining
355
+ * Container element
158
356
  */
159
- setItems: (items: TabItem[]) => TabsComponent;
357
+ element: HTMLElement;
160
358
 
161
359
  /**
162
- * Adds a new tab
163
- * @param item - Tab item configuration
164
- * @param index - Optional index to insert at (appends if omitted)
165
- * @returns The tabs component for chaining
360
+ * Creates and adds a new tab to the tabs component
361
+ * @param config - Tab configuration
362
+ * @returns The created tab component
166
363
  */
167
- addTab: (item: TabItem, index?: number) => TabsComponent;
364
+ addTab: (config: TabConfig) => TabComponent;
168
365
 
169
366
  /**
170
- * Removes a tab by ID or index
171
- * @param idOrIndex - Tab ID or index
172
- * @returns The tabs component for chaining
367
+ * Adds a pre-created tab to the tabs component
368
+ * @param tab - Tab component to add
369
+ * @returns Tabs component for chaining
173
370
  */
174
- removeTab: (idOrIndex: string | number) => TabsComponent;
371
+ add: (tab: TabComponent) => TabsComponent;
175
372
 
176
373
  /**
177
- * Gets the currently active tab
178
- * @returns The active tab item or null if none active
374
+ * Gets all tabs in the container
375
+ * @returns Array of tab components
179
376
  */
180
- getActiveTab: () => TabItem | null;
377
+ getTabs: () => TabComponent[];
181
378
 
182
379
  /**
183
- * Gets the index of the currently active tab
184
- * @returns The active tab index or -1 if none active
380
+ * Gets the active tab
381
+ * @returns Active tab or null if none
185
382
  */
186
- getActiveIndex: () => number;
383
+ getActiveTab: () => TabComponent | null;
187
384
 
188
385
  /**
189
- * Sets the active tab by index
190
- * @param index - Index of the tab to activate
191
- * @returns The tabs component for chaining
386
+ * Gets the indicator component
387
+ * @returns Tab indicator component
192
388
  */
193
- setActiveTab: (index: number) => TabsComponent;
389
+ getIndicator?: () => TabIndicator;
194
390
 
195
391
  /**
196
- * Sets the active tab by ID
197
- * @param id - ID of the tab to activate
198
- * @returns The tabs component for chaining
392
+ * Sets a tab as active
393
+ * @param tabOrValue - Tab component or tab value
394
+ * @returns Tabs component for chaining
199
395
  */
200
- setActiveTabById: (id: string) => TabsComponent;
396
+ setActiveTab: (tabOrValue: TabComponent | string) => TabsComponent;
201
397
 
202
398
  /**
203
- * Destroys the tabs component and cleans up resources
399
+ * Removes a tab from the container
400
+ * @param tabOrValue - Tab component or tab value
401
+ * @returns Tabs component for chaining
204
402
  */
205
- destroy: () => void;
403
+ removeTab: (tabOrValue: TabComponent | string) => TabsComponent;
206
404
 
207
405
  /**
208
- * Adds an event listener to the tabs component
209
- * @param event - Event name ('change', 'click', etc.)
210
- * @param handler - Event handler function
211
- * @returns The tabs component for chaining
406
+ * Adds an event listener
407
+ * @param event - Event name
408
+ * @param handler - Event handler
409
+ * @returns Tabs component for chaining
212
410
  */
213
411
  on: (event: string, handler: Function) => TabsComponent;
214
412
 
215
413
  /**
216
- * Removes an event listener from the tabs component
414
+ * Removes an event listener
217
415
  * @param event - Event name
218
- * @param handler - Event handler function
219
- * @returns The tabs component for chaining
416
+ * @param handler - Event handler
417
+ * @returns Tabs component for chaining
220
418
  */
221
419
  off: (event: string, handler: Function) => TabsComponent;
222
- }
223
-
224
- /**
225
- * Event data for tab change events
226
- */
227
- export interface TabChangeEventData {
420
+
228
421
  /**
229
- * Index of the newly activated tab
422
+ * Emit an event
423
+ * @param event - Event name
424
+ * @param data - Event data
425
+ * @returns Tabs component for chaining
230
426
  */
231
- index: number;
427
+ emit?: (event: string, data: any) => TabsComponent;
232
428
 
233
429
  /**
234
- * The newly activated tab item
430
+ * Destroys the tabs component and all tabs
235
431
  */
236
- tab: TabItem;
432
+ destroy: () => void;
237
433
 
238
434
  /**
239
- * Index of the previously active tab or -1
435
+ * Tab click event handler
240
436
  */
241
- previousIndex: number;
437
+ handleTabClick: (event: any, tab: TabComponent) => void;
242
438
 
243
439
  /**
244
- * The previously active tab item or null
440
+ * Scroll container for scrollable tabs
245
441
  */
246
- previousTab: TabItem | null;
442
+ scrollContainer?: HTMLElement;
247
443
  }
@@ -0,0 +1,107 @@
1
+ // src/components/tabs/utils.ts
2
+ import { TabsComponent, TabComponent } from './types';
3
+
4
+ /**
5
+ * Gets the active tab from a component
6
+ * @param component - Component with tabs
7
+ * @returns Active tab or null
8
+ */
9
+ export function getActiveTab(component: any): TabComponent | null {
10
+ // First try the standard method
11
+ if (typeof component.getActiveTab === 'function') {
12
+ return component.getActiveTab();
13
+ }
14
+
15
+ // Fallback: check if component has tabs array
16
+ if (Array.isArray(component.tabs)) {
17
+ return component.tabs.find(tab => tab.isActive && tab.isActive());
18
+ }
19
+
20
+ // If all else fails, return null
21
+ return null;
22
+ }
23
+
24
+ /**
25
+ * Updates tab panels based on active tab
26
+ * @param component - Component with tabs
27
+ */
28
+ export function updateTabPanels(component: any): void {
29
+ // Get active tab using our helper function
30
+ const activeTab = getActiveTab(component);
31
+ if (!activeTab) return;
32
+
33
+ // Make sure getValue exists
34
+ if (typeof activeTab.getValue !== 'function') return;
35
+
36
+ const activeValue = activeTab.getValue();
37
+
38
+ // Find all tab panels in the document
39
+ const tabPanels = document.querySelectorAll(`[role="tabpanel"]`);
40
+
41
+ tabPanels.forEach(panel => {
42
+ // Get the associated tab value
43
+ const forTab = panel.getAttribute('aria-labelledby')?.replace('tab-', '');
44
+
45
+ if (forTab === activeValue) {
46
+ panel.removeAttribute('hidden');
47
+ panel.setAttribute('tabindex', '0');
48
+ } else {
49
+ panel.setAttribute('hidden', 'true');
50
+ panel.setAttribute('tabindex', '-1');
51
+ }
52
+ });
53
+ }
54
+
55
+ /**
56
+ * Sets up keyboard navigation for tabs
57
+ * @param component - Tabs component
58
+ */
59
+ export function setupKeyboardNavigation(component: any): void {
60
+ // Skip if element is missing
61
+ if (!component.element) return;
62
+
63
+ component.element.addEventListener('keydown', (event: KeyboardEvent) => {
64
+ // Only handle arrow keys when tabs container has focus
65
+ if (event.target !== event.currentTarget) return;
66
+
67
+ // Skip if getTabs or setActiveTab don't exist
68
+ if (typeof component.getTabs !== 'function' ||
69
+ typeof component.setActiveTab !== 'function') return;
70
+
71
+ const tabs = component.getTabs();
72
+ const currentTab = getActiveTab(component);
73
+ const currentIndex = currentTab ? tabs.indexOf(currentTab) : -1;
74
+
75
+ let newIndex = currentIndex;
76
+
77
+ switch (event.key) {
78
+ case 'ArrowRight':
79
+ case 'ArrowDown':
80
+ newIndex = currentIndex < tabs.length - 1 ? currentIndex + 1 : 0;
81
+ break;
82
+
83
+ case 'ArrowLeft':
84
+ case 'ArrowUp':
85
+ newIndex = currentIndex > 0 ? currentIndex - 1 : tabs.length - 1;
86
+ break;
87
+
88
+ case 'Home':
89
+ newIndex = 0;
90
+ break;
91
+
92
+ case 'End':
93
+ newIndex = tabs.length - 1;
94
+ break;
95
+
96
+ default:
97
+ return; // Don't handle other keys
98
+ }
99
+
100
+ // If a new tab should be focused
101
+ if (newIndex !== currentIndex && tabs[newIndex]) {
102
+ event.preventDefault();
103
+ tabs[newIndex].element.focus();
104
+ component.setActiveTab(tabs[newIndex]);
105
+ }
106
+ });
107
+ }