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
@@ -1,6 +1,16 @@
1
1
  // src/components/badge/features.ts
2
- import { BADGE_VARIANTS, BADGE_SIZES, BADGE_COLORS, BADGE_POSITIONS } from './constants';
3
2
  import { BadgeConfig } from './types';
3
+ import { formatBadgeLabel } from './config';
4
+
5
+ // Common badge variants
6
+ const VARIANT_SMALL = 'small';
7
+ const VARIANT_LARGE = 'large';
8
+
9
+ // Common badge colors
10
+ const COLOR_ERROR = 'error';
11
+
12
+ // Common badge positions
13
+ const POSITION_TOP_RIGHT = 'top-right';
4
14
 
5
15
  /**
6
16
  * Higher-order function that adds visibility control features to a component
@@ -64,15 +74,24 @@ export const withVisibility = () => component => {
64
74
  */
65
75
  export const withVariant = (config: BadgeConfig) => component => {
66
76
  // Get variant from config with fallback to default
67
- const variant = config.variant || BADGE_VARIANTS.STANDARD;
68
-
69
- // Apply variant class if not standard variant
70
- if (variant !== BADGE_VARIANTS.STANDARD) {
71
- component.element.classList.add(`${component.getClass('badge')}--${variant}`);
77
+ const variant = config.variant || VARIANT_LARGE;
78
+
79
+ // Apply variant class
80
+ component.element.classList.add(`${component.getClass('badge')}--${variant}`);
81
+
82
+ // Small badges (dot variant) don't have text
83
+ if (variant === VARIANT_SMALL) {
84
+ component.element.textContent = '';
85
+ component.element.setAttribute('aria-hidden', 'true');
86
+ } else {
87
+ // Add accessibility for large badges
88
+ component.element.setAttribute('role', 'status');
72
89
 
73
- // Clear content if dot variant
74
- if (variant === BADGE_VARIANTS.DOT) {
75
- component.element.textContent = '';
90
+ // Set the label if available and variant is large
91
+ if (config.label !== undefined && config.label !== '') {
92
+ // Format the label according to max value
93
+ const formattedLabel = formatBadgeLabel(config.label, config.max);
94
+ component.element.textContent = formattedLabel;
76
95
  }
77
96
  }
78
97
 
@@ -86,7 +105,7 @@ export const withVariant = (config: BadgeConfig) => component => {
86
105
  */
87
106
  export const withColor = (config: BadgeConfig) => component => {
88
107
  // Get color from config with fallback to default
89
- const color = config.color || BADGE_COLORS.ERROR;
108
+ const color = config.color || COLOR_ERROR;
90
109
 
91
110
  // Apply color class
92
111
  component.element.classList.add(`${component.getClass('badge')}--${color}`);
@@ -94,40 +113,22 @@ export const withColor = (config: BadgeConfig) => component => {
94
113
  return component;
95
114
  };
96
115
 
97
- /**
98
- * Higher-order function that adds size features to a badge component
99
- * @param {BadgeConfig} config - Badge configuration
100
- * @returns {Function} Component enhancer with size features
101
- */
102
- export const withSize = (config: BadgeConfig) => component => {
103
- // Get size from config with fallback to default
104
- const size = config.size || BADGE_SIZES.MEDIUM;
105
-
106
- // Apply size class if not medium (default)
107
- if (size !== BADGE_SIZES.MEDIUM) {
108
- component.element.classList.add(`${component.getClass('badge')}--${size}`);
109
- }
110
-
111
- return component;
112
- };
113
-
114
116
  /**
115
117
  * Higher-order function that adds positioning features to a badge component
116
118
  * @param {BadgeConfig} config - Badge configuration
117
119
  * @returns {Function} Component enhancer with positioning features
118
120
  */
119
121
  export const withPosition = (config: BadgeConfig) => component => {
120
- // Skip for standalone badges
121
- if (config.standalone) {
122
- return component;
123
- }
124
-
125
122
  // Get position from config with fallback to default
126
- const position = config.position || BADGE_POSITIONS.TOP_RIGHT;
123
+ const position = config.position || POSITION_TOP_RIGHT;
127
124
 
128
- // Apply position class and positioned class
125
+ // Apply position class
129
126
  component.element.classList.add(`${component.getClass('badge')}--${position}`);
130
- component.element.classList.add(`${component.getClass('badge')}--positioned`);
127
+
128
+ // If there's a target, add positioned class
129
+ if (config.target) {
130
+ component.element.classList.add(`${component.getClass('badge')}--positioned`);
131
+ }
131
132
 
132
133
  return component;
133
134
  };
@@ -138,8 +139,8 @@ export const withPosition = (config: BadgeConfig) => component => {
138
139
  * @returns {Function} Component enhancer with max value features
139
140
  */
140
141
  export const withMax = (config: BadgeConfig) => component => {
141
- // Skip if no max is defined
142
- if (config.max === undefined) {
142
+ // Skip if no max is defined or for small badges
143
+ if (config.max === undefined || config.variant === VARIANT_SMALL) {
143
144
  return component;
144
145
  }
145
146
 
@@ -147,9 +148,14 @@ export const withMax = (config: BadgeConfig) => component => {
147
148
  component.config.max = config.max;
148
149
 
149
150
  // Apply max formatting if needed
150
- if (typeof config.content === 'number' && config.content > config.max) {
151
- component.element.textContent = String(config.max);
152
- component.element.classList.add(`${component.getClass('badge')}--max`);
151
+ if (config.label !== undefined && config.label !== '') {
152
+ const formattedLabel = formatBadgeLabel(config.label, config.max);
153
+ component.element.textContent = formattedLabel;
154
+
155
+ // Add overflow class if label was truncated
156
+ if (typeof config.label === 'number' && config.label > config.max) {
157
+ component.element.classList.add(`${component.getClass('badge')}--overflow`);
158
+ }
153
159
  }
154
160
 
155
161
  return component;
@@ -161,8 +167,8 @@ export const withMax = (config: BadgeConfig) => component => {
161
167
  * @returns {Function} Component enhancer with attachment features
162
168
  */
163
169
  export const withAttachment = (config: BadgeConfig) => component => {
164
- // Skip for standalone badges or if no target is provided
165
- if (config.standalone || !config.target) {
170
+ // Skip if no target is provided
171
+ if (!config.target) {
166
172
  return component;
167
173
  }
168
174
 
@@ -170,6 +176,9 @@ export const withAttachment = (config: BadgeConfig) => component => {
170
176
  const wrapper = document.createElement('div');
171
177
  wrapper.classList.add(component.getClass('badge-wrapper'));
172
178
 
179
+ // Make sure positioning context is correct
180
+ wrapper.style.position = 'relative';
181
+
173
182
  // Replace the target with the wrapper
174
183
  const parent = config.target.parentNode;
175
184
  if (parent) {
@@ -1,4 +1,29 @@
1
1
  // src/components/badge/index.ts
2
2
  export { default } from './badge';
3
- export { BADGE_VARIANTS, BADGE_SIZES, BADGE_COLORS, BADGE_POSITIONS } from './constants';
4
- export { BadgeConfig, BadgeComponent } from './types';
3
+ export { BadgeConfig, BadgeComponent, BadgeVariant, BadgeColor, BadgePosition } from './types';
4
+
5
+ // Export common badge constants for convenience and backward compatibility
6
+ export const BADGE_VARIANTS = {
7
+ SMALL: 'small',
8
+ LARGE: 'large'
9
+ } as const;
10
+
11
+ export const BADGE_COLORS = {
12
+ ERROR: 'error',
13
+ PRIMARY: 'primary',
14
+ SECONDARY: 'secondary',
15
+ TERTIARY: 'tertiary',
16
+ SUCCESS: 'success',
17
+ WARNING: 'warning',
18
+ INFO: 'info'
19
+ } as const;
20
+
21
+ export const BADGE_POSITIONS = {
22
+ TOP_RIGHT: 'top-right',
23
+ TOP_LEFT: 'top-left',
24
+ BOTTOM_RIGHT: 'bottom-right',
25
+ BOTTOM_LEFT: 'bottom-left'
26
+ } as const;
27
+
28
+ // Export max characters constant
29
+ export const BADGE_MAX_CHARACTERS = 4;
@@ -1,43 +1,78 @@
1
1
  // src/components/badge/types.ts
2
- import { BADGE_VARIANTS, BADGE_SIZES, BADGE_COLORS, BADGE_POSITIONS } from './constants';
2
+
3
+ /**
4
+ * Badge variant types
5
+ * @category Components
6
+ */
7
+ export type BadgeVariant = 'small' | 'large';
8
+
9
+ /**
10
+ * Badge color types
11
+ * @category Components
12
+ */
13
+ export type BadgeColor = 'error' | 'primary' | 'secondary' | 'tertiary' | 'success' | 'warning' | 'info';
14
+
15
+ /**
16
+ * Badge position types
17
+ * @category Components
18
+ */
19
+ export type BadgePosition = 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left';
3
20
 
4
21
  /**
5
22
  * Configuration interface for the Badge component
23
+ * Following Material Design 3 specifications
6
24
  */
7
25
  export interface BadgeConfig {
8
- /** Badge variant (standard, dot, outlined) */
9
- variant?: keyof typeof BADGE_VARIANTS | BADGE_VARIANTS;
10
-
11
- /** Badge size (small, medium, large) */
12
- size?: keyof typeof BADGE_SIZES | BADGE_SIZES;
13
-
14
- /** Badge color (primary, secondary, tertiary, error, success, warning, info) */
15
- color?: keyof typeof BADGE_COLORS | BADGE_COLORS;
16
-
17
- /** Badge position relative to its container (top-right, top-left, bottom-right, bottom-left) */
18
- position?: keyof typeof BADGE_POSITIONS | BADGE_POSITIONS;
19
-
20
- /** Text content inside the badge */
21
- content?: string | number;
22
-
23
- /** Maximum value to display (shows "{max}+" if content exceeds max) */
26
+ /**
27
+ * Badge variant (small dot or large numbered)
28
+ * Small badge (6dp) or Large badge (16dp height)
29
+ * @default 'large'
30
+ */
31
+ variant?: BadgeVariant | string;
32
+
33
+ /**
34
+ * Badge color (error is default)
35
+ * @default 'error'
36
+ */
37
+ color?: BadgeColor | string;
38
+
39
+ /**
40
+ * Badge position relative to its container
41
+ * @default 'top-right'
42
+ */
43
+ position?: BadgePosition | string;
44
+
45
+ /**
46
+ * Text label inside the badge (for large badges)
47
+ * Up to 4 characters, with "+" for overflow
48
+ */
49
+ label?: string | number;
50
+
51
+ /**
52
+ * Maximum value to display (shows "{max}+" if label exceeds max)
53
+ * Usually 999+ for large numbers
54
+ */
24
55
  max?: number;
25
56
 
26
57
  /** Whether the badge should be visible */
27
58
  visible?: boolean;
28
59
 
29
- /** Whether the badge should be standalone (not attached to another element) */
30
- standalone?: boolean;
31
-
32
60
  /** Target element to which badge will be attached */
33
61
  target?: HTMLElement;
34
62
 
35
63
  /** Additional CSS classes */
36
64
  class?: string;
65
+
66
+ /** CSS class prefix */
67
+ prefix?: string;
68
+
69
+ /** Component name */
70
+ componentName?: string;
37
71
  }
38
72
 
39
73
  /**
40
74
  * Badge component interface
75
+ * Following Material Design 3 specifications
41
76
  */
42
77
  export interface BadgeComponent {
43
78
  /** Badge element */
@@ -46,11 +81,11 @@ export interface BadgeComponent {
46
81
  /** Badge wrapper element (if badge is attached to target) */
47
82
  wrapper?: HTMLElement;
48
83
 
49
- /** Sets badge text content */
50
- setContent: (content: string | number) => BadgeComponent;
84
+ /** Sets badge text label */
85
+ setLabel: (label: string | number) => BadgeComponent;
51
86
 
52
- /** Gets badge text content */
53
- getContent: () => string;
87
+ /** Gets badge text label */
88
+ getLabel: () => string;
54
89
 
55
90
  /** Shows the badge */
56
91
  show: () => BadgeComponent;
@@ -68,16 +103,13 @@ export interface BadgeComponent {
68
103
  setMax: (max: number) => BadgeComponent;
69
104
 
70
105
  /** Sets badge color */
71
- setColor: (color: keyof typeof BADGE_COLORS | BADGE_COLORS) => BadgeComponent;
106
+ setColor: (color: BadgeColor | string) => BadgeComponent;
72
107
 
73
108
  /** Sets badge variant */
74
- setVariant: (variant: keyof typeof BADGE_VARIANTS | BADGE_VARIANTS) => BadgeComponent;
75
-
76
- /** Sets badge size */
77
- setSize: (size: keyof typeof BADGE_SIZES | BADGE_SIZES) => BadgeComponent;
109
+ setVariant: (variant: BadgeVariant | string) => BadgeComponent;
78
110
 
79
111
  /** Sets badge position */
80
- setPosition: (position: keyof typeof BADGE_POSITIONS | BADGE_POSITIONS) => BadgeComponent;
112
+ setPosition: (position: BadgePosition | string) => BadgeComponent;
81
113
 
82
114
  /** Attaches badge to a target element */
83
115
  attachTo: (target: HTMLElement) => BadgeComponent;
@@ -0,0 +1,154 @@
1
+ // src/components/bottom-app-bar/bottom-app-bar.ts
2
+ /**
3
+ * @module components/bottom-app-bar
4
+ * @description Bottom app bar implementation
5
+ */
6
+
7
+ import {
8
+ createBase,
9
+ withElement,
10
+ withEvents,
11
+ withLifecycle,
12
+ ElementComponent,
13
+ BaseComponent
14
+ } from '../../core/compose';
15
+
16
+ import { createConfig } from './config';
17
+ import { BottomAppBar, BottomAppBarConfig } from './types';
18
+
19
+ /**
20
+ * Creates a bottom app bar component
21
+ *
22
+ * @param {BottomAppBarConfig} config - Configuration options
23
+ * @returns {BottomAppBar} Bottom app bar component instance
24
+ */
25
+ export const createBottomAppBar = (config: BottomAppBarConfig = {}): BottomAppBar => {
26
+ // Process configuration with defaults
27
+ const componentConfig = createConfig(config);
28
+
29
+ // Create base component
30
+ const component = createBase(componentConfig);
31
+
32
+ // Create actions container
33
+ const actionsContainer = document.createElement('div');
34
+ actionsContainer.className = `${component.getClass('bottom-app-bar')}-actions`;
35
+
36
+ // FAB container for proper positioning
37
+ const fabContainer = document.createElement('div');
38
+ fabContainer.className = `${component.getClass('bottom-app-bar')}-fab-container`;
39
+
40
+ // Apply Element enhancer
41
+ const enhancedComponent = withElement({
42
+ tag: componentConfig.tag,
43
+ componentName: 'bottom-app-bar',
44
+ className: [
45
+ componentConfig.hasFab ? `${component.getClass('bottom-app-bar')}--with-fab` : '',
46
+ componentConfig.fabPosition === 'center' ? `${component.getClass('bottom-app-bar')}--fab-center` : '',
47
+ componentConfig.class
48
+ ],
49
+ attrs: {
50
+ role: 'toolbar',
51
+ 'aria-label': 'Bottom app bar'
52
+ },
53
+ interactive: true
54
+ })(component);
55
+
56
+ // Apply events enhancer for component events
57
+ const withEventsComponent = withEvents()(enhancedComponent);
58
+
59
+ // Apply lifecycle enhancer for cleanup
60
+ const withLifecycleComponent = withLifecycle()(withEventsComponent);
61
+
62
+ // Append actions and FAB containers to the main element
63
+ withLifecycleComponent.element.appendChild(actionsContainer);
64
+ withLifecycleComponent.element.appendChild(fabContainer);
65
+
66
+ // Flag to track visibility
67
+ let isVisible = true;
68
+
69
+ // Previous scroll position for determining scroll direction
70
+ let prevScrollY = window.scrollY;
71
+
72
+ // Handle scrolling behavior if autoHide is enabled
73
+ if (componentConfig.autoHide) {
74
+ const handleScroll = () => {
75
+ const currentScrollY = window.scrollY;
76
+
77
+ // Determine scroll direction
78
+ if (currentScrollY > prevScrollY + 10) {
79
+ // Scrolling down - hide the bottom bar
80
+ if (isVisible) {
81
+ bottomBar.hide();
82
+ componentConfig.onVisibilityChange?.(false);
83
+ }
84
+ } else if (currentScrollY < prevScrollY - 10) {
85
+ // Scrolling up - show the bottom bar
86
+ if (!isVisible) {
87
+ bottomBar.show();
88
+ componentConfig.onVisibilityChange?.(true);
89
+ }
90
+ }
91
+
92
+ prevScrollY = currentScrollY;
93
+ };
94
+
95
+ // Add scroll event listener
96
+ window.addEventListener('scroll', handleScroll, { passive: true });
97
+
98
+ // Clean up event listener on destroy
99
+ const originalDestroy = withLifecycleComponent.lifecycle.destroy;
100
+ withLifecycleComponent.lifecycle.destroy = () => {
101
+ window.removeEventListener('scroll', handleScroll);
102
+ originalDestroy();
103
+ };
104
+ }
105
+
106
+ const bottomBar: BottomAppBar = {
107
+ ...withLifecycleComponent,
108
+
109
+ addAction(button: HTMLElement) {
110
+ actionsContainer.appendChild(button);
111
+ return this;
112
+ },
113
+
114
+ addFab(fab: HTMLElement) {
115
+ // Clear existing FAB if any
116
+ fabContainer.innerHTML = '';
117
+
118
+ // Add the new FAB
119
+ fabContainer.appendChild(fab);
120
+
121
+ // Update component class to indicate it has a FAB
122
+ this.element.classList.add(`${component.getClass('bottom-app-bar')}--with-fab`);
123
+
124
+ return this;
125
+ },
126
+
127
+ show() {
128
+ this.element.classList.remove(`${component.getClass('bottom-app-bar')}--hidden`);
129
+ isVisible = true;
130
+ return this;
131
+ },
132
+
133
+ hide() {
134
+ this.element.classList.add(`${component.getClass('bottom-app-bar')}--hidden`);
135
+ isVisible = false;
136
+ return this;
137
+ },
138
+
139
+ isVisible() {
140
+ return isVisible;
141
+ },
142
+
143
+ getActionsContainer() {
144
+ return actionsContainer;
145
+ }
146
+ };
147
+
148
+ // Set the appropriate styles for transitions if needed
149
+ if (componentConfig.autoHide && componentConfig.transitionDuration) {
150
+ bottomBar.element.style.transition = `transform ${componentConfig.transitionDuration}ms ease-in-out`;
151
+ }
152
+
153
+ return bottomBar;
154
+ };
@@ -0,0 +1,29 @@
1
+ // src/components/bottom-app-bar/config.ts
2
+ /**
3
+ * @module components/bottom-app-bar
4
+ * @description Configuration for bottom app bar component
5
+ */
6
+
7
+ import { createComponentConfig, BaseComponentConfig } from '../../core/config/component-config';
8
+ import { PREFIX } from '../../core/config';
9
+ import { BottomAppBarConfig } from './types';
10
+
11
+ /**
12
+ * Default configuration for bottom app bar
13
+ */
14
+ export const defaultConfig: Partial<BottomAppBarConfig> = {
15
+ tag: 'div',
16
+ hasFab: false,
17
+ fabPosition: 'end',
18
+ autoHide: false,
19
+ transitionDuration: 300
20
+ };
21
+
22
+ /**
23
+ * Creates the configuration for a bottom app bar component
24
+ *
25
+ * @param {BottomAppBarConfig} config - User provided configuration
26
+ * @returns {BottomAppBarConfig} Complete configuration with defaults applied
27
+ */
28
+ export const createConfig = (config: BottomAppBarConfig = {} as BottomAppBarConfig): BottomAppBarConfig =>
29
+ createComponentConfig(defaultConfig, config, 'bottom-app-bar') as BottomAppBarConfig;
@@ -0,0 +1,17 @@
1
+ // src/components/bottom-app-bar/index.ts
2
+ /**
3
+ * @module components/bottom-app-bar
4
+ * @description Bottom app bar component for mobile interfaces
5
+ */
6
+
7
+ import { createBottomAppBar } from './bottom-app-bar';
8
+
9
+ export default createBottomAppBar;
10
+ export { createBottomAppBar };
11
+ export type { BottomAppBarConfig, BottomAppBar } from './types';
12
+
13
+ // Export position constants for convenience and backward compatibility
14
+ export const FAB_POSITIONS = {
15
+ CENTER: 'center',
16
+ END: 'end'
17
+ } as const;
@@ -0,0 +1,114 @@
1
+ // src/components/bottom-app-bar/types.ts
2
+ /**
3
+ * @module components/bottom-app-bar
4
+ * @description Type definitions for Bottom App Bar component
5
+ */
6
+
7
+ import { ElementComponent } from '../../core/compose';
8
+
9
+ /**
10
+ * FAB position type for Bottom App Bar
11
+ * @category Components
12
+ */
13
+ export type FabPosition = 'center' | 'end';
14
+
15
+ /**
16
+ * Configuration options for Bottom App Bar component
17
+ * @category Components
18
+ */
19
+ export interface BottomAppBarConfig {
20
+ /**
21
+ * Element to use for the container
22
+ * @default 'div'
23
+ */
24
+ tag?: string;
25
+
26
+ /**
27
+ * Whether to show FAB in the bottom bar
28
+ * @default false
29
+ */
30
+ hasFab?: boolean;
31
+
32
+ /**
33
+ * FAB position in bottom bar
34
+ * @default 'end'
35
+ */
36
+ fabPosition?: FabPosition;
37
+
38
+ /**
39
+ * Additional CSS classes to apply
40
+ */
41
+ class?: string;
42
+
43
+ /**
44
+ * Whether to enable auto-hide on scroll
45
+ * @default false
46
+ */
47
+ autoHide?: boolean;
48
+
49
+ /**
50
+ * Transition duration for show/hide in ms
51
+ * @default 300
52
+ */
53
+ transitionDuration?: number;
54
+
55
+ /**
56
+ * Optional callback when scrolling shows/hides the bar
57
+ */
58
+ onVisibilityChange?: (visible: boolean) => void;
59
+
60
+ /**
61
+ * Component prefix for class names
62
+ * @default 'mtrl'
63
+ */
64
+ prefix?: string;
65
+
66
+ /**
67
+ * Component name for class generation
68
+ */
69
+ componentName?: string;
70
+ }
71
+
72
+ /**
73
+ * Bottom App Bar component interface
74
+ * @category Components
75
+ */
76
+ export interface BottomAppBar extends ElementComponent {
77
+ /**
78
+ * Adds an action button to the bottom bar
79
+ * @param {HTMLElement} button - Button element to add
80
+ * @returns {BottomAppBar} BottomAppBar instance for chaining
81
+ */
82
+ addAction: (button: HTMLElement) => BottomAppBar;
83
+
84
+ /**
85
+ * Adds a floating action button to the bottom bar
86
+ * @param {HTMLElement} fab - FAB element to add
87
+ * @returns {BottomAppBar} BottomAppBar instance for chaining
88
+ */
89
+ addFab: (fab: HTMLElement) => BottomAppBar;
90
+
91
+ /**
92
+ * Shows the bottom bar
93
+ * @returns {BottomAppBar} BottomAppBar instance for chaining
94
+ */
95
+ show: () => BottomAppBar;
96
+
97
+ /**
98
+ * Hides the bottom bar
99
+ * @returns {BottomAppBar} BottomAppBar instance for chaining
100
+ */
101
+ hide: () => BottomAppBar;
102
+
103
+ /**
104
+ * Checks if the bottom bar is visible
105
+ * @returns {boolean} Whether the bottom bar is visible
106
+ */
107
+ isVisible: () => boolean;
108
+
109
+ /**
110
+ * Get the actions container element
111
+ * @returns {HTMLElement} Actions container element
112
+ */
113
+ getActionsContainer: () => HTMLElement;
114
+ }
@@ -73,6 +73,11 @@ export const withAPI = ({ disabled, lifecycle }: ApiOptions) =>
73
73
  getIcon() {
74
74
  return component.icon.getIcon();
75
75
  },
76
+
77
+ setAriaLabel(label: string) {
78
+ component.element.setAttribute('aria-label', label);
79
+ return this;
80
+ },
76
81
 
77
82
  destroy() {
78
83
  lifecycle.destroy();
@@ -13,7 +13,6 @@ import {
13
13
  } from '../../core/compose/features';
14
14
  import { withAPI } from './api';
15
15
  import { ButtonConfig } from './types';
16
- import { BUTTON_VARIANTS } from './constants';
17
16
  import { createBaseConfig, getElementConfig, getApiConfig } from './config';
18
17
 
19
18
  /**
@@ -5,13 +5,12 @@ import {
5
5
  BaseComponentConfig
6
6
  } from '../../core/config/component-config';
7
7
  import { ButtonConfig } from './types';
8
- import { BUTTON_VARIANTS } from './constants';
9
8
 
10
9
  /**
11
10
  * Default configuration for the Button component
12
11
  */
13
12
  export const defaultConfig: ButtonConfig = {
14
- variant: BUTTON_VARIANTS.FILLED,
13
+ variant: 'filled',
15
14
  type: 'button'
16
15
  // Don't set disabled: false as default - it should be undefined by default
17
16
  };
@@ -45,6 +44,11 @@ export const getElementConfig = (config: ButtonConfig) => {
45
44
  attrs.value = config.value;
46
45
  }
47
46
 
47
+ // Add aria-label attribute for accessibility
48
+ if (config.ariaLabel) {
49
+ attrs['aria-label'] = config.ariaLabel;
50
+ }
51
+
48
52
  return createElementConfig(config, {
49
53
  tag: 'button',
50
54
  attrs,