mithril-materialized 3.2.0 → 3.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core.css +3 -0
- package/dist/forms.css +3 -0
- package/dist/index.css +3 -0
- package/dist/index.esm.js +177 -107
- package/dist/index.js +177 -107
- package/dist/index.min.css +1 -1
- package/dist/index.umd.js +177 -107
- package/package.json +5 -5
- package/sass/components/forms/_input-fields.scss +3 -0
package/dist/index.umd.js
CHANGED
|
@@ -2793,10 +2793,6 @@
|
|
|
2793
2793
|
return m('span.character-counter', {
|
|
2794
2794
|
style: {
|
|
2795
2795
|
color: isOverLimit ? '#F44336' : '#9e9e9e',
|
|
2796
|
-
fontSize: '12px',
|
|
2797
|
-
display: 'block',
|
|
2798
|
-
textAlign: 'right',
|
|
2799
|
-
marginTop: '8px',
|
|
2800
2796
|
},
|
|
2801
2797
|
}, `${currentLength}/${maxLength}`);
|
|
2802
2798
|
},
|
|
@@ -2813,10 +2809,51 @@
|
|
|
2813
2809
|
textarea: undefined,
|
|
2814
2810
|
internalValue: '',
|
|
2815
2811
|
};
|
|
2816
|
-
const updateHeight = (textarea) => {
|
|
2817
|
-
textarea
|
|
2818
|
-
|
|
2819
|
-
|
|
2812
|
+
const updateHeight = (textarea, hiddenDiv) => {
|
|
2813
|
+
if (!textarea || !hiddenDiv)
|
|
2814
|
+
return;
|
|
2815
|
+
// Copy font properties from textarea to hidden div
|
|
2816
|
+
const computedStyle = window.getComputedStyle(textarea);
|
|
2817
|
+
hiddenDiv.style.fontFamily = computedStyle.fontFamily;
|
|
2818
|
+
hiddenDiv.style.fontSize = computedStyle.fontSize;
|
|
2819
|
+
hiddenDiv.style.lineHeight = computedStyle.lineHeight;
|
|
2820
|
+
// Copy padding from textarea (important for accurate measurement)
|
|
2821
|
+
hiddenDiv.style.paddingTop = computedStyle.paddingTop;
|
|
2822
|
+
hiddenDiv.style.paddingRight = computedStyle.paddingRight;
|
|
2823
|
+
hiddenDiv.style.paddingBottom = computedStyle.paddingBottom;
|
|
2824
|
+
hiddenDiv.style.paddingLeft = computedStyle.paddingLeft;
|
|
2825
|
+
// Handle text wrapping
|
|
2826
|
+
if (textarea.getAttribute('wrap') === 'off') {
|
|
2827
|
+
hiddenDiv.style.overflowWrap = 'normal';
|
|
2828
|
+
hiddenDiv.style.whiteSpace = 'pre';
|
|
2829
|
+
}
|
|
2830
|
+
else {
|
|
2831
|
+
hiddenDiv.style.overflowWrap = 'break-word';
|
|
2832
|
+
hiddenDiv.style.whiteSpace = 'pre-wrap';
|
|
2833
|
+
}
|
|
2834
|
+
// Set content with extra newline for measurement
|
|
2835
|
+
hiddenDiv.textContent = textarea.value + '\n';
|
|
2836
|
+
const content = hiddenDiv.innerHTML.replace(/\n/g, '<br>');
|
|
2837
|
+
hiddenDiv.innerHTML = content;
|
|
2838
|
+
// Set width to match textarea
|
|
2839
|
+
if (textarea.offsetWidth > 0) {
|
|
2840
|
+
hiddenDiv.style.width = textarea.offsetWidth + 'px';
|
|
2841
|
+
}
|
|
2842
|
+
else {
|
|
2843
|
+
hiddenDiv.style.width = window.innerWidth / 2 + 'px';
|
|
2844
|
+
}
|
|
2845
|
+
// Get the original/natural height of the textarea
|
|
2846
|
+
const originalHeight = textarea.offsetHeight;
|
|
2847
|
+
const measuredHeight = hiddenDiv.offsetHeight;
|
|
2848
|
+
// Key logic: Only set custom height when content requires MORE space than original height
|
|
2849
|
+
// This matches the Materialize CSS reference behavior
|
|
2850
|
+
if (originalHeight <= measuredHeight) {
|
|
2851
|
+
state.height = measuredHeight + 'px';
|
|
2852
|
+
}
|
|
2853
|
+
else {
|
|
2854
|
+
// Single line content or content that fits in original height - let CSS handle it
|
|
2855
|
+
state.height = undefined;
|
|
2856
|
+
}
|
|
2820
2857
|
};
|
|
2821
2858
|
const isControlled = (attrs) => attrs.value !== undefined && attrs.oninput !== undefined;
|
|
2822
2859
|
return {
|
|
@@ -2832,92 +2869,117 @@
|
|
|
2832
2869
|
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"]);
|
|
2833
2870
|
const controlled = isControlled(attrs);
|
|
2834
2871
|
const currentValue = controlled ? value || '' : state.internalValue;
|
|
2835
|
-
return
|
|
2836
|
-
|
|
2837
|
-
m('
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
const textarea = dom;
|
|
2853
|
-
if (state.height)
|
|
2854
|
-
textarea.style.height = state.height;
|
|
2855
|
-
// No need to manually sync in onupdate since value attribute handles it
|
|
2856
|
-
}, onfocus: () => {
|
|
2857
|
-
state.active = true;
|
|
2858
|
-
}, oninput: (e) => {
|
|
2859
|
-
state.active = true;
|
|
2860
|
-
state.hasInteracted = false;
|
|
2861
|
-
const target = e.target;
|
|
2862
|
-
// Update height for auto-resize
|
|
2863
|
-
updateHeight(target);
|
|
2864
|
-
// Update character count
|
|
2865
|
-
if (maxLength) {
|
|
2866
|
-
state.currentLength = target.value.length;
|
|
2867
|
-
state.hasInteracted = target.value.length > 0;
|
|
2868
|
-
}
|
|
2869
|
-
// Update internal state for uncontrolled mode
|
|
2870
|
-
if (!controlled) {
|
|
2871
|
-
state.internalValue = target.value;
|
|
2872
|
-
}
|
|
2873
|
-
// Call oninput handler
|
|
2874
|
-
if (oninput) {
|
|
2875
|
-
oninput(target.value);
|
|
2876
|
-
}
|
|
2877
|
-
}, onblur: (e) => {
|
|
2878
|
-
state.active = false;
|
|
2879
|
-
// const target = e.target as HTMLTextAreaElement;
|
|
2880
|
-
state.hasInteracted = true;
|
|
2881
|
-
// Call original onblur if provided
|
|
2882
|
-
if (onblur) {
|
|
2883
|
-
onblur(e);
|
|
2884
|
-
}
|
|
2885
|
-
if (onchange && state.textarea) {
|
|
2886
|
-
onchange(state.textarea.value);
|
|
2887
|
-
}
|
|
2888
|
-
}, onkeyup: onkeyup
|
|
2889
|
-
? (ev) => {
|
|
2890
|
-
onkeyup(ev, ev.target.value);
|
|
2891
|
-
}
|
|
2892
|
-
: undefined, onkeydown: onkeydown
|
|
2893
|
-
? (ev) => {
|
|
2894
|
-
onkeydown(ev, ev.target.value);
|
|
2872
|
+
return [
|
|
2873
|
+
// Hidden div for height measurement - positioned outside the input-field
|
|
2874
|
+
m('.hiddendiv', {
|
|
2875
|
+
style: {
|
|
2876
|
+
visibility: 'hidden',
|
|
2877
|
+
position: 'absolute',
|
|
2878
|
+
top: '0',
|
|
2879
|
+
left: '0',
|
|
2880
|
+
zIndex: '-1',
|
|
2881
|
+
whiteSpace: 'pre-wrap',
|
|
2882
|
+
wordWrap: 'break-word',
|
|
2883
|
+
overflowWrap: 'break-word',
|
|
2884
|
+
},
|
|
2885
|
+
oncreate: ({ dom }) => {
|
|
2886
|
+
const hiddenDiv = dom;
|
|
2887
|
+
if (state.textarea) {
|
|
2888
|
+
updateHeight(state.textarea, hiddenDiv);
|
|
2895
2889
|
}
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2890
|
+
},
|
|
2891
|
+
onupdate: ({ dom }) => {
|
|
2892
|
+
const hiddenDiv = dom;
|
|
2893
|
+
if (state.textarea) {
|
|
2894
|
+
updateHeight(state.textarea, hiddenDiv);
|
|
2899
2895
|
}
|
|
2900
|
-
|
|
2901
|
-
m(Label, {
|
|
2902
|
-
label,
|
|
2903
|
-
id,
|
|
2904
|
-
isMandatory,
|
|
2905
|
-
isActive: currentValue || placeholder || state.active,
|
|
2906
|
-
initialValue: currentValue !== '',
|
|
2907
|
-
}),
|
|
2908
|
-
m(HelperText, {
|
|
2909
|
-
helperText,
|
|
2910
|
-
dataError: state.hasInteracted && attrs.dataError ? attrs.dataError : undefined,
|
|
2911
|
-
dataSuccess: state.hasInteracted && attrs.dataSuccess ? attrs.dataSuccess : undefined,
|
|
2896
|
+
},
|
|
2912
2897
|
}),
|
|
2913
|
-
|
|
2914
|
-
? m(
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2898
|
+
m('.input-field', { className, style }, [
|
|
2899
|
+
iconName ? m('i.material-icons.prefix', iconName) : '',
|
|
2900
|
+
m('textarea.materialize-textarea', Object.assign(Object.assign({}, params), { id, tabindex: 0, value: controlled ? currentValue : undefined, style: {
|
|
2901
|
+
height: state.height,
|
|
2902
|
+
}, oncreate: ({ dom }) => {
|
|
2903
|
+
const textarea = (state.textarea = dom);
|
|
2904
|
+
// For uncontrolled mode, set initial value only
|
|
2905
|
+
if (!controlled && attrs.defaultValue !== undefined) {
|
|
2906
|
+
textarea.value = String(attrs.defaultValue);
|
|
2907
|
+
}
|
|
2908
|
+
// Height will be calculated by hidden div
|
|
2909
|
+
// Update character count state for counter component
|
|
2910
|
+
if (maxLength) {
|
|
2911
|
+
state.currentLength = textarea.value.length;
|
|
2912
|
+
}
|
|
2913
|
+
}, onupdate: ({ dom }) => {
|
|
2914
|
+
const textarea = dom;
|
|
2915
|
+
if (state.height)
|
|
2916
|
+
textarea.style.height = state.height;
|
|
2917
|
+
// No need to manually sync in onupdate since value attribute handles it
|
|
2918
|
+
}, onfocus: () => {
|
|
2919
|
+
state.active = true;
|
|
2920
|
+
}, oninput: (e) => {
|
|
2921
|
+
state.active = true;
|
|
2922
|
+
state.hasInteracted = false;
|
|
2923
|
+
const target = e.target;
|
|
2924
|
+
// Height will be recalculated by hidden div on next update
|
|
2925
|
+
// Update character count
|
|
2926
|
+
if (maxLength) {
|
|
2927
|
+
state.currentLength = target.value.length;
|
|
2928
|
+
state.hasInteracted = target.value.length > 0;
|
|
2929
|
+
}
|
|
2930
|
+
// Update internal state for uncontrolled mode
|
|
2931
|
+
if (!controlled) {
|
|
2932
|
+
state.internalValue = target.value;
|
|
2933
|
+
}
|
|
2934
|
+
// Call oninput handler
|
|
2935
|
+
if (oninput) {
|
|
2936
|
+
oninput(target.value);
|
|
2937
|
+
}
|
|
2938
|
+
}, onblur: (e) => {
|
|
2939
|
+
state.active = false;
|
|
2940
|
+
// const target = e.target as HTMLTextAreaElement;
|
|
2941
|
+
state.hasInteracted = true;
|
|
2942
|
+
// Call original onblur if provided
|
|
2943
|
+
if (onblur) {
|
|
2944
|
+
onblur(e);
|
|
2945
|
+
}
|
|
2946
|
+
if (onchange && state.textarea) {
|
|
2947
|
+
onchange(state.textarea.value);
|
|
2948
|
+
}
|
|
2949
|
+
}, onkeyup: onkeyup
|
|
2950
|
+
? (ev) => {
|
|
2951
|
+
onkeyup(ev, ev.target.value);
|
|
2952
|
+
}
|
|
2953
|
+
: undefined, onkeydown: onkeydown
|
|
2954
|
+
? (ev) => {
|
|
2955
|
+
onkeydown(ev, ev.target.value);
|
|
2956
|
+
}
|
|
2957
|
+
: undefined, onkeypress: onkeypress
|
|
2958
|
+
? (ev) => {
|
|
2959
|
+
onkeypress(ev, ev.target.value);
|
|
2960
|
+
}
|
|
2961
|
+
: undefined })),
|
|
2962
|
+
m(Label, {
|
|
2963
|
+
label,
|
|
2964
|
+
id,
|
|
2965
|
+
isMandatory,
|
|
2966
|
+
isActive: currentValue || placeholder || state.active,
|
|
2967
|
+
initialValue: currentValue !== '',
|
|
2968
|
+
}),
|
|
2969
|
+
m(HelperText, {
|
|
2970
|
+
helperText,
|
|
2971
|
+
dataError: state.hasInteracted && attrs.dataError ? attrs.dataError : undefined,
|
|
2972
|
+
dataSuccess: state.hasInteracted && attrs.dataSuccess ? attrs.dataSuccess : undefined,
|
|
2973
|
+
}),
|
|
2974
|
+
maxLength
|
|
2975
|
+
? m(CharacterCounter, {
|
|
2976
|
+
currentLength: state.currentLength,
|
|
2977
|
+
maxLength,
|
|
2978
|
+
show: state.currentLength > 0,
|
|
2979
|
+
})
|
|
2980
|
+
: undefined,
|
|
2981
|
+
]),
|
|
2982
|
+
];
|
|
2921
2983
|
},
|
|
2922
2984
|
};
|
|
2923
2985
|
};
|
|
@@ -3089,7 +3151,9 @@
|
|
|
3089
3151
|
state.isValid = true;
|
|
3090
3152
|
}
|
|
3091
3153
|
}
|
|
3092
|
-
else if ((type === 'email' || type === 'url') &&
|
|
3154
|
+
else if ((type === 'email' || type === 'url') &&
|
|
3155
|
+
target.classList.contains('invalid') &&
|
|
3156
|
+
target.value.length > 0) {
|
|
3093
3157
|
// Clear native validation errors if user is typing and input becomes valid
|
|
3094
3158
|
if (target.validity.valid) {
|
|
3095
3159
|
target.classList.remove('invalid');
|
|
@@ -4002,7 +4066,7 @@
|
|
|
4002
4066
|
m.redraw();
|
|
4003
4067
|
}
|
|
4004
4068
|
};
|
|
4005
|
-
const handleKeyDown = (e, items
|
|
4069
|
+
const handleKeyDown = (e, items) => {
|
|
4006
4070
|
const availableItems = items.filter((item) => !item.divider && !item.disabled);
|
|
4007
4071
|
switch (e.key) {
|
|
4008
4072
|
case 'ArrowDown':
|
|
@@ -4012,15 +4076,15 @@
|
|
|
4012
4076
|
state.focusedIndex = availableItems.length > 0 ? 0 : -1;
|
|
4013
4077
|
}
|
|
4014
4078
|
else {
|
|
4015
|
-
state.focusedIndex =
|
|
4079
|
+
state.focusedIndex = (state.focusedIndex + 1) % availableItems.length;
|
|
4016
4080
|
}
|
|
4017
|
-
|
|
4081
|
+
return undefined;
|
|
4018
4082
|
case 'ArrowUp':
|
|
4019
4083
|
e.preventDefault();
|
|
4020
4084
|
if (state.isOpen) {
|
|
4021
|
-
state.focusedIndex =
|
|
4085
|
+
state.focusedIndex = state.focusedIndex <= 0 ? availableItems.length - 1 : state.focusedIndex - 1;
|
|
4022
4086
|
}
|
|
4023
|
-
|
|
4087
|
+
return undefined;
|
|
4024
4088
|
case 'Enter':
|
|
4025
4089
|
case ' ':
|
|
4026
4090
|
e.preventDefault();
|
|
@@ -4029,18 +4093,20 @@
|
|
|
4029
4093
|
const value = (selectedItem.id || selectedItem.label);
|
|
4030
4094
|
state.isOpen = false;
|
|
4031
4095
|
state.focusedIndex = -1;
|
|
4032
|
-
return value;
|
|
4096
|
+
return value;
|
|
4033
4097
|
}
|
|
4034
4098
|
else if (!state.isOpen) {
|
|
4035
4099
|
state.isOpen = true;
|
|
4036
4100
|
state.focusedIndex = availableItems.length > 0 ? 0 : -1;
|
|
4037
4101
|
}
|
|
4038
|
-
|
|
4102
|
+
return undefined;
|
|
4039
4103
|
case 'Escape':
|
|
4040
4104
|
e.preventDefault();
|
|
4041
4105
|
state.isOpen = false;
|
|
4042
4106
|
state.focusedIndex = -1;
|
|
4043
|
-
|
|
4107
|
+
return undefined;
|
|
4108
|
+
default:
|
|
4109
|
+
return undefined;
|
|
4044
4110
|
}
|
|
4045
4111
|
};
|
|
4046
4112
|
return {
|
|
@@ -4073,7 +4139,9 @@
|
|
|
4073
4139
|
}
|
|
4074
4140
|
};
|
|
4075
4141
|
const selectedItem = currentCheckedId
|
|
4076
|
-
? items
|
|
4142
|
+
? items
|
|
4143
|
+
.filter((i) => (i.id ? i.id === currentCheckedId : i.label === currentCheckedId))
|
|
4144
|
+
.shift()
|
|
4077
4145
|
: undefined;
|
|
4078
4146
|
const title = selectedItem ? selectedItem.label : label || 'Select';
|
|
4079
4147
|
const availableItems = items.filter((item) => !item.divider && !item.disabled);
|
|
@@ -4081,12 +4149,14 @@
|
|
|
4081
4149
|
iconName ? m('i.material-icons.prefix', iconName) : undefined,
|
|
4082
4150
|
m(HelperText, { helperText }),
|
|
4083
4151
|
m('.select-wrapper', {
|
|
4084
|
-
onkeydown: disabled
|
|
4085
|
-
|
|
4086
|
-
|
|
4087
|
-
|
|
4088
|
-
|
|
4089
|
-
|
|
4152
|
+
onkeydown: disabled
|
|
4153
|
+
? undefined
|
|
4154
|
+
: (e) => {
|
|
4155
|
+
const selectedValue = handleKeyDown(e, items);
|
|
4156
|
+
if (selectedValue) {
|
|
4157
|
+
handleSelection(selectedValue);
|
|
4158
|
+
}
|
|
4159
|
+
},
|
|
4090
4160
|
tabindex: disabled ? -1 : 0,
|
|
4091
4161
|
'aria-expanded': state.isOpen ? 'true' : 'false',
|
|
4092
4162
|
'aria-haspopup': 'listbox',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mithril-materialized",
|
|
3
|
-
"version": "3.2.
|
|
3
|
+
"version": "3.2.2",
|
|
4
4
|
"description": "A materialize library for mithril.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.esm.js",
|
|
@@ -86,16 +86,16 @@
|
|
|
86
86
|
"concurrently": "^9.2.1",
|
|
87
87
|
"express": "^5.1.0",
|
|
88
88
|
"identity-obj-proxy": "^3.0.0",
|
|
89
|
-
"jest": "^30.1.
|
|
90
|
-
"jest-environment-jsdom": "^30.1.
|
|
89
|
+
"jest": "^30.1.2",
|
|
90
|
+
"jest-environment-jsdom": "^30.1.2",
|
|
91
91
|
"js-yaml": "^4.1.0",
|
|
92
92
|
"rimraf": "^6.0.1",
|
|
93
|
-
"rollup": "^4.
|
|
93
|
+
"rollup": "^4.50.0",
|
|
94
94
|
"rollup-plugin-postcss": "^4.0.2",
|
|
95
95
|
"sass": "^1.91.0",
|
|
96
96
|
"ts-jest": "^29.4.1",
|
|
97
97
|
"tslib": "^2.8.1",
|
|
98
|
-
"typedoc": "^0.28.
|
|
98
|
+
"typedoc": "^0.28.12",
|
|
99
99
|
"typescript": "^5.9.2"
|
|
100
100
|
}
|
|
101
101
|
}
|