mithril-materialized 3.3.7 → 3.4.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/dist/button.d.ts +1 -0
- package/dist/components.css +22 -0
- package/dist/forms.css +8 -0
- package/dist/index.css +22 -0
- package/dist/index.esm.js +270 -89
- package/dist/index.js +270 -88
- package/dist/index.min.css +2 -2
- package/dist/index.umd.js +270 -88
- package/dist/waves.d.ts +16 -0
- package/package.json +1 -1
- package/sass/components/_buttons.scss +21 -0
- package/sass/components/_theme-switcher.scss +10 -0
package/dist/index.js
CHANGED
|
@@ -434,6 +434,103 @@ const Icon = () => ({
|
|
|
434
434
|
},
|
|
435
435
|
});
|
|
436
436
|
|
|
437
|
+
/*!
|
|
438
|
+
* Waves Effect for Mithril Materialized
|
|
439
|
+
* Based on Waves v0.6.4 by Alfiana E. Sibuea
|
|
440
|
+
* Adapted for TypeScript and Mithril integration
|
|
441
|
+
*/
|
|
442
|
+
class WavesEffect {
|
|
443
|
+
static offset(elem) {
|
|
444
|
+
const rect = elem.getBoundingClientRect();
|
|
445
|
+
return {
|
|
446
|
+
top: rect.top + window.pageYOffset,
|
|
447
|
+
left: rect.left + window.pageXOffset
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
static createRipple(e, element) {
|
|
451
|
+
// Disable right click
|
|
452
|
+
if (e.button === 2) {
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
// Create ripple element
|
|
456
|
+
const ripple = document.createElement('div');
|
|
457
|
+
ripple.className = 'waves-ripple';
|
|
458
|
+
// Get click position relative to element
|
|
459
|
+
const pos = this.offset(element);
|
|
460
|
+
const relativeY = e.pageY - pos.top;
|
|
461
|
+
const relativeX = e.pageX - pos.left;
|
|
462
|
+
// Calculate scale based on element size
|
|
463
|
+
const scale = (element.clientWidth / 100) * 10;
|
|
464
|
+
// Set initial ripple position and style
|
|
465
|
+
ripple.style.cssText = `
|
|
466
|
+
top: ${relativeY}px;
|
|
467
|
+
left: ${relativeX}px;
|
|
468
|
+
transform: scale(0);
|
|
469
|
+
opacity: 1;
|
|
470
|
+
`;
|
|
471
|
+
// Add ripple to element
|
|
472
|
+
element.appendChild(ripple);
|
|
473
|
+
// Force reflow and animate
|
|
474
|
+
ripple.offsetHeight;
|
|
475
|
+
ripple.style.transform = `scale(${scale})`;
|
|
476
|
+
ripple.style.opacity = '1';
|
|
477
|
+
// Store reference for cleanup
|
|
478
|
+
ripple.setAttribute('data-created', Date.now().toString());
|
|
479
|
+
}
|
|
480
|
+
static removeRipples(element) {
|
|
481
|
+
const ripples = element.querySelectorAll('.waves-ripple');
|
|
482
|
+
ripples.forEach((ripple) => {
|
|
483
|
+
const created = parseInt(ripple.getAttribute('data-created') || '0');
|
|
484
|
+
const age = Date.now() - created;
|
|
485
|
+
const fadeOut = () => {
|
|
486
|
+
ripple.style.opacity = '0';
|
|
487
|
+
setTimeout(() => {
|
|
488
|
+
if (ripple.parentNode) {
|
|
489
|
+
ripple.parentNode.removeChild(ripple);
|
|
490
|
+
}
|
|
491
|
+
}, this.duration);
|
|
492
|
+
};
|
|
493
|
+
if (age >= 350) {
|
|
494
|
+
fadeOut();
|
|
495
|
+
}
|
|
496
|
+
else {
|
|
497
|
+
setTimeout(fadeOut, 350 - age);
|
|
498
|
+
}
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
WavesEffect.duration = 750;
|
|
503
|
+
WavesEffect.onMouseDown = (e) => {
|
|
504
|
+
const element = e.currentTarget;
|
|
505
|
+
if (element && element.classList.contains('waves-effect')) {
|
|
506
|
+
WavesEffect.createRipple(e, element);
|
|
507
|
+
}
|
|
508
|
+
};
|
|
509
|
+
WavesEffect.onMouseUp = (e) => {
|
|
510
|
+
const element = e.currentTarget;
|
|
511
|
+
if (element && element.classList.contains('waves-effect')) {
|
|
512
|
+
WavesEffect.removeRipples(element);
|
|
513
|
+
}
|
|
514
|
+
};
|
|
515
|
+
WavesEffect.onMouseLeave = (e) => {
|
|
516
|
+
const element = e.currentTarget;
|
|
517
|
+
if (element && element.classList.contains('waves-effect')) {
|
|
518
|
+
WavesEffect.removeRipples(element);
|
|
519
|
+
}
|
|
520
|
+
};
|
|
521
|
+
WavesEffect.onTouchStart = (e) => {
|
|
522
|
+
const element = e.currentTarget;
|
|
523
|
+
if (element && element.classList.contains('waves-effect')) {
|
|
524
|
+
WavesEffect.createRipple(e, element);
|
|
525
|
+
}
|
|
526
|
+
};
|
|
527
|
+
WavesEffect.onTouchEnd = (e) => {
|
|
528
|
+
const element = e.currentTarget;
|
|
529
|
+
if (element && element.classList.contains('waves-effect')) {
|
|
530
|
+
WavesEffect.removeRipples(element);
|
|
531
|
+
}
|
|
532
|
+
};
|
|
533
|
+
|
|
437
534
|
/**
|
|
438
535
|
* A factory to create new buttons.
|
|
439
536
|
*
|
|
@@ -447,13 +544,18 @@ const ButtonFactory = (element, defaultClassNames, type = '') => {
|
|
|
447
544
|
iconName, iconClass, label, className, variant } = attrs, params = __rest(attrs, ["tooltip", "tooltipPosition", "tooltipPostion", "iconName", "iconClass", "label", "className", "variant"]);
|
|
448
545
|
// Use variant or fallback to factory type
|
|
449
546
|
const buttonType = variant || type || 'button';
|
|
450
|
-
const cn = [tooltip ? 'tooltipped' : '', defaultClassNames, className]
|
|
451
|
-
.filter(Boolean)
|
|
452
|
-
.join(' ')
|
|
453
|
-
.trim();
|
|
547
|
+
const cn = [tooltip ? 'tooltipped' : '', defaultClassNames, className].filter(Boolean).join(' ').trim();
|
|
454
548
|
// Use tooltipPosition if available, fallback to legacy tooltipPostion
|
|
455
549
|
const position = tooltipPosition || tooltipPostion || 'top';
|
|
456
|
-
|
|
550
|
+
// Add waves effect event handlers if waves-effect class is present
|
|
551
|
+
const wavesHandlers = cn.includes('waves-effect') ? {
|
|
552
|
+
onmousedown: WavesEffect.onMouseDown,
|
|
553
|
+
onmouseup: WavesEffect.onMouseUp,
|
|
554
|
+
onmouseleave: WavesEffect.onMouseLeave,
|
|
555
|
+
ontouchstart: WavesEffect.onTouchStart,
|
|
556
|
+
ontouchend: WavesEffect.onTouchEnd
|
|
557
|
+
} : {};
|
|
558
|
+
return m(element, Object.assign(Object.assign(Object.assign({}, params), wavesHandlers), { className: cn, 'data-position': tooltip ? position : undefined, 'data-tooltip': tooltip || undefined, type: buttonType }), iconName ? m(Icon, { iconName, className: iconClass || 'left' }) : undefined, label ? label : undefined);
|
|
457
559
|
},
|
|
458
560
|
};
|
|
459
561
|
};
|
|
@@ -462,6 +564,7 @@ const Button = ButtonFactory('a', 'waves-effect waves-light btn', 'button');
|
|
|
462
564
|
const LargeButton = ButtonFactory('a', 'waves-effect waves-light btn-large', 'button');
|
|
463
565
|
const SmallButton = ButtonFactory('a', 'waves-effect waves-light btn-small', 'button');
|
|
464
566
|
const FlatButton = ButtonFactory('a', 'waves-effect waves-teal btn-flat', 'button');
|
|
567
|
+
const IconButton = ButtonFactory('button', 'btn-flat btn-icon waves-effect waves-teal', 'button');
|
|
465
568
|
const RoundIconButton = ButtonFactory('button', 'btn-floating btn-large waves-effect waves-light', 'button');
|
|
466
569
|
const SubmitButton = ButtonFactory('button', 'btn waves-effect waves-light', 'submit');
|
|
467
570
|
|
|
@@ -898,7 +1001,7 @@ const MaterialIcon = () => {
|
|
|
898
1001
|
};
|
|
899
1002
|
const rotation = (_a = rotationMap[direction]) !== null && _a !== void 0 ? _a : 0;
|
|
900
1003
|
const transform = rotation ? `rotate(${rotation}deg)` : undefined;
|
|
901
|
-
return m('svg', Object.assign(Object.assign({}, props), { style: Object.assign({ transform }, style), height: '
|
|
1004
|
+
return m('svg', Object.assign(Object.assign({}, props), { style: Object.assign({ transform }, style), height: '24px', width: '24px', viewBox: '0 0 24 24', xmlns: 'http://www.w3.org/2000/svg' }), iconPaths[name].map((d) => m('path', {
|
|
902
1005
|
d,
|
|
903
1006
|
fill: d.includes('M0 0h24v24H0z') ? 'none' : 'currentColor',
|
|
904
1007
|
})));
|
|
@@ -2813,6 +2916,7 @@ const TextArea = () => {
|
|
|
2813
2916
|
height: undefined,
|
|
2814
2917
|
active: false,
|
|
2815
2918
|
textarea: undefined,
|
|
2919
|
+
hiddenDiv: undefined,
|
|
2816
2920
|
internalValue: '',
|
|
2817
2921
|
};
|
|
2818
2922
|
const updateHeight = (textarea, hiddenDiv) => {
|
|
@@ -2909,13 +3013,13 @@ const TextArea = () => {
|
|
|
2909
3013
|
overflowWrap: 'break-word',
|
|
2910
3014
|
},
|
|
2911
3015
|
oncreate: ({ dom }) => {
|
|
2912
|
-
const hiddenDiv = dom;
|
|
3016
|
+
const hiddenDiv = state.hiddenDiv = dom;
|
|
2913
3017
|
if (state.textarea) {
|
|
2914
3018
|
updateHeight(state.textarea, hiddenDiv);
|
|
2915
3019
|
}
|
|
2916
3020
|
},
|
|
2917
3021
|
onupdate: ({ dom }) => {
|
|
2918
|
-
const hiddenDiv = dom;
|
|
3022
|
+
const hiddenDiv = state.hiddenDiv = dom;
|
|
2919
3023
|
if (state.textarea) {
|
|
2920
3024
|
updateHeight(state.textarea, hiddenDiv);
|
|
2921
3025
|
}
|
|
@@ -2940,7 +3044,10 @@ const TextArea = () => {
|
|
|
2940
3044
|
const textarea = dom;
|
|
2941
3045
|
if (state.height)
|
|
2942
3046
|
textarea.style.height = state.height;
|
|
2943
|
-
//
|
|
3047
|
+
// Trigger height recalculation when value changes programmatically
|
|
3048
|
+
if (state.hiddenDiv) {
|
|
3049
|
+
updateHeight(textarea, state.hiddenDiv);
|
|
3050
|
+
}
|
|
2944
3051
|
}, onfocus: () => {
|
|
2945
3052
|
state.active = true;
|
|
2946
3053
|
}, oninput: (e) => {
|
|
@@ -3100,9 +3207,6 @@ const InputField = (type, defaultClass = '') => () => {
|
|
|
3100
3207
|
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
3208
|
// const attributes = toAttrs(params);
|
|
3102
3209
|
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
3210
|
// Special rendering for minmax range sliders
|
|
3107
3211
|
if (type === 'range' && (attrs.minmax || attrs.valueDisplay)) {
|
|
3108
3212
|
return m(attrs.minmax ? DoubleRangeSlider : SingleRangeSlider, Object.assign(Object.assign({}, attrs), { state,
|
|
@@ -3123,12 +3227,15 @@ const InputField = (type, defaultClass = '') => () => {
|
|
|
3123
3227
|
}
|
|
3124
3228
|
else if (isNonInteractive) {
|
|
3125
3229
|
// Non-interactive components: prefer defaultValue, fallback to value
|
|
3126
|
-
value = ((
|
|
3230
|
+
value = ((_b = (_a = attrs.defaultValue) !== null && _a !== void 0 ? _a : attrs.value) !== null && _b !== void 0 ? _b : (isNumeric ? 0 : ''));
|
|
3127
3231
|
}
|
|
3128
3232
|
else {
|
|
3129
3233
|
// Interactive uncontrolled: use internal state
|
|
3130
|
-
value = ((
|
|
3234
|
+
value = ((_d = (_c = state.internalValue) !== null && _c !== void 0 ? _c : attrs.defaultValue) !== null && _d !== void 0 ? _d : (isNumeric ? 0 : ''));
|
|
3131
3235
|
}
|
|
3236
|
+
const isActive = state.active || ((_e = state.inputElement) === null || _e === void 0 ? void 0 : _e.value) || value || placeholder || type === 'color' || type === 'range'
|
|
3237
|
+
? true
|
|
3238
|
+
: false;
|
|
3132
3239
|
const rangeType = type === 'range' && !attrs.minmax;
|
|
3133
3240
|
return m('.input-field', { className: cn, style }, [
|
|
3134
3241
|
iconName ? m('i.material-icons.prefix', iconName) : undefined,
|
|
@@ -4342,12 +4449,17 @@ const FloatingActionButton = () => {
|
|
|
4342
4449
|
}
|
|
4343
4450
|
: undefined,
|
|
4344
4451
|
}, [
|
|
4345
|
-
m('a.btn-floating.btn-large', {
|
|
4452
|
+
m('a.btn-floating.btn-large.waves-effect.waves-light', {
|
|
4346
4453
|
className,
|
|
4454
|
+
onmousedown: WavesEffect.onMouseDown,
|
|
4455
|
+
onmouseup: WavesEffect.onMouseUp,
|
|
4456
|
+
onmouseleave: WavesEffect.onMouseLeave,
|
|
4457
|
+
ontouchstart: WavesEffect.onTouchStart,
|
|
4458
|
+
ontouchend: WavesEffect.onTouchEnd,
|
|
4347
4459
|
}, m('i.material-icons', { className: iconClass }, iconName)),
|
|
4348
4460
|
buttons &&
|
|
4349
4461
|
buttons.length > 0 &&
|
|
4350
|
-
m('ul', buttons.map((button, index) => m('li', m(`a.btn-floating.${button.className || 'red'}`, {
|
|
4462
|
+
m('ul', buttons.map((button, index) => m('li', m(`a.btn-floating.waves-effect.waves-light.${button.className || 'red'}`, {
|
|
4351
4463
|
style: {
|
|
4352
4464
|
opacity: state.isOpen ? '1' : '0',
|
|
4353
4465
|
transform: state.isOpen ? 'scale(1)' : 'scale(0.4)',
|
|
@@ -4358,6 +4470,11 @@ const FloatingActionButton = () => {
|
|
|
4358
4470
|
if (button.onclick)
|
|
4359
4471
|
button.onclick(e);
|
|
4360
4472
|
},
|
|
4473
|
+
onmousedown: WavesEffect.onMouseDown,
|
|
4474
|
+
onmouseup: WavesEffect.onMouseUp,
|
|
4475
|
+
onmouseleave: WavesEffect.onMouseLeave,
|
|
4476
|
+
ontouchstart: WavesEffect.onTouchStart,
|
|
4477
|
+
ontouchend: WavesEffect.onTouchEnd,
|
|
4361
4478
|
}, m('i.material-icons', { className: button.iconClass }, button.iconName))))),
|
|
4362
4479
|
]));
|
|
4363
4480
|
},
|
|
@@ -5754,11 +5871,13 @@ const RadioButtons = () => {
|
|
|
5754
5871
|
const Select = () => {
|
|
5755
5872
|
const state = {
|
|
5756
5873
|
id: '',
|
|
5874
|
+
dropdownId: '',
|
|
5757
5875
|
isOpen: false,
|
|
5758
5876
|
focusedIndex: -1,
|
|
5759
5877
|
inputRef: null,
|
|
5760
5878
|
dropdownRef: null,
|
|
5761
5879
|
internalSelectedIds: [],
|
|
5880
|
+
isInsideModal: false,
|
|
5762
5881
|
};
|
|
5763
5882
|
const isControlled = (attrs) => attrs.checkedId !== undefined && attrs.onchange !== undefined;
|
|
5764
5883
|
const isSelected = (id, selectedIds) => {
|
|
@@ -5847,9 +5966,124 @@ const Select = () => {
|
|
|
5847
5966
|
m.redraw();
|
|
5848
5967
|
}
|
|
5849
5968
|
};
|
|
5969
|
+
const getPortalStyles = (inputRef) => {
|
|
5970
|
+
if (!inputRef)
|
|
5971
|
+
return {};
|
|
5972
|
+
const rect = inputRef.getBoundingClientRect();
|
|
5973
|
+
const viewportHeight = window.innerHeight;
|
|
5974
|
+
return {
|
|
5975
|
+
position: 'fixed',
|
|
5976
|
+
top: `${rect.bottom}px`,
|
|
5977
|
+
left: `${rect.left}px`,
|
|
5978
|
+
width: `${rect.width}px`,
|
|
5979
|
+
zIndex: 10000, // Higher than modal z-index
|
|
5980
|
+
maxHeight: `${viewportHeight - rect.bottom - 20}px`, // Leave 20px margin from bottom
|
|
5981
|
+
};
|
|
5982
|
+
};
|
|
5983
|
+
const renderDropdownContent = (attrs, selectedIds, multiple, placeholder) => [
|
|
5984
|
+
placeholder && m('li.disabled', { tabindex: 0 }, m('span', placeholder)),
|
|
5985
|
+
// Render ungrouped options first
|
|
5986
|
+
attrs.options
|
|
5987
|
+
.filter((option) => !option.group)
|
|
5988
|
+
.map((option) => m('li', Object.assign({ key: option.id, class: option.disabled
|
|
5989
|
+
? 'disabled'
|
|
5990
|
+
: state.focusedIndex === attrs.options.indexOf(option)
|
|
5991
|
+
? 'focused'
|
|
5992
|
+
: '' }, (option.disabled
|
|
5993
|
+
? {}
|
|
5994
|
+
: {
|
|
5995
|
+
onclick: (e) => {
|
|
5996
|
+
e.stopPropagation();
|
|
5997
|
+
toggleOption(option.id, multiple, attrs);
|
|
5998
|
+
},
|
|
5999
|
+
})), [
|
|
6000
|
+
option.img && m('img', { src: option.img, alt: option.label }),
|
|
6001
|
+
m('span', [
|
|
6002
|
+
multiple
|
|
6003
|
+
? m('label', { for: option.id }, m('input', {
|
|
6004
|
+
id: option.id,
|
|
6005
|
+
type: 'checkbox',
|
|
6006
|
+
checked: selectedIds.includes(option.id),
|
|
6007
|
+
disabled: option.disabled ? true : undefined,
|
|
6008
|
+
onclick: (e) => {
|
|
6009
|
+
e.stopPropagation();
|
|
6010
|
+
},
|
|
6011
|
+
}), m('span', option.label))
|
|
6012
|
+
: m('span', option.label),
|
|
6013
|
+
].filter(Boolean)),
|
|
6014
|
+
])),
|
|
6015
|
+
// Render grouped options
|
|
6016
|
+
Object.entries(attrs.options
|
|
6017
|
+
.filter((option) => option.group)
|
|
6018
|
+
.reduce((groups, option) => {
|
|
6019
|
+
const group = option.group;
|
|
6020
|
+
if (!groups[group])
|
|
6021
|
+
groups[group] = [];
|
|
6022
|
+
groups[group].push(option);
|
|
6023
|
+
return groups;
|
|
6024
|
+
}, {}))
|
|
6025
|
+
.map(([groupName, groupOptions]) => [
|
|
6026
|
+
m('li.optgroup', { key: `group-${groupName}`, tabindex: 0 }, m('span', groupName)),
|
|
6027
|
+
...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
|
|
6028
|
+
? {}
|
|
6029
|
+
: {
|
|
6030
|
+
onclick: (e) => {
|
|
6031
|
+
e.stopPropagation();
|
|
6032
|
+
toggleOption(option.id, multiple, attrs);
|
|
6033
|
+
},
|
|
6034
|
+
})), [
|
|
6035
|
+
option.img && m('img', { src: option.img, alt: option.label }),
|
|
6036
|
+
m('span', [
|
|
6037
|
+
multiple
|
|
6038
|
+
? m('label', { for: option.id }, m('input', {
|
|
6039
|
+
id: option.id,
|
|
6040
|
+
type: 'checkbox',
|
|
6041
|
+
checked: selectedIds.includes(option.id),
|
|
6042
|
+
disabled: option.disabled ? true : undefined,
|
|
6043
|
+
onclick: (e) => {
|
|
6044
|
+
e.stopPropagation();
|
|
6045
|
+
},
|
|
6046
|
+
}), m('span', option.label))
|
|
6047
|
+
: m('span', option.label),
|
|
6048
|
+
].filter(Boolean)),
|
|
6049
|
+
])),
|
|
6050
|
+
])
|
|
6051
|
+
.reduce((acc, val) => acc.concat(val), []),
|
|
6052
|
+
];
|
|
6053
|
+
const updatePortalDropdown = (attrs, selectedIds, multiple, placeholder) => {
|
|
6054
|
+
var _a;
|
|
6055
|
+
if (!state.isInsideModal)
|
|
6056
|
+
return;
|
|
6057
|
+
let portalElement = document.getElementById(state.dropdownId);
|
|
6058
|
+
if (!state.isOpen) {
|
|
6059
|
+
// Clean up portal when dropdown is closed
|
|
6060
|
+
if (portalElement) {
|
|
6061
|
+
m.render(portalElement, []);
|
|
6062
|
+
(_a = portalElement.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(portalElement);
|
|
6063
|
+
}
|
|
6064
|
+
return;
|
|
6065
|
+
}
|
|
6066
|
+
if (!portalElement) {
|
|
6067
|
+
portalElement = document.createElement('div');
|
|
6068
|
+
portalElement.id = state.dropdownId;
|
|
6069
|
+
document.body.appendChild(portalElement);
|
|
6070
|
+
}
|
|
6071
|
+
const dropdownVnode = m('ul.dropdown-content.select-dropdown', {
|
|
6072
|
+
tabindex: 0,
|
|
6073
|
+
style: getPortalStyles(state.inputRef),
|
|
6074
|
+
oncreate: ({ dom }) => {
|
|
6075
|
+
state.dropdownRef = dom;
|
|
6076
|
+
},
|
|
6077
|
+
onremove: () => {
|
|
6078
|
+
state.dropdownRef = null;
|
|
6079
|
+
},
|
|
6080
|
+
}, renderDropdownContent(attrs, selectedIds, multiple, placeholder));
|
|
6081
|
+
m.render(portalElement, dropdownVnode);
|
|
6082
|
+
};
|
|
5850
6083
|
return {
|
|
5851
6084
|
oninit: ({ attrs }) => {
|
|
5852
6085
|
state.id = attrs.id || uniqueId();
|
|
6086
|
+
state.dropdownId = `${state.id}-dropdown`;
|
|
5853
6087
|
const controlled = isControlled(attrs);
|
|
5854
6088
|
// Warn developer for improper controlled usage
|
|
5855
6089
|
if (attrs.checkedId !== undefined && !controlled && !attrs.disabled) {
|
|
@@ -5868,9 +6102,20 @@ const Select = () => {
|
|
|
5868
6102
|
// Add global click listener to close dropdown
|
|
5869
6103
|
document.addEventListener('click', closeDropdown);
|
|
5870
6104
|
},
|
|
6105
|
+
oncreate: ({ dom }) => {
|
|
6106
|
+
// Detect if component is inside a modal
|
|
6107
|
+
state.isInsideModal = !!dom.closest('.modal');
|
|
6108
|
+
},
|
|
5871
6109
|
onremove: () => {
|
|
5872
6110
|
// Cleanup global listener
|
|
5873
6111
|
document.removeEventListener('click', closeDropdown);
|
|
6112
|
+
// Cleanup portaled dropdown if it exists
|
|
6113
|
+
if (state.isInsideModal && state.dropdownRef) {
|
|
6114
|
+
const portalElement = document.getElementById(state.dropdownId);
|
|
6115
|
+
if (portalElement && portalElement.parentNode) {
|
|
6116
|
+
portalElement.parentNode.removeChild(portalElement);
|
|
6117
|
+
}
|
|
6118
|
+
}
|
|
5874
6119
|
},
|
|
5875
6120
|
view: ({ attrs }) => {
|
|
5876
6121
|
var _a;
|
|
@@ -5894,6 +6139,10 @@ const Select = () => {
|
|
|
5894
6139
|
const { newRow, className = 'col s12', key, options, multiple = false, label, helperText, placeholder = '', isMandatory, iconName, style, } = attrs;
|
|
5895
6140
|
const finalClassName = newRow ? `${className} clear` : className;
|
|
5896
6141
|
const selectedOptions = options.filter((opt) => isSelected(opt.id, selectedIds));
|
|
6142
|
+
// Update portal dropdown when inside modal
|
|
6143
|
+
if (state.isInsideModal) {
|
|
6144
|
+
updatePortalDropdown(attrs, selectedIds, multiple, placeholder);
|
|
6145
|
+
}
|
|
5897
6146
|
return m('.input-field.select-space', {
|
|
5898
6147
|
className: finalClassName,
|
|
5899
6148
|
key,
|
|
@@ -5906,6 +6155,7 @@ const Select = () => {
|
|
|
5906
6155
|
tabindex: disabled ? -1 : 0,
|
|
5907
6156
|
'aria-expanded': state.isOpen ? 'true' : 'false',
|
|
5908
6157
|
'aria-haspopup': 'listbox',
|
|
6158
|
+
'aria-controls': state.dropdownId,
|
|
5909
6159
|
role: 'combobox',
|
|
5910
6160
|
}, [
|
|
5911
6161
|
m('input[type=text][readonly=true].select-dropdown.dropdown-trigger', {
|
|
@@ -5922,8 +6172,8 @@ const Select = () => {
|
|
|
5922
6172
|
}
|
|
5923
6173
|
},
|
|
5924
6174
|
}),
|
|
5925
|
-
// Dropdown Menu
|
|
5926
|
-
state.isOpen &&
|
|
6175
|
+
// Dropdown Menu - render inline only when NOT inside modal
|
|
6176
|
+
state.isOpen && !state.isInsideModal &&
|
|
5927
6177
|
m('ul.dropdown-content.select-dropdown', {
|
|
5928
6178
|
tabindex: 0,
|
|
5929
6179
|
oncreate: ({ dom }) => {
|
|
@@ -5933,76 +6183,7 @@ const Select = () => {
|
|
|
5933
6183
|
state.dropdownRef = null;
|
|
5934
6184
|
},
|
|
5935
6185
|
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
|
-
]),
|
|
6186
|
+
}, renderDropdownContent(attrs, selectedIds, multiple, placeholder)),
|
|
6006
6187
|
m(MaterialIcon, {
|
|
6007
6188
|
name: 'caret',
|
|
6008
6189
|
direction: 'down',
|
|
@@ -8947,6 +9128,7 @@ exports.FlatButton = FlatButton;
|
|
|
8947
9128
|
exports.FloatingActionButton = FloatingActionButton;
|
|
8948
9129
|
exports.HelperText = HelperText;
|
|
8949
9130
|
exports.Icon = Icon;
|
|
9131
|
+
exports.IconButton = IconButton;
|
|
8950
9132
|
exports.ImageList = ImageList;
|
|
8951
9133
|
exports.InputCheckbox = InputCheckbox;
|
|
8952
9134
|
exports.Label = Label;
|