mithril-materialized 3.3.7 → 3.3.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.esm.js +148 -80
- package/dist/index.js +148 -80
- package/dist/index.umd.js +148 -80
- package/package.json +1 -1
package/dist/index.esm.js
CHANGED
|
@@ -2811,6 +2811,7 @@ const TextArea = () => {
|
|
|
2811
2811
|
height: undefined,
|
|
2812
2812
|
active: false,
|
|
2813
2813
|
textarea: undefined,
|
|
2814
|
+
hiddenDiv: undefined,
|
|
2814
2815
|
internalValue: '',
|
|
2815
2816
|
};
|
|
2816
2817
|
const updateHeight = (textarea, hiddenDiv) => {
|
|
@@ -2907,13 +2908,13 @@ const TextArea = () => {
|
|
|
2907
2908
|
overflowWrap: 'break-word',
|
|
2908
2909
|
},
|
|
2909
2910
|
oncreate: ({ dom }) => {
|
|
2910
|
-
const hiddenDiv = dom;
|
|
2911
|
+
const hiddenDiv = state.hiddenDiv = dom;
|
|
2911
2912
|
if (state.textarea) {
|
|
2912
2913
|
updateHeight(state.textarea, hiddenDiv);
|
|
2913
2914
|
}
|
|
2914
2915
|
},
|
|
2915
2916
|
onupdate: ({ dom }) => {
|
|
2916
|
-
const hiddenDiv = dom;
|
|
2917
|
+
const hiddenDiv = state.hiddenDiv = dom;
|
|
2917
2918
|
if (state.textarea) {
|
|
2918
2919
|
updateHeight(state.textarea, hiddenDiv);
|
|
2919
2920
|
}
|
|
@@ -2938,7 +2939,10 @@ const TextArea = () => {
|
|
|
2938
2939
|
const textarea = dom;
|
|
2939
2940
|
if (state.height)
|
|
2940
2941
|
textarea.style.height = state.height;
|
|
2941
|
-
//
|
|
2942
|
+
// Trigger height recalculation when value changes programmatically
|
|
2943
|
+
if (state.hiddenDiv) {
|
|
2944
|
+
updateHeight(textarea, state.hiddenDiv);
|
|
2945
|
+
}
|
|
2942
2946
|
}, onfocus: () => {
|
|
2943
2947
|
state.active = true;
|
|
2944
2948
|
}, oninput: (e) => {
|
|
@@ -3098,9 +3102,6 @@ const InputField = (type, defaultClass = '') => () => {
|
|
|
3098
3102
|
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"]);
|
|
3099
3103
|
// const attributes = toAttrs(params);
|
|
3100
3104
|
const cn = [newRow ? 'clear' : '', defaultClass, className].filter(Boolean).join(' ').trim() || undefined;
|
|
3101
|
-
const isActive = state.active || ((_a = state.inputElement) === null || _a === void 0 ? void 0 : _a.value) || placeholder || type === 'color' || type === 'range'
|
|
3102
|
-
? true
|
|
3103
|
-
: false;
|
|
3104
3105
|
// Special rendering for minmax range sliders
|
|
3105
3106
|
if (type === 'range' && (attrs.minmax || attrs.valueDisplay)) {
|
|
3106
3107
|
return m(attrs.minmax ? DoubleRangeSlider : SingleRangeSlider, Object.assign(Object.assign({}, attrs), { state,
|
|
@@ -3121,12 +3122,15 @@ const InputField = (type, defaultClass = '') => () => {
|
|
|
3121
3122
|
}
|
|
3122
3123
|
else if (isNonInteractive) {
|
|
3123
3124
|
// Non-interactive components: prefer defaultValue, fallback to value
|
|
3124
|
-
value = ((
|
|
3125
|
+
value = ((_b = (_a = attrs.defaultValue) !== null && _a !== void 0 ? _a : attrs.value) !== null && _b !== void 0 ? _b : (isNumeric ? 0 : ''));
|
|
3125
3126
|
}
|
|
3126
3127
|
else {
|
|
3127
3128
|
// Interactive uncontrolled: use internal state
|
|
3128
|
-
value = ((
|
|
3129
|
+
value = ((_d = (_c = state.internalValue) !== null && _c !== void 0 ? _c : attrs.defaultValue) !== null && _d !== void 0 ? _d : (isNumeric ? 0 : ''));
|
|
3129
3130
|
}
|
|
3131
|
+
const isActive = state.active || ((_e = state.inputElement) === null || _e === void 0 ? void 0 : _e.value) || value || placeholder || type === 'color' || type === 'range'
|
|
3132
|
+
? true
|
|
3133
|
+
: false;
|
|
3130
3134
|
const rangeType = type === 'range' && !attrs.minmax;
|
|
3131
3135
|
return m('.input-field', { className: cn, style }, [
|
|
3132
3136
|
iconName ? m('i.material-icons.prefix', iconName) : undefined,
|
|
@@ -5752,11 +5756,13 @@ const RadioButtons = () => {
|
|
|
5752
5756
|
const Select = () => {
|
|
5753
5757
|
const state = {
|
|
5754
5758
|
id: '',
|
|
5759
|
+
dropdownId: '',
|
|
5755
5760
|
isOpen: false,
|
|
5756
5761
|
focusedIndex: -1,
|
|
5757
5762
|
inputRef: null,
|
|
5758
5763
|
dropdownRef: null,
|
|
5759
5764
|
internalSelectedIds: [],
|
|
5765
|
+
isInsideModal: false,
|
|
5760
5766
|
};
|
|
5761
5767
|
const isControlled = (attrs) => attrs.checkedId !== undefined && attrs.onchange !== undefined;
|
|
5762
5768
|
const isSelected = (id, selectedIds) => {
|
|
@@ -5845,9 +5851,124 @@ const Select = () => {
|
|
|
5845
5851
|
m.redraw();
|
|
5846
5852
|
}
|
|
5847
5853
|
};
|
|
5854
|
+
const getPortalStyles = (inputRef) => {
|
|
5855
|
+
if (!inputRef)
|
|
5856
|
+
return {};
|
|
5857
|
+
const rect = inputRef.getBoundingClientRect();
|
|
5858
|
+
const viewportHeight = window.innerHeight;
|
|
5859
|
+
return {
|
|
5860
|
+
position: 'fixed',
|
|
5861
|
+
top: `${rect.bottom}px`,
|
|
5862
|
+
left: `${rect.left}px`,
|
|
5863
|
+
width: `${rect.width}px`,
|
|
5864
|
+
zIndex: 10000, // Higher than modal z-index
|
|
5865
|
+
maxHeight: `${viewportHeight - rect.bottom - 20}px`, // Leave 20px margin from bottom
|
|
5866
|
+
};
|
|
5867
|
+
};
|
|
5868
|
+
const renderDropdownContent = (attrs, selectedIds, multiple, placeholder) => [
|
|
5869
|
+
placeholder && m('li.disabled', { tabindex: 0 }, m('span', placeholder)),
|
|
5870
|
+
// Render ungrouped options first
|
|
5871
|
+
attrs.options
|
|
5872
|
+
.filter((option) => !option.group)
|
|
5873
|
+
.map((option) => m('li', Object.assign({ key: option.id, class: option.disabled
|
|
5874
|
+
? 'disabled'
|
|
5875
|
+
: state.focusedIndex === attrs.options.indexOf(option)
|
|
5876
|
+
? 'focused'
|
|
5877
|
+
: '' }, (option.disabled
|
|
5878
|
+
? {}
|
|
5879
|
+
: {
|
|
5880
|
+
onclick: (e) => {
|
|
5881
|
+
e.stopPropagation();
|
|
5882
|
+
toggleOption(option.id, multiple, attrs);
|
|
5883
|
+
},
|
|
5884
|
+
})), [
|
|
5885
|
+
option.img && m('img', { src: option.img, alt: option.label }),
|
|
5886
|
+
m('span', [
|
|
5887
|
+
multiple
|
|
5888
|
+
? m('label', { for: option.id }, m('input', {
|
|
5889
|
+
id: option.id,
|
|
5890
|
+
type: 'checkbox',
|
|
5891
|
+
checked: selectedIds.includes(option.id),
|
|
5892
|
+
disabled: option.disabled ? true : undefined,
|
|
5893
|
+
onclick: (e) => {
|
|
5894
|
+
e.stopPropagation();
|
|
5895
|
+
},
|
|
5896
|
+
}), m('span', option.label))
|
|
5897
|
+
: m('span', option.label),
|
|
5898
|
+
].filter(Boolean)),
|
|
5899
|
+
])),
|
|
5900
|
+
// Render grouped options
|
|
5901
|
+
Object.entries(attrs.options
|
|
5902
|
+
.filter((option) => option.group)
|
|
5903
|
+
.reduce((groups, option) => {
|
|
5904
|
+
const group = option.group;
|
|
5905
|
+
if (!groups[group])
|
|
5906
|
+
groups[group] = [];
|
|
5907
|
+
groups[group].push(option);
|
|
5908
|
+
return groups;
|
|
5909
|
+
}, {}))
|
|
5910
|
+
.map(([groupName, groupOptions]) => [
|
|
5911
|
+
m('li.optgroup', { key: `group-${groupName}`, tabindex: 0 }, m('span', groupName)),
|
|
5912
|
+
...groupOptions.map((option) => m('li', Object.assign({ key: option.id, class: `optgroup-option${option.disabled ? ' disabled' : ''}${isSelected(option.id, selectedIds) ? ' selected' : ''}${state.focusedIndex === attrs.options.indexOf(option) ? ' focused' : ''}` }, (option.disabled
|
|
5913
|
+
? {}
|
|
5914
|
+
: {
|
|
5915
|
+
onclick: (e) => {
|
|
5916
|
+
e.stopPropagation();
|
|
5917
|
+
toggleOption(option.id, multiple, attrs);
|
|
5918
|
+
},
|
|
5919
|
+
})), [
|
|
5920
|
+
option.img && m('img', { src: option.img, alt: option.label }),
|
|
5921
|
+
m('span', [
|
|
5922
|
+
multiple
|
|
5923
|
+
? m('label', { for: option.id }, m('input', {
|
|
5924
|
+
id: option.id,
|
|
5925
|
+
type: 'checkbox',
|
|
5926
|
+
checked: selectedIds.includes(option.id),
|
|
5927
|
+
disabled: option.disabled ? true : undefined,
|
|
5928
|
+
onclick: (e) => {
|
|
5929
|
+
e.stopPropagation();
|
|
5930
|
+
},
|
|
5931
|
+
}), m('span', option.label))
|
|
5932
|
+
: m('span', option.label),
|
|
5933
|
+
].filter(Boolean)),
|
|
5934
|
+
])),
|
|
5935
|
+
])
|
|
5936
|
+
.reduce((acc, val) => acc.concat(val), []),
|
|
5937
|
+
];
|
|
5938
|
+
const updatePortalDropdown = (attrs, selectedIds, multiple, placeholder) => {
|
|
5939
|
+
var _a;
|
|
5940
|
+
if (!state.isInsideModal)
|
|
5941
|
+
return;
|
|
5942
|
+
let portalElement = document.getElementById(state.dropdownId);
|
|
5943
|
+
if (!state.isOpen) {
|
|
5944
|
+
// Clean up portal when dropdown is closed
|
|
5945
|
+
if (portalElement) {
|
|
5946
|
+
m.render(portalElement, []);
|
|
5947
|
+
(_a = portalElement.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(portalElement);
|
|
5948
|
+
}
|
|
5949
|
+
return;
|
|
5950
|
+
}
|
|
5951
|
+
if (!portalElement) {
|
|
5952
|
+
portalElement = document.createElement('div');
|
|
5953
|
+
portalElement.id = state.dropdownId;
|
|
5954
|
+
document.body.appendChild(portalElement);
|
|
5955
|
+
}
|
|
5956
|
+
const dropdownVnode = m('ul.dropdown-content.select-dropdown', {
|
|
5957
|
+
tabindex: 0,
|
|
5958
|
+
style: getPortalStyles(state.inputRef),
|
|
5959
|
+
oncreate: ({ dom }) => {
|
|
5960
|
+
state.dropdownRef = dom;
|
|
5961
|
+
},
|
|
5962
|
+
onremove: () => {
|
|
5963
|
+
state.dropdownRef = null;
|
|
5964
|
+
},
|
|
5965
|
+
}, renderDropdownContent(attrs, selectedIds, multiple, placeholder));
|
|
5966
|
+
m.render(portalElement, dropdownVnode);
|
|
5967
|
+
};
|
|
5848
5968
|
return {
|
|
5849
5969
|
oninit: ({ attrs }) => {
|
|
5850
5970
|
state.id = attrs.id || uniqueId();
|
|
5971
|
+
state.dropdownId = `${state.id}-dropdown`;
|
|
5851
5972
|
const controlled = isControlled(attrs);
|
|
5852
5973
|
// Warn developer for improper controlled usage
|
|
5853
5974
|
if (attrs.checkedId !== undefined && !controlled && !attrs.disabled) {
|
|
@@ -5866,9 +5987,20 @@ const Select = () => {
|
|
|
5866
5987
|
// Add global click listener to close dropdown
|
|
5867
5988
|
document.addEventListener('click', closeDropdown);
|
|
5868
5989
|
},
|
|
5990
|
+
oncreate: ({ dom }) => {
|
|
5991
|
+
// Detect if component is inside a modal
|
|
5992
|
+
state.isInsideModal = !!dom.closest('.modal');
|
|
5993
|
+
},
|
|
5869
5994
|
onremove: () => {
|
|
5870
5995
|
// Cleanup global listener
|
|
5871
5996
|
document.removeEventListener('click', closeDropdown);
|
|
5997
|
+
// Cleanup portaled dropdown if it exists
|
|
5998
|
+
if (state.isInsideModal && state.dropdownRef) {
|
|
5999
|
+
const portalElement = document.getElementById(state.dropdownId);
|
|
6000
|
+
if (portalElement && portalElement.parentNode) {
|
|
6001
|
+
portalElement.parentNode.removeChild(portalElement);
|
|
6002
|
+
}
|
|
6003
|
+
}
|
|
5872
6004
|
},
|
|
5873
6005
|
view: ({ attrs }) => {
|
|
5874
6006
|
var _a;
|
|
@@ -5892,6 +6024,10 @@ const Select = () => {
|
|
|
5892
6024
|
const { newRow, className = 'col s12', key, options, multiple = false, label, helperText, placeholder = '', isMandatory, iconName, style, } = attrs;
|
|
5893
6025
|
const finalClassName = newRow ? `${className} clear` : className;
|
|
5894
6026
|
const selectedOptions = options.filter((opt) => isSelected(opt.id, selectedIds));
|
|
6027
|
+
// Update portal dropdown when inside modal
|
|
6028
|
+
if (state.isInsideModal) {
|
|
6029
|
+
updatePortalDropdown(attrs, selectedIds, multiple, placeholder);
|
|
6030
|
+
}
|
|
5895
6031
|
return m('.input-field.select-space', {
|
|
5896
6032
|
className: finalClassName,
|
|
5897
6033
|
key,
|
|
@@ -5904,6 +6040,7 @@ const Select = () => {
|
|
|
5904
6040
|
tabindex: disabled ? -1 : 0,
|
|
5905
6041
|
'aria-expanded': state.isOpen ? 'true' : 'false',
|
|
5906
6042
|
'aria-haspopup': 'listbox',
|
|
6043
|
+
'aria-controls': state.dropdownId,
|
|
5907
6044
|
role: 'combobox',
|
|
5908
6045
|
}, [
|
|
5909
6046
|
m('input[type=text][readonly=true].select-dropdown.dropdown-trigger', {
|
|
@@ -5920,8 +6057,8 @@ const Select = () => {
|
|
|
5920
6057
|
}
|
|
5921
6058
|
},
|
|
5922
6059
|
}),
|
|
5923
|
-
// Dropdown Menu
|
|
5924
|
-
state.isOpen &&
|
|
6060
|
+
// Dropdown Menu - render inline only when NOT inside modal
|
|
6061
|
+
state.isOpen && !state.isInsideModal &&
|
|
5925
6062
|
m('ul.dropdown-content.select-dropdown', {
|
|
5926
6063
|
tabindex: 0,
|
|
5927
6064
|
oncreate: ({ dom }) => {
|
|
@@ -5931,76 +6068,7 @@ const Select = () => {
|
|
|
5931
6068
|
state.dropdownRef = null;
|
|
5932
6069
|
},
|
|
5933
6070
|
style: getDropdownStyles(state.inputRef, true, options),
|
|
5934
|
-
},
|
|
5935
|
-
placeholder && m('li.disabled', { tabindex: 0 }, m('span', placeholder)),
|
|
5936
|
-
// Render ungrouped options first
|
|
5937
|
-
options
|
|
5938
|
-
.filter((option) => !option.group)
|
|
5939
|
-
.map((option) => m('li', Object.assign({ key: option.id, class: option.disabled
|
|
5940
|
-
? 'disabled'
|
|
5941
|
-
: state.focusedIndex === options.indexOf(option)
|
|
5942
|
-
? 'focused'
|
|
5943
|
-
: '' }, (option.disabled
|
|
5944
|
-
? {}
|
|
5945
|
-
: {
|
|
5946
|
-
onclick: (e) => {
|
|
5947
|
-
e.stopPropagation();
|
|
5948
|
-
toggleOption(option.id, multiple, attrs);
|
|
5949
|
-
},
|
|
5950
|
-
})), [
|
|
5951
|
-
option.img && m('img', { src: option.img, alt: option.label }),
|
|
5952
|
-
m('span', [
|
|
5953
|
-
multiple
|
|
5954
|
-
? m('label', { for: option.id }, m('input', {
|
|
5955
|
-
id: option.id,
|
|
5956
|
-
type: 'checkbox',
|
|
5957
|
-
checked: selectedIds.includes(option.id),
|
|
5958
|
-
disabled: option.disabled ? true : undefined,
|
|
5959
|
-
onclick: (e) => {
|
|
5960
|
-
e.stopPropagation();
|
|
5961
|
-
},
|
|
5962
|
-
}), m('span', option.label))
|
|
5963
|
-
: m('span', option.label),
|
|
5964
|
-
].filter(Boolean)),
|
|
5965
|
-
])),
|
|
5966
|
-
// Render grouped options
|
|
5967
|
-
Object.entries(options
|
|
5968
|
-
.filter((option) => option.group)
|
|
5969
|
-
.reduce((groups, option) => {
|
|
5970
|
-
const group = option.group;
|
|
5971
|
-
if (!groups[group])
|
|
5972
|
-
groups[group] = [];
|
|
5973
|
-
groups[group].push(option);
|
|
5974
|
-
return groups;
|
|
5975
|
-
}, {}))
|
|
5976
|
-
.map(([groupName, groupOptions]) => [
|
|
5977
|
-
m('li.optgroup', { key: `group-${groupName}`, tabindex: 0 }, m('span', groupName)),
|
|
5978
|
-
...groupOptions.map((option) => m('li', Object.assign({ key: option.id, class: `optgroup-option${option.disabled ? ' disabled' : ''}${isSelected(option.id, selectedIds) ? ' selected' : ''}${state.focusedIndex === options.indexOf(option) ? ' focused' : ''}` }, (option.disabled
|
|
5979
|
-
? {}
|
|
5980
|
-
: {
|
|
5981
|
-
onclick: (e) => {
|
|
5982
|
-
e.stopPropagation();
|
|
5983
|
-
toggleOption(option.id, multiple, attrs);
|
|
5984
|
-
},
|
|
5985
|
-
})), [
|
|
5986
|
-
option.img && m('img', { src: option.img, alt: option.label }),
|
|
5987
|
-
m('span', [
|
|
5988
|
-
multiple
|
|
5989
|
-
? m('label', { for: option.id }, m('input', {
|
|
5990
|
-
id: option.id,
|
|
5991
|
-
type: 'checkbox',
|
|
5992
|
-
checked: selectedIds.includes(option.id),
|
|
5993
|
-
disabled: option.disabled ? true : undefined,
|
|
5994
|
-
onclick: (e) => {
|
|
5995
|
-
e.stopPropagation();
|
|
5996
|
-
},
|
|
5997
|
-
}), m('span', option.label))
|
|
5998
|
-
: m('span', option.label),
|
|
5999
|
-
].filter(Boolean)),
|
|
6000
|
-
])),
|
|
6001
|
-
])
|
|
6002
|
-
.reduce((acc, val) => acc.concat(val), []),
|
|
6003
|
-
]),
|
|
6071
|
+
}, renderDropdownContent(attrs, selectedIds, multiple, placeholder)),
|
|
6004
6072
|
m(MaterialIcon, {
|
|
6005
6073
|
name: 'caret',
|
|
6006
6074
|
direction: 'down',
|
package/dist/index.js
CHANGED
|
@@ -2813,6 +2813,7 @@ const TextArea = () => {
|
|
|
2813
2813
|
height: undefined,
|
|
2814
2814
|
active: false,
|
|
2815
2815
|
textarea: undefined,
|
|
2816
|
+
hiddenDiv: undefined,
|
|
2816
2817
|
internalValue: '',
|
|
2817
2818
|
};
|
|
2818
2819
|
const updateHeight = (textarea, hiddenDiv) => {
|
|
@@ -2909,13 +2910,13 @@ const TextArea = () => {
|
|
|
2909
2910
|
overflowWrap: 'break-word',
|
|
2910
2911
|
},
|
|
2911
2912
|
oncreate: ({ dom }) => {
|
|
2912
|
-
const hiddenDiv = dom;
|
|
2913
|
+
const hiddenDiv = state.hiddenDiv = dom;
|
|
2913
2914
|
if (state.textarea) {
|
|
2914
2915
|
updateHeight(state.textarea, hiddenDiv);
|
|
2915
2916
|
}
|
|
2916
2917
|
},
|
|
2917
2918
|
onupdate: ({ dom }) => {
|
|
2918
|
-
const hiddenDiv = dom;
|
|
2919
|
+
const hiddenDiv = state.hiddenDiv = dom;
|
|
2919
2920
|
if (state.textarea) {
|
|
2920
2921
|
updateHeight(state.textarea, hiddenDiv);
|
|
2921
2922
|
}
|
|
@@ -2940,7 +2941,10 @@ const TextArea = () => {
|
|
|
2940
2941
|
const textarea = dom;
|
|
2941
2942
|
if (state.height)
|
|
2942
2943
|
textarea.style.height = state.height;
|
|
2943
|
-
//
|
|
2944
|
+
// Trigger height recalculation when value changes programmatically
|
|
2945
|
+
if (state.hiddenDiv) {
|
|
2946
|
+
updateHeight(textarea, state.hiddenDiv);
|
|
2947
|
+
}
|
|
2944
2948
|
}, onfocus: () => {
|
|
2945
2949
|
state.active = true;
|
|
2946
2950
|
}, oninput: (e) => {
|
|
@@ -3100,9 +3104,6 @@ const InputField = (type, defaultClass = '') => () => {
|
|
|
3100
3104
|
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"]);
|
|
3101
3105
|
// const attributes = toAttrs(params);
|
|
3102
3106
|
const cn = [newRow ? 'clear' : '', defaultClass, className].filter(Boolean).join(' ').trim() || undefined;
|
|
3103
|
-
const isActive = state.active || ((_a = state.inputElement) === null || _a === void 0 ? void 0 : _a.value) || placeholder || type === 'color' || type === 'range'
|
|
3104
|
-
? true
|
|
3105
|
-
: false;
|
|
3106
3107
|
// Special rendering for minmax range sliders
|
|
3107
3108
|
if (type === 'range' && (attrs.minmax || attrs.valueDisplay)) {
|
|
3108
3109
|
return m(attrs.minmax ? DoubleRangeSlider : SingleRangeSlider, Object.assign(Object.assign({}, attrs), { state,
|
|
@@ -3123,12 +3124,15 @@ const InputField = (type, defaultClass = '') => () => {
|
|
|
3123
3124
|
}
|
|
3124
3125
|
else if (isNonInteractive) {
|
|
3125
3126
|
// Non-interactive components: prefer defaultValue, fallback to value
|
|
3126
|
-
value = ((
|
|
3127
|
+
value = ((_b = (_a = attrs.defaultValue) !== null && _a !== void 0 ? _a : attrs.value) !== null && _b !== void 0 ? _b : (isNumeric ? 0 : ''));
|
|
3127
3128
|
}
|
|
3128
3129
|
else {
|
|
3129
3130
|
// Interactive uncontrolled: use internal state
|
|
3130
|
-
value = ((
|
|
3131
|
+
value = ((_d = (_c = state.internalValue) !== null && _c !== void 0 ? _c : attrs.defaultValue) !== null && _d !== void 0 ? _d : (isNumeric ? 0 : ''));
|
|
3131
3132
|
}
|
|
3133
|
+
const isActive = state.active || ((_e = state.inputElement) === null || _e === void 0 ? void 0 : _e.value) || value || placeholder || type === 'color' || type === 'range'
|
|
3134
|
+
? true
|
|
3135
|
+
: false;
|
|
3132
3136
|
const rangeType = type === 'range' && !attrs.minmax;
|
|
3133
3137
|
return m('.input-field', { className: cn, style }, [
|
|
3134
3138
|
iconName ? m('i.material-icons.prefix', iconName) : undefined,
|
|
@@ -5754,11 +5758,13 @@ const RadioButtons = () => {
|
|
|
5754
5758
|
const Select = () => {
|
|
5755
5759
|
const state = {
|
|
5756
5760
|
id: '',
|
|
5761
|
+
dropdownId: '',
|
|
5757
5762
|
isOpen: false,
|
|
5758
5763
|
focusedIndex: -1,
|
|
5759
5764
|
inputRef: null,
|
|
5760
5765
|
dropdownRef: null,
|
|
5761
5766
|
internalSelectedIds: [],
|
|
5767
|
+
isInsideModal: false,
|
|
5762
5768
|
};
|
|
5763
5769
|
const isControlled = (attrs) => attrs.checkedId !== undefined && attrs.onchange !== undefined;
|
|
5764
5770
|
const isSelected = (id, selectedIds) => {
|
|
@@ -5847,9 +5853,124 @@ const Select = () => {
|
|
|
5847
5853
|
m.redraw();
|
|
5848
5854
|
}
|
|
5849
5855
|
};
|
|
5856
|
+
const getPortalStyles = (inputRef) => {
|
|
5857
|
+
if (!inputRef)
|
|
5858
|
+
return {};
|
|
5859
|
+
const rect = inputRef.getBoundingClientRect();
|
|
5860
|
+
const viewportHeight = window.innerHeight;
|
|
5861
|
+
return {
|
|
5862
|
+
position: 'fixed',
|
|
5863
|
+
top: `${rect.bottom}px`,
|
|
5864
|
+
left: `${rect.left}px`,
|
|
5865
|
+
width: `${rect.width}px`,
|
|
5866
|
+
zIndex: 10000, // Higher than modal z-index
|
|
5867
|
+
maxHeight: `${viewportHeight - rect.bottom - 20}px`, // Leave 20px margin from bottom
|
|
5868
|
+
};
|
|
5869
|
+
};
|
|
5870
|
+
const renderDropdownContent = (attrs, selectedIds, multiple, placeholder) => [
|
|
5871
|
+
placeholder && m('li.disabled', { tabindex: 0 }, m('span', placeholder)),
|
|
5872
|
+
// Render ungrouped options first
|
|
5873
|
+
attrs.options
|
|
5874
|
+
.filter((option) => !option.group)
|
|
5875
|
+
.map((option) => m('li', Object.assign({ key: option.id, class: option.disabled
|
|
5876
|
+
? 'disabled'
|
|
5877
|
+
: state.focusedIndex === attrs.options.indexOf(option)
|
|
5878
|
+
? 'focused'
|
|
5879
|
+
: '' }, (option.disabled
|
|
5880
|
+
? {}
|
|
5881
|
+
: {
|
|
5882
|
+
onclick: (e) => {
|
|
5883
|
+
e.stopPropagation();
|
|
5884
|
+
toggleOption(option.id, multiple, attrs);
|
|
5885
|
+
},
|
|
5886
|
+
})), [
|
|
5887
|
+
option.img && m('img', { src: option.img, alt: option.label }),
|
|
5888
|
+
m('span', [
|
|
5889
|
+
multiple
|
|
5890
|
+
? m('label', { for: option.id }, m('input', {
|
|
5891
|
+
id: option.id,
|
|
5892
|
+
type: 'checkbox',
|
|
5893
|
+
checked: selectedIds.includes(option.id),
|
|
5894
|
+
disabled: option.disabled ? true : undefined,
|
|
5895
|
+
onclick: (e) => {
|
|
5896
|
+
e.stopPropagation();
|
|
5897
|
+
},
|
|
5898
|
+
}), m('span', option.label))
|
|
5899
|
+
: m('span', option.label),
|
|
5900
|
+
].filter(Boolean)),
|
|
5901
|
+
])),
|
|
5902
|
+
// Render grouped options
|
|
5903
|
+
Object.entries(attrs.options
|
|
5904
|
+
.filter((option) => option.group)
|
|
5905
|
+
.reduce((groups, option) => {
|
|
5906
|
+
const group = option.group;
|
|
5907
|
+
if (!groups[group])
|
|
5908
|
+
groups[group] = [];
|
|
5909
|
+
groups[group].push(option);
|
|
5910
|
+
return groups;
|
|
5911
|
+
}, {}))
|
|
5912
|
+
.map(([groupName, groupOptions]) => [
|
|
5913
|
+
m('li.optgroup', { key: `group-${groupName}`, tabindex: 0 }, m('span', groupName)),
|
|
5914
|
+
...groupOptions.map((option) => m('li', Object.assign({ key: option.id, class: `optgroup-option${option.disabled ? ' disabled' : ''}${isSelected(option.id, selectedIds) ? ' selected' : ''}${state.focusedIndex === attrs.options.indexOf(option) ? ' focused' : ''}` }, (option.disabled
|
|
5915
|
+
? {}
|
|
5916
|
+
: {
|
|
5917
|
+
onclick: (e) => {
|
|
5918
|
+
e.stopPropagation();
|
|
5919
|
+
toggleOption(option.id, multiple, attrs);
|
|
5920
|
+
},
|
|
5921
|
+
})), [
|
|
5922
|
+
option.img && m('img', { src: option.img, alt: option.label }),
|
|
5923
|
+
m('span', [
|
|
5924
|
+
multiple
|
|
5925
|
+
? m('label', { for: option.id }, m('input', {
|
|
5926
|
+
id: option.id,
|
|
5927
|
+
type: 'checkbox',
|
|
5928
|
+
checked: selectedIds.includes(option.id),
|
|
5929
|
+
disabled: option.disabled ? true : undefined,
|
|
5930
|
+
onclick: (e) => {
|
|
5931
|
+
e.stopPropagation();
|
|
5932
|
+
},
|
|
5933
|
+
}), m('span', option.label))
|
|
5934
|
+
: m('span', option.label),
|
|
5935
|
+
].filter(Boolean)),
|
|
5936
|
+
])),
|
|
5937
|
+
])
|
|
5938
|
+
.reduce((acc, val) => acc.concat(val), []),
|
|
5939
|
+
];
|
|
5940
|
+
const updatePortalDropdown = (attrs, selectedIds, multiple, placeholder) => {
|
|
5941
|
+
var _a;
|
|
5942
|
+
if (!state.isInsideModal)
|
|
5943
|
+
return;
|
|
5944
|
+
let portalElement = document.getElementById(state.dropdownId);
|
|
5945
|
+
if (!state.isOpen) {
|
|
5946
|
+
// Clean up portal when dropdown is closed
|
|
5947
|
+
if (portalElement) {
|
|
5948
|
+
m.render(portalElement, []);
|
|
5949
|
+
(_a = portalElement.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(portalElement);
|
|
5950
|
+
}
|
|
5951
|
+
return;
|
|
5952
|
+
}
|
|
5953
|
+
if (!portalElement) {
|
|
5954
|
+
portalElement = document.createElement('div');
|
|
5955
|
+
portalElement.id = state.dropdownId;
|
|
5956
|
+
document.body.appendChild(portalElement);
|
|
5957
|
+
}
|
|
5958
|
+
const dropdownVnode = m('ul.dropdown-content.select-dropdown', {
|
|
5959
|
+
tabindex: 0,
|
|
5960
|
+
style: getPortalStyles(state.inputRef),
|
|
5961
|
+
oncreate: ({ dom }) => {
|
|
5962
|
+
state.dropdownRef = dom;
|
|
5963
|
+
},
|
|
5964
|
+
onremove: () => {
|
|
5965
|
+
state.dropdownRef = null;
|
|
5966
|
+
},
|
|
5967
|
+
}, renderDropdownContent(attrs, selectedIds, multiple, placeholder));
|
|
5968
|
+
m.render(portalElement, dropdownVnode);
|
|
5969
|
+
};
|
|
5850
5970
|
return {
|
|
5851
5971
|
oninit: ({ attrs }) => {
|
|
5852
5972
|
state.id = attrs.id || uniqueId();
|
|
5973
|
+
state.dropdownId = `${state.id}-dropdown`;
|
|
5853
5974
|
const controlled = isControlled(attrs);
|
|
5854
5975
|
// Warn developer for improper controlled usage
|
|
5855
5976
|
if (attrs.checkedId !== undefined && !controlled && !attrs.disabled) {
|
|
@@ -5868,9 +5989,20 @@ const Select = () => {
|
|
|
5868
5989
|
// Add global click listener to close dropdown
|
|
5869
5990
|
document.addEventListener('click', closeDropdown);
|
|
5870
5991
|
},
|
|
5992
|
+
oncreate: ({ dom }) => {
|
|
5993
|
+
// Detect if component is inside a modal
|
|
5994
|
+
state.isInsideModal = !!dom.closest('.modal');
|
|
5995
|
+
},
|
|
5871
5996
|
onremove: () => {
|
|
5872
5997
|
// Cleanup global listener
|
|
5873
5998
|
document.removeEventListener('click', closeDropdown);
|
|
5999
|
+
// Cleanup portaled dropdown if it exists
|
|
6000
|
+
if (state.isInsideModal && state.dropdownRef) {
|
|
6001
|
+
const portalElement = document.getElementById(state.dropdownId);
|
|
6002
|
+
if (portalElement && portalElement.parentNode) {
|
|
6003
|
+
portalElement.parentNode.removeChild(portalElement);
|
|
6004
|
+
}
|
|
6005
|
+
}
|
|
5874
6006
|
},
|
|
5875
6007
|
view: ({ attrs }) => {
|
|
5876
6008
|
var _a;
|
|
@@ -5894,6 +6026,10 @@ const Select = () => {
|
|
|
5894
6026
|
const { newRow, className = 'col s12', key, options, multiple = false, label, helperText, placeholder = '', isMandatory, iconName, style, } = attrs;
|
|
5895
6027
|
const finalClassName = newRow ? `${className} clear` : className;
|
|
5896
6028
|
const selectedOptions = options.filter((opt) => isSelected(opt.id, selectedIds));
|
|
6029
|
+
// Update portal dropdown when inside modal
|
|
6030
|
+
if (state.isInsideModal) {
|
|
6031
|
+
updatePortalDropdown(attrs, selectedIds, multiple, placeholder);
|
|
6032
|
+
}
|
|
5897
6033
|
return m('.input-field.select-space', {
|
|
5898
6034
|
className: finalClassName,
|
|
5899
6035
|
key,
|
|
@@ -5906,6 +6042,7 @@ const Select = () => {
|
|
|
5906
6042
|
tabindex: disabled ? -1 : 0,
|
|
5907
6043
|
'aria-expanded': state.isOpen ? 'true' : 'false',
|
|
5908
6044
|
'aria-haspopup': 'listbox',
|
|
6045
|
+
'aria-controls': state.dropdownId,
|
|
5909
6046
|
role: 'combobox',
|
|
5910
6047
|
}, [
|
|
5911
6048
|
m('input[type=text][readonly=true].select-dropdown.dropdown-trigger', {
|
|
@@ -5922,8 +6059,8 @@ const Select = () => {
|
|
|
5922
6059
|
}
|
|
5923
6060
|
},
|
|
5924
6061
|
}),
|
|
5925
|
-
// Dropdown Menu
|
|
5926
|
-
state.isOpen &&
|
|
6062
|
+
// Dropdown Menu - render inline only when NOT inside modal
|
|
6063
|
+
state.isOpen && !state.isInsideModal &&
|
|
5927
6064
|
m('ul.dropdown-content.select-dropdown', {
|
|
5928
6065
|
tabindex: 0,
|
|
5929
6066
|
oncreate: ({ dom }) => {
|
|
@@ -5933,76 +6070,7 @@ const Select = () => {
|
|
|
5933
6070
|
state.dropdownRef = null;
|
|
5934
6071
|
},
|
|
5935
6072
|
style: getDropdownStyles(state.inputRef, true, options),
|
|
5936
|
-
},
|
|
5937
|
-
placeholder && m('li.disabled', { tabindex: 0 }, m('span', placeholder)),
|
|
5938
|
-
// Render ungrouped options first
|
|
5939
|
-
options
|
|
5940
|
-
.filter((option) => !option.group)
|
|
5941
|
-
.map((option) => m('li', Object.assign({ key: option.id, class: option.disabled
|
|
5942
|
-
? 'disabled'
|
|
5943
|
-
: state.focusedIndex === options.indexOf(option)
|
|
5944
|
-
? 'focused'
|
|
5945
|
-
: '' }, (option.disabled
|
|
5946
|
-
? {}
|
|
5947
|
-
: {
|
|
5948
|
-
onclick: (e) => {
|
|
5949
|
-
e.stopPropagation();
|
|
5950
|
-
toggleOption(option.id, multiple, attrs);
|
|
5951
|
-
},
|
|
5952
|
-
})), [
|
|
5953
|
-
option.img && m('img', { src: option.img, alt: option.label }),
|
|
5954
|
-
m('span', [
|
|
5955
|
-
multiple
|
|
5956
|
-
? m('label', { for: option.id }, m('input', {
|
|
5957
|
-
id: option.id,
|
|
5958
|
-
type: 'checkbox',
|
|
5959
|
-
checked: selectedIds.includes(option.id),
|
|
5960
|
-
disabled: option.disabled ? true : undefined,
|
|
5961
|
-
onclick: (e) => {
|
|
5962
|
-
e.stopPropagation();
|
|
5963
|
-
},
|
|
5964
|
-
}), m('span', option.label))
|
|
5965
|
-
: m('span', option.label),
|
|
5966
|
-
].filter(Boolean)),
|
|
5967
|
-
])),
|
|
5968
|
-
// Render grouped options
|
|
5969
|
-
Object.entries(options
|
|
5970
|
-
.filter((option) => option.group)
|
|
5971
|
-
.reduce((groups, option) => {
|
|
5972
|
-
const group = option.group;
|
|
5973
|
-
if (!groups[group])
|
|
5974
|
-
groups[group] = [];
|
|
5975
|
-
groups[group].push(option);
|
|
5976
|
-
return groups;
|
|
5977
|
-
}, {}))
|
|
5978
|
-
.map(([groupName, groupOptions]) => [
|
|
5979
|
-
m('li.optgroup', { key: `group-${groupName}`, tabindex: 0 }, m('span', groupName)),
|
|
5980
|
-
...groupOptions.map((option) => m('li', Object.assign({ key: option.id, class: `optgroup-option${option.disabled ? ' disabled' : ''}${isSelected(option.id, selectedIds) ? ' selected' : ''}${state.focusedIndex === options.indexOf(option) ? ' focused' : ''}` }, (option.disabled
|
|
5981
|
-
? {}
|
|
5982
|
-
: {
|
|
5983
|
-
onclick: (e) => {
|
|
5984
|
-
e.stopPropagation();
|
|
5985
|
-
toggleOption(option.id, multiple, attrs);
|
|
5986
|
-
},
|
|
5987
|
-
})), [
|
|
5988
|
-
option.img && m('img', { src: option.img, alt: option.label }),
|
|
5989
|
-
m('span', [
|
|
5990
|
-
multiple
|
|
5991
|
-
? m('label', { for: option.id }, m('input', {
|
|
5992
|
-
id: option.id,
|
|
5993
|
-
type: 'checkbox',
|
|
5994
|
-
checked: selectedIds.includes(option.id),
|
|
5995
|
-
disabled: option.disabled ? true : undefined,
|
|
5996
|
-
onclick: (e) => {
|
|
5997
|
-
e.stopPropagation();
|
|
5998
|
-
},
|
|
5999
|
-
}), m('span', option.label))
|
|
6000
|
-
: m('span', option.label),
|
|
6001
|
-
].filter(Boolean)),
|
|
6002
|
-
])),
|
|
6003
|
-
])
|
|
6004
|
-
.reduce((acc, val) => acc.concat(val), []),
|
|
6005
|
-
]),
|
|
6073
|
+
}, renderDropdownContent(attrs, selectedIds, multiple, placeholder)),
|
|
6006
6074
|
m(MaterialIcon, {
|
|
6007
6075
|
name: 'caret',
|
|
6008
6076
|
direction: 'down',
|
package/dist/index.umd.js
CHANGED
|
@@ -2815,6 +2815,7 @@
|
|
|
2815
2815
|
height: undefined,
|
|
2816
2816
|
active: false,
|
|
2817
2817
|
textarea: undefined,
|
|
2818
|
+
hiddenDiv: undefined,
|
|
2818
2819
|
internalValue: '',
|
|
2819
2820
|
};
|
|
2820
2821
|
const updateHeight = (textarea, hiddenDiv) => {
|
|
@@ -2911,13 +2912,13 @@
|
|
|
2911
2912
|
overflowWrap: 'break-word',
|
|
2912
2913
|
},
|
|
2913
2914
|
oncreate: ({ dom }) => {
|
|
2914
|
-
const hiddenDiv = dom;
|
|
2915
|
+
const hiddenDiv = state.hiddenDiv = dom;
|
|
2915
2916
|
if (state.textarea) {
|
|
2916
2917
|
updateHeight(state.textarea, hiddenDiv);
|
|
2917
2918
|
}
|
|
2918
2919
|
},
|
|
2919
2920
|
onupdate: ({ dom }) => {
|
|
2920
|
-
const hiddenDiv = dom;
|
|
2921
|
+
const hiddenDiv = state.hiddenDiv = dom;
|
|
2921
2922
|
if (state.textarea) {
|
|
2922
2923
|
updateHeight(state.textarea, hiddenDiv);
|
|
2923
2924
|
}
|
|
@@ -2942,7 +2943,10 @@
|
|
|
2942
2943
|
const textarea = dom;
|
|
2943
2944
|
if (state.height)
|
|
2944
2945
|
textarea.style.height = state.height;
|
|
2945
|
-
//
|
|
2946
|
+
// Trigger height recalculation when value changes programmatically
|
|
2947
|
+
if (state.hiddenDiv) {
|
|
2948
|
+
updateHeight(textarea, state.hiddenDiv);
|
|
2949
|
+
}
|
|
2946
2950
|
}, onfocus: () => {
|
|
2947
2951
|
state.active = true;
|
|
2948
2952
|
}, oninput: (e) => {
|
|
@@ -3102,9 +3106,6 @@
|
|
|
3102
3106
|
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"]);
|
|
3103
3107
|
// const attributes = toAttrs(params);
|
|
3104
3108
|
const cn = [newRow ? 'clear' : '', defaultClass, className].filter(Boolean).join(' ').trim() || undefined;
|
|
3105
|
-
const isActive = state.active || ((_a = state.inputElement) === null || _a === void 0 ? void 0 : _a.value) || placeholder || type === 'color' || type === 'range'
|
|
3106
|
-
? true
|
|
3107
|
-
: false;
|
|
3108
3109
|
// Special rendering for minmax range sliders
|
|
3109
3110
|
if (type === 'range' && (attrs.minmax || attrs.valueDisplay)) {
|
|
3110
3111
|
return m(attrs.minmax ? DoubleRangeSlider : SingleRangeSlider, Object.assign(Object.assign({}, attrs), { state,
|
|
@@ -3125,12 +3126,15 @@
|
|
|
3125
3126
|
}
|
|
3126
3127
|
else if (isNonInteractive) {
|
|
3127
3128
|
// Non-interactive components: prefer defaultValue, fallback to value
|
|
3128
|
-
value = ((
|
|
3129
|
+
value = ((_b = (_a = attrs.defaultValue) !== null && _a !== void 0 ? _a : attrs.value) !== null && _b !== void 0 ? _b : (isNumeric ? 0 : ''));
|
|
3129
3130
|
}
|
|
3130
3131
|
else {
|
|
3131
3132
|
// Interactive uncontrolled: use internal state
|
|
3132
|
-
value = ((
|
|
3133
|
+
value = ((_d = (_c = state.internalValue) !== null && _c !== void 0 ? _c : attrs.defaultValue) !== null && _d !== void 0 ? _d : (isNumeric ? 0 : ''));
|
|
3133
3134
|
}
|
|
3135
|
+
const isActive = state.active || ((_e = state.inputElement) === null || _e === void 0 ? void 0 : _e.value) || value || placeholder || type === 'color' || type === 'range'
|
|
3136
|
+
? true
|
|
3137
|
+
: false;
|
|
3134
3138
|
const rangeType = type === 'range' && !attrs.minmax;
|
|
3135
3139
|
return m('.input-field', { className: cn, style }, [
|
|
3136
3140
|
iconName ? m('i.material-icons.prefix', iconName) : undefined,
|
|
@@ -5756,11 +5760,13 @@
|
|
|
5756
5760
|
const Select = () => {
|
|
5757
5761
|
const state = {
|
|
5758
5762
|
id: '',
|
|
5763
|
+
dropdownId: '',
|
|
5759
5764
|
isOpen: false,
|
|
5760
5765
|
focusedIndex: -1,
|
|
5761
5766
|
inputRef: null,
|
|
5762
5767
|
dropdownRef: null,
|
|
5763
5768
|
internalSelectedIds: [],
|
|
5769
|
+
isInsideModal: false,
|
|
5764
5770
|
};
|
|
5765
5771
|
const isControlled = (attrs) => attrs.checkedId !== undefined && attrs.onchange !== undefined;
|
|
5766
5772
|
const isSelected = (id, selectedIds) => {
|
|
@@ -5849,9 +5855,124 @@
|
|
|
5849
5855
|
m.redraw();
|
|
5850
5856
|
}
|
|
5851
5857
|
};
|
|
5858
|
+
const getPortalStyles = (inputRef) => {
|
|
5859
|
+
if (!inputRef)
|
|
5860
|
+
return {};
|
|
5861
|
+
const rect = inputRef.getBoundingClientRect();
|
|
5862
|
+
const viewportHeight = window.innerHeight;
|
|
5863
|
+
return {
|
|
5864
|
+
position: 'fixed',
|
|
5865
|
+
top: `${rect.bottom}px`,
|
|
5866
|
+
left: `${rect.left}px`,
|
|
5867
|
+
width: `${rect.width}px`,
|
|
5868
|
+
zIndex: 10000, // Higher than modal z-index
|
|
5869
|
+
maxHeight: `${viewportHeight - rect.bottom - 20}px`, // Leave 20px margin from bottom
|
|
5870
|
+
};
|
|
5871
|
+
};
|
|
5872
|
+
const renderDropdownContent = (attrs, selectedIds, multiple, placeholder) => [
|
|
5873
|
+
placeholder && m('li.disabled', { tabindex: 0 }, m('span', placeholder)),
|
|
5874
|
+
// Render ungrouped options first
|
|
5875
|
+
attrs.options
|
|
5876
|
+
.filter((option) => !option.group)
|
|
5877
|
+
.map((option) => m('li', Object.assign({ key: option.id, class: option.disabled
|
|
5878
|
+
? 'disabled'
|
|
5879
|
+
: state.focusedIndex === attrs.options.indexOf(option)
|
|
5880
|
+
? 'focused'
|
|
5881
|
+
: '' }, (option.disabled
|
|
5882
|
+
? {}
|
|
5883
|
+
: {
|
|
5884
|
+
onclick: (e) => {
|
|
5885
|
+
e.stopPropagation();
|
|
5886
|
+
toggleOption(option.id, multiple, attrs);
|
|
5887
|
+
},
|
|
5888
|
+
})), [
|
|
5889
|
+
option.img && m('img', { src: option.img, alt: option.label }),
|
|
5890
|
+
m('span', [
|
|
5891
|
+
multiple
|
|
5892
|
+
? m('label', { for: option.id }, m('input', {
|
|
5893
|
+
id: option.id,
|
|
5894
|
+
type: 'checkbox',
|
|
5895
|
+
checked: selectedIds.includes(option.id),
|
|
5896
|
+
disabled: option.disabled ? true : undefined,
|
|
5897
|
+
onclick: (e) => {
|
|
5898
|
+
e.stopPropagation();
|
|
5899
|
+
},
|
|
5900
|
+
}), m('span', option.label))
|
|
5901
|
+
: m('span', option.label),
|
|
5902
|
+
].filter(Boolean)),
|
|
5903
|
+
])),
|
|
5904
|
+
// Render grouped options
|
|
5905
|
+
Object.entries(attrs.options
|
|
5906
|
+
.filter((option) => option.group)
|
|
5907
|
+
.reduce((groups, option) => {
|
|
5908
|
+
const group = option.group;
|
|
5909
|
+
if (!groups[group])
|
|
5910
|
+
groups[group] = [];
|
|
5911
|
+
groups[group].push(option);
|
|
5912
|
+
return groups;
|
|
5913
|
+
}, {}))
|
|
5914
|
+
.map(([groupName, groupOptions]) => [
|
|
5915
|
+
m('li.optgroup', { key: `group-${groupName}`, tabindex: 0 }, m('span', groupName)),
|
|
5916
|
+
...groupOptions.map((option) => m('li', Object.assign({ key: option.id, class: `optgroup-option${option.disabled ? ' disabled' : ''}${isSelected(option.id, selectedIds) ? ' selected' : ''}${state.focusedIndex === attrs.options.indexOf(option) ? ' focused' : ''}` }, (option.disabled
|
|
5917
|
+
? {}
|
|
5918
|
+
: {
|
|
5919
|
+
onclick: (e) => {
|
|
5920
|
+
e.stopPropagation();
|
|
5921
|
+
toggleOption(option.id, multiple, attrs);
|
|
5922
|
+
},
|
|
5923
|
+
})), [
|
|
5924
|
+
option.img && m('img', { src: option.img, alt: option.label }),
|
|
5925
|
+
m('span', [
|
|
5926
|
+
multiple
|
|
5927
|
+
? m('label', { for: option.id }, m('input', {
|
|
5928
|
+
id: option.id,
|
|
5929
|
+
type: 'checkbox',
|
|
5930
|
+
checked: selectedIds.includes(option.id),
|
|
5931
|
+
disabled: option.disabled ? true : undefined,
|
|
5932
|
+
onclick: (e) => {
|
|
5933
|
+
e.stopPropagation();
|
|
5934
|
+
},
|
|
5935
|
+
}), m('span', option.label))
|
|
5936
|
+
: m('span', option.label),
|
|
5937
|
+
].filter(Boolean)),
|
|
5938
|
+
])),
|
|
5939
|
+
])
|
|
5940
|
+
.reduce((acc, val) => acc.concat(val), []),
|
|
5941
|
+
];
|
|
5942
|
+
const updatePortalDropdown = (attrs, selectedIds, multiple, placeholder) => {
|
|
5943
|
+
var _a;
|
|
5944
|
+
if (!state.isInsideModal)
|
|
5945
|
+
return;
|
|
5946
|
+
let portalElement = document.getElementById(state.dropdownId);
|
|
5947
|
+
if (!state.isOpen) {
|
|
5948
|
+
// Clean up portal when dropdown is closed
|
|
5949
|
+
if (portalElement) {
|
|
5950
|
+
m.render(portalElement, []);
|
|
5951
|
+
(_a = portalElement.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(portalElement);
|
|
5952
|
+
}
|
|
5953
|
+
return;
|
|
5954
|
+
}
|
|
5955
|
+
if (!portalElement) {
|
|
5956
|
+
portalElement = document.createElement('div');
|
|
5957
|
+
portalElement.id = state.dropdownId;
|
|
5958
|
+
document.body.appendChild(portalElement);
|
|
5959
|
+
}
|
|
5960
|
+
const dropdownVnode = m('ul.dropdown-content.select-dropdown', {
|
|
5961
|
+
tabindex: 0,
|
|
5962
|
+
style: getPortalStyles(state.inputRef),
|
|
5963
|
+
oncreate: ({ dom }) => {
|
|
5964
|
+
state.dropdownRef = dom;
|
|
5965
|
+
},
|
|
5966
|
+
onremove: () => {
|
|
5967
|
+
state.dropdownRef = null;
|
|
5968
|
+
},
|
|
5969
|
+
}, renderDropdownContent(attrs, selectedIds, multiple, placeholder));
|
|
5970
|
+
m.render(portalElement, dropdownVnode);
|
|
5971
|
+
};
|
|
5852
5972
|
return {
|
|
5853
5973
|
oninit: ({ attrs }) => {
|
|
5854
5974
|
state.id = attrs.id || uniqueId();
|
|
5975
|
+
state.dropdownId = `${state.id}-dropdown`;
|
|
5855
5976
|
const controlled = isControlled(attrs);
|
|
5856
5977
|
// Warn developer for improper controlled usage
|
|
5857
5978
|
if (attrs.checkedId !== undefined && !controlled && !attrs.disabled) {
|
|
@@ -5870,9 +5991,20 @@
|
|
|
5870
5991
|
// Add global click listener to close dropdown
|
|
5871
5992
|
document.addEventListener('click', closeDropdown);
|
|
5872
5993
|
},
|
|
5994
|
+
oncreate: ({ dom }) => {
|
|
5995
|
+
// Detect if component is inside a modal
|
|
5996
|
+
state.isInsideModal = !!dom.closest('.modal');
|
|
5997
|
+
},
|
|
5873
5998
|
onremove: () => {
|
|
5874
5999
|
// Cleanup global listener
|
|
5875
6000
|
document.removeEventListener('click', closeDropdown);
|
|
6001
|
+
// Cleanup portaled dropdown if it exists
|
|
6002
|
+
if (state.isInsideModal && state.dropdownRef) {
|
|
6003
|
+
const portalElement = document.getElementById(state.dropdownId);
|
|
6004
|
+
if (portalElement && portalElement.parentNode) {
|
|
6005
|
+
portalElement.parentNode.removeChild(portalElement);
|
|
6006
|
+
}
|
|
6007
|
+
}
|
|
5876
6008
|
},
|
|
5877
6009
|
view: ({ attrs }) => {
|
|
5878
6010
|
var _a;
|
|
@@ -5896,6 +6028,10 @@
|
|
|
5896
6028
|
const { newRow, className = 'col s12', key, options, multiple = false, label, helperText, placeholder = '', isMandatory, iconName, style, } = attrs;
|
|
5897
6029
|
const finalClassName = newRow ? `${className} clear` : className;
|
|
5898
6030
|
const selectedOptions = options.filter((opt) => isSelected(opt.id, selectedIds));
|
|
6031
|
+
// Update portal dropdown when inside modal
|
|
6032
|
+
if (state.isInsideModal) {
|
|
6033
|
+
updatePortalDropdown(attrs, selectedIds, multiple, placeholder);
|
|
6034
|
+
}
|
|
5899
6035
|
return m('.input-field.select-space', {
|
|
5900
6036
|
className: finalClassName,
|
|
5901
6037
|
key,
|
|
@@ -5908,6 +6044,7 @@
|
|
|
5908
6044
|
tabindex: disabled ? -1 : 0,
|
|
5909
6045
|
'aria-expanded': state.isOpen ? 'true' : 'false',
|
|
5910
6046
|
'aria-haspopup': 'listbox',
|
|
6047
|
+
'aria-controls': state.dropdownId,
|
|
5911
6048
|
role: 'combobox',
|
|
5912
6049
|
}, [
|
|
5913
6050
|
m('input[type=text][readonly=true].select-dropdown.dropdown-trigger', {
|
|
@@ -5924,8 +6061,8 @@
|
|
|
5924
6061
|
}
|
|
5925
6062
|
},
|
|
5926
6063
|
}),
|
|
5927
|
-
// Dropdown Menu
|
|
5928
|
-
state.isOpen &&
|
|
6064
|
+
// Dropdown Menu - render inline only when NOT inside modal
|
|
6065
|
+
state.isOpen && !state.isInsideModal &&
|
|
5929
6066
|
m('ul.dropdown-content.select-dropdown', {
|
|
5930
6067
|
tabindex: 0,
|
|
5931
6068
|
oncreate: ({ dom }) => {
|
|
@@ -5935,76 +6072,7 @@
|
|
|
5935
6072
|
state.dropdownRef = null;
|
|
5936
6073
|
},
|
|
5937
6074
|
style: getDropdownStyles(state.inputRef, true, options),
|
|
5938
|
-
},
|
|
5939
|
-
placeholder && m('li.disabled', { tabindex: 0 }, m('span', placeholder)),
|
|
5940
|
-
// Render ungrouped options first
|
|
5941
|
-
options
|
|
5942
|
-
.filter((option) => !option.group)
|
|
5943
|
-
.map((option) => m('li', Object.assign({ key: option.id, class: option.disabled
|
|
5944
|
-
? 'disabled'
|
|
5945
|
-
: state.focusedIndex === options.indexOf(option)
|
|
5946
|
-
? 'focused'
|
|
5947
|
-
: '' }, (option.disabled
|
|
5948
|
-
? {}
|
|
5949
|
-
: {
|
|
5950
|
-
onclick: (e) => {
|
|
5951
|
-
e.stopPropagation();
|
|
5952
|
-
toggleOption(option.id, multiple, attrs);
|
|
5953
|
-
},
|
|
5954
|
-
})), [
|
|
5955
|
-
option.img && m('img', { src: option.img, alt: option.label }),
|
|
5956
|
-
m('span', [
|
|
5957
|
-
multiple
|
|
5958
|
-
? m('label', { for: option.id }, m('input', {
|
|
5959
|
-
id: option.id,
|
|
5960
|
-
type: 'checkbox',
|
|
5961
|
-
checked: selectedIds.includes(option.id),
|
|
5962
|
-
disabled: option.disabled ? true : undefined,
|
|
5963
|
-
onclick: (e) => {
|
|
5964
|
-
e.stopPropagation();
|
|
5965
|
-
},
|
|
5966
|
-
}), m('span', option.label))
|
|
5967
|
-
: m('span', option.label),
|
|
5968
|
-
].filter(Boolean)),
|
|
5969
|
-
])),
|
|
5970
|
-
// Render grouped options
|
|
5971
|
-
Object.entries(options
|
|
5972
|
-
.filter((option) => option.group)
|
|
5973
|
-
.reduce((groups, option) => {
|
|
5974
|
-
const group = option.group;
|
|
5975
|
-
if (!groups[group])
|
|
5976
|
-
groups[group] = [];
|
|
5977
|
-
groups[group].push(option);
|
|
5978
|
-
return groups;
|
|
5979
|
-
}, {}))
|
|
5980
|
-
.map(([groupName, groupOptions]) => [
|
|
5981
|
-
m('li.optgroup', { key: `group-${groupName}`, tabindex: 0 }, m('span', groupName)),
|
|
5982
|
-
...groupOptions.map((option) => m('li', Object.assign({ key: option.id, class: `optgroup-option${option.disabled ? ' disabled' : ''}${isSelected(option.id, selectedIds) ? ' selected' : ''}${state.focusedIndex === options.indexOf(option) ? ' focused' : ''}` }, (option.disabled
|
|
5983
|
-
? {}
|
|
5984
|
-
: {
|
|
5985
|
-
onclick: (e) => {
|
|
5986
|
-
e.stopPropagation();
|
|
5987
|
-
toggleOption(option.id, multiple, attrs);
|
|
5988
|
-
},
|
|
5989
|
-
})), [
|
|
5990
|
-
option.img && m('img', { src: option.img, alt: option.label }),
|
|
5991
|
-
m('span', [
|
|
5992
|
-
multiple
|
|
5993
|
-
? m('label', { for: option.id }, m('input', {
|
|
5994
|
-
id: option.id,
|
|
5995
|
-
type: 'checkbox',
|
|
5996
|
-
checked: selectedIds.includes(option.id),
|
|
5997
|
-
disabled: option.disabled ? true : undefined,
|
|
5998
|
-
onclick: (e) => {
|
|
5999
|
-
e.stopPropagation();
|
|
6000
|
-
},
|
|
6001
|
-
}), m('span', option.label))
|
|
6002
|
-
: m('span', option.label),
|
|
6003
|
-
].filter(Boolean)),
|
|
6004
|
-
])),
|
|
6005
|
-
])
|
|
6006
|
-
.reduce((acc, val) => acc.concat(val), []),
|
|
6007
|
-
]),
|
|
6075
|
+
}, renderDropdownContent(attrs, selectedIds, multiple, placeholder)),
|
|
6008
6076
|
m(MaterialIcon, {
|
|
6009
6077
|
name: 'caret',
|
|
6010
6078
|
direction: 'down',
|