mithril-materialized 3.9.0 → 3.11.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.
- package/dist/badge.d.ts +129 -0
- package/dist/components.css +227 -0
- package/dist/index.css +228 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.esm.js +184 -23
- package/dist/index.js +184 -22
- package/dist/index.min.css +2 -2
- package/dist/index.umd.js +184 -22
- package/dist/toast.d.ts +18 -1
- package/dist/utilities.css +1 -0
- package/package.json +1 -1
- package/sass/components/_badge-component.scss +203 -0
- package/sass/components/_toast.scss +8 -0
- package/sass/materialize.scss +2 -0
package/dist/index.umd.js
CHANGED
|
@@ -509,6 +509,106 @@
|
|
|
509
509
|
};
|
|
510
510
|
};
|
|
511
511
|
|
|
512
|
+
/**
|
|
513
|
+
* Badge component
|
|
514
|
+
*
|
|
515
|
+
* Displays a badge anchored to a child element. Commonly used for notifications,
|
|
516
|
+
* counts, or status indicators. Supports flexible positioning, colors, and variants.
|
|
517
|
+
*
|
|
518
|
+
* @example
|
|
519
|
+
* ```typescript
|
|
520
|
+
* // Basic notification badge
|
|
521
|
+
* m(Badge, { badgeContent: 5 },
|
|
522
|
+
* m('button.btn', 'Messages')
|
|
523
|
+
* )
|
|
524
|
+
*
|
|
525
|
+
* // Dot badge on avatar
|
|
526
|
+
* m(Badge, {
|
|
527
|
+
* variant: 'dot',
|
|
528
|
+
* color: 'green',
|
|
529
|
+
* overlap: 'circular'
|
|
530
|
+
* },
|
|
531
|
+
* m('img.circle', { src: 'avatar.jpg' })
|
|
532
|
+
* )
|
|
533
|
+
* ```
|
|
534
|
+
*/
|
|
535
|
+
const Badge = () => {
|
|
536
|
+
return {
|
|
537
|
+
view: ({ attrs, children }) => {
|
|
538
|
+
const { badgeContent, max, anchorOrigin = { vertical: 'top', horizontal: 'right' }, overlap = 'rectangular', variant = 'standard', color = 'red', colorIntensity, invisible = false, showZero = false, 'aria-label': ariaLabel, badgeClassName = '', className = '' } = attrs, params = __rest(attrs, ["badgeContent", "max", "anchorOrigin", "overlap", "variant", "color", "colorIntensity", "invisible", "showZero", 'aria-label', "badgeClassName", "className"]);
|
|
539
|
+
// === VALIDATION: Single child element ===
|
|
540
|
+
const childArray = Array.isArray(children) ? children : children ? [children] : [];
|
|
541
|
+
if (childArray.length === 0) {
|
|
542
|
+
console.warn('Badge component requires a child element');
|
|
543
|
+
return null;
|
|
544
|
+
}
|
|
545
|
+
if (childArray.length > 1) {
|
|
546
|
+
console.warn('Badge component should only wrap a single child element. Using first child only.');
|
|
547
|
+
}
|
|
548
|
+
const child = childArray[0];
|
|
549
|
+
// === VISIBILITY LOGIC ===
|
|
550
|
+
// Hide badge if:
|
|
551
|
+
// 1. invisible prop is true, OR
|
|
552
|
+
// 2. For standard variant: badgeContent is undefined/null OR (badgeContent is 0 AND !showZero)
|
|
553
|
+
const shouldHideBadge = invisible ||
|
|
554
|
+
(variant === 'standard' &&
|
|
555
|
+
(badgeContent === undefined ||
|
|
556
|
+
badgeContent === null ||
|
|
557
|
+
(badgeContent === 0 && !showZero)));
|
|
558
|
+
// === BADGE CONTENT FORMATTING ===
|
|
559
|
+
// Apply max capping: if badgeContent > max, show "max+"
|
|
560
|
+
const getDisplayContent = () => {
|
|
561
|
+
if (variant === 'dot')
|
|
562
|
+
return '';
|
|
563
|
+
if (typeof badgeContent === 'number' && max !== undefined && badgeContent > max) {
|
|
564
|
+
return `${max}+`;
|
|
565
|
+
}
|
|
566
|
+
return String(badgeContent !== null && badgeContent !== void 0 ? badgeContent : '');
|
|
567
|
+
};
|
|
568
|
+
const displayContent = getDisplayContent();
|
|
569
|
+
// === CSS CLASS ASSEMBLY ===
|
|
570
|
+
// Wrapper classes
|
|
571
|
+
const wrapperClasses = ['badge-wrapper', className].filter(Boolean).join(' ').trim() || undefined;
|
|
572
|
+
// Badge element classes - using m-badge prefix to avoid Materialize conflicts
|
|
573
|
+
const positionClass = `m-badge--${anchorOrigin.vertical}-${anchorOrigin.horizontal}`;
|
|
574
|
+
const badgeClasses = [
|
|
575
|
+
'm-badge',
|
|
576
|
+
`m-badge--${variant}`,
|
|
577
|
+
positionClass,
|
|
578
|
+
`m-badge--${overlap}`,
|
|
579
|
+
`m-badge--${color}`,
|
|
580
|
+
colorIntensity ? `m-badge--${colorIntensity}` : '',
|
|
581
|
+
shouldHideBadge ? 'm-badge--invisible' : '',
|
|
582
|
+
badgeClassName,
|
|
583
|
+
]
|
|
584
|
+
.filter(Boolean)
|
|
585
|
+
.join(' ')
|
|
586
|
+
.trim();
|
|
587
|
+
// === ARIA ATTRIBUTES ===
|
|
588
|
+
const badgeAriaLabel = ariaLabel ||
|
|
589
|
+
(variant === 'dot'
|
|
590
|
+
? 'notification indicator'
|
|
591
|
+
: displayContent
|
|
592
|
+
? `${displayContent} notifications`
|
|
593
|
+
: 'notification badge');
|
|
594
|
+
// === RENDER ===
|
|
595
|
+
return m('.badge-wrapper', Object.assign(Object.assign({}, params), { className: wrapperClasses }), [
|
|
596
|
+
// Child element
|
|
597
|
+
child,
|
|
598
|
+
// Badge element - only render if not hidden
|
|
599
|
+
!shouldHideBadge
|
|
600
|
+
? m('span', {
|
|
601
|
+
className: badgeClasses,
|
|
602
|
+
'aria-label': badgeAriaLabel,
|
|
603
|
+
role: 'status',
|
|
604
|
+
'aria-live': 'polite',
|
|
605
|
+
}, variant === 'standard' ? displayContent : null)
|
|
606
|
+
: null,
|
|
607
|
+
]);
|
|
608
|
+
},
|
|
609
|
+
};
|
|
610
|
+
};
|
|
611
|
+
|
|
512
612
|
/**
|
|
513
613
|
* A simple material icon, defined by its icon name.
|
|
514
614
|
*
|
|
@@ -8193,33 +8293,75 @@
|
|
|
8193
8293
|
_createToast() {
|
|
8194
8294
|
const toast = document.createElement('div');
|
|
8195
8295
|
toast.classList.add('toast');
|
|
8196
|
-
// Add custom classes
|
|
8197
|
-
|
|
8198
|
-
|
|
8199
|
-
|
|
8200
|
-
|
|
8296
|
+
// Add custom classes (prefer className over deprecated classes)
|
|
8297
|
+
const customClasses = this.options.className || this.options.classes;
|
|
8298
|
+
if (customClasses) {
|
|
8299
|
+
toast.classList.add(...customClasses.split(' ').filter((c) => c));
|
|
8300
|
+
}
|
|
8301
|
+
// Create content wrapper
|
|
8302
|
+
const contentWrapper = document.createElement('div');
|
|
8303
|
+
contentWrapper.style.cssText = 'display: flex; align-items: center; gap: 12px;';
|
|
8304
|
+
// Set message content
|
|
8305
|
+
const messageEl = document.createElement('div');
|
|
8306
|
+
messageEl.style.flex = '1';
|
|
8201
8307
|
const message = this.options.html;
|
|
8202
8308
|
if (typeof message === 'object' && message && 'nodeType' in message) {
|
|
8203
|
-
|
|
8309
|
+
messageEl.appendChild(message);
|
|
8204
8310
|
}
|
|
8205
8311
|
else {
|
|
8206
|
-
|
|
8312
|
+
messageEl.innerHTML = message;
|
|
8313
|
+
}
|
|
8314
|
+
contentWrapper.appendChild(messageEl);
|
|
8315
|
+
// Add action button if provided
|
|
8316
|
+
if (this.options.action) {
|
|
8317
|
+
const action = this.options.action;
|
|
8318
|
+
const actionBtn = document.createElement('button');
|
|
8319
|
+
actionBtn.className = 'btn-flat toast-action';
|
|
8320
|
+
actionBtn.style.cssText = 'margin: 0; padding: 0 8px; min-width: auto; height: 36px;';
|
|
8321
|
+
if (action.variant === 'icon' && action.icon) {
|
|
8322
|
+
// Icon button variant
|
|
8323
|
+
actionBtn.innerHTML = `<i class="material-icons">${action.icon}</i>`;
|
|
8324
|
+
actionBtn.setAttribute('aria-label', action.label || action.icon);
|
|
8325
|
+
}
|
|
8326
|
+
else {
|
|
8327
|
+
// Flat button variant (default)
|
|
8328
|
+
if (action.icon) {
|
|
8329
|
+
actionBtn.innerHTML = `<i class="material-icons left">${action.icon}</i>${action.label || ''}`;
|
|
8330
|
+
}
|
|
8331
|
+
else {
|
|
8332
|
+
actionBtn.textContent = action.label || '';
|
|
8333
|
+
}
|
|
8334
|
+
}
|
|
8335
|
+
actionBtn.onclick = (e) => {
|
|
8336
|
+
e.stopPropagation();
|
|
8337
|
+
action.onclick();
|
|
8338
|
+
this.dismiss();
|
|
8339
|
+
};
|
|
8340
|
+
contentWrapper.appendChild(actionBtn);
|
|
8207
8341
|
}
|
|
8342
|
+
toast.appendChild(contentWrapper);
|
|
8208
8343
|
// Store reference
|
|
8209
8344
|
toast.M_Toast = this;
|
|
8345
|
+
// Measure natural width BEFORE appending to avoid interference from other toasts
|
|
8346
|
+
// Temporarily append to body in an isolated position
|
|
8347
|
+
toast.style.cssText = 'position: absolute; visibility: hidden; left: -9999px; display: flex;';
|
|
8348
|
+
document.body.appendChild(toast);
|
|
8349
|
+
// Force layout and measure
|
|
8350
|
+
const naturalWidth = toast.offsetWidth;
|
|
8351
|
+
// Remove from body
|
|
8352
|
+
document.body.removeChild(toast);
|
|
8353
|
+
// Lock the width to prevent changes based on other toasts
|
|
8354
|
+
toast.style.cssText = `width: ${naturalWidth}px;`;
|
|
8210
8355
|
// Append to container
|
|
8211
8356
|
Toast._container.appendChild(toast);
|
|
8212
8357
|
return toast;
|
|
8213
8358
|
}
|
|
8214
8359
|
_animateIn() {
|
|
8215
|
-
//
|
|
8216
|
-
|
|
8217
|
-
|
|
8218
|
-
|
|
8219
|
-
|
|
8220
|
-
opacity ${this.options.inDuration}ms cubic-bezier(0.215, 0.61, 0.355, 1);
|
|
8221
|
-
`;
|
|
8222
|
-
// Trigger animation
|
|
8360
|
+
// Width is already locked from _createToast
|
|
8361
|
+
// Set initial animation state (transitions are defined in CSS)
|
|
8362
|
+
this.el.style.transform = 'translateY(35px)';
|
|
8363
|
+
this.el.style.opacity = '0';
|
|
8364
|
+
// Trigger animation after a brief delay to ensure styles are applied
|
|
8223
8365
|
setTimeout(() => {
|
|
8224
8366
|
this.el.style.transform = 'translateY(0)';
|
|
8225
8367
|
this.el.style.opacity = '1';
|
|
@@ -8243,17 +8385,34 @@
|
|
|
8243
8385
|
}
|
|
8244
8386
|
const activationDistance = this.el.offsetWidth * this.options.activationPercent;
|
|
8245
8387
|
if (this.state.wasSwiped) {
|
|
8388
|
+
// Override transition temporarily for swipe
|
|
8246
8389
|
this.el.style.transition = 'transform .05s, opacity .05s';
|
|
8247
8390
|
this.el.style.transform = `translateX(${activationDistance}px)`;
|
|
8248
8391
|
this.el.style.opacity = '0';
|
|
8392
|
+
// Reset transition after swipe animation
|
|
8393
|
+
setTimeout(() => {
|
|
8394
|
+
this.el.style.transition = `opacity ${this.options.outDuration}ms cubic-bezier(0.165, 0.84, 0.44, 1),
|
|
8395
|
+
max-height ${this.options.outDuration}ms cubic-bezier(0.165, 0.84, 0.44, 1),
|
|
8396
|
+
margin-top ${this.options.outDuration}ms cubic-bezier(0.165, 0.84, 0.44, 1),
|
|
8397
|
+
padding-top ${this.options.outDuration}ms cubic-bezier(0.165, 0.84, 0.44, 1),
|
|
8398
|
+
padding-bottom ${this.options.outDuration}ms cubic-bezier(0.165, 0.84, 0.44, 1)`;
|
|
8399
|
+
}, 50);
|
|
8249
8400
|
}
|
|
8250
|
-
|
|
8251
|
-
|
|
8252
|
-
|
|
8253
|
-
|
|
8254
|
-
|
|
8255
|
-
|
|
8256
|
-
|
|
8401
|
+
else {
|
|
8402
|
+
// Set collapse transition timing
|
|
8403
|
+
this.el.style.transition = `opacity ${this.options.outDuration}ms cubic-bezier(0.165, 0.84, 0.44, 1),
|
|
8404
|
+
max-height ${this.options.outDuration}ms cubic-bezier(0.165, 0.84, 0.44, 1),
|
|
8405
|
+
margin-top ${this.options.outDuration}ms cubic-bezier(0.165, 0.84, 0.44, 1),
|
|
8406
|
+
padding-top ${this.options.outDuration}ms cubic-bezier(0.165, 0.84, 0.44, 1),
|
|
8407
|
+
padding-bottom ${this.options.outDuration}ms cubic-bezier(0.165, 0.84, 0.44, 1)`;
|
|
8408
|
+
}
|
|
8409
|
+
// Animate out - collapse height smoothly
|
|
8410
|
+
this.el.style.opacity = '0';
|
|
8411
|
+
this.el.style.maxHeight = '0';
|
|
8412
|
+
this.el.style.marginTop = '0';
|
|
8413
|
+
this.el.style.paddingTop = '0';
|
|
8414
|
+
this.el.style.paddingBottom = '0';
|
|
8415
|
+
this.el.style.overflow = 'hidden';
|
|
8257
8416
|
setTimeout(() => {
|
|
8258
8417
|
// Call completion callback
|
|
8259
8418
|
if (this.options.completeCallback) {
|
|
@@ -8282,8 +8441,10 @@
|
|
|
8282
8441
|
inDuration: 300,
|
|
8283
8442
|
outDuration: 375,
|
|
8284
8443
|
classes: '',
|
|
8444
|
+
className: '',
|
|
8285
8445
|
completeCallback: undefined,
|
|
8286
8446
|
activationPercent: 0.8,
|
|
8447
|
+
action: undefined,
|
|
8287
8448
|
};
|
|
8288
8449
|
Toast._onDragStart = (e) => {
|
|
8289
8450
|
const target = e.target;
|
|
@@ -11035,6 +11196,7 @@
|
|
|
11035
11196
|
exports.AnalogClock = AnalogClock;
|
|
11036
11197
|
exports.AnchorItem = AnchorItem;
|
|
11037
11198
|
exports.Autocomplete = Autocomplete;
|
|
11199
|
+
exports.Badge = Badge;
|
|
11038
11200
|
exports.Breadcrumb = Breadcrumb;
|
|
11039
11201
|
exports.BreadcrumbManager = BreadcrumbManager;
|
|
11040
11202
|
exports.Button = Button;
|
package/dist/toast.d.ts
CHANGED
|
@@ -1,4 +1,14 @@
|
|
|
1
1
|
import { FactoryComponent } from 'mithril';
|
|
2
|
+
export interface ToastAction {
|
|
3
|
+
/** Icon name for the action button */
|
|
4
|
+
icon?: string;
|
|
5
|
+
/** Text label for the action button (if no icon provided) */
|
|
6
|
+
label?: string;
|
|
7
|
+
/** Callback function called when action is clicked */
|
|
8
|
+
onclick: () => void;
|
|
9
|
+
/** Whether to use flat button style (default) or icon button style */
|
|
10
|
+
variant?: 'flat' | 'icon';
|
|
11
|
+
}
|
|
2
12
|
export interface ToastOptions {
|
|
3
13
|
/** HTML content for the toast */
|
|
4
14
|
html?: string;
|
|
@@ -8,12 +18,19 @@ export interface ToastOptions {
|
|
|
8
18
|
inDuration?: number;
|
|
9
19
|
/** Animation out duration in milliseconds */
|
|
10
20
|
outDuration?: number;
|
|
11
|
-
/**
|
|
21
|
+
/**
|
|
22
|
+
* Additional CSS classes
|
|
23
|
+
* @deprecated Use className instead. This property will be removed in a future version.
|
|
24
|
+
*/
|
|
12
25
|
classes?: string;
|
|
26
|
+
/** Additional CSS classes (preferred over deprecated 'classes' property) */
|
|
27
|
+
className?: string;
|
|
13
28
|
/** Callback function called when toast is dismissed */
|
|
14
29
|
completeCallback?: () => void;
|
|
15
30
|
/** Activation percentage for swipe dismissal */
|
|
16
31
|
activationPercent?: number;
|
|
32
|
+
/** Optional action button (for simple confirmations or undo actions) */
|
|
33
|
+
action?: ToastAction;
|
|
17
34
|
}
|
|
18
35
|
export declare class Toast {
|
|
19
36
|
el: HTMLElement;
|
package/dist/utilities.css
CHANGED
|
@@ -3155,6 +3155,7 @@ td, th {
|
|
|
3155
3155
|
align-items: center;
|
|
3156
3156
|
justify-content: space-between;
|
|
3157
3157
|
cursor: default;
|
|
3158
|
+
transition: transform 300ms cubic-bezier(0.215, 0.61, 0.355, 1), opacity 300ms cubic-bezier(0.215, 0.61, 0.355, 1), margin-top 300ms cubic-bezier(0.215, 0.61, 0.355, 1), max-height 300ms cubic-bezier(0.215, 0.61, 0.355, 1), padding-top 300ms cubic-bezier(0.215, 0.61, 0.355, 1), padding-bottom 300ms cubic-bezier(0.215, 0.61, 0.355, 1);
|
|
3158
3159
|
}
|
|
3159
3160
|
.toast .toast-action {
|
|
3160
3161
|
color: #eeff41;
|
package/package.json
CHANGED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
@use 'sass:color';
|
|
2
|
+
@use "variables";
|
|
3
|
+
@use "color-variables";
|
|
4
|
+
|
|
5
|
+
/* Badge Component
|
|
6
|
+
========================================================================== */
|
|
7
|
+
|
|
8
|
+
// Badge component variables
|
|
9
|
+
$badge-size-standard: 20px !default;
|
|
10
|
+
$badge-size-dot: 8px !default;
|
|
11
|
+
$badge-font-size: 12px !default;
|
|
12
|
+
$badge-font-weight: 500 !default;
|
|
13
|
+
$badge-default-color: color-variables.color("red", "base") !default;
|
|
14
|
+
$badge-text-color: #fff !default;
|
|
15
|
+
$badge-border-radius: 10px !default;
|
|
16
|
+
$badge-dot-border-radius: 50% !default;
|
|
17
|
+
|
|
18
|
+
// Wrapper - relative positioning container
|
|
19
|
+
.badge-wrapper {
|
|
20
|
+
position: relative;
|
|
21
|
+
display: inline-flex;
|
|
22
|
+
vertical-align: middle;
|
|
23
|
+
flex-shrink: 0;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Badge element - using .m-badge to avoid conflicts with Materialize's span.badge
|
|
27
|
+
.m-badge {
|
|
28
|
+
position: absolute;
|
|
29
|
+
display: flex;
|
|
30
|
+
align-items: center;
|
|
31
|
+
justify-content: center;
|
|
32
|
+
box-sizing: border-box;
|
|
33
|
+
font-family: Roboto, sans-serif;
|
|
34
|
+
font-weight: $badge-font-weight;
|
|
35
|
+
line-height: 1;
|
|
36
|
+
white-space: nowrap;
|
|
37
|
+
text-align: center;
|
|
38
|
+
border-radius: $badge-border-radius;
|
|
39
|
+
background-color: $badge-default-color;
|
|
40
|
+
color: $badge-text-color;
|
|
41
|
+
z-index: 1;
|
|
42
|
+
transition: transform 225ms cubic-bezier(0.4, 0, 0.2, 1);
|
|
43
|
+
|
|
44
|
+
// Standard variant (shows content)
|
|
45
|
+
&.m-badge--standard {
|
|
46
|
+
min-width: $badge-size-standard;
|
|
47
|
+
height: $badge-size-standard;
|
|
48
|
+
padding: 0 6px;
|
|
49
|
+
font-size: $badge-font-size;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Dot variant (minimal indicator)
|
|
53
|
+
&.m-badge--dot {
|
|
54
|
+
width: $badge-size-dot;
|
|
55
|
+
height: $badge-size-dot;
|
|
56
|
+
min-width: $badge-size-dot;
|
|
57
|
+
padding: 0;
|
|
58
|
+
border-radius: $badge-dot-border-radius;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Invisible state
|
|
62
|
+
&.m-badge--invisible {
|
|
63
|
+
transform: scale(0);
|
|
64
|
+
opacity: 0;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// === POSITIONING: Top-Right (default) ===
|
|
68
|
+
&.m-badge--top-right {
|
|
69
|
+
top: 0;
|
|
70
|
+
right: 0;
|
|
71
|
+
transform: scale(1) translate(50%, -50%);
|
|
72
|
+
transform-origin: 100% 0%;
|
|
73
|
+
|
|
74
|
+
&.m-badge--rectangular {
|
|
75
|
+
transform: scale(1) translate(50%, -50%);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
&.m-badge--circular {
|
|
79
|
+
transform: scale(1) translate(30%, -30%);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
&.m-badge--invisible {
|
|
83
|
+
transform: scale(0) translate(50%, -50%);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// === POSITIONING: Top-Left ===
|
|
88
|
+
&.m-badge--top-left {
|
|
89
|
+
top: 0;
|
|
90
|
+
left: 0;
|
|
91
|
+
transform: scale(1) translate(-50%, -50%);
|
|
92
|
+
transform-origin: 0% 0%;
|
|
93
|
+
|
|
94
|
+
&.m-badge--rectangular {
|
|
95
|
+
transform: scale(1) translate(-50%, -50%);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
&.m-badge--circular {
|
|
99
|
+
transform: scale(1) translate(-30%, -30%);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
&.m-badge--invisible {
|
|
103
|
+
transform: scale(0) translate(-50%, -50%);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// === POSITIONING: Bottom-Right ===
|
|
108
|
+
&.m-badge--bottom-right {
|
|
109
|
+
bottom: 0;
|
|
110
|
+
right: 0;
|
|
111
|
+
transform: scale(1) translate(50%, 50%);
|
|
112
|
+
transform-origin: 100% 100%;
|
|
113
|
+
|
|
114
|
+
&.m-badge--rectangular {
|
|
115
|
+
transform: scale(1) translate(50%, 50%);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
&.m-badge--circular {
|
|
119
|
+
transform: scale(1) translate(30%, 30%);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
&.m-badge--invisible {
|
|
123
|
+
transform: scale(0) translate(50%, 50%);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// === POSITIONING: Bottom-Left ===
|
|
128
|
+
&.m-badge--bottom-left {
|
|
129
|
+
bottom: 0;
|
|
130
|
+
left: 0;
|
|
131
|
+
transform: scale(1) translate(-50%, 50%);
|
|
132
|
+
transform-origin: 0% 100%;
|
|
133
|
+
|
|
134
|
+
&.m-badge--rectangular {
|
|
135
|
+
transform: scale(1) translate(-50%, 50%);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
&.m-badge--circular {
|
|
139
|
+
transform: scale(1) translate(-30%, 30%);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
&.m-badge--invisible {
|
|
143
|
+
transform: scale(0) translate(-50%, 50%);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// === COLOR VARIANTS ===
|
|
149
|
+
// Generate color classes for all MaterialColor options
|
|
150
|
+
.m-badge--red { background-color: color-variables.color("red", "base"); }
|
|
151
|
+
.m-badge--pink { background-color: color-variables.color("pink", "base"); }
|
|
152
|
+
.m-badge--purple { background-color: color-variables.color("purple", "base"); }
|
|
153
|
+
.m-badge--deep-purple { background-color: color-variables.color("deep-purple", "base"); }
|
|
154
|
+
.m-badge--indigo { background-color: color-variables.color("indigo", "base"); }
|
|
155
|
+
.m-badge--blue { background-color: color-variables.color("blue", "base"); }
|
|
156
|
+
.m-badge--light-blue { background-color: color-variables.color("light-blue", "base"); }
|
|
157
|
+
.m-badge--cyan { background-color: color-variables.color("cyan", "base"); }
|
|
158
|
+
.m-badge--teal { background-color: color-variables.color("teal", "base"); }
|
|
159
|
+
.m-badge--green { background-color: color-variables.color("green", "base"); }
|
|
160
|
+
.m-badge--light-green { background-color: color-variables.color("light-green", "base"); }
|
|
161
|
+
.m-badge--lime { background-color: color-variables.color("lime", "base"); }
|
|
162
|
+
.m-badge--yellow {
|
|
163
|
+
background-color: color-variables.color("yellow", "base");
|
|
164
|
+
color: #000; // Dark text for light background
|
|
165
|
+
}
|
|
166
|
+
.m-badge--amber { background-color: color-variables.color("amber", "base"); }
|
|
167
|
+
.m-badge--orange { background-color: color-variables.color("orange", "base"); }
|
|
168
|
+
.m-badge--deep-orange { background-color: color-variables.color("deep-orange", "base"); }
|
|
169
|
+
.m-badge--brown { background-color: color-variables.color("brown", "base"); }
|
|
170
|
+
.m-badge--grey { background-color: color-variables.color("grey", "base"); }
|
|
171
|
+
.m-badge--blue-grey { background-color: color-variables.color("blue-grey", "base"); }
|
|
172
|
+
|
|
173
|
+
// === COLOR INTENSITY MODIFIERS ===
|
|
174
|
+
// Use CSS filters for intensity adjustments (matching CircularProgress pattern)
|
|
175
|
+
.m-badge--lighten-5 { filter: brightness(1.4); }
|
|
176
|
+
.m-badge--lighten-4 { filter: brightness(1.3); }
|
|
177
|
+
.m-badge--lighten-3 { filter: brightness(1.2); }
|
|
178
|
+
.m-badge--lighten-2 { filter: brightness(1.1); }
|
|
179
|
+
.m-badge--lighten-1 { filter: brightness(1.05); }
|
|
180
|
+
.m-badge--darken-1 { filter: brightness(0.95); }
|
|
181
|
+
.m-badge--darken-2 { filter: brightness(0.9); }
|
|
182
|
+
.m-badge--darken-3 { filter: brightness(0.8); }
|
|
183
|
+
.m-badge--darken-4 { filter: brightness(0.7); }
|
|
184
|
+
|
|
185
|
+
// === ACCESSIBILITY ===
|
|
186
|
+
// Ensure badge is not announced multiple times by screen readers
|
|
187
|
+
.m-badge[role="status"] {
|
|
188
|
+
// Screen reader will announce changes due to aria-live="polite"
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// === REDUCED MOTION ===
|
|
192
|
+
@media (prefers-reduced-motion: reduce) {
|
|
193
|
+
.m-badge {
|
|
194
|
+
transition: none;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// === HIGH CONTRAST MODE ===
|
|
199
|
+
@media (prefers-contrast: high) {
|
|
200
|
+
.m-badge {
|
|
201
|
+
border: 2px solid currentColor;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
@@ -43,6 +43,14 @@
|
|
|
43
43
|
justify-content: space-between;
|
|
44
44
|
cursor: default;
|
|
45
45
|
|
|
46
|
+
// Smooth transitions for position changes when toasts are added/removed
|
|
47
|
+
transition: transform 300ms cubic-bezier(0.215, 0.61, 0.355, 1),
|
|
48
|
+
opacity 300ms cubic-bezier(0.215, 0.61, 0.355, 1),
|
|
49
|
+
margin-top 300ms cubic-bezier(0.215, 0.61, 0.355, 1),
|
|
50
|
+
max-height 300ms cubic-bezier(0.215, 0.61, 0.355, 1),
|
|
51
|
+
padding-top 300ms cubic-bezier(0.215, 0.61, 0.355, 1),
|
|
52
|
+
padding-bottom 300ms cubic-bezier(0.215, 0.61, 0.355, 1);
|
|
53
|
+
|
|
46
54
|
.toast-action {
|
|
47
55
|
color: variables.$toast-action-color;
|
|
48
56
|
font-weight: 500;
|