mithril-materialized 3.4.0 → 3.4.1
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 +220 -92
- package/dist/index.js +220 -92
- package/dist/index.min.css +1 -1
- package/dist/index.umd.js +220 -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,18 @@ const Select = () => {
|
|
|
5960
6083
|
}
|
|
5961
6084
|
};
|
|
5962
6085
|
const closeDropdown = (e) => {
|
|
6086
|
+
console.log('select closeDropdown called');
|
|
6087
|
+
if (!state.isMultiple) {
|
|
6088
|
+
state.isOpen = false;
|
|
6089
|
+
return;
|
|
6090
|
+
}
|
|
5963
6091
|
const target = e.target;
|
|
5964
|
-
|
|
6092
|
+
// When inside modal, check both the select component AND the portaled dropdown
|
|
6093
|
+
const isClickInsideSelect = target.closest('.input-field.select-space');
|
|
6094
|
+
const isClickInsidePortalDropdown = state.isInsideModal && state.dropdownRef && (state.dropdownRef.contains(target) || target === state.dropdownRef);
|
|
6095
|
+
if (!isClickInsideSelect && !isClickInsidePortalDropdown) {
|
|
6096
|
+
console.log('select closeDropdown called: set state');
|
|
5965
6097
|
state.isOpen = false;
|
|
5966
|
-
m.redraw();
|
|
5967
6098
|
}
|
|
5968
6099
|
};
|
|
5969
6100
|
const getPortalStyles = (inputRef) => {
|
|
@@ -5971,13 +6102,21 @@ const Select = () => {
|
|
|
5971
6102
|
return {};
|
|
5972
6103
|
const rect = inputRef.getBoundingClientRect();
|
|
5973
6104
|
const viewportHeight = window.innerHeight;
|
|
6105
|
+
const spaceBelow = viewportHeight - rect.bottom;
|
|
6106
|
+
const spaceAbove = rect.top;
|
|
6107
|
+
// Choose whether to show above or below based on available space
|
|
6108
|
+
const showAbove = spaceBelow < 200 && spaceAbove > spaceBelow;
|
|
5974
6109
|
return {
|
|
5975
6110
|
position: 'fixed',
|
|
5976
|
-
top: `${rect.bottom}px`,
|
|
6111
|
+
top: showAbove ? 'auto' : `${rect.bottom}px`,
|
|
6112
|
+
bottom: showAbove ? `${viewportHeight - rect.top}px` : 'auto',
|
|
5977
6113
|
left: `${rect.left}px`,
|
|
5978
6114
|
width: `${rect.width}px`,
|
|
5979
6115
|
zIndex: 10000, // Higher than modal z-index
|
|
5980
|
-
maxHeight: `${
|
|
6116
|
+
maxHeight: showAbove ? `${spaceAbove - 20}px` : `${spaceBelow - 20}px`, // Leave 20px margin
|
|
6117
|
+
overflow: 'auto',
|
|
6118
|
+
display: 'block',
|
|
6119
|
+
opacity: 1,
|
|
5981
6120
|
};
|
|
5982
6121
|
};
|
|
5983
6122
|
const renderDropdownContent = (attrs, selectedIds, multiple, placeholder) => [
|
|
@@ -5985,15 +6124,10 @@ const Select = () => {
|
|
|
5985
6124
|
// Render ungrouped options first
|
|
5986
6125
|
attrs.options
|
|
5987
6126
|
.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
|
|
6127
|
+
.map((option) => m('li', Object.assign({ class: option.disabled ? 'disabled' : state.focusedIndex === attrs.options.indexOf(option) ? 'focused' : '' }, (option.disabled
|
|
5993
6128
|
? {}
|
|
5994
6129
|
: {
|
|
5995
|
-
onclick: (
|
|
5996
|
-
e.stopPropagation();
|
|
6130
|
+
onclick: () => {
|
|
5997
6131
|
toggleOption(option.id, multiple, attrs);
|
|
5998
6132
|
},
|
|
5999
6133
|
})), [
|
|
@@ -6023,8 +6157,8 @@ const Select = () => {
|
|
|
6023
6157
|
return groups;
|
|
6024
6158
|
}, {}))
|
|
6025
6159
|
.map(([groupName, groupOptions]) => [
|
|
6026
|
-
m('li.optgroup', {
|
|
6027
|
-
...groupOptions.map((option) => m('li', Object.assign({
|
|
6160
|
+
m('li.optgroup', { tabindex: 0 }, m('span', groupName)),
|
|
6161
|
+
...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
6162
|
? {}
|
|
6029
6163
|
: {
|
|
6030
6164
|
onclick: (e) => {
|
|
@@ -6051,23 +6185,20 @@ const Select = () => {
|
|
|
6051
6185
|
.reduce((acc, val) => acc.concat(val), []),
|
|
6052
6186
|
];
|
|
6053
6187
|
const updatePortalDropdown = (attrs, selectedIds, multiple, placeholder) => {
|
|
6054
|
-
var _a;
|
|
6055
6188
|
if (!state.isInsideModal)
|
|
6056
6189
|
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);
|
|
6190
|
+
// Clean up existing portal
|
|
6191
|
+
const existingPortal = document.getElementById(state.dropdownId);
|
|
6192
|
+
if (existingPortal) {
|
|
6193
|
+
existingPortal.remove();
|
|
6070
6194
|
}
|
|
6195
|
+
if (!state.isOpen || !state.inputRef)
|
|
6196
|
+
return;
|
|
6197
|
+
// Create portal element
|
|
6198
|
+
const portalElement = document.createElement('div');
|
|
6199
|
+
portalElement.id = state.dropdownId;
|
|
6200
|
+
document.body.appendChild(portalElement);
|
|
6201
|
+
// Create dropdown with proper positioning
|
|
6071
6202
|
const dropdownVnode = m('ul.dropdown-content.select-dropdown', {
|
|
6072
6203
|
tabindex: 0,
|
|
6073
6204
|
style: getPortalStyles(state.inputRef),
|
|
@@ -6078,6 +6209,7 @@ const Select = () => {
|
|
|
6078
6209
|
state.dropdownRef = null;
|
|
6079
6210
|
},
|
|
6080
6211
|
}, renderDropdownContent(attrs, selectedIds, multiple, placeholder));
|
|
6212
|
+
// Render to portal
|
|
6081
6213
|
m.render(portalElement, dropdownVnode);
|
|
6082
6214
|
};
|
|
6083
6215
|
return {
|
|
@@ -6120,7 +6252,8 @@ const Select = () => {
|
|
|
6120
6252
|
view: ({ attrs }) => {
|
|
6121
6253
|
var _a;
|
|
6122
6254
|
const controlled = isControlled(attrs);
|
|
6123
|
-
const { disabled } = attrs;
|
|
6255
|
+
const { newRow, className = 'col s12', key, options, multiple = false, label, helperText, placeholder = '', isMandatory, iconName, style, disabled, } = attrs;
|
|
6256
|
+
state.isMultiple = multiple;
|
|
6124
6257
|
// Get selected IDs from props or internal state
|
|
6125
6258
|
let selectedIds;
|
|
6126
6259
|
if (controlled) {
|
|
@@ -6136,7 +6269,6 @@ const Select = () => {
|
|
|
6136
6269
|
// Interactive uncontrolled: use internal state
|
|
6137
6270
|
selectedIds = state.internalSelectedIds;
|
|
6138
6271
|
}
|
|
6139
|
-
const { newRow, className = 'col s12', key, options, multiple = false, label, helperText, placeholder = '', isMandatory, iconName, style, } = attrs;
|
|
6140
6272
|
const finalClassName = newRow ? `${className} clear` : className;
|
|
6141
6273
|
const selectedOptions = options.filter((opt) => isSelected(opt.id, selectedIds));
|
|
6142
6274
|
// Update portal dropdown when inside modal
|
|
@@ -6173,7 +6305,8 @@ const Select = () => {
|
|
|
6173
6305
|
},
|
|
6174
6306
|
}),
|
|
6175
6307
|
// Dropdown Menu - render inline only when NOT inside modal
|
|
6176
|
-
state.isOpen &&
|
|
6308
|
+
state.isOpen &&
|
|
6309
|
+
!state.isInsideModal &&
|
|
6177
6310
|
m('ul.dropdown-content.select-dropdown', {
|
|
6178
6311
|
tabindex: 0,
|
|
6179
6312
|
oncreate: ({ dom }) => {
|
|
@@ -6331,7 +6464,6 @@ const Tabs = () => {
|
|
|
6331
6464
|
}
|
|
6332
6465
|
state.isDragging = false;
|
|
6333
6466
|
state.translateX = 0;
|
|
6334
|
-
// m.redraw();
|
|
6335
6467
|
};
|
|
6336
6468
|
/** Initialize active tab - selectedTabId takes precedence, next active property or first available tab */
|
|
6337
6469
|
const setActiveTabId = (anchoredTabs, selectedTabId) => {
|
|
@@ -6358,7 +6490,6 @@ const Tabs = () => {
|
|
|
6358
6490
|
},
|
|
6359
6491
|
oncreate: () => {
|
|
6360
6492
|
updateIndicator();
|
|
6361
|
-
m.redraw();
|
|
6362
6493
|
},
|
|
6363
6494
|
view: ({ attrs }) => {
|
|
6364
6495
|
const { tabWidth, tabs, className, style, swipeable = false } = attrs;
|
|
@@ -6492,7 +6623,6 @@ const SearchSelect = () => {
|
|
|
6492
6623
|
else {
|
|
6493
6624
|
// Click outside, close dropdown
|
|
6494
6625
|
state.isOpen = false;
|
|
6495
|
-
m.redraw();
|
|
6496
6626
|
}
|
|
6497
6627
|
};
|
|
6498
6628
|
// Handle keyboard navigation
|
|
@@ -6727,9 +6857,7 @@ const SearchSelect = () => {
|
|
|
6727
6857
|
]),
|
|
6728
6858
|
// No options found message or list of options
|
|
6729
6859
|
...(filteredOptions.length === 0 && !showAddNew
|
|
6730
|
-
? [
|
|
6731
|
-
m('li.search-select-no-options', texts.noOptionsFound),
|
|
6732
|
-
]
|
|
6860
|
+
? [m('li.search-select-no-options', texts.noOptionsFound)]
|
|
6733
6861
|
: []),
|
|
6734
6862
|
// Add new option item
|
|
6735
6863
|
...(showAddNew
|