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,5 +1,23 @@
1
1
  // src/components/list/utils.ts
2
- import { LIST_CLASSES } from './constants';
2
+
3
+ // List element class names as strings for internal use
4
+ const LIST_CLASS_NAMES = {
5
+ ROOT: 'list',
6
+ GROUP: 'list-group',
7
+ GROUP_TITLE: 'list-group-title',
8
+ DIVIDER: 'list-divider',
9
+ SECTION: 'list-section',
10
+ SECTION_TITLE: 'list-section-title',
11
+ ITEM: 'list-item',
12
+ ITEM_CONTENT: 'list-item-content',
13
+ ITEM_LEADING: 'list-item-leading',
14
+ ITEM_TEXT: 'list-item-text',
15
+ ITEM_OVERLINE: 'list-item-overline',
16
+ ITEM_HEADLINE: 'list-item-headline',
17
+ ITEM_SUPPORTING: 'list-item-supporting',
18
+ ITEM_META: 'list-item-meta',
19
+ ITEM_TRAILING: 'list-item-trailing'
20
+ };
3
21
 
4
22
  /**
5
23
  * Creates a divider element
@@ -8,7 +26,7 @@ import { LIST_CLASSES } from './constants';
8
26
  */
9
27
  export const createDivider = (prefix: string): HTMLElement => {
10
28
  const divider = document.createElement('div');
11
- divider.className = `${prefix}-${LIST_CLASSES.DIVIDER}`;
29
+ divider.className = `${prefix}-${LIST_CLASS_NAMES.DIVIDER}`;
12
30
  divider.setAttribute('role', 'separator');
13
31
  return divider;
14
32
  };
@@ -21,7 +39,7 @@ export const createDivider = (prefix: string): HTMLElement => {
21
39
  */
22
40
  export const createSectionTitle = (title: string, prefix: string): HTMLElement => {
23
41
  const titleEl = document.createElement('div');
24
- titleEl.className = `${prefix}-${LIST_CLASSES.SECTION_TITLE}`;
42
+ titleEl.className = `${prefix}-${LIST_CLASS_NAMES.SECTION_TITLE}`;
25
43
  titleEl.textContent = title;
26
44
  return titleEl;
27
45
  };
@@ -44,4 +62,13 @@ export const createElement = (tag: string, className: string, content?: string |
44
62
  }
45
63
  }
46
64
  return element;
65
+ };
66
+
67
+ /**
68
+ * Gets the class name for a list element
69
+ * @param {string} element - Element name
70
+ * @returns {string} The class name string
71
+ */
72
+ export const getListClass = (element: keyof typeof LIST_CLASS_NAMES): string => {
73
+ return LIST_CLASS_NAMES[element];
47
74
  };
@@ -1,6 +1,6 @@
1
1
  // src/components/menu/features/items-manager.ts
2
2
  import { createMenuItem } from '../menu-item';
3
- import { MENU_EVENTS } from '../constants';
3
+ import { MENU_EVENT } from '../utils';
4
4
  import { BaseComponent, MenuConfig, MenuItemConfig, MenuItemData } from '../types';
5
5
 
6
6
  interface SubmenuMap {
@@ -63,8 +63,8 @@ export const withItemsManager = (config: MenuConfig) => (component: BaseComponen
63
63
  });
64
64
 
65
65
  // Handle submenu selection
66
- submenu.on?.(MENU_EVENTS.SELECT, (detail: any) => {
67
- component.emit?.(MENU_EVENTS.SELECT, {
66
+ submenu.on?.(MENU_EVENT.SELECT, (detail: any) => {
67
+ component.emit?.(MENU_EVENT.SELECT, {
68
68
  name: `${name}:${detail.name}`,
69
69
  text: detail.text,
70
70
  path: [name, detail.name]
@@ -116,7 +116,7 @@ export const withItemsManager = (config: MenuConfig) => (component: BaseComponen
116
116
  });
117
117
 
118
118
  // Emit submenu open event
119
- component.emit?.(MENU_EVENTS.SUBMENU_OPEN, { name });
119
+ component.emit?.(MENU_EVENT.SUBMENU_OPEN, { name });
120
120
  }
121
121
  };
122
122
 
@@ -143,7 +143,7 @@ export const withItemsManager = (config: MenuConfig) => (component: BaseComponen
143
143
  activeSubmenu = null;
144
144
 
145
145
  // Emit submenu close event
146
- component.emit?.(MENU_EVENTS.SUBMENU_CLOSE, { name });
146
+ component.emit?.(MENU_EVENT.SUBMENU_CLOSE, { name });
147
147
  };
148
148
 
149
149
  /**
@@ -160,7 +160,7 @@ export const withItemsManager = (config: MenuConfig) => (component: BaseComponen
160
160
 
161
161
  // Cancel any pending close timer for this item
162
162
  if (closeTimers.has(name)) {
163
- window.clearTimeout(closeTimers.get(name));
163
+ window.clearTimeout(closeTimers.get(name)!);
164
164
  closeTimers.delete(name);
165
165
  }
166
166
 
@@ -194,7 +194,7 @@ export const withItemsManager = (config: MenuConfig) => (component: BaseComponen
194
194
  if (submenu && submenu.element) {
195
195
  // Cancel any existing close timer for this item
196
196
  if (closeTimers.has(name)) {
197
- window.clearTimeout(closeTimers.get(name));
197
+ window.clearTimeout(closeTimers.get(name)!);
198
198
  }
199
199
 
200
200
  // Set a new close timer
@@ -254,7 +254,7 @@ export const withItemsManager = (config: MenuConfig) => (component: BaseComponen
254
254
  // For regular items, emit select event
255
255
  const name = item.getAttribute('data-name');
256
256
  if (name) {
257
- component.emit?.(MENU_EVENTS.SELECT, { name, text: item.textContent });
257
+ component.emit?.(MENU_EVENT.SELECT, { name, text: item.textContent });
258
258
  // Hide menu after selection unless configured otherwise
259
259
  if (!config.stayOpenOnSelect) {
260
260
  component.hide?.();
@@ -430,7 +430,7 @@ export const withItemsManager = (config: MenuConfig) => (component: BaseComponen
430
430
 
431
431
  /**
432
432
  * Gets all registered items
433
- * @returns {Map<string, MenuItemConfig>} Map of item names to configurations
433
+ * @returns {Map<string, MenuItemData>} Map of item names to data
434
434
  */
435
435
  getItems(): Map<string, MenuItemData> {
436
436
  const result = new Map<string, MenuItemData>();
@@ -1,6 +1,6 @@
1
1
  // src/components/menu/features/positioning.ts
2
2
  import { BaseComponent, MenuPositionConfig, MenuPosition } from '../types';
3
- import { MENU_ALIGN, MENU_VERTICAL_ALIGN } from '../constants';
3
+ import { MENU_ALIGNMENT, MENU_VERTICAL_ALIGNMENT } from '../utils';
4
4
 
5
5
  /**
6
6
  * Positions a menu element relative to a target element
@@ -34,8 +34,8 @@ export const positionMenu = (
34
34
  menuElement.style.opacity = originalOpacity;
35
35
 
36
36
  const {
37
- align = MENU_ALIGN.LEFT,
38
- vAlign = MENU_VERTICAL_ALIGN.BOTTOM,
37
+ align = MENU_ALIGNMENT.LEFT,
38
+ vAlign = MENU_VERTICAL_ALIGNMENT.BOTTOM,
39
39
  offsetX = 0,
40
40
  offsetY = 0
41
41
  } = options;
@@ -44,16 +44,16 @@ export const positionMenu = (
44
44
  let top = targetRect.bottom + offsetY;
45
45
 
46
46
  // Handle horizontal alignment
47
- if (align === MENU_ALIGN.RIGHT) {
47
+ if (align === MENU_ALIGNMENT.RIGHT) {
48
48
  left = targetRect.right - menuRect.width + offsetX;
49
- } else if (align === MENU_ALIGN.CENTER) {
49
+ } else if (align === MENU_ALIGNMENT.CENTER) {
50
50
  left = targetRect.left + (targetRect.width - menuRect.width) / 2 + offsetX;
51
51
  }
52
52
 
53
53
  // Handle vertical alignment
54
- if (vAlign === MENU_VERTICAL_ALIGN.TOP) {
54
+ if (vAlign === MENU_VERTICAL_ALIGNMENT.TOP) {
55
55
  top = targetRect.top - menuRect.height + offsetY;
56
- } else if (vAlign === MENU_VERTICAL_ALIGN.MIDDLE) {
56
+ } else if (vAlign === MENU_VERTICAL_ALIGNMENT.MIDDLE) {
57
57
  top = targetRect.top + (targetRect.height - menuRect.height) / 2 + offsetY;
58
58
  }
59
59
 
@@ -1,6 +1,6 @@
1
1
  // src/components/menu/features/visibility.ts
2
2
  import { BaseComponent, MenuConfig } from '../types';
3
- import { MENU_EVENTS } from '../constants';
3
+ import { MENU_EVENT, MENU_CLASSES } from '../utils';
4
4
 
5
5
  /**
6
6
  * Adds visibility management functionality to a menu component
@@ -56,11 +56,11 @@ export const withVisibility = (config: MenuConfig) => (component: BaseComponent)
56
56
  // Force a reflow before adding the visible class for animation
57
57
  // eslint-disable-next-line no-void
58
58
  void component.element.offsetHeight;
59
- component.element.classList.add(`${prefix}-menu--visible`);
59
+ component.element.classList.add(`${prefix}-${MENU_CLASSES.VISIBLE}`);
60
60
  component.element.setAttribute('aria-hidden', 'false');
61
61
 
62
62
  // Emit open event
63
- component.emit?.(MENU_EVENTS.OPEN, {});
63
+ component.emit?.(MENU_EVENT.OPEN, {});
64
64
 
65
65
  return this;
66
66
  },
@@ -92,7 +92,7 @@ export const withVisibility = (config: MenuConfig) => (component: BaseComponent)
92
92
  }
93
93
 
94
94
  // Hide the menu with visual indication first
95
- component.element.classList.remove(`${prefix}-menu--visible`);
95
+ component.element.classList.remove(`${prefix}-${MENU_CLASSES.VISIBLE}`);
96
96
  component.element.setAttribute('aria-hidden', 'true');
97
97
 
98
98
  // Define a reliable cleanup function
@@ -123,7 +123,7 @@ export const withVisibility = (config: MenuConfig) => (component: BaseComponent)
123
123
  setTimeout(cleanupElement, 300);
124
124
 
125
125
  // Emit close event
126
- component.emit?.(MENU_EVENTS.CLOSE, {});
126
+ component.emit?.(MENU_EVENT.CLOSE, {});
127
127
 
128
128
  return this;
129
129
  },
@@ -145,7 +145,7 @@ export const withVisibility = (config: MenuConfig) => (component: BaseComponent)
145
145
  if (!isVisible) return;
146
146
 
147
147
  // Store the opening button if available
148
- const openingButton = config.openingButton?.element;
148
+ const origin = config.origin?.element;
149
149
 
150
150
  // Check if click is outside the menu but not on the opening button
151
151
  const clickedElement = event.target as Node;
@@ -156,7 +156,7 @@ export const withVisibility = (config: MenuConfig) => (component: BaseComponent)
156
156
  }
157
157
 
158
158
  // Don't close if the click is on the opening button (it will handle opening/closing)
159
- if (openingButton && (openingButton === clickedElement || openingButton.contains(clickedElement))) {
159
+ if (origin && (origin === clickedElement || origin.contains(clickedElement))) {
160
160
  return;
161
161
  }
162
162
 
@@ -1,14 +1,12 @@
1
1
  // src/components/menu/index.ts
2
- export { default } from './menu'
3
- export {
4
- MENU_ALIGN,
5
- MENU_VERTICAL_ALIGN,
6
- MENU_ITEM_TYPES,
7
- MENU_EVENTS
8
- } from './constants'
2
+ export { default } from './menu';
9
3
  export {
10
4
  MenuConfig,
11
5
  MenuComponent,
12
6
  MenuItemConfig,
13
- MenuPositionConfig
14
- } from './types'
7
+ MenuPositionConfig,
8
+ MenuAlign,
9
+ MenuVerticalAlign,
10
+ MenuItemType,
11
+ MenuEvent
12
+ } from './types';
@@ -1,6 +1,6 @@
1
1
  // src/components/menu/menu-item.ts
2
2
  import { MenuItemConfig } from './types';
3
- import { MENU_ITEM_TYPES } from './constants';
3
+ import { MENU_ITEM_TYPE, getMenuClass } from './utils';
4
4
 
5
5
  /**
6
6
  * Creates a menu item element
@@ -10,10 +10,10 @@ import { MENU_ITEM_TYPES } from './constants';
10
10
  */
11
11
  export const createMenuItem = (itemConfig: MenuItemConfig, prefix: string): HTMLElement => {
12
12
  const item = document.createElement('li');
13
- item.className = `${prefix}-menu-item`;
13
+ item.className = `${prefix}-${getMenuClass('ITEM')}`;
14
14
 
15
- if (itemConfig.type === MENU_ITEM_TYPES.DIVIDER) {
16
- item.className = `${prefix}-menu-divider`;
15
+ if (itemConfig.type === MENU_ITEM_TYPE.DIVIDER) {
16
+ item.className = `${prefix}-${getMenuClass('DIVIDER')}`;
17
17
  return item;
18
18
  }
19
19
 
@@ -23,7 +23,7 @@ export const createMenuItem = (itemConfig: MenuItemConfig, prefix: string): HTML
23
23
 
24
24
  if (itemConfig.disabled) {
25
25
  item.setAttribute('aria-disabled', 'true');
26
- item.className += ` ${prefix}-menu-item--disabled`;
26
+ item.className += ` ${prefix}-${getMenuClass('ITEM')}--disabled`;
27
27
  }
28
28
 
29
29
  if (itemConfig.name) {
@@ -33,7 +33,7 @@ export const createMenuItem = (itemConfig: MenuItemConfig, prefix: string): HTML
33
33
  item.textContent = itemConfig.text || '';
34
34
 
35
35
  if (itemConfig.items?.length) {
36
- item.className += ` ${prefix}-menu-item--submenu`;
36
+ item.className += ` ${prefix}-${getMenuClass('ITEM')}--submenu`;
37
37
  item.setAttribute('aria-haspopup', 'true');
38
38
  item.setAttribute('aria-expanded', 'false');
39
39
  // We don't need to add a submenu indicator as it's handled by CSS ::after
@@ -18,6 +18,28 @@ import {
18
18
  * Creates a new Menu component
19
19
  * @param {MenuConfig} config - Menu configuration
20
20
  * @returns {MenuComponent} Menu component instance
21
+ *
22
+ * @example
23
+ * ```typescript
24
+ * // Create a basic menu with items
25
+ * const menu = createMenu({
26
+ * items: [
27
+ * { name: 'item1', text: 'Option 1' },
28
+ * { name: 'item2', text: 'Option 2' },
29
+ * { type: 'divider' },
30
+ * { name: 'item3', text: 'Option 3' }
31
+ * ]
32
+ * });
33
+ *
34
+ * // Show the menu positioned relative to a button
35
+ * const button = document.getElementById('menuButton');
36
+ * menu.position(button).show();
37
+ *
38
+ * // Listen for item selection
39
+ * menu.on('select', (event) => {
40
+ * console.log(`Selected: ${event.name}`);
41
+ * });
42
+ * ```
21
43
  */
22
44
  const createMenu = (config: MenuConfig = {}): MenuComponent => {
23
45
  const baseConfig = createBaseConfig(config);
@@ -1,9 +1,28 @@
1
1
  // src/components/menu/types.ts
2
- import {
3
- MENU_ALIGN,
4
- MENU_VERTICAL_ALIGN,
5
- MENU_ITEM_TYPES
6
- } from './constants';
2
+
3
+ /**
4
+ * Menu alignment options
5
+ * @category Components
6
+ */
7
+ export type MenuAlign = 'left' | 'right' | 'center';
8
+
9
+ /**
10
+ * Menu vertical alignment options
11
+ * @category Components
12
+ */
13
+ export type MenuVerticalAlign = 'top' | 'bottom' | 'middle';
14
+
15
+ /**
16
+ * Menu item types
17
+ * @category Components
18
+ */
19
+ export type MenuItemType = 'item' | 'divider';
20
+
21
+ /**
22
+ * Menu events
23
+ * @category Components
24
+ */
25
+ export type MenuEvent = 'select' | 'open' | 'close' | 'submenuOpen' | 'submenuClose';
7
26
 
8
27
  /**
9
28
  * Menu item configuration
@@ -16,7 +35,7 @@ export interface MenuItemConfig {
16
35
  text: string;
17
36
 
18
37
  /** Type of menu item */
19
- type?: keyof typeof MENU_ITEM_TYPES | string;
38
+ type?: MenuItemType | string;
20
39
 
21
40
  /** Whether the item is disabled */
22
41
  disabled?: boolean;
@@ -33,10 +52,10 @@ export interface MenuItemConfig {
33
52
  */
34
53
  export interface MenuPositionConfig {
35
54
  /** Horizontal alignment */
36
- align?: keyof typeof MENU_ALIGN | string;
55
+ align?: MenuAlign | string;
37
56
 
38
57
  /** Vertical alignment */
39
- vAlign?: keyof typeof MENU_VERTICAL_ALIGN | string;
58
+ vAlign?: MenuVerticalAlign | string;
40
59
 
41
60
  /** Horizontal offset in pixels */
42
61
  offsetX?: number;
@@ -94,8 +113,8 @@ export interface MenuConfig {
94
113
  /** Whether to keep menu open after selection */
95
114
  stayOpenOnSelect?: boolean;
96
115
 
97
- /** Button element that opens the menu */
98
- openingButton?: HTMLElement | { element: HTMLElement };
116
+ /** Orgin element that opens the menu */
117
+ origin?: HTMLElement | { element: HTMLElement };
99
118
 
100
119
  /** Parent item element (for submenus) */
101
120
  parentItem?: HTMLElement;
@@ -0,0 +1,67 @@
1
+ // src/components/menu/utils.ts
2
+
3
+ /**
4
+ * Menu alignment constants for internal use
5
+ * @internal
6
+ */
7
+ export const MENU_ALIGNMENT = {
8
+ LEFT: 'left',
9
+ RIGHT: 'right',
10
+ CENTER: 'center'
11
+ };
12
+
13
+ /**
14
+ * Menu vertical alignment constants for internal use
15
+ * @internal
16
+ */
17
+ export const MENU_VERTICAL_ALIGNMENT = {
18
+ TOP: 'top',
19
+ BOTTOM: 'bottom',
20
+ MIDDLE: 'middle'
21
+ };
22
+
23
+ /**
24
+ * Menu item types for internal use
25
+ * @internal
26
+ */
27
+ export const MENU_ITEM_TYPE = {
28
+ ITEM: 'item',
29
+ DIVIDER: 'divider'
30
+ };
31
+
32
+ /**
33
+ * Menu events for internal use
34
+ * @internal
35
+ */
36
+ export const MENU_EVENT = {
37
+ SELECT: 'select',
38
+ OPEN: 'open',
39
+ CLOSE: 'close',
40
+ SUBMENU_OPEN: 'submenuOpen',
41
+ SUBMENU_CLOSE: 'submenuClose'
42
+ };
43
+
44
+ /**
45
+ * Menu CSS classes for internal use
46
+ * @internal
47
+ */
48
+ export const MENU_CLASSES = {
49
+ ROOT: 'menu',
50
+ ITEM: 'menu-item',
51
+ ITEM_CONTAINER: 'menu-item-container',
52
+ LIST: 'menu-list',
53
+ DIVIDER: 'menu-divider',
54
+ SUBMENU: 'menu--submenu',
55
+ VISIBLE: 'menu--visible',
56
+ DISABLED: 'menu--disabled'
57
+ };
58
+
59
+ /**
60
+ * Gets a class name for menu elements
61
+ * @param {string} element - Element name from MENU_CLASSES
62
+ * @returns {string} The class name
63
+ * @internal
64
+ */
65
+ export const getMenuClass = (element: keyof typeof MENU_CLASSES): string => {
66
+ return MENU_CLASSES[element];
67
+ };
@@ -1,79 +1,107 @@
1
- // src/components/navigation/api.ts
2
- import {
3
- BaseComponent,
4
- NavigationComponent,
5
- ApiOptions,
6
- NavItemConfig,
7
- NavItemData
8
- } from './types';
1
+ // src/components/button/api.ts
2
+ import { ButtonComponent } from './types';
3
+
4
+ interface ApiOptions {
5
+ disabled: {
6
+ enable: () => void;
7
+ disable: () => void;
8
+ };
9
+ lifecycle: {
10
+ destroy: () => void;
11
+ };
12
+ }
13
+
14
+ interface ComponentWithElements {
15
+ element: HTMLElement;
16
+ text: {
17
+ setText: (content: string) => any;
18
+ getText: () => string;
19
+ getElement: () => HTMLElement | null;
20
+ };
21
+ icon: {
22
+ setIcon: (html: string) => any;
23
+ getIcon: () => string;
24
+ getElement: () => HTMLElement | null;
25
+ };
26
+ getClass: (name: string) => string;
27
+ }
9
28
 
10
29
  /**
11
- * Enhances navigation component with API methods
12
- * @param {ApiOptions} options - API configuration
30
+ * Enhances a button component with API methods
31
+ * @param {ApiOptions} options - API configuration options
13
32
  * @returns {Function} Higher-order function that adds API methods to component
33
+ * @internal This is an internal utility for the Button component
14
34
  */
15
35
  export const withAPI = ({ disabled, lifecycle }: ApiOptions) =>
16
- (component: BaseComponent): NavigationComponent => ({
36
+ (component: ComponentWithElements): ButtonComponent => ({
17
37
  ...component as any,
18
- element: component.element,
19
- items: component.items as Map<string, NavItemData>,
20
-
21
- // Item management
22
- addItem(config: NavItemConfig): NavigationComponent {
23
- component.addItem?.(config);
24
- return this;
25
- },
38
+ element: component.element as HTMLButtonElement,
39
+
40
+ getValue: () => component.element.value,
26
41
 
27
- removeItem(id: string): NavigationComponent {
28
- component.removeItem?.(id);
42
+ setValue(value: string) {
43
+ component.element.value = value;
29
44
  return this;
30
45
  },
31
46
 
32
- getItem(id: string): NavItemData | undefined {
33
- return component.getItem?.(id);
47
+ enable() {
48
+ disabled.enable();
49
+ return this;
34
50
  },
35
51
 
36
- getAllItems(): NavItemData[] {
37
- return component.getAllItems?.() || [];
52
+ disable() {
53
+ disabled.disable();
54
+ return this;
38
55
  },
39
56
 
40
- getItemPath(id: string): string[] {
41
- return component.getItemPath?.(id) || [];
42
- },
43
-
44
- // Active state management
45
- setActive(id: string): NavigationComponent {
46
- component.setActive?.(id);
57
+ setText(content: string) {
58
+ component.text.setText(content);
59
+ this.updateCircularStyle();
60
+
61
+ // If removing text from a button with an icon, ensure it has an accessible name
62
+ if (!content && component.icon.getElement()) {
63
+ if (!this.element.getAttribute('aria-label')) {
64
+ const className = this.element.className.split(' ')
65
+ .find(cls => !cls.startsWith(`${component.getClass('button')}`));
66
+
67
+ if (className) {
68
+ this.element.setAttribute('aria-label', className);
69
+ }
70
+ }
71
+ }
72
+
47
73
  return this;
48
74
  },
49
75
 
50
- getActive(): NavItemData | null {
51
- return component.getActive?.() || null;
52
- },
53
-
54
- // Event handling
55
- on(event: string, handler: Function): NavigationComponent {
56
- component.on?.(event, handler);
57
- return this;
76
+ getText() {
77
+ return component.text.getText();
58
78
  },
59
79
 
60
- off(event: string, handler: Function): NavigationComponent {
61
- component.off?.(event, handler);
80
+ setIcon(icon: string) {
81
+ component.icon.setIcon(icon);
82
+ this.updateCircularStyle();
62
83
  return this;
63
84
  },
64
-
65
- // State management
66
- enable(): NavigationComponent {
67
- disabled.enable();
68
- return this;
85
+
86
+ getIcon() {
87
+ return component.icon.getIcon();
69
88
  },
70
89
 
71
- disable(): NavigationComponent {
72
- disabled.disable();
90
+ setAriaLabel(label: string) {
91
+ component.element.setAttribute('aria-label', label);
73
92
  return this;
74
93
  },
75
94
 
76
- destroy(): void {
95
+ destroy() {
77
96
  lifecycle.destroy();
97
+ },
98
+
99
+ updateCircularStyle() {
100
+ const hasText = component.text.getText();
101
+ if (!hasText && component.icon.getElement()) {
102
+ component.element.classList.add(`${component.getClass('button')}--circular`);
103
+ } else {
104
+ component.element.classList.remove(`${component.getClass('button')}--circular`);
105
+ }
78
106
  }
79
107
  });
@@ -5,15 +5,14 @@ import {
5
5
  BaseComponentConfig
6
6
  } from '../../core/config/component-config';
7
7
  import { NavigationConfig, BaseComponent, ApiOptions } from './types';
8
- import { NAV_VARIANTS, NAV_POSITIONS, NAV_BEHAVIORS } from './constants';
9
8
 
10
9
  /**
11
10
  * Default configuration for the Navigation component
12
11
  */
13
12
  export const defaultConfig: NavigationConfig = {
14
- variant: NAV_VARIANTS.RAIL,
15
- position: NAV_POSITIONS.LEFT,
16
- behavior: NAV_BEHAVIORS.FIXED,
13
+ variant: 'rail',
14
+ position: 'left',
15
+ behavior: 'fixed',
17
16
  items: [],
18
17
  showLabels: true,
19
18
  scrimEnabled: false
@@ -32,16 +31,29 @@ export const createBaseConfig = (config: NavigationConfig = {}): NavigationConfi
32
31
  * @param {NavigationConfig} config - Navigation configuration
33
32
  * @returns {Object} Element configuration object for withElement
34
33
  */
35
- export const getElementConfig = (config: NavigationConfig) =>
36
- createElementConfig(config, {
34
+ export const getElementConfig = (config: NavigationConfig) => {
35
+ // Build class list - start with the variant class
36
+ const variantClass = config.variant ? `${config.prefix}-nav--${config.variant}` : '';
37
+
38
+ // Add position class if specified
39
+ const positionClass = config.position ? `${config.prefix}-nav--${config.position}` : '';
40
+
41
+ // Add user-provided classes
42
+ const userClass = config.class || '';
43
+
44
+ // Combine all classes
45
+ const classNames = [variantClass, positionClass, userClass].filter(Boolean);
46
+
47
+ return createElementConfig(config, {
37
48
  tag: 'nav',
38
49
  componentName: 'nav',
39
50
  attrs: {
40
51
  role: 'navigation',
41
52
  'aria-label': config.ariaLabel || 'Main Navigation'
42
53
  },
43
- className: config.class
54
+ className: classNames
44
55
  });
56
+ };
45
57
 
46
58
  /**
47
59
  * Creates API configuration for the Navigation component
@@ -50,11 +62,11 @@ export const getElementConfig = (config: NavigationConfig) =>
50
62
  */
51
63
  export const getApiConfig = (comp: BaseComponent): ApiOptions => ({
52
64
  disabled: {
53
- enable: comp.disabled?.enable,
54
- disable: comp.disabled?.disable
65
+ enable: comp.disabled?.enable || (() => {}),
66
+ disable: comp.disabled?.disable || (() => {})
55
67
  },
56
68
  lifecycle: {
57
- destroy: comp.lifecycle?.destroy
69
+ destroy: comp.lifecycle?.destroy || (() => {})
58
70
  }
59
71
  });
60
72