mtrl 0.2.6 → 0.2.8

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 (226) hide show
  1. package/demo/build.ts +349 -0
  2. package/demo/index.html +110 -0
  3. package/demo/main.js +448 -0
  4. package/demo/styles.css +239 -0
  5. package/index.ts +18 -0
  6. package/package.json +14 -3
  7. package/server.ts +86 -0
  8. package/src/components/badge/api.ts +70 -63
  9. package/src/components/badge/badge.ts +16 -2
  10. package/src/components/badge/config.ts +66 -13
  11. package/src/components/badge/features.ts +51 -42
  12. package/src/components/badge/index.ts +27 -2
  13. package/src/components/badge/types.ts +62 -30
  14. package/src/components/bottom-app-bar/bottom-app-bar.ts +154 -0
  15. package/src/components/bottom-app-bar/config.ts +29 -0
  16. package/src/components/bottom-app-bar/index.ts +17 -0
  17. package/src/components/bottom-app-bar/types.ts +114 -0
  18. package/src/components/button/api.ts +5 -0
  19. package/src/components/button/button.ts +0 -1
  20. package/src/components/button/config.ts +6 -2
  21. package/src/components/button/index.ts +10 -2
  22. package/src/components/button/types.ts +20 -2
  23. package/src/components/card/card.ts +13 -25
  24. package/src/components/card/config.ts +83 -30
  25. package/src/components/card/content.ts +8 -10
  26. package/src/components/card/features.ts +4 -3
  27. package/src/components/card/index.ts +29 -2
  28. package/src/components/card/types.ts +33 -22
  29. package/src/components/checkbox/config.ts +3 -4
  30. package/src/components/checkbox/index.ts +1 -2
  31. package/src/components/checkbox/types.ts +12 -3
  32. package/src/components/chip/api.ts +170 -221
  33. package/src/components/chip/chip.ts +34 -302
  34. package/src/components/chip/config.ts +1 -2
  35. package/src/components/chip/index.ts +10 -2
  36. package/src/components/chip/types.ts +224 -35
  37. package/src/components/datepicker/api.ts +265 -0
  38. package/src/components/datepicker/config.ts +141 -0
  39. package/src/components/datepicker/datepicker.ts +341 -0
  40. package/src/components/datepicker/index.ts +12 -0
  41. package/src/components/datepicker/render.ts +450 -0
  42. package/src/components/datepicker/types.ts +397 -0
  43. package/src/components/datepicker/utils.ts +289 -0
  44. package/src/components/dialog/api.ts +55 -21
  45. package/src/components/dialog/config.ts +12 -9
  46. package/src/components/dialog/dialog.ts +6 -3
  47. package/src/components/dialog/features.ts +345 -151
  48. package/src/components/dialog/index.ts +38 -8
  49. package/src/components/dialog/types.ts +40 -14
  50. package/src/components/divider/config.ts +81 -0
  51. package/src/components/divider/divider.ts +37 -0
  52. package/src/components/divider/features.ts +207 -0
  53. package/src/components/divider/index.ts +9 -0
  54. package/src/components/divider/types.ts +55 -0
  55. package/src/components/extended-fab/api.ts +141 -0
  56. package/src/components/extended-fab/config.ts +112 -0
  57. package/src/components/extended-fab/extended-fab.ts +125 -0
  58. package/src/components/extended-fab/index.ts +9 -0
  59. package/src/components/extended-fab/types.ts +304 -0
  60. package/src/components/fab/api.ts +97 -0
  61. package/src/components/fab/config.ts +93 -0
  62. package/src/components/fab/fab.ts +67 -0
  63. package/src/components/fab/index.ts +9 -0
  64. package/src/components/fab/types.ts +251 -0
  65. package/src/components/list/config.ts +4 -5
  66. package/src/components/list/features.ts +6 -7
  67. package/src/components/list/index.ts +7 -9
  68. package/src/components/list/list-item.ts +12 -13
  69. package/src/components/list/types.ts +50 -5
  70. package/src/components/list/utils.ts +30 -3
  71. package/src/components/menu/features/items-manager.ts +9 -9
  72. package/src/components/menu/features/positioning.ts +7 -7
  73. package/src/components/menu/features/visibility.ts +7 -7
  74. package/src/components/menu/index.ts +7 -9
  75. package/src/components/menu/menu-item.ts +6 -6
  76. package/src/components/menu/menu.ts +22 -0
  77. package/src/components/menu/types.ts +29 -10
  78. package/src/components/menu/utils.ts +67 -0
  79. package/src/components/navigation/api.ts +78 -50
  80. package/src/components/navigation/config.ts +22 -10
  81. package/src/components/navigation/features/items.ts +284 -0
  82. package/src/components/navigation/index.ts +0 -6
  83. package/src/components/navigation/nav-item.ts +70 -33
  84. package/src/components/navigation/navigation.ts +53 -3
  85. package/src/components/navigation/types.ts +117 -70
  86. package/src/components/progress/api.ts +2 -3
  87. package/src/components/progress/config.ts +2 -3
  88. package/src/components/progress/index.ts +0 -1
  89. package/src/components/progress/progress.ts +1 -2
  90. package/src/components/progress/types.ts +186 -33
  91. package/src/components/radios/config.ts +1 -1
  92. package/src/components/radios/index.ts +0 -1
  93. package/src/components/radios/types.ts +0 -7
  94. package/src/components/search/api.ts +203 -0
  95. package/src/components/search/config.ts +86 -0
  96. package/src/components/search/features/index.ts +4 -0
  97. package/src/components/search/features/search.ts +717 -0
  98. package/src/components/search/features/states.ts +169 -0
  99. package/src/components/search/features/structure.ts +197 -0
  100. package/src/components/search/index.ts +7 -0
  101. package/src/components/search/search.ts +52 -0
  102. package/src/components/search/types.ts +175 -0
  103. package/src/components/segmented-button/config.ts +80 -0
  104. package/src/components/segmented-button/index.ts +4 -0
  105. package/src/components/segmented-button/segment.ts +154 -0
  106. package/src/components/segmented-button/segmented-button.ts +249 -0
  107. package/src/components/segmented-button/types.ts +254 -0
  108. package/src/components/slider/accessibility.md +5 -5
  109. package/src/components/slider/api.ts +41 -120
  110. package/src/components/slider/config.ts +51 -47
  111. package/src/components/slider/features/handlers.ts +495 -0
  112. package/src/components/slider/features/index.ts +1 -2
  113. package/src/components/slider/features/slider.ts +66 -84
  114. package/src/components/slider/features/states.ts +195 -0
  115. package/src/components/slider/features/structure.ts +136 -206
  116. package/src/components/slider/features/ui.ts +145 -206
  117. package/src/components/slider/index.ts +2 -11
  118. package/src/components/slider/slider.ts +9 -12
  119. package/src/components/slider/types.ts +67 -26
  120. package/src/components/snackbar/config.ts +2 -3
  121. package/src/components/snackbar/constants.ts +0 -32
  122. package/src/components/snackbar/index.ts +0 -1
  123. package/src/components/snackbar/position.ts +9 -1
  124. package/src/components/snackbar/types.ts +122 -46
  125. package/src/components/switch/config.ts +2 -3
  126. package/src/components/switch/index.ts +0 -1
  127. package/src/components/switch/types.ts +3 -2
  128. package/src/components/tabs/config.ts +3 -4
  129. package/src/components/tabs/features.ts +4 -2
  130. package/src/components/tabs/index.ts +0 -15
  131. package/src/components/tabs/indicator.ts +73 -13
  132. package/src/components/tabs/tab-api.ts +12 -4
  133. package/src/components/tabs/tab.ts +18 -6
  134. package/src/components/tabs/types.ts +23 -5
  135. package/src/components/textfield/config.ts +2 -3
  136. package/src/components/textfield/index.ts +0 -1
  137. package/src/components/textfield/types.ts +17 -3
  138. package/src/components/timepicker/README.md +277 -0
  139. package/src/components/timepicker/api.ts +632 -0
  140. package/src/components/timepicker/clockdial.ts +482 -0
  141. package/src/components/timepicker/config.ts +228 -0
  142. package/src/components/timepicker/index.ts +3 -0
  143. package/src/components/timepicker/render.ts +613 -0
  144. package/src/components/timepicker/timepicker.ts +117 -0
  145. package/src/components/timepicker/types.ts +336 -0
  146. package/src/components/timepicker/utils.ts +241 -0
  147. package/src/components/tooltip/api.ts +1 -1
  148. package/src/components/tooltip/config.ts +27 -6
  149. package/src/components/tooltip/index.ts +0 -1
  150. package/src/components/tooltip/types.ts +13 -3
  151. package/src/components/top-app-bar/config.ts +83 -0
  152. package/src/components/top-app-bar/index.ts +11 -0
  153. package/src/components/top-app-bar/top-app-bar.ts +316 -0
  154. package/src/components/top-app-bar/types.ts +140 -0
  155. package/src/core/build/_ripple.scss +6 -6
  156. package/src/core/build/ripple.ts +72 -95
  157. package/src/core/compose/features/icon.ts +3 -1
  158. package/src/core/compose/features/ripple.ts +4 -1
  159. package/src/core/compose/features/textlabel.ts +23 -2
  160. package/src/core/dom/create.ts +5 -0
  161. package/src/index.ts +9 -0
  162. package/src/styles/abstract/_theme.scss +9 -1
  163. package/src/styles/components/_badge.scss +182 -0
  164. package/src/styles/components/_bottom-app-bar.scss +103 -0
  165. package/src/{components/button/_styles.scss → styles/components/_button.scss} +0 -10
  166. package/src/{components/checkbox/_styles.scss → styles/components/_checkbox.scss} +0 -2
  167. package/src/styles/components/_datepicker.scss +358 -0
  168. package/src/styles/components/_dialog.scss +259 -0
  169. package/src/styles/components/_divider.scss +57 -0
  170. package/src/styles/components/_extended-fab.scss +267 -0
  171. package/src/styles/components/_fab.scss +225 -0
  172. package/src/{components/navigation/_styles.scss → styles/components/_navigation.scss} +1 -0
  173. package/src/styles/components/_search.scss +306 -0
  174. package/src/styles/components/_segmented-button.scss +117 -0
  175. package/src/{components/slider/_styles.scss → styles/components/_slider.scss} +83 -24
  176. package/src/{components/switch/_styles.scss → styles/components/_switch.scss} +0 -2
  177. package/src/{components/tabs/_styles.scss → styles/components/_tabs.scss} +95 -33
  178. package/src/{components/textfield/_styles.scss → styles/components/_textfield.scss} +70 -67
  179. package/src/styles/components/_timepicker.scss +451 -0
  180. package/src/styles/components/_top-app-bar.scss +225 -0
  181. package/src/styles/main.scss +98 -49
  182. package/src/styles/themes/_autumn.scss +21 -0
  183. package/src/styles/themes/_base-theme.scss +61 -0
  184. package/src/styles/themes/_baseline.scss +58 -0
  185. package/src/styles/themes/_bluekhaki.scss +125 -0
  186. package/src/styles/themes/_brownbeige.scss +125 -0
  187. package/src/styles/themes/_browngreen.scss +125 -0
  188. package/src/styles/themes/_forest.scss +6 -0
  189. package/src/styles/themes/_greenbeige.scss +125 -0
  190. package/src/styles/themes/_material.scss +125 -0
  191. package/src/styles/themes/_ocean.scss +6 -0
  192. package/src/styles/themes/_sageivory.scss +125 -0
  193. package/src/styles/themes/_spring.scss +6 -0
  194. package/src/styles/themes/_summer.scss +5 -0
  195. package/src/styles/themes/_sunset.scss +5 -0
  196. package/src/styles/themes/_tealcaramel.scss +125 -0
  197. package/src/styles/themes/_winter.scss +6 -0
  198. package/src/components/badge/_styles.scss +0 -174
  199. package/src/components/badge/constants.ts +0 -30
  200. package/src/components/button/constants.ts +0 -11
  201. package/src/components/card/constants.ts +0 -84
  202. package/src/components/dialog/_styles.scss +0 -213
  203. package/src/components/dialog/constants.ts +0 -32
  204. package/src/components/menu/constants.ts +0 -154
  205. package/src/components/navigation/constants.ts +0 -200
  206. package/src/components/navigation/features/items.js +0 -192
  207. package/src/components/progress/constants.ts +0 -29
  208. package/src/components/slider/features/appearance.ts +0 -94
  209. package/src/components/slider/features/disabled.ts +0 -68
  210. package/src/components/slider/features/events.ts +0 -164
  211. package/src/components/slider/features/interactions.ts +0 -396
  212. package/src/components/slider/features/keyboard.ts +0 -233
  213. package/src/components/switch/constants.ts +0 -80
  214. package/src/components/tabs/constants.ts +0 -89
  215. package/src/core/collection/adapters/mongodb.js +0 -232
  216. /package/src/{components/card/_styles.scss → styles/components/_card.scss} +0 -0
  217. /package/src/{components/carousel/_styles.scss → styles/components/_carousel.scss} +0 -0
  218. /package/src/{components/chip/_styles.scss → styles/components/_chip.scss} +0 -0
  219. /package/src/{components/list/_styles.scss → styles/components/_list.scss} +0 -0
  220. /package/src/{components/menu/_styles.scss → styles/components/_menu.scss} +0 -0
  221. /package/src/{components/progress/_styles.scss → styles/components/_progress.scss} +0 -0
  222. /package/src/{components/radios/_styles.scss → styles/components/_radios.scss} +0 -0
  223. /package/src/{components/sheet/_styles.scss → styles/components/_sheet.scss} +0 -0
  224. /package/src/{components/snackbar/_styles.scss → styles/components/_snackbar.scss} +0 -0
  225. /package/src/{components/tooltip/_styles.scss → styles/components/_tooltip.scss} +0 -0
  226. /package/src/styles/utilities/{_color.scss → _colors.scss} +0 -0
@@ -8,7 +8,7 @@ export interface TabIndicatorConfig {
8
8
  /** Height of the indicator in pixels */
9
9
  height?: number;
10
10
  /** Width strategy - fixed size or dynamic based on tab width */
11
- widthStrategy?: 'fixed' | 'dynamic' | 'content';
11
+ widthStrategy?: 'fixed' | 'dynamic' | 'content' | 'auto';
12
12
  /** Fixed width in pixels when using fixed strategy */
13
13
  fixedWidth?: number;
14
14
  /** Animation duration in milliseconds */
@@ -21,6 +21,8 @@ export interface TabIndicatorConfig {
21
21
  prefix?: string;
22
22
  /** Custom color for the indicator */
23
23
  color?: string;
24
+ /** Tabs variant (primary or secondary) */
25
+ variant?: string;
24
26
  }
25
27
 
26
28
  /**
@@ -47,12 +49,13 @@ export interface TabIndicator {
47
49
  * Default configuration for tab indicator
48
50
  */
49
51
  const DEFAULT_CONFIG: TabIndicatorConfig = {
50
- widthStrategy: 'fixed',
52
+ widthStrategy: 'auto', // Changed to 'auto' to match variant behavior
51
53
  fixedWidth: 40,
52
54
  animationDuration: 250,
53
55
  animationTiming: 'cubic-bezier(0.4, 0, 0.2, 1)',
54
56
  visible: true,
55
- prefix: 'mtrl'
57
+ prefix: 'mtrl',
58
+ variant: 'primary'
56
59
  };
57
60
 
58
61
  /**
@@ -81,17 +84,38 @@ export const createTabIndicator = (config: TabIndicatorConfig = {}): TabIndicato
81
84
  let currentTab: TabComponent | null = null;
82
85
 
83
86
  /**
84
- * Calculates indicator width based on strategy
87
+ * Calculates indicator width based on strategy and variant
85
88
  * @param tab - Target tab
86
89
  * @returns Width in pixels
87
90
  */
88
91
  const calculateWidth = (tab: TabComponent): number => {
92
+ // Use auto strategy to determine based on variant
93
+ if (mergedConfig.widthStrategy === 'auto') {
94
+ // For secondary tabs, use full tab width
95
+ if (mergedConfig.variant === 'secondary') {
96
+ return tab.element.offsetWidth;
97
+ }
98
+
99
+ // For primary tabs (default), use text label width
100
+ const textElement = tab.element.querySelector(`.${prefix}-tab-text`) ||
101
+ tab.element.querySelector(`.${prefix}-button-text`);
102
+
103
+ if (textElement) {
104
+ return textElement.clientWidth;
105
+ }
106
+
107
+ // Fallback to dynamic if text element not found
108
+ return Math.max(tab.element.offsetWidth / 2, 30);
109
+ }
110
+
111
+ // Handle other strategies when explicitly set
89
112
  switch (mergedConfig.widthStrategy) {
90
113
  case 'dynamic':
91
114
  return Math.max(tab.element.offsetWidth / 2, 30);
92
115
  case 'content':
93
116
  // Try to match content width
94
- const text = tab.element.querySelector(`.${prefix}-button-text`);
117
+ const text = tab.element.querySelector(`.${prefix}-button-text`) ||
118
+ tab.element.querySelector(`.${prefix}-tab-text`);
95
119
  if (text) {
96
120
  return Math.max(text.clientWidth, 30);
97
121
  }
@@ -126,6 +150,46 @@ export const createTabIndicator = (config: TabIndicatorConfig = {}): TabIndicato
126
150
  };
127
151
  };
128
152
 
153
+ /**
154
+ * Calculates indicator position based on variant and width
155
+ * @param tab - Target tab
156
+ * @param indicatorWidth - Width of the indicator
157
+ * @returns {Object} Position information
158
+ */
159
+ const calculatePosition = (tab: TabComponent, indicatorWidth: number): { left: number } => {
160
+ const { left, width: tabWidth } = getTabPosition(tab.element);
161
+
162
+ // For primary tabs with text label width, center under the text
163
+ if (mergedConfig.variant !== 'secondary' &&
164
+ (mergedConfig.widthStrategy === 'auto' || mergedConfig.widthStrategy === 'content')) {
165
+ const textElement = tab.element.querySelector(`.${prefix}-tab-text`) ||
166
+ tab.element.querySelector(`.${prefix}-button-text`);
167
+
168
+ if (textElement) {
169
+ // Get text element position relative to tab
170
+ const textRect = textElement.getBoundingClientRect();
171
+ const tabRect = tab.element.getBoundingClientRect();
172
+ const textLeft = textRect.left - tabRect.left;
173
+
174
+ // Center indicator under text
175
+ return {
176
+ left: left + textLeft
177
+ };
178
+ }
179
+ }
180
+
181
+ // For secondary tabs or when no text element found
182
+ // For centered indicators, center in the tab
183
+ if (indicatorWidth < tabWidth) {
184
+ return {
185
+ left: left + ((tabWidth - indicatorWidth) / 2)
186
+ };
187
+ }
188
+
189
+ // For full-width indicators
190
+ return { left };
191
+ };
192
+
129
193
  /**
130
194
  * Moves indicator to specified tab
131
195
  * @param tab - Target tab
@@ -140,15 +204,11 @@ export const createTabIndicator = (config: TabIndicatorConfig = {}): TabIndicato
140
204
  // Store current tab for later updates
141
205
  currentTab = tab;
142
206
 
143
- // Calculate indicator width
207
+ // Calculate indicator width based on strategy and variant
144
208
  const width = calculateWidth(tab);
145
209
 
146
- // Get tab position directly from DOM
147
- const { left, width: tabWidth } = getTabPosition(tab.element);
148
-
149
- // Calculate position to center indicator on tab
150
- const tabCenter = left + (tabWidth / 2);
151
- const indicatorLeft = tabCenter - (width / 2);
210
+ // Calculate position based on width and variant
211
+ const { left } = calculatePosition(tab, width);
152
212
 
153
213
  // Apply position immediately if requested
154
214
  if (immediate) {
@@ -160,7 +220,7 @@ export const createTabIndicator = (config: TabIndicatorConfig = {}): TabIndicato
160
220
 
161
221
  // Update position and width
162
222
  element.style.width = `${width}px`;
163
- element.style.transform = `translateX(${indicatorLeft}px)`;
223
+ element.style.transform = `translateX(${left}px)`;
164
224
 
165
225
  // Restore transition after immediate update
166
226
  if (immediate) {
@@ -1,9 +1,17 @@
1
1
  // src/components/tabs/tab-api.ts
2
2
  import { TabComponent } from './types';
3
- import { TAB_STATES, TAB_LAYOUT } from './constants';
4
3
  import { BadgeComponent } from '../badge/types';
5
4
  import createBadge from '../badge';
6
5
 
6
+ const TAB_LAYOUT = {
7
+ /** Icon-only tab layout */
8
+ ICON_ONLY: 'icon-only',
9
+ /** Text-only tab layout */
10
+ TEXT_ONLY: 'text-only',
11
+ /** Icon and text layout */
12
+ ICON_AND_TEXT: 'icon-and-text'
13
+ }
14
+
7
15
  /**
8
16
  * API options for a Tab component
9
17
  */
@@ -82,7 +90,7 @@ export const withTabAPI = ({ disabled, lifecycle, button }: ApiOptions) =>
82
90
  * Activates the tab
83
91
  */
84
92
  activate() {
85
- component.element.classList.add(`${component.getClass('tab')}--${TAB_STATES.ACTIVE}`);
93
+ component.element.classList.add(`${component.getClass('tab')}--active`);
86
94
  component.element.setAttribute('aria-selected', 'true');
87
95
  return this;
88
96
  },
@@ -91,7 +99,7 @@ export const withTabAPI = ({ disabled, lifecycle, button }: ApiOptions) =>
91
99
  * Deactivates the tab
92
100
  */
93
101
  deactivate() {
94
- component.element.classList.remove(`${component.getClass('tab')}--${TAB_STATES.ACTIVE}`);
102
+ component.element.classList.remove(`${component.getClass('tab')}--active`);
95
103
  component.element.setAttribute('aria-selected', 'false');
96
104
  return this;
97
105
  },
@@ -100,7 +108,7 @@ export const withTabAPI = ({ disabled, lifecycle, button }: ApiOptions) =>
100
108
  * Checks if the tab is active
101
109
  */
102
110
  isActive() {
103
- return component.element.classList.contains(`${component.getClass('tab')}--${TAB_STATES.ACTIVE}`);
111
+ return component.element.classList.contains(`${component.getClass('tab')}--active`);
104
112
  },
105
113
 
106
114
  /**
@@ -8,6 +8,18 @@ import { createTabConfig } from './config';
8
8
  import createButton from '../button';
9
9
  import createBadge from '../badge';
10
10
 
11
+ /**
12
+ * Tab layout types
13
+ */
14
+ export const TAB_LAYOUT = {
15
+ /** Icon-only tab layout */
16
+ ICON_ONLY: 'icon-only',
17
+ /** Text-only tab layout */
18
+ TEXT_ONLY: 'text-only',
19
+ /** Icon and text layout */
20
+ ICON_AND_TEXT: 'icon-and-text'
21
+ };
22
+
11
23
  /**
12
24
  * Creates a new Tab component following MD3 guidelines
13
25
  * @param {TabConfig} config - Tab configuration object
@@ -49,7 +61,7 @@ export const createTab = (config: TabConfig = {}): TabComponent => {
49
61
  // Set up tab accessibility attributes
50
62
  baseComponent.element.setAttribute('role', 'tab');
51
63
  baseComponent.element.setAttribute('aria-selected',
52
- baseConfig.state === TAB_STATES.ACTIVE ? 'true' : 'false');
64
+ baseConfig.state === 'active' ? 'true' : 'false');
53
65
 
54
66
  // For better accessibility
55
67
  if (baseConfig.value) {
@@ -58,8 +70,8 @@ export const createTab = (config: TabConfig = {}): TabComponent => {
58
70
  }
59
71
 
60
72
  // Add active state if specified in config
61
- if (baseConfig.state === TAB_STATES.ACTIVE) {
62
- baseComponent.element.classList.add(`${baseComponent.getClass('tab')}--${TAB_STATES.ACTIVE}`);
73
+ if (baseConfig.state === 'active') {
74
+ baseComponent.element.classList.add(`${baseComponent.getClass('tab')}--active`);
63
75
  }
64
76
 
65
77
  // Forward button events to our component
@@ -95,7 +107,7 @@ export const createTab = (config: TabConfig = {}): TabComponent => {
95
107
  },
96
108
 
97
109
  activate() {
98
- this.element.classList.add(`${this.getClass('tab')}--${TAB_STATES.ACTIVE}`);
110
+ this.element.classList.add(`${this.getClass('tab')}--active`);
99
111
  this.element.setAttribute('aria-selected', 'true');
100
112
 
101
113
  // Dispatch event for screen readers
@@ -109,13 +121,13 @@ export const createTab = (config: TabConfig = {}): TabComponent => {
109
121
  },
110
122
 
111
123
  deactivate() {
112
- this.element.classList.remove(`${this.getClass('tab')}--${TAB_STATES.ACTIVE}`);
124
+ this.element.classList.remove(`${this.getClass('tab')}--active`);
113
125
  this.element.setAttribute('aria-selected', 'false');
114
126
  return this;
115
127
  },
116
128
 
117
129
  isActive() {
118
- return this.element.classList.contains(`${this.getClass('tab')}--${TAB_STATES.ACTIVE}`);
130
+ return this.element.classList.contains(`${this.getClass('tab')}--active`);
119
131
  },
120
132
 
121
133
  enable() {
@@ -1,8 +1,18 @@
1
1
  // src/components/tabs/types.ts
2
- import { TABS_VARIANTS, TAB_STATES, TAB_LAYOUT } from './constants';
3
2
  import { BadgeComponent } from '../badge/types';
4
3
  import { TabIndicator } from './indicator';
5
4
 
5
+ /**
6
+ * Available tabs variants
7
+ */
8
+ export type TabsVariant = 'primary' | 'secondary';
9
+
10
+ /**
11
+ * Available tabs states
12
+ */
13
+
14
+ export type TabStates = 'active' | 'inactive' | 'disabled';
15
+
6
16
  /**
7
17
  * Configuration for the tab indicator
8
18
  * @category Components
@@ -10,8 +20,14 @@ import { TabIndicator } from './indicator';
10
20
  export interface IndicatorConfig {
11
21
  /** Height of the indicator in pixels */
12
22
  height?: number;
13
- /** Width strategy for the indicator */
14
- widthStrategy?: 'fixed' | 'dynamic' | 'content';
23
+ /**
24
+ * Width strategy for the indicator
25
+ * - 'fixed': Uses a fixed width defined by fixedWidth
26
+ * - 'dynamic': Uses half the tab width
27
+ * - 'content': Uses the text content width
28
+ * - 'auto': Adapts based on variant (primary: text width, secondary: full tab width)
29
+ */
30
+ widthStrategy?: 'fixed' | 'dynamic' | 'content' | 'auto';
15
31
  /** Fixed width in pixels (when using fixed strategy) */
16
32
  fixedWidth?: number;
17
33
  /** Animation duration in milliseconds */
@@ -20,6 +36,8 @@ export interface IndicatorConfig {
20
36
  animationTiming?: string;
21
37
  /** Custom color for the indicator */
22
38
  color?: string;
39
+ /** Tab variant (primary or secondary) */
40
+ variant?: string;
23
41
  }
24
42
 
25
43
  /**
@@ -47,7 +65,7 @@ export interface TabConfig {
47
65
  * Tab state that determines if it's the active destination
48
66
  * @default 'inactive'
49
67
  */
50
- state?: keyof typeof TAB_STATES | string;
68
+ state?: TabStates | string;
51
69
 
52
70
  /**
53
71
  * Whether the tab is initially disabled
@@ -146,7 +164,7 @@ export interface TabsConfig {
146
164
  * Tabs variant (primary or secondary)
147
165
  * @default 'primary'
148
166
  */
149
- variant?: keyof typeof TABS_VARIANTS | string;
167
+ variant?: TabsVariant | string;
150
168
 
151
169
  /**
152
170
  * Initial tabs to create
@@ -5,14 +5,13 @@ import {
5
5
  BaseComponentConfig
6
6
  } from '../../core/config/component-config';
7
7
  import { TextfieldConfig, BaseComponent, ApiOptions } from './types';
8
- import { TEXTFIELD_VARIANTS, TEXTFIELD_TYPES } from './constants';
9
8
 
10
9
  /**
11
10
  * Default configuration for the Textfield component
12
11
  */
13
12
  export const defaultConfig: TextfieldConfig = {
14
- type: TEXTFIELD_TYPES.TEXT,
15
- variant: TEXTFIELD_VARIANTS.FILLED
13
+ type: 'text',
14
+ variant: 'filled'
16
15
  };
17
16
 
18
17
  /**
@@ -1,4 +1,3 @@
1
1
  // src/components/textfield/index.ts
2
2
  export { default } from './textfield'
3
- export { TEXTFIELD_VARIANTS, TEXTFIELD_TYPES } from './constants'
4
3
  export { TextfieldConfig, TextfieldComponent } from './types'
@@ -1,15 +1,29 @@
1
1
  // src/components/textfield/types.ts
2
- import { TEXTFIELD_VARIANTS, TEXTFIELD_TYPES } from './constants';
2
+
3
+ /**
4
+ * Available Textfield variants
5
+ */
6
+ export type TextfieldVariant = 'filled' | 'outlined';
7
+
8
+ /**
9
+ * Available Textfield states
10
+ */
11
+ export type TextfieldStates = 'active' | 'inactive' | 'disabled';
12
+
13
+ /**
14
+ * Available Textfield types
15
+ */
16
+ export type TextfieldTypes = 'text' | 'password' | 'email' | 'number' | 'tel' | 'url' | 'search' | 'multiline';
3
17
 
4
18
  /**
5
19
  * Configuration interface for the Textfield component
6
20
  */
7
21
  export interface TextfieldConfig {
8
22
  /** Input type (text, password, email, etc.) */
9
- type?: keyof typeof TEXTFIELD_TYPES | string;
23
+ type?: TextfieldTypes | string;
10
24
 
11
25
  /** Visual variant (filled, outlined) */
12
- variant?: keyof typeof TEXTFIELD_VARIANTS | string;
26
+ variant?: TextfieldVariant | string;
13
27
 
14
28
  /** Input name attribute */
15
29
  name?: string;
@@ -0,0 +1,277 @@
1
+ # TimePicker Component
2
+
3
+ The `TimePicker` component provides a user-friendly interface for selecting a time. It supports both dial-based
4
+ and keyboard input selection methods, 12-hour and 24-hour formats, and customizable settings.
5
+
6
+ ## Features
7
+
8
+ - **Two Selection Methods**: Choose between a dial interface or keyboard input
9
+ - **Time Format Support**: 12-hour (AM/PM) or 24-hour format
10
+ - **Flexible Configuration**: Customize title, action buttons, and constraints
11
+ - **Responsive Layout**: Adapts between vertical and horizontal layouts based on device orientation
12
+ - **Accessibility**: Keyboard navigation and screen reader support
13
+ - **Full TypeScript Support**: Comprehensive type definitions for better development experience
14
+
15
+ ## Basic Usage
16
+
17
+ ```javascript
18
+ import { createTimePicker } from 'mtrl';
19
+
20
+ // Create a basic time picker
21
+ const timePicker = createTimePicker({
22
+ title: 'Select time',
23
+ value: '14:30', // Initial time (2:30 PM)
24
+ onConfirm: (time) => {
25
+ console.log('Selected time:', time);
26
+ // Update UI with selected time
27
+ document.getElementById('time-input').value = time;
28
+ }
29
+ });
30
+
31
+ // Create an input to trigger the time picker
32
+ const timeInput = document.createElement('input');
33
+ timeInput.id = 'time-input';
34
+ timeInput.type = 'text';
35
+ timeInput.readOnly = true;
36
+ timeInput.value = '14:30';
37
+ timeInput.addEventListener('click', () => {
38
+ timePicker.open();
39
+ });
40
+
41
+ // Add input to the DOM
42
+ document.body.appendChild(timeInput);
43
+ ```
44
+
45
+ ## Configuration Options
46
+
47
+ The `createTimePicker` function accepts a configuration object with the following properties:
48
+
49
+ | Property | Type | Default | Description |
50
+ |----------|------|---------|-------------|
51
+ | `value` | `string` | Current time | Initial time value in 24-hour format (HH:MM or HH:MM:SS) |
52
+ | `type` | `TIME_PICKER_TYPE` | `DIAL` | Type of time picker to display (DIAL or INPUT) |
53
+ | `format` | `TIME_FORMAT` | `AMPM` | Time format to use (AMPM for 12-hour or MILITARY for 24-hour) |
54
+ | `orientation` | `TIME_PICKER_ORIENTATION` | `VERTICAL` | Layout orientation (VERTICAL or HORIZONTAL) |
55
+ | `title` | `string` | `undefined` | Title text for the time picker |
56
+ | `showSeconds` | `boolean` | `false` | Whether to show seconds selector |
57
+ | `class` | `string` | `undefined` | Additional CSS classes to add to the time picker |
58
+ | `closeOnSelect` | `boolean` | `true` | Whether to close the picker when time is selected |
59
+ | `minTime` | `string` | `undefined` | Minimum selectable time in 24-hour format (HH:MM) |
60
+ | `maxTime` | `string` | `undefined` | Maximum selectable time in 24-hour format (HH:MM) |
61
+ | `minuteStep` | `number` | `1` | Step interval for minute selection |
62
+ | `secondStep` | `number` | `1` | Step interval for second selection |
63
+ | `cancelText` | `string` | `'Cancel'` | Custom text for cancel button |
64
+ | `confirmText` | `string` | `'OK'` | Custom text for confirm button |
65
+ | `isOpen` | `boolean` | `false` | Whether the time picker is initially visible |
66
+ | `container` | `string \| HTMLElement` | `document.body` | CSS selector or element to append the time picker to |
67
+ | `clockIcon` | `string` | Default SVG | Custom icon for the clock button |
68
+ | `keyboardIcon` | `string` | Default SVG | Custom icon for the keyboard button |
69
+ | `onChange` | `function` | `undefined` | Callback when time is changed |
70
+ | `onOpen` | `function` | `undefined` | Callback when time picker is opened |
71
+ | `onClose` | `function` | `undefined` | Callback when time picker is closed |
72
+ | `onConfirm` | `function` | `undefined` | Callback when time is confirmed |
73
+ | `onCancel` | `function` | `undefined` | Callback when time picker is canceled |
74
+
75
+ ## API Methods
76
+
77
+ Once created, the TimePicker instance provides the following methods:
78
+
79
+ ### Open, Close, and Toggle
80
+
81
+ ```javascript
82
+ // Open the time picker
83
+ timePicker.open();
84
+
85
+ // Close the time picker
86
+ timePicker.close();
87
+
88
+ // Toggle the time picker open/closed state
89
+ timePicker.toggle();
90
+ ```
91
+
92
+ ### Get and Set Value
93
+
94
+ ```javascript
95
+ // Get the current time value as a string (HH:MM or HH:MM:SS)
96
+ const timeString = timePicker.getValue();
97
+
98
+ // Get the current time as an object with hours, minutes, seconds, and period
99
+ const timeObject = timePicker.getTimeObject();
100
+
101
+ // Set the time value
102
+ timePicker.setValue('15:45');
103
+ ```
104
+
105
+ ### Change Configuration
106
+
107
+ ```javascript
108
+ // Switch between dial and input types
109
+ timePicker.setType(TIME_PICKER_TYPE.INPUT);
110
+
111
+ // Switch between 12-hour and 24-hour formats
112
+ timePicker.setFormat(TIME_FORMAT.MILITARY);
113
+
114
+ // Change the orientation
115
+ timePicker.setOrientation(TIME_PICKER_ORIENTATION.HORIZONTAL);
116
+
117
+ // Update the title
118
+ timePicker.setTitle('Choose departure time');
119
+ ```
120
+
121
+ ### Event Handling
122
+
123
+ ```javascript
124
+ // Listen for time changes
125
+ timePicker.on('change', (time) => {
126
+ console.log('Time changed:', time);
127
+ });
128
+
129
+ // Listen for time picker opening
130
+ timePicker.on('open', () => {
131
+ console.log('Time picker opened');
132
+ });
133
+
134
+ // Remove an event listener
135
+ timePicker.off('change', myHandler);
136
+ ```
137
+
138
+ ### Cleanup
139
+
140
+ ```javascript
141
+ // Destroy the time picker and clean up resources
142
+ timePicker.destroy();
143
+ ```
144
+
145
+ ## Advanced Examples
146
+
147
+ ### 24-Hour Time Picker with Seconds
148
+
149
+ ```javascript
150
+ import { createTimePicker, TIME_FORMAT } from 'mtrl';
151
+
152
+ const timePicker = createTimePicker({
153
+ title: 'Select time',
154
+ value: '18:30:45',
155
+ format: TIME_FORMAT.MILITARY,
156
+ showSeconds: true,
157
+ onConfirm: (time) => {
158
+ console.log('Selected time with seconds:', time);
159
+ }
160
+ });
161
+ ```
162
+
163
+ ### Time Range Selection
164
+
165
+ ```javascript
166
+ import { createTimePicker } from 'mtrl';
167
+
168
+ // Start time picker
169
+ const startTimePicker = createTimePicker({
170
+ title: 'Select start time',
171
+ value: '09:00',
172
+ minTime: '08:00',
173
+ maxTime: '16:00',
174
+ onConfirm: (time) => {
175
+ startTimeInput.value = time;
176
+
177
+ // Update end time constraints
178
+ const [hours, minutes] = time.split(':');
179
+ const startDate = new Date();
180
+ startDate.setHours(parseInt(hours, 10), parseInt(minutes, 10));
181
+
182
+ // Add minimum 30 minutes to start time
183
+ const minEndDate = new Date(startDate);
184
+ minEndDate.setMinutes(minEndDate.getMinutes() + 30);
185
+
186
+ endTimePicker.minTime = `${minEndDate.getHours()}:${minEndDate.getMinutes()}`;
187
+ }
188
+ });
189
+
190
+ // End time picker
191
+ const endTimePicker = createTimePicker({
192
+ title: 'Select end time',
193
+ value: '17:00',
194
+ minTime: '09:30', // Initial minimum (30 min after default start)
195
+ onConfirm: (time) => {
196
+ endTimeInput.value = time;
197
+ }
198
+ });
199
+ ```
200
+
201
+ ## Accessibility
202
+
203
+ The TimePicker component is designed with accessibility in mind:
204
+
205
+ - Keyboard navigation for all controls
206
+ - ARIA attributes for screen reader support
207
+ - Large touch targets for better mobile accessibility
208
+ - High contrast colors and clear visual indicators
209
+ - Support for reducing motion based on user preferences
210
+
211
+ ## Customization
212
+
213
+ ### Custom Styling
214
+
215
+ You can customize the appearance of the TimePicker by overriding the CSS variables and classes:
216
+
217
+ ```css
218
+ /* Custom time picker styling */
219
+ .mtrl-time-picker-dialog {
220
+ --mtrl-primary: #6200ee;
221
+ --mtrl-on-primary: #ffffff;
222
+ --mtrl-surface-container-high: #f5f5f5;
223
+ --mtrl-on-surface: #1d1d1d;
224
+
225
+ /* Add custom shadows */
226
+ box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2);
227
+ }
228
+
229
+ /* Custom dial styling */
230
+ .mtrl-time-picker-dial-face {
231
+ background-color: rgba(98, 0, 238, 0.05);
232
+ }
233
+
234
+ /* Custom buttons */
235
+ .mtrl-time-picker-confirm {
236
+ font-weight: bold;
237
+ }
238
+ ```
239
+
240
+ ### Custom Icons
241
+
242
+ You can also provide custom icons for the clock and keyboard buttons:
243
+
244
+ ```javascript
245
+ const timePicker = createTimePicker({
246
+ // Other configuration...
247
+ clockIcon: '<svg>...</svg>', // Custom clock icon
248
+ keyboardIcon: '<svg>...</svg>' // Custom keyboard icon
249
+ });
250
+ ```
251
+
252
+ ## Browser Support
253
+
254
+ The TimePicker component supports all modern browsers:
255
+
256
+ - Chrome (latest)
257
+ - Firefox (latest)
258
+ - Safari (latest)
259
+ - Edge (latest)
260
+
261
+ ## TypeScript Support
262
+
263
+ The `TimePicker` component includes comprehensive TypeScript definitions:
264
+
265
+ ```typescript
266
+ import { createTimePicker, TIME_PICKER_TYPE, TIME_FORMAT, TimePickerConfig } from 'mtrl';
267
+
268
+ // Configuration with TypeScript
269
+ const config: TimePickerConfig = {
270
+ title: 'Select time',
271
+ value: '14:30',
272
+ type: TIME_PICKER_TYPE.DIAL,
273
+ format: TIME_FORMAT.AMPM
274
+ };
275
+
276
+ const timePicker = createTimePicker(config);
277
+ ```