mithril-materialized 3.4.0 → 3.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/core.css +0 -3
- package/dist/forms.css +0 -3
- package/dist/index.css +1 -3
- package/dist/index.esm.js +218 -92
- package/dist/index.js +218 -92
- package/dist/index.min.css +1 -1
- package/dist/index.umd.js +218 -92
- package/dist/utilities.css +1 -0
- package/package.json +1 -1
- package/sass/components/_chips.scss +1 -0
- package/sass/components/forms/_select.scss +3 -3
package/dist/index.js
CHANGED
|
@@ -230,8 +230,6 @@ const Autocomplete = () => {
|
|
|
230
230
|
if (attrs.onAutocomplete) {
|
|
231
231
|
attrs.onAutocomplete(suggestion.key);
|
|
232
232
|
}
|
|
233
|
-
// Force redraw to update label state
|
|
234
|
-
m.redraw();
|
|
235
233
|
};
|
|
236
234
|
const handleKeydown = (e, attrs) => {
|
|
237
235
|
if (!state.isOpen)
|
|
@@ -310,7 +308,7 @@ const Autocomplete = () => {
|
|
|
310
308
|
const id = attrs.id || state.id;
|
|
311
309
|
const { label, helperText, onchange, newRow, className = 'col s12', style, iconName, isMandatory, data = {}, limit = Infinity, minLength = 1 } = attrs, params = __rest(attrs, ["label", "helperText", "onchange", "newRow", "className", "style", "iconName", "isMandatory", "data", "limit", "minLength"]);
|
|
312
310
|
const controlled = isControlled(attrs);
|
|
313
|
-
const currentValue = controlled ?
|
|
311
|
+
const currentValue = controlled ? attrs.value || '' : state.internalValue;
|
|
314
312
|
const cn = newRow ? className + ' clear' : className;
|
|
315
313
|
// Update suggestions when input changes
|
|
316
314
|
state.suggestions = filterSuggestions(currentValue, data, limit, minLength);
|
|
@@ -325,7 +323,7 @@ const Autocomplete = () => {
|
|
|
325
323
|
style,
|
|
326
324
|
}, [
|
|
327
325
|
iconName ? m('i.material-icons.prefix', iconName) : '',
|
|
328
|
-
m('input', Object.assign(Object.assign({}, params), { className: 'autocomplete', type: 'text', tabindex: 0, id, value:
|
|
326
|
+
m('input', Object.assign(Object.assign({}, params), { className: 'autocomplete', type: 'text', tabindex: 0, id, value: currentValue, oncreate: (vnode) => {
|
|
329
327
|
state.inputElement = vnode.dom;
|
|
330
328
|
// Set initial value for uncontrolled mode
|
|
331
329
|
if (!controlled && attrs.defaultValue) {
|
|
@@ -361,14 +359,10 @@ const Autocomplete = () => {
|
|
|
361
359
|
}
|
|
362
360
|
}, onblur: (e) => {
|
|
363
361
|
state.isActive = false;
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
state.selectedIndex = -1;
|
|
369
|
-
m.redraw();
|
|
370
|
-
}
|
|
371
|
-
}, 150);
|
|
362
|
+
if (!e.relatedTarget || !e.relatedTarget.closest('.autocomplete-content')) {
|
|
363
|
+
state.isOpen = false;
|
|
364
|
+
state.selectedIndex = -1;
|
|
365
|
+
}
|
|
372
366
|
} })),
|
|
373
367
|
// Autocomplete dropdown
|
|
374
368
|
state.isOpen &&
|
|
@@ -384,7 +378,6 @@ const Autocomplete = () => {
|
|
|
384
378
|
},
|
|
385
379
|
onmouseover: () => {
|
|
386
380
|
state.selectedIndex = index;
|
|
387
|
-
m.redraw();
|
|
388
381
|
},
|
|
389
382
|
}, [
|
|
390
383
|
// Check if value contains image URL or icon
|
|
@@ -2485,36 +2478,52 @@ const handleKeyboardNavigation = (key, currentValue, min, max, step) => {
|
|
|
2485
2478
|
return null;
|
|
2486
2479
|
}
|
|
2487
2480
|
};
|
|
2481
|
+
const isControlled = (attrs) => {
|
|
2482
|
+
return attrs.value !== undefined && typeof attrs.oninput === 'function';
|
|
2483
|
+
};
|
|
2484
|
+
const isRangeControlled = (attrs) => {
|
|
2485
|
+
return (attrs.minValue !== undefined || attrs.maxValue !== undefined) && typeof attrs.oninput === 'function';
|
|
2486
|
+
};
|
|
2488
2487
|
const initRangeState = (state, attrs) => {
|
|
2489
|
-
const { min = 0, max = 100, value, minValue, maxValue } = attrs;
|
|
2488
|
+
const { min = 0, max = 100, value, minValue, maxValue, defaultValue } = attrs;
|
|
2490
2489
|
// Initialize single range value
|
|
2491
|
-
if (
|
|
2492
|
-
|
|
2490
|
+
if (isControlled(attrs)) {
|
|
2491
|
+
// Always use value from props in controlled mode
|
|
2492
|
+
state.singleValue = value !== undefined ? value : min;
|
|
2493
|
+
}
|
|
2494
|
+
else {
|
|
2495
|
+
// Use internal state for uncontrolled mode
|
|
2493
2496
|
if (state.singleValue === undefined) {
|
|
2494
|
-
state.singleValue =
|
|
2497
|
+
state.singleValue = defaultValue !== undefined ? defaultValue : value !== undefined ? value : min;
|
|
2495
2498
|
}
|
|
2496
|
-
|
|
2499
|
+
// Only update internal state if props changed and user hasn't interacted
|
|
2500
|
+
if (state.lastValue !== value && !state.hasUserInteracted && value !== undefined) {
|
|
2497
2501
|
state.singleValue = value;
|
|
2498
2502
|
state.lastValue = value;
|
|
2499
2503
|
}
|
|
2500
2504
|
}
|
|
2501
|
-
else if (state.singleValue === undefined) {
|
|
2502
|
-
state.singleValue = min;
|
|
2503
|
-
}
|
|
2504
2505
|
// Initialize range values
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
state.
|
|
2509
|
-
state.rangeMaxValue = currentMaxValue;
|
|
2506
|
+
if (isRangeControlled(attrs)) {
|
|
2507
|
+
// Always use values from props in controlled mode
|
|
2508
|
+
state.rangeMinValue = minValue !== undefined ? minValue : min;
|
|
2509
|
+
state.rangeMaxValue = maxValue !== undefined ? maxValue : max;
|
|
2510
2510
|
}
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
state.rangeMaxValue
|
|
2516
|
-
|
|
2517
|
-
|
|
2511
|
+
else {
|
|
2512
|
+
// Use internal state for uncontrolled mode
|
|
2513
|
+
const currentMinValue = minValue !== undefined ? minValue : min;
|
|
2514
|
+
const currentMaxValue = maxValue !== undefined ? maxValue : max;
|
|
2515
|
+
if (state.rangeMinValue === undefined || state.rangeMaxValue === undefined) {
|
|
2516
|
+
state.rangeMinValue = currentMinValue;
|
|
2517
|
+
state.rangeMaxValue = currentMaxValue;
|
|
2518
|
+
}
|
|
2519
|
+
if (!state.hasUserInteracted &&
|
|
2520
|
+
((minValue !== undefined && state.lastMinValue !== minValue) ||
|
|
2521
|
+
(maxValue !== undefined && state.lastMaxValue !== maxValue))) {
|
|
2522
|
+
state.rangeMinValue = currentMinValue;
|
|
2523
|
+
state.rangeMaxValue = currentMaxValue;
|
|
2524
|
+
state.lastMinValue = minValue;
|
|
2525
|
+
state.lastMaxValue = maxValue;
|
|
2526
|
+
}
|
|
2518
2527
|
}
|
|
2519
2528
|
// Initialize active thumb if not set
|
|
2520
2529
|
if (state.activeThumb === null) {
|
|
@@ -2531,15 +2540,18 @@ const updateRangeValues = (minValue, maxValue, attrs, state, immediate) => {
|
|
|
2531
2540
|
minValue = maxValue;
|
|
2532
2541
|
if (maxValue < minValue)
|
|
2533
2542
|
maxValue = minValue;
|
|
2534
|
-
state
|
|
2535
|
-
|
|
2543
|
+
// Only update internal state for uncontrolled mode
|
|
2544
|
+
if (!isRangeControlled(attrs)) {
|
|
2545
|
+
state.rangeMinValue = minValue;
|
|
2546
|
+
state.rangeMaxValue = maxValue;
|
|
2547
|
+
}
|
|
2536
2548
|
state.hasUserInteracted = true;
|
|
2537
|
-
// Call
|
|
2549
|
+
// Call appropriate handler based on interaction type, not control mode
|
|
2538
2550
|
if (immediate && attrs.oninput) {
|
|
2539
|
-
attrs.oninput(minValue, maxValue);
|
|
2551
|
+
attrs.oninput(minValue, maxValue); // Immediate feedback during drag
|
|
2540
2552
|
}
|
|
2541
|
-
|
|
2542
|
-
attrs.onchange(minValue, maxValue);
|
|
2553
|
+
if (!immediate && attrs.onchange) {
|
|
2554
|
+
attrs.onchange(minValue, maxValue); // Final value on interaction end (blur/mouseup)
|
|
2543
2555
|
}
|
|
2544
2556
|
};
|
|
2545
2557
|
// Single Range Slider Component
|
|
@@ -2573,19 +2585,24 @@ const SingleRangeSlider = {
|
|
|
2573
2585
|
: tooltipPos
|
|
2574
2586
|
: tooltipPos;
|
|
2575
2587
|
const updateSingleValue = (newValue, immediate = false) => {
|
|
2576
|
-
state
|
|
2588
|
+
// Only update internal state for uncontrolled mode
|
|
2589
|
+
if (!isControlled(attrs)) {
|
|
2590
|
+
state.singleValue = newValue;
|
|
2591
|
+
}
|
|
2577
2592
|
state.hasUserInteracted = true;
|
|
2593
|
+
// Call appropriate handler based on interaction type, not control mode
|
|
2578
2594
|
if (immediate && oninput) {
|
|
2579
|
-
oninput(newValue);
|
|
2595
|
+
oninput(newValue); // Immediate feedback during drag
|
|
2580
2596
|
}
|
|
2581
|
-
|
|
2582
|
-
onchange(newValue);
|
|
2597
|
+
if (!immediate && onchange) {
|
|
2598
|
+
onchange(newValue); // Final value on interaction end (blur/mouseup)
|
|
2583
2599
|
}
|
|
2584
2600
|
};
|
|
2585
2601
|
const handleMouseDown = (e) => {
|
|
2586
2602
|
if (disabled)
|
|
2587
2603
|
return;
|
|
2588
2604
|
e.preventDefault();
|
|
2605
|
+
e.stopPropagation();
|
|
2589
2606
|
state.isDragging = true;
|
|
2590
2607
|
if (finalValueDisplay === 'auto') {
|
|
2591
2608
|
m.redraw();
|
|
@@ -2660,6 +2677,11 @@ const SingleRangeSlider = {
|
|
|
2660
2677
|
updateSingleValue(newValue, false);
|
|
2661
2678
|
}
|
|
2662
2679
|
},
|
|
2680
|
+
onblur: () => {
|
|
2681
|
+
if (disabled || !onchange)
|
|
2682
|
+
return;
|
|
2683
|
+
onchange(state.singleValue);
|
|
2684
|
+
},
|
|
2663
2685
|
}, [
|
|
2664
2686
|
m(`.track.${orientation}`),
|
|
2665
2687
|
m(`.range-progress.${orientation}`, { style: progressStyle }),
|
|
@@ -2718,6 +2740,7 @@ const DoubleRangeSlider = {
|
|
|
2718
2740
|
if (disabled)
|
|
2719
2741
|
return;
|
|
2720
2742
|
e.preventDefault();
|
|
2743
|
+
e.stopPropagation();
|
|
2721
2744
|
state.isDragging = true;
|
|
2722
2745
|
state.activeThumb = thumb;
|
|
2723
2746
|
if (finalValueDisplay === 'auto') {
|
|
@@ -2808,6 +2831,11 @@ const DoubleRangeSlider = {
|
|
|
2808
2831
|
maxThumb.focus();
|
|
2809
2832
|
}
|
|
2810
2833
|
},
|
|
2834
|
+
onblur: () => {
|
|
2835
|
+
if (disabled || !attrs.onchange)
|
|
2836
|
+
return;
|
|
2837
|
+
attrs.onchange(state.rangeMinValue, state.rangeMaxValue);
|
|
2838
|
+
},
|
|
2811
2839
|
}, [
|
|
2812
2840
|
m(`.track.${orientation}`),
|
|
2813
2841
|
m(`.range.${orientation}`, { style: rangeStyle }),
|
|
@@ -3013,13 +3041,13 @@ const TextArea = () => {
|
|
|
3013
3041
|
overflowWrap: 'break-word',
|
|
3014
3042
|
},
|
|
3015
3043
|
oncreate: ({ dom }) => {
|
|
3016
|
-
const hiddenDiv = state.hiddenDiv = dom;
|
|
3044
|
+
const hiddenDiv = (state.hiddenDiv = dom);
|
|
3017
3045
|
if (state.textarea) {
|
|
3018
3046
|
updateHeight(state.textarea, hiddenDiv);
|
|
3019
3047
|
}
|
|
3020
3048
|
},
|
|
3021
3049
|
onupdate: ({ dom }) => {
|
|
3022
|
-
const hiddenDiv = state.hiddenDiv = dom;
|
|
3050
|
+
const hiddenDiv = (state.hiddenDiv = dom);
|
|
3023
3051
|
if (state.textarea) {
|
|
3024
3052
|
updateHeight(state.textarea, hiddenDiv);
|
|
3025
3053
|
}
|
|
@@ -3132,8 +3160,7 @@ const InputField = (type, defaultClass = '') => () => {
|
|
|
3132
3160
|
isDragging: false,
|
|
3133
3161
|
activeThumb: null,
|
|
3134
3162
|
};
|
|
3135
|
-
const isControlled = (attrs) => 'value' in attrs && typeof attrs.value !== 'undefined' &&
|
|
3136
|
-
(typeof attrs.oninput === 'function' || typeof attrs.onchange === 'function');
|
|
3163
|
+
const isControlled = (attrs) => 'value' in attrs && typeof attrs.value !== 'undefined' && typeof attrs.oninput === 'function';
|
|
3137
3164
|
const getValue = (target) => {
|
|
3138
3165
|
const val = target.value;
|
|
3139
3166
|
return (val ? (type === 'number' || type === 'range' ? +val : val) : val);
|
|
@@ -3183,7 +3210,7 @@ const InputField = (type, defaultClass = '') => () => {
|
|
|
3183
3210
|
const isNonInteractive = attrs.readonly || attrs.disabled;
|
|
3184
3211
|
// Warn developer for improper controlled usage
|
|
3185
3212
|
if (attrs.value !== undefined && !controlled && !isNonInteractive) {
|
|
3186
|
-
console.warn(`${type} input received 'value' prop without 'oninput'
|
|
3213
|
+
console.warn(`${type} input with label '${attrs.label}' received 'value' prop without 'oninput' handler. ` +
|
|
3187
3214
|
`Use 'defaultValue' for uncontrolled components or add an event handler for controlled components.`);
|
|
3188
3215
|
}
|
|
3189
3216
|
// Initialize internal value if not in controlled mode
|
|
@@ -4210,14 +4237,12 @@ const Dropdown = () => {
|
|
|
4210
4237
|
inputRef: null,
|
|
4211
4238
|
dropdownRef: null,
|
|
4212
4239
|
internalCheckedId: undefined,
|
|
4240
|
+
isInsideModal: false,
|
|
4213
4241
|
};
|
|
4214
4242
|
const isControlled = (attrs) => attrs.checkedId !== undefined && typeof attrs.onchange === 'function';
|
|
4215
|
-
const closeDropdown = (
|
|
4216
|
-
|
|
4217
|
-
|
|
4218
|
-
state.isOpen = false;
|
|
4219
|
-
m.redraw();
|
|
4220
|
-
}
|
|
4243
|
+
const closeDropdown = () => {
|
|
4244
|
+
state.isOpen = false;
|
|
4245
|
+
m.redraw(); // Needed to remove the dropdown options list (potentially added to document root)
|
|
4221
4246
|
};
|
|
4222
4247
|
const handleKeyDown = (e, items) => {
|
|
4223
4248
|
const availableItems = items.filter((item) => !item.divider && !item.disabled);
|
|
@@ -4262,6 +4287,83 @@ const Dropdown = () => {
|
|
|
4262
4287
|
return undefined;
|
|
4263
4288
|
}
|
|
4264
4289
|
};
|
|
4290
|
+
const getPortalStyles = (inputRef) => {
|
|
4291
|
+
if (!inputRef)
|
|
4292
|
+
return {};
|
|
4293
|
+
const rect = inputRef.getBoundingClientRect();
|
|
4294
|
+
const viewportHeight = window.innerHeight;
|
|
4295
|
+
const spaceBelow = viewportHeight - rect.bottom;
|
|
4296
|
+
const spaceAbove = rect.top;
|
|
4297
|
+
// Choose whether to show above or below based on available space
|
|
4298
|
+
const showAbove = spaceBelow < 200 && spaceAbove > spaceBelow;
|
|
4299
|
+
return {
|
|
4300
|
+
position: 'fixed',
|
|
4301
|
+
top: showAbove ? 'auto' : `${rect.bottom}px`,
|
|
4302
|
+
bottom: showAbove ? `${viewportHeight - rect.top}px` : 'auto',
|
|
4303
|
+
left: `${rect.left}px`,
|
|
4304
|
+
width: `${rect.width}px`,
|
|
4305
|
+
zIndex: 10000,
|
|
4306
|
+
maxHeight: showAbove ? `${spaceAbove - 20}px` : `${spaceBelow - 20}px`,
|
|
4307
|
+
overflow: 'auto',
|
|
4308
|
+
display: 'block',
|
|
4309
|
+
opacity: 1,
|
|
4310
|
+
};
|
|
4311
|
+
};
|
|
4312
|
+
const updatePortalDropdown = (items, selectedLabel, onSelectItem) => {
|
|
4313
|
+
if (!state.isInsideModal)
|
|
4314
|
+
return;
|
|
4315
|
+
// Clean up existing portal
|
|
4316
|
+
const existingPortal = document.getElementById(`${state.id}-dropdown`);
|
|
4317
|
+
if (existingPortal) {
|
|
4318
|
+
existingPortal.remove();
|
|
4319
|
+
}
|
|
4320
|
+
if (!state.isOpen || !state.inputRef)
|
|
4321
|
+
return;
|
|
4322
|
+
// Create portal element
|
|
4323
|
+
const portalElement = document.createElement('div');
|
|
4324
|
+
portalElement.id = `${state.id}-dropdown`;
|
|
4325
|
+
document.body.appendChild(portalElement);
|
|
4326
|
+
// Create dropdown content
|
|
4327
|
+
const availableItems = items.filter((item) => !item.divider && !item.disabled);
|
|
4328
|
+
const dropdownContent = items.map((item) => {
|
|
4329
|
+
if (item.divider) {
|
|
4330
|
+
return m('li.divider');
|
|
4331
|
+
}
|
|
4332
|
+
const itemIndex = availableItems.indexOf(item);
|
|
4333
|
+
const isSelected = selectedLabel === item.label;
|
|
4334
|
+
const isFocused = state.focusedIndex === itemIndex;
|
|
4335
|
+
return m('li', {
|
|
4336
|
+
class: `${isSelected ? 'selected' : ''} ${isFocused ? 'focused' : ''}${item.disabled ? ' disabled' : ''}`,
|
|
4337
|
+
onclick: item.disabled ? undefined : () => onSelectItem(item),
|
|
4338
|
+
}, m('span', {
|
|
4339
|
+
style: {
|
|
4340
|
+
display: 'flex',
|
|
4341
|
+
alignItems: 'center',
|
|
4342
|
+
padding: '14px 16px',
|
|
4343
|
+
},
|
|
4344
|
+
}, [
|
|
4345
|
+
item.iconName
|
|
4346
|
+
? m('i.material-icons', {
|
|
4347
|
+
style: { marginRight: '32px' },
|
|
4348
|
+
}, item.iconName)
|
|
4349
|
+
: undefined,
|
|
4350
|
+
item.label,
|
|
4351
|
+
]));
|
|
4352
|
+
});
|
|
4353
|
+
// Create dropdown with proper positioning
|
|
4354
|
+
const dropdownVnode = m('ul.dropdown-content.select-dropdown', {
|
|
4355
|
+
tabindex: 0,
|
|
4356
|
+
style: getPortalStyles(state.inputRef),
|
|
4357
|
+
oncreate: ({ dom }) => {
|
|
4358
|
+
state.dropdownRef = dom;
|
|
4359
|
+
},
|
|
4360
|
+
onremove: () => {
|
|
4361
|
+
state.dropdownRef = null;
|
|
4362
|
+
},
|
|
4363
|
+
}, dropdownContent);
|
|
4364
|
+
// Render to portal
|
|
4365
|
+
m.render(portalElement, dropdownVnode);
|
|
4366
|
+
};
|
|
4265
4367
|
return {
|
|
4266
4368
|
oninit: ({ attrs }) => {
|
|
4267
4369
|
var _a;
|
|
@@ -4273,9 +4375,18 @@ const Dropdown = () => {
|
|
|
4273
4375
|
// Add global click listener to close dropdown
|
|
4274
4376
|
document.addEventListener('click', closeDropdown);
|
|
4275
4377
|
},
|
|
4378
|
+
oncreate: ({ dom }) => {
|
|
4379
|
+
// Detect if component is inside a modal
|
|
4380
|
+
state.isInsideModal = !!dom.closest('.modal');
|
|
4381
|
+
},
|
|
4276
4382
|
onremove: () => {
|
|
4277
4383
|
// Cleanup global listener
|
|
4278
4384
|
document.removeEventListener('click', closeDropdown);
|
|
4385
|
+
// Cleanup portal
|
|
4386
|
+
const portalElement = document.getElementById(`${state.id}-dropdown`);
|
|
4387
|
+
if (portalElement) {
|
|
4388
|
+
portalElement.remove();
|
|
4389
|
+
}
|
|
4279
4390
|
},
|
|
4280
4391
|
view: ({ attrs }) => {
|
|
4281
4392
|
const { checkedId, key, label, onchange, disabled = false, items, iconName, helperText, style, className = 'col s12', } = attrs;
|
|
@@ -4298,6 +4409,16 @@ const Dropdown = () => {
|
|
|
4298
4409
|
: undefined;
|
|
4299
4410
|
const title = selectedItem ? selectedItem.label : label || 'Select';
|
|
4300
4411
|
const availableItems = items.filter((item) => !item.divider && !item.disabled);
|
|
4412
|
+
// Update portal dropdown when inside modal
|
|
4413
|
+
if (state.isInsideModal) {
|
|
4414
|
+
updatePortalDropdown(items, title, (item) => {
|
|
4415
|
+
if (item.id) {
|
|
4416
|
+
state.isOpen = false;
|
|
4417
|
+
state.focusedIndex = -1;
|
|
4418
|
+
handleSelection(item.id);
|
|
4419
|
+
}
|
|
4420
|
+
});
|
|
4421
|
+
}
|
|
4301
4422
|
return m('.dropdown-wrapper.input-field', { className, key, style }, [
|
|
4302
4423
|
iconName ? m('i.material-icons.prefix', iconName) : undefined,
|
|
4303
4424
|
m(HelperText, { helperText }),
|
|
@@ -4331,8 +4452,9 @@ const Dropdown = () => {
|
|
|
4331
4452
|
}
|
|
4332
4453
|
},
|
|
4333
4454
|
}),
|
|
4334
|
-
// Dropdown Menu
|
|
4455
|
+
// Dropdown Menu - render inline only when NOT inside modal
|
|
4335
4456
|
state.isOpen &&
|
|
4457
|
+
!state.isInsideModal &&
|
|
4336
4458
|
m('ul.dropdown-content.select-dropdown', {
|
|
4337
4459
|
tabindex: 0,
|
|
4338
4460
|
role: 'listbox',
|
|
@@ -4782,7 +4904,7 @@ const ModalPanel = () => {
|
|
|
4782
4904
|
maxWidth: '75%',
|
|
4783
4905
|
borderRadius: '4px',
|
|
4784
4906
|
})), { backgroundColor: 'var(--mm-modal-background, #fff)', maxHeight: '85%', overflow: 'auto', zIndex: '1003', padding: '0', flexDirection: 'column', boxShadow: '0 24px 38px 3px rgba(0,0,0,0.14), 0 9px 46px 8px rgba(0,0,0,0.12), 0 11px 15px -7px rgba(0,0,0,0.20)' }),
|
|
4785
|
-
onclick: (e) => e.stopPropagation(), // Prevent backdrop click when clicking inside modal
|
|
4907
|
+
// onclick: (e: Event) => e.stopPropagation(), // Prevent backdrop click when clicking inside modal
|
|
4786
4908
|
}, [
|
|
4787
4909
|
// Close button
|
|
4788
4910
|
showCloseButton &&
|
|
@@ -5878,6 +6000,7 @@ const Select = () => {
|
|
|
5878
6000
|
dropdownRef: null,
|
|
5879
6001
|
internalSelectedIds: [],
|
|
5880
6002
|
isInsideModal: false,
|
|
6003
|
+
isMultiple: false,
|
|
5881
6004
|
};
|
|
5882
6005
|
const isControlled = (attrs) => attrs.checkedId !== undefined && attrs.onchange !== undefined;
|
|
5883
6006
|
const isSelected = (id, selectedIds) => {
|
|
@@ -5960,10 +6083,16 @@ const Select = () => {
|
|
|
5960
6083
|
}
|
|
5961
6084
|
};
|
|
5962
6085
|
const closeDropdown = (e) => {
|
|
6086
|
+
if (!state.isMultiple) {
|
|
6087
|
+
state.isOpen = false;
|
|
6088
|
+
return;
|
|
6089
|
+
}
|
|
5963
6090
|
const target = e.target;
|
|
5964
|
-
|
|
6091
|
+
// When inside modal, check both the select component AND the portaled dropdown
|
|
6092
|
+
const isClickInsideSelect = target.closest('.input-field.select-space');
|
|
6093
|
+
const isClickInsidePortalDropdown = state.isInsideModal && state.dropdownRef && (state.dropdownRef.contains(target) || target === state.dropdownRef);
|
|
6094
|
+
if (!isClickInsideSelect && !isClickInsidePortalDropdown) {
|
|
5965
6095
|
state.isOpen = false;
|
|
5966
|
-
m.redraw();
|
|
5967
6096
|
}
|
|
5968
6097
|
};
|
|
5969
6098
|
const getPortalStyles = (inputRef) => {
|
|
@@ -5971,13 +6100,21 @@ const Select = () => {
|
|
|
5971
6100
|
return {};
|
|
5972
6101
|
const rect = inputRef.getBoundingClientRect();
|
|
5973
6102
|
const viewportHeight = window.innerHeight;
|
|
6103
|
+
const spaceBelow = viewportHeight - rect.bottom;
|
|
6104
|
+
const spaceAbove = rect.top;
|
|
6105
|
+
// Choose whether to show above or below based on available space
|
|
6106
|
+
const showAbove = spaceBelow < 200 && spaceAbove > spaceBelow;
|
|
5974
6107
|
return {
|
|
5975
6108
|
position: 'fixed',
|
|
5976
|
-
top: `${rect.bottom}px`,
|
|
6109
|
+
top: showAbove ? 'auto' : `${rect.bottom}px`,
|
|
6110
|
+
bottom: showAbove ? `${viewportHeight - rect.top}px` : 'auto',
|
|
5977
6111
|
left: `${rect.left}px`,
|
|
5978
6112
|
width: `${rect.width}px`,
|
|
5979
6113
|
zIndex: 10000, // Higher than modal z-index
|
|
5980
|
-
maxHeight: `${
|
|
6114
|
+
maxHeight: showAbove ? `${spaceAbove - 20}px` : `${spaceBelow - 20}px`, // Leave 20px margin
|
|
6115
|
+
overflow: 'auto',
|
|
6116
|
+
display: 'block',
|
|
6117
|
+
opacity: 1,
|
|
5981
6118
|
};
|
|
5982
6119
|
};
|
|
5983
6120
|
const renderDropdownContent = (attrs, selectedIds, multiple, placeholder) => [
|
|
@@ -5985,15 +6122,10 @@ const Select = () => {
|
|
|
5985
6122
|
// Render ungrouped options first
|
|
5986
6123
|
attrs.options
|
|
5987
6124
|
.filter((option) => !option.group)
|
|
5988
|
-
.map((option) => m('li', Object.assign({
|
|
5989
|
-
? 'disabled'
|
|
5990
|
-
: state.focusedIndex === attrs.options.indexOf(option)
|
|
5991
|
-
? 'focused'
|
|
5992
|
-
: '' }, (option.disabled
|
|
6125
|
+
.map((option) => m('li', Object.assign({ class: option.disabled ? 'disabled' : state.focusedIndex === attrs.options.indexOf(option) ? 'focused' : '' }, (option.disabled
|
|
5993
6126
|
? {}
|
|
5994
6127
|
: {
|
|
5995
|
-
onclick: (
|
|
5996
|
-
e.stopPropagation();
|
|
6128
|
+
onclick: () => {
|
|
5997
6129
|
toggleOption(option.id, multiple, attrs);
|
|
5998
6130
|
},
|
|
5999
6131
|
})), [
|
|
@@ -6023,8 +6155,8 @@ const Select = () => {
|
|
|
6023
6155
|
return groups;
|
|
6024
6156
|
}, {}))
|
|
6025
6157
|
.map(([groupName, groupOptions]) => [
|
|
6026
|
-
m('li.optgroup', {
|
|
6027
|
-
...groupOptions.map((option) => m('li', Object.assign({
|
|
6158
|
+
m('li.optgroup', { tabindex: 0 }, m('span', groupName)),
|
|
6159
|
+
...groupOptions.map((option) => m('li', Object.assign({ class: `optgroup-option${option.disabled ? ' disabled' : ''}${isSelected(option.id, selectedIds) ? ' selected' : ''}${state.focusedIndex === attrs.options.indexOf(option) ? ' focused' : ''}` }, (option.disabled
|
|
6028
6160
|
? {}
|
|
6029
6161
|
: {
|
|
6030
6162
|
onclick: (e) => {
|
|
@@ -6051,23 +6183,20 @@ const Select = () => {
|
|
|
6051
6183
|
.reduce((acc, val) => acc.concat(val), []),
|
|
6052
6184
|
];
|
|
6053
6185
|
const updatePortalDropdown = (attrs, selectedIds, multiple, placeholder) => {
|
|
6054
|
-
var _a;
|
|
6055
6186
|
if (!state.isInsideModal)
|
|
6056
6187
|
return;
|
|
6057
|
-
|
|
6058
|
-
|
|
6059
|
-
|
|
6060
|
-
|
|
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);
|
|
6188
|
+
// Clean up existing portal
|
|
6189
|
+
const existingPortal = document.getElementById(state.dropdownId);
|
|
6190
|
+
if (existingPortal) {
|
|
6191
|
+
existingPortal.remove();
|
|
6070
6192
|
}
|
|
6193
|
+
if (!state.isOpen || !state.inputRef)
|
|
6194
|
+
return;
|
|
6195
|
+
// Create portal element
|
|
6196
|
+
const portalElement = document.createElement('div');
|
|
6197
|
+
portalElement.id = state.dropdownId;
|
|
6198
|
+
document.body.appendChild(portalElement);
|
|
6199
|
+
// Create dropdown with proper positioning
|
|
6071
6200
|
const dropdownVnode = m('ul.dropdown-content.select-dropdown', {
|
|
6072
6201
|
tabindex: 0,
|
|
6073
6202
|
style: getPortalStyles(state.inputRef),
|
|
@@ -6078,6 +6207,7 @@ const Select = () => {
|
|
|
6078
6207
|
state.dropdownRef = null;
|
|
6079
6208
|
},
|
|
6080
6209
|
}, renderDropdownContent(attrs, selectedIds, multiple, placeholder));
|
|
6210
|
+
// Render to portal
|
|
6081
6211
|
m.render(portalElement, dropdownVnode);
|
|
6082
6212
|
};
|
|
6083
6213
|
return {
|
|
@@ -6120,7 +6250,8 @@ const Select = () => {
|
|
|
6120
6250
|
view: ({ attrs }) => {
|
|
6121
6251
|
var _a;
|
|
6122
6252
|
const controlled = isControlled(attrs);
|
|
6123
|
-
const { disabled } = attrs;
|
|
6253
|
+
const { newRow, className = 'col s12', key, options, multiple = false, label, helperText, placeholder = '', isMandatory, iconName, style, disabled, } = attrs;
|
|
6254
|
+
state.isMultiple = multiple;
|
|
6124
6255
|
// Get selected IDs from props or internal state
|
|
6125
6256
|
let selectedIds;
|
|
6126
6257
|
if (controlled) {
|
|
@@ -6136,7 +6267,6 @@ const Select = () => {
|
|
|
6136
6267
|
// Interactive uncontrolled: use internal state
|
|
6137
6268
|
selectedIds = state.internalSelectedIds;
|
|
6138
6269
|
}
|
|
6139
|
-
const { newRow, className = 'col s12', key, options, multiple = false, label, helperText, placeholder = '', isMandatory, iconName, style, } = attrs;
|
|
6140
6270
|
const finalClassName = newRow ? `${className} clear` : className;
|
|
6141
6271
|
const selectedOptions = options.filter((opt) => isSelected(opt.id, selectedIds));
|
|
6142
6272
|
// Update portal dropdown when inside modal
|
|
@@ -6173,7 +6303,8 @@ const Select = () => {
|
|
|
6173
6303
|
},
|
|
6174
6304
|
}),
|
|
6175
6305
|
// Dropdown Menu - render inline only when NOT inside modal
|
|
6176
|
-
state.isOpen &&
|
|
6306
|
+
state.isOpen &&
|
|
6307
|
+
!state.isInsideModal &&
|
|
6177
6308
|
m('ul.dropdown-content.select-dropdown', {
|
|
6178
6309
|
tabindex: 0,
|
|
6179
6310
|
oncreate: ({ dom }) => {
|
|
@@ -6331,7 +6462,6 @@ const Tabs = () => {
|
|
|
6331
6462
|
}
|
|
6332
6463
|
state.isDragging = false;
|
|
6333
6464
|
state.translateX = 0;
|
|
6334
|
-
// m.redraw();
|
|
6335
6465
|
};
|
|
6336
6466
|
/** Initialize active tab - selectedTabId takes precedence, next active property or first available tab */
|
|
6337
6467
|
const setActiveTabId = (anchoredTabs, selectedTabId) => {
|
|
@@ -6358,7 +6488,6 @@ const Tabs = () => {
|
|
|
6358
6488
|
},
|
|
6359
6489
|
oncreate: () => {
|
|
6360
6490
|
updateIndicator();
|
|
6361
|
-
m.redraw();
|
|
6362
6491
|
},
|
|
6363
6492
|
view: ({ attrs }) => {
|
|
6364
6493
|
const { tabWidth, tabs, className, style, swipeable = false } = attrs;
|
|
@@ -6492,7 +6621,6 @@ const SearchSelect = () => {
|
|
|
6492
6621
|
else {
|
|
6493
6622
|
// Click outside, close dropdown
|
|
6494
6623
|
state.isOpen = false;
|
|
6495
|
-
m.redraw();
|
|
6496
6624
|
}
|
|
6497
6625
|
};
|
|
6498
6626
|
// Handle keyboard navigation
|
|
@@ -6727,9 +6855,7 @@ const SearchSelect = () => {
|
|
|
6727
6855
|
]),
|
|
6728
6856
|
// No options found message or list of options
|
|
6729
6857
|
...(filteredOptions.length === 0 && !showAddNew
|
|
6730
|
-
? [
|
|
6731
|
-
m('li.search-select-no-options', texts.noOptionsFound),
|
|
6732
|
-
]
|
|
6858
|
+
? [m('li.search-select-no-options', texts.noOptionsFound)]
|
|
6733
6859
|
: []),
|
|
6734
6860
|
// Add new option item
|
|
6735
6861
|
...(showAddNew
|