mtrl 0.3.5 → 0.3.7
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.
- package/package.json +1 -1
- package/src/components/button/api.ts +16 -0
- package/src/components/button/types.ts +9 -0
- package/src/components/menu/api.ts +144 -267
- package/src/components/menu/config.ts +84 -40
- package/src/components/menu/features/anchor.ts +243 -0
- package/src/components/menu/features/controller.ts +1167 -0
- package/src/components/menu/features/index.ts +5 -0
- package/src/components/menu/features/position.ts +353 -0
- package/src/components/menu/index.ts +31 -63
- package/src/components/menu/menu.ts +72 -104
- package/src/components/menu/types.ts +264 -447
- package/src/components/select/api.ts +78 -0
- package/src/components/select/config.ts +76 -0
- package/src/components/select/features.ts +317 -0
- package/src/components/select/index.ts +38 -0
- package/src/components/select/select.ts +73 -0
- package/src/components/select/types.ts +355 -0
- package/src/components/textfield/api.ts +78 -6
- package/src/components/textfield/features/index.ts +17 -0
- package/src/components/textfield/features/leading-icon.ts +127 -0
- package/src/components/textfield/features/placement.ts +149 -0
- package/src/components/textfield/features/prefix-text.ts +107 -0
- package/src/components/textfield/features/suffix-text.ts +100 -0
- package/src/components/textfield/features/supporting-text.ts +113 -0
- package/src/components/textfield/features/trailing-icon.ts +108 -0
- package/src/components/textfield/textfield.ts +51 -15
- package/src/components/textfield/types.ts +70 -0
- package/src/core/collection/adapters/base.ts +62 -0
- package/src/core/collection/collection.ts +300 -0
- package/src/core/collection/index.ts +57 -0
- package/src/core/collection/list-manager.ts +333 -0
- package/src/core/dom/classes.ts +81 -9
- package/src/core/dom/create.ts +30 -19
- package/src/core/layout/README.md +531 -166
- package/src/core/layout/array.ts +3 -4
- package/src/core/layout/config.ts +193 -0
- package/src/core/layout/create.ts +1 -2
- package/src/core/layout/index.ts +12 -2
- package/src/core/layout/object.ts +2 -3
- package/src/core/layout/processor.ts +60 -12
- package/src/core/layout/result.ts +1 -2
- package/src/core/layout/types.ts +105 -50
- package/src/core/layout/utils.ts +69 -61
- package/src/index.ts +6 -2
- package/src/styles/abstract/_variables.scss +18 -0
- package/src/styles/components/_button.scss +21 -5
- package/src/styles/components/{_chip.scss → _chips.scss} +118 -4
- package/src/styles/components/_menu.scss +109 -18
- package/src/styles/components/_select.scss +265 -0
- package/src/styles/components/_textfield.scss +233 -42
- package/src/styles/main.scss +24 -23
- package/src/styles/utilities/_layout.scss +665 -0
- package/src/components/menu/features/items-manager.ts +0 -457
- package/src/components/menu/features/keyboard-navigation.ts +0 -133
- package/src/components/menu/features/positioning.ts +0 -127
- package/src/components/menu/features/visibility.ts +0 -230
- package/src/components/menu/menu-item.ts +0 -86
- package/src/components/menu/utils.ts +0 -67
- package/src/components/textfield/features.ts +0 -322
- package/src/core/collection/adapters/base.js +0 -26
- package/src/core/collection/collection.js +0 -259
- package/src/core/collection/list-manager.js +0 -157
- /package/src/core/collection/adapters/{route.js → route.ts} +0 -0
- /package/src/{core/build → styles/utilities}/_ripple.scss +0 -0
|
@@ -1,150 +1,118 @@
|
|
|
1
1
|
// src/components/menu/menu.ts
|
|
2
|
+
|
|
2
3
|
import { pipe } from '../../core/compose';
|
|
3
4
|
import { createBase, withElement } from '../../core/compose/component';
|
|
4
5
|
import { withEvents, withLifecycle } from '../../core/compose/features';
|
|
6
|
+
import { withController, withAnchor } from './features';
|
|
7
|
+
import { withPosition } from './features/position';
|
|
5
8
|
import { withAPI } from './api';
|
|
6
|
-
import { withVisibility } from './features/visibility';
|
|
7
|
-
import { withItemsManager } from './features/items-manager';
|
|
8
|
-
import { withPositioning } from './features/positioning';
|
|
9
|
-
import { withKeyboardNavigation } from './features/keyboard-navigation';
|
|
10
9
|
import { MenuConfig, MenuComponent } from './types';
|
|
11
|
-
import {
|
|
12
|
-
createBaseConfig,
|
|
13
|
-
getElementConfig,
|
|
14
|
-
getApiConfig
|
|
15
|
-
} from './config';
|
|
10
|
+
import { createBaseConfig, getElementConfig, getApiConfig } from './config';
|
|
16
11
|
|
|
17
12
|
/**
|
|
18
|
-
* Creates a new Menu component
|
|
13
|
+
* Creates a new Menu component with the specified configuration.
|
|
14
|
+
*
|
|
15
|
+
* The Menu component implements the Material Design 3 menu specifications,
|
|
16
|
+
* providing a flexible dropdown menu system with support for nested menus,
|
|
17
|
+
* keyboard navigation, and ARIA accessibility.
|
|
18
|
+
*
|
|
19
|
+
* Menus are built using a functional composition pattern, applying various
|
|
20
|
+
* features through the pipe function for a modular architecture.
|
|
19
21
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* and support keyboard navigation.
|
|
22
|
+
* The menu element is not added to the DOM until it's opened, and it's removed
|
|
23
|
+
* from the DOM when closed, following best practices for dropdown menus.
|
|
23
24
|
*
|
|
24
|
-
*
|
|
25
|
+
* @param {MenuConfig} config - Configuration options for the menu
|
|
26
|
+
* This must include an anchor element or selector, and an array of menu items.
|
|
27
|
+
* See {@link MenuConfig} for all available options.
|
|
25
28
|
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
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
|
|
29
|
+
* @returns {MenuComponent} A fully configured menu component instance with
|
|
30
|
+
* all requested features applied. The returned component has methods for
|
|
31
|
+
* menu manipulation, event handling, and lifecycle management.
|
|
36
32
|
*
|
|
37
|
-
* @
|
|
38
|
-
*
|
|
33
|
+
* @throws {Error} Throws an error if menu creation fails or if required
|
|
34
|
+
* configuration (like anchor) is missing.
|
|
35
|
+
*
|
|
36
|
+
* @category Components
|
|
39
37
|
*
|
|
40
38
|
* @example
|
|
41
|
-
*
|
|
42
|
-
*
|
|
39
|
+
* // Create a simple menu anchored to a button
|
|
40
|
+
* const menuButton = document.getElementById('menu-button');
|
|
43
41
|
* const menu = createMenu({
|
|
42
|
+
* anchor: menuButton,
|
|
44
43
|
* items: [
|
|
45
|
-
* {
|
|
46
|
-
* {
|
|
44
|
+
* { id: 'item1', text: 'Option 1' },
|
|
45
|
+
* { id: 'item2', text: 'Option 2' },
|
|
47
46
|
* { type: 'divider' },
|
|
48
|
-
* {
|
|
47
|
+
* { id: 'item3', text: 'Option 3' }
|
|
49
48
|
* ]
|
|
50
49
|
* });
|
|
51
50
|
*
|
|
52
|
-
* //
|
|
53
|
-
* const button = document.getElementById('menuButton');
|
|
54
|
-
* menu.position(button).show();
|
|
55
|
-
*
|
|
56
|
-
* // Listen for item selection
|
|
51
|
+
* // Add event listener for item selection
|
|
57
52
|
* menu.on('select', (event) => {
|
|
58
|
-
* console.log(
|
|
53
|
+
* console.log('Selected item:', event.itemId);
|
|
59
54
|
* });
|
|
60
|
-
*
|
|
55
|
+
*
|
|
56
|
+
* // Menu will be added to the DOM when opened and removed when closed
|
|
57
|
+
* menuButton.addEventListener('click', () => menu.toggle());
|
|
61
58
|
*
|
|
62
59
|
* @example
|
|
63
|
-
* ```typescript
|
|
64
60
|
* // Create a menu with nested submenus
|
|
65
61
|
* const menu = createMenu({
|
|
62
|
+
* anchor: '#more-button',
|
|
66
63
|
* items: [
|
|
67
|
-
* {
|
|
64
|
+
* { id: 'edit', text: 'Edit', icon: '<svg>...</svg>' },
|
|
68
65
|
* {
|
|
69
|
-
*
|
|
66
|
+
* id: 'share',
|
|
70
67
|
* text: 'Share',
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
* {
|
|
68
|
+
* hasSubmenu: true,
|
|
69
|
+
* submenu: [
|
|
70
|
+
* { id: 'email', text: 'Email' },
|
|
71
|
+
* { id: 'link', text: 'Copy link' }
|
|
74
72
|
* ]
|
|
75
73
|
* },
|
|
76
74
|
* { type: 'divider' },
|
|
77
|
-
* {
|
|
75
|
+
* { id: 'delete', text: 'Delete', icon: '<svg>...</svg>' }
|
|
78
76
|
* ],
|
|
79
|
-
*
|
|
77
|
+
* position: 'bottom-end'
|
|
80
78
|
* });
|
|
81
79
|
*
|
|
82
|
-
* // Add items dynamically
|
|
83
|
-
* menu.addItem({ name: 'newItem', text: 'New Item' });
|
|
84
|
-
* ```
|
|
85
|
-
*
|
|
86
80
|
* @example
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
* items:
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
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();
|
|
81
|
+
* // Specify a custom position for the menu
|
|
82
|
+
* const filterMenu = createMenu({
|
|
83
|
+
* anchor: filterButton,
|
|
84
|
+
* items: filterOptions,
|
|
85
|
+
* position: MENU_POSITION.TOP_START,
|
|
86
|
+
* width: '240px',
|
|
87
|
+
* maxHeight: '400px'
|
|
106
88
|
* });
|
|
107
89
|
*
|
|
108
|
-
* //
|
|
109
|
-
*
|
|
110
|
-
* if (menu.isVisible()) {
|
|
111
|
-
* menu.hide();
|
|
112
|
-
* }
|
|
113
|
-
* });
|
|
114
|
-
* ```
|
|
90
|
+
* // Update the menu's position programmatically
|
|
91
|
+
* filterMenu.setPosition(MENU_POSITION.BOTTOM_END);
|
|
115
92
|
*/
|
|
116
|
-
|
|
117
|
-
* @type {Function}
|
|
118
|
-
* @name createMenu
|
|
119
|
-
*/
|
|
120
|
-
const createMenu = (config: MenuConfig = {}): MenuComponent => {
|
|
121
|
-
const baseConfig = createBaseConfig(config);
|
|
122
|
-
|
|
93
|
+
const createMenu = (config: MenuConfig): MenuComponent => {
|
|
123
94
|
try {
|
|
124
|
-
//
|
|
95
|
+
// Validate and create the base configuration
|
|
96
|
+
const baseConfig = createBaseConfig(config);
|
|
97
|
+
|
|
98
|
+
// Create the component through functional composition
|
|
125
99
|
const menu = pipe(
|
|
126
|
-
createBase,
|
|
127
|
-
withEvents(),
|
|
128
|
-
withElement(getElementConfig(baseConfig)),
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
comp => withAPI(getApiConfig(comp))(comp)
|
|
100
|
+
createBase, // Base component
|
|
101
|
+
withEvents(), // Event handling
|
|
102
|
+
withElement(getElementConfig(baseConfig)), // DOM element
|
|
103
|
+
withPosition(baseConfig), // Position management
|
|
104
|
+
withController(baseConfig), // Menu controller
|
|
105
|
+
withAnchor(baseConfig), // Anchor management
|
|
106
|
+
withLifecycle(), // Lifecycle management
|
|
107
|
+
comp => withAPI(getApiConfig(comp))(comp) // Public API
|
|
135
108
|
)(baseConfig);
|
|
136
|
-
|
|
137
|
-
//
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
if (menu.setCreateSubmenuFunction) {
|
|
141
|
-
menu.setCreateSubmenuFunction(createMenu);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
return menu as MenuComponent;
|
|
109
|
+
|
|
110
|
+
// The menu will be added to the DOM when opened and removed when closed
|
|
111
|
+
|
|
112
|
+
return menu;
|
|
145
113
|
} catch (error) {
|
|
146
|
-
console.error('Menu creation error:', error
|
|
147
|
-
throw new Error(`Failed to create menu: ${error
|
|
114
|
+
console.error('Menu creation error:', error);
|
|
115
|
+
throw new Error(`Failed to create menu: ${(error as Error).message}`);
|
|
148
116
|
}
|
|
149
117
|
};
|
|
150
118
|
|