mithril-materialized 3.7.0 → 3.9.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/index.esm.js CHANGED
@@ -10711,6 +10711,302 @@ const Rating = () => {
10711
10711
  };
10712
10712
  };
10713
10713
 
10714
+ /**
10715
+ * ToggleButton component.
10716
+ *
10717
+ * A button that can be toggled on/off. Typically used within a ToggleGroup
10718
+ * to create grouped button controls where one or more buttons can be selected.
10719
+ *
10720
+ * @example
10721
+ * ```typescript
10722
+ * m(ToggleButton, {
10723
+ * value: 'bold',
10724
+ * iconName: 'format_bold',
10725
+ * checked: true,
10726
+ * tooltip: 'Bold',
10727
+ * onchange: () => console.log('Toggled')
10728
+ * })
10729
+ * ```
10730
+ */
10731
+ const ToggleButton = () => {
10732
+ return {
10733
+ view: ({ attrs }) => {
10734
+ const { checked, iconName, icon, label, onchange, className, tooltip } = attrs, rest = __rest(attrs, ["checked", "iconName", "icon", "label", "onchange", "className", "tooltip"]);
10735
+ const classes = [className, checked ? 'checked' : ''].filter(Boolean).join(' ');
10736
+ return m('button.btn-flat.waves-effect.toggle-button', Object.assign(Object.assign({}, rest), { className: classes, title: tooltip, onclick: () => {
10737
+ if (onchange) {
10738
+ onchange();
10739
+ }
10740
+ }, onmousedown: WavesEffect.onMouseDown, onmouseup: WavesEffect.onMouseUp, onmouseleave: WavesEffect.onMouseLeave, ontouchstart: WavesEffect.onTouchStart, ontouchend: WavesEffect.onTouchEnd }), [icon, iconName && m(Icon, { iconName, className: attrs.iconClass }), label]);
10741
+ },
10742
+ };
10743
+ };
10744
+
10745
+ /**
10746
+ * ToggleGroup component.
10747
+ *
10748
+ * A group of toggle buttons that can operate in single-select or multi-select mode.
10749
+ * The component supports both controlled and uncontrolled modes.
10750
+ *
10751
+ * **Controlled mode**: Manage the state externally using the `value` prop.
10752
+ * **Uncontrolled mode**: Let the component manage its own state using `defaultValue`.
10753
+ *
10754
+ * @example
10755
+ * ```typescript
10756
+ * // Single-select, controlled mode
10757
+ * let selected = 'left';
10758
+ * m(ToggleGroup, {
10759
+ * value: selected,
10760
+ * onchange: (v) => selected = v,
10761
+ * items: [
10762
+ * { value: 'left', iconName: 'format_align_left', tooltip: 'Align Left' },
10763
+ * { value: 'center', iconName: 'format_align_center', tooltip: 'Align Center' },
10764
+ * { value: 'right', iconName: 'format_align_right', tooltip: 'Align Right' }
10765
+ * ]
10766
+ * });
10767
+ *
10768
+ * // Multi-select, controlled mode
10769
+ * let selected = ['bold', 'italic'];
10770
+ * m(ToggleGroup, {
10771
+ * multiple: true,
10772
+ * value: selected,
10773
+ * onchange: (v) => selected = v,
10774
+ * items: [
10775
+ * { value: 'bold', iconName: 'format_bold', tooltip: 'Bold' },
10776
+ * { value: 'italic', iconName: 'format_italic', tooltip: 'Italic' },
10777
+ * { value: 'underline', iconName: 'format_underlined', tooltip: 'Underline' }
10778
+ * ]
10779
+ * });
10780
+ *
10781
+ * // Uncontrolled mode
10782
+ * m(ToggleGroup, {
10783
+ * defaultValue: 'left',
10784
+ * onchange: (v) => console.log('Changed to:', v),
10785
+ * items: [...]
10786
+ * });
10787
+ * ```
10788
+ */
10789
+ const ToggleGroup = () => {
10790
+ let internalValue;
10791
+ const handleSelect = (attrs, item, currentValue) => {
10792
+ if (attrs.disabled || item.disabled) {
10793
+ return;
10794
+ }
10795
+ const { value, multiple, onchange } = attrs;
10796
+ const isControlled = value !== undefined;
10797
+ if (multiple) {
10798
+ const currentValues = (Array.isArray(currentValue) ? currentValue : currentValue !== undefined ? [currentValue] : []);
10799
+ const newValues = currentValues.includes(item.value)
10800
+ ? currentValues.filter((v) => v !== item.value)
10801
+ : [...currentValues, item.value];
10802
+ if (!isControlled) {
10803
+ internalValue = newValues;
10804
+ }
10805
+ if (onchange) {
10806
+ onchange(newValues);
10807
+ }
10808
+ }
10809
+ else {
10810
+ const newValue = item.value;
10811
+ if (!isControlled) {
10812
+ internalValue = newValue;
10813
+ }
10814
+ if (onchange) {
10815
+ onchange(newValue);
10816
+ }
10817
+ }
10818
+ };
10819
+ return {
10820
+ oninit: ({ attrs }) => {
10821
+ internalValue = attrs.defaultValue;
10822
+ },
10823
+ view: ({ attrs }) => {
10824
+ const { value, items, multiple } = attrs;
10825
+ const isControlled = value !== undefined;
10826
+ const currentValue = isControlled ? value : internalValue;
10827
+ return m('.toggle-group', items.map((item) => m(ToggleButton, Object.assign(Object.assign({}, item), { checked: multiple && Array.isArray(currentValue)
10828
+ ? currentValue.includes(item.value)
10829
+ : currentValue === item.value, onchange: () => handleSelect(attrs, item, currentValue) }))));
10830
+ },
10831
+ };
10832
+ };
10833
+
10834
+ /** Size dimensions in pixels */
10835
+ const SIZE_MAP = {
10836
+ small: 36,
10837
+ medium: 50,
10838
+ large: 64,
10839
+ };
10840
+ /** Stroke width in pixels */
10841
+ const STROKE_WIDTH = 3;
10842
+ /** Create a CircularProgress component */
10843
+ const CircularProgress = () => {
10844
+ const state = {
10845
+ id: uniqueId(),
10846
+ };
10847
+ /**
10848
+ * Calculate SVG stroke properties for determinate progress
10849
+ */
10850
+ const calculateStrokeProperties = (size, value, max) => {
10851
+ const radius = (size - STROKE_WIDTH) / 2;
10852
+ const circumference = 2 * Math.PI * radius;
10853
+ const percentage = Math.min(100, Math.max(0, (value / max) * 100));
10854
+ const strokeDashoffset = circumference - (percentage / 100) * circumference;
10855
+ return {
10856
+ radius,
10857
+ circumference,
10858
+ strokeDashoffset,
10859
+ percentage,
10860
+ };
10861
+ };
10862
+ /**
10863
+ * Get size class name
10864
+ */
10865
+ const getSizeClass = (size = 'medium') => {
10866
+ return `circular-progress--${size}`;
10867
+ };
10868
+ /**
10869
+ * Get color class name
10870
+ */
10871
+ const getColorClass = (color, intensity) => {
10872
+ if (!color)
10873
+ return '';
10874
+ return intensity ? `circular-progress--${color} circular-progress--${intensity}` : `circular-progress--${color}`;
10875
+ };
10876
+ return {
10877
+ view: ({ attrs }) => {
10878
+ 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']);
10879
+ const isDeterminate = mode === 'determinate';
10880
+ const sizePixels = SIZE_MAP[size];
10881
+ const { radius, circumference, strokeDashoffset, percentage } = isDeterminate
10882
+ ? calculateStrokeProperties(sizePixels, value, max)
10883
+ : { radius: 0, circumference: 0, strokeDashoffset: 0, percentage: 0 };
10884
+ // Determine label content
10885
+ const labelContent = label !== undefined ? label : showPercentage && isDeterminate ? `${Math.round(percentage)}%` : '';
10886
+ // Build class names
10887
+ const classNames = [
10888
+ 'circular-progress',
10889
+ getSizeClass(size),
10890
+ getColorClass(color, colorIntensity),
10891
+ `circular-progress--${mode}`,
10892
+ className,
10893
+ ]
10894
+ .filter(Boolean)
10895
+ .join(' ');
10896
+ // ARIA attributes
10897
+ const ariaAttrs = isDeterminate
10898
+ ? {
10899
+ 'aria-valuenow': ariaValueNow !== undefined ? ariaValueNow : value,
10900
+ 'aria-valuemin': ariaValueMin,
10901
+ 'aria-valuemax': ariaValueMax,
10902
+ 'aria-valuetext': ariaValueText || `${Math.round(percentage)}%`,
10903
+ }
10904
+ : {
10905
+ 'aria-valuetext': ariaValueText || label || 'Loading',
10906
+ };
10907
+ 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), [
10908
+ // SVG circle
10909
+ m('svg.circular-progress__svg', {
10910
+ viewBox: `0 0 ${sizePixels} ${sizePixels}`,
10911
+ xmlns: 'http://www.w3.org/2000/svg',
10912
+ }, [
10913
+ // Background track circle
10914
+ m('circle.circular-progress__circle.circular-progress__circle--track', {
10915
+ cx: sizePixels / 2,
10916
+ cy: sizePixels / 2,
10917
+ r: radius || (sizePixels - STROKE_WIDTH) / 2,
10918
+ fill: 'none',
10919
+ stroke: 'currentColor',
10920
+ 'stroke-width': STROKE_WIDTH,
10921
+ }),
10922
+ // Progress indicator circle
10923
+ m('circle.circular-progress__circle.circular-progress__circle--indicator', {
10924
+ cx: sizePixels / 2,
10925
+ cy: sizePixels / 2,
10926
+ r: radius || (sizePixels - STROKE_WIDTH) / 2,
10927
+ fill: 'none',
10928
+ stroke: 'currentColor',
10929
+ 'stroke-width': STROKE_WIDTH,
10930
+ 'stroke-dasharray': isDeterminate ? circumference : undefined,
10931
+ 'stroke-dashoffset': isDeterminate ? strokeDashoffset : undefined,
10932
+ 'stroke-linecap': 'round',
10933
+ }),
10934
+ ]),
10935
+ // Label inside circle
10936
+ labelContent &&
10937
+ m('.circular-progress__label', {
10938
+ 'aria-hidden': 'true',
10939
+ }, labelContent),
10940
+ ]);
10941
+ },
10942
+ };
10943
+ };
10944
+
10945
+ /** Create a LinearProgress component */
10946
+ const LinearProgress = () => {
10947
+ const state = {
10948
+ id: uniqueId(),
10949
+ };
10950
+ /**
10951
+ * Get size class name
10952
+ */
10953
+ const getSizeClass = (size = 'medium') => {
10954
+ return `linear-progress__track--${size}`;
10955
+ };
10956
+ /**
10957
+ * Get color class name
10958
+ */
10959
+ const getColorClass = (color, intensity) => {
10960
+ if (!color)
10961
+ return '';
10962
+ return intensity ? `linear-progress--${color} linear-progress--${intensity}` : `linear-progress--${color}`;
10963
+ };
10964
+ return {
10965
+ view: ({ attrs }) => {
10966
+ 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']);
10967
+ const isDeterminate = mode === 'determinate';
10968
+ const percentage = Math.min(100, Math.max(0, (value / max) * 100));
10969
+ // Determine label content
10970
+ const labelContent = label !== undefined ? label : showPercentage && isDeterminate ? `${Math.round(percentage)}%` : '';
10971
+ // Build class names
10972
+ const classNames = [
10973
+ 'linear-progress',
10974
+ getColorClass(color, colorIntensity),
10975
+ className,
10976
+ ]
10977
+ .filter(Boolean)
10978
+ .join(' ');
10979
+ // ARIA attributes
10980
+ const ariaAttrs = isDeterminate
10981
+ ? {
10982
+ 'aria-valuenow': ariaValueNow !== undefined ? ariaValueNow : value,
10983
+ 'aria-valuemin': ariaValueMin,
10984
+ 'aria-valuemax': ariaValueMax,
10985
+ 'aria-valuetext': ariaValueText || `${Math.round(percentage)}%`,
10986
+ }
10987
+ : {
10988
+ 'aria-valuetext': ariaValueText || label || 'Loading',
10989
+ };
10990
+ return m('.linear-progress', Object.assign(Object.assign({}, params), { className: classNames, style,
10991
+ id }), [
10992
+ // Progress track container
10993
+ m('.linear-progress__track', Object.assign({ className: `linear-progress__track ${getSizeClass(size)}`, role: 'progressbar', 'aria-label': ariaLabel || (isDeterminate ? `Progress: ${Math.round(percentage)}%` : 'Loading') }, ariaAttrs), [
10994
+ // Progress bar
10995
+ m('.linear-progress__bar', {
10996
+ className: `linear-progress__bar ${isDeterminate ? '' : 'linear-progress__bar--indeterminate'}`,
10997
+ style: isDeterminate ? { width: `${percentage}%` } : undefined,
10998
+ }),
10999
+ ]),
11000
+ // Label at the end (right side)
11001
+ labelContent &&
11002
+ m('.linear-progress__label', {
11003
+ 'aria-hidden': 'true',
11004
+ }, labelContent),
11005
+ ]);
11006
+ },
11007
+ };
11008
+ };
11009
+
10714
11010
  /**
10715
11011
  * @fileoverview Core TypeScript utility types for mithril-materialized library
10716
11012
  * These types improve type safety and developer experience across all components
@@ -10732,4 +11028,4 @@ const isValidationError = (result) => !isValidationSuccess(result);
10732
11028
  // ============================================================================
10733
11029
  // All types are already exported via individual export declarations above
10734
11030
 
10735
- 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, 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 };
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 };
package/dist/index.js CHANGED
@@ -10713,6 +10713,302 @@ const Rating = () => {
10713
10713
  };
10714
10714
  };
10715
10715
 
10716
+ /**
10717
+ * ToggleButton component.
10718
+ *
10719
+ * A button that can be toggled on/off. Typically used within a ToggleGroup
10720
+ * to create grouped button controls where one or more buttons can be selected.
10721
+ *
10722
+ * @example
10723
+ * ```typescript
10724
+ * m(ToggleButton, {
10725
+ * value: 'bold',
10726
+ * iconName: 'format_bold',
10727
+ * checked: true,
10728
+ * tooltip: 'Bold',
10729
+ * onchange: () => console.log('Toggled')
10730
+ * })
10731
+ * ```
10732
+ */
10733
+ const ToggleButton = () => {
10734
+ return {
10735
+ view: ({ attrs }) => {
10736
+ const { checked, iconName, icon, label, onchange, className, tooltip } = attrs, rest = __rest(attrs, ["checked", "iconName", "icon", "label", "onchange", "className", "tooltip"]);
10737
+ const classes = [className, checked ? 'checked' : ''].filter(Boolean).join(' ');
10738
+ return m('button.btn-flat.waves-effect.toggle-button', Object.assign(Object.assign({}, rest), { className: classes, title: tooltip, onclick: () => {
10739
+ if (onchange) {
10740
+ onchange();
10741
+ }
10742
+ }, onmousedown: WavesEffect.onMouseDown, onmouseup: WavesEffect.onMouseUp, onmouseleave: WavesEffect.onMouseLeave, ontouchstart: WavesEffect.onTouchStart, ontouchend: WavesEffect.onTouchEnd }), [icon, iconName && m(Icon, { iconName, className: attrs.iconClass }), label]);
10743
+ },
10744
+ };
10745
+ };
10746
+
10747
+ /**
10748
+ * ToggleGroup component.
10749
+ *
10750
+ * A group of toggle buttons that can operate in single-select or multi-select mode.
10751
+ * The component supports both controlled and uncontrolled modes.
10752
+ *
10753
+ * **Controlled mode**: Manage the state externally using the `value` prop.
10754
+ * **Uncontrolled mode**: Let the component manage its own state using `defaultValue`.
10755
+ *
10756
+ * @example
10757
+ * ```typescript
10758
+ * // Single-select, controlled mode
10759
+ * let selected = 'left';
10760
+ * m(ToggleGroup, {
10761
+ * value: selected,
10762
+ * onchange: (v) => selected = v,
10763
+ * items: [
10764
+ * { value: 'left', iconName: 'format_align_left', tooltip: 'Align Left' },
10765
+ * { value: 'center', iconName: 'format_align_center', tooltip: 'Align Center' },
10766
+ * { value: 'right', iconName: 'format_align_right', tooltip: 'Align Right' }
10767
+ * ]
10768
+ * });
10769
+ *
10770
+ * // Multi-select, controlled mode
10771
+ * let selected = ['bold', 'italic'];
10772
+ * m(ToggleGroup, {
10773
+ * multiple: true,
10774
+ * value: selected,
10775
+ * onchange: (v) => selected = v,
10776
+ * items: [
10777
+ * { value: 'bold', iconName: 'format_bold', tooltip: 'Bold' },
10778
+ * { value: 'italic', iconName: 'format_italic', tooltip: 'Italic' },
10779
+ * { value: 'underline', iconName: 'format_underlined', tooltip: 'Underline' }
10780
+ * ]
10781
+ * });
10782
+ *
10783
+ * // Uncontrolled mode
10784
+ * m(ToggleGroup, {
10785
+ * defaultValue: 'left',
10786
+ * onchange: (v) => console.log('Changed to:', v),
10787
+ * items: [...]
10788
+ * });
10789
+ * ```
10790
+ */
10791
+ const ToggleGroup = () => {
10792
+ let internalValue;
10793
+ const handleSelect = (attrs, item, currentValue) => {
10794
+ if (attrs.disabled || item.disabled) {
10795
+ return;
10796
+ }
10797
+ const { value, multiple, onchange } = attrs;
10798
+ const isControlled = value !== undefined;
10799
+ if (multiple) {
10800
+ const currentValues = (Array.isArray(currentValue) ? currentValue : currentValue !== undefined ? [currentValue] : []);
10801
+ const newValues = currentValues.includes(item.value)
10802
+ ? currentValues.filter((v) => v !== item.value)
10803
+ : [...currentValues, item.value];
10804
+ if (!isControlled) {
10805
+ internalValue = newValues;
10806
+ }
10807
+ if (onchange) {
10808
+ onchange(newValues);
10809
+ }
10810
+ }
10811
+ else {
10812
+ const newValue = item.value;
10813
+ if (!isControlled) {
10814
+ internalValue = newValue;
10815
+ }
10816
+ if (onchange) {
10817
+ onchange(newValue);
10818
+ }
10819
+ }
10820
+ };
10821
+ return {
10822
+ oninit: ({ attrs }) => {
10823
+ internalValue = attrs.defaultValue;
10824
+ },
10825
+ view: ({ attrs }) => {
10826
+ const { value, items, multiple } = attrs;
10827
+ const isControlled = value !== undefined;
10828
+ const currentValue = isControlled ? value : internalValue;
10829
+ return m('.toggle-group', items.map((item) => m(ToggleButton, Object.assign(Object.assign({}, item), { checked: multiple && Array.isArray(currentValue)
10830
+ ? currentValue.includes(item.value)
10831
+ : currentValue === item.value, onchange: () => handleSelect(attrs, item, currentValue) }))));
10832
+ },
10833
+ };
10834
+ };
10835
+
10836
+ /** Size dimensions in pixels */
10837
+ const SIZE_MAP = {
10838
+ small: 36,
10839
+ medium: 50,
10840
+ large: 64,
10841
+ };
10842
+ /** Stroke width in pixels */
10843
+ const STROKE_WIDTH = 3;
10844
+ /** Create a CircularProgress component */
10845
+ const CircularProgress = () => {
10846
+ const state = {
10847
+ id: uniqueId(),
10848
+ };
10849
+ /**
10850
+ * Calculate SVG stroke properties for determinate progress
10851
+ */
10852
+ const calculateStrokeProperties = (size, value, max) => {
10853
+ const radius = (size - STROKE_WIDTH) / 2;
10854
+ const circumference = 2 * Math.PI * radius;
10855
+ const percentage = Math.min(100, Math.max(0, (value / max) * 100));
10856
+ const strokeDashoffset = circumference - (percentage / 100) * circumference;
10857
+ return {
10858
+ radius,
10859
+ circumference,
10860
+ strokeDashoffset,
10861
+ percentage,
10862
+ };
10863
+ };
10864
+ /**
10865
+ * Get size class name
10866
+ */
10867
+ const getSizeClass = (size = 'medium') => {
10868
+ return `circular-progress--${size}`;
10869
+ };
10870
+ /**
10871
+ * Get color class name
10872
+ */
10873
+ const getColorClass = (color, intensity) => {
10874
+ if (!color)
10875
+ return '';
10876
+ return intensity ? `circular-progress--${color} circular-progress--${intensity}` : `circular-progress--${color}`;
10877
+ };
10878
+ return {
10879
+ view: ({ attrs }) => {
10880
+ 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']);
10881
+ const isDeterminate = mode === 'determinate';
10882
+ const sizePixels = SIZE_MAP[size];
10883
+ const { radius, circumference, strokeDashoffset, percentage } = isDeterminate
10884
+ ? calculateStrokeProperties(sizePixels, value, max)
10885
+ : { radius: 0, circumference: 0, strokeDashoffset: 0, percentage: 0 };
10886
+ // Determine label content
10887
+ const labelContent = label !== undefined ? label : showPercentage && isDeterminate ? `${Math.round(percentage)}%` : '';
10888
+ // Build class names
10889
+ const classNames = [
10890
+ 'circular-progress',
10891
+ getSizeClass(size),
10892
+ getColorClass(color, colorIntensity),
10893
+ `circular-progress--${mode}`,
10894
+ className,
10895
+ ]
10896
+ .filter(Boolean)
10897
+ .join(' ');
10898
+ // ARIA attributes
10899
+ const ariaAttrs = isDeterminate
10900
+ ? {
10901
+ 'aria-valuenow': ariaValueNow !== undefined ? ariaValueNow : value,
10902
+ 'aria-valuemin': ariaValueMin,
10903
+ 'aria-valuemax': ariaValueMax,
10904
+ 'aria-valuetext': ariaValueText || `${Math.round(percentage)}%`,
10905
+ }
10906
+ : {
10907
+ 'aria-valuetext': ariaValueText || label || 'Loading',
10908
+ };
10909
+ 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), [
10910
+ // SVG circle
10911
+ m('svg.circular-progress__svg', {
10912
+ viewBox: `0 0 ${sizePixels} ${sizePixels}`,
10913
+ xmlns: 'http://www.w3.org/2000/svg',
10914
+ }, [
10915
+ // Background track circle
10916
+ m('circle.circular-progress__circle.circular-progress__circle--track', {
10917
+ cx: sizePixels / 2,
10918
+ cy: sizePixels / 2,
10919
+ r: radius || (sizePixels - STROKE_WIDTH) / 2,
10920
+ fill: 'none',
10921
+ stroke: 'currentColor',
10922
+ 'stroke-width': STROKE_WIDTH,
10923
+ }),
10924
+ // Progress indicator circle
10925
+ m('circle.circular-progress__circle.circular-progress__circle--indicator', {
10926
+ cx: sizePixels / 2,
10927
+ cy: sizePixels / 2,
10928
+ r: radius || (sizePixels - STROKE_WIDTH) / 2,
10929
+ fill: 'none',
10930
+ stroke: 'currentColor',
10931
+ 'stroke-width': STROKE_WIDTH,
10932
+ 'stroke-dasharray': isDeterminate ? circumference : undefined,
10933
+ 'stroke-dashoffset': isDeterminate ? strokeDashoffset : undefined,
10934
+ 'stroke-linecap': 'round',
10935
+ }),
10936
+ ]),
10937
+ // Label inside circle
10938
+ labelContent &&
10939
+ m('.circular-progress__label', {
10940
+ 'aria-hidden': 'true',
10941
+ }, labelContent),
10942
+ ]);
10943
+ },
10944
+ };
10945
+ };
10946
+
10947
+ /** Create a LinearProgress component */
10948
+ const LinearProgress = () => {
10949
+ const state = {
10950
+ id: uniqueId(),
10951
+ };
10952
+ /**
10953
+ * Get size class name
10954
+ */
10955
+ const getSizeClass = (size = 'medium') => {
10956
+ return `linear-progress__track--${size}`;
10957
+ };
10958
+ /**
10959
+ * Get color class name
10960
+ */
10961
+ const getColorClass = (color, intensity) => {
10962
+ if (!color)
10963
+ return '';
10964
+ return intensity ? `linear-progress--${color} linear-progress--${intensity}` : `linear-progress--${color}`;
10965
+ };
10966
+ return {
10967
+ view: ({ attrs }) => {
10968
+ 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']);
10969
+ const isDeterminate = mode === 'determinate';
10970
+ const percentage = Math.min(100, Math.max(0, (value / max) * 100));
10971
+ // Determine label content
10972
+ const labelContent = label !== undefined ? label : showPercentage && isDeterminate ? `${Math.round(percentage)}%` : '';
10973
+ // Build class names
10974
+ const classNames = [
10975
+ 'linear-progress',
10976
+ getColorClass(color, colorIntensity),
10977
+ className,
10978
+ ]
10979
+ .filter(Boolean)
10980
+ .join(' ');
10981
+ // ARIA attributes
10982
+ const ariaAttrs = isDeterminate
10983
+ ? {
10984
+ 'aria-valuenow': ariaValueNow !== undefined ? ariaValueNow : value,
10985
+ 'aria-valuemin': ariaValueMin,
10986
+ 'aria-valuemax': ariaValueMax,
10987
+ 'aria-valuetext': ariaValueText || `${Math.round(percentage)}%`,
10988
+ }
10989
+ : {
10990
+ 'aria-valuetext': ariaValueText || label || 'Loading',
10991
+ };
10992
+ return m('.linear-progress', Object.assign(Object.assign({}, params), { className: classNames, style,
10993
+ id }), [
10994
+ // Progress track container
10995
+ m('.linear-progress__track', Object.assign({ className: `linear-progress__track ${getSizeClass(size)}`, role: 'progressbar', 'aria-label': ariaLabel || (isDeterminate ? `Progress: ${Math.round(percentage)}%` : 'Loading') }, ariaAttrs), [
10996
+ // Progress bar
10997
+ m('.linear-progress__bar', {
10998
+ className: `linear-progress__bar ${isDeterminate ? '' : 'linear-progress__bar--indeterminate'}`,
10999
+ style: isDeterminate ? { width: `${percentage}%` } : undefined,
11000
+ }),
11001
+ ]),
11002
+ // Label at the end (right side)
11003
+ labelContent &&
11004
+ m('.linear-progress__label', {
11005
+ 'aria-hidden': 'true',
11006
+ }, labelContent),
11007
+ ]);
11008
+ },
11009
+ };
11010
+ };
11011
+
10716
11012
  /**
10717
11013
  * @fileoverview Core TypeScript utility types for mithril-materialized library
10718
11014
  * These types improve type safety and developer experience across all components
@@ -10744,6 +11040,7 @@ exports.ButtonFactory = ButtonFactory;
10744
11040
  exports.Carousel = Carousel;
10745
11041
  exports.CharacterCounter = CharacterCounter;
10746
11042
  exports.Chips = Chips;
11043
+ exports.CircularProgress = CircularProgress;
10747
11044
  exports.CodeBlock = CodeBlock;
10748
11045
  exports.Collapsible = Collapsible;
10749
11046
  exports.CollapsibleItem = CollapsibleItem;
@@ -10766,6 +11063,7 @@ exports.ImageList = ImageList;
10766
11063
  exports.InputCheckbox = InputCheckbox;
10767
11064
  exports.Label = Label;
10768
11065
  exports.LargeButton = LargeButton;
11066
+ exports.LinearProgress = LinearProgress;
10769
11067
  exports.ListItem = ListItem;
10770
11068
  exports.Mandatory = Mandatory;
10771
11069
  exports.Masonry = Masonry;
@@ -10808,6 +11106,7 @@ exports.TimeRangePicker = TimeRangePicker;
10808
11106
  exports.Timeline = Timeline;
10809
11107
  exports.Toast = Toast;
10810
11108
  exports.ToastComponent = ToastComponent;
11109
+ exports.ToggleGroup = ToggleGroup;
10811
11110
  exports.Tooltip = Tooltip;
10812
11111
  exports.TooltipComponent = TooltipComponent;
10813
11112
  exports.TreeView = TreeView;