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/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 ? (attrs.value || '') : state.internalValue;
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: controlled ? currentValue : undefined, oncreate: (vnode) => {
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
- // Delay closing to allow clicks on suggestions
367
- setTimeout(() => {
368
- if (!e.relatedTarget || !e.relatedTarget.closest('.autocomplete-content')) {
369
- state.isOpen = false;
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 (value !== undefined) {
2494
- const currentValue = value;
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 = currentValue;
2499
+ state.singleValue = defaultValue !== undefined ? defaultValue : value !== undefined ? value : min;
2497
2500
  }
2498
- if (state.lastValue !== value && !state.hasUserInteracted) {
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
- const currentMinValue = minValue !== undefined ? minValue : min;
2508
- const currentMaxValue = maxValue !== undefined ? maxValue : max;
2509
- if (state.rangeMinValue === undefined || state.rangeMaxValue === undefined) {
2510
- state.rangeMinValue = currentMinValue;
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
- if (!state.hasUserInteracted &&
2514
- ((minValue !== undefined && state.lastMinValue !== minValue) ||
2515
- (maxValue !== undefined && state.lastMaxValue !== maxValue))) {
2516
- state.rangeMinValue = currentMinValue;
2517
- state.rangeMaxValue = currentMaxValue;
2518
- state.lastMinValue = minValue;
2519
- state.lastMaxValue = maxValue;
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.rangeMinValue = minValue;
2537
- state.rangeMaxValue = maxValue;
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 oninput for immediate feedback or onchange for final changes
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
- else if (!immediate && attrs.onchange) {
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.singleValue = newValue;
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
- else if (!immediate && onchange) {
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' or 'onchange' handler. ` +
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 = (e) => {
4218
- const target = e.target;
4219
- if (!target.closest('.dropdown-wrapper.input-field')) {
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 using Select component's positioning logic
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
- if (!target.closest('.input-field.select-space')) {
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: `${viewportHeight - rect.bottom - 20}px`, // Leave 20px margin from bottom
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({ key: option.id, class: option.disabled
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: (e) => {
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', { key: `group-${groupName}`, tabindex: 0 }, m('span', groupName)),
6029
- ...groupOptions.map((option) => m('li', Object.assign({ key: option.id, class: `optgroup-option${option.disabled ? ' disabled' : ''}${isSelected(option.id, selectedIds) ? ' selected' : ''}${state.focusedIndex === attrs.options.indexOf(option) ? ' focused' : ''}` }, (option.disabled
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
- let portalElement = document.getElementById(state.dropdownId);
6060
- if (!state.isOpen) {
6061
- // Clean up portal when dropdown is closed
6062
- if (portalElement) {
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 && !state.isInsideModal &&
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
@@ -2225,6 +2225,7 @@ table span.badge {
2225
2225
  font-size: 16px;
2226
2226
  line-height: 32px;
2227
2227
  padding-left: 8px;
2228
+ min-height: 1lh;
2228
2229
  }
2229
2230
 
2230
2231
  .chips {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mithril-materialized",
3
- "version": "3.4.0",
3
+ "version": "3.4.1",
4
4
  "description": "A materialize library for mithril.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.esm.js",
@@ -33,6 +33,7 @@
33
33
  font-size: 16px;
34
34
  line-height: 32px;
35
35
  padding-left: 8px;
36
+ min-height: 1lh;
36
37
  }
37
38
  }
38
39
 
@@ -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
- background-color: var(--mm-dropdown-selected, variables.$select-option-selected);
155
- }
153
+ // &.selected {
154
+ // background-color: var(--mm-dropdown-selected, variables.$select-option-selected);
155
+ // }
156
156
  }
157
157
  }
158
158