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/button.d.ts
CHANGED
|
@@ -83,5 +83,6 @@ export declare const Button: m.FactoryComponent<ButtonAttrs>;
|
|
|
83
83
|
export declare const LargeButton: m.FactoryComponent<ButtonAttrs>;
|
|
84
84
|
export declare const SmallButton: m.FactoryComponent<ButtonAttrs>;
|
|
85
85
|
export declare const FlatButton: m.FactoryComponent<ButtonAttrs>;
|
|
86
|
+
export declare const IconButton: m.FactoryComponent<ButtonAttrs>;
|
|
86
87
|
export declare const RoundIconButton: m.FactoryComponent<ButtonAttrs>;
|
|
87
88
|
export declare const SubmitButton: m.FactoryComponent<ButtonAttrs>;
|
package/dist/components.css
CHANGED
|
@@ -977,6 +977,20 @@ button.btn-floating {
|
|
|
977
977
|
display: block;
|
|
978
978
|
}
|
|
979
979
|
|
|
980
|
+
.btn-flat.btn-icon {
|
|
981
|
+
min-width: auto;
|
|
982
|
+
padding: 0 8px;
|
|
983
|
+
width: auto;
|
|
984
|
+
line-height: 36px;
|
|
985
|
+
background-color: transparent !important;
|
|
986
|
+
}
|
|
987
|
+
.btn-flat.btn-icon i {
|
|
988
|
+
margin: 0;
|
|
989
|
+
}
|
|
990
|
+
.btn-flat.btn-icon:hover, .btn-flat.btn-icon:focus, .btn-flat.btn-icon:active, .btn-flat.btn-icon.active {
|
|
991
|
+
background-color: transparent !important;
|
|
992
|
+
}
|
|
993
|
+
|
|
980
994
|
.modal:focus {
|
|
981
995
|
outline: none;
|
|
982
996
|
}
|
|
@@ -4245,6 +4259,11 @@ body {
|
|
|
4245
4259
|
.theme-switcher .theme-switcher-buttons .btn-flat .material-icons {
|
|
4246
4260
|
font-size: 1rem;
|
|
4247
4261
|
}
|
|
4262
|
+
.theme-switcher .theme-switcher-buttons .btn-flat svg {
|
|
4263
|
+
width: 1rem !important;
|
|
4264
|
+
height: 1rem !important;
|
|
4265
|
+
flex-shrink: 0;
|
|
4266
|
+
}
|
|
4248
4267
|
.theme-switcher .theme-switcher-buttons .btn-flat span {
|
|
4249
4268
|
font-size: 0.75rem;
|
|
4250
4269
|
font-weight: 500;
|
|
@@ -4271,6 +4290,9 @@ body {
|
|
|
4271
4290
|
.theme-toggle .material-icons {
|
|
4272
4291
|
font-size: 1.25rem;
|
|
4273
4292
|
}
|
|
4293
|
+
.theme-toggle svg {
|
|
4294
|
+
flex-shrink: 0;
|
|
4295
|
+
}
|
|
4274
4296
|
|
|
4275
4297
|
nav .theme-toggle {
|
|
4276
4298
|
background: transparent;
|
package/dist/forms.css
CHANGED
|
@@ -2722,6 +2722,11 @@ input[type=range]::-ms-thumb {
|
|
|
2722
2722
|
.theme-switcher .theme-switcher-buttons .btn-flat .material-icons {
|
|
2723
2723
|
font-size: 1rem;
|
|
2724
2724
|
}
|
|
2725
|
+
.theme-switcher .theme-switcher-buttons .btn-flat svg {
|
|
2726
|
+
width: 1rem !important;
|
|
2727
|
+
height: 1rem !important;
|
|
2728
|
+
flex-shrink: 0;
|
|
2729
|
+
}
|
|
2725
2730
|
.theme-switcher .theme-switcher-buttons .btn-flat span {
|
|
2726
2731
|
font-size: 0.75rem;
|
|
2727
2732
|
font-weight: 500;
|
|
@@ -2748,6 +2753,9 @@ input[type=range]::-ms-thumb {
|
|
|
2748
2753
|
.theme-toggle .material-icons {
|
|
2749
2754
|
font-size: 1.25rem;
|
|
2750
2755
|
}
|
|
2756
|
+
.theme-toggle svg {
|
|
2757
|
+
flex-shrink: 0;
|
|
2758
|
+
}
|
|
2751
2759
|
|
|
2752
2760
|
nav .theme-toggle {
|
|
2753
2761
|
background: transparent;
|
package/dist/index.css
CHANGED
|
@@ -5234,6 +5234,20 @@ button.btn-floating {
|
|
|
5234
5234
|
display: block;
|
|
5235
5235
|
}
|
|
5236
5236
|
|
|
5237
|
+
.btn-flat.btn-icon {
|
|
5238
|
+
min-width: auto;
|
|
5239
|
+
padding: 0 8px;
|
|
5240
|
+
width: auto;
|
|
5241
|
+
line-height: 36px;
|
|
5242
|
+
background-color: transparent !important;
|
|
5243
|
+
}
|
|
5244
|
+
.btn-flat.btn-icon i {
|
|
5245
|
+
margin: 0;
|
|
5246
|
+
}
|
|
5247
|
+
.btn-flat.btn-icon:hover, .btn-flat.btn-icon:focus, .btn-flat.btn-icon:active, .btn-flat.btn-icon.active {
|
|
5248
|
+
background-color: transparent !important;
|
|
5249
|
+
}
|
|
5250
|
+
|
|
5237
5251
|
.dropdown-content:focus {
|
|
5238
5252
|
outline: 0;
|
|
5239
5253
|
}
|
|
@@ -9060,6 +9074,11 @@ input[type=range]::-ms-thumb {
|
|
|
9060
9074
|
.theme-switcher .theme-switcher-buttons .btn-flat .material-icons {
|
|
9061
9075
|
font-size: 1rem;
|
|
9062
9076
|
}
|
|
9077
|
+
.theme-switcher .theme-switcher-buttons .btn-flat svg {
|
|
9078
|
+
width: 1rem !important;
|
|
9079
|
+
height: 1rem !important;
|
|
9080
|
+
flex-shrink: 0;
|
|
9081
|
+
}
|
|
9063
9082
|
.theme-switcher .theme-switcher-buttons .btn-flat span {
|
|
9064
9083
|
font-size: 0.75rem;
|
|
9065
9084
|
font-weight: 500;
|
|
@@ -9086,6 +9105,9 @@ input[type=range]::-ms-thumb {
|
|
|
9086
9105
|
.theme-toggle .material-icons {
|
|
9087
9106
|
font-size: 1.25rem;
|
|
9088
9107
|
}
|
|
9108
|
+
.theme-toggle svg {
|
|
9109
|
+
flex-shrink: 0;
|
|
9110
|
+
}
|
|
9089
9111
|
|
|
9090
9112
|
nav .theme-toggle {
|
|
9091
9113
|
background: transparent;
|
package/dist/index.esm.js
CHANGED
|
@@ -432,6 +432,103 @@ const Icon = () => ({
|
|
|
432
432
|
},
|
|
433
433
|
});
|
|
434
434
|
|
|
435
|
+
/*!
|
|
436
|
+
* Waves Effect for Mithril Materialized
|
|
437
|
+
* Based on Waves v0.6.4 by Alfiana E. Sibuea
|
|
438
|
+
* Adapted for TypeScript and Mithril integration
|
|
439
|
+
*/
|
|
440
|
+
class WavesEffect {
|
|
441
|
+
static offset(elem) {
|
|
442
|
+
const rect = elem.getBoundingClientRect();
|
|
443
|
+
return {
|
|
444
|
+
top: rect.top + window.pageYOffset,
|
|
445
|
+
left: rect.left + window.pageXOffset
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
static createRipple(e, element) {
|
|
449
|
+
// Disable right click
|
|
450
|
+
if (e.button === 2) {
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
// Create ripple element
|
|
454
|
+
const ripple = document.createElement('div');
|
|
455
|
+
ripple.className = 'waves-ripple';
|
|
456
|
+
// Get click position relative to element
|
|
457
|
+
const pos = this.offset(element);
|
|
458
|
+
const relativeY = e.pageY - pos.top;
|
|
459
|
+
const relativeX = e.pageX - pos.left;
|
|
460
|
+
// Calculate scale based on element size
|
|
461
|
+
const scale = (element.clientWidth / 100) * 10;
|
|
462
|
+
// Set initial ripple position and style
|
|
463
|
+
ripple.style.cssText = `
|
|
464
|
+
top: ${relativeY}px;
|
|
465
|
+
left: ${relativeX}px;
|
|
466
|
+
transform: scale(0);
|
|
467
|
+
opacity: 1;
|
|
468
|
+
`;
|
|
469
|
+
// Add ripple to element
|
|
470
|
+
element.appendChild(ripple);
|
|
471
|
+
// Force reflow and animate
|
|
472
|
+
ripple.offsetHeight;
|
|
473
|
+
ripple.style.transform = `scale(${scale})`;
|
|
474
|
+
ripple.style.opacity = '1';
|
|
475
|
+
// Store reference for cleanup
|
|
476
|
+
ripple.setAttribute('data-created', Date.now().toString());
|
|
477
|
+
}
|
|
478
|
+
static removeRipples(element) {
|
|
479
|
+
const ripples = element.querySelectorAll('.waves-ripple');
|
|
480
|
+
ripples.forEach((ripple) => {
|
|
481
|
+
const created = parseInt(ripple.getAttribute('data-created') || '0');
|
|
482
|
+
const age = Date.now() - created;
|
|
483
|
+
const fadeOut = () => {
|
|
484
|
+
ripple.style.opacity = '0';
|
|
485
|
+
setTimeout(() => {
|
|
486
|
+
if (ripple.parentNode) {
|
|
487
|
+
ripple.parentNode.removeChild(ripple);
|
|
488
|
+
}
|
|
489
|
+
}, this.duration);
|
|
490
|
+
};
|
|
491
|
+
if (age >= 350) {
|
|
492
|
+
fadeOut();
|
|
493
|
+
}
|
|
494
|
+
else {
|
|
495
|
+
setTimeout(fadeOut, 350 - age);
|
|
496
|
+
}
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
WavesEffect.duration = 750;
|
|
501
|
+
WavesEffect.onMouseDown = (e) => {
|
|
502
|
+
const element = e.currentTarget;
|
|
503
|
+
if (element && element.classList.contains('waves-effect')) {
|
|
504
|
+
WavesEffect.createRipple(e, element);
|
|
505
|
+
}
|
|
506
|
+
};
|
|
507
|
+
WavesEffect.onMouseUp = (e) => {
|
|
508
|
+
const element = e.currentTarget;
|
|
509
|
+
if (element && element.classList.contains('waves-effect')) {
|
|
510
|
+
WavesEffect.removeRipples(element);
|
|
511
|
+
}
|
|
512
|
+
};
|
|
513
|
+
WavesEffect.onMouseLeave = (e) => {
|
|
514
|
+
const element = e.currentTarget;
|
|
515
|
+
if (element && element.classList.contains('waves-effect')) {
|
|
516
|
+
WavesEffect.removeRipples(element);
|
|
517
|
+
}
|
|
518
|
+
};
|
|
519
|
+
WavesEffect.onTouchStart = (e) => {
|
|
520
|
+
const element = e.currentTarget;
|
|
521
|
+
if (element && element.classList.contains('waves-effect')) {
|
|
522
|
+
WavesEffect.createRipple(e, element);
|
|
523
|
+
}
|
|
524
|
+
};
|
|
525
|
+
WavesEffect.onTouchEnd = (e) => {
|
|
526
|
+
const element = e.currentTarget;
|
|
527
|
+
if (element && element.classList.contains('waves-effect')) {
|
|
528
|
+
WavesEffect.removeRipples(element);
|
|
529
|
+
}
|
|
530
|
+
};
|
|
531
|
+
|
|
435
532
|
/**
|
|
436
533
|
* A factory to create new buttons.
|
|
437
534
|
*
|
|
@@ -445,13 +542,18 @@ const ButtonFactory = (element, defaultClassNames, type = '') => {
|
|
|
445
542
|
iconName, iconClass, label, className, variant } = attrs, params = __rest(attrs, ["tooltip", "tooltipPosition", "tooltipPostion", "iconName", "iconClass", "label", "className", "variant"]);
|
|
446
543
|
// Use variant or fallback to factory type
|
|
447
544
|
const buttonType = variant || type || 'button';
|
|
448
|
-
const cn = [tooltip ? 'tooltipped' : '', defaultClassNames, className]
|
|
449
|
-
.filter(Boolean)
|
|
450
|
-
.join(' ')
|
|
451
|
-
.trim();
|
|
545
|
+
const cn = [tooltip ? 'tooltipped' : '', defaultClassNames, className].filter(Boolean).join(' ').trim();
|
|
452
546
|
// Use tooltipPosition if available, fallback to legacy tooltipPostion
|
|
453
547
|
const position = tooltipPosition || tooltipPostion || 'top';
|
|
454
|
-
|
|
548
|
+
// Add waves effect event handlers if waves-effect class is present
|
|
549
|
+
const wavesHandlers = cn.includes('waves-effect') ? {
|
|
550
|
+
onmousedown: WavesEffect.onMouseDown,
|
|
551
|
+
onmouseup: WavesEffect.onMouseUp,
|
|
552
|
+
onmouseleave: WavesEffect.onMouseLeave,
|
|
553
|
+
ontouchstart: WavesEffect.onTouchStart,
|
|
554
|
+
ontouchend: WavesEffect.onTouchEnd
|
|
555
|
+
} : {};
|
|
556
|
+
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);
|
|
455
557
|
},
|
|
456
558
|
};
|
|
457
559
|
};
|
|
@@ -460,6 +562,7 @@ const Button = ButtonFactory('a', 'waves-effect waves-light btn', 'button');
|
|
|
460
562
|
const LargeButton = ButtonFactory('a', 'waves-effect waves-light btn-large', 'button');
|
|
461
563
|
const SmallButton = ButtonFactory('a', 'waves-effect waves-light btn-small', 'button');
|
|
462
564
|
const FlatButton = ButtonFactory('a', 'waves-effect waves-teal btn-flat', 'button');
|
|
565
|
+
const IconButton = ButtonFactory('button', 'btn-flat btn-icon waves-effect waves-teal', 'button');
|
|
463
566
|
const RoundIconButton = ButtonFactory('button', 'btn-floating btn-large waves-effect waves-light', 'button');
|
|
464
567
|
const SubmitButton = ButtonFactory('button', 'btn waves-effect waves-light', 'submit');
|
|
465
568
|
|
|
@@ -896,7 +999,7 @@ const MaterialIcon = () => {
|
|
|
896
999
|
};
|
|
897
1000
|
const rotation = (_a = rotationMap[direction]) !== null && _a !== void 0 ? _a : 0;
|
|
898
1001
|
const transform = rotation ? `rotate(${rotation}deg)` : undefined;
|
|
899
|
-
return m('svg', Object.assign(Object.assign({}, props), { style: Object.assign({ transform }, style), height: '
|
|
1002
|
+
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', {
|
|
900
1003
|
d,
|
|
901
1004
|
fill: d.includes('M0 0h24v24H0z') ? 'none' : 'currentColor',
|
|
902
1005
|
})));
|
|
@@ -2811,6 +2914,7 @@ const TextArea = () => {
|
|
|
2811
2914
|
height: undefined,
|
|
2812
2915
|
active: false,
|
|
2813
2916
|
textarea: undefined,
|
|
2917
|
+
hiddenDiv: undefined,
|
|
2814
2918
|
internalValue: '',
|
|
2815
2919
|
};
|
|
2816
2920
|
const updateHeight = (textarea, hiddenDiv) => {
|
|
@@ -2907,13 +3011,13 @@ const TextArea = () => {
|
|
|
2907
3011
|
overflowWrap: 'break-word',
|
|
2908
3012
|
},
|
|
2909
3013
|
oncreate: ({ dom }) => {
|
|
2910
|
-
const hiddenDiv = dom;
|
|
3014
|
+
const hiddenDiv = state.hiddenDiv = dom;
|
|
2911
3015
|
if (state.textarea) {
|
|
2912
3016
|
updateHeight(state.textarea, hiddenDiv);
|
|
2913
3017
|
}
|
|
2914
3018
|
},
|
|
2915
3019
|
onupdate: ({ dom }) => {
|
|
2916
|
-
const hiddenDiv = dom;
|
|
3020
|
+
const hiddenDiv = state.hiddenDiv = dom;
|
|
2917
3021
|
if (state.textarea) {
|
|
2918
3022
|
updateHeight(state.textarea, hiddenDiv);
|
|
2919
3023
|
}
|
|
@@ -2938,7 +3042,10 @@ const TextArea = () => {
|
|
|
2938
3042
|
const textarea = dom;
|
|
2939
3043
|
if (state.height)
|
|
2940
3044
|
textarea.style.height = state.height;
|
|
2941
|
-
//
|
|
3045
|
+
// Trigger height recalculation when value changes programmatically
|
|
3046
|
+
if (state.hiddenDiv) {
|
|
3047
|
+
updateHeight(textarea, state.hiddenDiv);
|
|
3048
|
+
}
|
|
2942
3049
|
}, onfocus: () => {
|
|
2943
3050
|
state.active = true;
|
|
2944
3051
|
}, oninput: (e) => {
|
|
@@ -3098,9 +3205,6 @@ const InputField = (type, defaultClass = '') => () => {
|
|
|
3098
3205
|
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
3206
|
// const attributes = toAttrs(params);
|
|
3100
3207
|
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
3208
|
// Special rendering for minmax range sliders
|
|
3105
3209
|
if (type === 'range' && (attrs.minmax || attrs.valueDisplay)) {
|
|
3106
3210
|
return m(attrs.minmax ? DoubleRangeSlider : SingleRangeSlider, Object.assign(Object.assign({}, attrs), { state,
|
|
@@ -3121,12 +3225,15 @@ const InputField = (type, defaultClass = '') => () => {
|
|
|
3121
3225
|
}
|
|
3122
3226
|
else if (isNonInteractive) {
|
|
3123
3227
|
// Non-interactive components: prefer defaultValue, fallback to value
|
|
3124
|
-
value = ((
|
|
3228
|
+
value = ((_b = (_a = attrs.defaultValue) !== null && _a !== void 0 ? _a : attrs.value) !== null && _b !== void 0 ? _b : (isNumeric ? 0 : ''));
|
|
3125
3229
|
}
|
|
3126
3230
|
else {
|
|
3127
3231
|
// Interactive uncontrolled: use internal state
|
|
3128
|
-
value = ((
|
|
3232
|
+
value = ((_d = (_c = state.internalValue) !== null && _c !== void 0 ? _c : attrs.defaultValue) !== null && _d !== void 0 ? _d : (isNumeric ? 0 : ''));
|
|
3129
3233
|
}
|
|
3234
|
+
const isActive = state.active || ((_e = state.inputElement) === null || _e === void 0 ? void 0 : _e.value) || value || placeholder || type === 'color' || type === 'range'
|
|
3235
|
+
? true
|
|
3236
|
+
: false;
|
|
3130
3237
|
const rangeType = type === 'range' && !attrs.minmax;
|
|
3131
3238
|
return m('.input-field', { className: cn, style }, [
|
|
3132
3239
|
iconName ? m('i.material-icons.prefix', iconName) : undefined,
|
|
@@ -4340,12 +4447,17 @@ const FloatingActionButton = () => {
|
|
|
4340
4447
|
}
|
|
4341
4448
|
: undefined,
|
|
4342
4449
|
}, [
|
|
4343
|
-
m('a.btn-floating.btn-large', {
|
|
4450
|
+
m('a.btn-floating.btn-large.waves-effect.waves-light', {
|
|
4344
4451
|
className,
|
|
4452
|
+
onmousedown: WavesEffect.onMouseDown,
|
|
4453
|
+
onmouseup: WavesEffect.onMouseUp,
|
|
4454
|
+
onmouseleave: WavesEffect.onMouseLeave,
|
|
4455
|
+
ontouchstart: WavesEffect.onTouchStart,
|
|
4456
|
+
ontouchend: WavesEffect.onTouchEnd,
|
|
4345
4457
|
}, m('i.material-icons', { className: iconClass }, iconName)),
|
|
4346
4458
|
buttons &&
|
|
4347
4459
|
buttons.length > 0 &&
|
|
4348
|
-
m('ul', buttons.map((button, index) => m('li', m(`a.btn-floating.${button.className || 'red'}`, {
|
|
4460
|
+
m('ul', buttons.map((button, index) => m('li', m(`a.btn-floating.waves-effect.waves-light.${button.className || 'red'}`, {
|
|
4349
4461
|
style: {
|
|
4350
4462
|
opacity: state.isOpen ? '1' : '0',
|
|
4351
4463
|
transform: state.isOpen ? 'scale(1)' : 'scale(0.4)',
|
|
@@ -4356,6 +4468,11 @@ const FloatingActionButton = () => {
|
|
|
4356
4468
|
if (button.onclick)
|
|
4357
4469
|
button.onclick(e);
|
|
4358
4470
|
},
|
|
4471
|
+
onmousedown: WavesEffect.onMouseDown,
|
|
4472
|
+
onmouseup: WavesEffect.onMouseUp,
|
|
4473
|
+
onmouseleave: WavesEffect.onMouseLeave,
|
|
4474
|
+
ontouchstart: WavesEffect.onTouchStart,
|
|
4475
|
+
ontouchend: WavesEffect.onTouchEnd,
|
|
4359
4476
|
}, m('i.material-icons', { className: button.iconClass }, button.iconName))))),
|
|
4360
4477
|
]));
|
|
4361
4478
|
},
|
|
@@ -5752,11 +5869,13 @@ const RadioButtons = () => {
|
|
|
5752
5869
|
const Select = () => {
|
|
5753
5870
|
const state = {
|
|
5754
5871
|
id: '',
|
|
5872
|
+
dropdownId: '',
|
|
5755
5873
|
isOpen: false,
|
|
5756
5874
|
focusedIndex: -1,
|
|
5757
5875
|
inputRef: null,
|
|
5758
5876
|
dropdownRef: null,
|
|
5759
5877
|
internalSelectedIds: [],
|
|
5878
|
+
isInsideModal: false,
|
|
5760
5879
|
};
|
|
5761
5880
|
const isControlled = (attrs) => attrs.checkedId !== undefined && attrs.onchange !== undefined;
|
|
5762
5881
|
const isSelected = (id, selectedIds) => {
|
|
@@ -5845,9 +5964,124 @@ const Select = () => {
|
|
|
5845
5964
|
m.redraw();
|
|
5846
5965
|
}
|
|
5847
5966
|
};
|
|
5967
|
+
const getPortalStyles = (inputRef) => {
|
|
5968
|
+
if (!inputRef)
|
|
5969
|
+
return {};
|
|
5970
|
+
const rect = inputRef.getBoundingClientRect();
|
|
5971
|
+
const viewportHeight = window.innerHeight;
|
|
5972
|
+
return {
|
|
5973
|
+
position: 'fixed',
|
|
5974
|
+
top: `${rect.bottom}px`,
|
|
5975
|
+
left: `${rect.left}px`,
|
|
5976
|
+
width: `${rect.width}px`,
|
|
5977
|
+
zIndex: 10000, // Higher than modal z-index
|
|
5978
|
+
maxHeight: `${viewportHeight - rect.bottom - 20}px`, // Leave 20px margin from bottom
|
|
5979
|
+
};
|
|
5980
|
+
};
|
|
5981
|
+
const renderDropdownContent = (attrs, selectedIds, multiple, placeholder) => [
|
|
5982
|
+
placeholder && m('li.disabled', { tabindex: 0 }, m('span', placeholder)),
|
|
5983
|
+
// Render ungrouped options first
|
|
5984
|
+
attrs.options
|
|
5985
|
+
.filter((option) => !option.group)
|
|
5986
|
+
.map((option) => m('li', Object.assign({ key: option.id, class: option.disabled
|
|
5987
|
+
? 'disabled'
|
|
5988
|
+
: state.focusedIndex === attrs.options.indexOf(option)
|
|
5989
|
+
? 'focused'
|
|
5990
|
+
: '' }, (option.disabled
|
|
5991
|
+
? {}
|
|
5992
|
+
: {
|
|
5993
|
+
onclick: (e) => {
|
|
5994
|
+
e.stopPropagation();
|
|
5995
|
+
toggleOption(option.id, multiple, attrs);
|
|
5996
|
+
},
|
|
5997
|
+
})), [
|
|
5998
|
+
option.img && m('img', { src: option.img, alt: option.label }),
|
|
5999
|
+
m('span', [
|
|
6000
|
+
multiple
|
|
6001
|
+
? m('label', { for: option.id }, m('input', {
|
|
6002
|
+
id: option.id,
|
|
6003
|
+
type: 'checkbox',
|
|
6004
|
+
checked: selectedIds.includes(option.id),
|
|
6005
|
+
disabled: option.disabled ? true : undefined,
|
|
6006
|
+
onclick: (e) => {
|
|
6007
|
+
e.stopPropagation();
|
|
6008
|
+
},
|
|
6009
|
+
}), m('span', option.label))
|
|
6010
|
+
: m('span', option.label),
|
|
6011
|
+
].filter(Boolean)),
|
|
6012
|
+
])),
|
|
6013
|
+
// Render grouped options
|
|
6014
|
+
Object.entries(attrs.options
|
|
6015
|
+
.filter((option) => option.group)
|
|
6016
|
+
.reduce((groups, option) => {
|
|
6017
|
+
const group = option.group;
|
|
6018
|
+
if (!groups[group])
|
|
6019
|
+
groups[group] = [];
|
|
6020
|
+
groups[group].push(option);
|
|
6021
|
+
return groups;
|
|
6022
|
+
}, {}))
|
|
6023
|
+
.map(([groupName, groupOptions]) => [
|
|
6024
|
+
m('li.optgroup', { key: `group-${groupName}`, tabindex: 0 }, m('span', groupName)),
|
|
6025
|
+
...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
|
|
6026
|
+
? {}
|
|
6027
|
+
: {
|
|
6028
|
+
onclick: (e) => {
|
|
6029
|
+
e.stopPropagation();
|
|
6030
|
+
toggleOption(option.id, multiple, attrs);
|
|
6031
|
+
},
|
|
6032
|
+
})), [
|
|
6033
|
+
option.img && m('img', { src: option.img, alt: option.label }),
|
|
6034
|
+
m('span', [
|
|
6035
|
+
multiple
|
|
6036
|
+
? m('label', { for: option.id }, m('input', {
|
|
6037
|
+
id: option.id,
|
|
6038
|
+
type: 'checkbox',
|
|
6039
|
+
checked: selectedIds.includes(option.id),
|
|
6040
|
+
disabled: option.disabled ? true : undefined,
|
|
6041
|
+
onclick: (e) => {
|
|
6042
|
+
e.stopPropagation();
|
|
6043
|
+
},
|
|
6044
|
+
}), m('span', option.label))
|
|
6045
|
+
: m('span', option.label),
|
|
6046
|
+
].filter(Boolean)),
|
|
6047
|
+
])),
|
|
6048
|
+
])
|
|
6049
|
+
.reduce((acc, val) => acc.concat(val), []),
|
|
6050
|
+
];
|
|
6051
|
+
const updatePortalDropdown = (attrs, selectedIds, multiple, placeholder) => {
|
|
6052
|
+
var _a;
|
|
6053
|
+
if (!state.isInsideModal)
|
|
6054
|
+
return;
|
|
6055
|
+
let portalElement = document.getElementById(state.dropdownId);
|
|
6056
|
+
if (!state.isOpen) {
|
|
6057
|
+
// Clean up portal when dropdown is closed
|
|
6058
|
+
if (portalElement) {
|
|
6059
|
+
m.render(portalElement, []);
|
|
6060
|
+
(_a = portalElement.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(portalElement);
|
|
6061
|
+
}
|
|
6062
|
+
return;
|
|
6063
|
+
}
|
|
6064
|
+
if (!portalElement) {
|
|
6065
|
+
portalElement = document.createElement('div');
|
|
6066
|
+
portalElement.id = state.dropdownId;
|
|
6067
|
+
document.body.appendChild(portalElement);
|
|
6068
|
+
}
|
|
6069
|
+
const dropdownVnode = m('ul.dropdown-content.select-dropdown', {
|
|
6070
|
+
tabindex: 0,
|
|
6071
|
+
style: getPortalStyles(state.inputRef),
|
|
6072
|
+
oncreate: ({ dom }) => {
|
|
6073
|
+
state.dropdownRef = dom;
|
|
6074
|
+
},
|
|
6075
|
+
onremove: () => {
|
|
6076
|
+
state.dropdownRef = null;
|
|
6077
|
+
},
|
|
6078
|
+
}, renderDropdownContent(attrs, selectedIds, multiple, placeholder));
|
|
6079
|
+
m.render(portalElement, dropdownVnode);
|
|
6080
|
+
};
|
|
5848
6081
|
return {
|
|
5849
6082
|
oninit: ({ attrs }) => {
|
|
5850
6083
|
state.id = attrs.id || uniqueId();
|
|
6084
|
+
state.dropdownId = `${state.id}-dropdown`;
|
|
5851
6085
|
const controlled = isControlled(attrs);
|
|
5852
6086
|
// Warn developer for improper controlled usage
|
|
5853
6087
|
if (attrs.checkedId !== undefined && !controlled && !attrs.disabled) {
|
|
@@ -5866,9 +6100,20 @@ const Select = () => {
|
|
|
5866
6100
|
// Add global click listener to close dropdown
|
|
5867
6101
|
document.addEventListener('click', closeDropdown);
|
|
5868
6102
|
},
|
|
6103
|
+
oncreate: ({ dom }) => {
|
|
6104
|
+
// Detect if component is inside a modal
|
|
6105
|
+
state.isInsideModal = !!dom.closest('.modal');
|
|
6106
|
+
},
|
|
5869
6107
|
onremove: () => {
|
|
5870
6108
|
// Cleanup global listener
|
|
5871
6109
|
document.removeEventListener('click', closeDropdown);
|
|
6110
|
+
// Cleanup portaled dropdown if it exists
|
|
6111
|
+
if (state.isInsideModal && state.dropdownRef) {
|
|
6112
|
+
const portalElement = document.getElementById(state.dropdownId);
|
|
6113
|
+
if (portalElement && portalElement.parentNode) {
|
|
6114
|
+
portalElement.parentNode.removeChild(portalElement);
|
|
6115
|
+
}
|
|
6116
|
+
}
|
|
5872
6117
|
},
|
|
5873
6118
|
view: ({ attrs }) => {
|
|
5874
6119
|
var _a;
|
|
@@ -5892,6 +6137,10 @@ const Select = () => {
|
|
|
5892
6137
|
const { newRow, className = 'col s12', key, options, multiple = false, label, helperText, placeholder = '', isMandatory, iconName, style, } = attrs;
|
|
5893
6138
|
const finalClassName = newRow ? `${className} clear` : className;
|
|
5894
6139
|
const selectedOptions = options.filter((opt) => isSelected(opt.id, selectedIds));
|
|
6140
|
+
// Update portal dropdown when inside modal
|
|
6141
|
+
if (state.isInsideModal) {
|
|
6142
|
+
updatePortalDropdown(attrs, selectedIds, multiple, placeholder);
|
|
6143
|
+
}
|
|
5895
6144
|
return m('.input-field.select-space', {
|
|
5896
6145
|
className: finalClassName,
|
|
5897
6146
|
key,
|
|
@@ -5904,6 +6153,7 @@ const Select = () => {
|
|
|
5904
6153
|
tabindex: disabled ? -1 : 0,
|
|
5905
6154
|
'aria-expanded': state.isOpen ? 'true' : 'false',
|
|
5906
6155
|
'aria-haspopup': 'listbox',
|
|
6156
|
+
'aria-controls': state.dropdownId,
|
|
5907
6157
|
role: 'combobox',
|
|
5908
6158
|
}, [
|
|
5909
6159
|
m('input[type=text][readonly=true].select-dropdown.dropdown-trigger', {
|
|
@@ -5920,8 +6170,8 @@ const Select = () => {
|
|
|
5920
6170
|
}
|
|
5921
6171
|
},
|
|
5922
6172
|
}),
|
|
5923
|
-
// Dropdown Menu
|
|
5924
|
-
state.isOpen &&
|
|
6173
|
+
// Dropdown Menu - render inline only when NOT inside modal
|
|
6174
|
+
state.isOpen && !state.isInsideModal &&
|
|
5925
6175
|
m('ul.dropdown-content.select-dropdown', {
|
|
5926
6176
|
tabindex: 0,
|
|
5927
6177
|
oncreate: ({ dom }) => {
|
|
@@ -5931,76 +6181,7 @@ const Select = () => {
|
|
|
5931
6181
|
state.dropdownRef = null;
|
|
5932
6182
|
},
|
|
5933
6183
|
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
|
-
]),
|
|
6184
|
+
}, renderDropdownContent(attrs, selectedIds, multiple, placeholder)),
|
|
6004
6185
|
m(MaterialIcon, {
|
|
6005
6186
|
name: 'caret',
|
|
6006
6187
|
direction: 'down',
|
|
@@ -8920,4 +9101,4 @@ const isValidationError = (result) => !isValidationSuccess(result);
|
|
|
8920
9101
|
// ============================================================================
|
|
8921
9102
|
// All types are already exported via individual export declarations above
|
|
8922
9103
|
|
|
8923
|
-
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 };
|
|
9104
|
+
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, IconButton, 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 };
|