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