mtrl 0.3.0 → 0.3.2

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 (140) hide show
  1. package/.env +15 -0
  2. package/CONTRIBUTING.md +8 -8
  3. package/DOCS.md +3 -3
  4. package/README.md +43 -20
  5. package/TESTING.md +128 -18
  6. package/dist/index.js +14865 -0
  7. package/git-user-stats.js +545 -0
  8. package/index.ts +9 -69
  9. package/package.json +10 -3
  10. package/src/components/badge/api.ts +15 -1
  11. package/src/components/badge/badge.ts +43 -4
  12. package/src/components/badge/config.ts +40 -8
  13. package/src/components/badge/index.ts +64 -3
  14. package/src/components/badge/types.ts +175 -33
  15. package/src/components/button/api.ts +63 -1
  16. package/src/components/button/button.ts +39 -3
  17. package/src/components/button/config.ts +21 -4
  18. package/src/components/button/index.ts +26 -1
  19. package/src/components/button/types.ts +7 -1
  20. package/src/components/card/api.ts +78 -9
  21. package/src/components/card/card.ts +58 -3
  22. package/src/components/card/config.ts +41 -11
  23. package/src/components/card/features.ts +39 -12
  24. package/src/components/card/index.ts +84 -19
  25. package/src/components/card/types.ts +218 -29
  26. package/src/components/carousel/carousel.ts +92 -28
  27. package/src/components/carousel/constants.ts +107 -21
  28. package/src/components/carousel/index.ts +31 -13
  29. package/src/components/checkbox/checkbox.ts +83 -16
  30. package/src/components/checkbox/index.ts +43 -1
  31. package/src/components/checkbox/types.ts +219 -32
  32. package/src/components/chips/api.ts +194 -0
  33. package/src/components/{chip → chips/chip}/api.ts +42 -2
  34. package/src/components/chips/chip/chip.ts +131 -0
  35. package/src/components/{chip → chips/chip}/config.ts +3 -3
  36. package/src/components/chips/chip/index.ts +3 -0
  37. package/src/components/chips/chips.md +481 -0
  38. package/src/components/chips/chips.ts +75 -0
  39. package/src/components/chips/config.ts +109 -0
  40. package/src/components/chips/constants.ts +61 -0
  41. package/src/components/chips/features/chip-items.ts +33 -0
  42. package/src/components/chips/features/container.ts +77 -0
  43. package/src/components/chips/features/controller.ts +448 -0
  44. package/src/components/chips/features/index.ts +5 -0
  45. package/src/components/chips/features/label.ts +108 -0
  46. package/src/components/chips/index.ts +11 -0
  47. package/src/components/chips/schema.ts +61 -0
  48. package/src/components/{chip → chips}/types.ts +203 -92
  49. package/src/components/dialog/dialog.ts +99 -16
  50. package/src/components/dialog/index.ts +97 -1
  51. package/src/components/dialog/types.ts +375 -69
  52. package/src/components/divider/config.ts +90 -6
  53. package/src/components/divider/divider.ts +32 -2
  54. package/src/components/divider/features.ts +26 -0
  55. package/src/components/divider/index.ts +30 -0
  56. package/src/components/divider/types.ts +86 -9
  57. package/src/components/extended-fab/api.ts +53 -1
  58. package/src/components/extended-fab/config.ts +29 -1
  59. package/src/components/extended-fab/extended-fab.ts +28 -0
  60. package/src/components/extended-fab/index.ts +36 -0
  61. package/src/components/extended-fab/types.ts +458 -13
  62. package/src/components/fab/api.ts +42 -2
  63. package/src/components/fab/config.ts +29 -1
  64. package/src/components/fab/fab.ts +16 -2
  65. package/src/components/fab/index.ts +35 -0
  66. package/src/components/fab/types.ts +374 -10
  67. package/src/components/list/api.ts +12 -2
  68. package/src/components/list/config.ts +21 -0
  69. package/src/components/list/features.ts +6 -0
  70. package/src/components/list/index.ts +56 -1
  71. package/src/components/list/list-item.ts +46 -2
  72. package/src/components/list/list.ts +73 -2
  73. package/src/components/list/types.ts +172 -0
  74. package/src/components/list/utils.ts +26 -2
  75. package/src/components/menu/api.ts +217 -20
  76. package/src/components/menu/config.ts +27 -0
  77. package/src/components/menu/features/visibility.ts +55 -6
  78. package/src/components/menu/index.ts +64 -0
  79. package/src/components/menu/menu-item.ts +46 -3
  80. package/src/components/menu/menu.ts +77 -1
  81. package/src/components/menu/types.ts +404 -39
  82. package/src/components/navigation/index.ts +4 -1
  83. package/src/components/navigation/types.ts +33 -0
  84. package/src/components/sheet/config.ts +1 -2
  85. package/src/components/sheet/features/gestures.ts +1 -1
  86. package/src/components/sheet/features/position.ts +1 -2
  87. package/src/components/sheet/features/state.ts +1 -1
  88. package/src/components/sheet/index.ts +10 -2
  89. package/src/components/sheet/sheet.ts +1 -2
  90. package/src/components/sheet/types.ts +29 -1
  91. package/src/components/slider/api.ts +1 -1
  92. package/src/components/slider/config.ts +1 -1
  93. package/src/components/slider/features/controller.ts +1 -1
  94. package/src/components/slider/features/handlers.ts +1 -1
  95. package/src/components/slider/features/states.ts +1 -1
  96. package/src/components/slider/index.ts +12 -5
  97. package/src/components/slider/schema.ts +1 -1
  98. package/src/components/slider/types.ts +31 -0
  99. package/src/components/snackbar/index.ts +7 -1
  100. package/src/components/snackbar/types.ts +25 -0
  101. package/src/components/switch/index.ts +5 -1
  102. package/src/components/switch/types.ts +13 -0
  103. package/src/components/tabs/tab-api.ts +1 -1
  104. package/src/components/tabs/types.ts +1 -1
  105. package/src/components/textfield/index.ts +7 -1
  106. package/src/components/textfield/types.ts +36 -0
  107. package/src/components/tooltip/api.ts +6 -2
  108. package/src/components/tooltip/config.ts +9 -28
  109. package/src/components/tooltip/index.ts +10 -1
  110. package/src/components/tooltip/types.ts +38 -3
  111. package/src/index.ts +129 -31
  112. package/src/styles/abstract/_mixins.scss +23 -9
  113. package/src/styles/abstract/_variables.scss +14 -4
  114. package/src/styles/components/_card.scss +1 -1
  115. package/src/styles/components/_chip.scss +323 -113
  116. package/src/styles/components/_tabs.scss +1 -1
  117. package/src/components/checkbox/constants.ts +0 -37
  118. package/src/components/chip/chip-set.ts +0 -225
  119. package/src/components/chip/chip.ts +0 -118
  120. package/src/components/chip/constants.ts +0 -28
  121. package/src/components/chip/index.ts +0 -12
  122. package/src/components/list/constants.ts +0 -116
  123. package/src/components/sheet/constants.ts +0 -20
  124. package/src/components/slider/constants.ts +0 -32
  125. package/src/components/snackbar/constants.ts +0 -26
  126. package/src/components/tooltip/constants.ts +0 -27
  127. package/test/components/button.test.js +0 -170
  128. package/test/components/checkbox.test.js +0 -238
  129. package/test/components/list.test.js +0 -105
  130. package/test/components/menu.test.js +0 -385
  131. package/test/components/navigation.test.js +0 -227
  132. package/test/components/snackbar.test.js +0 -234
  133. package/test/components/switch.test.js +0 -186
  134. package/test/components/textfield.test.js +0 -314
  135. package/test/core/emitter.test.js +0 -141
  136. package/test/core/ripple.test.js +0 -66
  137. package/test/setup.js +0 -371
  138. package/tsconfig.json +0 -22
  139. package/typedoc.json +0 -28
  140. package/typedoc.simple.json +0 -14
@@ -2,9 +2,17 @@
2
2
  import { ApiOptions, BaseComponent, MenuComponent, MenuItemConfig, MenuPositionConfig } from './types';
3
3
 
4
4
  /**
5
- * Enhances menu component with API methods
6
- * @param {ApiOptions} options - API configuration
5
+ * Enhances a menu component with public API methods
6
+ *
7
+ * This is a higher-order function that wraps a base component and exposes
8
+ * a standardized public API following the MenuComponent interface. It ensures
9
+ * all methods return the component instance for method chaining and handles
10
+ * proper resource cleanup during component destruction.
11
+ *
12
+ * @param {ApiOptions} options - API configuration including lifecycle methods
7
13
  * @returns {Function} Higher-order function that adds API methods to component
14
+ * @internal
15
+ * @category Components
8
16
  */
9
17
  export const withAPI = ({ lifecycle }: ApiOptions) =>
10
18
  (component: BaseComponent): MenuComponent => ({
@@ -13,7 +21,19 @@ export const withAPI = ({ lifecycle }: ApiOptions) =>
13
21
 
14
22
  /**
15
23
  * Shows the menu
16
- * @returns {MenuComponent} Component instance
24
+ *
25
+ * Makes the menu visible in the DOM. This method triggers the 'open' event.
26
+ * If called when the menu is already visible, it has no effect.
27
+ *
28
+ * @returns {MenuComponent} Component instance for method chaining
29
+ * @example
30
+ * ```typescript
31
+ * // Show a menu
32
+ * menu.show();
33
+ *
34
+ * // Position and show a menu in one chain
35
+ * menu.position(buttonElement).show();
36
+ * ```
17
37
  */
18
38
  show(): MenuComponent {
19
39
  component.show?.();
@@ -22,7 +42,23 @@ export const withAPI = ({ lifecycle }: ApiOptions) =>
22
42
 
23
43
  /**
24
44
  * Hides the menu
25
- * @returns {MenuComponent} Component instance
45
+ *
46
+ * Makes the menu invisible in the DOM. This method triggers the 'close' event.
47
+ * If called when the menu is already hidden, it has no effect.
48
+ *
49
+ * @returns {MenuComponent} Component instance for method chaining
50
+ * @example
51
+ * ```typescript
52
+ * // Hide a menu
53
+ * menu.hide();
54
+ *
55
+ * // Listen for clicks outside the menu to hide it
56
+ * document.addEventListener('click', (event) => {
57
+ * if (menu.isVisible() && !menu.element.contains(event.target)) {
58
+ * menu.hide();
59
+ * }
60
+ * });
61
+ * ```
26
62
  */
27
63
  hide(): MenuComponent {
28
64
  component.hide?.();
@@ -30,10 +66,27 @@ export const withAPI = ({ lifecycle }: ApiOptions) =>
30
66
  },
31
67
 
32
68
  /**
33
- * Positions the menu relative to a target
34
- * @param {HTMLElement} target - Target element
35
- * @param {MenuPositionConfig} options - Position options
36
- * @returns {MenuComponent} Component instance
69
+ * Positions the menu relative to a target element
70
+ *
71
+ * Calculates and sets the position of the menu relative to the specified target element.
72
+ * This allows precise control over menu placement in the UI.
73
+ *
74
+ * @param {HTMLElement} target - Target element to position against
75
+ * @param {MenuPositionConfig} options - Position configuration
76
+ * @returns {MenuComponent} Component instance for method chaining
77
+ * @example
78
+ * ```typescript
79
+ * // Position relative to a button with default alignment (left, bottom)
80
+ * menu.position(document.getElementById('menuButton'));
81
+ *
82
+ * // Position with custom alignment
83
+ * menu.position(buttonElement, {
84
+ * align: 'right', // Align to the right edge of the target
85
+ * vAlign: 'top', // Position above the target
86
+ * offsetX: 5, // Add 5px horizontal offset
87
+ * offsetY: 10 // Add 10px vertical offset
88
+ * });
89
+ * ```
37
90
  */
38
91
  position(target: HTMLElement, options?: MenuPositionConfig): MenuComponent {
39
92
  component.position?.(target, options);
@@ -42,8 +95,33 @@ export const withAPI = ({ lifecycle }: ApiOptions) =>
42
95
 
43
96
  /**
44
97
  * Adds an item to the menu
98
+ *
99
+ * Dynamically adds a new item to the menu. This can be called at any time,
100
+ * even after the menu has been rendered and shown.
101
+ *
45
102
  * @param {MenuItemConfig} config - Item configuration
46
- * @returns {MenuComponent} Component instance
103
+ * @returns {MenuComponent} Component instance for method chaining
104
+ * @example
105
+ * ```typescript
106
+ * // Add a standard item
107
+ * menu.addItem({ name: 'edit', text: 'Edit' });
108
+ *
109
+ * // Add a divider
110
+ * menu.addItem({ type: 'divider' });
111
+ *
112
+ * // Add a disabled item
113
+ * menu.addItem({ name: 'print', text: 'Print', disabled: true });
114
+ *
115
+ * // Add an item with a submenu
116
+ * menu.addItem({
117
+ * name: 'share',
118
+ * text: 'Share',
119
+ * items: [
120
+ * { name: 'email', text: 'Email' },
121
+ * { name: 'link', text: 'Copy Link' }
122
+ * ]
123
+ * });
124
+ * ```
47
125
  */
48
126
  addItem(config: MenuItemConfig): MenuComponent {
49
127
  component.addItem?.(config);
@@ -51,9 +129,23 @@ export const withAPI = ({ lifecycle }: ApiOptions) =>
51
129
  },
52
130
 
53
131
  /**
54
- * Removes an item by name
55
- * @param {string} name - Item name to remove
56
- * @returns {MenuComponent} Component instance
132
+ * Removes an item from the menu
133
+ *
134
+ * Dynamically removes an item from the menu by its name identifier.
135
+ * If the item doesn't exist, no action is taken.
136
+ *
137
+ * @param {string} name - Name identifier of the item to remove
138
+ * @returns {MenuComponent} Component instance for method chaining
139
+ * @example
140
+ * ```typescript
141
+ * // Remove an item by name
142
+ * menu.removeItem('delete');
143
+ *
144
+ * // Check if a condition is met, then remove an item
145
+ * if (!userHasEditPermission) {
146
+ * menu.removeItem('edit');
147
+ * }
148
+ * ```
57
149
  */
58
150
  removeItem(name: string): MenuComponent {
59
151
  component.removeItem?.(name);
@@ -61,8 +153,29 @@ export const withAPI = ({ lifecycle }: ApiOptions) =>
61
153
  },
62
154
 
63
155
  /**
64
- * Gets all registered items
65
- * @returns {Map<string, any>} Map of item names to configurations
156
+ * Gets all registered menu items
157
+ *
158
+ * Returns a Map containing all the current menu items, indexed by their name.
159
+ * Each entry contains the item's DOM element and configuration.
160
+ *
161
+ * @returns {Map<string, MenuItemData>} Map of item names to their data
162
+ * @example
163
+ * ```typescript
164
+ * // Get all items
165
+ * const items = menu.getItems();
166
+ *
167
+ * // Check if a specific item exists
168
+ * if (items.has('delete')) {
169
+ * console.log('Delete item exists');
170
+ * }
171
+ *
172
+ * // Get the element for a specific item
173
+ * const editItem = items.get('edit');
174
+ * if (editItem) {
175
+ * console.log('Edit item element:', editItem.element);
176
+ * console.log('Edit item config:', editItem.config);
177
+ * }
178
+ * ```
66
179
  */
67
180
  getItems() {
68
181
  return component.getItems?.() || new Map();
@@ -70,7 +183,29 @@ export const withAPI = ({ lifecycle }: ApiOptions) =>
70
183
 
71
184
  /**
72
185
  * Checks if the menu is currently visible
186
+ *
187
+ * Returns true if the menu is currently visible in the DOM,
188
+ * false otherwise.
189
+ *
73
190
  * @returns {boolean} Whether the menu is visible
191
+ * @example
192
+ * ```typescript
193
+ * // Check if menu is visible before performing an action
194
+ * if (menu.isVisible()) {
195
+ * menu.hide();
196
+ * } else {
197
+ * menu.position(button).show();
198
+ * }
199
+ *
200
+ * // Toggle menu visibility
201
+ * toggleButton.addEventListener('click', () => {
202
+ * if (menu.isVisible()) {
203
+ * menu.hide();
204
+ * } else {
205
+ * menu.position(toggleButton).show();
206
+ * }
207
+ * });
208
+ * ```
74
209
  */
75
210
  isVisible(): boolean {
76
211
  return component.isVisible?.() || false;
@@ -78,9 +213,35 @@ export const withAPI = ({ lifecycle }: ApiOptions) =>
78
213
 
79
214
  /**
80
215
  * Registers an event handler
81
- * @param {string} event - Event name
82
- * @param {Function} handler - Event handler
83
- * @returns {MenuComponent} Component instance
216
+ *
217
+ * Subscribes to menu events like 'select', 'open', 'close', etc.
218
+ * The handler will be called whenever the specified event occurs.
219
+ *
220
+ * @param {string} event - Event name to listen for
221
+ * @param {Function} handler - Callback function to execute when the event occurs
222
+ * @returns {MenuComponent} Component instance for method chaining
223
+ * @example
224
+ * ```typescript
225
+ * // Listen for item selection
226
+ * menu.on('select', (event) => {
227
+ * console.log(`Selected item: ${event.name}`);
228
+ *
229
+ * if (event.name === 'delete') {
230
+ * confirmDelete();
231
+ * }
232
+ * });
233
+ *
234
+ * // Listen for menu opening
235
+ * menu.on('open', () => {
236
+ * console.log('Menu opened');
237
+ * analytics.trackMenuOpen();
238
+ * });
239
+ *
240
+ * // Listen for submenu opening
241
+ * menu.on('submenuOpen', (data) => {
242
+ * console.log('Submenu opened:', data);
243
+ * });
244
+ * ```
84
245
  */
85
246
  on(event: string, handler: Function): MenuComponent {
86
247
  component.on?.(event, handler);
@@ -89,9 +250,26 @@ export const withAPI = ({ lifecycle }: ApiOptions) =>
89
250
 
90
251
  /**
91
252
  * Unregisters an event handler
92
- * @param {string} event - Event name
93
- * @param {Function} handler - Event handler
94
- * @returns {MenuComponent} Component instance
253
+ *
254
+ * Removes a previously registered event handler.
255
+ * If the handler is not found, no action is taken.
256
+ *
257
+ * @param {string} event - Event name to stop listening for
258
+ * @param {Function} handler - The handler function to remove
259
+ * @returns {MenuComponent} Component instance for method chaining
260
+ * @example
261
+ * ```typescript
262
+ * // Define a handler function
263
+ * const handleSelection = (event) => {
264
+ * console.log(`Selected: ${event.name}`);
265
+ * };
266
+ *
267
+ * // Register the handler
268
+ * menu.on('select', handleSelection);
269
+ *
270
+ * // Later, unregister the handler
271
+ * menu.off('select', handleSelection);
272
+ * ```
95
273
  */
96
274
  off(event: string, handler: Function): MenuComponent {
97
275
  component.off?.(event, handler);
@@ -100,7 +278,26 @@ export const withAPI = ({ lifecycle }: ApiOptions) =>
100
278
 
101
279
  /**
102
280
  * Destroys the menu component and cleans up resources
281
+ *
282
+ * Performs complete cleanup by removing event listeners, DOM elements,
283
+ * and references to prevent memory leaks. After calling this method,
284
+ * the menu instance should not be used again.
285
+ *
103
286
  * @returns {MenuComponent} Component instance
287
+ * @example
288
+ * ```typescript
289
+ * // When no longer needed, destroy the menu
290
+ * menu.destroy();
291
+ *
292
+ * // When navigating away or changing views
293
+ * function changeView() {
294
+ * // Clean up existing components
295
+ * contextMenu.destroy();
296
+ *
297
+ * // Set up new view
298
+ * setupNewView();
299
+ * }
300
+ * ```
104
301
  */
105
302
  destroy(): MenuComponent {
106
303
  // First close any open submenus
@@ -9,24 +9,45 @@ import { MenuConfig, BaseComponent, ApiOptions } from './types';
9
9
 
10
10
  /**
11
11
  * Default configuration for the Menu component
12
+ *
13
+ * Defines the standard behavior and initial state for menus.
14
+ * These defaults are merged with user-provided configuration options.
15
+ *
16
+ * @internal
17
+ * @category Components
12
18
  */
13
19
  export const defaultConfig: MenuConfig = {
20
+ /** Empty initial items array */
14
21
  items: [],
22
+
23
+ /** By default, menus close when an item is selected */
15
24
  stayOpenOnSelect: false
16
25
  };
17
26
 
18
27
  /**
19
28
  * Creates the base configuration for Menu component
29
+ *
30
+ * Merges user-provided configuration with default values
31
+ * to ensure all required properties are available.
32
+ *
20
33
  * @param {MenuConfig} config - User provided configuration
21
34
  * @returns {MenuConfig} Complete configuration with defaults applied
35
+ * @internal
36
+ * @category Components
22
37
  */
23
38
  export const createBaseConfig = (config: MenuConfig = {}): MenuConfig =>
24
39
  createComponentConfig(defaultConfig, config, 'menu') as MenuConfig;
25
40
 
26
41
  /**
27
42
  * Generates element configuration for the Menu component
43
+ *
44
+ * Creates the configuration needed for the DOM element creation.
45
+ * Sets up proper accessibility attributes like ARIA roles and states.
46
+ *
28
47
  * @param {MenuConfig} config - Menu configuration
29
48
  * @returns {Object} Element configuration object for withElement
49
+ * @internal
50
+ * @category Components
30
51
  */
31
52
  export const getElementConfig = (config: MenuConfig) =>
32
53
  createElementConfig(config, {
@@ -42,8 +63,14 @@ export const getElementConfig = (config: MenuConfig) =>
42
63
 
43
64
  /**
44
65
  * Creates API configuration for the Menu component
66
+ *
67
+ * Builds the configuration needed for the withAPI feature,
68
+ * including lifecycle methods for proper resource cleanup.
69
+ *
45
70
  * @param {BaseComponent} comp - Component with lifecycle feature
46
71
  * @returns {ApiOptions} API configuration object
72
+ * @internal
73
+ * @category Components
47
74
  */
48
75
  export const getApiConfig = (comp: BaseComponent): ApiOptions => ({
49
76
  lifecycle: {
@@ -4,8 +4,24 @@ import { MENU_EVENT, MENU_CLASSES } from '../utils';
4
4
 
5
5
  /**
6
6
  * Adds visibility management functionality to a menu component
7
- * @param {MenuConfig} config - Menu configuration
8
- * @returns {Function} Component enhancer
7
+ *
8
+ * This feature adds the ability to show and hide the menu with smooth transitions,
9
+ * along with proper event handling for clicks outside the menu and keyboard shortcuts.
10
+ * It implements the following functionality:
11
+ *
12
+ * - Tracking visibility state
13
+ * - Showing the menu with animation
14
+ * - Hiding the menu with animation
15
+ * - Automatic handling of clicks outside the menu
16
+ * - Keyboard shortcut (Escape) for dismissing the menu
17
+ * - Proper ARIA attributes for accessibility
18
+ * - Event emission for component state changes
19
+ *
20
+ * @param {MenuConfig} config - Menu configuration options
21
+ * @returns {Function} Component enhancer function that adds visibility features
22
+ *
23
+ * @internal
24
+ * @category Components
9
25
  */
10
26
  export const withVisibility = (config: MenuConfig) => (component: BaseComponent): BaseComponent => {
11
27
  let isVisible = false;
@@ -19,6 +35,13 @@ export const withVisibility = (config: MenuConfig) => (component: BaseComponent)
19
35
 
20
36
  /**
21
37
  * Shows the menu
38
+ *
39
+ * Makes the menu visible with a smooth transition animation.
40
+ * Handles DOM insertion, event binding, and ARIA attribute updates.
41
+ * Emits an 'open' event when the menu becomes visible.
42
+ *
43
+ * @returns {BaseComponent} The component instance for method chaining
44
+ * @internal
22
45
  */
23
46
  show() {
24
47
  if (isVisible) return this;
@@ -67,6 +90,14 @@ export const withVisibility = (config: MenuConfig) => (component: BaseComponent)
67
90
 
68
91
  /**
69
92
  * Hides the menu
93
+ *
94
+ * Makes the menu invisible with a smooth transition animation.
95
+ * Handles event cleanup, ARIA attribute updates, and DOM removal.
96
+ * Emits a 'close' event when the menu becomes hidden.
97
+ * Also ensures any open submenus are closed first.
98
+ *
99
+ * @returns {BaseComponent} The component instance for method chaining
100
+ * @internal
70
101
  */
71
102
  hide() {
72
103
  // Return early if already hidden
@@ -130,7 +161,12 @@ export const withVisibility = (config: MenuConfig) => (component: BaseComponent)
130
161
 
131
162
  /**
132
163
  * Returns whether the menu is currently visible
133
- * @returns {boolean} Visibility state
164
+ *
165
+ * Provides the current visibility state of the menu component.
166
+ * This method is used internally and exposed via the public API.
167
+ *
168
+ * @returns {boolean} True if the menu is visible, false otherwise
169
+ * @internal
134
170
  */
135
171
  isVisible() {
136
172
  return isVisible;
@@ -139,7 +175,14 @@ export const withVisibility = (config: MenuConfig) => (component: BaseComponent)
139
175
 
140
176
  /**
141
177
  * Handles clicks outside the menu
142
- * @param {MouseEvent} event - Mouse event
178
+ *
179
+ * Event handler for detecting and responding to mouse clicks outside the menu.
180
+ * Checks if the click occurred outside both the menu and its origin element,
181
+ * and if so, hides the menu. This provides the auto-dismiss behavior expected
182
+ * of temporary surfaces like menus.
183
+ *
184
+ * @param {MouseEvent} event - Mouse event containing target information
185
+ * @internal
143
186
  */
144
187
  const handleOutsideClick = (event: MouseEvent) => {
145
188
  if (!isVisible) return;
@@ -165,8 +208,14 @@ export const withVisibility = (config: MenuConfig) => (component: BaseComponent)
165
208
  };
166
209
 
167
210
  /**
168
- * Handles keyboard events
169
- * @param {KeyboardEvent} event - Keyboard event
211
+ * Handles keyboard events for the menu
212
+ *
213
+ * Event handler for keyboard interactions with the menu.
214
+ * Currently implements the Escape key to dismiss the menu,
215
+ * following standard interaction patterns for temporary UI surfaces.
216
+ *
217
+ * @param {KeyboardEvent} event - Keyboard event containing key information
218
+ * @internal
170
219
  */
171
220
  const handleKeydown = (event: KeyboardEvent) => {
172
221
  if (!isVisible) return;
@@ -1,5 +1,69 @@
1
1
  // src/components/menu/index.ts
2
+
3
+ /**
4
+ * @module Menu
5
+ *
6
+ * Menu component following Material Design 3 guidelines.
7
+ * Menus display a list of choices on a temporary surface, appearing when users
8
+ * interact with a button, action, or other control.
9
+ *
10
+ * The main export is the {@link default | createMenu} factory function that creates
11
+ * a {@link MenuComponent} instance with the provided configuration.
12
+ *
13
+ * Features:
14
+ * - Configurable positioning relative to other elements
15
+ * - Support for nested submenus
16
+ * - Keyboard navigation and accessibility
17
+ * - Item selection events
18
+ * - Automatic handling of outside clicks
19
+ * - Support for dividers and disabled items
20
+ * - Dynamic item management
21
+ *
22
+ * @example
23
+ * ```typescript
24
+ * // Create a basic menu
25
+ * const menu = createMenu({
26
+ * items: [
27
+ * { name: 'edit', text: 'Edit' },
28
+ * { name: 'duplicate', text: 'Duplicate' },
29
+ * { type: 'divider' },
30
+ * { name: 'delete', text: 'Delete', class: 'danger-item' }
31
+ * ]
32
+ * });
33
+ *
34
+ * // Show the menu positioned relative to a button
35
+ * const button = document.getElementById('menuButton');
36
+ * button.addEventListener('click', () => {
37
+ * menu.position(button).show();
38
+ * });
39
+ *
40
+ * // Handle menu selection
41
+ * menu.on('select', (event) => {
42
+ * console.log(`Selected: ${event.name}`);
43
+ *
44
+ * if (event.name === 'delete') {
45
+ * // Confirm deletion
46
+ * if (confirm('Are you sure?')) {
47
+ * deleteItem();
48
+ * }
49
+ * }
50
+ * });
51
+ * ```
52
+ *
53
+ * @category Components
54
+ */
55
+
56
+ /**
57
+ * Factory function to create a new Menu component.
58
+ * @see MenuComponent for the full API reference
59
+ */
2
60
  export { default } from './menu';
61
+
62
+ /**
63
+ * Menu component types and interfaces
64
+ *
65
+ * These types define the structure and behavior of the Menu component.
66
+ */
3
67
  export {
4
68
  MenuConfig,
5
69
  MenuComponent,
@@ -3,10 +3,53 @@ import { MenuItemConfig } from './types';
3
3
  import { MENU_ITEM_TYPE, getMenuClass } from './utils';
4
4
 
5
5
  /**
6
- * Creates a menu item element
6
+ * Creates a DOM element for a menu item
7
+ *
8
+ * Generates an HTMLElement (li) based on the provided configuration.
9
+ * Handles different types of menu items (standard, divider, submenu),
10
+ * applies proper CSS classes, and sets appropriate ARIA attributes
11
+ * for accessibility.
12
+ *
7
13
  * @param {MenuItemConfig} itemConfig - Item configuration
8
- * @param {string} prefix - CSS class prefix
9
- * @returns {HTMLElement} Menu item element
14
+ * @param {string} prefix - CSS class prefix (default: 'mtrl')
15
+ * @returns {HTMLElement} Menu item DOM element
16
+ *
17
+ * @example
18
+ * ```typescript
19
+ * // Create a standard menu item
20
+ * const itemElement = createMenuItem(
21
+ * { name: 'edit', text: 'Edit' },
22
+ * 'mtrl'
23
+ * );
24
+ *
25
+ * // Create a disabled menu item
26
+ * const disabledItem = createMenuItem(
27
+ * { name: 'print', text: 'Print', disabled: true },
28
+ * 'mtrl'
29
+ * );
30
+ *
31
+ * // Create a divider
32
+ * const divider = createMenuItem(
33
+ * { type: 'divider' },
34
+ * 'mtrl'
35
+ * );
36
+ *
37
+ * // Create an item with submenu indicator
38
+ * const submenuItem = createMenuItem(
39
+ * {
40
+ * name: 'share',
41
+ * text: 'Share',
42
+ * items: [
43
+ * { name: 'email', text: 'Email' },
44
+ * { name: 'link', text: 'Copy Link' }
45
+ * ]
46
+ * },
47
+ * 'mtrl'
48
+ * );
49
+ * ```
50
+ *
51
+ * @internal
52
+ * @category Components
10
53
  */
11
54
  export const createMenuItem = (itemConfig: MenuItemConfig, prefix: string): HTMLElement => {
12
55
  const item = document.createElement('li');
@@ -16,7 +16,25 @@ import {
16
16
 
17
17
  /**
18
18
  * Creates a new Menu component
19
- * @param {MenuConfig} config - Menu configuration
19
+ *
20
+ * Creates a Material Design 3 menu that displays a list of choices on a temporary surface.
21
+ * The menu can be positioned relative to other elements, trigger events on selection,
22
+ * and support keyboard navigation.
23
+ *
24
+ * The returned component provides these methods:
25
+ *
26
+ * `show()` - Shows the menu
27
+ * `hide()` - Hides the menu
28
+ * `position(target, options)` - Positions the menu relative to a target element
29
+ * `addItem(config)` - Adds a menu item
30
+ * `removeItem(name)` - Removes a menu item
31
+ * `getItems()` - Gets all menu items
32
+ * `isVisible()` - Checks if the menu is visible
33
+ * `on(event, handler)` - Adds event listener
34
+ * `off(event, handler)` - Removes event listener
35
+ * `destroy()` - Destroys the menu component
36
+ *
37
+ * @param {MenuConfig} config - Menu configuration options
20
38
  * @returns {MenuComponent} Menu component instance
21
39
  *
22
40
  * @example
@@ -40,6 +58,64 @@ import {
40
58
  * console.log(`Selected: ${event.name}`);
41
59
  * });
42
60
  * ```
61
+ *
62
+ * @example
63
+ * ```typescript
64
+ * // Create a menu with nested submenus
65
+ * const menu = createMenu({
66
+ * items: [
67
+ * { name: 'edit', text: 'Edit' },
68
+ * {
69
+ * name: 'share',
70
+ * text: 'Share',
71
+ * items: [
72
+ * { name: 'email', text: 'Email' },
73
+ * { name: 'link', text: 'Copy Link' }
74
+ * ]
75
+ * },
76
+ * { type: 'divider' },
77
+ * { name: 'delete', text: 'Delete' }
78
+ * ],
79
+ * stayOpenOnSelect: true
80
+ * });
81
+ *
82
+ * // Add items dynamically
83
+ * menu.addItem({ name: 'newItem', text: 'New Item' });
84
+ * ```
85
+ *
86
+ * @example
87
+ * ```typescript
88
+ * // Using menu with custom positioning
89
+ * const menu = createMenu({
90
+ * items: [
91
+ * { name: 'cut', text: 'Cut' },
92
+ * { name: 'copy', text: 'Copy' },
93
+ * { name: 'paste', text: 'Paste' }
94
+ * ]
95
+ * });
96
+ *
97
+ * // Position with custom alignment
98
+ * contextButton.addEventListener('click', (event) => {
99
+ * menu.position(contextButton, {
100
+ * align: 'right',
101
+ * vAlign: 'top',
102
+ * offsetY: 8
103
+ * }).show();
104
+ *
105
+ * event.stopPropagation();
106
+ * });
107
+ *
108
+ * // Close menu when clicking outside
109
+ * document.addEventListener('click', () => {
110
+ * if (menu.isVisible()) {
111
+ * menu.hide();
112
+ * }
113
+ * });
114
+ * ```
115
+ */
116
+ /**
117
+ * @type {Function}
118
+ * @name createMenu
43
119
  */
44
120
  const createMenu = (config: MenuConfig = {}): MenuComponent => {
45
121
  const baseConfig = createBaseConfig(config);