mtrl 0.3.5 → 0.3.6
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/menu/api.ts +143 -268
- package/src/components/menu/config.ts +84 -40
- package/src/components/menu/features/anchor.ts +159 -0
- package/src/components/menu/features/controller.ts +970 -0
- package/src/components/menu/features/index.ts +4 -0
- package/src/components/menu/index.ts +31 -63
- package/src/components/menu/menu.ts +107 -97
- package/src/components/menu/types.ts +263 -447
- 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 +2 -1
- package/src/styles/components/_menu.scss +20 -8
- package/src/styles/main.scss +23 -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/{core/build → styles/utilities}/_ripple.scss +0 -0
package/src/core/layout/utils.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
// src/core/layout/utils.ts
|
|
2
2
|
/**
|
|
3
3
|
* @module core/layout
|
|
4
|
-
* @description
|
|
4
|
+
* @description Utility functions for layout creation
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { PREFIX } from '../config';
|
|
8
8
|
import { ComponentLike } from './types';
|
|
9
|
+
import { normalizeClasses as normalizeClassesUtil } from '../utils';
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* Checks if a value is a component object (has an element property)
|
|
12
|
-
* Optimized fast path check by only validating that element property exists
|
|
13
13
|
*
|
|
14
14
|
* @param value - Value to check
|
|
15
15
|
* @returns True if the value is a component-like object
|
|
@@ -29,9 +29,18 @@ export function createFragment(): DocumentFragment {
|
|
|
29
29
|
return document.createDocumentFragment();
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Normalizes class values into an array of strings
|
|
34
|
+
*/
|
|
35
|
+
export function normalizeClasses(...classes: (string | string[])[]): string[] {
|
|
36
|
+
return normalizeClassesUtil(...classes);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Constant for prefix with dash
|
|
40
|
+
const PREFIX_WITH_DASH = `${PREFIX}-`;
|
|
41
|
+
|
|
32
42
|
/**
|
|
33
43
|
* Processes className options to add prefix if needed
|
|
34
|
-
* Supports BEM naming conventions when enabled
|
|
35
44
|
*
|
|
36
45
|
* @param options - Element options
|
|
37
46
|
* @param skipPrefix - Whether to skip adding prefixes
|
|
@@ -43,76 +52,73 @@ export function processClassNames(
|
|
|
43
52
|
skipPrefix: boolean = false,
|
|
44
53
|
useBEM: boolean = false
|
|
45
54
|
): Record<string, any> {
|
|
46
|
-
// Fast path - if no options or skipping prefix, return
|
|
55
|
+
// Fast path - if no options or skipping prefix, return a new object
|
|
47
56
|
if (!options || skipPrefix) return { ...options };
|
|
48
57
|
|
|
49
|
-
//
|
|
58
|
+
// Avoid unnecessary clone if no class properties exist
|
|
59
|
+
const hasClassProps = options.class || options.className || options.rawClass;
|
|
60
|
+
if (!hasClassProps) return { ...options };
|
|
61
|
+
|
|
62
|
+
// Create clone only once
|
|
50
63
|
const processed = { ...options };
|
|
51
64
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
return cls;
|
|
65
|
+
// Unify class and className as aliases
|
|
66
|
+
if (processed.className) {
|
|
67
|
+
if (!processed.class) {
|
|
68
|
+
// Simple transfer if only className exists
|
|
69
|
+
processed.class = processed.className;
|
|
70
|
+
} else {
|
|
71
|
+
// Merge if both exist
|
|
72
|
+
const classNames = normalizeClasses([processed.class, processed.className]);
|
|
73
|
+
processed.class = classNames.join(' ');
|
|
62
74
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
if (cls.includes('__')) {
|
|
67
|
-
// This is a BEM element, prefix only the block part
|
|
68
|
-
const [block, element] = cls.split('__');
|
|
69
|
-
return `${PREFIX}-${block}__${element}`;
|
|
70
|
-
} else if (cls.includes('--')) {
|
|
71
|
-
// This is a BEM modifier, prefix only the block part
|
|
72
|
-
const [block, modifier] = cls.split('--');
|
|
73
|
-
return `${PREFIX}-${block}--${modifier}`;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Standard case - prefix the entire class name
|
|
78
|
-
return `${PREFIX}-${cls}`;
|
|
79
|
-
};
|
|
75
|
+
// Always remove className after processing
|
|
76
|
+
delete processed.className;
|
|
77
|
+
}
|
|
80
78
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
79
|
+
// Process prefixed classes
|
|
80
|
+
if (processed.class && !skipPrefix) {
|
|
81
|
+
// Handle string format
|
|
82
|
+
if (typeof processed.class === 'string') {
|
|
83
|
+
const classes = processed.class.split(/\s+/).filter(Boolean);
|
|
84
|
+
|
|
85
|
+
if (useBEM) {
|
|
86
|
+
// Handle BEM notation with special prefixing rules
|
|
87
|
+
processed.class = classes.map(cls => {
|
|
88
|
+
if (!cls || cls.startsWith(PREFIX_WITH_DASH)) return cls;
|
|
89
|
+
|
|
90
|
+
if (cls.includes('__')) {
|
|
91
|
+
const [block, element] = cls.split('__');
|
|
92
|
+
return `${PREFIX_WITH_DASH}${block}__${element}`;
|
|
93
|
+
} else if (cls.includes('--')) {
|
|
94
|
+
const [block, modifier] = cls.split('--');
|
|
95
|
+
return `${PREFIX_WITH_DASH}${block}--${modifier}`;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return `${PREFIX_WITH_DASH}${cls}`;
|
|
99
|
+
}).join(' ');
|
|
100
|
+
} else {
|
|
101
|
+
// Standard prefix handling
|
|
102
|
+
processed.class = classes.map(cls =>
|
|
103
|
+
cls && !cls.startsWith(PREFIX_WITH_DASH) ? `${PREFIX_WITH_DASH}${cls}` : cls
|
|
104
|
+
).filter(Boolean).join(' ');
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
// Handle array format
|
|
108
|
+
else if (Array.isArray(processed.class)) {
|
|
109
|
+
processed.class = processed.class
|
|
101
110
|
.filter(Boolean)
|
|
111
|
+
.map(cls => typeof cls === 'string' && !cls.startsWith(PREFIX_WITH_DASH) ?
|
|
112
|
+
`${PREFIX_WITH_DASH}${cls}` : cls)
|
|
102
113
|
.join(' ');
|
|
103
114
|
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// Process both possible class properties for compatibility
|
|
107
|
-
processProperty('class');
|
|
108
|
-
processProperty('className');
|
|
115
|
+
}
|
|
109
116
|
|
|
110
117
|
return processed;
|
|
111
118
|
}
|
|
112
119
|
|
|
113
120
|
/**
|
|
114
121
|
* Flattens a nested layout into a simple object with element and component references
|
|
115
|
-
* Optimized by using a direct property access loop and early exits
|
|
116
122
|
*
|
|
117
123
|
* @param layout - Layout object
|
|
118
124
|
* @returns Flattened layout with all elements and components
|
|
@@ -120,17 +126,19 @@ export function processClassNames(
|
|
|
120
126
|
export function flattenLayout(layout: Record<string, any>): Record<string, any> {
|
|
121
127
|
const flattened: Record<string, any> = {};
|
|
122
128
|
|
|
129
|
+
// Fast path - return empty object for empty layout
|
|
130
|
+
if (!layout || typeof layout !== 'object') return flattened;
|
|
131
|
+
|
|
123
132
|
for (const key in layout) {
|
|
124
133
|
const value = layout[key];
|
|
125
134
|
|
|
126
135
|
// Only include components, elements, and non-functions
|
|
127
|
-
// Fast path with fewer type checks
|
|
128
136
|
if (value &&
|
|
129
137
|
typeof value !== 'function' &&
|
|
130
|
-
(value instanceof HTMLElement ||
|
|
138
|
+
(value instanceof HTMLElement || isComponent(value))) {
|
|
131
139
|
flattened[key] = value;
|
|
132
140
|
}
|
|
133
141
|
}
|
|
134
142
|
|
|
135
143
|
return flattened;
|
|
136
|
-
}
|
|
144
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
// Direct component imports
|
|
9
|
-
import { createElement } from './core/dom
|
|
9
|
+
import { createElement, addClass, removeClass, hasClass, toggleClass } from './core/dom';
|
|
10
10
|
import createLayout from './core/layout';
|
|
11
11
|
import createBadge from './components/badge';
|
|
12
12
|
import createBottomAppBar from './components/bottom-app-bar';
|
|
@@ -45,6 +45,7 @@ import createTooltip from './components/tooltip';
|
|
|
45
45
|
|
|
46
46
|
// Export all "create*" functions
|
|
47
47
|
export {
|
|
48
|
+
addClass, removeClass, hasClass, toggleClass,
|
|
48
49
|
createElement,
|
|
49
50
|
createLayout,
|
|
50
51
|
createBadge,
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
// src/components/menu/_menu.scss
|
|
2
|
-
@use '../../styles/abstract/base' as base;
|
|
3
2
|
@use '../../styles/abstract/variables' as v;
|
|
4
|
-
@use '../../styles/abstract/functions' as f;
|
|
5
3
|
@use '../../styles/abstract/mixins' as m;
|
|
4
|
+
@use '../../styles/abstract/functions' as f;
|
|
6
5
|
@use '../../styles/abstract/theme' as t;
|
|
7
6
|
|
|
8
|
-
$component: '
|
|
7
|
+
$component: 'mtrl-menu';
|
|
9
8
|
|
|
10
9
|
.#{$component} {
|
|
11
10
|
// Base styles
|
|
@@ -21,19 +20,25 @@ $component: '#{base.$prefix}-menu';
|
|
|
21
20
|
color: t.color('on-surface');
|
|
22
21
|
@include m.elevation(2);
|
|
23
22
|
|
|
24
|
-
|
|
23
|
+
// Initial state - hidden
|
|
24
|
+
display: block;
|
|
25
25
|
opacity: 0;
|
|
26
26
|
transform: scale(0.8);
|
|
27
27
|
transform-origin: top left;
|
|
28
28
|
pointer-events: none;
|
|
29
|
+
visibility: hidden; // Added for better measurement handling
|
|
29
30
|
transition: opacity v.motion('duration-short2') v.motion('easing-standard'),
|
|
30
|
-
transform v.motion('duration-short2') v.motion('easing-standard')
|
|
31
|
+
transform v.motion('duration-short2') v.motion('easing-standard'),
|
|
32
|
+
visibility 0s linear v.motion('duration-short2'); // Delay visibility change
|
|
31
33
|
|
|
32
34
|
&--visible {
|
|
33
|
-
display: block;
|
|
34
35
|
opacity: 1;
|
|
35
36
|
transform: scale(1);
|
|
36
37
|
pointer-events: auto;
|
|
38
|
+
visibility: visible;
|
|
39
|
+
transition: opacity v.motion('duration-short2') v.motion('easing-standard'),
|
|
40
|
+
transform v.motion('duration-short2') v.motion('easing-standard'),
|
|
41
|
+
visibility 0s linear 0s; // No delay for visibility
|
|
37
42
|
}
|
|
38
43
|
|
|
39
44
|
&--submenu {
|
|
@@ -62,7 +67,6 @@ $component: '#{base.$prefix}-menu';
|
|
|
62
67
|
cursor: pointer;
|
|
63
68
|
user-select: none;
|
|
64
69
|
color: t.color('on-surface');
|
|
65
|
-
@include m.motion-transition(background-color);
|
|
66
70
|
|
|
67
71
|
&:hover {
|
|
68
72
|
@include m.state-layer(t.color('on-surface'), 'hover');
|
|
@@ -82,12 +86,20 @@ $component: '#{base.$prefix}-menu';
|
|
|
82
86
|
padding-right: 48px;
|
|
83
87
|
|
|
84
88
|
&::after {
|
|
85
|
-
|
|
89
|
+
content: '';
|
|
86
90
|
position: absolute;
|
|
87
91
|
right: 16px;
|
|
88
92
|
top: 50%;
|
|
89
93
|
transform: translateY(-50%);
|
|
94
|
+
width: 24px;
|
|
95
|
+
height: 24px;
|
|
96
|
+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='24' width='24' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='9 18 15 12 9 6'%3E%3C/polyline%3E%3C/svg%3E");
|
|
97
|
+
background-position: center;
|
|
98
|
+
background-repeat: no-repeat;
|
|
99
|
+
background-size: contain;
|
|
90
100
|
opacity: 0.87;
|
|
101
|
+
// This ensures the SVG inherits the exact same color as the menu item text
|
|
102
|
+
color: inherit;
|
|
91
103
|
}
|
|
92
104
|
|
|
93
105
|
&[aria-expanded="true"] {
|
package/src/styles/main.scss
CHANGED
|
@@ -37,29 +37,29 @@
|
|
|
37
37
|
@use './components/tabs' as tabs;
|
|
38
38
|
@use './components/top-app-bar' as top-app-bar;
|
|
39
39
|
|
|
40
|
-
@use '
|
|
41
|
-
@use '
|
|
42
|
-
@use '
|
|
43
|
-
@use '
|
|
44
|
-
@use '
|
|
45
|
-
@use '
|
|
46
|
-
@use '
|
|
47
|
-
@use '
|
|
48
|
-
@use '
|
|
49
|
-
@use '
|
|
50
|
-
@use '
|
|
51
|
-
|
|
52
|
-
@use '
|
|
53
|
-
|
|
54
|
-
@use '
|
|
55
|
-
@use '
|
|
56
|
-
@use '
|
|
57
|
-
|
|
58
|
-
@use '
|
|
59
|
-
@use '
|
|
60
|
-
@use '
|
|
61
|
-
|
|
62
|
-
@use '
|
|
40
|
+
@use './components/button' as button;
|
|
41
|
+
@use './components/fab' as fab;
|
|
42
|
+
@use './components/extended-fab' as extended-fab;
|
|
43
|
+
@use './components/segmented-button' as segmented-button;
|
|
44
|
+
@use './components/card' as card;
|
|
45
|
+
@use './components/carousel' as carousel;
|
|
46
|
+
@use './components/checkbox' as checkbox;
|
|
47
|
+
@use './components/chip' as chip;
|
|
48
|
+
@use './components/datepicker' as datepicker;
|
|
49
|
+
@use './components/dialog' as dialog;
|
|
50
|
+
@use './components/divider' as divider;
|
|
51
|
+
|
|
52
|
+
@use './components/progress' as progress;
|
|
53
|
+
|
|
54
|
+
@use './components/radios' as radios;
|
|
55
|
+
@use './components/timepicker' as timepicker;
|
|
56
|
+
@use './components/search' as search;
|
|
57
|
+
|
|
58
|
+
@use './components/snackbar' as snackbar;
|
|
59
|
+
@use './components/navigation' as navigation;
|
|
60
|
+
@use './components/list' as list;
|
|
61
|
+
|
|
62
|
+
@use './utilities/ripple';
|
|
63
63
|
|
|
64
64
|
// Initialize theme system
|
|
65
65
|
:root {
|