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/README.md +37 -27
- package/dist/advanced.css +2 -2
- package/dist/components.css +241 -3
- package/dist/core.css +3 -3
- package/dist/forms.css +3 -3
- package/dist/index.css +242 -4
- package/dist/index.d.ts +1 -0
- package/dist/index.esm.js +360 -17
- package/dist/index.js +360 -16
- package/dist/index.min.css +2 -2
- package/dist/index.umd.js +360 -16
- package/dist/rating.d.ts +65 -0
- package/dist/switch.d.ts +2 -2
- package/dist/utilities.css +2 -2
- package/package.json +1 -1
- package/sass/components/_datatable.scss +1 -1
- package/sass/components/_rating.scss +341 -0
- package/sass/components/_theme-variables.scss +9 -0
- package/sass/components/_variables.scss +2 -1
- package/sass/components/forms/_input-fields.scss +1 -1
- package/sass/materialize.scss +2 -1
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 (!
|
|
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
|
|
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' &&
|
|
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 (!
|
|
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
|
|
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' && ((
|
|
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 (!
|
|
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
|
-
|
|
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 (!
|
|
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
|
-
|
|
5808
|
-
|
|
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
|
-
|
|
5814
|
-
|
|
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;
|
package/dist/rating.d.ts
ADDED
|
@@ -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
|
|
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<
|
|
12
|
+
export declare const Switch: FactoryComponent<SwitchAttrs>;
|
package/dist/utilities.css
CHANGED
|
@@ -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(
|
|
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(
|
|
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