mtrl 0.2.7 → 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 (164) 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/package.json +14 -3
  6. package/server.ts +86 -0
  7. package/src/components/badge/api.ts +23 -14
  8. package/src/components/badge/badge.ts +2 -2
  9. package/src/components/badge/config.ts +10 -11
  10. package/src/components/badge/features.ts +15 -10
  11. package/src/components/badge/index.ts +27 -2
  12. package/src/components/badge/types.ts +28 -8
  13. package/src/components/bottom-app-bar/bottom-app-bar.ts +2 -44
  14. package/src/components/bottom-app-bar/config.ts +1 -45
  15. package/src/components/bottom-app-bar/index.ts +7 -1
  16. package/src/components/bottom-app-bar/types.ts +7 -1
  17. package/src/components/button/button.ts +0 -1
  18. package/src/components/button/config.ts +1 -2
  19. package/src/components/button/index.ts +10 -2
  20. package/src/components/button/types.ts +14 -2
  21. package/src/components/card/config.ts +17 -9
  22. package/src/components/card/content.ts +8 -10
  23. package/src/components/card/features.ts +4 -6
  24. package/src/components/card/index.ts +29 -2
  25. package/src/components/card/types.ts +6 -23
  26. package/src/components/checkbox/config.ts +3 -4
  27. package/src/components/checkbox/index.ts +1 -2
  28. package/src/components/checkbox/types.ts +12 -3
  29. package/src/components/chip/api.ts +170 -221
  30. package/src/components/chip/chip.ts +34 -302
  31. package/src/components/chip/config.ts +1 -2
  32. package/src/components/chip/index.ts +10 -2
  33. package/src/components/chip/types.ts +224 -35
  34. package/src/components/datepicker/api.ts +18 -25
  35. package/src/components/datepicker/config.ts +9 -12
  36. package/src/components/datepicker/datepicker.ts +7 -12
  37. package/src/components/datepicker/index.ts +10 -7
  38. package/src/components/datepicker/render.ts +16 -18
  39. package/src/components/datepicker/types.ts +164 -35
  40. package/src/components/datepicker/utils.ts +1 -2
  41. package/src/components/dialog/api.ts +7 -8
  42. package/src/components/dialog/config.ts +3 -4
  43. package/src/components/dialog/features.ts +56 -22
  44. package/src/components/dialog/index.ts +38 -8
  45. package/src/components/dialog/types.ts +33 -10
  46. package/src/components/divider/index.ts +5 -1
  47. package/src/components/extended-fab/config.ts +6 -2
  48. package/src/components/extended-fab/index.ts +7 -2
  49. package/src/components/extended-fab/types.ts +21 -4
  50. package/src/components/fab/config.ts +3 -4
  51. package/src/components/fab/fab.ts +1 -1
  52. package/src/components/fab/index.ts +7 -2
  53. package/src/components/fab/types.ts +21 -4
  54. package/src/components/list/config.ts +4 -5
  55. package/src/components/list/features.ts +6 -7
  56. package/src/components/list/index.ts +7 -9
  57. package/src/components/list/list-item.ts +12 -13
  58. package/src/components/list/types.ts +50 -5
  59. package/src/components/list/utils.ts +30 -3
  60. package/src/components/menu/features/items-manager.ts +9 -9
  61. package/src/components/menu/features/positioning.ts +7 -7
  62. package/src/components/menu/features/visibility.ts +7 -7
  63. package/src/components/menu/index.ts +7 -9
  64. package/src/components/menu/menu-item.ts +6 -6
  65. package/src/components/menu/menu.ts +22 -0
  66. package/src/components/menu/types.ts +29 -10
  67. package/src/components/menu/utils.ts +67 -0
  68. package/src/components/navigation/config.ts +22 -10
  69. package/src/components/navigation/features/items.ts +31 -27
  70. package/src/components/navigation/index.ts +0 -6
  71. package/src/components/navigation/nav-item.ts +12 -24
  72. package/src/components/navigation/navigation.ts +4 -6
  73. package/src/components/navigation/types.ts +228 -203
  74. package/src/components/progress/api.ts +2 -3
  75. package/src/components/progress/config.ts +2 -3
  76. package/src/components/progress/index.ts +0 -1
  77. package/src/components/progress/progress.ts +1 -2
  78. package/src/components/progress/types.ts +186 -33
  79. package/src/components/radios/config.ts +1 -1
  80. package/src/components/radios/index.ts +0 -1
  81. package/src/components/radios/types.ts +0 -7
  82. package/src/components/search/config.ts +1 -2
  83. package/src/components/search/features/search.ts +14 -15
  84. package/src/components/search/features/states.ts +5 -1
  85. package/src/components/search/features/structure.ts +3 -4
  86. package/src/components/search/index.ts +0 -3
  87. package/src/components/search/types.ts +18 -6
  88. package/src/components/segmented-button/config.ts +20 -7
  89. package/src/components/segmented-button/segment.ts +6 -7
  90. package/src/components/segmented-button/segmented-button.ts +4 -5
  91. package/src/components/segmented-button/types.ts +37 -2
  92. package/src/components/slider/types.ts +34 -8
  93. package/src/components/snackbar/config.ts +2 -3
  94. package/src/components/snackbar/constants.ts +0 -32
  95. package/src/components/snackbar/index.ts +0 -1
  96. package/src/components/snackbar/position.ts +9 -1
  97. package/src/components/snackbar/types.ts +122 -46
  98. package/src/components/switch/config.ts +2 -3
  99. package/src/components/switch/index.ts +0 -1
  100. package/src/components/switch/types.ts +3 -2
  101. package/src/components/tabs/config.ts +3 -4
  102. package/src/components/tabs/index.ts +0 -15
  103. package/src/components/tabs/tab-api.ts +12 -4
  104. package/src/components/tabs/tab.ts +18 -6
  105. package/src/components/tabs/types.ts +13 -3
  106. package/src/components/textfield/config.ts +2 -3
  107. package/src/components/textfield/index.ts +0 -1
  108. package/src/components/textfield/types.ts +17 -3
  109. package/src/components/timepicker/api.ts +1 -1
  110. package/src/components/timepicker/clockdial.ts +1 -1
  111. package/src/components/timepicker/config.ts +102 -4
  112. package/src/components/timepicker/index.ts +1 -6
  113. package/src/components/timepicker/render.ts +1 -1
  114. package/src/components/timepicker/timepicker.ts +1 -1
  115. package/src/components/tooltip/api.ts +1 -1
  116. package/src/components/tooltip/config.ts +27 -6
  117. package/src/components/tooltip/index.ts +0 -1
  118. package/src/components/tooltip/types.ts +13 -3
  119. package/src/core/compose/features/textlabel.ts +0 -3
  120. package/src/{components/tabs/_styles.scss → styles/components/_tabs.scss} +1 -1
  121. package/src/{components/textfield/_styles.scss → styles/components/_textfield.scss} +70 -67
  122. package/src/styles/main.scss +98 -49
  123. package/src/components/badge/constants.ts +0 -40
  124. package/src/components/button/constants.ts +0 -11
  125. package/src/components/card/constants.ts +0 -84
  126. package/src/components/datepicker/constants.ts +0 -98
  127. package/src/components/dialog/constants.ts +0 -32
  128. package/src/components/extended-fab/constants.ts +0 -36
  129. package/src/components/fab/constants.ts +0 -41
  130. package/src/components/menu/constants.ts +0 -154
  131. package/src/components/navigation/constants.ts +0 -200
  132. package/src/components/progress/constants.ts +0 -29
  133. package/src/components/search/constants.ts +0 -21
  134. package/src/components/segmented-button/constants.ts +0 -42
  135. package/src/components/switch/constants.ts +0 -80
  136. package/src/components/tabs/constants.ts +0 -89
  137. package/src/components/timepicker/constants.ts +0 -138
  138. /package/src/{components/badge/_styles.scss → styles/components/_badge.scss} +0 -0
  139. /package/src/{components/bottom-app-bar/_styles.scss → styles/components/_bottom-app-bar.scss} +0 -0
  140. /package/src/{components/button/_styles.scss → styles/components/_button.scss} +0 -0
  141. /package/src/{components/card/_styles.scss → styles/components/_card.scss} +0 -0
  142. /package/src/{components/carousel/_styles.scss → styles/components/_carousel.scss} +0 -0
  143. /package/src/{components/checkbox/_styles.scss → styles/components/_checkbox.scss} +0 -0
  144. /package/src/{components/chip/_styles.scss → styles/components/_chip.scss} +0 -0
  145. /package/src/{components/datepicker/_styles.scss → styles/components/_datepicker.scss} +0 -0
  146. /package/src/{components/dialog/_styles.scss → styles/components/_dialog.scss} +0 -0
  147. /package/src/{components/divider/_styles.scss → styles/components/_divider.scss} +0 -0
  148. /package/src/{components/extended-fab/_styles.scss → styles/components/_extended-fab.scss} +0 -0
  149. /package/src/{components/fab/_styles.scss → styles/components/_fab.scss} +0 -0
  150. /package/src/{components/list/_styles.scss → styles/components/_list.scss} +0 -0
  151. /package/src/{components/menu/_styles.scss → styles/components/_menu.scss} +0 -0
  152. /package/src/{components/navigation/_styles.scss → styles/components/_navigation.scss} +0 -0
  153. /package/src/{components/progress/_styles.scss → styles/components/_progress.scss} +0 -0
  154. /package/src/{components/radios/_styles.scss → styles/components/_radios.scss} +0 -0
  155. /package/src/{components/search/_styles.scss → styles/components/_search.scss} +0 -0
  156. /package/src/{components/segmented-button/_styles.scss → styles/components/_segmented-button.scss} +0 -0
  157. /package/src/{components/sheet/_styles.scss → styles/components/_sheet.scss} +0 -0
  158. /package/src/{components/slider/_styles.scss → styles/components/_slider.scss} +0 -0
  159. /package/src/{components/snackbar/_styles.scss → styles/components/_snackbar.scss} +0 -0
  160. /package/src/{components/switch/_styles.scss → styles/components/_switch.scss} +0 -0
  161. /package/src/{components/timepicker/_styles.scss → styles/components/_timepicker.scss} +0 -0
  162. /package/src/{components/tooltip/_styles.scss → styles/components/_tooltip.scss} +0 -0
  163. /package/src/{components/top-app-bar/_styles.scss → styles/components/_top-app-bar.scss} +0 -0
  164. /package/src/styles/utilities/{_color.scss → _colors.scss} +0 -0
@@ -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
+ };
@@ -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
 
@@ -1,18 +1,12 @@
1
1
  // src/components/navigation/features/items.ts
2
2
  import { createNavItem, getAllNestedItems } from '../nav-item';
3
- import { NavItemConfig, NavItemData } from '../types';
4
-
5
- // Type definitions to help with TypeScript conversion
6
- interface Component {
7
- element: HTMLElement;
8
- emit?: (event: string, data: any) => void;
9
- lifecycle?: {
10
- destroy: () => void;
11
- };
12
- [key: string]: any;
13
- }
3
+ import { NavItemConfig, NavItemData, BaseComponent, NavClass } from '../types';
14
4
 
15
- interface ItemsComponent extends Component {
5
+ /**
6
+ * Interface for a component with items management capabilities
7
+ * @internal
8
+ */
9
+ interface ItemsComponent extends BaseComponent {
16
10
  items: Map<string, NavItemData>;
17
11
  addItem: (config: NavItemConfig) => ItemsComponent;
18
12
  removeItem: (id: string) => ItemsComponent;
@@ -23,15 +17,25 @@ interface ItemsComponent extends Component {
23
17
  setActive: (id: string) => ItemsComponent;
24
18
  }
25
19
 
20
+ /**
21
+ * Interface for navigation configuration
22
+ * @internal
23
+ */
26
24
  interface NavigationConfig {
27
25
  prefix?: string;
28
26
  items?: NavItemConfig[];
29
27
  [key: string]: any;
30
28
  }
31
29
 
32
- export const withNavItems = (config: NavigationConfig) => (component: Component): ItemsComponent => {
30
+ /**
31
+ * Adds navigation items management to a component
32
+ * @param {NavigationConfig} config - Navigation configuration
33
+ * @returns {Function} Component enhancer function
34
+ */
35
+ export const withNavItems = (config: NavigationConfig) => (component: BaseComponent): ItemsComponent => {
33
36
  const items = new Map<string, NavItemData>();
34
37
  let activeItem: NavItemData | null = null;
38
+ const prefix = config.prefix || 'mtrl';
35
39
 
36
40
  /**
37
41
  * Recursively stores items in the items Map
@@ -43,9 +47,9 @@ export const withNavItems = (config: NavigationConfig) => (component: Component)
43
47
 
44
48
  if (itemConfig.items?.length) {
45
49
  itemConfig.items.forEach(nestedConfig => {
46
- const container = item.closest(`.${config.prefix}-nav-item-container`);
50
+ const container = item.closest(`.${prefix}-${NavClass.ITEM_CONTAINER}`);
47
51
  if (container) {
48
- const nestedContainer = container.querySelector(`.${config.prefix}-nav-nested-container`);
52
+ const nestedContainer = container.querySelector(`.${prefix}-${NavClass.NESTED_CONTAINER}`);
49
53
  if (nestedContainer) {
50
54
  const nestedItem = nestedContainer.querySelector(`[data-id="${nestedConfig.id}"]`) as HTMLElement;
51
55
  if (nestedItem) {
@@ -68,7 +72,7 @@ export const withNavItems = (config: NavigationConfig) => (component: Component)
68
72
  const role = item.getAttribute('role');
69
73
 
70
74
  if (active) {
71
- item.classList.add(`${config.prefix}-nav-item--active`);
75
+ item.classList.add(`${prefix}-${NavClass.ITEM}--active`);
72
76
 
73
77
  // Set appropriate attribute based on role
74
78
  if (role === 'tab') {
@@ -79,7 +83,7 @@ export const withNavItems = (config: NavigationConfig) => (component: Component)
79
83
  item.setAttribute('aria-current', 'page');
80
84
  }
81
85
  } else {
82
- item.classList.remove(`${config.prefix}-nav-item--active`);
86
+ item.classList.remove(`${prefix}-${NavClass.ITEM}--active`);
83
87
 
84
88
  // Remove appropriate attribute based on role
85
89
  if (role === 'tab') {
@@ -94,7 +98,7 @@ export const withNavItems = (config: NavigationConfig) => (component: Component)
94
98
  // Create initial items
95
99
  if (config.items) {
96
100
  config.items.forEach(itemConfig => {
97
- const item = createNavItem(itemConfig, component.element, config.prefix || 'mtrl');
101
+ const item = createNavItem(itemConfig, component.element, prefix);
98
102
  storeItem(itemConfig, item);
99
103
 
100
104
  if (itemConfig.active) {
@@ -106,7 +110,7 @@ export const withNavItems = (config: NavigationConfig) => (component: Component)
106
110
 
107
111
  // Handle item clicks
108
112
  component.element.addEventListener('click', (event: Event) => {
109
- const item = (event.target as HTMLElement).closest(`.${config.prefix}-nav-item`) as HTMLElement;
113
+ const item = (event.target as HTMLElement).closest(`.${prefix}-${NavClass.ITEM}`) as HTMLElement;
110
114
  if (!item || (item as any).disabled || item.getAttribute('aria-haspopup') === 'menu') return;
111
115
 
112
116
  const id = item.dataset.id;
@@ -151,12 +155,12 @@ export const withNavItems = (config: NavigationConfig) => (component: Component)
151
155
 
152
156
  if (!currentItem) return path;
153
157
 
154
- let parentContainer = currentItem.element.closest(`.${config.prefix}-nav-nested-container`);
158
+ let parentContainer = currentItem.element.closest(`.${prefix}-${NavClass.NESTED_CONTAINER}`);
155
159
  while (parentContainer) {
156
160
  const parentItemContainer = parentContainer.parentElement;
157
161
  if (!parentItemContainer) break;
158
162
 
159
- const parentItem = parentItemContainer.querySelector(`.${config.prefix}-nav-item`);
163
+ const parentItem = parentItemContainer.querySelector(`.${prefix}-${NavClass.ITEM}`);
160
164
  if (!parentItem) break;
161
165
 
162
166
  const parentId = parentItem.getAttribute('data-id');
@@ -165,7 +169,7 @@ export const withNavItems = (config: NavigationConfig) => (component: Component)
165
169
  path.unshift(parentId);
166
170
 
167
171
  // Move up to next level
168
- parentContainer = parentItemContainer.closest(`.${config.prefix}-nav-nested-container`);
172
+ parentContainer = parentItemContainer.closest(`.${prefix}-${NavClass.NESTED_CONTAINER}`);
169
173
  }
170
174
 
171
175
  return path;
@@ -189,7 +193,7 @@ export const withNavItems = (config: NavigationConfig) => (component: Component)
189
193
  addItem(itemConfig: NavItemConfig) {
190
194
  if (items.has(itemConfig.id)) return this;
191
195
 
192
- const item = createNavItem(itemConfig, component.element, config.prefix || 'mtrl');
196
+ const item = createNavItem(itemConfig, component.element, prefix);
193
197
  storeItem(itemConfig, item);
194
198
 
195
199
  if (itemConfig.active) {
@@ -210,7 +214,7 @@ export const withNavItems = (config: NavigationConfig) => (component: Component)
210
214
  if (!item) return this;
211
215
 
212
216
  // Remove all nested items first
213
- const nestedItems = getAllNestedItems(item.element, config.prefix || 'mtrl');
217
+ const nestedItems = getAllNestedItems(item.element, prefix);
214
218
  nestedItems.forEach(nestedItem => {
215
219
  const nestedId = nestedItem.dataset.id;
216
220
  if (nestedId) items.delete(nestedId);
@@ -221,7 +225,7 @@ export const withNavItems = (config: NavigationConfig) => (component: Component)
221
225
  }
222
226
 
223
227
  // Remove the entire item container
224
- const container = item.element.closest(`.${config.prefix}-nav-item-container`);
228
+ const container = item.element.closest(`.${prefix}-${NavClass.ITEM_CONTAINER}`);
225
229
  if (container) {
226
230
  container.remove();
227
231
  }
@@ -255,9 +259,9 @@ export const withNavItems = (config: NavigationConfig) => (component: Component)
255
259
  const parentItem = items.get(parentId);
256
260
  if (parentItem) {
257
261
  const parentButton = parentItem.element;
258
- const container = parentButton.closest(`.${config.prefix}-nav-item-container`);
262
+ const container = parentButton.closest(`.${prefix}-${NavClass.ITEM_CONTAINER}`);
259
263
  if (container) {
260
- const nestedContainer = container.querySelector(`.${config.prefix}-nav-nested-container`);
264
+ const nestedContainer = container.querySelector(`.${prefix}-${NavClass.NESTED_CONTAINER}`);
261
265
  if (nestedContainer) {
262
266
  parentButton.setAttribute('aria-expanded', 'true');
263
267
  nestedContainer.hidden = false;
@@ -1,11 +1,5 @@
1
1
  // src/components/navigation/index.ts
2
2
  export { default } from './navigation'
3
- export {
4
- NAV_VARIANTS,
5
- NAV_POSITIONS,
6
- NAV_BEHAVIORS,
7
- NAV_STATES
8
- } from './constants'
9
3
  export {
10
4
  NavigationConfig,
11
5
  NavigationComponent,