mtrl 0.2.8 → 0.3.0
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/index.ts +4 -0
- package/package.json +1 -1
- package/src/components/button/button.ts +34 -5
- package/src/components/navigation/api.ts +131 -96
- package/src/components/navigation/features/controller.ts +273 -0
- package/src/components/navigation/features/items.ts +133 -64
- package/src/components/navigation/navigation.ts +17 -2
- package/src/components/navigation/system/core.ts +302 -0
- package/src/components/navigation/system/events.ts +240 -0
- package/src/components/navigation/system/index.ts +184 -0
- package/src/components/navigation/system/mobile.ts +278 -0
- package/src/components/navigation/system/state.ts +77 -0
- package/src/components/navigation/system/types.ts +364 -0
- package/src/components/slider/config.ts +20 -2
- package/src/components/slider/features/controller.ts +737 -0
- package/src/components/slider/features/handlers.ts +18 -16
- package/src/components/slider/features/index.ts +3 -2
- package/src/components/slider/features/range.ts +104 -0
- package/src/components/slider/schema.ts +141 -0
- package/src/components/slider/slider.ts +34 -13
- package/src/components/switch/api.ts +16 -0
- package/src/components/switch/config.ts +1 -18
- package/src/components/switch/features.ts +198 -0
- package/src/components/switch/index.ts +1 -0
- package/src/components/switch/switch.ts +3 -3
- package/src/components/switch/types.ts +14 -2
- package/src/components/textfield/api.ts +53 -0
- package/src/components/textfield/features.ts +322 -0
- package/src/components/textfield/textfield.ts +8 -0
- package/src/components/textfield/types.ts +12 -3
- package/src/components/timepicker/clockdial.ts +1 -4
- package/src/core/compose/features/textinput.ts +15 -2
- package/src/core/composition/features/dom.ts +45 -0
- package/src/core/composition/features/icon.ts +131 -0
- package/src/core/composition/features/index.ts +12 -0
- package/src/core/composition/features/label.ts +155 -0
- package/src/core/composition/features/layout.ts +47 -0
- package/src/core/composition/index.ts +26 -0
- package/src/core/index.ts +1 -1
- package/src/core/layout/README.md +350 -0
- package/src/core/layout/array.ts +181 -0
- package/src/core/layout/create.ts +55 -0
- package/src/core/layout/index.ts +26 -0
- package/src/core/layout/object.ts +124 -0
- package/src/core/layout/processor.ts +58 -0
- package/src/core/layout/result.ts +85 -0
- package/src/core/layout/types.ts +125 -0
- package/src/core/layout/utils.ts +136 -0
- package/src/index.ts +1 -0
- package/src/styles/abstract/_variables.scss +28 -0
- package/src/styles/components/_navigation-mobile.scss +244 -0
- package/src/styles/components/_navigation-system.scss +151 -0
- package/src/styles/components/_switch.scss +133 -69
- package/src/styles/components/_textfield.scss +259 -27
- package/demo/build.ts +0 -349
- package/demo/index.html +0 -110
- package/demo/main.js +0 -448
- package/demo/styles.css +0 -239
- package/server.ts +0 -86
- package/src/components/slider/features/slider.ts +0 -318
- package/src/components/slider/features/structure.ts +0 -181
- package/src/components/slider/features/ui.ts +0 -388
- package/src/components/textfield/constants.ts +0 -100
- package/src/core/layout/index.js +0 -95
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
// src/components/navigation/_mobile.scss
|
|
2
|
+
@use '../../styles/abstract/variables' as v;
|
|
3
|
+
@use '../../styles/abstract/mixins' as m;
|
|
4
|
+
@use '../../styles/abstract/base' as base;
|
|
5
|
+
@use '../../styles/abstract/theme' as t;
|
|
6
|
+
|
|
7
|
+
$prefix: base.$prefix;
|
|
8
|
+
|
|
9
|
+
// Mobile navigation overlay
|
|
10
|
+
.#{$prefix}-nav-overlay {
|
|
11
|
+
position: fixed;
|
|
12
|
+
top: 0;
|
|
13
|
+
left: 0;
|
|
14
|
+
right: 0;
|
|
15
|
+
bottom: 0;
|
|
16
|
+
background-color: t.alpha('shadow', 0.5);
|
|
17
|
+
z-index: v.z-index('modal') - 1; // Just below modal level but above most content
|
|
18
|
+
opacity: 0;
|
|
19
|
+
visibility: hidden;
|
|
20
|
+
transition: opacity 0.3s v.motion('easing-standard'), visibility 0.3s v.motion('easing-standard');
|
|
21
|
+
|
|
22
|
+
// Add backdrop blur for a modern effect where supported
|
|
23
|
+
@supports (backdrop-filter: blur(4px)) {
|
|
24
|
+
backdrop-filter: blur(4px);
|
|
25
|
+
background-color: t.alpha('shadow', 0.4);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
&.active {
|
|
29
|
+
opacity: 1;
|
|
30
|
+
visibility: visible;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Close button for mobile drawer
|
|
35
|
+
.#{$prefix}-nav-close-btn {
|
|
36
|
+
position: absolute;
|
|
37
|
+
top: 12px;
|
|
38
|
+
right: 12px;
|
|
39
|
+
width: 40px;
|
|
40
|
+
height: 40px;
|
|
41
|
+
border-radius: 50%;
|
|
42
|
+
background-color: transparent;
|
|
43
|
+
border: none;
|
|
44
|
+
cursor: pointer;
|
|
45
|
+
display: none; // Hidden by default, shown on mobile
|
|
46
|
+
align-items: center;
|
|
47
|
+
justify-content: center;
|
|
48
|
+
color: t.color('on-surface');
|
|
49
|
+
z-index: 1;
|
|
50
|
+
-webkit-tap-highlight-color: transparent; // Removes default mobile tap highlight
|
|
51
|
+
|
|
52
|
+
&:hover {
|
|
53
|
+
background-color: t.alpha('on-surface', 0.05);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Touch feedback state
|
|
57
|
+
&.active {
|
|
58
|
+
background-color: t.alpha('on-surface', 0.12);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
&:focus-visible {
|
|
62
|
+
outline: 2px solid t.color('primary');
|
|
63
|
+
outline-offset: 2px;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
svg {
|
|
67
|
+
width: 24px;
|
|
68
|
+
height: 24px;
|
|
69
|
+
stroke: currentColor;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Ensure WCAG-compliant touch target size
|
|
73
|
+
@media (pointer: coarse) {
|
|
74
|
+
min-width: 48px;
|
|
75
|
+
min-height: 48px;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Body class to prevent scrolling when drawer is open
|
|
80
|
+
.#{$prefix}-body-drawer-open {
|
|
81
|
+
overflow: hidden;
|
|
82
|
+
|
|
83
|
+
// On iOS we need to handle the body position differently
|
|
84
|
+
@supports (-webkit-overflow-scrolling: touch) {
|
|
85
|
+
position: fixed;
|
|
86
|
+
width: 100%;
|
|
87
|
+
height: 100%;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Responsive behavior for navigation system - apply to small screens or touch devices
|
|
92
|
+
@media (max-width: 960px), (pointer: coarse) {
|
|
93
|
+
// Rail navigation
|
|
94
|
+
.#{$prefix}-nav--rail {
|
|
95
|
+
width: 56px;
|
|
96
|
+
z-index: v.z-index('modal') - 1;
|
|
97
|
+
border-right: 1px solid t.color('outline-variant');
|
|
98
|
+
|
|
99
|
+
// Hide labels on mobile
|
|
100
|
+
.#{$prefix}-nav-label {
|
|
101
|
+
display: none;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Center align items
|
|
105
|
+
.#{$prefix}-nav-item {
|
|
106
|
+
justify-content: center;
|
|
107
|
+
height: 56px;
|
|
108
|
+
-webkit-tap-highlight-color: transparent; // Remove default mobile highlight
|
|
109
|
+
|
|
110
|
+
// Increase touch targets for better accessibility
|
|
111
|
+
@media (pointer: coarse) {
|
|
112
|
+
min-height: 48px;
|
|
113
|
+
padding: 12px 8px;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Touch-friendly active state
|
|
117
|
+
&:active {
|
|
118
|
+
background-color: t.alpha('on-surface', 0.12);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Larger icons on mobile
|
|
123
|
+
.#{$prefix}-nav-icon {
|
|
124
|
+
font-size: 1.25rem;
|
|
125
|
+
|
|
126
|
+
// Ensure icon touch target meets WCAG requirements
|
|
127
|
+
@media (pointer: coarse) {
|
|
128
|
+
min-width: 44px;
|
|
129
|
+
min-height: 44px;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Drawer navigation
|
|
135
|
+
.#{$prefix}-nav--drawer {
|
|
136
|
+
position: fixed;
|
|
137
|
+
top: 0;
|
|
138
|
+
bottom: 0;
|
|
139
|
+
left: 0;
|
|
140
|
+
width: 280px;
|
|
141
|
+
max-width: 85vw;
|
|
142
|
+
z-index: v.z-index('modal');
|
|
143
|
+
background-color: t.color('surface');
|
|
144
|
+
box-shadow: v.elevation('level-3');
|
|
145
|
+
transform: translateX(-100%);
|
|
146
|
+
transition: transform 0.3s v.motion('easing-standard');
|
|
147
|
+
overflow-y: auto;
|
|
148
|
+
-webkit-overflow-scrolling: touch; // Smooth scrolling on iOS
|
|
149
|
+
|
|
150
|
+
&:not(.#{$prefix}-nav--hidden) {
|
|
151
|
+
transform: translateX(0);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Optimize drawer items for touch
|
|
155
|
+
.#{$prefix}-nav-item {
|
|
156
|
+
-webkit-tap-highlight-color: transparent;
|
|
157
|
+
|
|
158
|
+
@media (pointer: coarse) {
|
|
159
|
+
min-height: 48px;
|
|
160
|
+
padding: 12px 16px;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Touch feedback
|
|
164
|
+
&:active {
|
|
165
|
+
background-color: t.alpha('on-surface', 0.12);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Support RTL languages
|
|
170
|
+
@include m.rtl {
|
|
171
|
+
left: auto;
|
|
172
|
+
right: 0;
|
|
173
|
+
transform: translateX(100%);
|
|
174
|
+
|
|
175
|
+
&:not(.#{$prefix}-nav--hidden) {
|
|
176
|
+
transform: translateX(0);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Desktop behavior
|
|
183
|
+
@media (min-width: 961px) {
|
|
184
|
+
.#{$prefix}-nav--drawer {
|
|
185
|
+
// For desktop, transition by width instead of transform
|
|
186
|
+
transition: width 0.3s v.motion('easing-standard'), opacity 0.3s v.motion('easing-standard');
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.#{$prefix}-nav-close-btn {
|
|
190
|
+
display: none !important; // Always hidden on desktop
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Hide overlay on desktop
|
|
194
|
+
.#{$prefix}-nav-overlay.active {
|
|
195
|
+
opacity: 0;
|
|
196
|
+
visibility: hidden;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Safe area insets for notched devices like iPhone X and newer
|
|
201
|
+
@supports (padding-bottom: env(safe-area-inset-bottom)) {
|
|
202
|
+
.#{$prefix}-nav--drawer {
|
|
203
|
+
padding-top: env(safe-area-inset-top);
|
|
204
|
+
padding-bottom: env(safe-area-inset-bottom);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
.#{$prefix}-nav--rail {
|
|
208
|
+
padding-top: env(safe-area-inset-top);
|
|
209
|
+
padding-bottom: env(safe-area-inset-bottom);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
.#{$prefix}-nav--bar {
|
|
213
|
+
// For bottom navigation bars
|
|
214
|
+
&.#{$prefix}-nav--bottom {
|
|
215
|
+
padding-bottom: env(safe-area-inset-bottom);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Gesture hints for touch interfaces
|
|
221
|
+
@media (pointer: coarse) {
|
|
222
|
+
// Add visual affordance for swipe gesture
|
|
223
|
+
.#{$prefix}-nav--drawer:not(.#{$prefix}-nav--hidden) {
|
|
224
|
+
&::after {
|
|
225
|
+
content: '';
|
|
226
|
+
position: absolute;
|
|
227
|
+
top: 50%;
|
|
228
|
+
right: 0;
|
|
229
|
+
width: 4px;
|
|
230
|
+
height: 40px;
|
|
231
|
+
border-radius: 4px 0 0 4px;
|
|
232
|
+
background-color: t.alpha('on-surface', 0.1);
|
|
233
|
+
transform: translateY(-50%);
|
|
234
|
+
opacity: 0.5;
|
|
235
|
+
// Hide after interaction to avoid cognitive load
|
|
236
|
+
animation: fadeOutAfterDelay 4s forwards;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
@keyframes fadeOutAfterDelay {
|
|
241
|
+
0%, 50% { opacity: 0.5; }
|
|
242
|
+
100% { opacity: 0; }
|
|
243
|
+
}
|
|
244
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
// src/components/navigation/system.scss
|
|
2
|
+
|
|
3
|
+
// Navigation system specific styles
|
|
4
|
+
// These complement the existing navigation component styles
|
|
5
|
+
|
|
6
|
+
$nav-animation-duration: 0.2s;
|
|
7
|
+
$nav-animation-function: cubic-bezier(0.4, 0, 0.2, 1);
|
|
8
|
+
|
|
9
|
+
// Drawer animation states
|
|
10
|
+
.mtrl-nav--drawer {
|
|
11
|
+
transition: transform $nav-animation-duration $nav-animation-function,
|
|
12
|
+
opacity $nav-animation-duration $nav-animation-function;
|
|
13
|
+
|
|
14
|
+
// Hidden state (transform off-screen)
|
|
15
|
+
&.mtrl-nav--hidden {
|
|
16
|
+
transform: translateX(-100%);
|
|
17
|
+
opacity: 0;
|
|
18
|
+
pointer-events: none;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Right positioned drawer
|
|
22
|
+
&.mtrl-nav--right {
|
|
23
|
+
&.mtrl-nav--hidden {
|
|
24
|
+
transform: translateX(100%);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Animation classes
|
|
29
|
+
&.mtrl-nav--animate-in {
|
|
30
|
+
animation: nav-drawer-in $nav-animation-duration $nav-animation-function;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
&.mtrl-nav--animate-out {
|
|
34
|
+
animation: nav-drawer-out $nav-animation-duration $nav-animation-function;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Navigation system container (optional - for wrapping rail + drawer)
|
|
39
|
+
.mtrl-nav-system {
|
|
40
|
+
display: flex;
|
|
41
|
+
position: relative;
|
|
42
|
+
|
|
43
|
+
// Rail is fixed width
|
|
44
|
+
.mtrl-nav--rail {
|
|
45
|
+
flex: 0 0 auto;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Drawer expands
|
|
49
|
+
.mtrl-nav--drawer {
|
|
50
|
+
flex: 1 0 auto;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Drawer entry animation
|
|
55
|
+
@keyframes nav-drawer-in {
|
|
56
|
+
0% {
|
|
57
|
+
transform: translateX(-20px);
|
|
58
|
+
opacity: 0;
|
|
59
|
+
}
|
|
60
|
+
100% {
|
|
61
|
+
transform: translateX(0);
|
|
62
|
+
opacity: 1;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Drawer exit animation
|
|
67
|
+
@keyframes nav-drawer-out {
|
|
68
|
+
0% {
|
|
69
|
+
transform: translateX(0);
|
|
70
|
+
opacity: 1;
|
|
71
|
+
}
|
|
72
|
+
100% {
|
|
73
|
+
transform: translateX(-20px);
|
|
74
|
+
opacity: 0;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Right-side drawer animations
|
|
79
|
+
.mtrl-nav--drawer.mtrl-nav--right {
|
|
80
|
+
&.mtrl-nav--animate-in {
|
|
81
|
+
animation: nav-drawer-right-in $nav-animation-duration $nav-animation-function;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
&.mtrl-nav--animate-out {
|
|
85
|
+
animation: nav-drawer-right-out $nav-animation-duration $nav-animation-function;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
@keyframes nav-drawer-right-in {
|
|
90
|
+
0% {
|
|
91
|
+
transform: translateX(20px);
|
|
92
|
+
opacity: 0;
|
|
93
|
+
}
|
|
94
|
+
100% {
|
|
95
|
+
transform: translateX(0);
|
|
96
|
+
opacity: 1;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
@keyframes nav-drawer-right-out {
|
|
101
|
+
0% {
|
|
102
|
+
transform: translateX(0);
|
|
103
|
+
opacity: 1;
|
|
104
|
+
}
|
|
105
|
+
100% {
|
|
106
|
+
transform: translateX(20px);
|
|
107
|
+
opacity: 0;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Mobile optimizations
|
|
112
|
+
@media (max-width: 960px) {
|
|
113
|
+
.mtrl-nav-system {
|
|
114
|
+
flex-direction: column;
|
|
115
|
+
|
|
116
|
+
.mtrl-nav--rail {
|
|
117
|
+
// Mobile rail can be top or bottom
|
|
118
|
+
&.mtrl-nav--top, &.mtrl-nav--bottom {
|
|
119
|
+
width: 100%;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.mtrl-nav--drawer {
|
|
124
|
+
// Mobile drawer takes full width
|
|
125
|
+
width: 100%;
|
|
126
|
+
max-width: 100%;
|
|
127
|
+
|
|
128
|
+
&.mtrl-nav--hidden {
|
|
129
|
+
transform: translateY(-100%);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
&.mtrl-nav--bottom {
|
|
133
|
+
&.mtrl-nav--hidden {
|
|
134
|
+
transform: translateY(100%);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Focus and accessibility enhancements
|
|
142
|
+
.mtrl-nav-system {
|
|
143
|
+
[role="navigation"] {
|
|
144
|
+
&:focus-within {
|
|
145
|
+
outline: 2px solid var(--mtrl-primary-color, #6200ee);
|
|
146
|
+
outline-offset: -2px;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
@use 'navigation-mobile'
|
|
@@ -10,12 +10,36 @@ $component: '#{base.$prefix}-switch';
|
|
|
10
10
|
.#{$component} {
|
|
11
11
|
// Base styles
|
|
12
12
|
display: inline-flex;
|
|
13
|
-
|
|
13
|
+
flex-direction: column;
|
|
14
14
|
position: relative;
|
|
15
|
-
|
|
15
|
+
width: 100%;
|
|
16
16
|
padding: 4px 0;
|
|
17
17
|
user-select: none;
|
|
18
18
|
|
|
19
|
+
// Container for switch and label
|
|
20
|
+
&-container {
|
|
21
|
+
display: flex;
|
|
22
|
+
flex-direction: row;
|
|
23
|
+
justify-content: space-between;
|
|
24
|
+
align-items: center;
|
|
25
|
+
min-height: 48px;
|
|
26
|
+
width: 100%;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Content wrapper
|
|
30
|
+
&-content {
|
|
31
|
+
display: flex;
|
|
32
|
+
flex-direction: column;
|
|
33
|
+
flex-grow: 1;
|
|
34
|
+
margin-right: 12px;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Label
|
|
38
|
+
&-label {
|
|
39
|
+
@include m.typography('title-medium');
|
|
40
|
+
color: t.color('on-surface');
|
|
41
|
+
}
|
|
42
|
+
|
|
19
43
|
// Input (visually hidden but accessible)
|
|
20
44
|
&-input {
|
|
21
45
|
position: absolute;
|
|
@@ -46,6 +70,24 @@ $component: '#{base.$prefix}-switch';
|
|
|
46
70
|
background-color: t.color('surface-container-highest');
|
|
47
71
|
border: 2px solid t.color('outline');
|
|
48
72
|
@include m.motion-transition(background-color, border-color, outline);
|
|
73
|
+
|
|
74
|
+
// Ripple effect (consistent size for both hover and press)
|
|
75
|
+
&::after {
|
|
76
|
+
content: '';
|
|
77
|
+
position: absolute;
|
|
78
|
+
width: 40px;
|
|
79
|
+
height: 40px;
|
|
80
|
+
top: -6px;
|
|
81
|
+
background-color: transparent;
|
|
82
|
+
opacity: 0;
|
|
83
|
+
border-radius: 50%;
|
|
84
|
+
z-index: 0;
|
|
85
|
+
transform: translateX(-6px);
|
|
86
|
+
transition: opacity v.motion('duration-medium1') v.motion('easing-emphasized'),
|
|
87
|
+
background-color v.motion('duration-medium1') v.motion('easing-emphasized'),
|
|
88
|
+
transform v.motion('duration-medium1') cubic-bezier(0.2, 1.4, 0.35, 1);
|
|
89
|
+
pointer-events: none;
|
|
90
|
+
}
|
|
49
91
|
}
|
|
50
92
|
|
|
51
93
|
// Switch thumb (slider)
|
|
@@ -58,10 +100,17 @@ $component: '#{base.$prefix}-switch';
|
|
|
58
100
|
border-radius: 50%;
|
|
59
101
|
background-color: t.color('outline');
|
|
60
102
|
transform: translateX(0);
|
|
61
|
-
transition:
|
|
103
|
+
transition: transform v.motion('duration-medium1') cubic-bezier(0.2, 1.4, 0.35, 1),
|
|
104
|
+
background-color v.motion('duration-medium1') v.motion('easing-emphasized'),
|
|
105
|
+
width v.motion('duration-medium1') v.motion('easing-emphasized'),
|
|
106
|
+
height v.motion('duration-medium1') v.motion('easing-emphasized'),
|
|
107
|
+
top v.motion('duration-medium1') v.motion('easing-emphasized'),
|
|
108
|
+
left v.motion('duration-medium1') v.motion('easing-emphasized');
|
|
62
109
|
display: flex;
|
|
63
110
|
align-items: center;
|
|
64
111
|
justify-content: center;
|
|
112
|
+
z-index: 1; /* Ensure thumb is above ripple */
|
|
113
|
+
pointer-events: none;
|
|
65
114
|
|
|
66
115
|
// Icon inside thumb
|
|
67
116
|
&-icon {
|
|
@@ -80,44 +129,25 @@ $component: '#{base.$prefix}-switch';
|
|
|
80
129
|
}
|
|
81
130
|
}
|
|
82
131
|
|
|
83
|
-
//
|
|
84
|
-
&-
|
|
85
|
-
@include m.typography('body-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
// Label position variants
|
|
91
|
-
&--label-start {
|
|
92
|
-
.#{$component}-label {
|
|
93
|
-
margin-left: 0;
|
|
94
|
-
margin-right: 12px;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
&--label-end {
|
|
99
|
-
flex-direction: row;
|
|
100
|
-
|
|
101
|
-
.#{$component}-label {
|
|
102
|
-
margin-left: 12px;
|
|
103
|
-
margin-right: 0;
|
|
132
|
+
// Supporting text
|
|
133
|
+
&-helper {
|
|
134
|
+
@include m.typography('body-medium');
|
|
135
|
+
color: t.color('on-surface-variant');
|
|
136
|
+
|
|
137
|
+
&--error {
|
|
138
|
+
color: t.color('error');
|
|
104
139
|
}
|
|
105
140
|
}
|
|
106
141
|
|
|
107
142
|
// RTL support
|
|
108
143
|
@include m.rtl {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
margin-left: 12px;
|
|
112
|
-
margin-right: 0;
|
|
113
|
-
}
|
|
144
|
+
&-container {
|
|
145
|
+
flex-direction: row;
|
|
114
146
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
margin-right: 12px;
|
|
120
|
-
}
|
|
147
|
+
|
|
148
|
+
&-content {
|
|
149
|
+
margin-left: 12px;
|
|
150
|
+
margin-right: 0;
|
|
121
151
|
}
|
|
122
152
|
|
|
123
153
|
// Adjust thumb movement for RTL layout
|
|
@@ -131,6 +161,10 @@ $component: '#{base.$prefix}-switch';
|
|
|
131
161
|
.#{$component}-track {
|
|
132
162
|
background-color: t.color('primary');
|
|
133
163
|
border-color: t.color('primary');
|
|
164
|
+
|
|
165
|
+
&::after {
|
|
166
|
+
transform: translateX(14px);
|
|
167
|
+
}
|
|
134
168
|
}
|
|
135
169
|
|
|
136
170
|
.#{$component}-thumb {
|
|
@@ -148,9 +182,22 @@ $component: '#{base.$prefix}-switch';
|
|
|
148
182
|
}
|
|
149
183
|
}
|
|
150
184
|
|
|
185
|
+
// Error state
|
|
186
|
+
&--error {
|
|
187
|
+
.#{$component}-track {
|
|
188
|
+
border-color: t.color('error');
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
151
192
|
// Disabled state
|
|
152
193
|
&--disabled {
|
|
153
|
-
|
|
194
|
+
.#{$component}-track {
|
|
195
|
+
opacity: 0.38;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
.#{$component}-label {
|
|
199
|
+
opacity: 0.38;
|
|
200
|
+
}
|
|
154
201
|
|
|
155
202
|
// Specific styles for disabled + checked
|
|
156
203
|
&.#{$component}--checked {
|
|
@@ -161,53 +208,70 @@ $component: '#{base.$prefix}-switch';
|
|
|
161
208
|
}
|
|
162
209
|
|
|
163
210
|
.#{$component}-thumb {
|
|
164
|
-
background-color: t.color('
|
|
165
|
-
opacity:
|
|
211
|
+
background-color: t.color('surface-dim');
|
|
212
|
+
opacity: 0.38;
|
|
166
213
|
&-icon {
|
|
167
|
-
color: t.color('
|
|
214
|
+
color: t.color('on-surface');
|
|
168
215
|
}
|
|
169
216
|
}
|
|
170
217
|
}
|
|
171
218
|
}
|
|
172
219
|
|
|
173
|
-
//
|
|
174
|
-
&:not(
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
top: -8px;
|
|
180
|
-
left: -8px;
|
|
181
|
-
right: -8px;
|
|
182
|
-
bottom: -8px;
|
|
183
|
-
background-color: t.color('on-surface');
|
|
184
|
-
opacity: v.state('hover-state-layer-opacity');
|
|
185
|
-
border-radius: 20px;
|
|
186
|
-
}
|
|
220
|
+
// Interactive states for enabled switches
|
|
221
|
+
&:not(.#{$component}--disabled) {
|
|
222
|
+
// Remove track hover effects
|
|
223
|
+
.#{$component}-input:hover + .#{$component}-track::before,
|
|
224
|
+
.#{$component}-input:active + .#{$component}-track::before {
|
|
225
|
+
display: none;
|
|
187
226
|
}
|
|
188
|
-
|
|
189
|
-
//
|
|
190
|
-
.#{$component}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
227
|
+
|
|
228
|
+
// Handle hover and active states for both unchecked
|
|
229
|
+
&:not(.#{$component}--checked) {
|
|
230
|
+
.#{$component}-input:hover ~ .#{$component}-track,
|
|
231
|
+
.#{$component}-input:active ~ .#{$component}-track {
|
|
232
|
+
.#{$component}-thumb {
|
|
233
|
+
background-color: t.color('on-surface-variant');
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
&::after {
|
|
237
|
+
background-color: t.color('on-surface');
|
|
238
|
+
opacity: v.state('hover-state-layer-opacity');
|
|
239
|
+
transform: translateX(-6px);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
.#{$component}-input:active ~ .#{$component}-track::after {
|
|
199
244
|
opacity: v.state('pressed-state-layer-opacity');
|
|
200
|
-
border-radius: 20px;
|
|
201
245
|
}
|
|
202
246
|
}
|
|
203
247
|
|
|
204
|
-
// Hover/active states
|
|
248
|
+
// Hover/active states for checked
|
|
205
249
|
&.#{$component}--checked {
|
|
206
|
-
.#{$component}-input:hover
|
|
207
|
-
.#{$component}-input:active
|
|
208
|
-
|
|
250
|
+
.#{$component}-input:hover ~ .#{$component}-track,
|
|
251
|
+
.#{$component}-input:active ~ .#{$component}-track {
|
|
252
|
+
&::after {
|
|
253
|
+
background-color: t.color('primary');
|
|
254
|
+
opacity: v.state('hover-state-layer-opacity');
|
|
255
|
+
transform: translateX(14px);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
.#{$component}-input:active ~ .#{$component}-track::after {
|
|
260
|
+
opacity: v.state('pressed-state-layer-opacity');
|
|
209
261
|
}
|
|
210
262
|
}
|
|
263
|
+
|
|
264
|
+
// Expand thumb on press for both checked and unchecked
|
|
265
|
+
.#{$component}-input:active ~ .#{$component}-track .#{$component}-thumb {
|
|
266
|
+
width: 28px;
|
|
267
|
+
height: 28px;
|
|
268
|
+
top: 0;
|
|
269
|
+
left: 0;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
&.#{$component}--checked .#{$component}-input:active ~ .#{$component}-track .#{$component}-thumb {
|
|
273
|
+
left: 0;
|
|
274
|
+
}
|
|
211
275
|
}
|
|
212
276
|
|
|
213
277
|
// Accessibility
|