mtrl 0.2.5 → 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 (70) hide show
  1. package/package.json +1 -1
  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 +149 -155
  36. package/src/components/slider/accessibility.md +59 -0
  37. package/src/components/slider/config.ts +4 -6
  38. package/src/components/slider/features/disabled.ts +41 -16
  39. package/src/components/slider/features/interactions.ts +153 -18
  40. package/src/components/slider/features/keyboard.ts +127 -6
  41. package/src/components/slider/features/structure.ts +32 -5
  42. package/src/components/slider/features/ui.ts +18 -8
  43. package/src/components/tabs/_styles.scss +285 -155
  44. package/src/components/tabs/api.ts +178 -400
  45. package/src/components/tabs/config.ts +46 -52
  46. package/src/components/tabs/constants.ts +85 -8
  47. package/src/components/tabs/features.ts +401 -0
  48. package/src/components/tabs/index.ts +60 -3
  49. package/src/components/tabs/indicator.ts +225 -0
  50. package/src/components/tabs/responsive.ts +144 -0
  51. package/src/components/tabs/scroll-indicators.ts +149 -0
  52. package/src/components/tabs/state.ts +186 -0
  53. package/src/components/tabs/tab-api.ts +258 -0
  54. package/src/components/tabs/tab.ts +255 -0
  55. package/src/components/tabs/tabs.ts +50 -31
  56. package/src/components/tabs/types.ts +324 -128
  57. package/src/components/tabs/utils.ts +107 -0
  58. package/src/components/textfield/_styles.scss +0 -98
  59. package/src/components/textfield/config.ts +2 -3
  60. package/src/components/textfield/constants.ts +0 -14
  61. package/src/components/textfield/index.ts +2 -2
  62. package/src/components/textfield/textfield.ts +0 -2
  63. package/src/components/textfield/types.ts +1 -4
  64. package/src/core/compose/component.ts +1 -1
  65. package/src/core/compose/features/badge.ts +79 -0
  66. package/src/core/compose/features/index.ts +3 -1
  67. package/src/styles/abstract/_theme.scss +106 -2
  68. package/src/components/card/actions.ts +0 -48
  69. package/src/components/card/header.ts +0 -88
  70. package/src/components/card/media.ts +0 -52
@@ -1,80 +1,74 @@
1
1
  // src/components/tabs/config.ts
2
2
  import {
3
3
  createComponentConfig,
4
- createElementConfig
4
+ createElementConfig,
5
+ BaseComponentConfig
5
6
  } from '../../core/config/component-config';
6
- import { TabsConfig } from './types';
7
- import { TABS_VARIANTS } from './constants';
7
+ import { withElement } from '../../core/compose/component';
8
+ import { TabConfig } from './types';
9
+ import { TABS_VARIANTS, TAB_STATES } from './constants';
10
+
11
+ /**
12
+ * Default configuration for a Tab
13
+ */
14
+ export const defaultTabConfig: TabConfig = {
15
+ state: TAB_STATES.INACTIVE,
16
+ componentName: 'tab',
17
+ ripple: true
18
+ };
8
19
 
9
20
  /**
10
21
  * Default configuration for the Tabs component
11
22
  */
12
- export const defaultConfig: TabsConfig = {
23
+ export const defaultTabsConfig = {
13
24
  variant: TABS_VARIANTS.PRIMARY,
14
- showIndicator: true,
15
- animated: true,
16
25
  scrollable: true,
17
- activeIndex: 0
26
+ showDivider: true,
27
+ componentName: 'tabs'
18
28
  };
19
29
 
20
- /**
21
- * Creates the base configuration for Tabs component
22
- * @param {TabsConfig} config - User provided configuration
23
- * @returns {TabsConfig} Complete configuration with defaults applied
24
- */
25
- export const createBaseConfig = (config: TabsConfig = {}): TabsConfig =>
26
- createComponentConfig(defaultConfig, config, 'tabs') as TabsConfig;
30
+ export const createTabsConfig = (config = {}) =>
31
+ createComponentConfig(defaultTabsConfig, config, 'tabs');
27
32
 
28
33
  /**
29
- * Generates element configuration for the Tabs component
30
- * @param {TabsConfig} config - Tabs configuration
31
- * @returns {Object} Element configuration object for withElement
34
+ * Creates the base configuration for a Tab
35
+ * @param {TabConfig} config - User provided configuration
36
+ * @returns {TabConfig} Complete configuration with defaults applied
32
37
  */
33
- export const getElementConfig = (config: TabsConfig) => {
34
- // Create the attributes object
35
- const attrs: Record<string, any> = {
36
- role: 'tablist'
38
+ export const createTabConfig = (config: TabConfig = {}): TabConfig =>
39
+ createComponentConfig(defaultTabConfig, config, 'tab') as TabConfig;
40
+
41
+
42
+ export const getTabsElementConfig = (config) => {
43
+ const elementConfig = {
44
+ tag: 'div',
45
+ attrs: {
46
+ role: 'tablist',
47
+ 'aria-orientation': 'horizontal'
48
+ },
49
+ className: [
50
+ `${config.prefix}-tabs`,
51
+ `${config.prefix}-tabs--${config.variant || TABS_VARIANTS.PRIMARY}`,
52
+ config.class
53
+ ]
37
54
  };
38
55
 
39
- // Only add disabled attribute if it's explicitly true
40
- if (config.disabled === true) {
41
- attrs['aria-disabled'] = 'true';
42
- }
43
-
44
- const extraClasses: string[] = [];
45
-
46
- if (config.scrollable) {
47
- extraClasses.push('--scrollable');
48
- }
49
-
50
- if (config.animated) {
51
- extraClasses.push('--animated');
52
- }
53
-
54
- return createElementConfig(config, {
55
- tag: 'div',
56
- attrs,
57
- className: config.class,
58
- extraClasses,
59
- forwardEvents: {
60
- keydown: true
61
- }
62
- });
56
+ return (component) => withElement(elementConfig)(component);
63
57
  };
64
58
 
65
59
  /**
66
- * Creates API configuration for the Tabs component
60
+ * Creates API configuration for the Tab component
67
61
  * @param {Object} comp - Component with disabled and lifecycle features
68
62
  * @returns {Object} API configuration object
69
63
  */
70
- export const getApiConfig = (comp) => ({
64
+ export const getTabApiConfig = (comp) => ({
71
65
  disabled: {
72
66
  enable: () => comp.disabled.enable(),
73
- disable: () => comp.disabled.disable()
67
+ disable: () => comp.disabled.disable(),
68
+ isDisabled: () => comp.disabled.isDisabled && comp.disabled.isDisabled()
74
69
  },
75
70
  lifecycle: {
76
71
  destroy: () => comp.lifecycle.destroy()
77
- }
78
- });
79
-
80
- export default defaultConfig;
72
+ },
73
+ button: comp.button
74
+ });
@@ -1,12 +1,89 @@
1
1
  // src/components/tabs/constants.ts
2
2
 
3
+ /**
4
+ * Tab variants
5
+ */
3
6
  export const TABS_VARIANTS = {
7
+ /** Primary tabs (standard MD3 style) */
4
8
  PRIMARY: 'primary',
5
- SECONDARY: 'secondary',
6
- SEGMENTED: 'segmented',
7
- NEUTRAL: 'neutral'
8
- }
9
-
10
- export const ANIMATION_DURATION = 250
11
- export const DEFAULT_TAB_MIN_WIDTH = 90
12
- export const DEFAULT_TAB_MAX_WIDTH = 360
9
+ /** Secondary tabs (less prominent variant) */
10
+ SECONDARY: 'secondary'
11
+ };
12
+
13
+ /**
14
+ * Tab states
15
+ */
16
+ export const TAB_STATES = {
17
+ /** Active (selected) tab state */
18
+ ACTIVE: 'active',
19
+ /** Inactive (unselected) tab state */
20
+ INACTIVE: 'inactive',
21
+ /** Disabled tab state */
22
+ DISABLED: 'disabled'
23
+ };
24
+
25
+ /**
26
+ * Tab layout types
27
+ */
28
+ export const TAB_LAYOUT = {
29
+ /** Icon-only tab layout */
30
+ ICON_ONLY: 'icon-only',
31
+ /** Text-only tab layout */
32
+ TEXT_ONLY: 'text-only',
33
+ /** Icon and text layout */
34
+ ICON_AND_TEXT: 'icon-and-text'
35
+ };
36
+
37
+ /**
38
+ * Tab interaction states (for styling)
39
+ */
40
+ export const TAB_INTERACTION_STATES = {
41
+ /** Default enabled state */
42
+ ENABLED: 'enabled',
43
+ /** Hover state */
44
+ HOVER: 'hover',
45
+ /** Focus state */
46
+ FOCUS: 'focus',
47
+ /** Pressed/active state */
48
+ PRESSED: 'pressed'
49
+ };
50
+
51
+ /**
52
+ * Tab animation constants
53
+ */
54
+ export const TAB_ANIMATION = {
55
+ /** Standard transition duration in ms */
56
+ TRANSITION_DURATION: 200,
57
+ /** Standard transition timing function */
58
+ TRANSITION_TIMING: 'cubic-bezier(0.4, 0, 0.2, 1)',
59
+ /** Ripple animation duration in ms */
60
+ RIPPLE_DURATION: 400
61
+ };
62
+
63
+ /**
64
+ * Tab accessibility roles
65
+ */
66
+ export const TAB_A11Y = {
67
+ /** Tab role */
68
+ TAB_ROLE: 'tab',
69
+ /** Tablist role */
70
+ TABLIST_ROLE: 'tablist',
71
+ /** Tabpanel role */
72
+ TABPANEL_ROLE: 'tabpanel'
73
+ };
74
+
75
+ /**
76
+ * MD3 tokens for tab colors
77
+ */
78
+ export const TAB_COLORS = {
79
+ /** Surface color for container */
80
+ SURFACE: 'surface',
81
+ /** Primary color for active tab and indicator */
82
+ PRIMARY: 'primary',
83
+ /** On-surface color for active secondary tabs */
84
+ ON_SURFACE: 'on-surface',
85
+ /** On-surface-variant for inactive tabs */
86
+ ON_SURFACE_VARIANT: 'on-surface-variant',
87
+ /** Outline variant for divider */
88
+ OUTLINE_VARIANT: 'outline-variant'
89
+ };
@@ -0,0 +1,401 @@
1
+ // src/components/tabs/features.ts
2
+ import { createTab } from './tab';
3
+ import { TabConfig, TabComponent } from './types';
4
+ import { BaseComponent } from '../../core/compose/component';
5
+ import { updateTabPanels, getActiveTab } from './utils';
6
+ import { createTabIndicator, TabIndicator } from './indicator';
7
+
8
+ /**
9
+ * Configuration for tabs management feature
10
+ */
11
+ export interface TabsManagementConfig {
12
+ /** Initial tabs to create */
13
+ tabs?: TabConfig[];
14
+ /** Tab variant */
15
+ variant?: string;
16
+ /** Component prefix */
17
+ prefix?: string;
18
+ /** Other configuration properties */
19
+ [key: string]: any;
20
+ }
21
+
22
+ /**
23
+ * Component with tabs management capabilities
24
+ */
25
+ export interface TabsManagementComponent {
26
+ /** Array of tab components */
27
+ tabs: TabComponent[];
28
+
29
+ /** Target container for tabs */
30
+ tabsContainer: HTMLElement;
31
+
32
+ /** Tab click handler */
33
+ handleTabClick: (event: Event, tab: TabComponent) => void;
34
+
35
+ /** Get all tabs */
36
+ getTabs?: () => TabComponent[];
37
+
38
+ /** Get the active tab */
39
+ getActiveTab?: () => TabComponent | null;
40
+ }
41
+
42
+ /**
43
+ * Adds tabs management capabilities to a component
44
+ * @param {TabsManagementConfig} config - Tabs configuration
45
+ * @returns {Function} Component enhancer with tabs management
46
+ */
47
+ export const withTabsManagement = <T extends TabsManagementConfig>(config: T) =>
48
+ <C extends any>(component: C): C & TabsManagementComponent => {
49
+ const tabs: TabComponent[] = [];
50
+
51
+ // Store the target container for tabs
52
+ const tabsContainer = component.scrollContainer || component.element;
53
+
54
+ // Create initial tabs if provided in config
55
+ if (Array.isArray(config.tabs)) {
56
+ config.tabs.forEach(tabConfig => {
57
+ // Create a merged config that inherits from tabs component
58
+ const mergedConfig = {
59
+ ...tabConfig,
60
+ prefix: config.prefix,
61
+ variant: tabConfig.variant || config.variant
62
+ };
63
+
64
+ // Create the tab
65
+ const tab = createTab(mergedConfig);
66
+
67
+ // Add to internal tabs array
68
+ tabs.push(tab);
69
+
70
+ // Add to DOM
71
+ tabsContainer.appendChild(tab.element);
72
+ });
73
+ }
74
+
75
+ /**
76
+ * Gets all tabs
77
+ */
78
+ const getTabs = () => {
79
+ return [...tabs];
80
+ };
81
+
82
+ /**
83
+ * Gets the active tab
84
+ */
85
+ const getActiveTab = () => {
86
+ return tabs.find(tab => tab.isActive()) || null;
87
+ };
88
+
89
+ /**
90
+ * Handles tab click events
91
+ */
92
+ const handleTabClick = (event: any, tab: TabComponent) => {
93
+ // Check if event is a DOM event with preventDefault
94
+ if (event && typeof event.preventDefault === 'function') {
95
+ event.preventDefault();
96
+ }
97
+
98
+ // Skip if tab is disabled
99
+ if (tab.disabled && tab.disabled.isDisabled && tab.disabled.isDisabled()) {
100
+ return;
101
+ }
102
+
103
+ // Deactivate all tabs first
104
+ tabs.forEach(t => t.deactivate());
105
+
106
+ // Activate the clicked tab
107
+ tab.activate();
108
+
109
+ // Get the tab value
110
+ const value = tab.getValue();
111
+
112
+ // Update tab panels
113
+ updateTabPanels({
114
+ tabs,
115
+ getActiveTab: () => tabs.find(t => t.isActive()) || null
116
+ });
117
+
118
+ // Emit change event if component has emit method
119
+ if (typeof component['emit'] === 'function') {
120
+ component['emit']('change', {
121
+ tab,
122
+ value
123
+ });
124
+ }
125
+ };
126
+
127
+ // Add click handlers to existing tabs
128
+ tabs.forEach(tab => {
129
+ // Add event listener directly and via API if available
130
+ if (tab.on && typeof tab.on === 'function') {
131
+ tab.on('click', (event) => handleTabClick(event, tab));
132
+ }
133
+ // Also add direct DOM event listener as a fallback
134
+ tab.element.addEventListener('click', (event) => handleTabClick(event, tab));
135
+ });
136
+
137
+ return {
138
+ ...component,
139
+ tabs,
140
+ tabsContainer,
141
+ handleTabClick,
142
+ getTabs,
143
+ getActiveTab
144
+ };
145
+ };
146
+
147
+ /**
148
+ * Configuration for scrollable feature
149
+ */
150
+ export interface ScrollableConfig {
151
+ /** Whether tabs are scrollable horizontally */
152
+ scrollable?: boolean;
153
+ /** Other configuration properties */
154
+ [key: string]: any;
155
+ }
156
+
157
+ /**
158
+ * Component with scrollable capabilities
159
+ */
160
+ export interface ScrollableComponent {
161
+ /** Scroll container element */
162
+ scrollContainer?: HTMLElement;
163
+ }
164
+
165
+ /**
166
+ * Adds scrollable capabilities to a component
167
+ * @param {ScrollableConfig} config - Scrollable configuration
168
+ * @returns {Function} Component enhancer with scrollable container
169
+ */
170
+ export const withScrollable = <T extends ScrollableConfig>(config: T) =>
171
+ <C extends any>(component: C): C & ScrollableComponent => {
172
+ // Skip if scrollable is explicitly false
173
+ if (config.scrollable === false) {
174
+ return component as C & ScrollableComponent;
175
+ }
176
+
177
+ // Add scrollable class
178
+ component.element.classList.add(`${component.getClass('tabs')}--scrollable`);
179
+
180
+ // Create container for tabs that can scroll
181
+ const scrollContainer = document.createElement('div');
182
+ scrollContainer.className = `${component.getClass('tabs')}-scroll`;
183
+
184
+ // Move any existing children to scroll container
185
+ while (component.element.firstChild) {
186
+ scrollContainer.appendChild(component.element.firstChild);
187
+ }
188
+
189
+ // Add scroll container to the main element
190
+ component.element.appendChild(scrollContainer);
191
+
192
+ return {
193
+ ...component,
194
+ scrollContainer
195
+ };
196
+ };
197
+
198
+ /**
199
+ * Configuration for divider feature
200
+ */
201
+ export interface DividerConfig {
202
+ /** Whether to show a divider below the tabs */
203
+ showDivider?: boolean;
204
+ /** Other configuration properties */
205
+ [key: string]: any;
206
+ }
207
+
208
+ /**
209
+ * Adds a divider to a component
210
+ * @param {DividerConfig} config - Divider configuration
211
+ * @returns {Function} Component enhancer with divider
212
+ */
213
+ export const withDivider = <T extends DividerConfig>(config: T) =>
214
+ <C extends any>(component: C): C => {
215
+ // Skip if divider is explicitly disabled
216
+ if (config.showDivider === false) {
217
+ return component;
218
+ }
219
+
220
+ // Create the divider element
221
+ const divider = document.createElement('div');
222
+ divider.className = `${component.getClass('tabs')}-divider`;
223
+
224
+ // Add the divider to the main element
225
+ component.element.appendChild(divider);
226
+
227
+ return component;
228
+ };
229
+
230
+ /**
231
+ * Configuration for indicator feature
232
+ */
233
+ export interface IndicatorFeatureConfig {
234
+ /** Component prefix */
235
+ prefix?: string;
236
+ /** Width strategy for the indicator */
237
+ widthStrategy?: 'fixed' | 'dynamic' | 'content';
238
+ /** Height of the indicator in pixels */
239
+ height?: number;
240
+ /** Fixed width in pixels (when using fixed strategy) */
241
+ fixedWidth?: number;
242
+ /** Animation duration in milliseconds */
243
+ animationDuration?: number;
244
+ /** Animation timing function */
245
+ animationTiming?: string;
246
+ /** Custom color for the indicator */
247
+ color?: string;
248
+ /** Legacy height property */
249
+ indicatorHeight?: number;
250
+ /** Legacy width strategy property */
251
+ indicatorWidthStrategy?: 'fixed' | 'dynamic' | 'content';
252
+ /** Indicator configuration object */
253
+ indicator?: {
254
+ widthStrategy?: 'fixed' | 'dynamic' | 'content';
255
+ height?: number;
256
+ fixedWidth?: number;
257
+ animationDuration?: number;
258
+ animationTiming?: string;
259
+ color?: string;
260
+ };
261
+ }
262
+
263
+ /**
264
+ * Component with indicator capability
265
+ */
266
+ export interface IndicatorComponent {
267
+ /** The indicator instance */
268
+ indicator: TabIndicator;
269
+ /** Get the indicator instance */
270
+ getIndicator: () => TabIndicator;
271
+ }
272
+
273
+ /**
274
+ * Enhances a component with tab indicator functionality
275
+ * @param config - Indicator configuration
276
+ * @returns Component enhancer with indicator functionality
277
+ */
278
+ export const withIndicator = <T extends IndicatorFeatureConfig>(config: T) =>
279
+ <C extends any>(component: C): C & IndicatorComponent => {
280
+ // Create indicator with proper config
281
+ const indicatorConfig = config.indicator || {};
282
+ const indicator: TabIndicator = createTabIndicator({
283
+ prefix: config.prefix,
284
+ // Support both new and legacy config
285
+ widthStrategy: indicatorConfig.widthStrategy || config.indicatorWidthStrategy || 'fixed',
286
+ height: indicatorConfig.height || config.indicatorHeight || 3,
287
+ fixedWidth: indicatorConfig.fixedWidth || 40,
288
+ animationDuration: indicatorConfig.animationDuration || 250,
289
+ animationTiming: indicatorConfig.animationTiming || 'cubic-bezier(0.4, 0, 0.2, 1)',
290
+ color: indicatorConfig.color
291
+ });
292
+
293
+ // Find the scroll container and add the indicator to it
294
+ const scrollContainer = component.scrollContainer || component.element;
295
+ if (!scrollContainer) {
296
+ console.error('No scroll container found - cannot add indicator');
297
+ throw new Error('Failed to create tabs: No scroll container found');
298
+ }
299
+
300
+ // Add the indicator to the scroll container
301
+ scrollContainer.appendChild(indicator.element);
302
+
303
+ // Store the original handlers to enhance
304
+ const originalHandleTabClick = component.handleTabClick;
305
+
306
+ // Replace tab click handler to ensure indicator updates
307
+ component.handleTabClick = function(event, tab) {
308
+ // Skip if tab is disabled
309
+ if (tab.disabled && tab.disabled.isDisabled && tab.disabled.isDisabled()) {
310
+ return;
311
+ }
312
+
313
+ // Call original handler
314
+ originalHandleTabClick.call(this, event, tab);
315
+
316
+ // Move indicator with a slight delay to ensure DOM updates
317
+ setTimeout(() => {
318
+ indicator.moveToTab(tab);
319
+ }, 10);
320
+ };
321
+
322
+ // Position indicator on initial active tab
323
+ setTimeout(() => {
324
+ const activeTab = component.tabs.find(tab => tab.isActive());
325
+ if (activeTab) {
326
+ indicator.moveToTab(activeTab, true);
327
+ }
328
+ }, 50);
329
+
330
+ // Add scroll event handling
331
+ const scrollHandler = () => {
332
+ const activeTab = component.tabs.find(tab => tab.isActive());
333
+ if (activeTab) {
334
+ indicator.moveToTab(activeTab, true);
335
+ }
336
+ };
337
+
338
+ if (scrollContainer) {
339
+ scrollContainer.addEventListener('scroll', scrollHandler);
340
+ }
341
+
342
+ // Watch for window resize to update indicator
343
+ const resizeObserver = new ResizeObserver(() => {
344
+ const activeTab = component.tabs.find(tab => tab.isActive());
345
+ if (activeTab) {
346
+ indicator.moveToTab(activeTab, true);
347
+ }
348
+ });
349
+
350
+ resizeObserver.observe(scrollContainer);
351
+
352
+ // Add MutationObserver to detect tab state changes
353
+ const mutationObserver = new MutationObserver((mutations) => {
354
+ for (const mutation of mutations) {
355
+ if (mutation.type === 'attributes' &&
356
+ mutation.attributeName === 'class' &&
357
+ (mutation.target as HTMLElement).classList.contains(`${config.prefix}-tab--active`)) {
358
+ // Find the corresponding tab component
359
+ const tabElement = mutation.target as HTMLElement;
360
+ const activeTab = component.tabs.find(tab => tab.element === tabElement);
361
+ if (activeTab) {
362
+ indicator.moveToTab(activeTab);
363
+ }
364
+ }
365
+ }
366
+ });
367
+
368
+ // Observe all tabs for class changes
369
+ if (Array.isArray(component.tabs)) {
370
+ component.tabs.forEach(tab => {
371
+ if (tab.element) {
372
+ mutationObserver.observe(tab.element, { attributes: true });
373
+ }
374
+ });
375
+ }
376
+
377
+ // Enhance component's destroy method
378
+ const originalDestroy = component.destroy || (() => {});
379
+
380
+ // Override destroy to clean up resources
381
+ (component as any).destroy = function() {
382
+ indicator.destroy();
383
+ resizeObserver.disconnect();
384
+ mutationObserver.disconnect();
385
+
386
+ if (scrollContainer) {
387
+ scrollContainer.removeEventListener('scroll', scrollHandler);
388
+ }
389
+
390
+ // Call original destroy if it exists
391
+ if (typeof originalDestroy === 'function') {
392
+ originalDestroy.call(this);
393
+ }
394
+ };
395
+
396
+ return {
397
+ ...component,
398
+ indicator,
399
+ getIndicator: () => indicator
400
+ };
401
+ };
@@ -1,4 +1,61 @@
1
1
  // src/components/tabs/index.ts
2
- export { default } from './tabs';
3
- export { TABS_VARIANTS } from './constants';
4
- export { TabsConfig, TabsComponent, TabItem, TabChangeEventData } from './types';
2
+ import createTabs from './tabs';
3
+ import { createTab } from './tab';
4
+ import { addScrollIndicators } from './scroll-indicators';
5
+ import { setupResponsiveBehavior } from './responsive';
6
+ import { createTabsState } from './state';
7
+ import { createTabIndicator } from './indicator';
8
+ import { updateTabPanels, setupKeyboardNavigation } from './utils';
9
+
10
+ export {
11
+ // Main component creators
12
+ createTabs,
13
+ createTab,
14
+
15
+ // Constants
16
+ TABS_VARIANTS,
17
+ TAB_STATES,
18
+ TAB_LAYOUT,
19
+ TAB_INTERACTION_STATES,
20
+ TAB_ANIMATION,
21
+ TAB_A11Y,
22
+ TAB_COLORS
23
+ } from './constants';
24
+
25
+ export {
26
+ // Types
27
+ TabsConfig,
28
+ TabsComponent,
29
+ TabComponent,
30
+ TabConfig,
31
+ TabChangeEventData,
32
+ IndicatorConfig
33
+ } from './types';
34
+
35
+ // Export enhancers and utilities
36
+ export {
37
+ addScrollIndicators,
38
+ setupResponsiveBehavior,
39
+ createTabsState,
40
+ createTabIndicator,
41
+ updateTabPanels,
42
+ setupKeyboardNavigation
43
+ };
44
+
45
+ // Export features
46
+ export {
47
+ withTabsManagement,
48
+ withScrollable,
49
+ withDivider,
50
+ withIndicator,
51
+ TabsManagementConfig,
52
+ TabsManagementComponent,
53
+ ScrollableConfig,
54
+ ScrollableComponent,
55
+ DividerConfig,
56
+ IndicatorFeatureConfig,
57
+ IndicatorComponent
58
+ } from './features';
59
+
60
+ // Default export
61
+ export default createTabs;