mtrl 0.7.0 → 0.8.0-next.4

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 (48) hide show
  1. package/dist/components/button/api.d.ts +4 -0
  2. package/dist/components/button/features/toggle.d.ts +42 -0
  3. package/dist/components/button/types.d.ts +30 -0
  4. package/dist/components/button-group/button-group.d.ts +6 -4
  5. package/dist/components/button-group/config.d.ts +15 -7
  6. package/dist/components/button-group/constants.d.ts +85 -17
  7. package/dist/components/button-group/index.d.ts +1 -1
  8. package/dist/components/button-group/types.d.ts +68 -4
  9. package/dist/components/carousel/api.d.ts +11 -30
  10. package/dist/components/carousel/arrangement.d.ts +23 -0
  11. package/dist/components/carousel/carousel.d.ts +13 -63
  12. package/dist/components/carousel/config.d.ts +2 -31
  13. package/dist/components/carousel/constants.d.ts +25 -151
  14. package/dist/components/carousel/features/index.d.ts +2 -5
  15. package/dist/components/carousel/features/scroll.d.ts +19 -0
  16. package/dist/components/carousel/features/slides.d.ts +18 -8
  17. package/dist/components/carousel/index.d.ts +3 -37
  18. package/dist/components/carousel/keylines.d.ts +65 -0
  19. package/dist/components/carousel/strategy.d.ts +36 -0
  20. package/dist/components/carousel/types.d.ts +36 -242
  21. package/dist/components/dialog/api.d.ts +1 -1
  22. package/dist/components/index.d.ts +2 -0
  23. package/dist/components/slider/constants.d.ts +8 -8
  24. package/dist/components/slider/types.d.ts +4 -1
  25. package/dist/components/snackbar/api.d.ts +1 -1
  26. package/dist/components/snackbar/config.d.ts +2 -1
  27. package/dist/components/snackbar/constants.d.ts +18 -0
  28. package/dist/components/snackbar/index.d.ts +2 -1
  29. package/dist/components/snackbar/queue.d.ts +10 -2
  30. package/dist/components/snackbar/types.d.ts +37 -1
  31. package/dist/index.cjs +20 -10
  32. package/dist/index.cjs.map +54 -38
  33. package/dist/index.js +20 -10
  34. package/dist/index.js.map +54 -38
  35. package/dist/package.json +1 -1
  36. package/dist/styles.css +2 -2
  37. package/package.json +3 -6
  38. package/src/styles/abstract/_theme.scss +4 -2
  39. package/src/styles/abstract/_variables.scss +59 -12
  40. package/src/styles/components/_button-group.scss +122 -349
  41. package/src/styles/components/_button.scss +333 -591
  42. package/src/styles/components/_carousel.scss +131 -611
  43. package/src/styles/components/_icon-button.scss +23 -23
  44. package/src/styles/components/_select.scss +9 -1
  45. package/src/styles/components/_slider.scss +10 -9
  46. package/src/styles/themes/_index.scss +1 -0
  47. package/src/styles/themes/_legacy.scss +73 -0
  48. package/dist/components/carousel/features/drag.d.ts +0 -8
@@ -1,645 +1,165 @@
1
- // src/components/carousel/_styles.scss
1
+ // src/styles/components/_carousel.scss
2
2
  @use '../../styles/abstract/base' as base;
3
3
  @use '../../styles/abstract/variables' as v;
4
- @use '../../styles/abstract/functions' as f;
5
4
  @use '../../styles/abstract/mixins' as m;
6
5
  @use '../../styles/abstract/theme' as t;
7
6
 
8
7
  $component: '#{base.$prefix}-carousel';
9
8
 
10
- // Layout size adjustments
11
- $large-item-width-multi-browse: 240px;
12
- $medium-item-width-multi-browse: 180px;
13
- $small-item-width-multi-browse: 48px;
14
-
15
- $large-item-width-hero: 300px;
16
- $small-item-width-hero: 48px;
9
+ // =============================================================================
10
+ // CAROUSEL COMPONENT STYLES
11
+ // =============================================================================
12
+ // Material 3 carousel (m3.material.io carousel specs; Compose Material 3
13
+ // carousel). The component lays the items out from a keyline strategy:
14
+ // each item is translated to its centre and clipped to its visible width
15
+ // (transform, clip-path and --mtrl-carousel-fade are set per item from the
16
+ // component). This stylesheet owns everything static: the native scroller,
17
+ // the snap points, the item shell and the text over the image.
18
+ //
19
+ // Custom properties set by the component: --mtrl-carousel-corner (item
20
+ // corner radius, 28dp by default) and --mtrl-carousel-fade per item (1 for
21
+ // a large item, 0 for a small one).
22
+ // =============================================================================
17
23
 
18
24
  .#{$component} {
19
- // Base styles
20
25
  position: relative;
21
26
  width: 100%;
27
+ height: 100%;
22
28
  overflow: hidden;
23
- user-select: none;
24
- padding: 8px 0;
25
- outline: none;
26
-
27
- // Focus styles
28
- &:focus {
29
- outline: none;
30
- }
31
-
32
- &:focus-visible {
33
- outline: 2px solid t.color('outline');
34
- outline-offset: 2px;
35
- }
36
-
37
- // Layout specific styles
38
- &-layout {
39
- &--multi-browse {
40
- // Multi-browse layout with different sized items
41
- .#{$component}-slides {
42
- scroll-snap-type: x mandatory;
43
- padding: 0 16px;
44
- }
45
-
46
- .#{$component}-slide {
47
- height: 320px;
48
-
49
- &--large {
50
- width: $large-item-width-multi-browse;
51
- flex: 0 0 auto;
52
- }
53
-
54
- &--medium {
55
- width: $medium-item-width-multi-browse;
56
- flex: 0 0 auto;
57
- }
58
-
59
- &--small {
60
- width: $small-item-width-multi-browse;
61
- flex: 0 0 auto;
62
- }
63
- }
64
- }
65
-
66
- &--uncontained {
67
- // Uncontained layout with uniform sized items
68
- overflow: visible;
69
-
70
- .#{$component}-slides {
71
- scroll-snap-type: none;
72
- padding: 0 16px;
73
- }
74
-
75
- .#{$component}-slide {
76
- width: 240px;
77
- height: 320px;
78
- flex: 0 0 auto;
79
- }
80
- }
81
-
82
- &--hero {
83
- // Hero layout with one large item and smaller previews
84
- .#{$component}-slides {
85
- scroll-snap-type: x mandatory;
86
- padding: 0 16px;
87
- }
88
-
89
- .#{$component}-slide {
90
- height: 360px;
91
-
92
- &--large {
93
- width: $large-item-width-hero;
94
- flex: 0 0 auto;
95
- }
96
-
97
- &--small {
98
- width: $small-item-width-hero;
99
- flex: 0 0 auto;
100
- }
101
- }
102
-
103
- // Center-aligned hero
104
- &[data-centered="true"] {
105
- .#{$component}-slides {
106
- padding: 0;
107
- justify-content: center;
108
- }
109
-
110
- .#{$component}-slide--large {
111
- margin: 0 16px;
112
- }
113
- }
114
- }
115
-
116
- &--full-screen {
117
- // Full-screen layout for immersive experiences
118
- height: 100%;
119
- max-height: 100vh;
120
- padding: 0;
121
-
122
- .#{$component}-wrapper {
123
- height: 100%;
124
- }
125
-
126
- .#{$component}-slides {
127
- height: 100%;
128
- flex-direction: column;
129
- scroll-snap-type: y mandatory;
130
- padding: 0;
131
- }
132
-
133
- .#{$component}-slide {
134
- width: 100%;
135
- height: 100vh;
136
- flex: 0 0 auto;
137
- border-radius: 0 !important;
138
- scroll-snap-align: start;
139
-
140
- &-image {
141
- height: 100%;
142
- }
143
-
144
- &-content {
145
- position: absolute;
146
- bottom: 0;
147
- left: 0;
148
- right: 0;
149
- padding: 24px;
150
- background: linear-gradient(to top, rgba(0,0,0,0.8) 0%, rgba(0,0,0,0.4) 50%, rgba(0,0,0,0) 100%);
151
- color: #FFFFFF;
152
- }
153
-
154
- &-title {
155
- font-size: 24px;
156
- font-weight: 500;
157
- margin-bottom: 8px;
158
- }
159
-
160
- &-description {
161
- font-size: 16px;
162
- margin-bottom: 16px;
163
- }
164
- }
165
-
166
- // Hide show all link in full-screen mode
167
- .#{$component}-show-all {
168
- display: none;
169
- }
170
- }
171
- }
172
-
173
- // Scroll behavior
174
- &[data-scroll-behavior="snap"] {
175
- .#{$component}-slides {
176
- scroll-snap-type: x mandatory;
177
-
178
- &[data-vertical-scroll="true"] {
179
- scroll-snap-type: y mandatory;
180
- }
181
- }
182
-
183
- .#{$component}-slide {
184
- scroll-snap-align: start;
185
-
186
- &--active {
187
- z-index: 1;
188
- }
189
- }
190
- }
191
-
192
- // Slides container
193
- &-slides {
194
- display: flex;
29
+
30
+ &__scroller {
31
+ position: relative;
32
+ width: 100%;
33
+ height: 100%;
195
34
  overflow-x: auto;
196
- scroll-snap-type: none;
197
- gap: 12px;
198
- padding: 0 16px;
199
- margin: 0 -16px;
200
- -ms-overflow-style: none; // Hide scrollbar on IE/Edge
201
- scrollbar-width: none; // Hide scrollbar on Firefox
202
- scroll-behavior: smooth;
203
-
204
- // Set gap dynamically from data attribute
205
- &[data-gap] {
206
- gap: attr(data-gap px);
207
- }
208
-
209
- // Hide scrollbar on Chrome/Safari
35
+ overflow-y: hidden;
36
+ overscroll-behavior-x: contain;
37
+ scrollbar-width: none;
38
+ cursor: grab;
39
+
210
40
  &::-webkit-scrollbar {
211
41
  display: none;
212
42
  }
213
-
214
- // Force overscroll behavior to be auto (no bounce/elastic effect)
215
- overscroll-behavior: auto;
216
- -webkit-overflow-scrolling: auto; // Disable momentum scrolling on iOS
217
-
218
- // Completely disable all scrolling effects and resistance
219
- &[data-dragging="true"] {
220
- * {
221
- pointer-events: none;
222
- }
223
- }
224
43
  }
225
-
226
- // Individual slide
227
- &-slide {
44
+
45
+ &--snap &__scroller {
46
+ scroll-snap-type: x mandatory;
47
+ }
48
+
49
+ &--snap.#{$component}--vertical &__scroller {
50
+ scroll-snap-type: y mandatory;
51
+ }
52
+
53
+ // A mouse drag turns snapping off until the release scroll settles
54
+ &[data-settling] &__scroller {
55
+ scroll-snap-type: none;
56
+ }
57
+
58
+ &[data-dragging] &__scroller {
59
+ cursor: grabbing;
60
+ }
61
+
62
+ &--vertical &__scroller {
63
+ overflow-x: hidden;
64
+ overflow-y: auto;
65
+ overscroll-behavior-x: auto;
66
+ overscroll-behavior-y: contain;
67
+ }
68
+
69
+ // Sized by the component to the scroll range; holds the snap points and the items
70
+ &__track {
228
71
  position: relative;
229
- flex-shrink: 0;
230
- width: 240px;
231
- height: 320px;
232
- overflow: hidden;
233
- box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
234
- transition: transform 0.2s ease, box-shadow 0.2s ease;
235
- background-color: #FFFFFF;
236
-
237
- // Set border radius dynamically from data attribute
238
- &[data-border-radius] {
239
- border-radius: attr(data-border-radius px);
240
- }
241
-
242
- // Default border radius
243
- border-radius: 16px;
244
-
245
- // Active state
246
- &--active {
247
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
248
- transform: translateY(-4px);
249
- z-index: 1;
250
- }
251
-
252
- // Image container
253
- &-image {
254
- position: relative;
255
- width: 100%;
256
- height: 70%;
257
- overflow: hidden;
258
-
259
- // Ensure images cover the slide properly
260
- img {
261
- width: 100%;
262
- height: 100%;
263
- object-fit: cover;
264
- transition: transform 0.2s ease;
265
- }
266
- }
267
-
268
- // Content container
269
- &-content {
270
- padding: 8px 12px;
271
- height: 30%;
272
- display: flex;
273
- flex-direction: column;
274
- justify-content: space-between;
275
- }
276
-
277
- // Slide title
278
- &-title {
279
- position: absolute;
280
- bottom: 16px;
281
- left: 16px;
282
- color: #FFFFFF;
283
- font-size: 18px;
284
- font-weight: 500;
285
- text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
286
- z-index: 2;
287
- pointer-events: none;
288
-
289
- &.visually-hidden {
290
- position: absolute;
291
- width: 1px;
292
- height: 1px;
293
- padding: 0;
294
- margin: -1px;
295
- overflow: hidden;
296
- clip: rect(0, 0, 0, 0);
297
- white-space: nowrap;
298
- border: 0;
299
- }
300
- }
301
-
302
- // Slide description
303
- &-description {
304
- position: absolute;
305
- bottom: 48px;
306
- left: 16px;
307
- color: #FFFFFF;
308
- font-size: 14px;
309
- max-width: 80%;
310
- z-index: 2;
311
- pointer-events: none;
312
-
313
- &.visually-hidden {
314
- position: absolute;
315
- width: 1px;
316
- height: 1px;
317
- padding: 0;
318
- margin: -1px;
319
- overflow: hidden;
320
- clip: rect(0, 0, 0, 0);
321
- white-space: nowrap;
322
- border: 0;
323
- }
324
- }
325
-
326
- // Overlay for accent color
327
- &-overlay {
328
- position: absolute;
329
- top: 0;
330
- left: 0;
331
- width: 100%;
332
- height: 100%;
333
- background: linear-gradient(to bottom, rgba(0,0,0,0) 50%, rgba(0,0,0,0.6) 100%);
334
- opacity: 0.7;
335
- pointer-events: none;
336
- z-index: 1;
337
- }
338
-
339
- // Button for actions
340
- &-button {
341
- display: inline-block;
342
- position: absolute;
343
- bottom: 16px;
344
- right: 16px;
345
- color: #FFFFFF;
346
- padding: 8px 16px;
347
- border-radius: 20px;
348
- text-decoration: none;
349
- font-size: 14px;
350
- font-weight: 500;
351
- z-index: 2;
352
- background-color: rgba(0, 0, 0, 0.6);
353
-
354
- &:hover {
355
- opacity: 0.9;
356
- }
357
- }
358
-
359
- // Different sizes for multi-browse layout
360
- &--large {
361
- // Styles already set based on layout
362
- }
363
-
364
- &--medium {
365
- // Medium items may show less detail
366
- .#{$component}-slide-content {
367
- padding: 6px 10px;
368
- }
369
-
370
- .#{$component}-slide-title {
371
- font-size: 16px;
372
- }
373
-
374
- .#{$component}-slide-description {
375
- font-size: 12px;
376
- }
377
- }
378
-
379
- &--small {
380
- // Small items are primarily visual with minimal text
381
- .#{$component}-slide-content {
382
- padding: 4px 8px;
383
- }
384
-
385
- // Handle text abbreviation for small items
386
- .#{$component}-slide-title,
387
- .#{$component}-slide-description {
388
- font-size: 12px;
389
- white-space: nowrap;
390
- overflow: hidden;
391
- text-overflow: ellipsis;
392
- }
393
-
394
- .#{$component}-slide-button {
395
- padding: 4px 8px;
396
- font-size: 12px;
397
- }
398
- }
72
+ height: 100%;
399
73
  }
400
-
401
- // Show all link
402
- &-show-all {
403
- display: flex;
404
- align-items: center;
405
- justify-content: flex-end;
406
- padding: 8px 16px;
407
- cursor: pointer;
408
- text-align: right;
409
-
410
- span {
411
- color: #1967D2; // Google blue
412
- font-size: 16px;
413
- font-weight: 500;
414
- transition: color 0.2s ease;
415
- }
416
-
417
- &:hover {
418
- span {
419
- text-decoration: underline;
420
- }
421
- }
422
-
74
+
75
+ &--vertical &__track {
76
+ width: 100%;
77
+ }
78
+
79
+ &__snap {
80
+ position: absolute;
81
+ top: 0;
82
+ left: 0;
83
+ width: 1px;
84
+ height: 1px;
85
+ scroll-snap-align: start;
86
+ pointer-events: none;
87
+ }
88
+
89
+ // Item: positioned and clipped by the component
90
+ &__item {
91
+ position: absolute;
92
+ top: 0;
93
+ left: 0;
94
+ height: 100%;
95
+ overflow: hidden;
96
+ border-radius: var(--#{base.$prefix}-carousel-corner, 28px);
97
+ background-color: t.color('surface-container');
98
+ will-change: transform, clip-path;
99
+ user-select: none;
100
+
423
101
  &:focus {
424
102
  outline: none;
425
103
  }
426
-
104
+
105
+ // Focus ring: 3dp secondary, inside the clip so it stays visible
427
106
  &:focus-visible {
428
- outline: 2px solid t.color('outline');
429
- outline-offset: 2px;
107
+ outline: 3px solid t.color('secondary');
108
+ outline-offset: -3px;
430
109
  }
431
110
  }
432
-
433
- // Drag-related styles
434
- &[data-swipe="true"] {
435
- cursor: grab;
436
- }
437
-
438
- &[data-dragging="true"] {
439
- cursor: grabbing;
440
- }
441
-
442
- // Add parallax effect for multi-browse layout
443
- &[data-enable-parallax="true"] {
444
- .#{$component}-slide-image img {
445
- transition: transform 0.1s ease-out;
446
- }
111
+
112
+ &--vertical &__item {
113
+ width: 100%;
114
+ height: auto;
447
115
  }
448
-
449
- // Navigation elements (can be shown with a CSS class if needed)
450
- &-navigation {
451
- display: none;
452
-
453
- &.visible {
454
- display: flex;
455
- position: absolute;
456
- bottom: 16px;
457
- left: 50%;
458
- transform: translateX(-50%);
459
- justify-content: center;
460
- align-items: center;
461
- gap: 8px;
462
- z-index: 2;
463
- }
116
+
117
+ &__image {
118
+ display: block;
119
+ width: 100%;
120
+ height: 100%;
121
+ object-fit: cover;
122
+ pointer-events: none;
464
123
  }
465
-
466
- // Responsive adjustments for different screen sizes
467
- @media (max-width: 600px) {
468
- // In compact windows, adjust multi-browse layout
469
- &-layout--multi-browse {
470
- .#{$component}-slide {
471
- &--large {
472
- width: 200px;
473
- height: 280px;
474
- }
475
-
476
- &--medium {
477
- width: 150px;
478
- height: 280px;
479
- }
480
- }
481
- }
482
-
483
- // For hero layout, ensure good visibility on mobile
484
- &-layout--hero {
485
- .#{$component}-slide {
486
- &--large {
487
- width: 280px;
488
- height: 320px;
489
- }
490
- }
491
- }
492
-
493
- // General mobile adjustments
494
- &-slide {
495
- &-title {
496
- font-size: 16px;
497
- }
498
-
499
- &-description {
500
- font-size: 12px;
501
- bottom: 42px;
502
- }
503
- }
504
-
505
- &-show-all {
506
- padding: 6px 12px;
507
-
508
- span {
509
- font-size: 14px;
510
- }
124
+
125
+ // Text over the image fades out as the item shrinks
126
+ &__content {
127
+ position: absolute;
128
+ inset: auto 0 0 0;
129
+ padding: 16px;
130
+ color: #fff; // text over the scrim, independent of the theme
131
+ background: linear-gradient(to top, t.alpha('scrim', 0.6), transparent);
132
+ opacity: var(--#{base.$prefix}-carousel-fade, 1);
133
+ pointer-events: none;
134
+
135
+ > * {
136
+ pointer-events: auto;
511
137
  }
512
138
  }
513
-
514
- // Even smaller screens
515
- @media (max-width: 360px) {
516
- // Further compact layout for small screens
517
- &-layout--multi-browse {
518
- .#{$component}-slide {
519
- &--large {
520
- width: 160px;
521
- height: 240px;
522
- }
523
-
524
- &--medium,
525
- &--small {
526
- width: 120px;
527
- height: 240px;
528
- }
529
- }
530
- }
531
-
532
- &-layout--hero {
533
- .#{$component}-slide {
534
- &--large {
535
- width: 240px;
536
- height: 280px;
537
- }
538
- }
539
- }
540
-
541
- &-slide {
542
- &-title {
543
- font-size: 14px;
544
- bottom: 12px;
545
- left: 12px;
546
- }
547
-
548
- &-description {
549
- font-size: 11px;
550
- bottom: 36px;
551
- left: 12px;
552
- }
553
-
554
- &-button {
555
- bottom: 12px;
556
- right: 12px;
557
- padding: 6px 12px;
558
- font-size: 12px;
559
- }
560
- }
561
-
562
- &-show-all {
563
- padding: 4px 10px;
564
-
565
- span {
566
- font-size: 13px;
567
- }
568
- }
139
+
140
+ &__title {
141
+ @include m.typography('title-medium');
142
+ @include m.truncate;
569
143
  }
570
-
571
- // Print styles
572
- @media print {
573
- overflow: visible;
574
-
575
- &-slides {
576
- display: grid;
577
- grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
578
- gap: 16px;
579
- overflow: visible;
580
- }
581
-
582
- &-slide {
583
- page-break-inside: avoid;
584
- break-inside: avoid;
585
- }
586
-
587
- &-show-all,
588
- &-navigation {
589
- display: none; /* Hide navigation and show all for print */
590
- }
144
+
145
+ &__description {
146
+ @include m.typography('body-medium');
147
+ margin-top: 4px;
148
+ @include m.truncate;
591
149
  }
592
-
593
- // RTL support
594
- [dir="rtl"] & {
595
- .#{$component}-slides {
596
- direction: rtl;
597
- }
598
-
599
- .#{$component}-slide {
600
- &-title {
601
- left: auto;
602
- right: 16px;
603
- }
604
-
605
- &-description {
606
- left: auto;
607
- right: 16px;
608
- }
609
-
610
- &-button {
611
- right: auto;
612
- left: 16px;
613
- }
614
- }
615
-
616
- .#{$component}-show-all {
617
- justify-content: flex-start;
618
- }
150
+
151
+ &__button {
152
+ display: inline-block;
153
+ margin-top: 12px;
154
+ color: inherit;
155
+ text-decoration: none;
156
+ @include m.typography('label-large');
619
157
  }
620
-
621
- // Animation for parallax effect
622
- @keyframes slide-in {
623
- 0% {
624
- opacity: 0;
625
- transform: translateX(30px);
626
- }
627
- 100% {
628
- opacity: 1;
629
- transform: translateX(0);
158
+
159
+ // Reduced motion: no smooth scrolling; the component keeps every item one size
160
+ @media (prefers-reduced-motion: reduce) {
161
+ &__scroller {
162
+ scroll-behavior: auto;
630
163
  }
631
164
  }
632
-
633
- // Accessibility classes for text that should be visually hidden
634
- .visually-hidden {
635
- position: absolute;
636
- width: 1px;
637
- height: 1px;
638
- padding: 0;
639
- margin: -1px;
640
- overflow: hidden;
641
- clip: rect(0, 0, 0, 0);
642
- white-space: nowrap;
643
- border: 0;
644
- }
645
- }
165
+ }