mtrl 0.3.6 → 0.3.8

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 (45) hide show
  1. package/package.json +2 -2
  2. package/src/components/button/api.ts +16 -0
  3. package/src/components/button/types.ts +9 -0
  4. package/src/components/menu/api.ts +61 -22
  5. package/src/components/menu/config.ts +10 -8
  6. package/src/components/menu/features/anchor.ts +254 -19
  7. package/src/components/menu/features/controller.ts +724 -271
  8. package/src/components/menu/features/index.ts +11 -2
  9. package/src/components/menu/features/position.ts +353 -0
  10. package/src/components/menu/index.ts +5 -5
  11. package/src/components/menu/menu.ts +21 -61
  12. package/src/components/menu/types.ts +30 -16
  13. package/src/components/select/api.ts +78 -0
  14. package/src/components/select/config.ts +76 -0
  15. package/src/components/select/features.ts +331 -0
  16. package/src/components/select/index.ts +38 -0
  17. package/src/components/select/select.ts +73 -0
  18. package/src/components/select/types.ts +355 -0
  19. package/src/components/textfield/api.ts +78 -6
  20. package/src/components/textfield/features/index.ts +17 -0
  21. package/src/components/textfield/features/leading-icon.ts +127 -0
  22. package/src/components/textfield/features/placement.ts +149 -0
  23. package/src/components/textfield/features/prefix-text.ts +107 -0
  24. package/src/components/textfield/features/suffix-text.ts +100 -0
  25. package/src/components/textfield/features/supporting-text.ts +113 -0
  26. package/src/components/textfield/features/trailing-icon.ts +108 -0
  27. package/src/components/textfield/textfield.ts +51 -15
  28. package/src/components/textfield/types.ts +70 -0
  29. package/src/core/collection/adapters/base.ts +62 -0
  30. package/src/core/collection/collection.ts +300 -0
  31. package/src/core/collection/index.ts +57 -0
  32. package/src/core/collection/list-manager.ts +333 -0
  33. package/src/index.ts +4 -45
  34. package/src/styles/abstract/_variables.scss +18 -0
  35. package/src/styles/components/_button.scss +21 -5
  36. package/src/styles/components/{_chip.scss → _chips.scss} +118 -4
  37. package/src/styles/components/_menu.scss +97 -24
  38. package/src/styles/components/_select.scss +272 -0
  39. package/src/styles/components/_textfield.scss +233 -42
  40. package/src/styles/main.scss +2 -1
  41. package/src/components/textfield/features.ts +0 -322
  42. package/src/core/collection/adapters/base.js +0 -26
  43. package/src/core/collection/collection.js +0 -259
  44. package/src/core/collection/list-manager.js +0 -157
  45. /package/src/core/collection/adapters/{route.js → route.ts} +0 -0
@@ -1,4 +1,4 @@
1
- // src/components/menu/_menu.scss
1
+ // src/styles/components/_menu.scss
2
2
  @use '../../styles/abstract/variables' as v;
3
3
  @use '../../styles/abstract/mixins' as m;
4
4
  @use '../../styles/abstract/functions' as f;
@@ -15,32 +15,39 @@ $component: 'mtrl-menu';
15
15
  z-index: f.get-z-index('menu');
16
16
  min-width: 112px;
17
17
  max-width: 280px;
18
- padding: 8px 0;
18
+ padding: 8px 0; // Keep padding consistent
19
19
  background-color: t.color('surface-container');
20
20
  color: t.color('on-surface');
21
21
  @include m.elevation(2);
22
+ overflow: hidden;
22
23
 
23
- // Initial state - hidden
24
+ // Initial state - hidden with proper transform origin
24
25
  display: block;
25
26
  opacity: 0;
26
- transform: scale(0.8);
27
- transform-origin: top left;
27
+ transform-origin: top center; // Default transform origin
28
+ transform: scaleY(0);
28
29
  pointer-events: none;
29
- visibility: hidden; // Added for better measurement handling
30
- transition: opacity 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
33
30
 
31
+ // Material Design 3 standard animation curve for menus
32
+ transition:
33
+ transform v.motion('duration-medium1') v.motion('easing-emphasized'),
34
+ opacity v.motion('duration-medium1') v.motion('easing-emphasized');
35
+
36
+ // Visible state
34
37
  &--visible {
35
38
  opacity: 1;
36
- transform: scale(1);
39
+ transform: scaleY(1);
37
40
  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
42
41
  }
43
42
 
43
+ // Position variations with appropriate transform origins
44
+ &--position-top,
45
+ &--position-top-start,
46
+ &--position-top-end {
47
+ transform-origin: bottom center;
48
+ }
49
+
50
+ // Submenu styling
44
51
  &--submenu {
45
52
  position: absolute;
46
53
  z-index: f.get-z-index('menu') + 1;
@@ -54,6 +61,7 @@ $component: 'mtrl-menu';
54
61
  overflow-y: auto;
55
62
  max-height: calc(100vh - 96px);
56
63
  @include m.scrollbar;
64
+ width: 100%;
57
65
  }
58
66
 
59
67
  // Menu items
@@ -64,10 +72,17 @@ $component: 'mtrl-menu';
64
72
  position: relative;
65
73
  min-height: 48px;
66
74
  padding: 12px 16px;
75
+ padding-right: 42px;
67
76
  cursor: pointer;
68
77
  user-select: none;
69
78
  color: t.color('on-surface');
79
+ white-space: nowrap;
80
+ text-overflow: ellipsis;
81
+ overflow: hidden;
82
+ width: 100%;
83
+ box-sizing: border-box;
70
84
 
85
+ // State layer effects
71
86
  &:hover {
72
87
  @include m.state-layer(t.color('on-surface'), 'hover');
73
88
  }
@@ -88,18 +103,15 @@ $component: 'mtrl-menu';
88
103
  &::after {
89
104
  content: '';
90
105
  position: absolute;
91
- right: 16px;
106
+ right: 12px;
92
107
  top: 50%;
93
108
  transform: translateY(-50%);
94
109
  width: 24px;
95
110
  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;
111
+ mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M10 17l5-5-5-5v10z'/%3E%3C/svg%3E") center / contain no-repeat;
112
+ -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M10 17l5-5-5-5v10z'/%3E%3C/svg%3E") center / contain no-repeat;
113
+ background-color: currentColor;
100
114
  opacity: 0.87;
101
- // This ensures the SVG inherits the exact same color as the menu item text
102
- color: inherit;
103
115
  }
104
116
 
105
117
  &[aria-expanded="true"] {
@@ -116,6 +128,58 @@ $component: 'mtrl-menu';
116
128
  pointer-events: none;
117
129
  color: t.alpha('on-surface', 0.38);
118
130
  }
131
+
132
+ // Selected state for select component
133
+ &--selected {
134
+ color: t.color('primary');
135
+
136
+ &::after {
137
+ content: "";
138
+ position: absolute;
139
+ right: 12px;
140
+ top: 50%;
141
+ transform: translateY(-50%);
142
+ width: 18px;
143
+ height: 18px;
144
+ mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpolyline points='20 6 9 17 4 12' stroke='white' stroke-width='2' fill='none'/%3E%3C/svg%3E") center / contain no-repeat;
145
+ -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpolyline points='20 6 9 17 4 12' stroke='white' stroke-width='2' fill='none'/%3E%3C/svg%3E") center / contain no-repeat;
146
+ background-color: currentColor;
147
+ }
148
+ }
149
+
150
+ // Content containers inside menu items
151
+ &-content {
152
+ display: flex;
153
+ align-items: center;
154
+ width: 100%;
155
+ overflow: hidden;
156
+ }
157
+
158
+ &-icon {
159
+ margin-right: 12px;
160
+ display: flex;
161
+ align-items: center;
162
+ justify-content: center;
163
+ flex-shrink: 0;
164
+
165
+ svg {
166
+ width: 20px;
167
+ height: 20px;
168
+ }
169
+ }
170
+
171
+ &-text {
172
+ flex: 1;
173
+ white-space: nowrap;
174
+ overflow: hidden;
175
+ text-overflow: ellipsis;
176
+ }
177
+
178
+ &-shortcut {
179
+ margin-left: 12px;
180
+ color: t.color('on-surface-variant');
181
+ flex-shrink: 0;
182
+ }
119
183
  }
120
184
 
121
185
  // Divider
@@ -128,14 +192,17 @@ $component: 'mtrl-menu';
128
192
  // Accessibility
129
193
  @include m.focus-ring('.#{$component}-item:focus-visible');
130
194
 
195
+ // Reduced motion preference support
131
196
  @include m.reduced-motion {
132
- transition: none;
197
+ transition: opacity 0.1s linear;
198
+ transform: none !important;
133
199
 
134
- .#{$component}-item {
135
- transition: none;
200
+ &--visible {
201
+ transform: none !important;
136
202
  }
137
203
  }
138
204
 
205
+ // High contrast mode
139
206
  @include m.high-contrast {
140
207
  border: 1px solid CurrentColor;
141
208
 
@@ -153,6 +220,12 @@ $component: 'mtrl-menu';
153
220
  @include m.rtl {
154
221
  transform-origin: top right;
155
222
 
223
+ &--position-top,
224
+ &--position-top-start,
225
+ &--position-top-end {
226
+ transform-origin: bottom right;
227
+ }
228
+
156
229
  .#{$component}-item {
157
230
  &--submenu {
158
231
  padding-right: 16px;
@@ -0,0 +1,272 @@
1
+ // src/styles/components/_select.scss
2
+ @use '../../styles/abstract/base' as base;
3
+ @use '../../styles/abstract/variables' as v;
4
+ @use '../../styles/abstract/functions' as f;
5
+ @use '../../styles/abstract/mixins' as m;
6
+ @use '../../styles/abstract/theme' as t;
7
+
8
+ $component: '#{base.$prefix}-select';
9
+
10
+ .#{$component} {
11
+ // Base styles - building on textfield base
12
+ position: relative;
13
+ width: 100%;
14
+ cursor: pointer;
15
+
16
+ // Transition
17
+ @include m.motion-transition(
18
+ border-color,
19
+ background-color,
20
+ box-shadow
21
+ );
22
+
23
+ // Ensure the textfield input works with our select
24
+ .#{base.$prefix}-textfield-input {
25
+ cursor: pointer;
26
+ pointer-events: none; // Let clicks pass through to the container
27
+ caret-color: transparent; // Hide text cursor since it's not editable
28
+ }
29
+
30
+ // Trailing icon (dropdown arrow)
31
+ .#{base.$prefix}-textfield-trailing-icon {
32
+ pointer-events: auto; // Allow clicks on the icon
33
+ cursor: pointer;
34
+ transform: translateY(-50%); // Keep vertical centering
35
+ transition: v.motion('duration-medium1') v.motion('easing-emphasized'); // Add transition for smooth rotation and color changes
36
+
37
+ svg {
38
+ width: 24px;
39
+ height: 24px;
40
+ vertical-align: middle;
41
+ color: inherit;
42
+ transition: v.motion('duration-medium1') v.motion('easing-emphasized'); // Add transition for the SVG itself
43
+ }
44
+ }
45
+
46
+ // State when menu is open
47
+ &--open {
48
+ z-index: 2; // Keep above other elements when open
49
+
50
+ // These styles handle the focused state when the menu is open
51
+ .#{base.$prefix}-textfield--filled {
52
+ &::before {
53
+ opacity: 1; // Show the active indicator
54
+ }
55
+
56
+ .#{base.$prefix}-textfield-label {
57
+ color: t.color('primary');
58
+ }
59
+
60
+ // Fix for filled variant positioning
61
+ .#{base.$prefix}-textfield-trailing-icon {
62
+ top: 28px; // Match the filled textfield's icon positioning
63
+ }
64
+ }
65
+
66
+ .#{base.$prefix}-textfield--outlined {
67
+ &::before {
68
+ opacity: 1;
69
+ border-color: t.color('primary');
70
+ }
71
+
72
+ .#{base.$prefix}-textfield-label {
73
+ color: t.color('primary');
74
+ }
75
+ }
76
+
77
+ // Apply rotation to SVG instead of the container for better positioning
78
+ .#{base.$prefix}-textfield-trailing-icon {
79
+ color: t.color('primary');
80
+
81
+ svg {
82
+ transform: rotate(180deg); // Rotate the SVG, not the container
83
+ }
84
+ }
85
+ }
86
+
87
+ // Disabled state
88
+ &--disabled {
89
+ pointer-events: none;
90
+ opacity: 0.38;
91
+ cursor: default;
92
+
93
+ .#{base.$prefix}-textfield-input,
94
+ .#{base.$prefix}-textfield-trailing-icon {
95
+ cursor: default;
96
+ }
97
+ }
98
+
99
+ // Error state
100
+ &--error {
101
+ .#{base.$prefix}-textfield-trailing-icon {
102
+ color: t.color('error');
103
+ }
104
+ }
105
+
106
+ // Menu styles specifically for select
107
+ & + .#{base.$prefix}-menu {
108
+ // Additional styles for the connected menu
109
+ max-height: 20rem;
110
+ overflow-y: auto;
111
+
112
+ .#{base.$prefix}-menu-item {
113
+ min-height: 3rem;
114
+
115
+ // Selected state
116
+ &--selected {
117
+ background-color: t.alpha('primary', 0.12);
118
+ color: t.color('primary');
119
+ font-weight: 500;
120
+
121
+ &::after {
122
+ content: "";
123
+ display: block;
124
+ position: absolute;
125
+ right: 12px;
126
+ width: 18px;
127
+ height: 18px;
128
+ // Use mask-image instead of background-image
129
+ mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18' height='18' viewBox='0 0 24 24'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");
130
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18' height='18' viewBox='0 0 24 24'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");
131
+ mask-size: contain;
132
+ -webkit-mask-size: contain;
133
+ mask-repeat: no-repeat;
134
+ -webkit-mask-repeat: no-repeat;
135
+ mask-position: center;
136
+ -webkit-mask-position: center;
137
+ // Use currentColor to match the text color
138
+ background-color: currentColor;
139
+ }
140
+ }
141
+ }
142
+ }
143
+
144
+ // Size variants
145
+ &--small {
146
+ .#{base.$prefix}-textfield {
147
+ height: v.textfield('height-small');
148
+ }
149
+
150
+ .#{base.$prefix}-textfield-input {
151
+ height: v.textfield('height-small');
152
+ padding-top: 8px;
153
+ padding-bottom: 8px;
154
+ }
155
+ }
156
+
157
+ &--large {
158
+ .#{base.$prefix}-textfield {
159
+ height: v.textfield('height-large');
160
+ }
161
+
162
+ .#{base.$prefix}-textfield-input {
163
+ height: v.textfield('height-large');
164
+ padding-top: 16px;
165
+ padding-bottom: 16px;
166
+ }
167
+ }
168
+
169
+ // Variant adjustments
170
+ &--filled {
171
+ // Specific adjustments for filled variant
172
+ .#{base.$prefix}-textfield-label {
173
+ // Adjust label position for filled select
174
+ }
175
+ }
176
+
177
+ &--outlined {
178
+ // Specific adjustments for outlined variant
179
+ .#{base.$prefix}-textfield-label {
180
+ // Adjust label position for outlined select
181
+ }
182
+ }
183
+
184
+ // Focus styles
185
+ &:focus-within {
186
+ .#{base.$prefix}-textfield-trailing-icon {
187
+ color: t.color('primary');
188
+ }
189
+ }
190
+
191
+ // Helper text positioning
192
+ .#{base.$prefix}-textfield-helper {
193
+ margin-top: 4px;
194
+ }
195
+ }
196
+
197
+ // Specific styles for the menu when used with select
198
+ .#{base.$prefix}-select-menu {
199
+ margin-top: 4px;
200
+ max-height: 300px;
201
+ overflow-y: auto;
202
+
203
+ .#{base.$prefix}-menu-list {
204
+ padding: 8px 0;
205
+ }
206
+
207
+ .#{base.$prefix}-menu-item {
208
+ padding: 0 16px;
209
+ height: 48px;
210
+ display: flex;
211
+ align-items: center;
212
+ position: relative;
213
+
214
+ &--selected {
215
+ color: t.color('primary');
216
+ font-weight: 500;
217
+ }
218
+
219
+ &-icon {
220
+ margin-right: 16px;
221
+ display: flex;
222
+ align-items: center;
223
+
224
+ svg {
225
+ width: 18px;
226
+ height: 18px;
227
+ }
228
+ }
229
+
230
+ &-text {
231
+ flex: 1;
232
+ @include m.truncate;
233
+ }
234
+ }
235
+
236
+ .#{base.$prefix}-menu-divider {
237
+ height: 1px;
238
+ margin: 8px 0;
239
+ background-color: t.color('outline-variant');
240
+ }
241
+ }
242
+
243
+ // Animation for the menu
244
+ @keyframes select-menu-fade-in {
245
+ from {
246
+ opacity: 0;
247
+ transform: translateY(-8px);
248
+ }
249
+ to {
250
+ opacity: 1;
251
+ transform: translateY(0);
252
+ }
253
+ }
254
+
255
+ @keyframes select-menu-fade-out {
256
+ from {
257
+ opacity: 1;
258
+ transform: translateY(0);
259
+ }
260
+ to {
261
+ opacity: 0;
262
+ transform: translateY(-8px);
263
+ }
264
+ }
265
+
266
+ .#{base.$prefix}-select-menu--visible {
267
+ animation: select-menu-fade-in 0.2s ease forwards;
268
+ }
269
+
270
+ .#{base.$prefix}-select-menu--hiding {
271
+ animation: select-menu-fade-out 0.2s ease forwards;
272
+ }