mtrl 0.3.5 → 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 (65) 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 +144 -267
  5. package/src/components/menu/config.ts +84 -40
  6. package/src/components/menu/features/anchor.ts +243 -0
  7. package/src/components/menu/features/controller.ts +1167 -0
  8. package/src/components/menu/features/index.ts +5 -0
  9. package/src/components/menu/features/position.ts +353 -0
  10. package/src/components/menu/index.ts +31 -63
  11. package/src/components/menu/menu.ts +72 -104
  12. package/src/components/menu/types.ts +264 -447
  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/core/dom/classes.ts +81 -9
  34. package/src/core/dom/create.ts +30 -19
  35. package/src/core/layout/README.md +531 -166
  36. package/src/core/layout/array.ts +3 -4
  37. package/src/core/layout/config.ts +193 -0
  38. package/src/core/layout/create.ts +1 -2
  39. package/src/core/layout/index.ts +12 -2
  40. package/src/core/layout/object.ts +2 -3
  41. package/src/core/layout/processor.ts +60 -12
  42. package/src/core/layout/result.ts +1 -2
  43. package/src/core/layout/types.ts +105 -50
  44. package/src/core/layout/utils.ts +69 -61
  45. package/src/index.ts +6 -2
  46. package/src/styles/abstract/_variables.scss +18 -0
  47. package/src/styles/components/_button.scss +21 -5
  48. package/src/styles/components/{_chip.scss → _chips.scss} +118 -4
  49. package/src/styles/components/_menu.scss +109 -18
  50. package/src/styles/components/_select.scss +265 -0
  51. package/src/styles/components/_textfield.scss +233 -42
  52. package/src/styles/main.scss +24 -23
  53. package/src/styles/utilities/_layout.scss +665 -0
  54. package/src/components/menu/features/items-manager.ts +0 -457
  55. package/src/components/menu/features/keyboard-navigation.ts +0 -133
  56. package/src/components/menu/features/positioning.ts +0 -127
  57. package/src/components/menu/features/visibility.ts +0 -230
  58. package/src/components/menu/menu-item.ts +0 -86
  59. package/src/components/menu/utils.ts +0 -67
  60. package/src/components/textfield/features.ts +0 -322
  61. package/src/core/collection/adapters/base.js +0 -26
  62. package/src/core/collection/collection.js +0 -259
  63. package/src/core/collection/list-manager.js +0 -157
  64. /package/src/core/collection/adapters/{route.js → route.ts} +0 -0
  65. /package/src/{core/build → styles/utilities}/_ripple.scss +0 -0
@@ -1,11 +1,10 @@
1
- // src/components/menu/_menu.scss
2
- @use '../../styles/abstract/base' as base;
1
+ // src/styles/components/_menu.scss
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: '#{base.$prefix}-menu';
7
+ $component: 'mtrl-menu';
9
8
 
10
9
  .#{$component} {
11
10
  // Base styles
@@ -16,26 +15,39 @@ $component: '#{base.$prefix}-menu';
16
15
  z-index: f.get-z-index('menu');
17
16
  min-width: 112px;
18
17
  max-width: 280px;
19
- padding: 8px 0;
18
+ padding: 8px 0; // Keep padding consistent
20
19
  background-color: t.color('surface-container');
21
20
  color: t.color('on-surface');
22
21
  @include m.elevation(2);
22
+ overflow: hidden;
23
23
 
24
- display: none;
24
+ // Initial state - hidden with proper transform origin
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
- transition: opacity v.motion('duration-short2') v.motion('easing-standard'),
30
- transform v.motion('duration-short2') v.motion('easing-standard');
31
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
32
37
  &--visible {
33
- display: block;
34
38
  opacity: 1;
35
- transform: scale(1);
39
+ transform: scaleY(1);
36
40
  pointer-events: auto;
37
41
  }
38
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
39
51
  &--submenu {
40
52
  position: absolute;
41
53
  z-index: f.get-z-index('menu') + 1;
@@ -49,6 +61,7 @@ $component: '#{base.$prefix}-menu';
49
61
  overflow-y: auto;
50
62
  max-height: calc(100vh - 96px);
51
63
  @include m.scrollbar;
64
+ width: 100%;
52
65
  }
53
66
 
54
67
  // Menu items
@@ -62,8 +75,13 @@ $component: '#{base.$prefix}-menu';
62
75
  cursor: pointer;
63
76
  user-select: none;
64
77
  color: t.color('on-surface');
65
- @include m.motion-transition(background-color);
78
+ white-space: nowrap;
79
+ text-overflow: ellipsis;
80
+ overflow: hidden;
81
+ width: 100%;
82
+ box-sizing: border-box;
66
83
 
84
+ // State layer effects
67
85
  &:hover {
68
86
  @include m.state-layer(t.color('on-surface'), 'hover');
69
87
  }
@@ -82,11 +100,22 @@ $component: '#{base.$prefix}-menu';
82
100
  padding-right: 48px;
83
101
 
84
102
  &::after {
85
- @include m.icon('chevron_right');
103
+ content: '';
86
104
  position: absolute;
87
- right: 16px;
105
+ right: 12px;
88
106
  top: 50%;
89
107
  transform: translateY(-50%);
108
+ width: 24px;
109
+ height: 24px;
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;
90
119
  opacity: 0.87;
91
120
  }
92
121
 
@@ -104,6 +133,59 @@ $component: '#{base.$prefix}-menu';
104
133
  pointer-events: none;
105
134
  color: t.alpha('on-surface', 0.38);
106
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
+ }
107
189
  }
108
190
 
109
191
  // Divider
@@ -116,14 +198,17 @@ $component: '#{base.$prefix}-menu';
116
198
  // Accessibility
117
199
  @include m.focus-ring('.#{$component}-item:focus-visible');
118
200
 
201
+ // Reduced motion preference support
119
202
  @include m.reduced-motion {
120
- transition: none;
203
+ transition: opacity 0.1s linear;
204
+ transform: none !important;
121
205
 
122
- .#{$component}-item {
123
- transition: none;
206
+ &--visible {
207
+ transform: none !important;
124
208
  }
125
209
  }
126
210
 
211
+ // High contrast mode
127
212
  @include m.high-contrast {
128
213
  border: 1px solid CurrentColor;
129
214
 
@@ -141,6 +226,12 @@ $component: '#{base.$prefix}-menu';
141
226
  @include m.rtl {
142
227
  transform-origin: top right;
143
228
 
229
+ &--position-top,
230
+ &--position-top-start,
231
+ &--position-top-end {
232
+ transform-origin: bottom right;
233
+ }
234
+
144
235
  .#{$component}-item {
145
236
  &--submenu {
146
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
+ }