mithril-materialized 3.8.0 → 3.10.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/circular-progress.d.ts +43 -0
- package/dist/components.css +227 -0
- package/dist/index.css +702 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.esm.js +277 -1
- package/dist/index.js +279 -0
- package/dist/index.min.css +1 -1
- package/dist/index.umd.js +279 -0
- package/dist/linear-progress.d.ts +40 -0
- package/package.json +1 -1
- package/sass/components/_badge-component.scss +203 -0
- package/sass/components/_circular-progress.scss +220 -0
- package/sass/components/_linear-progress.scss +183 -0
- package/sass/materialize.scss +4 -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
|
*
|
|
@@ -10831,6 +10931,182 @@ const ToggleGroup = () => {
|
|
|
10831
10931
|
};
|
|
10832
10932
|
};
|
|
10833
10933
|
|
|
10934
|
+
/** Size dimensions in pixels */
|
|
10935
|
+
const SIZE_MAP = {
|
|
10936
|
+
small: 36,
|
|
10937
|
+
medium: 50,
|
|
10938
|
+
large: 64,
|
|
10939
|
+
};
|
|
10940
|
+
/** Stroke width in pixels */
|
|
10941
|
+
const STROKE_WIDTH = 3;
|
|
10942
|
+
/** Create a CircularProgress component */
|
|
10943
|
+
const CircularProgress = () => {
|
|
10944
|
+
const state = {
|
|
10945
|
+
id: uniqueId(),
|
|
10946
|
+
};
|
|
10947
|
+
/**
|
|
10948
|
+
* Calculate SVG stroke properties for determinate progress
|
|
10949
|
+
*/
|
|
10950
|
+
const calculateStrokeProperties = (size, value, max) => {
|
|
10951
|
+
const radius = (size - STROKE_WIDTH) / 2;
|
|
10952
|
+
const circumference = 2 * Math.PI * radius;
|
|
10953
|
+
const percentage = Math.min(100, Math.max(0, (value / max) * 100));
|
|
10954
|
+
const strokeDashoffset = circumference - (percentage / 100) * circumference;
|
|
10955
|
+
return {
|
|
10956
|
+
radius,
|
|
10957
|
+
circumference,
|
|
10958
|
+
strokeDashoffset,
|
|
10959
|
+
percentage,
|
|
10960
|
+
};
|
|
10961
|
+
};
|
|
10962
|
+
/**
|
|
10963
|
+
* Get size class name
|
|
10964
|
+
*/
|
|
10965
|
+
const getSizeClass = (size = 'medium') => {
|
|
10966
|
+
return `circular-progress--${size}`;
|
|
10967
|
+
};
|
|
10968
|
+
/**
|
|
10969
|
+
* Get color class name
|
|
10970
|
+
*/
|
|
10971
|
+
const getColorClass = (color, intensity) => {
|
|
10972
|
+
if (!color)
|
|
10973
|
+
return '';
|
|
10974
|
+
return intensity ? `circular-progress--${color} circular-progress--${intensity}` : `circular-progress--${color}`;
|
|
10975
|
+
};
|
|
10976
|
+
return {
|
|
10977
|
+
view: ({ attrs }) => {
|
|
10978
|
+
const { mode = 'indeterminate', value = 0, max = 100, size = 'medium', color = 'teal', colorIntensity, label, showPercentage = false, className = '', style = {}, id = state.id, 'aria-label': ariaLabel, 'aria-valuemin': ariaValueMin = 0, 'aria-valuemax': ariaValueMax = max, 'aria-valuenow': ariaValueNow, 'aria-valuetext': ariaValueText } = attrs, params = __rest(attrs, ["mode", "value", "max", "size", "color", "colorIntensity", "label", "showPercentage", "className", "style", "id", 'aria-label', 'aria-valuemin', 'aria-valuemax', 'aria-valuenow', 'aria-valuetext']);
|
|
10979
|
+
const isDeterminate = mode === 'determinate';
|
|
10980
|
+
const sizePixels = SIZE_MAP[size];
|
|
10981
|
+
const { radius, circumference, strokeDashoffset, percentage } = isDeterminate
|
|
10982
|
+
? calculateStrokeProperties(sizePixels, value, max)
|
|
10983
|
+
: { radius: 0, circumference: 0, strokeDashoffset: 0, percentage: 0 };
|
|
10984
|
+
// Determine label content
|
|
10985
|
+
const labelContent = label !== undefined ? label : showPercentage && isDeterminate ? `${Math.round(percentage)}%` : '';
|
|
10986
|
+
// Build class names
|
|
10987
|
+
const classNames = [
|
|
10988
|
+
'circular-progress',
|
|
10989
|
+
getSizeClass(size),
|
|
10990
|
+
getColorClass(color, colorIntensity),
|
|
10991
|
+
`circular-progress--${mode}`,
|
|
10992
|
+
className,
|
|
10993
|
+
]
|
|
10994
|
+
.filter(Boolean)
|
|
10995
|
+
.join(' ');
|
|
10996
|
+
// ARIA attributes
|
|
10997
|
+
const ariaAttrs = isDeterminate
|
|
10998
|
+
? {
|
|
10999
|
+
'aria-valuenow': ariaValueNow !== undefined ? ariaValueNow : value,
|
|
11000
|
+
'aria-valuemin': ariaValueMin,
|
|
11001
|
+
'aria-valuemax': ariaValueMax,
|
|
11002
|
+
'aria-valuetext': ariaValueText || `${Math.round(percentage)}%`,
|
|
11003
|
+
}
|
|
11004
|
+
: {
|
|
11005
|
+
'aria-valuetext': ariaValueText || label || 'Loading',
|
|
11006
|
+
};
|
|
11007
|
+
return m('.circular-progress', Object.assign(Object.assign(Object.assign({}, params), { className: classNames, style: Object.assign({ width: `${sizePixels}px`, height: `${sizePixels}px` }, style), id, role: 'progressbar', 'aria-label': ariaLabel || (isDeterminate ? `Progress: ${Math.round(percentage)}%` : 'Loading') }), ariaAttrs), [
|
|
11008
|
+
// SVG circle
|
|
11009
|
+
m('svg.circular-progress__svg', {
|
|
11010
|
+
viewBox: `0 0 ${sizePixels} ${sizePixels}`,
|
|
11011
|
+
xmlns: 'http://www.w3.org/2000/svg',
|
|
11012
|
+
}, [
|
|
11013
|
+
// Background track circle
|
|
11014
|
+
m('circle.circular-progress__circle.circular-progress__circle--track', {
|
|
11015
|
+
cx: sizePixels / 2,
|
|
11016
|
+
cy: sizePixels / 2,
|
|
11017
|
+
r: radius || (sizePixels - STROKE_WIDTH) / 2,
|
|
11018
|
+
fill: 'none',
|
|
11019
|
+
stroke: 'currentColor',
|
|
11020
|
+
'stroke-width': STROKE_WIDTH,
|
|
11021
|
+
}),
|
|
11022
|
+
// Progress indicator circle
|
|
11023
|
+
m('circle.circular-progress__circle.circular-progress__circle--indicator', {
|
|
11024
|
+
cx: sizePixels / 2,
|
|
11025
|
+
cy: sizePixels / 2,
|
|
11026
|
+
r: radius || (sizePixels - STROKE_WIDTH) / 2,
|
|
11027
|
+
fill: 'none',
|
|
11028
|
+
stroke: 'currentColor',
|
|
11029
|
+
'stroke-width': STROKE_WIDTH,
|
|
11030
|
+
'stroke-dasharray': isDeterminate ? circumference : undefined,
|
|
11031
|
+
'stroke-dashoffset': isDeterminate ? strokeDashoffset : undefined,
|
|
11032
|
+
'stroke-linecap': 'round',
|
|
11033
|
+
}),
|
|
11034
|
+
]),
|
|
11035
|
+
// Label inside circle
|
|
11036
|
+
labelContent &&
|
|
11037
|
+
m('.circular-progress__label', {
|
|
11038
|
+
'aria-hidden': 'true',
|
|
11039
|
+
}, labelContent),
|
|
11040
|
+
]);
|
|
11041
|
+
},
|
|
11042
|
+
};
|
|
11043
|
+
};
|
|
11044
|
+
|
|
11045
|
+
/** Create a LinearProgress component */
|
|
11046
|
+
const LinearProgress = () => {
|
|
11047
|
+
const state = {
|
|
11048
|
+
id: uniqueId(),
|
|
11049
|
+
};
|
|
11050
|
+
/**
|
|
11051
|
+
* Get size class name
|
|
11052
|
+
*/
|
|
11053
|
+
const getSizeClass = (size = 'medium') => {
|
|
11054
|
+
return `linear-progress__track--${size}`;
|
|
11055
|
+
};
|
|
11056
|
+
/**
|
|
11057
|
+
* Get color class name
|
|
11058
|
+
*/
|
|
11059
|
+
const getColorClass = (color, intensity) => {
|
|
11060
|
+
if (!color)
|
|
11061
|
+
return '';
|
|
11062
|
+
return intensity ? `linear-progress--${color} linear-progress--${intensity}` : `linear-progress--${color}`;
|
|
11063
|
+
};
|
|
11064
|
+
return {
|
|
11065
|
+
view: ({ attrs }) => {
|
|
11066
|
+
const { mode = 'indeterminate', value = 0, max = 100, size = 'medium', color = 'teal', colorIntensity, label, showPercentage = false, className = '', style = {}, id = state.id, 'aria-label': ariaLabel, 'aria-valuemin': ariaValueMin = 0, 'aria-valuemax': ariaValueMax = max, 'aria-valuenow': ariaValueNow, 'aria-valuetext': ariaValueText } = attrs, params = __rest(attrs, ["mode", "value", "max", "size", "color", "colorIntensity", "label", "showPercentage", "className", "style", "id", 'aria-label', 'aria-valuemin', 'aria-valuemax', 'aria-valuenow', 'aria-valuetext']);
|
|
11067
|
+
const isDeterminate = mode === 'determinate';
|
|
11068
|
+
const percentage = Math.min(100, Math.max(0, (value / max) * 100));
|
|
11069
|
+
// Determine label content
|
|
11070
|
+
const labelContent = label !== undefined ? label : showPercentage && isDeterminate ? `${Math.round(percentage)}%` : '';
|
|
11071
|
+
// Build class names
|
|
11072
|
+
const classNames = [
|
|
11073
|
+
'linear-progress',
|
|
11074
|
+
getColorClass(color, colorIntensity),
|
|
11075
|
+
className,
|
|
11076
|
+
]
|
|
11077
|
+
.filter(Boolean)
|
|
11078
|
+
.join(' ');
|
|
11079
|
+
// ARIA attributes
|
|
11080
|
+
const ariaAttrs = isDeterminate
|
|
11081
|
+
? {
|
|
11082
|
+
'aria-valuenow': ariaValueNow !== undefined ? ariaValueNow : value,
|
|
11083
|
+
'aria-valuemin': ariaValueMin,
|
|
11084
|
+
'aria-valuemax': ariaValueMax,
|
|
11085
|
+
'aria-valuetext': ariaValueText || `${Math.round(percentage)}%`,
|
|
11086
|
+
}
|
|
11087
|
+
: {
|
|
11088
|
+
'aria-valuetext': ariaValueText || label || 'Loading',
|
|
11089
|
+
};
|
|
11090
|
+
return m('.linear-progress', Object.assign(Object.assign({}, params), { className: classNames, style,
|
|
11091
|
+
id }), [
|
|
11092
|
+
// Progress track container
|
|
11093
|
+
m('.linear-progress__track', Object.assign({ className: `linear-progress__track ${getSizeClass(size)}`, role: 'progressbar', 'aria-label': ariaLabel || (isDeterminate ? `Progress: ${Math.round(percentage)}%` : 'Loading') }, ariaAttrs), [
|
|
11094
|
+
// Progress bar
|
|
11095
|
+
m('.linear-progress__bar', {
|
|
11096
|
+
className: `linear-progress__bar ${isDeterminate ? '' : 'linear-progress__bar--indeterminate'}`,
|
|
11097
|
+
style: isDeterminate ? { width: `${percentage}%` } : undefined,
|
|
11098
|
+
}),
|
|
11099
|
+
]),
|
|
11100
|
+
// Label at the end (right side)
|
|
11101
|
+
labelContent &&
|
|
11102
|
+
m('.linear-progress__label', {
|
|
11103
|
+
'aria-hidden': 'true',
|
|
11104
|
+
}, labelContent),
|
|
11105
|
+
]);
|
|
11106
|
+
},
|
|
11107
|
+
};
|
|
11108
|
+
};
|
|
11109
|
+
|
|
10834
11110
|
/**
|
|
10835
11111
|
* @fileoverview Core TypeScript utility types for mithril-materialized library
|
|
10836
11112
|
* These types improve type safety and developer experience across all components
|
|
@@ -10852,4 +11128,4 @@ const isValidationError = (result) => !isValidationSuccess(result);
|
|
|
10852
11128
|
// ============================================================================
|
|
10853
11129
|
// All types are already exported via individual export declarations above
|
|
10854
11130
|
|
|
10855
|
-
export { AnalogClock, AnchorItem, Autocomplete, Breadcrumb, BreadcrumbManager, Button, ButtonFactory, Carousel, CharacterCounter, Chips, CodeBlock, Collapsible, CollapsibleItem, Collection, CollectionMode, ColorInput, DataTable, DatePicker, DigitalClock, DoubleRangeSlider, Dropdown, EmailInput, FileInput, FileUpload, FlatButton, FloatingActionButton, HelperText, Icon, IconButton, ImageList, InputCheckbox, Label, LargeButton, 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 };
|
|
11131
|
+
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
|
*
|
|
@@ -10833,6 +10933,182 @@ const ToggleGroup = () => {
|
|
|
10833
10933
|
};
|
|
10834
10934
|
};
|
|
10835
10935
|
|
|
10936
|
+
/** Size dimensions in pixels */
|
|
10937
|
+
const SIZE_MAP = {
|
|
10938
|
+
small: 36,
|
|
10939
|
+
medium: 50,
|
|
10940
|
+
large: 64,
|
|
10941
|
+
};
|
|
10942
|
+
/** Stroke width in pixels */
|
|
10943
|
+
const STROKE_WIDTH = 3;
|
|
10944
|
+
/** Create a CircularProgress component */
|
|
10945
|
+
const CircularProgress = () => {
|
|
10946
|
+
const state = {
|
|
10947
|
+
id: uniqueId(),
|
|
10948
|
+
};
|
|
10949
|
+
/**
|
|
10950
|
+
* Calculate SVG stroke properties for determinate progress
|
|
10951
|
+
*/
|
|
10952
|
+
const calculateStrokeProperties = (size, value, max) => {
|
|
10953
|
+
const radius = (size - STROKE_WIDTH) / 2;
|
|
10954
|
+
const circumference = 2 * Math.PI * radius;
|
|
10955
|
+
const percentage = Math.min(100, Math.max(0, (value / max) * 100));
|
|
10956
|
+
const strokeDashoffset = circumference - (percentage / 100) * circumference;
|
|
10957
|
+
return {
|
|
10958
|
+
radius,
|
|
10959
|
+
circumference,
|
|
10960
|
+
strokeDashoffset,
|
|
10961
|
+
percentage,
|
|
10962
|
+
};
|
|
10963
|
+
};
|
|
10964
|
+
/**
|
|
10965
|
+
* Get size class name
|
|
10966
|
+
*/
|
|
10967
|
+
const getSizeClass = (size = 'medium') => {
|
|
10968
|
+
return `circular-progress--${size}`;
|
|
10969
|
+
};
|
|
10970
|
+
/**
|
|
10971
|
+
* Get color class name
|
|
10972
|
+
*/
|
|
10973
|
+
const getColorClass = (color, intensity) => {
|
|
10974
|
+
if (!color)
|
|
10975
|
+
return '';
|
|
10976
|
+
return intensity ? `circular-progress--${color} circular-progress--${intensity}` : `circular-progress--${color}`;
|
|
10977
|
+
};
|
|
10978
|
+
return {
|
|
10979
|
+
view: ({ attrs }) => {
|
|
10980
|
+
const { mode = 'indeterminate', value = 0, max = 100, size = 'medium', color = 'teal', colorIntensity, label, showPercentage = false, className = '', style = {}, id = state.id, 'aria-label': ariaLabel, 'aria-valuemin': ariaValueMin = 0, 'aria-valuemax': ariaValueMax = max, 'aria-valuenow': ariaValueNow, 'aria-valuetext': ariaValueText } = attrs, params = __rest(attrs, ["mode", "value", "max", "size", "color", "colorIntensity", "label", "showPercentage", "className", "style", "id", 'aria-label', 'aria-valuemin', 'aria-valuemax', 'aria-valuenow', 'aria-valuetext']);
|
|
10981
|
+
const isDeterminate = mode === 'determinate';
|
|
10982
|
+
const sizePixels = SIZE_MAP[size];
|
|
10983
|
+
const { radius, circumference, strokeDashoffset, percentage } = isDeterminate
|
|
10984
|
+
? calculateStrokeProperties(sizePixels, value, max)
|
|
10985
|
+
: { radius: 0, circumference: 0, strokeDashoffset: 0, percentage: 0 };
|
|
10986
|
+
// Determine label content
|
|
10987
|
+
const labelContent = label !== undefined ? label : showPercentage && isDeterminate ? `${Math.round(percentage)}%` : '';
|
|
10988
|
+
// Build class names
|
|
10989
|
+
const classNames = [
|
|
10990
|
+
'circular-progress',
|
|
10991
|
+
getSizeClass(size),
|
|
10992
|
+
getColorClass(color, colorIntensity),
|
|
10993
|
+
`circular-progress--${mode}`,
|
|
10994
|
+
className,
|
|
10995
|
+
]
|
|
10996
|
+
.filter(Boolean)
|
|
10997
|
+
.join(' ');
|
|
10998
|
+
// ARIA attributes
|
|
10999
|
+
const ariaAttrs = isDeterminate
|
|
11000
|
+
? {
|
|
11001
|
+
'aria-valuenow': ariaValueNow !== undefined ? ariaValueNow : value,
|
|
11002
|
+
'aria-valuemin': ariaValueMin,
|
|
11003
|
+
'aria-valuemax': ariaValueMax,
|
|
11004
|
+
'aria-valuetext': ariaValueText || `${Math.round(percentage)}%`,
|
|
11005
|
+
}
|
|
11006
|
+
: {
|
|
11007
|
+
'aria-valuetext': ariaValueText || label || 'Loading',
|
|
11008
|
+
};
|
|
11009
|
+
return m('.circular-progress', Object.assign(Object.assign(Object.assign({}, params), { className: classNames, style: Object.assign({ width: `${sizePixels}px`, height: `${sizePixels}px` }, style), id, role: 'progressbar', 'aria-label': ariaLabel || (isDeterminate ? `Progress: ${Math.round(percentage)}%` : 'Loading') }), ariaAttrs), [
|
|
11010
|
+
// SVG circle
|
|
11011
|
+
m('svg.circular-progress__svg', {
|
|
11012
|
+
viewBox: `0 0 ${sizePixels} ${sizePixels}`,
|
|
11013
|
+
xmlns: 'http://www.w3.org/2000/svg',
|
|
11014
|
+
}, [
|
|
11015
|
+
// Background track circle
|
|
11016
|
+
m('circle.circular-progress__circle.circular-progress__circle--track', {
|
|
11017
|
+
cx: sizePixels / 2,
|
|
11018
|
+
cy: sizePixels / 2,
|
|
11019
|
+
r: radius || (sizePixels - STROKE_WIDTH) / 2,
|
|
11020
|
+
fill: 'none',
|
|
11021
|
+
stroke: 'currentColor',
|
|
11022
|
+
'stroke-width': STROKE_WIDTH,
|
|
11023
|
+
}),
|
|
11024
|
+
// Progress indicator circle
|
|
11025
|
+
m('circle.circular-progress__circle.circular-progress__circle--indicator', {
|
|
11026
|
+
cx: sizePixels / 2,
|
|
11027
|
+
cy: sizePixels / 2,
|
|
11028
|
+
r: radius || (sizePixels - STROKE_WIDTH) / 2,
|
|
11029
|
+
fill: 'none',
|
|
11030
|
+
stroke: 'currentColor',
|
|
11031
|
+
'stroke-width': STROKE_WIDTH,
|
|
11032
|
+
'stroke-dasharray': isDeterminate ? circumference : undefined,
|
|
11033
|
+
'stroke-dashoffset': isDeterminate ? strokeDashoffset : undefined,
|
|
11034
|
+
'stroke-linecap': 'round',
|
|
11035
|
+
}),
|
|
11036
|
+
]),
|
|
11037
|
+
// Label inside circle
|
|
11038
|
+
labelContent &&
|
|
11039
|
+
m('.circular-progress__label', {
|
|
11040
|
+
'aria-hidden': 'true',
|
|
11041
|
+
}, labelContent),
|
|
11042
|
+
]);
|
|
11043
|
+
},
|
|
11044
|
+
};
|
|
11045
|
+
};
|
|
11046
|
+
|
|
11047
|
+
/** Create a LinearProgress component */
|
|
11048
|
+
const LinearProgress = () => {
|
|
11049
|
+
const state = {
|
|
11050
|
+
id: uniqueId(),
|
|
11051
|
+
};
|
|
11052
|
+
/**
|
|
11053
|
+
* Get size class name
|
|
11054
|
+
*/
|
|
11055
|
+
const getSizeClass = (size = 'medium') => {
|
|
11056
|
+
return `linear-progress__track--${size}`;
|
|
11057
|
+
};
|
|
11058
|
+
/**
|
|
11059
|
+
* Get color class name
|
|
11060
|
+
*/
|
|
11061
|
+
const getColorClass = (color, intensity) => {
|
|
11062
|
+
if (!color)
|
|
11063
|
+
return '';
|
|
11064
|
+
return intensity ? `linear-progress--${color} linear-progress--${intensity}` : `linear-progress--${color}`;
|
|
11065
|
+
};
|
|
11066
|
+
return {
|
|
11067
|
+
view: ({ attrs }) => {
|
|
11068
|
+
const { mode = 'indeterminate', value = 0, max = 100, size = 'medium', color = 'teal', colorIntensity, label, showPercentage = false, className = '', style = {}, id = state.id, 'aria-label': ariaLabel, 'aria-valuemin': ariaValueMin = 0, 'aria-valuemax': ariaValueMax = max, 'aria-valuenow': ariaValueNow, 'aria-valuetext': ariaValueText } = attrs, params = __rest(attrs, ["mode", "value", "max", "size", "color", "colorIntensity", "label", "showPercentage", "className", "style", "id", 'aria-label', 'aria-valuemin', 'aria-valuemax', 'aria-valuenow', 'aria-valuetext']);
|
|
11069
|
+
const isDeterminate = mode === 'determinate';
|
|
11070
|
+
const percentage = Math.min(100, Math.max(0, (value / max) * 100));
|
|
11071
|
+
// Determine label content
|
|
11072
|
+
const labelContent = label !== undefined ? label : showPercentage && isDeterminate ? `${Math.round(percentage)}%` : '';
|
|
11073
|
+
// Build class names
|
|
11074
|
+
const classNames = [
|
|
11075
|
+
'linear-progress',
|
|
11076
|
+
getColorClass(color, colorIntensity),
|
|
11077
|
+
className,
|
|
11078
|
+
]
|
|
11079
|
+
.filter(Boolean)
|
|
11080
|
+
.join(' ');
|
|
11081
|
+
// ARIA attributes
|
|
11082
|
+
const ariaAttrs = isDeterminate
|
|
11083
|
+
? {
|
|
11084
|
+
'aria-valuenow': ariaValueNow !== undefined ? ariaValueNow : value,
|
|
11085
|
+
'aria-valuemin': ariaValueMin,
|
|
11086
|
+
'aria-valuemax': ariaValueMax,
|
|
11087
|
+
'aria-valuetext': ariaValueText || `${Math.round(percentage)}%`,
|
|
11088
|
+
}
|
|
11089
|
+
: {
|
|
11090
|
+
'aria-valuetext': ariaValueText || label || 'Loading',
|
|
11091
|
+
};
|
|
11092
|
+
return m('.linear-progress', Object.assign(Object.assign({}, params), { className: classNames, style,
|
|
11093
|
+
id }), [
|
|
11094
|
+
// Progress track container
|
|
11095
|
+
m('.linear-progress__track', Object.assign({ className: `linear-progress__track ${getSizeClass(size)}`, role: 'progressbar', 'aria-label': ariaLabel || (isDeterminate ? `Progress: ${Math.round(percentage)}%` : 'Loading') }, ariaAttrs), [
|
|
11096
|
+
// Progress bar
|
|
11097
|
+
m('.linear-progress__bar', {
|
|
11098
|
+
className: `linear-progress__bar ${isDeterminate ? '' : 'linear-progress__bar--indeterminate'}`,
|
|
11099
|
+
style: isDeterminate ? { width: `${percentage}%` } : undefined,
|
|
11100
|
+
}),
|
|
11101
|
+
]),
|
|
11102
|
+
// Label at the end (right side)
|
|
11103
|
+
labelContent &&
|
|
11104
|
+
m('.linear-progress__label', {
|
|
11105
|
+
'aria-hidden': 'true',
|
|
11106
|
+
}, labelContent),
|
|
11107
|
+
]);
|
|
11108
|
+
},
|
|
11109
|
+
};
|
|
11110
|
+
};
|
|
11111
|
+
|
|
10836
11112
|
/**
|
|
10837
11113
|
* @fileoverview Core TypeScript utility types for mithril-materialized library
|
|
10838
11114
|
* These types improve type safety and developer experience across all components
|
|
@@ -10857,6 +11133,7 @@ const isValidationError = (result) => !isValidationSuccess(result);
|
|
|
10857
11133
|
exports.AnalogClock = AnalogClock;
|
|
10858
11134
|
exports.AnchorItem = AnchorItem;
|
|
10859
11135
|
exports.Autocomplete = Autocomplete;
|
|
11136
|
+
exports.Badge = Badge;
|
|
10860
11137
|
exports.Breadcrumb = Breadcrumb;
|
|
10861
11138
|
exports.BreadcrumbManager = BreadcrumbManager;
|
|
10862
11139
|
exports.Button = Button;
|
|
@@ -10864,6 +11141,7 @@ exports.ButtonFactory = ButtonFactory;
|
|
|
10864
11141
|
exports.Carousel = Carousel;
|
|
10865
11142
|
exports.CharacterCounter = CharacterCounter;
|
|
10866
11143
|
exports.Chips = Chips;
|
|
11144
|
+
exports.CircularProgress = CircularProgress;
|
|
10867
11145
|
exports.CodeBlock = CodeBlock;
|
|
10868
11146
|
exports.Collapsible = Collapsible;
|
|
10869
11147
|
exports.CollapsibleItem = CollapsibleItem;
|
|
@@ -10886,6 +11164,7 @@ exports.ImageList = ImageList;
|
|
|
10886
11164
|
exports.InputCheckbox = InputCheckbox;
|
|
10887
11165
|
exports.Label = Label;
|
|
10888
11166
|
exports.LargeButton = LargeButton;
|
|
11167
|
+
exports.LinearProgress = LinearProgress;
|
|
10889
11168
|
exports.ListItem = ListItem;
|
|
10890
11169
|
exports.Mandatory = Mandatory;
|
|
10891
11170
|
exports.Masonry = Masonry;
|