mtrl 0.3.3 → 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/components/segmented-button/config.ts +59 -20
- package/src/components/segmented-button/index.ts +1 -1
- package/src/components/segmented-button/segment.ts +51 -97
- package/src/components/segmented-button/segmented-button.ts +114 -2
- package/src/components/segmented-button/types.ts +52 -0
- package/src/core/compose/features/icon.ts +15 -13
- 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/_button.scss +6 -0
- package/src/styles/components/_chip.scss +4 -5
- package/src/styles/components/_menu.scss +20 -8
- package/src/styles/components/_segmented-button.scss +173 -63
- 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,
|
|
@@ -124,7 +124,7 @@ $container: '#{base.$prefix}-chips';
|
|
|
124
124
|
background-color: t.color('on-surface');
|
|
125
125
|
opacity: 0.08;
|
|
126
126
|
pointer-events: none;
|
|
127
|
-
border-radius:
|
|
127
|
+
border-radius: v.chip('border-radius');
|
|
128
128
|
}
|
|
129
129
|
|
|
130
130
|
&:active::after {
|
|
@@ -253,7 +253,6 @@ $container: '#{base.$prefix}-chips';
|
|
|
253
253
|
|
|
254
254
|
// Filter chip
|
|
255
255
|
&--filter {
|
|
256
|
-
|
|
257
256
|
color: t.color('on-surface');
|
|
258
257
|
position: relative;
|
|
259
258
|
border: 1px solid t.alpha('outline', v.chip('outlined-border-opacity'));
|
|
@@ -266,7 +265,7 @@ $container: '#{base.$prefix}-chips';
|
|
|
266
265
|
background-color: t.color('on-surface');
|
|
267
266
|
opacity: 0.08;
|
|
268
267
|
pointer-events: none;
|
|
269
|
-
border-radius:
|
|
268
|
+
border-radius: calc(v.chip('border-radius') - 1px);
|
|
270
269
|
}
|
|
271
270
|
|
|
272
271
|
&.#{$component}--selected {
|
|
@@ -309,7 +308,7 @@ $container: '#{base.$prefix}-chips';
|
|
|
309
308
|
background-color: t.color('on-secondary-container');
|
|
310
309
|
opacity: 0.08;
|
|
311
310
|
pointer-events: none;
|
|
312
|
-
border-radius:
|
|
311
|
+
border-radius: calc(v.chip('border-radius') - 1px);
|
|
313
312
|
}
|
|
314
313
|
}
|
|
315
314
|
}
|
|
@@ -330,7 +329,7 @@ $container: '#{base.$prefix}-chips';
|
|
|
330
329
|
background-color: t.color('on-surface');
|
|
331
330
|
opacity: 0.08;
|
|
332
331
|
pointer-events: none;
|
|
333
|
-
border-radius:
|
|
332
|
+
border-radius: v.chip('border-radius');
|
|
334
333
|
}
|
|
335
334
|
|
|
336
335
|
&.#{$component}--selected {
|
|
@@ -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"] {
|
|
@@ -13,49 +13,103 @@ $component: '#{base.$prefix}-segmented-button';
|
|
|
13
13
|
display: inline-flex;
|
|
14
14
|
align-items: center;
|
|
15
15
|
justify-content: center;
|
|
16
|
-
|
|
17
|
-
border-radius: v.shape('full');
|
|
16
|
+
border-radius: 20px; // Half of height per MD3 specs
|
|
18
17
|
border: 1px solid t.color('outline');
|
|
19
18
|
background-color: transparent;
|
|
20
19
|
overflow: hidden;
|
|
21
20
|
|
|
22
|
-
//
|
|
21
|
+
// Density variables with defaults (Material Design 3 standard density)
|
|
22
|
+
--segment-height: 40px;
|
|
23
|
+
--segment-padding-x: 24px;
|
|
24
|
+
--segment-padding-icon-only: 12px;
|
|
25
|
+
--segment-padding-icon-text-left: 16px;
|
|
26
|
+
--segment-padding-icon-text-right: 24px;
|
|
27
|
+
--segment-icon-size: 18px;
|
|
28
|
+
--segment-text-size: 0.875rem;
|
|
29
|
+
--segment-border-radius: 20px;
|
|
30
|
+
|
|
31
|
+
// Set height from the CSS variable
|
|
32
|
+
height: var(--segment-height);
|
|
33
|
+
// Adjust border radius based on height
|
|
34
|
+
border-radius: calc(var(--segment-height) / 2);
|
|
35
|
+
|
|
36
|
+
// Comfortable density (medium)
|
|
37
|
+
&--comfortable {
|
|
38
|
+
--segment-height: 36px;
|
|
39
|
+
--segment-padding-x: 20px;
|
|
40
|
+
--segment-padding-icon-only: 10px;
|
|
41
|
+
--segment-padding-icon-text-left: 14px;
|
|
42
|
+
--segment-padding-icon-text-right: 20px;
|
|
43
|
+
--segment-icon-size: 16px;
|
|
44
|
+
--segment-text-size: 0.8125rem;
|
|
45
|
+
--segment-border-radius: 18px;
|
|
46
|
+
|
|
47
|
+
border-radius: var(--segment-border-radius);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Compact density (small)
|
|
51
|
+
&--compact {
|
|
52
|
+
--segment-height: 32px;
|
|
53
|
+
--segment-padding-x: 16px;
|
|
54
|
+
--segment-padding-icon-only: 8px;
|
|
55
|
+
--segment-padding-icon-text-left: 12px;
|
|
56
|
+
--segment-padding-icon-text-right: 16px;
|
|
57
|
+
--segment-icon-size: 16px;
|
|
58
|
+
--segment-text-size: 0.75rem;
|
|
59
|
+
--segment-border-radius: 16px;
|
|
60
|
+
|
|
61
|
+
border-radius: var(--segment-border-radius);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Disabled state for whole component
|
|
23
65
|
&--disabled {
|
|
24
66
|
opacity: 0.38;
|
|
25
67
|
pointer-events: none;
|
|
26
68
|
}
|
|
27
69
|
|
|
28
|
-
//
|
|
29
|
-
|
|
30
|
-
//
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
align-items: center;
|
|
34
|
-
justify-content: center;
|
|
35
|
-
height: 100%;
|
|
36
|
-
min-width: 48px;
|
|
37
|
-
padding: 0 12px;
|
|
38
|
-
border: none;
|
|
70
|
+
// Style for button elements used as segments
|
|
71
|
+
.#{base.$prefix}-button {
|
|
72
|
+
// Reset button styles that we don't want
|
|
73
|
+
margin: 0;
|
|
74
|
+
box-shadow: none;
|
|
39
75
|
background-color: transparent;
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
76
|
+
border: none;
|
|
77
|
+
position: relative; // For pseudo-elements
|
|
78
|
+
border-radius: 0; // Reset any border-radius
|
|
79
|
+
min-width: 48px;
|
|
80
|
+
height: 100%;
|
|
81
|
+
color: t.color('on-surface'); // Original color
|
|
82
|
+
|
|
83
|
+
// Use CSS variables for dynamic sizing based on density
|
|
84
|
+
padding: 0 var(--segment-padding-x);
|
|
43
85
|
|
|
44
|
-
//
|
|
45
|
-
|
|
46
|
-
|
|
86
|
+
// Icon-only segments have equal padding all around
|
|
87
|
+
&.#{base.$prefix}-button--circular {
|
|
88
|
+
padding: 0 var(--segment-padding-icon-only);
|
|
47
89
|
}
|
|
48
90
|
|
|
49
|
-
//
|
|
50
|
-
|
|
91
|
+
// Segments with both icon and text
|
|
92
|
+
&:has(.#{base.$prefix}-button-icon + .#{base.$prefix}-button-text) {
|
|
93
|
+
padding: 0 var(--segment-padding-icon-text-right) 0 var(--segment-padding-icon-text-left);
|
|
94
|
+
}
|
|
51
95
|
|
|
52
|
-
//
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
96
|
+
// Only add border radius to first and last segments
|
|
97
|
+
&:first-child {
|
|
98
|
+
border-top-left-radius: var(--segment-border-radius);
|
|
99
|
+
border-bottom-left-radius: var(--segment-border-radius);
|
|
100
|
+
}
|
|
57
101
|
|
|
58
|
-
|
|
102
|
+
&:last-child {
|
|
103
|
+
border-top-right-radius: var(--segment-border-radius);
|
|
104
|
+
border-bottom-right-radius: var(--segment-border-radius);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Hover state - keeping original color
|
|
108
|
+
&:hover:not([disabled]) {
|
|
109
|
+
background-color: t.alpha('on-surface', 0.08);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Focus state
|
|
59
113
|
&:focus {
|
|
60
114
|
outline: none;
|
|
61
115
|
}
|
|
@@ -65,53 +119,109 @@ $component: '#{base.$prefix}-segmented-button';
|
|
|
65
119
|
outline-offset: -2px;
|
|
66
120
|
}
|
|
67
121
|
|
|
68
|
-
|
|
69
|
-
|
|
122
|
+
// Replace border with pseudo-elements for better control
|
|
123
|
+
// Each segment has its own right border (except last)
|
|
124
|
+
&:not(:last-child)::after {
|
|
125
|
+
content: '';
|
|
126
|
+
position: absolute;
|
|
127
|
+
top: 0;
|
|
128
|
+
right: 0;
|
|
129
|
+
height: 100%;
|
|
130
|
+
width: 1px;
|
|
131
|
+
background-color: t.color('outline');
|
|
132
|
+
pointer-events: none;
|
|
70
133
|
}
|
|
71
134
|
|
|
72
|
-
//
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
135
|
+
// Disabled state handling
|
|
136
|
+
&[disabled] {
|
|
137
|
+
opacity: 0.38;
|
|
138
|
+
|
|
139
|
+
// When a disabled button has a right border, make it lower opacity
|
|
140
|
+
&::after {
|
|
141
|
+
background-color: t.alpha('outline', 0.38);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// When a disabled button is before a non-disabled button, let the non-disabled handle the border
|
|
145
|
+
+ .#{base.$prefix}-button:not([disabled])::before {
|
|
146
|
+
content: '';
|
|
147
|
+
position: absolute;
|
|
148
|
+
top: 0;
|
|
149
|
+
left: 0;
|
|
150
|
+
height: 100%;
|
|
151
|
+
width: 1px;
|
|
152
|
+
background-color: t.color('outline');
|
|
153
|
+
pointer-events: none;
|
|
154
|
+
}
|
|
76
155
|
|
|
77
|
-
|
|
78
|
-
|
|
156
|
+
// When a disabled button is after a non-disabled button, make the non-disabled button's border low opacity
|
|
157
|
+
&:not(:first-child) {
|
|
158
|
+
&::before {
|
|
159
|
+
content: '';
|
|
160
|
+
position: absolute;
|
|
161
|
+
top: 0;
|
|
162
|
+
left: 0;
|
|
163
|
+
height: 100%;
|
|
164
|
+
width: 1px;
|
|
165
|
+
background-color: t.alpha('outline', 0.38);
|
|
166
|
+
pointer-events: none;
|
|
167
|
+
}
|
|
79
168
|
}
|
|
80
169
|
}
|
|
81
170
|
|
|
82
|
-
//
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
171
|
+
// Ensure all button styles are reset appropriately
|
|
172
|
+
&.#{base.$prefix}-button--filled,
|
|
173
|
+
&.#{base.$prefix}-button--outlined,
|
|
174
|
+
&.#{base.$prefix}-button--tonal,
|
|
175
|
+
&.#{base.$prefix}-button--elevated,
|
|
176
|
+
&.#{base.$prefix}-button--text {
|
|
177
|
+
background-color: transparent;
|
|
178
|
+
box-shadow: none;
|
|
179
|
+
color: t.color('on-surface');
|
|
86
180
|
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Selected state
|
|
184
|
+
.#{base.$prefix}-segment--selected {
|
|
185
|
+
background-color: t.color('secondary-container');
|
|
186
|
+
color: t.color('on-secondary-container');
|
|
87
187
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
// For when both icon and text are present
|
|
91
|
-
margin: 0 auto;
|
|
188
|
+
&:hover:not([disabled]) {
|
|
189
|
+
background-color: t.alpha('secondary-container', 0.8);
|
|
92
190
|
}
|
|
93
191
|
|
|
94
|
-
//
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
svg {
|
|
103
|
-
width: 18px;
|
|
104
|
-
height: 18px;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
+ .#{$component}-segment-text {
|
|
108
|
-
margin-left: 8px;
|
|
109
|
-
}
|
|
192
|
+
// Ensure color change even with different button variants
|
|
193
|
+
&.#{base.$prefix}-button--filled,
|
|
194
|
+
&.#{base.$prefix}-button--outlined,
|
|
195
|
+
&.#{base.$prefix}-button--tonal,
|
|
196
|
+
&.#{base.$prefix}-button--elevated,
|
|
197
|
+
&.#{base.$prefix}-button--text {
|
|
198
|
+
background-color: t.color('secondary-container');
|
|
199
|
+
color: t.color('on-secondary-container');
|
|
110
200
|
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Ensure proper spacing in button contents
|
|
204
|
+
.#{base.$prefix}-button-text {
|
|
205
|
+
margin: 0;
|
|
206
|
+
white-space: nowrap;
|
|
207
|
+
@include m.typography('label-large');
|
|
208
|
+
// Apply density-specific font sizing
|
|
209
|
+
font-size: var(--segment-text-size);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
.#{base.$prefix}-button-icon + .#{base.$prefix}-button-text {
|
|
213
|
+
margin-left: 8px; // MD3 spec for space between icon and text
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Icon sizing per MD3
|
|
217
|
+
.#{base.$prefix}-button-icon {
|
|
218
|
+
display: flex;
|
|
219
|
+
align-items: center;
|
|
220
|
+
justify-content: center;
|
|
111
221
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
222
|
+
svg {
|
|
223
|
+
width: var(--segment-icon-size);
|
|
224
|
+
height: var(--segment-icon-size);
|
|
115
225
|
}
|
|
116
226
|
}
|
|
117
227
|
}
|
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 {
|