mtrl 0.6.2 → 0.7.0

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/README.md +39 -0
  2. package/dist/README.md +39 -0
  3. package/dist/components/badge/index.d.ts +2 -2
  4. package/dist/components/button/index.d.ts +1 -0
  5. package/dist/components/card/index.d.ts +0 -1
  6. package/dist/components/chips/index.d.ts +3 -4
  7. package/dist/components/datepicker/index.d.ts +3 -3
  8. package/dist/components/dialog/index.d.ts +2 -2
  9. package/dist/components/drawer/api.d.ts +54 -0
  10. package/dist/components/drawer/config.d.ts +106 -0
  11. package/dist/components/drawer/constants.d.ts +97 -0
  12. package/dist/components/drawer/drawer.d.ts +59 -0
  13. package/dist/components/drawer/features/headline.d.ts +17 -0
  14. package/dist/components/drawer/features/index.d.ts +3 -0
  15. package/dist/components/drawer/features/items.d.ts +18 -0
  16. package/dist/components/drawer/features/state.d.ts +18 -0
  17. package/dist/components/drawer/index.d.ts +6 -0
  18. package/dist/components/drawer/types.d.ts +233 -0
  19. package/dist/components/fab/index.d.ts +2 -2
  20. package/dist/components/index.d.ts +38 -47
  21. package/dist/components/menu/constants.d.ts +2 -0
  22. package/dist/components/menu/features/opener.d.ts +1 -1
  23. package/dist/components/menu/types.d.ts +14 -0
  24. package/dist/components/progress/index.d.ts +2 -3
  25. package/dist/components/progress/types.d.ts +8 -0
  26. package/dist/components/search/index.d.ts +0 -1
  27. package/dist/components/segmented-button/index.d.ts +3 -4
  28. package/dist/components/slider/index.d.ts +2 -3
  29. package/dist/components/snackbar/index.d.ts +2 -3
  30. package/dist/components/textfield/index.d.ts +0 -1
  31. package/dist/index.cjs +10 -20
  32. package/dist/index.cjs.map +52 -70
  33. package/dist/index.d.ts +0 -1
  34. package/dist/index.js +10 -20
  35. package/dist/index.js.map +52 -70
  36. package/dist/package.json +1 -1
  37. package/dist/styles.css +2 -2
  38. package/package.json +23 -5
  39. package/src/styles/components/_button-group.scss +1 -1
  40. package/src/styles/components/_button.scss +1 -1
  41. package/src/styles/components/_drawer.scss +611 -0
  42. package/src/styles/components/_extended-fab.scss +1 -1
  43. package/src/styles/components/_fab.scss +1 -1
  44. package/src/styles/components/_icon-button.scss +1 -1
  45. package/src/styles/components/_menu.scss +25 -0
  46. package/src/styles/components/_segmented-button.scss +1 -1
  47. package/src/styles/main.scss +1 -0
  48. package/dist/constants.d.ts +0 -30
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mtrl",
3
- "version": "0.6.2",
3
+ "version": "0.7.0",
4
4
  "description": "A functional TypeScript/JavaScript component library with composable architecture based on Material Design 3",
5
5
  "author": "floor",
6
6
  "license": "MIT License",
@@ -19,6 +19,7 @@
19
19
  "tested",
20
20
  "type-safe"
21
21
  ],
22
+ "sideEffects": false,
22
23
  "type": "module",
23
24
  "main": "./dist/index.cjs",
24
25
  "module": "./dist/index.js",
@@ -30,7 +31,27 @@
30
31
  "require": "./dist/index.cjs",
31
32
  "types": "./dist/index.d.ts"
32
33
  },
33
- "./styles": "./dist/styles.css"
34
+ "./styles": "./dist/styles.css",
35
+ "./components/*": {
36
+ "development": "./src/components/*/index.ts",
37
+ "import": "./dist/components/*/index.js",
38
+ "types": "./dist/components/*/index.d.ts"
39
+ },
40
+ "./components/*/constants": {
41
+ "development": "./src/components/*/constants.ts",
42
+ "import": "./dist/components/*/constants.js",
43
+ "types": "./dist/components/*/constants.d.ts"
44
+ },
45
+ "./core": {
46
+ "development": "./src/core/index.ts",
47
+ "import": "./dist/core/index.js",
48
+ "types": "./dist/core/index.d.ts"
49
+ },
50
+ "./core/*": {
51
+ "development": "./src/core/*/index.ts",
52
+ "import": "./dist/core/*/index.js",
53
+ "types": "./dist/core/*/index.d.ts"
54
+ }
34
55
  },
35
56
  "files": [
36
57
  "dist",
@@ -39,9 +60,6 @@
39
60
  "scripts": {
40
61
  "start": "bun run server.js",
41
62
  "dev": "bun --watch server.js",
42
- "build:demo": "bun run demo/build.ts",
43
- "dev:demo": "bun run demo/build.ts --watch",
44
- "clean:demo": "rm -rf demo/dist",
45
63
  "build:js": "bun build demo/main.js --outfile=demo/dist/bundle.js --format=esm",
46
64
  "build:css": "sass src/styles/main.scss:demo/dist/styles.css --style=compressed",
47
65
  "build": "bun run build:js && bun run build:css",
@@ -390,7 +390,7 @@ $component: '#{base.$prefix}-button-group';
390
390
  // DARK THEME ADJUSTMENTS
391
391
  // =============================================================================
392
392
 
393
- body[data-theme-mode=dark] {
393
+ html[data-theme-mode=dark] {
394
394
  .#{$component} {
395
395
  &--outlined {
396
396
  > .#{base.$prefix}-button {
@@ -717,7 +717,7 @@ $component: '#{base.$prefix}-button';
717
717
  }
718
718
 
719
719
  // FIX: Disabled on dark theme (recommandations do not work as expected)
720
- body[data-theme-mode=dark] {
720
+ html[data-theme-mode=dark] {
721
721
  // Interactive states
722
722
  .#{$component} {
723
723
  &:disabled {
@@ -0,0 +1,611 @@
1
+ // src/styles/components/_drawer.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}-drawer";
9
+
10
+ // Must match DRAWER_DEFAULTS.WIDTH in constants.ts
11
+ // Only used as a CSS fallback if --drawer-width is not set by JS
12
+ $default-width: 360px;
13
+
14
+ // ==========================================================================
15
+ // ROOT CONTAINER
16
+ // ==========================================================================
17
+
18
+ .#{$component} {
19
+ display: flex;
20
+ flex-direction: column;
21
+ position: relative;
22
+ height: 100%;
23
+ z-index: 1200;
24
+ pointer-events: none;
25
+
26
+ // --open modifier enables interaction
27
+ &--open {
28
+ pointer-events: auto;
29
+
30
+ .#{$component}__sheet {
31
+ transform: translateX(0);
32
+ visibility: visible;
33
+ }
34
+
35
+ .#{$component}__scrim {
36
+ pointer-events: auto;
37
+ }
38
+
39
+ .#{$component}__scrim--visible {
40
+ opacity: 1;
41
+ }
42
+ }
43
+
44
+ // ========================================================================
45
+ // POSITION VARIANTS
46
+ // ========================================================================
47
+
48
+ &--start {
49
+ .#{$component}__sheet {
50
+ left: 0;
51
+ right: auto;
52
+ border-top-right-radius: 16px;
53
+ border-bottom-right-radius: 16px;
54
+ border-top-left-radius: 0;
55
+ border-bottom-left-radius: 0;
56
+ transform: translateX(-100%);
57
+ }
58
+
59
+ @include m.rtl {
60
+ .#{$component}__sheet {
61
+ left: auto;
62
+ right: 0;
63
+ border-top-right-radius: 0;
64
+ border-bottom-right-radius: 0;
65
+ border-top-left-radius: 16px;
66
+ border-bottom-left-radius: 16px;
67
+ transform: translateX(100%);
68
+ }
69
+ }
70
+
71
+ &.#{$component}--open {
72
+ .#{$component}__sheet {
73
+ transform: translateX(0);
74
+ }
75
+ }
76
+ }
77
+
78
+ &--end {
79
+ .#{$component}__sheet {
80
+ left: auto;
81
+ right: 0;
82
+ border-top-left-radius: 16px;
83
+ border-bottom-left-radius: 16px;
84
+ border-top-right-radius: 0;
85
+ border-bottom-right-radius: 0;
86
+ transform: translateX(100%);
87
+ }
88
+
89
+ @include m.rtl {
90
+ .#{$component}__sheet {
91
+ left: 0;
92
+ right: auto;
93
+ border-top-left-radius: 0;
94
+ border-bottom-left-radius: 0;
95
+ border-top-right-radius: 16px;
96
+ border-bottom-right-radius: 16px;
97
+ transform: translateX(-100%);
98
+ }
99
+ }
100
+
101
+ &.#{$component}--open {
102
+ .#{$component}__sheet {
103
+ transform: translateX(0);
104
+ }
105
+ }
106
+ }
107
+
108
+ // ========================================================================
109
+ // VARIANT: STANDARD
110
+ // ========================================================================
111
+
112
+ &--standard {
113
+ position: relative;
114
+ z-index: auto;
115
+ pointer-events: auto;
116
+ flex-shrink: 0;
117
+ overflow: hidden;
118
+ // Closed: collapse to 0. --drawer-width is set inline by the JS component.
119
+ width: 0;
120
+ transition: width v.motion("duration-short4")
121
+ v.motion("easing-standard");
122
+
123
+ .#{$component}__sheet {
124
+ position: relative;
125
+ background-color: t.color("surface");
126
+ @include m.elevation(0);
127
+ }
128
+
129
+ // Open — expand to the concrete width value via custom property
130
+ &.#{$component}--open {
131
+ width: var(--drawer-width, $default-width);
132
+ }
133
+
134
+ // Border on the end edge (standard variant visual separator)
135
+ &.#{$component}--start {
136
+ .#{$component}__sheet {
137
+ border-right: 1px solid t.color("outline-variant");
138
+ border-top-right-radius: 0;
139
+ border-bottom-right-radius: 0;
140
+ }
141
+
142
+ @include m.rtl {
143
+ .#{$component}__sheet {
144
+ border-right: none;
145
+ border-left: 1px solid t.color("outline-variant");
146
+ border-top-left-radius: 0;
147
+ border-bottom-left-radius: 0;
148
+ }
149
+ }
150
+ }
151
+
152
+ &.#{$component}--end {
153
+ .#{$component}__sheet {
154
+ border-left: 1px solid t.color("outline-variant");
155
+ border-top-left-radius: 0;
156
+ border-bottom-left-radius: 0;
157
+ }
158
+
159
+ @include m.rtl {
160
+ .#{$component}__sheet {
161
+ border-left: none;
162
+ border-right: 1px solid t.color("outline-variant");
163
+ border-top-right-radius: 0;
164
+ border-bottom-right-radius: 0;
165
+ }
166
+ }
167
+ }
168
+ }
169
+
170
+ // ========================================================================
171
+ // VARIANT: MODAL
172
+ // ========================================================================
173
+
174
+ &--modal {
175
+ position: fixed;
176
+ top: 0;
177
+ left: 0;
178
+ right: 0;
179
+ bottom: 0;
180
+ z-index: 1200;
181
+
182
+ .#{$component}__sheet {
183
+ position: absolute;
184
+ top: 0;
185
+ bottom: 0;
186
+ background-color: t.color("surface-container-low");
187
+ @include m.elevation(1);
188
+ }
189
+ }
190
+
191
+ // ========================================================================
192
+ // SHEET (inner panel)
193
+ // ========================================================================
194
+
195
+ &__sheet {
196
+ display: flex;
197
+ flex-direction: column;
198
+ width: var(--drawer-width, $default-width);
199
+ max-width: calc(100vw - 56px);
200
+ height: 100%;
201
+ overflow-y: auto;
202
+ overflow-x: hidden;
203
+ box-sizing: border-box;
204
+ padding: 12px 0;
205
+ @include m.motion-transition(transform, visibility);
206
+
207
+ // Smooth scrollbar
208
+ scrollbar-width: thin;
209
+ scrollbar-color: t.color("outline-variant") transparent;
210
+ }
211
+
212
+ // ========================================================================
213
+ // SCRIM (modal overlay)
214
+ // ========================================================================
215
+
216
+ &__scrim {
217
+ position: fixed;
218
+ top: 0;
219
+ left: 0;
220
+ right: 0;
221
+ bottom: 0;
222
+ background-color: t.alpha("scrim", 0.32);
223
+ opacity: 0;
224
+ pointer-events: none;
225
+ z-index: -1;
226
+ @include m.motion-transition(opacity);
227
+ }
228
+
229
+ // ========================================================================
230
+ // HEADLINE
231
+ // ========================================================================
232
+
233
+ &__headline {
234
+ @include m.typography("title-small");
235
+ color: t.color("on-surface-variant");
236
+ padding: 16px 28px 0;
237
+ margin-bottom: 4px;
238
+ white-space: nowrap;
239
+ overflow: hidden;
240
+ text-overflow: ellipsis;
241
+ }
242
+
243
+ // ========================================================================
244
+ // ITEMS CONTAINER
245
+ // ========================================================================
246
+
247
+ &__items {
248
+ display: flex;
249
+ flex-direction: column;
250
+ flex: 1;
251
+ padding: 0;
252
+ margin: 0;
253
+ }
254
+
255
+ // ========================================================================
256
+ // NAVIGATION ITEM
257
+ // ========================================================================
258
+
259
+ &__item {
260
+ display: flex;
261
+ align-items: center;
262
+ position: relative;
263
+ width: calc(100% - 24px);
264
+ height: 56px;
265
+ margin: 0 12px;
266
+ padding: 0 24px 0 16px;
267
+ gap: 12px;
268
+ border: none;
269
+ border-radius: 28px;
270
+ background: none;
271
+ cursor: pointer;
272
+ color: t.color("on-surface-variant");
273
+ text-decoration: none;
274
+ box-sizing: border-box;
275
+ outline: none;
276
+ -webkit-tap-highlight-color: transparent;
277
+ @include m.typography("label-large");
278
+ @include m.motion-transition(background-color, color);
279
+
280
+ // Active indicator (background shape — positioned behind content)
281
+ .#{$component}__active-indicator {
282
+ position: absolute;
283
+ inset: 0;
284
+ border-radius: 28px;
285
+ background-color: transparent;
286
+ pointer-events: none;
287
+ @include m.motion-transition(background-color);
288
+ }
289
+
290
+ // ======================================================================
291
+ // HOVER STATE
292
+ // ======================================================================
293
+
294
+ &:hover:not([disabled]) {
295
+ .#{$component}__active-indicator {
296
+ background-color: t.alpha("on-surface", 0.08);
297
+ }
298
+ }
299
+
300
+ // ======================================================================
301
+ // FOCUS-VISIBLE STATE
302
+ // ======================================================================
303
+
304
+ &:focus-visible {
305
+ .#{$component}__active-indicator {
306
+ background-color: t.alpha("on-surface", 0.12);
307
+ }
308
+ }
309
+
310
+ // ======================================================================
311
+ // PRESSED STATE
312
+ // ======================================================================
313
+
314
+ &:active:not([disabled]) {
315
+ .#{$component}__active-indicator {
316
+ background-color: t.alpha("on-surface", 0.12);
317
+ }
318
+ }
319
+
320
+ // ======================================================================
321
+ // ACTIVE / SELECTED STATE
322
+ // ======================================================================
323
+
324
+ &--active {
325
+ color: t.color("on-secondary-container");
326
+
327
+ .#{$component}__active-indicator {
328
+ background-color: t.color("secondary-container");
329
+ }
330
+
331
+ &:hover:not([disabled]) {
332
+ .#{$component}__active-indicator {
333
+ background-color: t.color("secondary-container");
334
+
335
+ // Overlay hover state on top of active indicator
336
+ &::before {
337
+ content: "";
338
+ position: absolute;
339
+ inset: 0;
340
+ border-radius: inherit;
341
+ background-color: t.alpha(
342
+ "on-secondary-container",
343
+ 0.08
344
+ );
345
+ pointer-events: none;
346
+ }
347
+ }
348
+ }
349
+
350
+ &:focus-visible {
351
+ .#{$component}__active-indicator {
352
+ background-color: t.color("secondary-container");
353
+
354
+ &::before {
355
+ content: "";
356
+ position: absolute;
357
+ inset: 0;
358
+ border-radius: inherit;
359
+ background-color: t.alpha(
360
+ "on-secondary-container",
361
+ 0.12
362
+ );
363
+ pointer-events: none;
364
+ }
365
+ }
366
+ }
367
+
368
+ &:active:not([disabled]) {
369
+ .#{$component}__active-indicator {
370
+ background-color: t.color("secondary-container");
371
+
372
+ &::before {
373
+ content: "";
374
+ position: absolute;
375
+ inset: 0;
376
+ border-radius: inherit;
377
+ background-color: t.alpha(
378
+ "on-secondary-container",
379
+ 0.12
380
+ );
381
+ pointer-events: none;
382
+ }
383
+ }
384
+ }
385
+
386
+ .#{$component}__item-icon {
387
+ color: t.color("on-secondary-container");
388
+ }
389
+
390
+ .#{$component}__item-label {
391
+ color: t.color("on-secondary-container");
392
+ font-weight: 700;
393
+ }
394
+
395
+ .#{$component}__item-badge {
396
+ color: t.color("on-secondary-container");
397
+ }
398
+ }
399
+
400
+ // ======================================================================
401
+ // DISABLED STATE
402
+ // ======================================================================
403
+
404
+ &[disabled] {
405
+ cursor: default;
406
+ opacity: 0.38;
407
+ pointer-events: none;
408
+ }
409
+ }
410
+
411
+ // ========================================================================
412
+ // ITEM ICON
413
+ // ========================================================================
414
+
415
+ &__item-icon {
416
+ display: flex;
417
+ align-items: center;
418
+ justify-content: center;
419
+ flex-shrink: 0;
420
+ width: 24px;
421
+ height: 24px;
422
+ color: inherit;
423
+ position: relative;
424
+ z-index: 1;
425
+
426
+ svg {
427
+ width: 24px;
428
+ height: 24px;
429
+ fill: currentColor;
430
+ }
431
+ }
432
+
433
+ // ========================================================================
434
+ // ITEM LABEL
435
+ // ========================================================================
436
+
437
+ &__item-label {
438
+ flex: 1;
439
+ text-align: left;
440
+ white-space: nowrap;
441
+ overflow: hidden;
442
+ text-overflow: ellipsis;
443
+ position: relative;
444
+ z-index: 1;
445
+ color: inherit;
446
+
447
+ @include m.rtl {
448
+ text-align: right;
449
+ }
450
+ }
451
+
452
+ // ========================================================================
453
+ // ITEM BADGE
454
+ // ========================================================================
455
+
456
+ &__item-badge {
457
+ @include m.typography("label-large");
458
+ color: t.color("on-surface-variant");
459
+ margin-left: auto;
460
+ position: relative;
461
+ z-index: 1;
462
+ flex-shrink: 0;
463
+
464
+ @include m.rtl {
465
+ margin-left: 0;
466
+ margin-right: auto;
467
+ }
468
+ }
469
+
470
+ // ========================================================================
471
+ // DIVIDER
472
+ // ========================================================================
473
+
474
+ &__divider {
475
+ width: calc(100% - 56px);
476
+ height: 1px;
477
+ margin: 8px 28px;
478
+ border: none;
479
+ background-color: t.color("outline-variant");
480
+ }
481
+
482
+ // ========================================================================
483
+ // SECTION LABEL
484
+ // ========================================================================
485
+
486
+ &__section-label {
487
+ @include m.typography("title-small");
488
+ color: t.color("on-surface-variant");
489
+ padding: 18px 28px 4px;
490
+ white-space: nowrap;
491
+ overflow: hidden;
492
+ text-overflow: ellipsis;
493
+ }
494
+
495
+ // ========================================================================
496
+ // DENSE VARIANT — compact items for admin UIs
497
+ // ========================================================================
498
+
499
+ &--dense {
500
+ .#{$component}__sheet {
501
+ padding: 8px 0;
502
+ }
503
+
504
+ .#{$component}__headline {
505
+ @include m.typography("label-large");
506
+ padding: 10px 20px 0;
507
+ margin-bottom: 2px;
508
+ }
509
+
510
+ .#{$component}__item {
511
+ height: 36px;
512
+ margin: 0 8px;
513
+ padding: 0 16px 0 12px;
514
+ width: calc(100% - 16px);
515
+ gap: 8px;
516
+ border-radius: 18px;
517
+ @include m.typography("body-medium");
518
+
519
+ .#{$component}__active-indicator {
520
+ border-radius: 18px;
521
+ }
522
+ }
523
+
524
+ .#{$component}__item-icon {
525
+ width: 20px;
526
+ height: 20px;
527
+
528
+ svg {
529
+ width: 20px;
530
+ height: 20px;
531
+ }
532
+ }
533
+
534
+ .#{$component}__item-badge {
535
+ @include m.typography("label-medium");
536
+ }
537
+
538
+ .#{$component}__divider {
539
+ width: calc(100% - 40px);
540
+ margin: 4px 20px;
541
+ }
542
+
543
+ .#{$component}__section-label {
544
+ @include m.typography("label-large");
545
+ padding: 10px 20px 2px;
546
+ }
547
+ }
548
+
549
+ // ========================================================================
550
+ // RTL — item padding direction
551
+ // ========================================================================
552
+
553
+ @include m.rtl {
554
+ &__item {
555
+ padding: 0 16px 0 24px;
556
+ }
557
+
558
+ &--dense {
559
+ .#{$component}__item {
560
+ padding: 0 12px 0 16px;
561
+ }
562
+ }
563
+ }
564
+
565
+ // ========================================================================
566
+ // REDUCED MOTION
567
+ // ========================================================================
568
+
569
+ @include m.reduced-motion {
570
+ &__sheet {
571
+ transition: none;
572
+ }
573
+
574
+ &__scrim {
575
+ transition: none;
576
+ }
577
+
578
+ &__item,
579
+ &__active-indicator {
580
+ transition: none;
581
+ }
582
+ }
583
+
584
+ // ========================================================================
585
+ // HIGH CONTRAST (forced-colors)
586
+ // ========================================================================
587
+
588
+ @include m.high-contrast {
589
+ &__sheet {
590
+ border: 1px solid currentColor;
591
+ }
592
+
593
+ &__item {
594
+ &--active {
595
+ .#{$component}__active-indicator {
596
+ outline: 2px solid currentColor;
597
+ outline-offset: -2px;
598
+ }
599
+ }
600
+
601
+ &:focus-visible {
602
+ outline: 2px solid currentColor;
603
+ outline-offset: -2px;
604
+ }
605
+ }
606
+
607
+ &__divider {
608
+ background-color: currentColor;
609
+ }
610
+ }
611
+ }
@@ -290,7 +290,7 @@ $component: '#{base.$prefix}-extended-fab';
290
290
 
291
291
 
292
292
  // FIX: Disabled on dark theme (recommandations do not work as expected)
293
- body[data-theme-mode=dark] {
293
+ html[data-theme-mode=dark] {
294
294
  // Interactive states
295
295
  .#{$component} {
296
296
  &:disabled {
@@ -225,7 +225,7 @@ $component: '#{base.$prefix}-fab';
225
225
  }
226
226
 
227
227
  // FIX: Disabled on dark theme (recommandations do not work as expected)
228
- body[data-theme-mode=dark] {
228
+ html[data-theme-mode=dark] {
229
229
  // Interactive states
230
230
  .#{$component} {
231
231
  &:disabled {