mtrl 0.2.9 → 0.3.1

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 (99) hide show
  1. package/CLAUDE.md +33 -0
  2. package/package.json +3 -1
  3. package/src/components/button/button.ts +34 -5
  4. package/src/components/navigation/index.ts +4 -1
  5. package/src/components/navigation/system/core.ts +302 -0
  6. package/src/components/navigation/system/events.ts +240 -0
  7. package/src/components/navigation/system/index.ts +184 -0
  8. package/src/components/navigation/system/mobile.ts +278 -0
  9. package/src/components/navigation/system/state.ts +77 -0
  10. package/src/components/navigation/system/types.ts +364 -0
  11. package/src/components/navigation/types.ts +33 -0
  12. package/src/components/slider/config.ts +2 -2
  13. package/src/components/slider/features/controller.ts +1 -25
  14. package/src/components/slider/features/handlers.ts +0 -1
  15. package/src/components/slider/features/range.ts +7 -7
  16. package/src/components/slider/{structure.ts → schema.ts} +2 -13
  17. package/src/components/slider/slider.ts +3 -2
  18. package/src/components/snackbar/index.ts +7 -1
  19. package/src/components/snackbar/types.ts +25 -0
  20. package/src/components/switch/api.ts +16 -0
  21. package/src/components/switch/config.ts +1 -18
  22. package/src/components/switch/features.ts +198 -0
  23. package/src/components/switch/index.ts +6 -1
  24. package/src/components/switch/switch.ts +3 -3
  25. package/src/components/switch/types.ts +27 -2
  26. package/src/components/textfield/index.ts +7 -1
  27. package/src/components/textfield/types.ts +36 -0
  28. package/src/core/composition/features/dom.ts +26 -14
  29. package/src/core/composition/features/icon.ts +18 -18
  30. package/src/core/composition/features/index.ts +3 -2
  31. package/src/core/composition/features/label.ts +16 -17
  32. package/src/core/composition/features/layout.ts +47 -0
  33. package/src/core/composition/index.ts +4 -4
  34. package/src/core/layout/README.md +350 -0
  35. package/src/core/layout/array.ts +181 -0
  36. package/src/core/layout/create.ts +55 -0
  37. package/src/core/layout/index.ts +26 -0
  38. package/src/core/layout/object.ts +124 -0
  39. package/src/core/layout/processor.ts +58 -0
  40. package/src/core/layout/result.ts +85 -0
  41. package/src/core/layout/types.ts +125 -0
  42. package/src/core/layout/utils.ts +136 -0
  43. package/src/styles/abstract/_variables.scss +28 -0
  44. package/src/styles/components/_switch.scss +133 -69
  45. package/src/styles/components/_textfield.scss +9 -16
  46. package/test/components/badge.test.ts +545 -0
  47. package/test/components/bottom-app-bar.test.ts +303 -0
  48. package/test/components/button.test.ts +233 -0
  49. package/test/components/card.test.ts +560 -0
  50. package/test/components/carousel.test.ts +951 -0
  51. package/test/components/checkbox.test.ts +462 -0
  52. package/test/components/chip.test.ts +692 -0
  53. package/test/components/datepicker.test.ts +1124 -0
  54. package/test/components/dialog.test.ts +990 -0
  55. package/test/components/divider.test.ts +412 -0
  56. package/test/components/extended-fab.test.ts +672 -0
  57. package/test/components/fab.test.ts +561 -0
  58. package/test/components/list.test.ts +365 -0
  59. package/test/components/menu.test.ts +718 -0
  60. package/test/components/navigation.test.ts +186 -0
  61. package/test/components/progress.test.ts +567 -0
  62. package/test/components/radios.test.ts +699 -0
  63. package/test/components/search.test.ts +1135 -0
  64. package/test/components/segmented-button.test.ts +732 -0
  65. package/test/components/sheet.test.ts +641 -0
  66. package/test/components/slider.test.ts +1220 -0
  67. package/test/components/snackbar.test.ts +461 -0
  68. package/test/components/switch.test.ts +452 -0
  69. package/test/components/tabs.test.ts +1369 -0
  70. package/test/components/textfield.test.ts +400 -0
  71. package/test/components/timepicker.test.ts +592 -0
  72. package/test/components/tooltip.test.ts +630 -0
  73. package/test/components/top-app-bar.test.ts +566 -0
  74. package/test/core/dom.attributes.test.ts +148 -0
  75. package/test/core/dom.classes.test.ts +152 -0
  76. package/test/core/dom.events.test.ts +243 -0
  77. package/test/core/emitter.test.ts +141 -0
  78. package/test/core/ripple.test.ts +99 -0
  79. package/test/core/state.store.test.ts +189 -0
  80. package/test/core/utils.normalize.test.ts +61 -0
  81. package/test/core/utils.object.test.ts +120 -0
  82. package/test/setup.ts +451 -0
  83. package/tsconfig.json +2 -2
  84. package/src/components/navigation/system-types.ts +0 -124
  85. package/src/components/navigation/system.ts +0 -776
  86. package/src/components/snackbar/constants.ts +0 -26
  87. package/src/core/composition/features/structure.ts +0 -22
  88. package/src/core/layout/index.js +0 -95
  89. package/src/core/structure.ts +0 -288
  90. package/test/components/button.test.js +0 -170
  91. package/test/components/checkbox.test.js +0 -238
  92. package/test/components/list.test.js +0 -105
  93. package/test/components/menu.test.js +0 -385
  94. package/test/components/navigation.test.js +0 -227
  95. package/test/components/snackbar.test.js +0 -234
  96. package/test/components/switch.test.js +0 -186
  97. package/test/components/textfield.test.js +0 -314
  98. package/test/core/emitter.test.js +0 -141
  99. package/test/core/ripple.test.js +0 -66
@@ -10,12 +10,36 @@ $component: '#{base.$prefix}-switch';
10
10
  .#{$component} {
11
11
  // Base styles
12
12
  display: inline-flex;
13
- align-items: center;
13
+ flex-direction: column;
14
14
  position: relative;
15
- min-height: 32px;
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: all v.motion('duration-short4') v.motion('easing-emphasized');
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
- // Label
84
- &-label {
85
- @include m.typography('body-large');
86
- margin-left: 12px;
87
- color: t.color('on-surface');
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
- &--label-start {
110
- .#{$component}-label {
111
- margin-left: 12px;
112
- margin-right: 0;
113
- }
144
+ &-container {
145
+ flex-direction: row;
114
146
  }
115
-
116
- &--label-end {
117
- .#{$component}-label {
118
- margin-left: 0;
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
- opacity: 0.38;
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('on-primary');
165
- opacity: 1;
211
+ background-color: t.color('surface-dim');
212
+ opacity: 0.38;
166
213
  &-icon {
167
- color: t.color('outline')
214
+ color: t.color('on-surface');
168
215
  }
169
216
  }
170
217
  }
171
218
  }
172
219
 
173
- // Hover effects (state layer)
174
- &:not(&--disabled) {
175
- .#{$component}-input:hover + .#{$component}-track {
176
- &::before {
177
- content: '';
178
- position: absolute;
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
- // Active/pressed state
190
- .#{$component}-input:active + .#{$component}-track {
191
- &::before {
192
- content: '';
193
- position: absolute;
194
- top: -8px;
195
- left: -8px;
196
- right: -8px;
197
- bottom: -8px;
198
- background-color: t.color('on-surface');
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 when checked
248
+ // Hover/active states for checked
205
249
  &.#{$component}--checked {
206
- .#{$component}-input:hover + .#{$component}-track::before,
207
- .#{$component}-input:active + .#{$component}-track::before {
208
- background-color: t.color('primary');
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
@@ -286,12 +286,12 @@ $component: '#{base.$prefix}-textfield';
286
286
  }
287
287
  }
288
288
 
289
- // Hover state
290
- &:hover {
291
- .#{$component}-label {
292
- color: t.color('primary');
293
- }
294
- }
289
+ // // Hover state
290
+ // &:hover {
291
+ // .#{$component}-label {
292
+ // color: t.color('primary');
293
+ // }
294
+ // }
295
295
 
296
296
  // Error state
297
297
  &.#{$component}--error {
@@ -382,6 +382,7 @@ $component: '#{base.$prefix}-textfield';
382
382
 
383
383
  // ===== OUTLINED VARIANT =====
384
384
  &--outlined {
385
+ background-color: inherit;
385
386
  border-radius: f.get-shape('extra-small');
386
387
  @include m.motion-transition(border-color);
387
388
 
@@ -410,6 +411,7 @@ $component: '#{base.$prefix}-textfield';
410
411
  }
411
412
 
412
413
  .#{$component}-label {
414
+ background-color: inherit;
413
415
  padding: 0 4px;
414
416
  left: 12px;
415
417
  top: 49%;
@@ -431,7 +433,6 @@ $component: '#{base.$prefix}-textfield';
431
433
  &:not(.#{$component}--empty) .#{$component}-label,
432
434
  &.#{$component}--focused .#{$component}-label {
433
435
  padding: 0 4px;
434
- background-color: t.color('surface');
435
436
  transform: translateY(-147%) scale(0.75);
436
437
  }
437
438
 
@@ -445,18 +446,10 @@ $component: '#{base.$prefix}-textfield';
445
446
  border-width: 2px;
446
447
  }
447
448
  }
448
- }
449
-
450
- // Hover state
451
- &:hover {
449
+
452
450
  .#{$component}-label {
453
451
  color: t.color('primary');
454
452
  }
455
-
456
- &::before {
457
- opacity: 1;
458
- border: 1px solid t.color('primary');
459
- }
460
453
  }
461
454
 
462
455
  // Error state