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.
- package/package.json +6 -3
- package/src/components/badge/_styles.scss +9 -9
- package/src/components/button/_styles.scss +0 -56
- package/src/components/button/button.ts +0 -2
- package/src/components/button/constants.ts +0 -6
- package/src/components/button/index.ts +2 -2
- package/src/components/button/types.ts +1 -7
- package/src/components/card/_styles.scss +67 -25
- package/src/components/card/api.ts +54 -3
- package/src/components/card/card.ts +33 -2
- package/src/components/card/config.ts +143 -21
- package/src/components/card/constants.ts +20 -19
- package/src/components/card/content.ts +299 -2
- package/src/components/card/features.ts +155 -4
- package/src/components/card/index.ts +31 -9
- package/src/components/card/types.ts +138 -15
- package/src/components/chip/chip.ts +1 -9
- package/src/components/chip/constants.ts +0 -10
- package/src/components/chip/index.ts +1 -1
- package/src/components/chip/types.ts +1 -4
- package/src/components/progress/_styles.scss +0 -65
- package/src/components/progress/config.ts +1 -2
- package/src/components/progress/constants.ts +0 -14
- package/src/components/progress/index.ts +1 -1
- package/src/components/progress/progress.ts +1 -4
- package/src/components/progress/types.ts +1 -4
- package/src/components/radios/_styles.scss +0 -45
- package/src/components/radios/api.ts +85 -60
- package/src/components/radios/config.ts +1 -2
- package/src/components/radios/constants.ts +0 -9
- package/src/components/radios/index.ts +1 -1
- package/src/components/radios/radio.ts +34 -11
- package/src/components/radios/radios.ts +2 -1
- package/src/components/radios/types.ts +1 -7
- package/src/components/slider/_styles.scss +193 -281
- package/src/components/slider/accessibility.md +59 -0
- package/src/components/slider/api.ts +36 -101
- package/src/components/slider/config.ts +29 -78
- package/src/components/slider/constants.ts +12 -8
- package/src/components/slider/features/appearance.ts +1 -47
- package/src/components/slider/features/disabled.ts +41 -16
- package/src/components/slider/features/interactions.ts +166 -26
- package/src/components/slider/features/keyboard.ts +125 -6
- package/src/components/slider/features/structure.ts +182 -195
- package/src/components/slider/features/ui.ts +234 -303
- package/src/components/slider/index.ts +11 -1
- package/src/components/slider/slider.ts +1 -1
- package/src/components/slider/types.ts +10 -25
- package/src/components/tabs/_styles.scss +285 -155
- package/src/components/tabs/api.ts +178 -400
- package/src/components/tabs/config.ts +46 -52
- package/src/components/tabs/constants.ts +85 -8
- package/src/components/tabs/features.ts +401 -0
- package/src/components/tabs/index.ts +60 -3
- package/src/components/tabs/indicator.ts +225 -0
- package/src/components/tabs/responsive.ts +144 -0
- package/src/components/tabs/scroll-indicators.ts +149 -0
- package/src/components/tabs/state.ts +186 -0
- package/src/components/tabs/tab-api.ts +258 -0
- package/src/components/tabs/tab.ts +255 -0
- package/src/components/tabs/tabs.ts +50 -31
- package/src/components/tabs/types.ts +324 -128
- package/src/components/tabs/utils.ts +107 -0
- package/src/components/textfield/_styles.scss +0 -98
- package/src/components/textfield/config.ts +2 -3
- package/src/components/textfield/constants.ts +0 -14
- package/src/components/textfield/index.ts +2 -2
- package/src/components/textfield/textfield.ts +0 -2
- package/src/components/textfield/types.ts +1 -4
- package/src/core/compose/component.ts +1 -1
- package/src/core/compose/features/badge.ts +79 -0
- package/src/core/compose/features/index.ts +3 -1
- package/src/styles/abstract/_theme.scss +106 -2
- package/src/components/card/actions.ts +0 -48
- package/src/components/card/header.ts +0 -88
- package/src/components/card/media.ts +0 -52
|
@@ -1,443 +1,221 @@
|
|
|
1
1
|
// src/components/tabs/api.ts
|
|
2
|
-
import { TabsComponent,
|
|
3
|
-
import {
|
|
2
|
+
import { TabsComponent, TabComponent, TabConfig } from './types';
|
|
3
|
+
import { createTab } from './tab';
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* API options for a Tabs component
|
|
7
|
+
*/
|
|
5
8
|
interface ApiOptions {
|
|
6
|
-
|
|
7
|
-
enable: () => void;
|
|
8
|
-
disable: () => void;
|
|
9
|
-
isDisabled: () => boolean;
|
|
10
|
-
};
|
|
9
|
+
/** The component's lifecycle API */
|
|
11
10
|
lifecycle: {
|
|
12
11
|
destroy: () => void;
|
|
13
12
|
};
|
|
14
13
|
}
|
|
15
14
|
|
|
15
|
+
/**
|
|
16
|
+
* Component with required elements and methods
|
|
17
|
+
*/
|
|
16
18
|
interface ComponentWithElements {
|
|
19
|
+
/** The DOM element */
|
|
17
20
|
element: HTMLElement;
|
|
21
|
+
/** Array of tab components */
|
|
22
|
+
tabs: TabComponent[];
|
|
23
|
+
/** Container for tabs */
|
|
24
|
+
tabsContainer: HTMLElement;
|
|
25
|
+
/** Tab click handler */
|
|
26
|
+
handleTabClick: (event: Event, tab: TabComponent) => void;
|
|
27
|
+
/** Scroll container (optional) */
|
|
28
|
+
scrollContainer?: HTMLElement;
|
|
29
|
+
/** Class name helper */
|
|
18
30
|
getClass: (name: string) => string;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
31
|
+
/** Event subscription (optional) */
|
|
32
|
+
on?: (event: string, handler: Function) => any;
|
|
33
|
+
/** Event unsubscription (optional) */
|
|
34
|
+
off?: (event: string, handler: Function) => any;
|
|
35
|
+
/** Event emission (optional) */
|
|
36
|
+
emit?: (event: string, data: any) => any;
|
|
37
|
+
/** Component configuration */
|
|
38
|
+
config: Record<string, any>;
|
|
24
39
|
}
|
|
25
40
|
|
|
26
|
-
/**
|
|
27
|
-
* Creates DOM elements for the tabs component
|
|
28
|
-
* @param component - Base component with element and class getter
|
|
29
|
-
* @returns Component with tabs-specific elements
|
|
30
|
-
*/
|
|
31
|
-
const setupElements = (component: ComponentWithElements) => {
|
|
32
|
-
const baseClass = component.getClass('tabs');
|
|
33
|
-
|
|
34
|
-
// Create tabs list container
|
|
35
|
-
const tabsListElement = document.createElement('div');
|
|
36
|
-
tabsListElement.className = `${baseClass}__list`;
|
|
37
|
-
tabsListElement.setAttribute('role', 'none');
|
|
38
|
-
|
|
39
|
-
// Create tabs indicator
|
|
40
|
-
const indicatorElement = document.createElement('span');
|
|
41
|
-
indicatorElement.className = `${baseClass}__indicator`;
|
|
42
|
-
|
|
43
|
-
// Append elements to container
|
|
44
|
-
component.element.appendChild(tabsListElement);
|
|
45
|
-
component.element.appendChild(indicatorElement);
|
|
46
|
-
|
|
47
|
-
return {
|
|
48
|
-
...component,
|
|
49
|
-
tabsListElement,
|
|
50
|
-
indicatorElement
|
|
51
|
-
};
|
|
52
|
-
};
|
|
53
|
-
|
|
54
41
|
/**
|
|
55
42
|
* Enhances a tabs component with API methods
|
|
56
43
|
* @param {ApiOptions} options - API configuration options
|
|
57
44
|
* @returns {Function} Higher-order function that adds API methods to component
|
|
58
|
-
* @internal This is an internal utility for the Tabs component
|
|
59
45
|
*/
|
|
60
|
-
export const withAPI = ({
|
|
61
|
-
(component:
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
let activeIndex = -1;
|
|
46
|
+
export const withAPI = ({ lifecycle }: ApiOptions) =>
|
|
47
|
+
(component: ComponentWithElements): TabsComponent => ({
|
|
48
|
+
...component as any,
|
|
49
|
+
element: component.element,
|
|
65
50
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
if (item.disabled) {
|
|
82
|
-
tabElement.disabled = true;
|
|
83
|
-
tabElement.setAttribute('aria-disabled', 'true');
|
|
51
|
+
/**
|
|
52
|
+
* Creates and adds a new tab
|
|
53
|
+
*/
|
|
54
|
+
addTab(config: TabConfig) {
|
|
55
|
+
// Create a merged config that inherits from tabs component
|
|
56
|
+
const mergedConfig = {
|
|
57
|
+
...config,
|
|
58
|
+
prefix: component.config.prefix,
|
|
59
|
+
variant: config.variant || component.config.variant
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
// Ensure value is set if not provided
|
|
63
|
+
if (mergedConfig.value === undefined) {
|
|
64
|
+
mergedConfig.value = ''; // Default empty value
|
|
84
65
|
}
|
|
85
66
|
|
|
86
|
-
// Create
|
|
87
|
-
const
|
|
88
|
-
contentElement.className = `${baseClass}__tab-content`;
|
|
67
|
+
// Create the tab
|
|
68
|
+
const tab = createTab(mergedConfig);
|
|
89
69
|
|
|
90
|
-
// Add
|
|
91
|
-
|
|
92
|
-
const iconElement = document.createElement('div');
|
|
93
|
-
iconElement.className = `${baseClass}__tab-icon`;
|
|
94
|
-
iconElement.innerHTML = item.icon;
|
|
95
|
-
contentElement.appendChild(iconElement);
|
|
96
|
-
}
|
|
70
|
+
// Add to internal tabs array
|
|
71
|
+
component.tabs.push(tab);
|
|
97
72
|
|
|
98
|
-
// Add
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
labelElement.textContent = item.label;
|
|
102
|
-
contentElement.appendChild(labelElement);
|
|
73
|
+
// Add to DOM
|
|
74
|
+
const targetContainer = component.tabsContainer;
|
|
75
|
+
targetContainer.appendChild(tab.element);
|
|
103
76
|
|
|
104
|
-
|
|
77
|
+
// Add click handler with robust event handling
|
|
78
|
+
if (tab.on && typeof tab.on === 'function') {
|
|
79
|
+
tab.on('click', (event) => component.handleTabClick(event, tab));
|
|
80
|
+
}
|
|
105
81
|
|
|
106
|
-
// Add
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
const clickedIndex = parseInt(tabElement.getAttribute('data-tab-index') || '0', 10);
|
|
110
|
-
api.setActiveTab(clickedIndex);
|
|
111
|
-
}
|
|
82
|
+
// Add direct DOM event handler as a fallback
|
|
83
|
+
tab.element.addEventListener('click', (event) => {
|
|
84
|
+
component.handleTabClick(event, tab);
|
|
112
85
|
});
|
|
113
86
|
|
|
114
|
-
return
|
|
115
|
-
}
|
|
87
|
+
return tab;
|
|
88
|
+
},
|
|
116
89
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
90
|
+
/**
|
|
91
|
+
* Adds a pre-created tab
|
|
92
|
+
*/
|
|
93
|
+
add(tab: TabComponent) {
|
|
94
|
+
component.tabs.push(tab);
|
|
95
|
+
|
|
96
|
+
// Add tab to DOM
|
|
97
|
+
const targetContainer = component.tabsContainer;
|
|
98
|
+
targetContainer.appendChild(tab.element);
|
|
99
|
+
|
|
100
|
+
// Add click handler via API and direct DOM event
|
|
101
|
+
if (tab.on && typeof tab.on === 'function') {
|
|
102
|
+
tab.on('click', (event) => component.handleTabClick(event, tab));
|
|
123
103
|
}
|
|
124
104
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
) as HTMLElement;
|
|
129
|
-
|
|
130
|
-
if (!tabElement) return;
|
|
131
|
-
|
|
132
|
-
// Calculate position
|
|
133
|
-
const tabRect = tabElement.getBoundingClientRect();
|
|
134
|
-
const listRect = enhancedComponent.tabsListElement.getBoundingClientRect();
|
|
135
|
-
|
|
136
|
-
const left = tabElement.offsetLeft;
|
|
137
|
-
const width = tabRect.width;
|
|
138
|
-
|
|
139
|
-
// Update indicator style
|
|
140
|
-
enhancedComponent.indicatorElement.style.transition = animate ?
|
|
141
|
-
`transform ${ANIMATION_DURATION}ms cubic-bezier(0.4, 0, 0.2, 1)` : 'none';
|
|
142
|
-
enhancedComponent.indicatorElement.style.transform = `translateX(${left}px)`;
|
|
143
|
-
enhancedComponent.indicatorElement.style.width = `${width}px`;
|
|
105
|
+
tab.element.addEventListener('click', (event) => {
|
|
106
|
+
component.handleTabClick(event, tab);
|
|
107
|
+
});
|
|
144
108
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
const scrollLeft = enhancedComponent.tabsListElement.scrollLeft;
|
|
148
|
-
const listWidth = listRect.width;
|
|
149
|
-
|
|
150
|
-
if (left < scrollLeft) {
|
|
151
|
-
enhancedComponent.tabsListElement.scrollTo({
|
|
152
|
-
left: left,
|
|
153
|
-
behavior: animate ? 'smooth' : 'auto'
|
|
154
|
-
});
|
|
155
|
-
} else if (left + width > scrollLeft + listWidth) {
|
|
156
|
-
enhancedComponent.tabsListElement.scrollTo({
|
|
157
|
-
left: left + width - listWidth,
|
|
158
|
-
behavior: animate ? 'smooth' : 'auto'
|
|
159
|
-
});
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
};
|
|
109
|
+
return this;
|
|
110
|
+
},
|
|
163
111
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
const index = parseInt(tab.getAttribute('data-tab-index') || '-1', 10);
|
|
171
|
-
tab.setAttribute('aria-selected', index === activeIndex ? 'true' : 'false');
|
|
172
|
-
|
|
173
|
-
if (index === activeIndex) {
|
|
174
|
-
tab.classList.add(`${component.getClass('tabs')}__tab--active`);
|
|
175
|
-
} else {
|
|
176
|
-
tab.classList.remove(`${component.getClass('tabs')}__tab--active`);
|
|
177
|
-
}
|
|
178
|
-
});
|
|
179
|
-
};
|
|
112
|
+
/**
|
|
113
|
+
* Gets all tabs
|
|
114
|
+
*/
|
|
115
|
+
getTabs() {
|
|
116
|
+
return [...component.tabs];
|
|
117
|
+
},
|
|
180
118
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
// Clear existing tabs
|
|
210
|
-
while (this.tabsListElement.firstChild) {
|
|
211
|
-
this.tabsListElement.removeChild(this.tabsListElement.firstChild);
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
// Store items and create elements
|
|
215
|
-
items = [...newItems];
|
|
216
|
-
|
|
217
|
-
// Create tab elements
|
|
218
|
-
items.forEach((item, index) => {
|
|
219
|
-
const tabElement = createTabElement(item, index);
|
|
220
|
-
this.tabsListElement.appendChild(tabElement);
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
// Reset active tab if needed
|
|
224
|
-
if (activeIndex >= items.length) {
|
|
225
|
-
activeIndex = items.length > 0 ? 0 : -1;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
// Update UI
|
|
229
|
-
updateTabStates();
|
|
230
|
-
updateIndicator(false);
|
|
231
|
-
|
|
232
|
-
return this;
|
|
233
|
-
},
|
|
234
|
-
|
|
235
|
-
addTab(item, index) {
|
|
236
|
-
const newItems = [...items];
|
|
237
|
-
|
|
238
|
-
if (index !== undefined && index >= 0 && index <= items.length) {
|
|
239
|
-
// Insert at specific position
|
|
240
|
-
newItems.splice(index, 0, item);
|
|
241
|
-
|
|
242
|
-
// Adjust active index if needed
|
|
243
|
-
if (activeIndex >= index) {
|
|
244
|
-
activeIndex++;
|
|
245
|
-
}
|
|
246
|
-
} else {
|
|
247
|
-
// Append to end
|
|
248
|
-
newItems.push(item);
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
return this.setItems(newItems);
|
|
252
|
-
},
|
|
253
|
-
|
|
254
|
-
removeTab(idOrIndex) {
|
|
255
|
-
if (items.length === 0) return this;
|
|
256
|
-
|
|
257
|
-
let index = -1;
|
|
258
|
-
|
|
259
|
-
if (typeof idOrIndex === 'number') {
|
|
260
|
-
index = idOrIndex;
|
|
261
|
-
} else {
|
|
262
|
-
// Find by ID
|
|
263
|
-
index = items.findIndex(item => item.id === idOrIndex);
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
if (index < 0 || index >= items.length) return this;
|
|
267
|
-
|
|
268
|
-
const newItems = items.filter((_, i) => i !== index);
|
|
269
|
-
|
|
270
|
-
// Handle active index adjustment
|
|
271
|
-
let newActiveIndex = activeIndex;
|
|
272
|
-
|
|
273
|
-
if (activeIndex === index) {
|
|
274
|
-
// Removed active tab, select a new one
|
|
275
|
-
if (newItems.length > 0) {
|
|
276
|
-
newActiveIndex = Math.min(activeIndex, newItems.length - 1);
|
|
277
|
-
} else {
|
|
278
|
-
newActiveIndex = -1;
|
|
279
|
-
}
|
|
280
|
-
} else if (activeIndex > index) {
|
|
281
|
-
// Active tab is after removed tab, adjust index
|
|
282
|
-
newActiveIndex--;
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
// Update items
|
|
286
|
-
items = newItems;
|
|
287
|
-
|
|
288
|
-
// Rebuild the tabs
|
|
289
|
-
this.setItems(newItems);
|
|
290
|
-
|
|
291
|
-
// Set the correct active tab
|
|
292
|
-
if (newActiveIndex >= 0) {
|
|
293
|
-
this.setActiveTab(newActiveIndex);
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
return this;
|
|
297
|
-
},
|
|
298
|
-
|
|
299
|
-
getActiveTab() {
|
|
300
|
-
return activeIndex >= 0 && activeIndex < items.length ? items[activeIndex] : null;
|
|
301
|
-
},
|
|
302
|
-
|
|
303
|
-
getActiveIndex() {
|
|
304
|
-
return activeIndex;
|
|
305
|
-
},
|
|
306
|
-
|
|
307
|
-
setActiveTab(index) {
|
|
308
|
-
if (
|
|
309
|
-
index < 0 ||
|
|
310
|
-
index >= items.length ||
|
|
311
|
-
items[index].disabled ||
|
|
312
|
-
disabled.isDisabled() ||
|
|
313
|
-
index === activeIndex
|
|
314
|
-
) {
|
|
315
|
-
return this;
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
const previousIndex = activeIndex;
|
|
319
|
-
const previousTab = this.getActiveTab();
|
|
320
|
-
|
|
321
|
-
activeIndex = index;
|
|
322
|
-
const currentTab = items[index];
|
|
323
|
-
|
|
324
|
-
// Update DOM
|
|
325
|
-
updateTabStates();
|
|
326
|
-
updateIndicator();
|
|
327
|
-
|
|
328
|
-
// Emit change event
|
|
329
|
-
component.events.emit('change', {
|
|
330
|
-
index,
|
|
331
|
-
tab: currentTab,
|
|
332
|
-
previousIndex,
|
|
333
|
-
previousTab
|
|
334
|
-
} as TabChangeEventData);
|
|
335
|
-
|
|
336
|
-
return this;
|
|
337
|
-
},
|
|
338
|
-
|
|
339
|
-
setActiveTabById(id) {
|
|
340
|
-
const index = items.findIndex(item => item.id === id);
|
|
341
|
-
if (index >= 0) {
|
|
342
|
-
this.setActiveTab(index);
|
|
343
|
-
}
|
|
344
|
-
return this;
|
|
345
|
-
},
|
|
346
|
-
|
|
347
|
-
destroy() {
|
|
348
|
-
// Clean up event listeners
|
|
349
|
-
const tabElements = enhancedComponent.tabsListElement.querySelectorAll('[role="tab"]');
|
|
350
|
-
tabElements.forEach(tab => {
|
|
351
|
-
tab.replaceWith(tab.cloneNode(true));
|
|
119
|
+
/**
|
|
120
|
+
* Gets the active tab
|
|
121
|
+
*/
|
|
122
|
+
getActiveTab() {
|
|
123
|
+
return component.tabs.find(tab => tab.isActive()) || null;
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Sets a tab as active
|
|
128
|
+
*/
|
|
129
|
+
setActiveTab(tabOrValue: TabComponent | string) {
|
|
130
|
+
const targetTab = typeof tabOrValue === 'string'
|
|
131
|
+
? component.tabs.find(tab => tab.getValue() === tabOrValue)
|
|
132
|
+
: tabOrValue;
|
|
133
|
+
|
|
134
|
+
if (!targetTab) return this;
|
|
135
|
+
|
|
136
|
+
// Deactivate all tabs first
|
|
137
|
+
component.tabs.forEach(tab => tab.deactivate());
|
|
138
|
+
|
|
139
|
+
// Activate the target tab
|
|
140
|
+
targetTab.activate();
|
|
141
|
+
|
|
142
|
+
// Emit change event
|
|
143
|
+
if (component.emit) {
|
|
144
|
+
component.emit('change', {
|
|
145
|
+
tab: targetTab,
|
|
146
|
+
value: targetTab.getValue()
|
|
352
147
|
});
|
|
353
|
-
|
|
354
|
-
// Call lifecycle destroy
|
|
355
|
-
lifecycle.destroy();
|
|
356
|
-
},
|
|
148
|
+
}
|
|
357
149
|
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
150
|
+
return this;
|
|
151
|
+
},
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Removes a tab
|
|
155
|
+
*/
|
|
156
|
+
removeTab(tabOrValue: TabComponent | string) {
|
|
157
|
+
const targetTab = typeof tabOrValue === 'string'
|
|
158
|
+
? component.tabs.find(tab => tab.getValue() === tabOrValue)
|
|
159
|
+
: tabOrValue;
|
|
160
|
+
|
|
161
|
+
if (!targetTab) return this;
|
|
162
|
+
|
|
163
|
+
// Remove from array
|
|
164
|
+
const index = component.tabs.indexOf(targetTab);
|
|
165
|
+
if (index !== -1) {
|
|
166
|
+
component.tabs.splice(index, 1);
|
|
167
|
+
}
|
|
362
168
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
169
|
+
// Clean up tab and remove from DOM
|
|
170
|
+
if (targetTab.element.parentNode) {
|
|
171
|
+
targetTab.element.parentNode.removeChild(targetTab.element);
|
|
366
172
|
}
|
|
367
|
-
};
|
|
368
|
-
|
|
369
|
-
// Set up keyboard navigation
|
|
370
|
-
enhancedComponent.element.addEventListener('keydown', (e: KeyboardEvent) => {
|
|
371
|
-
if (disabled.isDisabled() || !items.length) return;
|
|
372
173
|
|
|
373
|
-
|
|
374
|
-
let newIndex = activeIndex;
|
|
174
|
+
targetTab.destroy();
|
|
375
175
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
break;
|
|
388
|
-
|
|
389
|
-
case 'ArrowLeft':
|
|
390
|
-
case 'ArrowUp':
|
|
391
|
-
// Move to previous non-disabled tab
|
|
392
|
-
for (let i = 1; i <= items.length; i++) {
|
|
393
|
-
const index = (activeIndex - i + items.length) % items.length;
|
|
394
|
-
if (!items[index].disabled) {
|
|
395
|
-
newIndex = index;
|
|
396
|
-
break;
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
break;
|
|
400
|
-
|
|
401
|
-
case 'Home':
|
|
402
|
-
// Move to first non-disabled tab
|
|
403
|
-
for (let i = 0; i < items.length; i++) {
|
|
404
|
-
if (!items[i].disabled) {
|
|
405
|
-
newIndex = i;
|
|
406
|
-
break;
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
break;
|
|
410
|
-
|
|
411
|
-
case 'End':
|
|
412
|
-
// Move to last non-disabled tab
|
|
413
|
-
for (let i = items.length - 1; i >= 0; i--) {
|
|
414
|
-
if (!items[i].disabled) {
|
|
415
|
-
newIndex = i;
|
|
416
|
-
break;
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
break;
|
|
420
|
-
|
|
421
|
-
default:
|
|
422
|
-
return;
|
|
176
|
+
return this;
|
|
177
|
+
},
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Adds an event listener
|
|
181
|
+
*/
|
|
182
|
+
on(event: string, handler: Function) {
|
|
183
|
+
if (component.on) {
|
|
184
|
+
component.on(event, handler);
|
|
423
185
|
}
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
)
|
|
433
|
-
|
|
434
|
-
if (tabElement) {
|
|
435
|
-
tabElement.focus();
|
|
436
|
-
}
|
|
186
|
+
return this;
|
|
187
|
+
},
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Removes an event listener
|
|
191
|
+
*/
|
|
192
|
+
off(event: string, handler: Function) {
|
|
193
|
+
if (component.off) {
|
|
194
|
+
component.off(event, handler);
|
|
437
195
|
}
|
|
438
|
-
|
|
196
|
+
return this;
|
|
197
|
+
},
|
|
439
198
|
|
|
440
|
-
|
|
441
|
-
|
|
199
|
+
/**
|
|
200
|
+
* Destroys the tabs component
|
|
201
|
+
*/
|
|
202
|
+
destroy() {
|
|
203
|
+
// Clean up all tabs first
|
|
204
|
+
component.tabs.forEach(tab => tab.destroy());
|
|
205
|
+
component.tabs.length = 0;
|
|
206
|
+
|
|
207
|
+
// Then destroy container
|
|
208
|
+
lifecycle.destroy();
|
|
209
|
+
}
|
|
210
|
+
});
|
|
442
211
|
|
|
443
|
-
|
|
212
|
+
/**
|
|
213
|
+
* Creates API configuration for the Tabs component
|
|
214
|
+
* @param {Object} comp - Component with lifecycle feature
|
|
215
|
+
* @returns {Object} API configuration object
|
|
216
|
+
*/
|
|
217
|
+
export const getApiConfig = (comp) => ({
|
|
218
|
+
lifecycle: {
|
|
219
|
+
destroy: () => comp.lifecycle.destroy()
|
|
220
|
+
}
|
|
221
|
+
});
|