mithril-materialized 3.2.2 → 3.3.1

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.umd.js CHANGED
@@ -2855,20 +2855,40 @@
2855
2855
  state.height = undefined;
2856
2856
  }
2857
2857
  };
2858
- const isControlled = (attrs) => attrs.value !== undefined && attrs.oninput !== undefined;
2858
+ const isControlled = (attrs) => attrs.value !== undefined && (attrs.oninput !== undefined || attrs.onchange !== undefined);
2859
2859
  return {
2860
2860
  oninit: ({ attrs }) => {
2861
+ const controlled = isControlled(attrs);
2862
+ const isNonInteractive = attrs.readonly || attrs.disabled;
2863
+ // Warn developer for improper controlled usage
2864
+ if (attrs.value !== undefined && !controlled && !isNonInteractive) {
2865
+ console.warn(`TextArea received 'value' prop without 'oninput' or 'onchange' handler. ` +
2866
+ `Use 'defaultValue' for uncontrolled components or add an event handler for controlled components.`);
2867
+ }
2861
2868
  // Initialize internal value for uncontrolled mode
2862
- if (!isControlled(attrs)) {
2869
+ if (!controlled) {
2863
2870
  state.internalValue = attrs.defaultValue || '';
2864
2871
  }
2865
2872
  },
2866
2873
  onremove: () => {
2867
2874
  },
2868
2875
  view: ({ attrs }) => {
2876
+ var _a, _b, _c, _d;
2869
2877
  const { className = 'col s12', helperText, iconName, id = state.id, value, placeholder, isMandatory, label, maxLength, oninput, onchange, onkeydown, onkeypress, onkeyup, onblur, style } = attrs, params = __rest(attrs, ["className", "helperText", "iconName", "id", "value", "placeholder", "isMandatory", "label", "maxLength", "oninput", "onchange", "onkeydown", "onkeypress", "onkeyup", "onblur", "style"]);
2870
2878
  const controlled = isControlled(attrs);
2871
- const currentValue = controlled ? value || '' : state.internalValue;
2879
+ const isNonInteractive = attrs.readonly || attrs.disabled;
2880
+ let currentValue;
2881
+ if (controlled) {
2882
+ currentValue = value || '';
2883
+ }
2884
+ else if (isNonInteractive) {
2885
+ // Non-interactive components: prefer defaultValue, fallback to value
2886
+ currentValue = (_b = (_a = attrs.defaultValue) !== null && _a !== void 0 ? _a : value) !== null && _b !== void 0 ? _b : '';
2887
+ }
2888
+ else {
2889
+ // Interactive uncontrolled: use internal state
2890
+ currentValue = (_d = (_c = state.internalValue) !== null && _c !== void 0 ? _c : attrs.defaultValue) !== null && _d !== void 0 ? _d : '';
2891
+ }
2872
2892
  return [
2873
2893
  // Hidden div for height measurement - positioned outside the input-field
2874
2894
  m('.hiddendiv', {
@@ -2999,7 +3019,8 @@
2999
3019
  isDragging: false,
3000
3020
  activeThumb: null,
3001
3021
  };
3002
- const isControlled = (attrs) => 'value' in attrs && typeof attrs.value !== 'undefined' && typeof attrs.oninput === 'function';
3022
+ const isControlled = (attrs) => 'value' in attrs && typeof attrs.value !== 'undefined' &&
3023
+ (typeof attrs.oninput === 'function' || typeof attrs.onchange === 'function');
3003
3024
  const getValue = (target) => {
3004
3025
  const val = target.value;
3005
3026
  return (val ? (type === 'number' || type === 'range' ? +val : val) : val);
@@ -3045,8 +3066,15 @@
3045
3066
  // Range slider rendering functions are now in separate module
3046
3067
  return {
3047
3068
  oninit: ({ attrs }) => {
3069
+ const controlled = isControlled(attrs);
3070
+ const isNonInteractive = attrs.readonly || attrs.disabled;
3071
+ // Warn developer for improper controlled usage
3072
+ if (attrs.value !== undefined && !controlled && !isNonInteractive) {
3073
+ console.warn(`${type} input received 'value' prop without 'oninput' or 'onchange' handler. ` +
3074
+ `Use 'defaultValue' for uncontrolled components or add an event handler for controlled components.`);
3075
+ }
3048
3076
  // Initialize internal value if not in controlled mode
3049
- if (!isControlled(attrs)) {
3077
+ if (!controlled) {
3050
3078
  const isNumeric = ['number', 'range'].includes(type);
3051
3079
  if (attrs.defaultValue !== undefined) {
3052
3080
  if (isNumeric) {
@@ -3062,7 +3090,7 @@
3062
3090
  }
3063
3091
  },
3064
3092
  view: ({ attrs }) => {
3065
- var _a, _b;
3093
+ var _a, _b, _c, _d, _e, _f;
3066
3094
  const { className = 'col s12', dataError, dataSuccess, helperText, iconName, id = state.id, placeholder, isMandatory, label, maxLength, newRow, oninput, onchange, onkeydown, onkeypress, onkeyup, style, validate, canClear } = attrs, params = __rest(attrs, ["className", "dataError", "dataSuccess", "helperText", "iconName", "id", "placeholder", "isMandatory", "label", "maxLength", "newRow", "oninput", "onchange", "onkeydown", "onkeypress", "onkeyup", "style", "validate", "canClear"]);
3067
3095
  // const attributes = toAttrs(params);
3068
3096
  const cn = [newRow ? 'clear' : '', defaultClass, className].filter(Boolean).join(' ').trim() || undefined;
@@ -3082,7 +3110,19 @@
3082
3110
  }
3083
3111
  const isNumeric = ['number', 'range'].includes(type);
3084
3112
  const controlled = isControlled(attrs);
3085
- const value = (controlled ? attrs.value : state.internalValue);
3113
+ const isNonInteractive = attrs.readonly || attrs.disabled;
3114
+ let value;
3115
+ if (controlled) {
3116
+ value = attrs.value;
3117
+ }
3118
+ else if (isNonInteractive) {
3119
+ // Non-interactive components: prefer defaultValue, fallback to value
3120
+ value = ((_c = (_b = attrs.defaultValue) !== null && _b !== void 0 ? _b : attrs.value) !== null && _c !== void 0 ? _c : (isNumeric ? 0 : ''));
3121
+ }
3122
+ else {
3123
+ // Interactive uncontrolled: use internal state
3124
+ value = ((_e = (_d = state.internalValue) !== null && _d !== void 0 ? _d : attrs.defaultValue) !== null && _e !== void 0 ? _e : (isNumeric ? 0 : ''));
3125
+ }
3086
3126
  const rangeType = type === 'range' && !attrs.minmax;
3087
3127
  return m('.input-field', { className: cn, style }, [
3088
3128
  iconName ? m('i.material-icons.prefix', iconName) : undefined,
@@ -3248,7 +3288,7 @@
3248
3288
  }
3249
3289
  } })),
3250
3290
  // Clear button - only for text inputs with canClear enabled and has content
3251
- canClear && type === 'text' && ((_b = state.inputElement) === null || _b === void 0 ? void 0 : _b.value)
3291
+ canClear && type === 'text' && ((_f = state.inputElement) === null || _f === void 0 ? void 0 : _f.value)
3252
3292
  ? m(MaterialIcon, {
3253
3293
  name: 'close',
3254
3294
  className: 'input-clear-btn',
@@ -5645,17 +5685,35 @@
5645
5685
  return {
5646
5686
  oninit: ({ attrs }) => {
5647
5687
  state.componentId = attrs.id || uniqueId();
5688
+ const controlled = isControlled(attrs);
5689
+ // Warn developer for improper controlled usage
5690
+ if (attrs.checkedId !== undefined && !controlled && !attrs.disabled) {
5691
+ console.warn(`RadioButtons component received 'checkedId' prop without 'onchange' handler. ` +
5692
+ `Use 'defaultCheckedId' for uncontrolled components or add 'onchange' for controlled components.`);
5693
+ }
5648
5694
  // Initialize internal state for uncontrolled mode
5649
- if (!isControlled(attrs)) {
5695
+ if (!controlled) {
5650
5696
  state.internalCheckedId = attrs.defaultCheckedId;
5651
5697
  }
5652
5698
  },
5653
5699
  view: ({ attrs }) => {
5700
+ var _a, _b;
5654
5701
  const { checkedId, newRow, className = 'col s12', label = '', disabled, description, options, isMandatory, checkboxClass, layout = 'vertical', onchange, } = attrs;
5655
5702
  const { groupId, componentId } = state;
5656
5703
  const controlled = isControlled(attrs);
5657
5704
  // Get current checked ID from props or internal state
5658
- const currentCheckedId = controlled ? checkedId : state.internalCheckedId;
5705
+ let currentCheckedId;
5706
+ if (controlled) {
5707
+ currentCheckedId = checkedId;
5708
+ }
5709
+ else if (disabled) {
5710
+ // Non-interactive components: prefer defaultCheckedId, fallback to checkedId
5711
+ currentCheckedId = (_a = attrs.defaultCheckedId) !== null && _a !== void 0 ? _a : checkedId;
5712
+ }
5713
+ else {
5714
+ // Interactive uncontrolled: use internal state
5715
+ currentCheckedId = (_b = state.internalCheckedId) !== null && _b !== void 0 ? _b : attrs.defaultCheckedId;
5716
+ }
5659
5717
  const handleChange = (id) => {
5660
5718
  // Update internal state for uncontrolled mode
5661
5719
  if (!controlled) {
@@ -5785,8 +5843,14 @@
5785
5843
  return {
5786
5844
  oninit: ({ attrs }) => {
5787
5845
  state.id = attrs.id || uniqueId();
5846
+ const controlled = isControlled(attrs);
5847
+ // Warn developer for improper controlled usage
5848
+ if (attrs.checkedId !== undefined && !controlled && !attrs.disabled) {
5849
+ console.warn(`Select component received 'checkedId' prop without 'onchange' handler. ` +
5850
+ `Use 'defaultCheckedId' for uncontrolled components or add 'onchange' for controlled components.`);
5851
+ }
5788
5852
  // Initialize internal state for uncontrolled mode
5789
- if (!isControlled(attrs)) {
5853
+ if (!controlled) {
5790
5854
  const defaultIds = attrs.defaultCheckedId !== undefined
5791
5855
  ? Array.isArray(attrs.defaultCheckedId)
5792
5856
  ? attrs.defaultCheckedId
@@ -5802,16 +5866,32 @@
5802
5866
  document.removeEventListener('click', closeDropdown);
5803
5867
  },
5804
5868
  view: ({ attrs }) => {
5869
+ var _a;
5805
5870
  const controlled = isControlled(attrs);
5871
+ const { disabled } = attrs;
5806
5872
  // Get selected IDs from props or internal state
5807
- const selectedIds = controlled
5808
- ? attrs.checkedId !== undefined
5873
+ let selectedIds;
5874
+ if (controlled) {
5875
+ selectedIds = attrs.checkedId !== undefined
5809
5876
  ? Array.isArray(attrs.checkedId)
5810
5877
  ? attrs.checkedId
5811
5878
  : [attrs.checkedId]
5812
- : []
5813
- : state.internalSelectedIds;
5814
- const { newRow, className = 'col s12', key, options, multiple = false, label, helperText, placeholder = '', isMandatory, iconName, disabled, style, } = attrs;
5879
+ : [];
5880
+ }
5881
+ else if (disabled) {
5882
+ // Non-interactive components: prefer defaultCheckedId, fallback to checkedId
5883
+ const fallbackId = (_a = attrs.defaultCheckedId) !== null && _a !== void 0 ? _a : attrs.checkedId;
5884
+ selectedIds = fallbackId !== undefined
5885
+ ? Array.isArray(fallbackId)
5886
+ ? fallbackId
5887
+ : [fallbackId]
5888
+ : [];
5889
+ }
5890
+ else {
5891
+ // Interactive uncontrolled: use internal state
5892
+ selectedIds = state.internalSelectedIds;
5893
+ }
5894
+ const { newRow, className = 'col s12', key, options, multiple = false, label, helperText, placeholder = '', isMandatory, iconName, style, } = attrs;
5815
5895
  const finalClassName = newRow ? `${className} clear` : className;
5816
5896
  const selectedOptions = options.filter((opt) => isSelected(opt.id, selectedIds));
5817
5897
  return m('.input-field.select-space', {
@@ -8548,6 +8628,269 @@
8548
8628
  };
8549
8629
  };
8550
8630
 
8631
+ /** Default star icons */
8632
+ const DEFAULT_ICONS = {
8633
+ filled: '★',
8634
+ empty: '☆',
8635
+ half: '☆', // We'll handle half-fill with CSS
8636
+ };
8637
+ /** Create a Rating component */
8638
+ const Rating = () => {
8639
+ const state = {
8640
+ id: uniqueId(),
8641
+ internalValue: 0,
8642
+ hoverValue: null,
8643
+ isHovering: false,
8644
+ isFocused: false,
8645
+ };
8646
+ const isControlled = (attrs) => typeof attrs.value !== 'undefined' && typeof attrs.onchange === 'function';
8647
+ const getCurrentValue = (attrs) => {
8648
+ var _a, _b, _c, _d;
8649
+ const controlled = isControlled(attrs);
8650
+ const isNonInteractive = attrs.readonly || attrs.disabled;
8651
+ if (controlled) {
8652
+ return attrs.value || 0;
8653
+ }
8654
+ // Non-interactive components: prefer defaultValue, fallback to value
8655
+ if (isNonInteractive) {
8656
+ return (_b = (_a = attrs.defaultValue) !== null && _a !== void 0 ? _a : attrs.value) !== null && _b !== void 0 ? _b : 0;
8657
+ }
8658
+ // Interactive uncontrolled: use internal state (user can change it)
8659
+ return (_d = (_c = state.internalValue) !== null && _c !== void 0 ? _c : attrs.defaultValue) !== null && _d !== void 0 ? _d : 0;
8660
+ };
8661
+ const getDisplayValue = (attrs) => state.isHovering && state.hoverValue !== null ? state.hoverValue : getCurrentValue(attrs);
8662
+ const getLabelText = (value, max, getLabelFn) => {
8663
+ if (getLabelFn) {
8664
+ return getLabelFn(value, max);
8665
+ }
8666
+ if (value === 0) {
8667
+ return `No rating`;
8668
+ }
8669
+ if (value === 1) {
8670
+ return `1 star out of ${max}`;
8671
+ }
8672
+ return `${value} stars out of ${max}`;
8673
+ };
8674
+ const getSizeClass = (size = 'medium') => {
8675
+ switch (size) {
8676
+ case 'small':
8677
+ return 'rating--small';
8678
+ case 'large':
8679
+ return 'rating--large';
8680
+ default:
8681
+ return 'rating--medium';
8682
+ }
8683
+ };
8684
+ const getDensityClass = (density = 'standard') => {
8685
+ switch (density) {
8686
+ case 'compact':
8687
+ return 'rating--compact';
8688
+ case 'comfortable':
8689
+ return 'rating--comfortable';
8690
+ default:
8691
+ return 'rating--standard';
8692
+ }
8693
+ };
8694
+ const handleItemClick = (attrs, clickValue) => {
8695
+ var _a;
8696
+ if (attrs.readonly || attrs.disabled)
8697
+ return;
8698
+ const currentValue = getCurrentValue(attrs);
8699
+ const newValue = attrs.clearable && currentValue === clickValue ? 0 : clickValue;
8700
+ if (!isControlled(attrs)) {
8701
+ state.internalValue = newValue;
8702
+ }
8703
+ (_a = attrs.onchange) === null || _a === void 0 ? void 0 : _a.call(attrs, newValue);
8704
+ };
8705
+ const handleItemHover = (attrs, hoverValue) => {
8706
+ var _a;
8707
+ if (attrs.readonly || attrs.disabled)
8708
+ return;
8709
+ state.hoverValue = hoverValue;
8710
+ state.isHovering = true;
8711
+ (_a = attrs.onmouseover) === null || _a === void 0 ? void 0 : _a.call(attrs, hoverValue);
8712
+ };
8713
+ const handleMouseLeave = (attrs) => {
8714
+ if (attrs.readonly || attrs.disabled)
8715
+ return;
8716
+ state.isHovering = false;
8717
+ state.hoverValue = null;
8718
+ };
8719
+ const handleKeyDown = (attrs, e) => {
8720
+ var _a;
8721
+ if (attrs.readonly || attrs.disabled)
8722
+ return;
8723
+ const max = attrs.max || 5;
8724
+ const step = attrs.step || 1;
8725
+ const currentValue = getCurrentValue(attrs);
8726
+ let newValue = currentValue;
8727
+ switch (e.key) {
8728
+ case 'ArrowRight':
8729
+ case 'ArrowUp':
8730
+ e.preventDefault();
8731
+ newValue = Math.min(max, currentValue + step);
8732
+ break;
8733
+ case 'ArrowLeft':
8734
+ case 'ArrowDown':
8735
+ e.preventDefault();
8736
+ newValue = Math.max(0, currentValue - step);
8737
+ break;
8738
+ case 'Home':
8739
+ e.preventDefault();
8740
+ newValue = attrs.clearable ? 0 : step;
8741
+ break;
8742
+ case 'End':
8743
+ e.preventDefault();
8744
+ newValue = max;
8745
+ break;
8746
+ case ' ':
8747
+ case 'Enter':
8748
+ e.preventDefault();
8749
+ // If focused and not hovering, increment by step
8750
+ if (!state.isHovering) {
8751
+ newValue = currentValue + step > max ? (attrs.clearable ? 0 : step) : currentValue + step;
8752
+ }
8753
+ break;
8754
+ case 'Escape':
8755
+ if (attrs.clearable) {
8756
+ e.preventDefault();
8757
+ newValue = 0;
8758
+ }
8759
+ break;
8760
+ default:
8761
+ return;
8762
+ }
8763
+ if (newValue !== currentValue) {
8764
+ if (!isControlled(attrs)) {
8765
+ state.internalValue = newValue;
8766
+ }
8767
+ (_a = attrs.onchange) === null || _a === void 0 ? void 0 : _a.call(attrs, newValue);
8768
+ }
8769
+ };
8770
+ const RatingItem = () => {
8771
+ return {
8772
+ view: ({ attrs }) => {
8773
+ const { index, displayValue, step, icons, allowHalfSteps, disabled, onclick, onmouseover } = attrs;
8774
+ const itemValue = (index + 1) * step;
8775
+ // Calculate fill state based on displayValue vs itemValue
8776
+ const diff = displayValue - itemValue;
8777
+ const fillState = diff >= 0 ? 'full' : allowHalfSteps && diff >= -step / 2 ? 'half' : 'empty';
8778
+ return m('.rating__item', {
8779
+ className: [
8780
+ fillState === 'full' ? 'rating__item--filled' : '',
8781
+ fillState === 'half' ? 'rating__item--half' : '',
8782
+ fillState !== 'empty' ? 'rating__item--active' : '',
8783
+ disabled ? 'rating__item--disabled' : '',
8784
+ ]
8785
+ .filter(Boolean)
8786
+ .join(' '),
8787
+ onclick,
8788
+ onmouseover,
8789
+ }, [
8790
+ // Empty icon (background)
8791
+ m('.rating__icon.rating__icon--empty', { 'aria-hidden': 'true' }, typeof icons.empty === 'string' ? icons.empty : m(icons.empty)),
8792
+ // Filled icon (foreground)
8793
+ m('.rating__icon.rating__icon--filled', {
8794
+ 'aria-hidden': 'true',
8795
+ style: {
8796
+ clipPath: fillState === 'half' ? 'inset(0 50% 0 0)' : undefined,
8797
+ },
8798
+ }, typeof icons.filled === 'string' ? icons.filled : m(icons.filled)),
8799
+ ]);
8800
+ },
8801
+ };
8802
+ };
8803
+ return {
8804
+ oninit: ({ attrs }) => {
8805
+ const controlled = isControlled(attrs);
8806
+ const isNonInteractive = attrs.readonly || attrs.disabled;
8807
+ // Warn developer for improper controlled usage
8808
+ if (attrs.value !== undefined && !controlled && !isNonInteractive) {
8809
+ console.warn(`Rating component received 'value' prop without 'onchange' handler. ` +
8810
+ `Use 'defaultValue' for uncontrolled components or add 'onchange' for controlled components.`);
8811
+ }
8812
+ if (!controlled) {
8813
+ state.internalValue = attrs.defaultValue || 0;
8814
+ }
8815
+ },
8816
+ view: ({ attrs }) => {
8817
+ const { max = 5, step = 1, size = 'medium', density = 'standard', className = '', style = {}, readonly: readonly = false, disabled = false, id = state.id, name } = attrs, ariaAttrs = __rest(attrs, ["max", "step", "size", "density", "className", "style", "readonly", "disabled", "id", "name"]);
8818
+ const currentValue = getCurrentValue(attrs);
8819
+ const displayValue = getDisplayValue(attrs);
8820
+ const itemCount = Math.ceil(max / step);
8821
+ return m('.rating', {
8822
+ className: [
8823
+ 'rating',
8824
+ getSizeClass(size),
8825
+ getDensityClass(density),
8826
+ readonly ? 'rating--read-only' : '',
8827
+ disabled ? 'rating--disabled' : '',
8828
+ state.isFocused ? 'rating--focused' : '',
8829
+ className,
8830
+ ]
8831
+ .filter(Boolean)
8832
+ .join(' '),
8833
+ style,
8834
+ id,
8835
+ role: readonly ? 'img' : 'slider',
8836
+ tabindex: readonly || disabled ? -1 : 0,
8837
+ 'aria-valuemin': 0,
8838
+ 'aria-valuemax': max,
8839
+ 'aria-valuenow': currentValue,
8840
+ 'aria-valuetext': getLabelText(currentValue, max, attrs.getLabelText),
8841
+ 'aria-label': ariaAttrs['aria-label'] ||
8842
+ attrs.ariaLabel ||
8843
+ `Rating: ${getLabelText(currentValue, max, attrs.getLabelText)}`,
8844
+ 'aria-labelledby': ariaAttrs['aria-labelledby'],
8845
+ 'aria-readonly': readonly,
8846
+ 'aria-disabled': disabled,
8847
+ onkeydown: (e) => handleKeyDown(attrs, e),
8848
+ onfocus: () => {
8849
+ state.isFocused = true;
8850
+ },
8851
+ onblur: () => {
8852
+ state.isFocused = false;
8853
+ handleMouseLeave(attrs);
8854
+ },
8855
+ onmouseleave: () => handleMouseLeave(attrs),
8856
+ }, [
8857
+ // Hidden input for form submission
8858
+ name &&
8859
+ m('input', {
8860
+ type: 'hidden',
8861
+ name,
8862
+ value: currentValue,
8863
+ }),
8864
+ // Rating items
8865
+ m('.rating__items', {
8866
+ className: 'rating__items',
8867
+ },
8868
+ // Array.from({ length: itemCount }, (_, i) => renderRatingItem(attrs, i))
8869
+ [...Array(itemCount)].map((_, i) => {
8870
+ const itemValue = (i + 1) * step;
8871
+ return m(RatingItem, {
8872
+ key: `rating-item-${i}`,
8873
+ index: i,
8874
+ displayValue: displayValue,
8875
+ step,
8876
+ icons: Object.assign(Object.assign({}, DEFAULT_ICONS), attrs.icon),
8877
+ allowHalfSteps: attrs.allowHalfSteps,
8878
+ disabled: attrs.disabled,
8879
+ onclick: () => handleItemClick(attrs, itemValue),
8880
+ onmouseover: () => handleItemHover(attrs, itemValue),
8881
+ });
8882
+ })),
8883
+ // Screen reader text
8884
+ m('.rating__sr-only', {
8885
+ className: 'rating__sr-only',
8886
+ 'aria-live': 'polite',
8887
+ 'aria-atomic': 'true',
8888
+ }, getLabelText(displayValue, max, attrs.getLabelText)),
8889
+ ]);
8890
+ },
8891
+ };
8892
+ };
8893
+
8551
8894
  /**
8552
8895
  * @fileoverview Core TypeScript utility types for mithril-materialized library
8553
8896
  * These types improve type safety and developer experience across all components
@@ -8616,6 +8959,7 @@
8616
8959
  exports.RadioButton = RadioButton;
8617
8960
  exports.RadioButtons = RadioButtons;
8618
8961
  exports.RangeInput = RangeInput;
8962
+ exports.Rating = Rating;
8619
8963
  exports.RoundIconButton = RoundIconButton;
8620
8964
  exports.SearchSelect = SearchSelect;
8621
8965
  exports.SecondaryContent = SecondaryContent;
@@ -0,0 +1,65 @@
1
+ import m, { FactoryComponent, Attributes } from 'mithril';
2
+ /** Rating component size options */
3
+ export type RatingSize = 'small' | 'medium' | 'large';
4
+ /** Rating component density options */
5
+ export type RatingDensity = 'compact' | 'standard' | 'comfortable';
6
+ /** Custom icon configuration */
7
+ export interface RatingIconConfig {
8
+ /** Custom filled icon component or string */
9
+ filled?: string | m.Component;
10
+ /** Custom empty icon component or string */
11
+ empty?: string | m.Component;
12
+ /** Custom half-filled icon component or string (for fractional ratings) */
13
+ half?: string | m.Component;
14
+ }
15
+ /** Rating component attributes */
16
+ export interface RatingAttrs extends Attributes {
17
+ /** Current rating value */
18
+ value?: number;
19
+ /** Maximum rating value (default: 5) */
20
+ max?: number;
21
+ /** Step size for rating increments (default: 1, can be 0.5 for half-steps) */
22
+ step?: number;
23
+ /** Initial value for uncontrolled mode */
24
+ defaultValue?: number;
25
+ /** Whether the rating is read-only */
26
+ readonly?: boolean;
27
+ /** Whether the rating is disabled */
28
+ disabled?: boolean;
29
+ /** Whether the rating can be cleared/reset to 0 */
30
+ clearable?: boolean;
31
+ /** Size variant */
32
+ size?: RatingSize;
33
+ /** Density variant */
34
+ density?: RatingDensity;
35
+ /** Custom icon configuration */
36
+ icon?: RatingIconConfig;
37
+ /** Class name for the container */
38
+ className?: string;
39
+ /** Additional CSS styles */
40
+ style?: any;
41
+ /** Callback when rating changes */
42
+ onchange?: (value: number) => void;
43
+ /** Callback when rating is hovered (preview mode) */
44
+ onmouseover?: (value: number) => void;
45
+ /** Function to get label text for accessibility */
46
+ getLabelText?: (value: number, max: number) => string;
47
+ /** Whether to show tooltips on hover */
48
+ showTooltips?: boolean;
49
+ /** Custom tooltip labels for each rating value */
50
+ tooltipLabels?: string[];
51
+ /** Whether to allow fractional display (e.g., 3.5 stars) */
52
+ allowHalfSteps?: boolean;
53
+ /** Name for form submission */
54
+ name?: string;
55
+ /** HTML ID for the component */
56
+ id?: string;
57
+ /** ARIA label for the component */
58
+ 'aria-label'?: string;
59
+ /** ARIA label for the component (camelCase alternative) */
60
+ ariaLabel?: string;
61
+ /** ARIA labelledby reference */
62
+ 'aria-labelledby'?: string;
63
+ }
64
+ /** Create a Rating component */
65
+ export declare const Rating: FactoryComponent<RatingAttrs>;
package/dist/switch.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { FactoryComponent } from 'mithril';
2
2
  import { InputAttrs } from './input-options';
3
- export interface SwitchOptions extends Partial<InputAttrs<boolean>> {
3
+ export interface SwitchAttrs extends Pick<InputAttrs<boolean>, 'label' | 'disabled' | 'id' | 'className' | 'onchange' | 'newRow' | 'isMandatory'> {
4
4
  /** Left text label */
5
5
  left?: string;
6
6
  /** Right text label */
@@ -9,4 +9,4 @@ export interface SwitchOptions extends Partial<InputAttrs<boolean>> {
9
9
  checked?: boolean;
10
10
  }
11
11
  /** Component to display a switch with two values. */
12
- export declare const Switch: FactoryComponent<SwitchOptions>;
12
+ export declare const Switch: FactoryComponent<SwitchAttrs>;
@@ -2630,7 +2630,7 @@ table.striped tr {
2630
2630
  border-bottom: none;
2631
2631
  }
2632
2632
  table.striped > tbody > tr:nth-child(odd) {
2633
- background-color: rgba(242, 242, 242, 0.5);
2633
+ background-color: var(--mm-table-striped-color, rgba(55, 55, 55, 0.5));
2634
2634
  }
2635
2635
  table.striped > tbody > tr > td {
2636
2636
  border-radius: 0;
@@ -2639,7 +2639,7 @@ table.highlight > tbody > tr {
2639
2639
  transition: background-color 0.25s ease;
2640
2640
  }
2641
2641
  table.highlight > tbody > tr:hover {
2642
- background-color: rgba(242, 242, 242, 0.5);
2642
+ background-color: var(--mm-table-striped-color, rgba(55, 55, 55, 0.5));
2643
2643
  }
2644
2644
  table.centered thead tr th, table.centered tbody tr td {
2645
2645
  text-align: center;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mithril-materialized",
3
- "version": "3.2.2",
3
+ "version": "3.3.1",
4
4
  "description": "A materialize library for mithril.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.esm.js",
@@ -373,7 +373,7 @@
373
373
  }
374
374
 
375
375
  .datatable.striped tbody tr:nth-child(odd) {
376
- background-color: rgba(255, 255, 255, 0.05);
376
+ background-color: variables.$table-striped-color;
377
377
  }
378
378
 
379
379
  .datatable.fixed-header thead th {