mithril-materialized 3.2.1 → 3.3.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/README.md +37 -27
- package/dist/advanced.css +2 -2
- package/dist/components.css +241 -3
- package/dist/core.css +5 -2
- package/dist/forms.css +5 -2
- package/dist/index.css +244 -3
- package/dist/index.d.ts +1 -0
- package/dist/index.esm.js +516 -109
- package/dist/index.js +516 -108
- package/dist/index.min.css +2 -2
- package/dist/index.umd.js +516 -108
- package/dist/rating.d.ts +65 -0
- package/dist/switch.d.ts +2 -2
- package/dist/utilities.css +2 -2
- package/package.json +5 -5
- 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 +3 -0
- package/sass/materialize.scss +2 -1
package/dist/index.js
CHANGED
|
@@ -2791,10 +2791,6 @@ const CharacterCounter = () => {
|
|
|
2791
2791
|
return m('span.character-counter', {
|
|
2792
2792
|
style: {
|
|
2793
2793
|
color: isOverLimit ? '#F44336' : '#9e9e9e',
|
|
2794
|
-
fontSize: '12px',
|
|
2795
|
-
display: 'block',
|
|
2796
|
-
textAlign: 'right',
|
|
2797
|
-
marginTop: '8px',
|
|
2798
2794
|
},
|
|
2799
2795
|
}, `${currentLength}/${maxLength}`);
|
|
2800
2796
|
},
|
|
@@ -2811,111 +2807,197 @@ const TextArea = () => {
|
|
|
2811
2807
|
textarea: undefined,
|
|
2812
2808
|
internalValue: '',
|
|
2813
2809
|
};
|
|
2814
|
-
const updateHeight = (textarea) => {
|
|
2815
|
-
textarea
|
|
2816
|
-
|
|
2817
|
-
|
|
2810
|
+
const updateHeight = (textarea, hiddenDiv) => {
|
|
2811
|
+
if (!textarea || !hiddenDiv)
|
|
2812
|
+
return;
|
|
2813
|
+
// Copy font properties from textarea to hidden div
|
|
2814
|
+
const computedStyle = window.getComputedStyle(textarea);
|
|
2815
|
+
hiddenDiv.style.fontFamily = computedStyle.fontFamily;
|
|
2816
|
+
hiddenDiv.style.fontSize = computedStyle.fontSize;
|
|
2817
|
+
hiddenDiv.style.lineHeight = computedStyle.lineHeight;
|
|
2818
|
+
// Copy padding from textarea (important for accurate measurement)
|
|
2819
|
+
hiddenDiv.style.paddingTop = computedStyle.paddingTop;
|
|
2820
|
+
hiddenDiv.style.paddingRight = computedStyle.paddingRight;
|
|
2821
|
+
hiddenDiv.style.paddingBottom = computedStyle.paddingBottom;
|
|
2822
|
+
hiddenDiv.style.paddingLeft = computedStyle.paddingLeft;
|
|
2823
|
+
// Handle text wrapping
|
|
2824
|
+
if (textarea.getAttribute('wrap') === 'off') {
|
|
2825
|
+
hiddenDiv.style.overflowWrap = 'normal';
|
|
2826
|
+
hiddenDiv.style.whiteSpace = 'pre';
|
|
2827
|
+
}
|
|
2828
|
+
else {
|
|
2829
|
+
hiddenDiv.style.overflowWrap = 'break-word';
|
|
2830
|
+
hiddenDiv.style.whiteSpace = 'pre-wrap';
|
|
2831
|
+
}
|
|
2832
|
+
// Set content with extra newline for measurement
|
|
2833
|
+
hiddenDiv.textContent = textarea.value + '\n';
|
|
2834
|
+
const content = hiddenDiv.innerHTML.replace(/\n/g, '<br>');
|
|
2835
|
+
hiddenDiv.innerHTML = content;
|
|
2836
|
+
// Set width to match textarea
|
|
2837
|
+
if (textarea.offsetWidth > 0) {
|
|
2838
|
+
hiddenDiv.style.width = textarea.offsetWidth + 'px';
|
|
2839
|
+
}
|
|
2840
|
+
else {
|
|
2841
|
+
hiddenDiv.style.width = window.innerWidth / 2 + 'px';
|
|
2842
|
+
}
|
|
2843
|
+
// Get the original/natural height of the textarea
|
|
2844
|
+
const originalHeight = textarea.offsetHeight;
|
|
2845
|
+
const measuredHeight = hiddenDiv.offsetHeight;
|
|
2846
|
+
// Key logic: Only set custom height when content requires MORE space than original height
|
|
2847
|
+
// This matches the Materialize CSS reference behavior
|
|
2848
|
+
if (originalHeight <= measuredHeight) {
|
|
2849
|
+
state.height = measuredHeight + 'px';
|
|
2850
|
+
}
|
|
2851
|
+
else {
|
|
2852
|
+
// Single line content or content that fits in original height - let CSS handle it
|
|
2853
|
+
state.height = undefined;
|
|
2854
|
+
}
|
|
2818
2855
|
};
|
|
2819
|
-
const isControlled = (attrs) => attrs.value !== undefined && attrs.oninput !== undefined;
|
|
2856
|
+
const isControlled = (attrs) => attrs.value !== undefined && (attrs.oninput !== undefined || attrs.onchange !== undefined);
|
|
2820
2857
|
return {
|
|
2821
2858
|
oninit: ({ attrs }) => {
|
|
2859
|
+
const controlled = isControlled(attrs);
|
|
2860
|
+
const isNonInteractive = attrs.readonly || attrs.disabled;
|
|
2861
|
+
// Warn developer for improper controlled usage
|
|
2862
|
+
if (attrs.value !== undefined && !controlled && !isNonInteractive) {
|
|
2863
|
+
console.warn(`TextArea received 'value' prop without 'oninput' or 'onchange' handler. ` +
|
|
2864
|
+
`Use 'defaultValue' for uncontrolled components or add an event handler for controlled components.`);
|
|
2865
|
+
}
|
|
2822
2866
|
// Initialize internal value for uncontrolled mode
|
|
2823
|
-
if (!
|
|
2867
|
+
if (!controlled) {
|
|
2824
2868
|
state.internalValue = attrs.defaultValue || '';
|
|
2825
2869
|
}
|
|
2826
2870
|
},
|
|
2827
2871
|
onremove: () => {
|
|
2828
2872
|
},
|
|
2829
2873
|
view: ({ attrs }) => {
|
|
2874
|
+
var _a, _b, _c, _d;
|
|
2830
2875
|
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"]);
|
|
2831
2876
|
const controlled = isControlled(attrs);
|
|
2832
|
-
const
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
const
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
// Update character count
|
|
2863
|
-
if (maxLength) {
|
|
2864
|
-
state.currentLength = target.value.length;
|
|
2865
|
-
state.hasInteracted = target.value.length > 0;
|
|
2866
|
-
}
|
|
2867
|
-
// Update internal state for uncontrolled mode
|
|
2868
|
-
if (!controlled) {
|
|
2869
|
-
state.internalValue = target.value;
|
|
2870
|
-
}
|
|
2871
|
-
// Call oninput handler
|
|
2872
|
-
if (oninput) {
|
|
2873
|
-
oninput(target.value);
|
|
2874
|
-
}
|
|
2875
|
-
}, onblur: (e) => {
|
|
2876
|
-
state.active = false;
|
|
2877
|
-
// const target = e.target as HTMLTextAreaElement;
|
|
2878
|
-
state.hasInteracted = true;
|
|
2879
|
-
// Call original onblur if provided
|
|
2880
|
-
if (onblur) {
|
|
2881
|
-
onblur(e);
|
|
2882
|
-
}
|
|
2883
|
-
if (onchange && state.textarea) {
|
|
2884
|
-
onchange(state.textarea.value);
|
|
2885
|
-
}
|
|
2886
|
-
}, onkeyup: onkeyup
|
|
2887
|
-
? (ev) => {
|
|
2888
|
-
onkeyup(ev, ev.target.value);
|
|
2889
|
-
}
|
|
2890
|
-
: undefined, onkeydown: onkeydown
|
|
2891
|
-
? (ev) => {
|
|
2892
|
-
onkeydown(ev, ev.target.value);
|
|
2877
|
+
const isNonInteractive = attrs.readonly || attrs.disabled;
|
|
2878
|
+
let currentValue;
|
|
2879
|
+
if (controlled) {
|
|
2880
|
+
currentValue = value || '';
|
|
2881
|
+
}
|
|
2882
|
+
else if (isNonInteractive) {
|
|
2883
|
+
// Non-interactive components: prefer defaultValue, fallback to value
|
|
2884
|
+
currentValue = (_b = (_a = attrs.defaultValue) !== null && _a !== void 0 ? _a : value) !== null && _b !== void 0 ? _b : '';
|
|
2885
|
+
}
|
|
2886
|
+
else {
|
|
2887
|
+
// Interactive uncontrolled: use internal state
|
|
2888
|
+
currentValue = (_d = (_c = state.internalValue) !== null && _c !== void 0 ? _c : attrs.defaultValue) !== null && _d !== void 0 ? _d : '';
|
|
2889
|
+
}
|
|
2890
|
+
return [
|
|
2891
|
+
// Hidden div for height measurement - positioned outside the input-field
|
|
2892
|
+
m('.hiddendiv', {
|
|
2893
|
+
style: {
|
|
2894
|
+
visibility: 'hidden',
|
|
2895
|
+
position: 'absolute',
|
|
2896
|
+
top: '0',
|
|
2897
|
+
left: '0',
|
|
2898
|
+
zIndex: '-1',
|
|
2899
|
+
whiteSpace: 'pre-wrap',
|
|
2900
|
+
wordWrap: 'break-word',
|
|
2901
|
+
overflowWrap: 'break-word',
|
|
2902
|
+
},
|
|
2903
|
+
oncreate: ({ dom }) => {
|
|
2904
|
+
const hiddenDiv = dom;
|
|
2905
|
+
if (state.textarea) {
|
|
2906
|
+
updateHeight(state.textarea, hiddenDiv);
|
|
2893
2907
|
}
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2908
|
+
},
|
|
2909
|
+
onupdate: ({ dom }) => {
|
|
2910
|
+
const hiddenDiv = dom;
|
|
2911
|
+
if (state.textarea) {
|
|
2912
|
+
updateHeight(state.textarea, hiddenDiv);
|
|
2897
2913
|
}
|
|
2898
|
-
|
|
2899
|
-
m(Label, {
|
|
2900
|
-
label,
|
|
2901
|
-
id,
|
|
2902
|
-
isMandatory,
|
|
2903
|
-
isActive: currentValue || placeholder || state.active,
|
|
2904
|
-
initialValue: currentValue !== '',
|
|
2905
|
-
}),
|
|
2906
|
-
m(HelperText, {
|
|
2907
|
-
helperText,
|
|
2908
|
-
dataError: state.hasInteracted && attrs.dataError ? attrs.dataError : undefined,
|
|
2909
|
-
dataSuccess: state.hasInteracted && attrs.dataSuccess ? attrs.dataSuccess : undefined,
|
|
2914
|
+
},
|
|
2910
2915
|
}),
|
|
2911
|
-
|
|
2912
|
-
? m(
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2916
|
+
m('.input-field', { className, style }, [
|
|
2917
|
+
iconName ? m('i.material-icons.prefix', iconName) : '',
|
|
2918
|
+
m('textarea.materialize-textarea', Object.assign(Object.assign({}, params), { id, tabindex: 0, value: controlled ? currentValue : undefined, style: {
|
|
2919
|
+
height: state.height,
|
|
2920
|
+
}, oncreate: ({ dom }) => {
|
|
2921
|
+
const textarea = (state.textarea = dom);
|
|
2922
|
+
// For uncontrolled mode, set initial value only
|
|
2923
|
+
if (!controlled && attrs.defaultValue !== undefined) {
|
|
2924
|
+
textarea.value = String(attrs.defaultValue);
|
|
2925
|
+
}
|
|
2926
|
+
// Height will be calculated by hidden div
|
|
2927
|
+
// Update character count state for counter component
|
|
2928
|
+
if (maxLength) {
|
|
2929
|
+
state.currentLength = textarea.value.length;
|
|
2930
|
+
}
|
|
2931
|
+
}, onupdate: ({ dom }) => {
|
|
2932
|
+
const textarea = dom;
|
|
2933
|
+
if (state.height)
|
|
2934
|
+
textarea.style.height = state.height;
|
|
2935
|
+
// No need to manually sync in onupdate since value attribute handles it
|
|
2936
|
+
}, onfocus: () => {
|
|
2937
|
+
state.active = true;
|
|
2938
|
+
}, oninput: (e) => {
|
|
2939
|
+
state.active = true;
|
|
2940
|
+
state.hasInteracted = false;
|
|
2941
|
+
const target = e.target;
|
|
2942
|
+
// Height will be recalculated by hidden div on next update
|
|
2943
|
+
// Update character count
|
|
2944
|
+
if (maxLength) {
|
|
2945
|
+
state.currentLength = target.value.length;
|
|
2946
|
+
state.hasInteracted = target.value.length > 0;
|
|
2947
|
+
}
|
|
2948
|
+
// Update internal state for uncontrolled mode
|
|
2949
|
+
if (!controlled) {
|
|
2950
|
+
state.internalValue = target.value;
|
|
2951
|
+
}
|
|
2952
|
+
// Call oninput handler
|
|
2953
|
+
if (oninput) {
|
|
2954
|
+
oninput(target.value);
|
|
2955
|
+
}
|
|
2956
|
+
}, onblur: (e) => {
|
|
2957
|
+
state.active = false;
|
|
2958
|
+
// const target = e.target as HTMLTextAreaElement;
|
|
2959
|
+
state.hasInteracted = true;
|
|
2960
|
+
// Call original onblur if provided
|
|
2961
|
+
if (onblur) {
|
|
2962
|
+
onblur(e);
|
|
2963
|
+
}
|
|
2964
|
+
if (onchange && state.textarea) {
|
|
2965
|
+
onchange(state.textarea.value);
|
|
2966
|
+
}
|
|
2967
|
+
}, onkeyup: onkeyup
|
|
2968
|
+
? (ev) => {
|
|
2969
|
+
onkeyup(ev, ev.target.value);
|
|
2970
|
+
}
|
|
2971
|
+
: undefined, onkeydown: onkeydown
|
|
2972
|
+
? (ev) => {
|
|
2973
|
+
onkeydown(ev, ev.target.value);
|
|
2974
|
+
}
|
|
2975
|
+
: undefined, onkeypress: onkeypress
|
|
2976
|
+
? (ev) => {
|
|
2977
|
+
onkeypress(ev, ev.target.value);
|
|
2978
|
+
}
|
|
2979
|
+
: undefined })),
|
|
2980
|
+
m(Label, {
|
|
2981
|
+
label,
|
|
2982
|
+
id,
|
|
2983
|
+
isMandatory,
|
|
2984
|
+
isActive: currentValue || placeholder || state.active,
|
|
2985
|
+
initialValue: currentValue !== '',
|
|
2986
|
+
}),
|
|
2987
|
+
m(HelperText, {
|
|
2988
|
+
helperText,
|
|
2989
|
+
dataError: state.hasInteracted && attrs.dataError ? attrs.dataError : undefined,
|
|
2990
|
+
dataSuccess: state.hasInteracted && attrs.dataSuccess ? attrs.dataSuccess : undefined,
|
|
2991
|
+
}),
|
|
2992
|
+
maxLength
|
|
2993
|
+
? m(CharacterCounter, {
|
|
2994
|
+
currentLength: state.currentLength,
|
|
2995
|
+
maxLength,
|
|
2996
|
+
show: state.currentLength > 0,
|
|
2997
|
+
})
|
|
2998
|
+
: undefined,
|
|
2999
|
+
]),
|
|
3000
|
+
];
|
|
2919
3001
|
},
|
|
2920
3002
|
};
|
|
2921
3003
|
};
|
|
@@ -2935,7 +3017,8 @@ const InputField = (type, defaultClass = '') => () => {
|
|
|
2935
3017
|
isDragging: false,
|
|
2936
3018
|
activeThumb: null,
|
|
2937
3019
|
};
|
|
2938
|
-
const isControlled = (attrs) => 'value' in attrs && typeof attrs.value !== 'undefined' &&
|
|
3020
|
+
const isControlled = (attrs) => 'value' in attrs && typeof attrs.value !== 'undefined' &&
|
|
3021
|
+
(typeof attrs.oninput === 'function' || typeof attrs.onchange === 'function');
|
|
2939
3022
|
const getValue = (target) => {
|
|
2940
3023
|
const val = target.value;
|
|
2941
3024
|
return (val ? (type === 'number' || type === 'range' ? +val : val) : val);
|
|
@@ -2981,8 +3064,15 @@ const InputField = (type, defaultClass = '') => () => {
|
|
|
2981
3064
|
// Range slider rendering functions are now in separate module
|
|
2982
3065
|
return {
|
|
2983
3066
|
oninit: ({ attrs }) => {
|
|
3067
|
+
const controlled = isControlled(attrs);
|
|
3068
|
+
const isNonInteractive = attrs.readonly || attrs.disabled;
|
|
3069
|
+
// Warn developer for improper controlled usage
|
|
3070
|
+
if (attrs.value !== undefined && !controlled && !isNonInteractive) {
|
|
3071
|
+
console.warn(`${type} input received 'value' prop without 'oninput' or 'onchange' handler. ` +
|
|
3072
|
+
`Use 'defaultValue' for uncontrolled components or add an event handler for controlled components.`);
|
|
3073
|
+
}
|
|
2984
3074
|
// Initialize internal value if not in controlled mode
|
|
2985
|
-
if (!
|
|
3075
|
+
if (!controlled) {
|
|
2986
3076
|
const isNumeric = ['number', 'range'].includes(type);
|
|
2987
3077
|
if (attrs.defaultValue !== undefined) {
|
|
2988
3078
|
if (isNumeric) {
|
|
@@ -2998,7 +3088,7 @@ const InputField = (type, defaultClass = '') => () => {
|
|
|
2998
3088
|
}
|
|
2999
3089
|
},
|
|
3000
3090
|
view: ({ attrs }) => {
|
|
3001
|
-
var _a, _b;
|
|
3091
|
+
var _a, _b, _c, _d, _e, _f;
|
|
3002
3092
|
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"]);
|
|
3003
3093
|
// const attributes = toAttrs(params);
|
|
3004
3094
|
const cn = [newRow ? 'clear' : '', defaultClass, className].filter(Boolean).join(' ').trim() || undefined;
|
|
@@ -3018,7 +3108,19 @@ const InputField = (type, defaultClass = '') => () => {
|
|
|
3018
3108
|
}
|
|
3019
3109
|
const isNumeric = ['number', 'range'].includes(type);
|
|
3020
3110
|
const controlled = isControlled(attrs);
|
|
3021
|
-
const
|
|
3111
|
+
const isNonInteractive = attrs.readonly || attrs.disabled;
|
|
3112
|
+
let value;
|
|
3113
|
+
if (controlled) {
|
|
3114
|
+
value = attrs.value;
|
|
3115
|
+
}
|
|
3116
|
+
else if (isNonInteractive) {
|
|
3117
|
+
// Non-interactive components: prefer defaultValue, fallback to value
|
|
3118
|
+
value = ((_c = (_b = attrs.defaultValue) !== null && _b !== void 0 ? _b : attrs.value) !== null && _c !== void 0 ? _c : (isNumeric ? 0 : ''));
|
|
3119
|
+
}
|
|
3120
|
+
else {
|
|
3121
|
+
// Interactive uncontrolled: use internal state
|
|
3122
|
+
value = ((_e = (_d = state.internalValue) !== null && _d !== void 0 ? _d : attrs.defaultValue) !== null && _e !== void 0 ? _e : (isNumeric ? 0 : ''));
|
|
3123
|
+
}
|
|
3022
3124
|
const rangeType = type === 'range' && !attrs.minmax;
|
|
3023
3125
|
return m('.input-field', { className: cn, style }, [
|
|
3024
3126
|
iconName ? m('i.material-icons.prefix', iconName) : undefined,
|
|
@@ -3087,7 +3189,9 @@ const InputField = (type, defaultClass = '') => () => {
|
|
|
3087
3189
|
state.isValid = true;
|
|
3088
3190
|
}
|
|
3089
3191
|
}
|
|
3090
|
-
else if ((type === 'email' || type === 'url') &&
|
|
3192
|
+
else if ((type === 'email' || type === 'url') &&
|
|
3193
|
+
target.classList.contains('invalid') &&
|
|
3194
|
+
target.value.length > 0) {
|
|
3091
3195
|
// Clear native validation errors if user is typing and input becomes valid
|
|
3092
3196
|
if (target.validity.valid) {
|
|
3093
3197
|
target.classList.remove('invalid');
|
|
@@ -3182,7 +3286,7 @@ const InputField = (type, defaultClass = '') => () => {
|
|
|
3182
3286
|
}
|
|
3183
3287
|
} })),
|
|
3184
3288
|
// Clear button - only for text inputs with canClear enabled and has content
|
|
3185
|
-
canClear && type === 'text' && ((
|
|
3289
|
+
canClear && type === 'text' && ((_f = state.inputElement) === null || _f === void 0 ? void 0 : _f.value)
|
|
3186
3290
|
? m(MaterialIcon, {
|
|
3187
3291
|
name: 'close',
|
|
3188
3292
|
className: 'input-clear-btn',
|
|
@@ -5579,17 +5683,35 @@ const RadioButtons = () => {
|
|
|
5579
5683
|
return {
|
|
5580
5684
|
oninit: ({ attrs }) => {
|
|
5581
5685
|
state.componentId = attrs.id || uniqueId();
|
|
5686
|
+
const controlled = isControlled(attrs);
|
|
5687
|
+
// Warn developer for improper controlled usage
|
|
5688
|
+
if (attrs.checkedId !== undefined && !controlled && !attrs.disabled) {
|
|
5689
|
+
console.warn(`RadioButtons component received 'checkedId' prop without 'onchange' handler. ` +
|
|
5690
|
+
`Use 'defaultCheckedId' for uncontrolled components or add 'onchange' for controlled components.`);
|
|
5691
|
+
}
|
|
5582
5692
|
// Initialize internal state for uncontrolled mode
|
|
5583
|
-
if (!
|
|
5693
|
+
if (!controlled) {
|
|
5584
5694
|
state.internalCheckedId = attrs.defaultCheckedId;
|
|
5585
5695
|
}
|
|
5586
5696
|
},
|
|
5587
5697
|
view: ({ attrs }) => {
|
|
5698
|
+
var _a, _b;
|
|
5588
5699
|
const { checkedId, newRow, className = 'col s12', label = '', disabled, description, options, isMandatory, checkboxClass, layout = 'vertical', onchange, } = attrs;
|
|
5589
5700
|
const { groupId, componentId } = state;
|
|
5590
5701
|
const controlled = isControlled(attrs);
|
|
5591
5702
|
// Get current checked ID from props or internal state
|
|
5592
|
-
|
|
5703
|
+
let currentCheckedId;
|
|
5704
|
+
if (controlled) {
|
|
5705
|
+
currentCheckedId = checkedId;
|
|
5706
|
+
}
|
|
5707
|
+
else if (disabled) {
|
|
5708
|
+
// Non-interactive components: prefer defaultCheckedId, fallback to checkedId
|
|
5709
|
+
currentCheckedId = (_a = attrs.defaultCheckedId) !== null && _a !== void 0 ? _a : checkedId;
|
|
5710
|
+
}
|
|
5711
|
+
else {
|
|
5712
|
+
// Interactive uncontrolled: use internal state
|
|
5713
|
+
currentCheckedId = (_b = state.internalCheckedId) !== null && _b !== void 0 ? _b : attrs.defaultCheckedId;
|
|
5714
|
+
}
|
|
5593
5715
|
const handleChange = (id) => {
|
|
5594
5716
|
// Update internal state for uncontrolled mode
|
|
5595
5717
|
if (!controlled) {
|
|
@@ -5719,8 +5841,14 @@ const Select = () => {
|
|
|
5719
5841
|
return {
|
|
5720
5842
|
oninit: ({ attrs }) => {
|
|
5721
5843
|
state.id = attrs.id || uniqueId();
|
|
5844
|
+
const controlled = isControlled(attrs);
|
|
5845
|
+
// Warn developer for improper controlled usage
|
|
5846
|
+
if (attrs.checkedId !== undefined && !controlled && !attrs.disabled) {
|
|
5847
|
+
console.warn(`Select component received 'checkedId' prop without 'onchange' handler. ` +
|
|
5848
|
+
`Use 'defaultCheckedId' for uncontrolled components or add 'onchange' for controlled components.`);
|
|
5849
|
+
}
|
|
5722
5850
|
// Initialize internal state for uncontrolled mode
|
|
5723
|
-
if (!
|
|
5851
|
+
if (!controlled) {
|
|
5724
5852
|
const defaultIds = attrs.defaultCheckedId !== undefined
|
|
5725
5853
|
? Array.isArray(attrs.defaultCheckedId)
|
|
5726
5854
|
? attrs.defaultCheckedId
|
|
@@ -5736,16 +5864,32 @@ const Select = () => {
|
|
|
5736
5864
|
document.removeEventListener('click', closeDropdown);
|
|
5737
5865
|
},
|
|
5738
5866
|
view: ({ attrs }) => {
|
|
5867
|
+
var _a;
|
|
5739
5868
|
const controlled = isControlled(attrs);
|
|
5869
|
+
const { disabled } = attrs;
|
|
5740
5870
|
// Get selected IDs from props or internal state
|
|
5741
|
-
|
|
5742
|
-
|
|
5871
|
+
let selectedIds;
|
|
5872
|
+
if (controlled) {
|
|
5873
|
+
selectedIds = attrs.checkedId !== undefined
|
|
5743
5874
|
? Array.isArray(attrs.checkedId)
|
|
5744
5875
|
? attrs.checkedId
|
|
5745
5876
|
: [attrs.checkedId]
|
|
5746
|
-
: []
|
|
5747
|
-
|
|
5748
|
-
|
|
5877
|
+
: [];
|
|
5878
|
+
}
|
|
5879
|
+
else if (disabled) {
|
|
5880
|
+
// Non-interactive components: prefer defaultCheckedId, fallback to checkedId
|
|
5881
|
+
const fallbackId = (_a = attrs.defaultCheckedId) !== null && _a !== void 0 ? _a : attrs.checkedId;
|
|
5882
|
+
selectedIds = fallbackId !== undefined
|
|
5883
|
+
? Array.isArray(fallbackId)
|
|
5884
|
+
? fallbackId
|
|
5885
|
+
: [fallbackId]
|
|
5886
|
+
: [];
|
|
5887
|
+
}
|
|
5888
|
+
else {
|
|
5889
|
+
// Interactive uncontrolled: use internal state
|
|
5890
|
+
selectedIds = state.internalSelectedIds;
|
|
5891
|
+
}
|
|
5892
|
+
const { newRow, className = 'col s12', key, options, multiple = false, label, helperText, placeholder = '', isMandatory, iconName, style, } = attrs;
|
|
5749
5893
|
const finalClassName = newRow ? `${className} clear` : className;
|
|
5750
5894
|
const selectedOptions = options.filter((opt) => isSelected(opt.id, selectedIds));
|
|
5751
5895
|
return m('.input-field.select-space', {
|
|
@@ -8482,6 +8626,269 @@ const ImageList = () => {
|
|
|
8482
8626
|
};
|
|
8483
8627
|
};
|
|
8484
8628
|
|
|
8629
|
+
/** Default star icons */
|
|
8630
|
+
const DEFAULT_ICONS = {
|
|
8631
|
+
filled: '★',
|
|
8632
|
+
empty: '☆',
|
|
8633
|
+
half: '☆', // We'll handle half-fill with CSS
|
|
8634
|
+
};
|
|
8635
|
+
/** Create a Rating component */
|
|
8636
|
+
const Rating = () => {
|
|
8637
|
+
const state = {
|
|
8638
|
+
id: uniqueId(),
|
|
8639
|
+
internalValue: 0,
|
|
8640
|
+
hoverValue: null,
|
|
8641
|
+
isHovering: false,
|
|
8642
|
+
isFocused: false,
|
|
8643
|
+
};
|
|
8644
|
+
const isControlled = (attrs) => typeof attrs.value !== 'undefined' && typeof attrs.onchange === 'function';
|
|
8645
|
+
const getCurrentValue = (attrs) => {
|
|
8646
|
+
var _a, _b, _c, _d;
|
|
8647
|
+
const controlled = isControlled(attrs);
|
|
8648
|
+
const isNonInteractive = attrs.readonly || attrs.disabled;
|
|
8649
|
+
if (controlled) {
|
|
8650
|
+
return attrs.value || 0;
|
|
8651
|
+
}
|
|
8652
|
+
// Non-interactive components: prefer defaultValue, fallback to value
|
|
8653
|
+
if (isNonInteractive) {
|
|
8654
|
+
return (_b = (_a = attrs.defaultValue) !== null && _a !== void 0 ? _a : attrs.value) !== null && _b !== void 0 ? _b : 0;
|
|
8655
|
+
}
|
|
8656
|
+
// Interactive uncontrolled: use internal state (user can change it)
|
|
8657
|
+
return (_d = (_c = state.internalValue) !== null && _c !== void 0 ? _c : attrs.defaultValue) !== null && _d !== void 0 ? _d : 0;
|
|
8658
|
+
};
|
|
8659
|
+
const getDisplayValue = (attrs) => state.isHovering && state.hoverValue !== null ? state.hoverValue : getCurrentValue(attrs);
|
|
8660
|
+
const getLabelText = (value, max, getLabelFn) => {
|
|
8661
|
+
if (getLabelFn) {
|
|
8662
|
+
return getLabelFn(value, max);
|
|
8663
|
+
}
|
|
8664
|
+
if (value === 0) {
|
|
8665
|
+
return `No rating`;
|
|
8666
|
+
}
|
|
8667
|
+
if (value === 1) {
|
|
8668
|
+
return `1 star out of ${max}`;
|
|
8669
|
+
}
|
|
8670
|
+
return `${value} stars out of ${max}`;
|
|
8671
|
+
};
|
|
8672
|
+
const getSizeClass = (size = 'medium') => {
|
|
8673
|
+
switch (size) {
|
|
8674
|
+
case 'small':
|
|
8675
|
+
return 'rating--small';
|
|
8676
|
+
case 'large':
|
|
8677
|
+
return 'rating--large';
|
|
8678
|
+
default:
|
|
8679
|
+
return 'rating--medium';
|
|
8680
|
+
}
|
|
8681
|
+
};
|
|
8682
|
+
const getDensityClass = (density = 'standard') => {
|
|
8683
|
+
switch (density) {
|
|
8684
|
+
case 'compact':
|
|
8685
|
+
return 'rating--compact';
|
|
8686
|
+
case 'comfortable':
|
|
8687
|
+
return 'rating--comfortable';
|
|
8688
|
+
default:
|
|
8689
|
+
return 'rating--standard';
|
|
8690
|
+
}
|
|
8691
|
+
};
|
|
8692
|
+
const handleItemClick = (attrs, clickValue) => {
|
|
8693
|
+
var _a;
|
|
8694
|
+
if (attrs.readonly || attrs.disabled)
|
|
8695
|
+
return;
|
|
8696
|
+
const currentValue = getCurrentValue(attrs);
|
|
8697
|
+
const newValue = attrs.clearable && currentValue === clickValue ? 0 : clickValue;
|
|
8698
|
+
if (!isControlled(attrs)) {
|
|
8699
|
+
state.internalValue = newValue;
|
|
8700
|
+
}
|
|
8701
|
+
(_a = attrs.onchange) === null || _a === void 0 ? void 0 : _a.call(attrs, newValue);
|
|
8702
|
+
};
|
|
8703
|
+
const handleItemHover = (attrs, hoverValue) => {
|
|
8704
|
+
var _a;
|
|
8705
|
+
if (attrs.readonly || attrs.disabled)
|
|
8706
|
+
return;
|
|
8707
|
+
state.hoverValue = hoverValue;
|
|
8708
|
+
state.isHovering = true;
|
|
8709
|
+
(_a = attrs.onmouseover) === null || _a === void 0 ? void 0 : _a.call(attrs, hoverValue);
|
|
8710
|
+
};
|
|
8711
|
+
const handleMouseLeave = (attrs) => {
|
|
8712
|
+
if (attrs.readonly || attrs.disabled)
|
|
8713
|
+
return;
|
|
8714
|
+
state.isHovering = false;
|
|
8715
|
+
state.hoverValue = null;
|
|
8716
|
+
};
|
|
8717
|
+
const handleKeyDown = (attrs, e) => {
|
|
8718
|
+
var _a;
|
|
8719
|
+
if (attrs.readonly || attrs.disabled)
|
|
8720
|
+
return;
|
|
8721
|
+
const max = attrs.max || 5;
|
|
8722
|
+
const step = attrs.step || 1;
|
|
8723
|
+
const currentValue = getCurrentValue(attrs);
|
|
8724
|
+
let newValue = currentValue;
|
|
8725
|
+
switch (e.key) {
|
|
8726
|
+
case 'ArrowRight':
|
|
8727
|
+
case 'ArrowUp':
|
|
8728
|
+
e.preventDefault();
|
|
8729
|
+
newValue = Math.min(max, currentValue + step);
|
|
8730
|
+
break;
|
|
8731
|
+
case 'ArrowLeft':
|
|
8732
|
+
case 'ArrowDown':
|
|
8733
|
+
e.preventDefault();
|
|
8734
|
+
newValue = Math.max(0, currentValue - step);
|
|
8735
|
+
break;
|
|
8736
|
+
case 'Home':
|
|
8737
|
+
e.preventDefault();
|
|
8738
|
+
newValue = attrs.clearable ? 0 : step;
|
|
8739
|
+
break;
|
|
8740
|
+
case 'End':
|
|
8741
|
+
e.preventDefault();
|
|
8742
|
+
newValue = max;
|
|
8743
|
+
break;
|
|
8744
|
+
case ' ':
|
|
8745
|
+
case 'Enter':
|
|
8746
|
+
e.preventDefault();
|
|
8747
|
+
// If focused and not hovering, increment by step
|
|
8748
|
+
if (!state.isHovering) {
|
|
8749
|
+
newValue = currentValue + step > max ? (attrs.clearable ? 0 : step) : currentValue + step;
|
|
8750
|
+
}
|
|
8751
|
+
break;
|
|
8752
|
+
case 'Escape':
|
|
8753
|
+
if (attrs.clearable) {
|
|
8754
|
+
e.preventDefault();
|
|
8755
|
+
newValue = 0;
|
|
8756
|
+
}
|
|
8757
|
+
break;
|
|
8758
|
+
default:
|
|
8759
|
+
return;
|
|
8760
|
+
}
|
|
8761
|
+
if (newValue !== currentValue) {
|
|
8762
|
+
if (!isControlled(attrs)) {
|
|
8763
|
+
state.internalValue = newValue;
|
|
8764
|
+
}
|
|
8765
|
+
(_a = attrs.onchange) === null || _a === void 0 ? void 0 : _a.call(attrs, newValue);
|
|
8766
|
+
}
|
|
8767
|
+
};
|
|
8768
|
+
const RatingItem = () => {
|
|
8769
|
+
return {
|
|
8770
|
+
view: ({ attrs }) => {
|
|
8771
|
+
const { index, displayValue, step, icons, allowHalfSteps, disabled, onclick, onmouseover } = attrs;
|
|
8772
|
+
const itemValue = (index + 1) * step;
|
|
8773
|
+
// Calculate fill state based on displayValue vs itemValue
|
|
8774
|
+
const diff = displayValue - itemValue;
|
|
8775
|
+
const fillState = diff >= 0 ? 'full' : allowHalfSteps && diff >= -step / 2 ? 'half' : 'empty';
|
|
8776
|
+
return m('.rating__item', {
|
|
8777
|
+
className: [
|
|
8778
|
+
fillState === 'full' ? 'rating__item--filled' : '',
|
|
8779
|
+
fillState === 'half' ? 'rating__item--half' : '',
|
|
8780
|
+
fillState !== 'empty' ? 'rating__item--active' : '',
|
|
8781
|
+
disabled ? 'rating__item--disabled' : '',
|
|
8782
|
+
]
|
|
8783
|
+
.filter(Boolean)
|
|
8784
|
+
.join(' '),
|
|
8785
|
+
onclick,
|
|
8786
|
+
onmouseover,
|
|
8787
|
+
}, [
|
|
8788
|
+
// Empty icon (background)
|
|
8789
|
+
m('.rating__icon.rating__icon--empty', { 'aria-hidden': 'true' }, typeof icons.empty === 'string' ? icons.empty : m(icons.empty)),
|
|
8790
|
+
// Filled icon (foreground)
|
|
8791
|
+
m('.rating__icon.rating__icon--filled', {
|
|
8792
|
+
'aria-hidden': 'true',
|
|
8793
|
+
style: {
|
|
8794
|
+
clipPath: fillState === 'half' ? 'inset(0 50% 0 0)' : undefined,
|
|
8795
|
+
},
|
|
8796
|
+
}, typeof icons.filled === 'string' ? icons.filled : m(icons.filled)),
|
|
8797
|
+
]);
|
|
8798
|
+
},
|
|
8799
|
+
};
|
|
8800
|
+
};
|
|
8801
|
+
return {
|
|
8802
|
+
oninit: ({ attrs }) => {
|
|
8803
|
+
const controlled = isControlled(attrs);
|
|
8804
|
+
const isNonInteractive = attrs.readonly || attrs.disabled;
|
|
8805
|
+
// Warn developer for improper controlled usage
|
|
8806
|
+
if (attrs.value !== undefined && !controlled && !isNonInteractive) {
|
|
8807
|
+
console.warn(`Rating component received 'value' prop without 'onchange' handler. ` +
|
|
8808
|
+
`Use 'defaultValue' for uncontrolled components or add 'onchange' for controlled components.`);
|
|
8809
|
+
}
|
|
8810
|
+
if (!controlled) {
|
|
8811
|
+
state.internalValue = attrs.defaultValue || 0;
|
|
8812
|
+
}
|
|
8813
|
+
},
|
|
8814
|
+
view: ({ attrs }) => {
|
|
8815
|
+
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"]);
|
|
8816
|
+
const currentValue = getCurrentValue(attrs);
|
|
8817
|
+
const displayValue = getDisplayValue(attrs);
|
|
8818
|
+
const itemCount = Math.ceil(max / step);
|
|
8819
|
+
return m('.rating', {
|
|
8820
|
+
className: [
|
|
8821
|
+
'rating',
|
|
8822
|
+
getSizeClass(size),
|
|
8823
|
+
getDensityClass(density),
|
|
8824
|
+
readonly ? 'rating--read-only' : '',
|
|
8825
|
+
disabled ? 'rating--disabled' : '',
|
|
8826
|
+
state.isFocused ? 'rating--focused' : '',
|
|
8827
|
+
className,
|
|
8828
|
+
]
|
|
8829
|
+
.filter(Boolean)
|
|
8830
|
+
.join(' '),
|
|
8831
|
+
style,
|
|
8832
|
+
id,
|
|
8833
|
+
role: readonly ? 'img' : 'slider',
|
|
8834
|
+
tabindex: readonly || disabled ? -1 : 0,
|
|
8835
|
+
'aria-valuemin': 0,
|
|
8836
|
+
'aria-valuemax': max,
|
|
8837
|
+
'aria-valuenow': currentValue,
|
|
8838
|
+
'aria-valuetext': getLabelText(currentValue, max, attrs.getLabelText),
|
|
8839
|
+
'aria-label': ariaAttrs['aria-label'] ||
|
|
8840
|
+
attrs.ariaLabel ||
|
|
8841
|
+
`Rating: ${getLabelText(currentValue, max, attrs.getLabelText)}`,
|
|
8842
|
+
'aria-labelledby': ariaAttrs['aria-labelledby'],
|
|
8843
|
+
'aria-readonly': readonly,
|
|
8844
|
+
'aria-disabled': disabled,
|
|
8845
|
+
onkeydown: (e) => handleKeyDown(attrs, e),
|
|
8846
|
+
onfocus: () => {
|
|
8847
|
+
state.isFocused = true;
|
|
8848
|
+
},
|
|
8849
|
+
onblur: () => {
|
|
8850
|
+
state.isFocused = false;
|
|
8851
|
+
handleMouseLeave(attrs);
|
|
8852
|
+
},
|
|
8853
|
+
onmouseleave: () => handleMouseLeave(attrs),
|
|
8854
|
+
}, [
|
|
8855
|
+
// Hidden input for form submission
|
|
8856
|
+
name &&
|
|
8857
|
+
m('input', {
|
|
8858
|
+
type: 'hidden',
|
|
8859
|
+
name,
|
|
8860
|
+
value: currentValue,
|
|
8861
|
+
}),
|
|
8862
|
+
// Rating items
|
|
8863
|
+
m('.rating__items', {
|
|
8864
|
+
className: 'rating__items',
|
|
8865
|
+
},
|
|
8866
|
+
// Array.from({ length: itemCount }, (_, i) => renderRatingItem(attrs, i))
|
|
8867
|
+
[...Array(itemCount)].map((_, i) => {
|
|
8868
|
+
const itemValue = (i + 1) * step;
|
|
8869
|
+
return m(RatingItem, {
|
|
8870
|
+
key: `rating-item-${i}`,
|
|
8871
|
+
index: i,
|
|
8872
|
+
displayValue: displayValue,
|
|
8873
|
+
step,
|
|
8874
|
+
icons: Object.assign(Object.assign({}, DEFAULT_ICONS), attrs.icon),
|
|
8875
|
+
allowHalfSteps: attrs.allowHalfSteps,
|
|
8876
|
+
disabled: attrs.disabled,
|
|
8877
|
+
onclick: () => handleItemClick(attrs, itemValue),
|
|
8878
|
+
onmouseover: () => handleItemHover(attrs, itemValue),
|
|
8879
|
+
});
|
|
8880
|
+
})),
|
|
8881
|
+
// Screen reader text
|
|
8882
|
+
m('.rating__sr-only', {
|
|
8883
|
+
className: 'rating__sr-only',
|
|
8884
|
+
'aria-live': 'polite',
|
|
8885
|
+
'aria-atomic': 'true',
|
|
8886
|
+
}, getLabelText(displayValue, max, attrs.getLabelText)),
|
|
8887
|
+
]);
|
|
8888
|
+
},
|
|
8889
|
+
};
|
|
8890
|
+
};
|
|
8891
|
+
|
|
8485
8892
|
/**
|
|
8486
8893
|
* @fileoverview Core TypeScript utility types for mithril-materialized library
|
|
8487
8894
|
* These types improve type safety and developer experience across all components
|
|
@@ -8550,6 +8957,7 @@ exports.PushpinComponent = PushpinComponent;
|
|
|
8550
8957
|
exports.RadioButton = RadioButton;
|
|
8551
8958
|
exports.RadioButtons = RadioButtons;
|
|
8552
8959
|
exports.RangeInput = RangeInput;
|
|
8960
|
+
exports.Rating = Rating;
|
|
8553
8961
|
exports.RoundIconButton = RoundIconButton;
|
|
8554
8962
|
exports.SearchSelect = SearchSelect;
|
|
8555
8963
|
exports.SecondaryContent = SecondaryContent;
|