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