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.umd.js
CHANGED
|
@@ -232,8 +232,6 @@
|
|
|
232
232
|
if (attrs.onAutocomplete) {
|
|
233
233
|
attrs.onAutocomplete(suggestion.key);
|
|
234
234
|
}
|
|
235
|
-
// Force redraw to update label state
|
|
236
|
-
m.redraw();
|
|
237
235
|
};
|
|
238
236
|
const handleKeydown = (e, attrs) => {
|
|
239
237
|
if (!state.isOpen)
|
|
@@ -312,7 +310,7 @@
|
|
|
312
310
|
const id = attrs.id || state.id;
|
|
313
311
|
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"]);
|
|
314
312
|
const controlled = isControlled(attrs);
|
|
315
|
-
const currentValue = controlled ?
|
|
313
|
+
const currentValue = controlled ? attrs.value || '' : state.internalValue;
|
|
316
314
|
const cn = newRow ? className + ' clear' : className;
|
|
317
315
|
// Update suggestions when input changes
|
|
318
316
|
state.suggestions = filterSuggestions(currentValue, data, limit, minLength);
|
|
@@ -327,7 +325,7 @@
|
|
|
327
325
|
style,
|
|
328
326
|
}, [
|
|
329
327
|
iconName ? m('i.material-icons.prefix', iconName) : '',
|
|
330
|
-
m('input', Object.assign(Object.assign({}, params), { className: 'autocomplete', type: 'text', tabindex: 0, id, value:
|
|
328
|
+
m('input', Object.assign(Object.assign({}, params), { className: 'autocomplete', type: 'text', tabindex: 0, id, value: currentValue, oncreate: (vnode) => {
|
|
331
329
|
state.inputElement = vnode.dom;
|
|
332
330
|
// Set initial value for uncontrolled mode
|
|
333
331
|
if (!controlled && attrs.defaultValue) {
|
|
@@ -363,14 +361,10 @@
|
|
|
363
361
|
}
|
|
364
362
|
}, onblur: (e) => {
|
|
365
363
|
state.isActive = false;
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
state.selectedIndex = -1;
|
|
371
|
-
m.redraw();
|
|
372
|
-
}
|
|
373
|
-
}, 150);
|
|
364
|
+
if (!e.relatedTarget || !e.relatedTarget.closest('.autocomplete-content')) {
|
|
365
|
+
state.isOpen = false;
|
|
366
|
+
state.selectedIndex = -1;
|
|
367
|
+
}
|
|
374
368
|
} })),
|
|
375
369
|
// Autocomplete dropdown
|
|
376
370
|
state.isOpen &&
|
|
@@ -386,7 +380,6 @@
|
|
|
386
380
|
},
|
|
387
381
|
onmouseover: () => {
|
|
388
382
|
state.selectedIndex = index;
|
|
389
|
-
m.redraw();
|
|
390
383
|
},
|
|
391
384
|
}, [
|
|
392
385
|
// Check if value contains image URL or icon
|
|
@@ -2487,36 +2480,52 @@
|
|
|
2487
2480
|
return null;
|
|
2488
2481
|
}
|
|
2489
2482
|
};
|
|
2483
|
+
const isControlled = (attrs) => {
|
|
2484
|
+
return attrs.value !== undefined && typeof attrs.oninput === 'function';
|
|
2485
|
+
};
|
|
2486
|
+
const isRangeControlled = (attrs) => {
|
|
2487
|
+
return (attrs.minValue !== undefined || attrs.maxValue !== undefined) && typeof attrs.oninput === 'function';
|
|
2488
|
+
};
|
|
2490
2489
|
const initRangeState = (state, attrs) => {
|
|
2491
|
-
const { min = 0, max = 100, value, minValue, maxValue } = attrs;
|
|
2490
|
+
const { min = 0, max = 100, value, minValue, maxValue, defaultValue } = attrs;
|
|
2492
2491
|
// Initialize single range value
|
|
2493
|
-
if (
|
|
2494
|
-
|
|
2492
|
+
if (isControlled(attrs)) {
|
|
2493
|
+
// Always use value from props in controlled mode
|
|
2494
|
+
state.singleValue = value !== undefined ? value : min;
|
|
2495
|
+
}
|
|
2496
|
+
else {
|
|
2497
|
+
// Use internal state for uncontrolled mode
|
|
2495
2498
|
if (state.singleValue === undefined) {
|
|
2496
|
-
state.singleValue =
|
|
2499
|
+
state.singleValue = defaultValue !== undefined ? defaultValue : value !== undefined ? value : min;
|
|
2497
2500
|
}
|
|
2498
|
-
|
|
2501
|
+
// Only update internal state if props changed and user hasn't interacted
|
|
2502
|
+
if (state.lastValue !== value && !state.hasUserInteracted && value !== undefined) {
|
|
2499
2503
|
state.singleValue = value;
|
|
2500
2504
|
state.lastValue = value;
|
|
2501
2505
|
}
|
|
2502
2506
|
}
|
|
2503
|
-
else if (state.singleValue === undefined) {
|
|
2504
|
-
state.singleValue = min;
|
|
2505
|
-
}
|
|
2506
2507
|
// Initialize range values
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
state.
|
|
2511
|
-
state.rangeMaxValue = currentMaxValue;
|
|
2508
|
+
if (isRangeControlled(attrs)) {
|
|
2509
|
+
// Always use values from props in controlled mode
|
|
2510
|
+
state.rangeMinValue = minValue !== undefined ? minValue : min;
|
|
2511
|
+
state.rangeMaxValue = maxValue !== undefined ? maxValue : max;
|
|
2512
2512
|
}
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
state.rangeMaxValue
|
|
2518
|
-
|
|
2519
|
-
|
|
2513
|
+
else {
|
|
2514
|
+
// Use internal state for uncontrolled mode
|
|
2515
|
+
const currentMinValue = minValue !== undefined ? minValue : min;
|
|
2516
|
+
const currentMaxValue = maxValue !== undefined ? maxValue : max;
|
|
2517
|
+
if (state.rangeMinValue === undefined || state.rangeMaxValue === undefined) {
|
|
2518
|
+
state.rangeMinValue = currentMinValue;
|
|
2519
|
+
state.rangeMaxValue = currentMaxValue;
|
|
2520
|
+
}
|
|
2521
|
+
if (!state.hasUserInteracted &&
|
|
2522
|
+
((minValue !== undefined && state.lastMinValue !== minValue) ||
|
|
2523
|
+
(maxValue !== undefined && state.lastMaxValue !== maxValue))) {
|
|
2524
|
+
state.rangeMinValue = currentMinValue;
|
|
2525
|
+
state.rangeMaxValue = currentMaxValue;
|
|
2526
|
+
state.lastMinValue = minValue;
|
|
2527
|
+
state.lastMaxValue = maxValue;
|
|
2528
|
+
}
|
|
2520
2529
|
}
|
|
2521
2530
|
// Initialize active thumb if not set
|
|
2522
2531
|
if (state.activeThumb === null) {
|
|
@@ -2533,15 +2542,18 @@
|
|
|
2533
2542
|
minValue = maxValue;
|
|
2534
2543
|
if (maxValue < minValue)
|
|
2535
2544
|
maxValue = minValue;
|
|
2536
|
-
state
|
|
2537
|
-
|
|
2545
|
+
// Only update internal state for uncontrolled mode
|
|
2546
|
+
if (!isRangeControlled(attrs)) {
|
|
2547
|
+
state.rangeMinValue = minValue;
|
|
2548
|
+
state.rangeMaxValue = maxValue;
|
|
2549
|
+
}
|
|
2538
2550
|
state.hasUserInteracted = true;
|
|
2539
|
-
// Call
|
|
2551
|
+
// Call appropriate handler based on interaction type, not control mode
|
|
2540
2552
|
if (immediate && attrs.oninput) {
|
|
2541
|
-
attrs.oninput(minValue, maxValue);
|
|
2553
|
+
attrs.oninput(minValue, maxValue); // Immediate feedback during drag
|
|
2542
2554
|
}
|
|
2543
|
-
|
|
2544
|
-
attrs.onchange(minValue, maxValue);
|
|
2555
|
+
if (!immediate && attrs.onchange) {
|
|
2556
|
+
attrs.onchange(minValue, maxValue); // Final value on interaction end (blur/mouseup)
|
|
2545
2557
|
}
|
|
2546
2558
|
};
|
|
2547
2559
|
// Single Range Slider Component
|
|
@@ -2575,19 +2587,24 @@
|
|
|
2575
2587
|
: tooltipPos
|
|
2576
2588
|
: tooltipPos;
|
|
2577
2589
|
const updateSingleValue = (newValue, immediate = false) => {
|
|
2578
|
-
state
|
|
2590
|
+
// Only update internal state for uncontrolled mode
|
|
2591
|
+
if (!isControlled(attrs)) {
|
|
2592
|
+
state.singleValue = newValue;
|
|
2593
|
+
}
|
|
2579
2594
|
state.hasUserInteracted = true;
|
|
2595
|
+
// Call appropriate handler based on interaction type, not control mode
|
|
2580
2596
|
if (immediate && oninput) {
|
|
2581
|
-
oninput(newValue);
|
|
2597
|
+
oninput(newValue); // Immediate feedback during drag
|
|
2582
2598
|
}
|
|
2583
|
-
|
|
2584
|
-
onchange(newValue);
|
|
2599
|
+
if (!immediate && onchange) {
|
|
2600
|
+
onchange(newValue); // Final value on interaction end (blur/mouseup)
|
|
2585
2601
|
}
|
|
2586
2602
|
};
|
|
2587
2603
|
const handleMouseDown = (e) => {
|
|
2588
2604
|
if (disabled)
|
|
2589
2605
|
return;
|
|
2590
2606
|
e.preventDefault();
|
|
2607
|
+
e.stopPropagation();
|
|
2591
2608
|
state.isDragging = true;
|
|
2592
2609
|
if (finalValueDisplay === 'auto') {
|
|
2593
2610
|
m.redraw();
|
|
@@ -2662,6 +2679,11 @@
|
|
|
2662
2679
|
updateSingleValue(newValue, false);
|
|
2663
2680
|
}
|
|
2664
2681
|
},
|
|
2682
|
+
onblur: () => {
|
|
2683
|
+
if (disabled || !onchange)
|
|
2684
|
+
return;
|
|
2685
|
+
onchange(state.singleValue);
|
|
2686
|
+
},
|
|
2665
2687
|
}, [
|
|
2666
2688
|
m(`.track.${orientation}`),
|
|
2667
2689
|
m(`.range-progress.${orientation}`, { style: progressStyle }),
|
|
@@ -2720,6 +2742,7 @@
|
|
|
2720
2742
|
if (disabled)
|
|
2721
2743
|
return;
|
|
2722
2744
|
e.preventDefault();
|
|
2745
|
+
e.stopPropagation();
|
|
2723
2746
|
state.isDragging = true;
|
|
2724
2747
|
state.activeThumb = thumb;
|
|
2725
2748
|
if (finalValueDisplay === 'auto') {
|
|
@@ -2810,6 +2833,11 @@
|
|
|
2810
2833
|
maxThumb.focus();
|
|
2811
2834
|
}
|
|
2812
2835
|
},
|
|
2836
|
+
onblur: () => {
|
|
2837
|
+
if (disabled || !attrs.onchange)
|
|
2838
|
+
return;
|
|
2839
|
+
attrs.onchange(state.rangeMinValue, state.rangeMaxValue);
|
|
2840
|
+
},
|
|
2813
2841
|
}, [
|
|
2814
2842
|
m(`.track.${orientation}`),
|
|
2815
2843
|
m(`.range.${orientation}`, { style: rangeStyle }),
|
|
@@ -3015,13 +3043,13 @@
|
|
|
3015
3043
|
overflowWrap: 'break-word',
|
|
3016
3044
|
},
|
|
3017
3045
|
oncreate: ({ dom }) => {
|
|
3018
|
-
const hiddenDiv = state.hiddenDiv = dom;
|
|
3046
|
+
const hiddenDiv = (state.hiddenDiv = dom);
|
|
3019
3047
|
if (state.textarea) {
|
|
3020
3048
|
updateHeight(state.textarea, hiddenDiv);
|
|
3021
3049
|
}
|
|
3022
3050
|
},
|
|
3023
3051
|
onupdate: ({ dom }) => {
|
|
3024
|
-
const hiddenDiv = state.hiddenDiv = dom;
|
|
3052
|
+
const hiddenDiv = (state.hiddenDiv = dom);
|
|
3025
3053
|
if (state.textarea) {
|
|
3026
3054
|
updateHeight(state.textarea, hiddenDiv);
|
|
3027
3055
|
}
|
|
@@ -3134,8 +3162,7 @@
|
|
|
3134
3162
|
isDragging: false,
|
|
3135
3163
|
activeThumb: null,
|
|
3136
3164
|
};
|
|
3137
|
-
const isControlled = (attrs) => 'value' in attrs && typeof attrs.value !== 'undefined' &&
|
|
3138
|
-
(typeof attrs.oninput === 'function' || typeof attrs.onchange === 'function');
|
|
3165
|
+
const isControlled = (attrs) => 'value' in attrs && typeof attrs.value !== 'undefined' && typeof attrs.oninput === 'function';
|
|
3139
3166
|
const getValue = (target) => {
|
|
3140
3167
|
const val = target.value;
|
|
3141
3168
|
return (val ? (type === 'number' || type === 'range' ? +val : val) : val);
|
|
@@ -3185,7 +3212,7 @@
|
|
|
3185
3212
|
const isNonInteractive = attrs.readonly || attrs.disabled;
|
|
3186
3213
|
// Warn developer for improper controlled usage
|
|
3187
3214
|
if (attrs.value !== undefined && !controlled && !isNonInteractive) {
|
|
3188
|
-
console.warn(`${type} input received 'value' prop without 'oninput'
|
|
3215
|
+
console.warn(`${type} input with label '${attrs.label}' received 'value' prop without 'oninput' handler. ` +
|
|
3189
3216
|
`Use 'defaultValue' for uncontrolled components or add an event handler for controlled components.`);
|
|
3190
3217
|
}
|
|
3191
3218
|
// Initialize internal value if not in controlled mode
|
|
@@ -4212,14 +4239,12 @@
|
|
|
4212
4239
|
inputRef: null,
|
|
4213
4240
|
dropdownRef: null,
|
|
4214
4241
|
internalCheckedId: undefined,
|
|
4242
|
+
isInsideModal: false,
|
|
4215
4243
|
};
|
|
4216
4244
|
const isControlled = (attrs) => attrs.checkedId !== undefined && typeof attrs.onchange === 'function';
|
|
4217
|
-
const closeDropdown = (
|
|
4218
|
-
|
|
4219
|
-
|
|
4220
|
-
state.isOpen = false;
|
|
4221
|
-
m.redraw();
|
|
4222
|
-
}
|
|
4245
|
+
const closeDropdown = () => {
|
|
4246
|
+
state.isOpen = false;
|
|
4247
|
+
m.redraw(); // Needed to remove the dropdown options list (potentially added to document root)
|
|
4223
4248
|
};
|
|
4224
4249
|
const handleKeyDown = (e, items) => {
|
|
4225
4250
|
const availableItems = items.filter((item) => !item.divider && !item.disabled);
|
|
@@ -4264,6 +4289,83 @@
|
|
|
4264
4289
|
return undefined;
|
|
4265
4290
|
}
|
|
4266
4291
|
};
|
|
4292
|
+
const getPortalStyles = (inputRef) => {
|
|
4293
|
+
if (!inputRef)
|
|
4294
|
+
return {};
|
|
4295
|
+
const rect = inputRef.getBoundingClientRect();
|
|
4296
|
+
const viewportHeight = window.innerHeight;
|
|
4297
|
+
const spaceBelow = viewportHeight - rect.bottom;
|
|
4298
|
+
const spaceAbove = rect.top;
|
|
4299
|
+
// Choose whether to show above or below based on available space
|
|
4300
|
+
const showAbove = spaceBelow < 200 && spaceAbove > spaceBelow;
|
|
4301
|
+
return {
|
|
4302
|
+
position: 'fixed',
|
|
4303
|
+
top: showAbove ? 'auto' : `${rect.bottom}px`,
|
|
4304
|
+
bottom: showAbove ? `${viewportHeight - rect.top}px` : 'auto',
|
|
4305
|
+
left: `${rect.left}px`,
|
|
4306
|
+
width: `${rect.width}px`,
|
|
4307
|
+
zIndex: 10000,
|
|
4308
|
+
maxHeight: showAbove ? `${spaceAbove - 20}px` : `${spaceBelow - 20}px`,
|
|
4309
|
+
overflow: 'auto',
|
|
4310
|
+
display: 'block',
|
|
4311
|
+
opacity: 1,
|
|
4312
|
+
};
|
|
4313
|
+
};
|
|
4314
|
+
const updatePortalDropdown = (items, selectedLabel, onSelectItem) => {
|
|
4315
|
+
if (!state.isInsideModal)
|
|
4316
|
+
return;
|
|
4317
|
+
// Clean up existing portal
|
|
4318
|
+
const existingPortal = document.getElementById(`${state.id}-dropdown`);
|
|
4319
|
+
if (existingPortal) {
|
|
4320
|
+
existingPortal.remove();
|
|
4321
|
+
}
|
|
4322
|
+
if (!state.isOpen || !state.inputRef)
|
|
4323
|
+
return;
|
|
4324
|
+
// Create portal element
|
|
4325
|
+
const portalElement = document.createElement('div');
|
|
4326
|
+
portalElement.id = `${state.id}-dropdown`;
|
|
4327
|
+
document.body.appendChild(portalElement);
|
|
4328
|
+
// Create dropdown content
|
|
4329
|
+
const availableItems = items.filter((item) => !item.divider && !item.disabled);
|
|
4330
|
+
const dropdownContent = items.map((item) => {
|
|
4331
|
+
if (item.divider) {
|
|
4332
|
+
return m('li.divider');
|
|
4333
|
+
}
|
|
4334
|
+
const itemIndex = availableItems.indexOf(item);
|
|
4335
|
+
const isSelected = selectedLabel === item.label;
|
|
4336
|
+
const isFocused = state.focusedIndex === itemIndex;
|
|
4337
|
+
return m('li', {
|
|
4338
|
+
class: `${isSelected ? 'selected' : ''} ${isFocused ? 'focused' : ''}${item.disabled ? ' disabled' : ''}`,
|
|
4339
|
+
onclick: item.disabled ? undefined : () => onSelectItem(item),
|
|
4340
|
+
}, m('span', {
|
|
4341
|
+
style: {
|
|
4342
|
+
display: 'flex',
|
|
4343
|
+
alignItems: 'center',
|
|
4344
|
+
padding: '14px 16px',
|
|
4345
|
+
},
|
|
4346
|
+
}, [
|
|
4347
|
+
item.iconName
|
|
4348
|
+
? m('i.material-icons', {
|
|
4349
|
+
style: { marginRight: '32px' },
|
|
4350
|
+
}, item.iconName)
|
|
4351
|
+
: undefined,
|
|
4352
|
+
item.label,
|
|
4353
|
+
]));
|
|
4354
|
+
});
|
|
4355
|
+
// Create dropdown with proper positioning
|
|
4356
|
+
const dropdownVnode = m('ul.dropdown-content.select-dropdown', {
|
|
4357
|
+
tabindex: 0,
|
|
4358
|
+
style: getPortalStyles(state.inputRef),
|
|
4359
|
+
oncreate: ({ dom }) => {
|
|
4360
|
+
state.dropdownRef = dom;
|
|
4361
|
+
},
|
|
4362
|
+
onremove: () => {
|
|
4363
|
+
state.dropdownRef = null;
|
|
4364
|
+
},
|
|
4365
|
+
}, dropdownContent);
|
|
4366
|
+
// Render to portal
|
|
4367
|
+
m.render(portalElement, dropdownVnode);
|
|
4368
|
+
};
|
|
4267
4369
|
return {
|
|
4268
4370
|
oninit: ({ attrs }) => {
|
|
4269
4371
|
var _a;
|
|
@@ -4275,9 +4377,18 @@
|
|
|
4275
4377
|
// Add global click listener to close dropdown
|
|
4276
4378
|
document.addEventListener('click', closeDropdown);
|
|
4277
4379
|
},
|
|
4380
|
+
oncreate: ({ dom }) => {
|
|
4381
|
+
// Detect if component is inside a modal
|
|
4382
|
+
state.isInsideModal = !!dom.closest('.modal');
|
|
4383
|
+
},
|
|
4278
4384
|
onremove: () => {
|
|
4279
4385
|
// Cleanup global listener
|
|
4280
4386
|
document.removeEventListener('click', closeDropdown);
|
|
4387
|
+
// Cleanup portal
|
|
4388
|
+
const portalElement = document.getElementById(`${state.id}-dropdown`);
|
|
4389
|
+
if (portalElement) {
|
|
4390
|
+
portalElement.remove();
|
|
4391
|
+
}
|
|
4281
4392
|
},
|
|
4282
4393
|
view: ({ attrs }) => {
|
|
4283
4394
|
const { checkedId, key, label, onchange, disabled = false, items, iconName, helperText, style, className = 'col s12', } = attrs;
|
|
@@ -4300,6 +4411,16 @@
|
|
|
4300
4411
|
: undefined;
|
|
4301
4412
|
const title = selectedItem ? selectedItem.label : label || 'Select';
|
|
4302
4413
|
const availableItems = items.filter((item) => !item.divider && !item.disabled);
|
|
4414
|
+
// Update portal dropdown when inside modal
|
|
4415
|
+
if (state.isInsideModal) {
|
|
4416
|
+
updatePortalDropdown(items, title, (item) => {
|
|
4417
|
+
if (item.id) {
|
|
4418
|
+
state.isOpen = false;
|
|
4419
|
+
state.focusedIndex = -1;
|
|
4420
|
+
handleSelection(item.id);
|
|
4421
|
+
}
|
|
4422
|
+
});
|
|
4423
|
+
}
|
|
4303
4424
|
return m('.dropdown-wrapper.input-field', { className, key, style }, [
|
|
4304
4425
|
iconName ? m('i.material-icons.prefix', iconName) : undefined,
|
|
4305
4426
|
m(HelperText, { helperText }),
|
|
@@ -4333,8 +4454,9 @@
|
|
|
4333
4454
|
}
|
|
4334
4455
|
},
|
|
4335
4456
|
}),
|
|
4336
|
-
// Dropdown Menu
|
|
4457
|
+
// Dropdown Menu - render inline only when NOT inside modal
|
|
4337
4458
|
state.isOpen &&
|
|
4459
|
+
!state.isInsideModal &&
|
|
4338
4460
|
m('ul.dropdown-content.select-dropdown', {
|
|
4339
4461
|
tabindex: 0,
|
|
4340
4462
|
role: 'listbox',
|
|
@@ -4784,7 +4906,7 @@
|
|
|
4784
4906
|
maxWidth: '75%',
|
|
4785
4907
|
borderRadius: '4px',
|
|
4786
4908
|
})), { 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)' }),
|
|
4787
|
-
onclick: (e) => e.stopPropagation(), // Prevent backdrop click when clicking inside modal
|
|
4909
|
+
// onclick: (e: Event) => e.stopPropagation(), // Prevent backdrop click when clicking inside modal
|
|
4788
4910
|
}, [
|
|
4789
4911
|
// Close button
|
|
4790
4912
|
showCloseButton &&
|
|
@@ -5880,6 +6002,7 @@
|
|
|
5880
6002
|
dropdownRef: null,
|
|
5881
6003
|
internalSelectedIds: [],
|
|
5882
6004
|
isInsideModal: false,
|
|
6005
|
+
isMultiple: false,
|
|
5883
6006
|
};
|
|
5884
6007
|
const isControlled = (attrs) => attrs.checkedId !== undefined && attrs.onchange !== undefined;
|
|
5885
6008
|
const isSelected = (id, selectedIds) => {
|
|
@@ -5962,10 +6085,16 @@
|
|
|
5962
6085
|
}
|
|
5963
6086
|
};
|
|
5964
6087
|
const closeDropdown = (e) => {
|
|
6088
|
+
if (!state.isMultiple) {
|
|
6089
|
+
state.isOpen = false;
|
|
6090
|
+
return;
|
|
6091
|
+
}
|
|
5965
6092
|
const target = e.target;
|
|
5966
|
-
|
|
6093
|
+
// When inside modal, check both the select component AND the portaled dropdown
|
|
6094
|
+
const isClickInsideSelect = target.closest('.input-field.select-space');
|
|
6095
|
+
const isClickInsidePortalDropdown = state.isInsideModal && state.dropdownRef && (state.dropdownRef.contains(target) || target === state.dropdownRef);
|
|
6096
|
+
if (!isClickInsideSelect && !isClickInsidePortalDropdown) {
|
|
5967
6097
|
state.isOpen = false;
|
|
5968
|
-
m.redraw();
|
|
5969
6098
|
}
|
|
5970
6099
|
};
|
|
5971
6100
|
const getPortalStyles = (inputRef) => {
|
|
@@ -5973,13 +6102,21 @@
|
|
|
5973
6102
|
return {};
|
|
5974
6103
|
const rect = inputRef.getBoundingClientRect();
|
|
5975
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;
|
|
5976
6109
|
return {
|
|
5977
6110
|
position: 'fixed',
|
|
5978
|
-
top: `${rect.bottom}px`,
|
|
6111
|
+
top: showAbove ? 'auto' : `${rect.bottom}px`,
|
|
6112
|
+
bottom: showAbove ? `${viewportHeight - rect.top}px` : 'auto',
|
|
5979
6113
|
left: `${rect.left}px`,
|
|
5980
6114
|
width: `${rect.width}px`,
|
|
5981
6115
|
zIndex: 10000, // Higher than modal z-index
|
|
5982
|
-
maxHeight: `${
|
|
6116
|
+
maxHeight: showAbove ? `${spaceAbove - 20}px` : `${spaceBelow - 20}px`, // Leave 20px margin
|
|
6117
|
+
overflow: 'auto',
|
|
6118
|
+
display: 'block',
|
|
6119
|
+
opacity: 1,
|
|
5983
6120
|
};
|
|
5984
6121
|
};
|
|
5985
6122
|
const renderDropdownContent = (attrs, selectedIds, multiple, placeholder) => [
|
|
@@ -5987,15 +6124,10 @@
|
|
|
5987
6124
|
// Render ungrouped options first
|
|
5988
6125
|
attrs.options
|
|
5989
6126
|
.filter((option) => !option.group)
|
|
5990
|
-
.map((option) => m('li', Object.assign({
|
|
5991
|
-
? 'disabled'
|
|
5992
|
-
: state.focusedIndex === attrs.options.indexOf(option)
|
|
5993
|
-
? 'focused'
|
|
5994
|
-
: '' }, (option.disabled
|
|
6127
|
+
.map((option) => m('li', Object.assign({ class: option.disabled ? 'disabled' : state.focusedIndex === attrs.options.indexOf(option) ? 'focused' : '' }, (option.disabled
|
|
5995
6128
|
? {}
|
|
5996
6129
|
: {
|
|
5997
|
-
onclick: (
|
|
5998
|
-
e.stopPropagation();
|
|
6130
|
+
onclick: () => {
|
|
5999
6131
|
toggleOption(option.id, multiple, attrs);
|
|
6000
6132
|
},
|
|
6001
6133
|
})), [
|
|
@@ -6025,8 +6157,8 @@
|
|
|
6025
6157
|
return groups;
|
|
6026
6158
|
}, {}))
|
|
6027
6159
|
.map(([groupName, groupOptions]) => [
|
|
6028
|
-
m('li.optgroup', {
|
|
6029
|
-
...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
|
|
6030
6162
|
? {}
|
|
6031
6163
|
: {
|
|
6032
6164
|
onclick: (e) => {
|
|
@@ -6053,23 +6185,20 @@
|
|
|
6053
6185
|
.reduce((acc, val) => acc.concat(val), []),
|
|
6054
6186
|
];
|
|
6055
6187
|
const updatePortalDropdown = (attrs, selectedIds, multiple, placeholder) => {
|
|
6056
|
-
var _a;
|
|
6057
6188
|
if (!state.isInsideModal)
|
|
6058
6189
|
return;
|
|
6059
|
-
|
|
6060
|
-
|
|
6061
|
-
|
|
6062
|
-
|
|
6063
|
-
m.render(portalElement, []);
|
|
6064
|
-
(_a = portalElement.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(portalElement);
|
|
6065
|
-
}
|
|
6066
|
-
return;
|
|
6067
|
-
}
|
|
6068
|
-
if (!portalElement) {
|
|
6069
|
-
portalElement = document.createElement('div');
|
|
6070
|
-
portalElement.id = state.dropdownId;
|
|
6071
|
-
document.body.appendChild(portalElement);
|
|
6190
|
+
// Clean up existing portal
|
|
6191
|
+
const existingPortal = document.getElementById(state.dropdownId);
|
|
6192
|
+
if (existingPortal) {
|
|
6193
|
+
existingPortal.remove();
|
|
6072
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
|
|
6073
6202
|
const dropdownVnode = m('ul.dropdown-content.select-dropdown', {
|
|
6074
6203
|
tabindex: 0,
|
|
6075
6204
|
style: getPortalStyles(state.inputRef),
|
|
@@ -6080,6 +6209,7 @@
|
|
|
6080
6209
|
state.dropdownRef = null;
|
|
6081
6210
|
},
|
|
6082
6211
|
}, renderDropdownContent(attrs, selectedIds, multiple, placeholder));
|
|
6212
|
+
// Render to portal
|
|
6083
6213
|
m.render(portalElement, dropdownVnode);
|
|
6084
6214
|
};
|
|
6085
6215
|
return {
|
|
@@ -6122,7 +6252,8 @@
|
|
|
6122
6252
|
view: ({ attrs }) => {
|
|
6123
6253
|
var _a;
|
|
6124
6254
|
const controlled = isControlled(attrs);
|
|
6125
|
-
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;
|
|
6126
6257
|
// Get selected IDs from props or internal state
|
|
6127
6258
|
let selectedIds;
|
|
6128
6259
|
if (controlled) {
|
|
@@ -6138,7 +6269,6 @@
|
|
|
6138
6269
|
// Interactive uncontrolled: use internal state
|
|
6139
6270
|
selectedIds = state.internalSelectedIds;
|
|
6140
6271
|
}
|
|
6141
|
-
const { newRow, className = 'col s12', key, options, multiple = false, label, helperText, placeholder = '', isMandatory, iconName, style, } = attrs;
|
|
6142
6272
|
const finalClassName = newRow ? `${className} clear` : className;
|
|
6143
6273
|
const selectedOptions = options.filter((opt) => isSelected(opt.id, selectedIds));
|
|
6144
6274
|
// Update portal dropdown when inside modal
|
|
@@ -6175,7 +6305,8 @@
|
|
|
6175
6305
|
},
|
|
6176
6306
|
}),
|
|
6177
6307
|
// Dropdown Menu - render inline only when NOT inside modal
|
|
6178
|
-
state.isOpen &&
|
|
6308
|
+
state.isOpen &&
|
|
6309
|
+
!state.isInsideModal &&
|
|
6179
6310
|
m('ul.dropdown-content.select-dropdown', {
|
|
6180
6311
|
tabindex: 0,
|
|
6181
6312
|
oncreate: ({ dom }) => {
|
|
@@ -6333,7 +6464,6 @@
|
|
|
6333
6464
|
}
|
|
6334
6465
|
state.isDragging = false;
|
|
6335
6466
|
state.translateX = 0;
|
|
6336
|
-
// m.redraw();
|
|
6337
6467
|
};
|
|
6338
6468
|
/** Initialize active tab - selectedTabId takes precedence, next active property or first available tab */
|
|
6339
6469
|
const setActiveTabId = (anchoredTabs, selectedTabId) => {
|
|
@@ -6360,7 +6490,6 @@
|
|
|
6360
6490
|
},
|
|
6361
6491
|
oncreate: () => {
|
|
6362
6492
|
updateIndicator();
|
|
6363
|
-
m.redraw();
|
|
6364
6493
|
},
|
|
6365
6494
|
view: ({ attrs }) => {
|
|
6366
6495
|
const { tabWidth, tabs, className, style, swipeable = false } = attrs;
|
|
@@ -6494,7 +6623,6 @@
|
|
|
6494
6623
|
else {
|
|
6495
6624
|
// Click outside, close dropdown
|
|
6496
6625
|
state.isOpen = false;
|
|
6497
|
-
m.redraw();
|
|
6498
6626
|
}
|
|
6499
6627
|
};
|
|
6500
6628
|
// Handle keyboard navigation
|
|
@@ -6729,9 +6857,7 @@
|
|
|
6729
6857
|
]),
|
|
6730
6858
|
// No options found message or list of options
|
|
6731
6859
|
...(filteredOptions.length === 0 && !showAddNew
|
|
6732
|
-
? [
|
|
6733
|
-
m('li.search-select-no-options', texts.noOptionsFound),
|
|
6734
|
-
]
|
|
6860
|
+
? [m('li.search-select-no-options', texts.noOptionsFound)]
|
|
6735
6861
|
: []),
|
|
6736
6862
|
// Add new option item
|
|
6737
6863
|
...(showAddNew
|
package/dist/utilities.css
CHANGED
package/package.json
CHANGED
|
@@ -150,9 +150,9 @@ body.keyboard-focused {
|
|
|
150
150
|
background-color: var(--mm-dropdown-hover, variables.$select-option-hover);
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
-
&.selected {
|
|
154
|
-
|
|
155
|
-
}
|
|
153
|
+
// &.selected {
|
|
154
|
+
// background-color: var(--mm-dropdown-selected, variables.$select-option-selected);
|
|
155
|
+
// }
|
|
156
156
|
}
|
|
157
157
|
}
|
|
158
158
|
|