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.esm.js
CHANGED
|
@@ -505,6 +505,106 @@ const Autocomplete = () => {
|
|
|
505
505
|
};
|
|
506
506
|
};
|
|
507
507
|
|
|
508
|
+
/**
|
|
509
|
+
* Badge component
|
|
510
|
+
*
|
|
511
|
+
* Displays a badge anchored to a child element. Commonly used for notifications,
|
|
512
|
+
* counts, or status indicators. Supports flexible positioning, colors, and variants.
|
|
513
|
+
*
|
|
514
|
+
* @example
|
|
515
|
+
* ```typescript
|
|
516
|
+
* // Basic notification badge
|
|
517
|
+
* m(Badge, { badgeContent: 5 },
|
|
518
|
+
* m('button.btn', 'Messages')
|
|
519
|
+
* )
|
|
520
|
+
*
|
|
521
|
+
* // Dot badge on avatar
|
|
522
|
+
* m(Badge, {
|
|
523
|
+
* variant: 'dot',
|
|
524
|
+
* color: 'green',
|
|
525
|
+
* overlap: 'circular'
|
|
526
|
+
* },
|
|
527
|
+
* m('img.circle', { src: 'avatar.jpg' })
|
|
528
|
+
* )
|
|
529
|
+
* ```
|
|
530
|
+
*/
|
|
531
|
+
const Badge = () => {
|
|
532
|
+
return {
|
|
533
|
+
view: ({ attrs, children }) => {
|
|
534
|
+
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"]);
|
|
535
|
+
// === VALIDATION: Single child element ===
|
|
536
|
+
const childArray = Array.isArray(children) ? children : children ? [children] : [];
|
|
537
|
+
if (childArray.length === 0) {
|
|
538
|
+
console.warn('Badge component requires a child element');
|
|
539
|
+
return null;
|
|
540
|
+
}
|
|
541
|
+
if (childArray.length > 1) {
|
|
542
|
+
console.warn('Badge component should only wrap a single child element. Using first child only.');
|
|
543
|
+
}
|
|
544
|
+
const child = childArray[0];
|
|
545
|
+
// === VISIBILITY LOGIC ===
|
|
546
|
+
// Hide badge if:
|
|
547
|
+
// 1. invisible prop is true, OR
|
|
548
|
+
// 2. For standard variant: badgeContent is undefined/null OR (badgeContent is 0 AND !showZero)
|
|
549
|
+
const shouldHideBadge = invisible ||
|
|
550
|
+
(variant === 'standard' &&
|
|
551
|
+
(badgeContent === undefined ||
|
|
552
|
+
badgeContent === null ||
|
|
553
|
+
(badgeContent === 0 && !showZero)));
|
|
554
|
+
// === BADGE CONTENT FORMATTING ===
|
|
555
|
+
// Apply max capping: if badgeContent > max, show "max+"
|
|
556
|
+
const getDisplayContent = () => {
|
|
557
|
+
if (variant === 'dot')
|
|
558
|
+
return '';
|
|
559
|
+
if (typeof badgeContent === 'number' && max !== undefined && badgeContent > max) {
|
|
560
|
+
return `${max}+`;
|
|
561
|
+
}
|
|
562
|
+
return String(badgeContent !== null && badgeContent !== void 0 ? badgeContent : '');
|
|
563
|
+
};
|
|
564
|
+
const displayContent = getDisplayContent();
|
|
565
|
+
// === CSS CLASS ASSEMBLY ===
|
|
566
|
+
// Wrapper classes
|
|
567
|
+
const wrapperClasses = ['badge-wrapper', className].filter(Boolean).join(' ').trim() || undefined;
|
|
568
|
+
// Badge element classes - using m-badge prefix to avoid Materialize conflicts
|
|
569
|
+
const positionClass = `m-badge--${anchorOrigin.vertical}-${anchorOrigin.horizontal}`;
|
|
570
|
+
const badgeClasses = [
|
|
571
|
+
'm-badge',
|
|
572
|
+
`m-badge--${variant}`,
|
|
573
|
+
positionClass,
|
|
574
|
+
`m-badge--${overlap}`,
|
|
575
|
+
`m-badge--${color}`,
|
|
576
|
+
colorIntensity ? `m-badge--${colorIntensity}` : '',
|
|
577
|
+
shouldHideBadge ? 'm-badge--invisible' : '',
|
|
578
|
+
badgeClassName,
|
|
579
|
+
]
|
|
580
|
+
.filter(Boolean)
|
|
581
|
+
.join(' ')
|
|
582
|
+
.trim();
|
|
583
|
+
// === ARIA ATTRIBUTES ===
|
|
584
|
+
const badgeAriaLabel = ariaLabel ||
|
|
585
|
+
(variant === 'dot'
|
|
586
|
+
? 'notification indicator'
|
|
587
|
+
: displayContent
|
|
588
|
+
? `${displayContent} notifications`
|
|
589
|
+
: 'notification badge');
|
|
590
|
+
// === RENDER ===
|
|
591
|
+
return m('.badge-wrapper', Object.assign(Object.assign({}, params), { className: wrapperClasses }), [
|
|
592
|
+
// Child element
|
|
593
|
+
child,
|
|
594
|
+
// Badge element - only render if not hidden
|
|
595
|
+
!shouldHideBadge
|
|
596
|
+
? m('span', {
|
|
597
|
+
className: badgeClasses,
|
|
598
|
+
'aria-label': badgeAriaLabel,
|
|
599
|
+
role: 'status',
|
|
600
|
+
'aria-live': 'polite',
|
|
601
|
+
}, variant === 'standard' ? displayContent : null)
|
|
602
|
+
: null,
|
|
603
|
+
]);
|
|
604
|
+
},
|
|
605
|
+
};
|
|
606
|
+
};
|
|
607
|
+
|
|
508
608
|
/**
|
|
509
609
|
* A simple material icon, defined by its icon name.
|
|
510
610
|
*
|
|
@@ -8189,33 +8289,75 @@ class Toast {
|
|
|
8189
8289
|
_createToast() {
|
|
8190
8290
|
const toast = document.createElement('div');
|
|
8191
8291
|
toast.classList.add('toast');
|
|
8192
|
-
// Add custom classes
|
|
8193
|
-
|
|
8194
|
-
|
|
8195
|
-
|
|
8196
|
-
|
|
8292
|
+
// Add custom classes (prefer className over deprecated classes)
|
|
8293
|
+
const customClasses = this.options.className || this.options.classes;
|
|
8294
|
+
if (customClasses) {
|
|
8295
|
+
toast.classList.add(...customClasses.split(' ').filter((c) => c));
|
|
8296
|
+
}
|
|
8297
|
+
// Create content wrapper
|
|
8298
|
+
const contentWrapper = document.createElement('div');
|
|
8299
|
+
contentWrapper.style.cssText = 'display: flex; align-items: center; gap: 12px;';
|
|
8300
|
+
// Set message content
|
|
8301
|
+
const messageEl = document.createElement('div');
|
|
8302
|
+
messageEl.style.flex = '1';
|
|
8197
8303
|
const message = this.options.html;
|
|
8198
8304
|
if (typeof message === 'object' && message && 'nodeType' in message) {
|
|
8199
|
-
|
|
8305
|
+
messageEl.appendChild(message);
|
|
8200
8306
|
}
|
|
8201
8307
|
else {
|
|
8202
|
-
|
|
8308
|
+
messageEl.innerHTML = message;
|
|
8309
|
+
}
|
|
8310
|
+
contentWrapper.appendChild(messageEl);
|
|
8311
|
+
// Add action button if provided
|
|
8312
|
+
if (this.options.action) {
|
|
8313
|
+
const action = this.options.action;
|
|
8314
|
+
const actionBtn = document.createElement('button');
|
|
8315
|
+
actionBtn.className = 'btn-flat toast-action';
|
|
8316
|
+
actionBtn.style.cssText = 'margin: 0; padding: 0 8px; min-width: auto; height: 36px;';
|
|
8317
|
+
if (action.variant === 'icon' && action.icon) {
|
|
8318
|
+
// Icon button variant
|
|
8319
|
+
actionBtn.innerHTML = `<i class="material-icons">${action.icon}</i>`;
|
|
8320
|
+
actionBtn.setAttribute('aria-label', action.label || action.icon);
|
|
8321
|
+
}
|
|
8322
|
+
else {
|
|
8323
|
+
// Flat button variant (default)
|
|
8324
|
+
if (action.icon) {
|
|
8325
|
+
actionBtn.innerHTML = `<i class="material-icons left">${action.icon}</i>${action.label || ''}`;
|
|
8326
|
+
}
|
|
8327
|
+
else {
|
|
8328
|
+
actionBtn.textContent = action.label || '';
|
|
8329
|
+
}
|
|
8330
|
+
}
|
|
8331
|
+
actionBtn.onclick = (e) => {
|
|
8332
|
+
e.stopPropagation();
|
|
8333
|
+
action.onclick();
|
|
8334
|
+
this.dismiss();
|
|
8335
|
+
};
|
|
8336
|
+
contentWrapper.appendChild(actionBtn);
|
|
8203
8337
|
}
|
|
8338
|
+
toast.appendChild(contentWrapper);
|
|
8204
8339
|
// Store reference
|
|
8205
8340
|
toast.M_Toast = this;
|
|
8341
|
+
// Measure natural width BEFORE appending to avoid interference from other toasts
|
|
8342
|
+
// Temporarily append to body in an isolated position
|
|
8343
|
+
toast.style.cssText = 'position: absolute; visibility: hidden; left: -9999px; display: flex;';
|
|
8344
|
+
document.body.appendChild(toast);
|
|
8345
|
+
// Force layout and measure
|
|
8346
|
+
const naturalWidth = toast.offsetWidth;
|
|
8347
|
+
// Remove from body
|
|
8348
|
+
document.body.removeChild(toast);
|
|
8349
|
+
// Lock the width to prevent changes based on other toasts
|
|
8350
|
+
toast.style.cssText = `width: ${naturalWidth}px;`;
|
|
8206
8351
|
// Append to container
|
|
8207
8352
|
Toast._container.appendChild(toast);
|
|
8208
8353
|
return toast;
|
|
8209
8354
|
}
|
|
8210
8355
|
_animateIn() {
|
|
8211
|
-
//
|
|
8212
|
-
|
|
8213
|
-
|
|
8214
|
-
|
|
8215
|
-
|
|
8216
|
-
opacity ${this.options.inDuration}ms cubic-bezier(0.215, 0.61, 0.355, 1);
|
|
8217
|
-
`;
|
|
8218
|
-
// Trigger animation
|
|
8356
|
+
// Width is already locked from _createToast
|
|
8357
|
+
// Set initial animation state (transitions are defined in CSS)
|
|
8358
|
+
this.el.style.transform = 'translateY(35px)';
|
|
8359
|
+
this.el.style.opacity = '0';
|
|
8360
|
+
// Trigger animation after a brief delay to ensure styles are applied
|
|
8219
8361
|
setTimeout(() => {
|
|
8220
8362
|
this.el.style.transform = 'translateY(0)';
|
|
8221
8363
|
this.el.style.opacity = '1';
|
|
@@ -8239,17 +8381,34 @@ class Toast {
|
|
|
8239
8381
|
}
|
|
8240
8382
|
const activationDistance = this.el.offsetWidth * this.options.activationPercent;
|
|
8241
8383
|
if (this.state.wasSwiped) {
|
|
8384
|
+
// Override transition temporarily for swipe
|
|
8242
8385
|
this.el.style.transition = 'transform .05s, opacity .05s';
|
|
8243
8386
|
this.el.style.transform = `translateX(${activationDistance}px)`;
|
|
8244
8387
|
this.el.style.opacity = '0';
|
|
8388
|
+
// Reset transition after swipe animation
|
|
8389
|
+
setTimeout(() => {
|
|
8390
|
+
this.el.style.transition = `opacity ${this.options.outDuration}ms cubic-bezier(0.165, 0.84, 0.44, 1),
|
|
8391
|
+
max-height ${this.options.outDuration}ms cubic-bezier(0.165, 0.84, 0.44, 1),
|
|
8392
|
+
margin-top ${this.options.outDuration}ms cubic-bezier(0.165, 0.84, 0.44, 1),
|
|
8393
|
+
padding-top ${this.options.outDuration}ms cubic-bezier(0.165, 0.84, 0.44, 1),
|
|
8394
|
+
padding-bottom ${this.options.outDuration}ms cubic-bezier(0.165, 0.84, 0.44, 1)`;
|
|
8395
|
+
}, 50);
|
|
8245
8396
|
}
|
|
8246
|
-
|
|
8247
|
-
|
|
8248
|
-
|
|
8249
|
-
|
|
8250
|
-
|
|
8251
|
-
|
|
8252
|
-
|
|
8397
|
+
else {
|
|
8398
|
+
// Set collapse transition timing
|
|
8399
|
+
this.el.style.transition = `opacity ${this.options.outDuration}ms cubic-bezier(0.165, 0.84, 0.44, 1),
|
|
8400
|
+
max-height ${this.options.outDuration}ms cubic-bezier(0.165, 0.84, 0.44, 1),
|
|
8401
|
+
margin-top ${this.options.outDuration}ms cubic-bezier(0.165, 0.84, 0.44, 1),
|
|
8402
|
+
padding-top ${this.options.outDuration}ms cubic-bezier(0.165, 0.84, 0.44, 1),
|
|
8403
|
+
padding-bottom ${this.options.outDuration}ms cubic-bezier(0.165, 0.84, 0.44, 1)`;
|
|
8404
|
+
}
|
|
8405
|
+
// Animate out - collapse height smoothly
|
|
8406
|
+
this.el.style.opacity = '0';
|
|
8407
|
+
this.el.style.maxHeight = '0';
|
|
8408
|
+
this.el.style.marginTop = '0';
|
|
8409
|
+
this.el.style.paddingTop = '0';
|
|
8410
|
+
this.el.style.paddingBottom = '0';
|
|
8411
|
+
this.el.style.overflow = 'hidden';
|
|
8253
8412
|
setTimeout(() => {
|
|
8254
8413
|
// Call completion callback
|
|
8255
8414
|
if (this.options.completeCallback) {
|
|
@@ -8278,8 +8437,10 @@ Toast.defaults = {
|
|
|
8278
8437
|
inDuration: 300,
|
|
8279
8438
|
outDuration: 375,
|
|
8280
8439
|
classes: '',
|
|
8440
|
+
className: '',
|
|
8281
8441
|
completeCallback: undefined,
|
|
8282
8442
|
activationPercent: 0.8,
|
|
8443
|
+
action: undefined,
|
|
8283
8444
|
};
|
|
8284
8445
|
Toast._onDragStart = (e) => {
|
|
8285
8446
|
const target = e.target;
|
|
@@ -11028,4 +11189,4 @@ const isValidationError = (result) => !isValidationSuccess(result);
|
|
|
11028
11189
|
// ============================================================================
|
|
11029
11190
|
// All types are already exported via individual export declarations above
|
|
11030
11191
|
|
|
11031
|
-
export { AnalogClock, AnchorItem, Autocomplete, Breadcrumb, BreadcrumbManager, Button, ButtonFactory, Carousel, CharacterCounter, Chips, CircularProgress, CodeBlock, Collapsible, CollapsibleItem, Collection, CollectionMode, ColorInput, DataTable, DatePicker, DigitalClock, DoubleRangeSlider, Dropdown, EmailInput, FileInput, FileUpload, FlatButton, FloatingActionButton, HelperText, Icon, IconButton, ImageList, InputCheckbox, Label, LargeButton, LinearProgress, ListItem, Mandatory, Masonry, MaterialBox, MaterialIcon, ModalPanel, NumberInput, Options, OptionsList, Pagination, PaginationControls, Parallax, PasswordInput, Pushpin, PushpinComponent, RadioButton, RadioButtons, RangeInput, Rating, RoundIconButton, SearchSelect, SecondaryContent, Select, Sidenav, SidenavItem, SidenavManager, SingleRangeSlider, SmallButton, Stepper, SubmitButton, Switch, Tabs, TextArea, TextInput, ThemeManager, ThemeSwitcher, ThemeToggle, TimePicker, TimeRangePicker, Timeline, Toast, ToastComponent, ToggleGroup, Tooltip, TooltipComponent, TreeView, UrlInput, Wizard, addLeadingZero, clearPortal, createBreadcrumb, formatTime, generateHourOptions, generateMinuteOptions, getDropdownStyles, getPortalContainer, initPushpins, initTooltips, isNumeric, isTimeDisabled, isValidationError, isValidationSuccess, padLeft, parseTime, range, releasePortalContainer, renderToPortal, scrollToValue, snapToNearestItem, sortOptions, timeToMinutes, toast, uniqueId, uuid4 };
|
|
11192
|
+
export { AnalogClock, AnchorItem, Autocomplete, Badge, Breadcrumb, BreadcrumbManager, Button, ButtonFactory, Carousel, CharacterCounter, Chips, CircularProgress, CodeBlock, Collapsible, CollapsibleItem, Collection, CollectionMode, ColorInput, DataTable, DatePicker, DigitalClock, DoubleRangeSlider, Dropdown, EmailInput, FileInput, FileUpload, FlatButton, FloatingActionButton, HelperText, Icon, IconButton, ImageList, InputCheckbox, Label, LargeButton, LinearProgress, ListItem, Mandatory, Masonry, MaterialBox, MaterialIcon, ModalPanel, NumberInput, Options, OptionsList, Pagination, PaginationControls, Parallax, PasswordInput, Pushpin, PushpinComponent, RadioButton, RadioButtons, RangeInput, Rating, RoundIconButton, SearchSelect, SecondaryContent, Select, Sidenav, SidenavItem, SidenavManager, SingleRangeSlider, SmallButton, Stepper, SubmitButton, Switch, Tabs, TextArea, TextInput, ThemeManager, ThemeSwitcher, ThemeToggle, TimePicker, TimeRangePicker, Timeline, Toast, ToastComponent, ToggleGroup, Tooltip, TooltipComponent, TreeView, UrlInput, Wizard, addLeadingZero, clearPortal, createBreadcrumb, formatTime, generateHourOptions, generateMinuteOptions, getDropdownStyles, getPortalContainer, initPushpins, initTooltips, isNumeric, isTimeDisabled, isValidationError, isValidationSuccess, padLeft, parseTime, range, releasePortalContainer, renderToPortal, scrollToValue, snapToNearestItem, sortOptions, timeToMinutes, toast, uniqueId, uuid4 };
|
package/dist/index.js
CHANGED
|
@@ -507,6 +507,106 @@ const Autocomplete = () => {
|
|
|
507
507
|
};
|
|
508
508
|
};
|
|
509
509
|
|
|
510
|
+
/**
|
|
511
|
+
* Badge component
|
|
512
|
+
*
|
|
513
|
+
* Displays a badge anchored to a child element. Commonly used for notifications,
|
|
514
|
+
* counts, or status indicators. Supports flexible positioning, colors, and variants.
|
|
515
|
+
*
|
|
516
|
+
* @example
|
|
517
|
+
* ```typescript
|
|
518
|
+
* // Basic notification badge
|
|
519
|
+
* m(Badge, { badgeContent: 5 },
|
|
520
|
+
* m('button.btn', 'Messages')
|
|
521
|
+
* )
|
|
522
|
+
*
|
|
523
|
+
* // Dot badge on avatar
|
|
524
|
+
* m(Badge, {
|
|
525
|
+
* variant: 'dot',
|
|
526
|
+
* color: 'green',
|
|
527
|
+
* overlap: 'circular'
|
|
528
|
+
* },
|
|
529
|
+
* m('img.circle', { src: 'avatar.jpg' })
|
|
530
|
+
* )
|
|
531
|
+
* ```
|
|
532
|
+
*/
|
|
533
|
+
const Badge = () => {
|
|
534
|
+
return {
|
|
535
|
+
view: ({ attrs, children }) => {
|
|
536
|
+
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"]);
|
|
537
|
+
// === VALIDATION: Single child element ===
|
|
538
|
+
const childArray = Array.isArray(children) ? children : children ? [children] : [];
|
|
539
|
+
if (childArray.length === 0) {
|
|
540
|
+
console.warn('Badge component requires a child element');
|
|
541
|
+
return null;
|
|
542
|
+
}
|
|
543
|
+
if (childArray.length > 1) {
|
|
544
|
+
console.warn('Badge component should only wrap a single child element. Using first child only.');
|
|
545
|
+
}
|
|
546
|
+
const child = childArray[0];
|
|
547
|
+
// === VISIBILITY LOGIC ===
|
|
548
|
+
// Hide badge if:
|
|
549
|
+
// 1. invisible prop is true, OR
|
|
550
|
+
// 2. For standard variant: badgeContent is undefined/null OR (badgeContent is 0 AND !showZero)
|
|
551
|
+
const shouldHideBadge = invisible ||
|
|
552
|
+
(variant === 'standard' &&
|
|
553
|
+
(badgeContent === undefined ||
|
|
554
|
+
badgeContent === null ||
|
|
555
|
+
(badgeContent === 0 && !showZero)));
|
|
556
|
+
// === BADGE CONTENT FORMATTING ===
|
|
557
|
+
// Apply max capping: if badgeContent > max, show "max+"
|
|
558
|
+
const getDisplayContent = () => {
|
|
559
|
+
if (variant === 'dot')
|
|
560
|
+
return '';
|
|
561
|
+
if (typeof badgeContent === 'number' && max !== undefined && badgeContent > max) {
|
|
562
|
+
return `${max}+`;
|
|
563
|
+
}
|
|
564
|
+
return String(badgeContent !== null && badgeContent !== void 0 ? badgeContent : '');
|
|
565
|
+
};
|
|
566
|
+
const displayContent = getDisplayContent();
|
|
567
|
+
// === CSS CLASS ASSEMBLY ===
|
|
568
|
+
// Wrapper classes
|
|
569
|
+
const wrapperClasses = ['badge-wrapper', className].filter(Boolean).join(' ').trim() || undefined;
|
|
570
|
+
// Badge element classes - using m-badge prefix to avoid Materialize conflicts
|
|
571
|
+
const positionClass = `m-badge--${anchorOrigin.vertical}-${anchorOrigin.horizontal}`;
|
|
572
|
+
const badgeClasses = [
|
|
573
|
+
'm-badge',
|
|
574
|
+
`m-badge--${variant}`,
|
|
575
|
+
positionClass,
|
|
576
|
+
`m-badge--${overlap}`,
|
|
577
|
+
`m-badge--${color}`,
|
|
578
|
+
colorIntensity ? `m-badge--${colorIntensity}` : '',
|
|
579
|
+
shouldHideBadge ? 'm-badge--invisible' : '',
|
|
580
|
+
badgeClassName,
|
|
581
|
+
]
|
|
582
|
+
.filter(Boolean)
|
|
583
|
+
.join(' ')
|
|
584
|
+
.trim();
|
|
585
|
+
// === ARIA ATTRIBUTES ===
|
|
586
|
+
const badgeAriaLabel = ariaLabel ||
|
|
587
|
+
(variant === 'dot'
|
|
588
|
+
? 'notification indicator'
|
|
589
|
+
: displayContent
|
|
590
|
+
? `${displayContent} notifications`
|
|
591
|
+
: 'notification badge');
|
|
592
|
+
// === RENDER ===
|
|
593
|
+
return m('.badge-wrapper', Object.assign(Object.assign({}, params), { className: wrapperClasses }), [
|
|
594
|
+
// Child element
|
|
595
|
+
child,
|
|
596
|
+
// Badge element - only render if not hidden
|
|
597
|
+
!shouldHideBadge
|
|
598
|
+
? m('span', {
|
|
599
|
+
className: badgeClasses,
|
|
600
|
+
'aria-label': badgeAriaLabel,
|
|
601
|
+
role: 'status',
|
|
602
|
+
'aria-live': 'polite',
|
|
603
|
+
}, variant === 'standard' ? displayContent : null)
|
|
604
|
+
: null,
|
|
605
|
+
]);
|
|
606
|
+
},
|
|
607
|
+
};
|
|
608
|
+
};
|
|
609
|
+
|
|
510
610
|
/**
|
|
511
611
|
* A simple material icon, defined by its icon name.
|
|
512
612
|
*
|
|
@@ -8191,33 +8291,75 @@ class Toast {
|
|
|
8191
8291
|
_createToast() {
|
|
8192
8292
|
const toast = document.createElement('div');
|
|
8193
8293
|
toast.classList.add('toast');
|
|
8194
|
-
// Add custom classes
|
|
8195
|
-
|
|
8196
|
-
|
|
8197
|
-
|
|
8198
|
-
|
|
8294
|
+
// Add custom classes (prefer className over deprecated classes)
|
|
8295
|
+
const customClasses = this.options.className || this.options.classes;
|
|
8296
|
+
if (customClasses) {
|
|
8297
|
+
toast.classList.add(...customClasses.split(' ').filter((c) => c));
|
|
8298
|
+
}
|
|
8299
|
+
// Create content wrapper
|
|
8300
|
+
const contentWrapper = document.createElement('div');
|
|
8301
|
+
contentWrapper.style.cssText = 'display: flex; align-items: center; gap: 12px;';
|
|
8302
|
+
// Set message content
|
|
8303
|
+
const messageEl = document.createElement('div');
|
|
8304
|
+
messageEl.style.flex = '1';
|
|
8199
8305
|
const message = this.options.html;
|
|
8200
8306
|
if (typeof message === 'object' && message && 'nodeType' in message) {
|
|
8201
|
-
|
|
8307
|
+
messageEl.appendChild(message);
|
|
8202
8308
|
}
|
|
8203
8309
|
else {
|
|
8204
|
-
|
|
8310
|
+
messageEl.innerHTML = message;
|
|
8311
|
+
}
|
|
8312
|
+
contentWrapper.appendChild(messageEl);
|
|
8313
|
+
// Add action button if provided
|
|
8314
|
+
if (this.options.action) {
|
|
8315
|
+
const action = this.options.action;
|
|
8316
|
+
const actionBtn = document.createElement('button');
|
|
8317
|
+
actionBtn.className = 'btn-flat toast-action';
|
|
8318
|
+
actionBtn.style.cssText = 'margin: 0; padding: 0 8px; min-width: auto; height: 36px;';
|
|
8319
|
+
if (action.variant === 'icon' && action.icon) {
|
|
8320
|
+
// Icon button variant
|
|
8321
|
+
actionBtn.innerHTML = `<i class="material-icons">${action.icon}</i>`;
|
|
8322
|
+
actionBtn.setAttribute('aria-label', action.label || action.icon);
|
|
8323
|
+
}
|
|
8324
|
+
else {
|
|
8325
|
+
// Flat button variant (default)
|
|
8326
|
+
if (action.icon) {
|
|
8327
|
+
actionBtn.innerHTML = `<i class="material-icons left">${action.icon}</i>${action.label || ''}`;
|
|
8328
|
+
}
|
|
8329
|
+
else {
|
|
8330
|
+
actionBtn.textContent = action.label || '';
|
|
8331
|
+
}
|
|
8332
|
+
}
|
|
8333
|
+
actionBtn.onclick = (e) => {
|
|
8334
|
+
e.stopPropagation();
|
|
8335
|
+
action.onclick();
|
|
8336
|
+
this.dismiss();
|
|
8337
|
+
};
|
|
8338
|
+
contentWrapper.appendChild(actionBtn);
|
|
8205
8339
|
}
|
|
8340
|
+
toast.appendChild(contentWrapper);
|
|
8206
8341
|
// Store reference
|
|
8207
8342
|
toast.M_Toast = this;
|
|
8343
|
+
// Measure natural width BEFORE appending to avoid interference from other toasts
|
|
8344
|
+
// Temporarily append to body in an isolated position
|
|
8345
|
+
toast.style.cssText = 'position: absolute; visibility: hidden; left: -9999px; display: flex;';
|
|
8346
|
+
document.body.appendChild(toast);
|
|
8347
|
+
// Force layout and measure
|
|
8348
|
+
const naturalWidth = toast.offsetWidth;
|
|
8349
|
+
// Remove from body
|
|
8350
|
+
document.body.removeChild(toast);
|
|
8351
|
+
// Lock the width to prevent changes based on other toasts
|
|
8352
|
+
toast.style.cssText = `width: ${naturalWidth}px;`;
|
|
8208
8353
|
// Append to container
|
|
8209
8354
|
Toast._container.appendChild(toast);
|
|
8210
8355
|
return toast;
|
|
8211
8356
|
}
|
|
8212
8357
|
_animateIn() {
|
|
8213
|
-
//
|
|
8214
|
-
|
|
8215
|
-
|
|
8216
|
-
|
|
8217
|
-
|
|
8218
|
-
opacity ${this.options.inDuration}ms cubic-bezier(0.215, 0.61, 0.355, 1);
|
|
8219
|
-
`;
|
|
8220
|
-
// Trigger animation
|
|
8358
|
+
// Width is already locked from _createToast
|
|
8359
|
+
// Set initial animation state (transitions are defined in CSS)
|
|
8360
|
+
this.el.style.transform = 'translateY(35px)';
|
|
8361
|
+
this.el.style.opacity = '0';
|
|
8362
|
+
// Trigger animation after a brief delay to ensure styles are applied
|
|
8221
8363
|
setTimeout(() => {
|
|
8222
8364
|
this.el.style.transform = 'translateY(0)';
|
|
8223
8365
|
this.el.style.opacity = '1';
|
|
@@ -8241,17 +8383,34 @@ class Toast {
|
|
|
8241
8383
|
}
|
|
8242
8384
|
const activationDistance = this.el.offsetWidth * this.options.activationPercent;
|
|
8243
8385
|
if (this.state.wasSwiped) {
|
|
8386
|
+
// Override transition temporarily for swipe
|
|
8244
8387
|
this.el.style.transition = 'transform .05s, opacity .05s';
|
|
8245
8388
|
this.el.style.transform = `translateX(${activationDistance}px)`;
|
|
8246
8389
|
this.el.style.opacity = '0';
|
|
8390
|
+
// Reset transition after swipe animation
|
|
8391
|
+
setTimeout(() => {
|
|
8392
|
+
this.el.style.transition = `opacity ${this.options.outDuration}ms cubic-bezier(0.165, 0.84, 0.44, 1),
|
|
8393
|
+
max-height ${this.options.outDuration}ms cubic-bezier(0.165, 0.84, 0.44, 1),
|
|
8394
|
+
margin-top ${this.options.outDuration}ms cubic-bezier(0.165, 0.84, 0.44, 1),
|
|
8395
|
+
padding-top ${this.options.outDuration}ms cubic-bezier(0.165, 0.84, 0.44, 1),
|
|
8396
|
+
padding-bottom ${this.options.outDuration}ms cubic-bezier(0.165, 0.84, 0.44, 1)`;
|
|
8397
|
+
}, 50);
|
|
8247
8398
|
}
|
|
8248
|
-
|
|
8249
|
-
|
|
8250
|
-
|
|
8251
|
-
|
|
8252
|
-
|
|
8253
|
-
|
|
8254
|
-
|
|
8399
|
+
else {
|
|
8400
|
+
// Set collapse transition timing
|
|
8401
|
+
this.el.style.transition = `opacity ${this.options.outDuration}ms cubic-bezier(0.165, 0.84, 0.44, 1),
|
|
8402
|
+
max-height ${this.options.outDuration}ms cubic-bezier(0.165, 0.84, 0.44, 1),
|
|
8403
|
+
margin-top ${this.options.outDuration}ms cubic-bezier(0.165, 0.84, 0.44, 1),
|
|
8404
|
+
padding-top ${this.options.outDuration}ms cubic-bezier(0.165, 0.84, 0.44, 1),
|
|
8405
|
+
padding-bottom ${this.options.outDuration}ms cubic-bezier(0.165, 0.84, 0.44, 1)`;
|
|
8406
|
+
}
|
|
8407
|
+
// Animate out - collapse height smoothly
|
|
8408
|
+
this.el.style.opacity = '0';
|
|
8409
|
+
this.el.style.maxHeight = '0';
|
|
8410
|
+
this.el.style.marginTop = '0';
|
|
8411
|
+
this.el.style.paddingTop = '0';
|
|
8412
|
+
this.el.style.paddingBottom = '0';
|
|
8413
|
+
this.el.style.overflow = 'hidden';
|
|
8255
8414
|
setTimeout(() => {
|
|
8256
8415
|
// Call completion callback
|
|
8257
8416
|
if (this.options.completeCallback) {
|
|
@@ -8280,8 +8439,10 @@ Toast.defaults = {
|
|
|
8280
8439
|
inDuration: 300,
|
|
8281
8440
|
outDuration: 375,
|
|
8282
8441
|
classes: '',
|
|
8442
|
+
className: '',
|
|
8283
8443
|
completeCallback: undefined,
|
|
8284
8444
|
activationPercent: 0.8,
|
|
8445
|
+
action: undefined,
|
|
8285
8446
|
};
|
|
8286
8447
|
Toast._onDragStart = (e) => {
|
|
8287
8448
|
const target = e.target;
|
|
@@ -11033,6 +11194,7 @@ const isValidationError = (result) => !isValidationSuccess(result);
|
|
|
11033
11194
|
exports.AnalogClock = AnalogClock;
|
|
11034
11195
|
exports.AnchorItem = AnchorItem;
|
|
11035
11196
|
exports.Autocomplete = Autocomplete;
|
|
11197
|
+
exports.Badge = Badge;
|
|
11036
11198
|
exports.Breadcrumb = Breadcrumb;
|
|
11037
11199
|
exports.BreadcrumbManager = BreadcrumbManager;
|
|
11038
11200
|
exports.Button = Button;
|