mtrl 0.3.6 → 0.3.7

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 +1 -1
  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 +15 -13
  5. package/src/components/menu/config.ts +5 -5
  6. package/src/components/menu/features/anchor.ts +99 -15
  7. package/src/components/menu/features/controller.ts +418 -221
  8. package/src/components/menu/features/index.ts +2 -1
  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 +18 -60
  12. package/src/components/menu/types.ts +17 -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 +317 -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 -1
  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 +103 -24
  38. package/src/styles/components/_select.scss +265 -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
@@ -67,7 +75,13 @@ $component: 'mtrl-menu';
67
75
  cursor: pointer;
68
76
  user-select: none;
69
77
  color: t.color('on-surface');
78
+ white-space: nowrap;
79
+ text-overflow: ellipsis;
80
+ overflow: hidden;
81
+ width: 100%;
82
+ box-sizing: border-box;
70
83
 
84
+ // State layer effects
71
85
  &:hover {
72
86
  @include m.state-layer(t.color('on-surface'), 'hover');
73
87
  }
@@ -88,18 +102,21 @@ $component: 'mtrl-menu';
88
102
  &::after {
89
103
  content: '';
90
104
  position: absolute;
91
- right: 16px;
105
+ right: 12px;
92
106
  top: 50%;
93
107
  transform: translateY(-50%);
94
108
  width: 24px;
95
109
  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;
110
+ mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='24px' viewBox='0 0 24 24' width='24px'%3E%3Cpath d='M0 0h24v24H0V0z' fill='none'/%3E%3Cpath d='M10 17l5-5-5-5v10z'/%3E%3C/svg%3E");
111
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='24px' viewBox='0 0 24 24' width='24px'%3E%3Cpath d='M0 0h24v24H0V0z' fill='none'/%3E%3Cpath d='M10 17l5-5-5-5v10z'/%3E%3C/svg%3E");
112
+ mask-size: contain;
113
+ -webkit-mask-size: contain;
114
+ mask-repeat: no-repeat;
115
+ -webkit-mask-repeat: no-repeat;
116
+ mask-position: center;
117
+ -webkit-mask-position: center;
118
+ background-color: currentColor;
100
119
  opacity: 0.87;
101
- // This ensures the SVG inherits the exact same color as the menu item text
102
- color: inherit;
103
120
  }
104
121
 
105
122
  &[aria-expanded="true"] {
@@ -116,6 +133,59 @@ $component: 'mtrl-menu';
116
133
  pointer-events: none;
117
134
  color: t.alpha('on-surface', 0.38);
118
135
  }
136
+
137
+ // Selected state for select component
138
+ &--selected {
139
+ color: t.color('primary');
140
+ font-weight: 500;
141
+
142
+ &::after {
143
+ content: "";
144
+ display: block;
145
+ position: absolute;
146
+ right: 12px;
147
+ width: 18px;
148
+ height: 18px;
149
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18' height='18' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");
150
+ background-repeat: no-repeat;
151
+ background-position: center;
152
+ color: t.color('primary');
153
+ }
154
+ }
155
+
156
+ // Content containers inside menu items
157
+ &-content {
158
+ display: flex;
159
+ align-items: center;
160
+ width: 100%;
161
+ overflow: hidden;
162
+ }
163
+
164
+ &-icon {
165
+ margin-right: 12px;
166
+ display: flex;
167
+ align-items: center;
168
+ justify-content: center;
169
+ flex-shrink: 0;
170
+
171
+ svg {
172
+ width: 20px;
173
+ height: 20px;
174
+ }
175
+ }
176
+
177
+ &-text {
178
+ flex: 1;
179
+ white-space: nowrap;
180
+ overflow: hidden;
181
+ text-overflow: ellipsis;
182
+ }
183
+
184
+ &-shortcut {
185
+ margin-left: 12px;
186
+ color: t.color('on-surface-variant');
187
+ flex-shrink: 0;
188
+ }
119
189
  }
120
190
 
121
191
  // Divider
@@ -128,14 +198,17 @@ $component: 'mtrl-menu';
128
198
  // Accessibility
129
199
  @include m.focus-ring('.#{$component}-item:focus-visible');
130
200
 
201
+ // Reduced motion preference support
131
202
  @include m.reduced-motion {
132
- transition: none;
203
+ transition: opacity 0.1s linear;
204
+ transform: none !important;
133
205
 
134
- .#{$component}-item {
135
- transition: none;
206
+ &--visible {
207
+ transform: none !important;
136
208
  }
137
209
  }
138
210
 
211
+ // High contrast mode
139
212
  @include m.high-contrast {
140
213
  border: 1px solid CurrentColor;
141
214
 
@@ -153,6 +226,12 @@ $component: 'mtrl-menu';
153
226
  @include m.rtl {
154
227
  transform-origin: top right;
155
228
 
229
+ &--position-top,
230
+ &--position-top-start,
231
+ &--position-top-end {
232
+ transform-origin: bottom right;
233
+ }
234
+
156
235
  .#{$component}-item {
157
236
  &--submenu {
158
237
  padding-right: 16px;
@@ -0,0 +1,265 @@
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
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18' height='18' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");
129
+ background-repeat: no-repeat;
130
+ background-position: center;
131
+ color: t.color('primary');
132
+ }
133
+ }
134
+ }
135
+ }
136
+
137
+ // Size variants
138
+ &--small {
139
+ .#{base.$prefix}-textfield {
140
+ height: v.textfield('height-small');
141
+ }
142
+
143
+ .#{base.$prefix}-textfield-input {
144
+ height: v.textfield('height-small');
145
+ padding-top: 8px;
146
+ padding-bottom: 8px;
147
+ }
148
+ }
149
+
150
+ &--large {
151
+ .#{base.$prefix}-textfield {
152
+ height: v.textfield('height-large');
153
+ }
154
+
155
+ .#{base.$prefix}-textfield-input {
156
+ height: v.textfield('height-large');
157
+ padding-top: 16px;
158
+ padding-bottom: 16px;
159
+ }
160
+ }
161
+
162
+ // Variant adjustments
163
+ &--filled {
164
+ // Specific adjustments for filled variant
165
+ .#{base.$prefix}-textfield-label {
166
+ // Adjust label position for filled select
167
+ }
168
+ }
169
+
170
+ &--outlined {
171
+ // Specific adjustments for outlined variant
172
+ .#{base.$prefix}-textfield-label {
173
+ // Adjust label position for outlined select
174
+ }
175
+ }
176
+
177
+ // Focus styles
178
+ &:focus-within {
179
+ .#{base.$prefix}-textfield-trailing-icon {
180
+ color: t.color('primary');
181
+ }
182
+ }
183
+
184
+ // Helper text positioning
185
+ .#{base.$prefix}-textfield-helper {
186
+ margin-top: 4px;
187
+ }
188
+ }
189
+
190
+ // Specific styles for the menu when used with select
191
+ .#{base.$prefix}-select-menu {
192
+ margin-top: 4px;
193
+ max-height: 300px;
194
+ overflow-y: auto;
195
+
196
+ .#{base.$prefix}-menu-list {
197
+ padding: 8px 0;
198
+ }
199
+
200
+ .#{base.$prefix}-menu-item {
201
+ padding: 0 16px;
202
+ height: 48px;
203
+ display: flex;
204
+ align-items: center;
205
+ position: relative;
206
+
207
+ &--selected {
208
+ color: t.color('primary');
209
+ font-weight: 500;
210
+ }
211
+
212
+ &-icon {
213
+ margin-right: 16px;
214
+ display: flex;
215
+ align-items: center;
216
+
217
+ svg {
218
+ width: 18px;
219
+ height: 18px;
220
+ }
221
+ }
222
+
223
+ &-text {
224
+ flex: 1;
225
+ @include m.truncate;
226
+ }
227
+ }
228
+
229
+ .#{base.$prefix}-menu-divider {
230
+ height: 1px;
231
+ margin: 8px 0;
232
+ background-color: t.color('outline-variant');
233
+ }
234
+ }
235
+
236
+ // Animation for the menu
237
+ @keyframes select-menu-fade-in {
238
+ from {
239
+ opacity: 0;
240
+ transform: translateY(-8px);
241
+ }
242
+ to {
243
+ opacity: 1;
244
+ transform: translateY(0);
245
+ }
246
+ }
247
+
248
+ @keyframes select-menu-fade-out {
249
+ from {
250
+ opacity: 1;
251
+ transform: translateY(0);
252
+ }
253
+ to {
254
+ opacity: 0;
255
+ transform: translateY(-8px);
256
+ }
257
+ }
258
+
259
+ .#{base.$prefix}-select-menu--visible {
260
+ animation: select-menu-fade-in 0.2s ease forwards;
261
+ }
262
+
263
+ .#{base.$prefix}-select-menu--hiding {
264
+ animation: select-menu-fade-out 0.2s ease forwards;
265
+ }