mithril-materialized 3.15.0 → 3.17.0

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.
Files changed (47) hide show
  1. package/README.md +32 -2
  2. package/dist/advanced.css +109 -4
  3. package/dist/button.d.ts +3 -1
  4. package/dist/circular-progress.d.ts +2 -2
  5. package/dist/combobox.d.ts +35 -0
  6. package/dist/components.css +144 -17
  7. package/dist/core.css +278 -3
  8. package/dist/floating-action-button.d.ts +2 -1
  9. package/dist/form-section.d.ts +35 -0
  10. package/dist/forms.css +280 -5
  11. package/dist/index.css +357 -22
  12. package/dist/index.d.ts +3 -2
  13. package/dist/index.esm.js +728 -299
  14. package/dist/index.esm.js.map +1 -0
  15. package/dist/index.js +741 -298
  16. package/dist/index.js.map +1 -0
  17. package/dist/index.min.css +2 -2
  18. package/dist/index.umd.js +741 -298
  19. package/dist/index.umd.js.map +1 -0
  20. package/dist/input-options.d.ts +4 -1
  21. package/dist/likert-scale.d.ts +2 -1
  22. package/dist/linear-progress.d.ts +2 -2
  23. package/dist/material-icon.d.ts +2 -1
  24. package/dist/radio.d.ts +9 -0
  25. package/dist/range-slider.d.ts +3 -2
  26. package/dist/rating.d.ts +2 -1
  27. package/dist/search-select.d.ts +15 -2
  28. package/dist/select.d.ts +14 -1
  29. package/dist/sidenav.d.ts +11 -1
  30. package/dist/treeview.d.ts +2 -2
  31. package/dist/types.d.ts +5 -0
  32. package/dist/utilities.css +93 -2
  33. package/dist/utils.d.ts +30 -1
  34. package/package.json +20 -20
  35. package/sass/components/_badge-component.scss +8 -0
  36. package/sass/components/_buttons.scss +9 -9
  37. package/sass/components/_global.scss +93 -0
  38. package/sass/components/_likert-scale.scss +24 -0
  39. package/sass/components/_sidenav.scss +21 -3
  40. package/sass/components/_theme-variables.scss +22 -1
  41. package/sass/components/_toggle-group.scss +3 -0
  42. package/sass/components/forms/_form-groups.scss +1 -1
  43. package/sass/components/forms/_form-section.scss +63 -0
  44. package/sass/components/forms/_forms.scss +1 -0
  45. package/sass/components/forms/_input-fields.scss +55 -0
  46. package/sass/components/forms/_range-enhanced.scss +3 -3
  47. package/sass/components/forms/_select.scss +85 -0
package/dist/index.umd.js CHANGED
@@ -38,6 +38,35 @@
38
38
  return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
39
39
  };
40
40
 
41
+ // import './styles/input.css';
42
+ const Mandatory = { view: ({ attrs }) => m('span.mandatory', Object.assign({}, attrs), '*') };
43
+ /** Simple label element, used for most components. */
44
+ const Label = () => {
45
+ return {
46
+ view: (_a) => {
47
+ var _b = _a.attrs, { label, id, isMandatory, isActive, className, initialValue } = _b, params = __rest(_b, ["label", "id", "isMandatory", "isActive", "className", "initialValue"]);
48
+ return label
49
+ ? m('label', Object.assign(Object.assign({}, params), { className: [className, isActive ? 'active' : ''].filter(Boolean).join(' ').trim() || undefined, for: id, oncreate: ({ dom }) => {
50
+ if (!initialValue)
51
+ return;
52
+ const labelEl = dom;
53
+ labelEl.classList.add('active');
54
+ } }), [m.trust(label), isMandatory ? m(Mandatory) : undefined])
55
+ : undefined;
56
+ },
57
+ };
58
+ };
59
+ /** Create a helper text, often used for displaying a small help text. May be replaced by the validation message. */
60
+ const HelperText = () => {
61
+ return {
62
+ view: ({ attrs: { helperText, dataError, dataSuccess, className } }) => {
63
+ return helperText || dataError || dataSuccess
64
+ ? m('span.helper-text.left', { className, 'data-error': dataError, 'data-success': dataSuccess }, dataError ? m.trust(dataError) : dataSuccess ? m.trust(dataSuccess) : helperText ? m.trust(helperText) : '')
65
+ : undefined;
66
+ },
67
+ };
68
+ };
69
+
41
70
  // Utility functions for the library
42
71
  /**
43
72
  * Create a unique ID
@@ -98,6 +127,42 @@
98
127
  * @returns
99
128
  */
100
129
  const padLeft = (n, width = 2, z = '0') => String(n).padStart(width, z);
130
+ const normalizeSelection = (value) => {
131
+ if (value === undefined) {
132
+ return [];
133
+ }
134
+ return Array.isArray(value) ? value : [value];
135
+ };
136
+ const resolveControllableValue = ({ controlled, disabled, controlledValue, defaultValue, internalValue, fallbackValue, }) => {
137
+ var _a, _b;
138
+ if (controlled) {
139
+ return controlledValue !== null && controlledValue !== void 0 ? controlledValue : fallbackValue;
140
+ }
141
+ if (disabled) {
142
+ return (_a = defaultValue !== null && defaultValue !== void 0 ? defaultValue : controlledValue) !== null && _a !== void 0 ? _a : fallbackValue;
143
+ }
144
+ return (_b = internalValue !== null && internalValue !== void 0 ? internalValue : defaultValue) !== null && _b !== void 0 ? _b : fallbackValue;
145
+ };
146
+ const renderFieldChrome = ({ label, id, isMandatory, isActive, initialValue, helperText, dataError, dataSuccess, }) => {
147
+ return [
148
+ label
149
+ ? m(Label, {
150
+ label,
151
+ id,
152
+ isMandatory,
153
+ isActive,
154
+ initialValue,
155
+ })
156
+ : undefined,
157
+ helperText || dataError || dataSuccess
158
+ ? m(HelperText, {
159
+ helperText,
160
+ dataError,
161
+ dataSuccess,
162
+ })
163
+ : undefined,
164
+ ].filter(Boolean);
165
+ };
101
166
  // Keep only essential dropdown positioning styles
102
167
  const getDropdownStyles = (inputRef, overlap = false, options, isDropDown = false) => {
103
168
  if (!inputRef) {
@@ -129,8 +194,10 @@
129
194
  groups.add(option.group);
130
195
  }
131
196
  });
132
- // Calculate total height: options + group headers + padding
133
- estimatedHeight = totalOptions * itemHeight + groups.size * groupHeaderHeight;
197
+ // Match the select dropdown's CSS max-height. Positioning with the full
198
+ // option count would otherwise leave a gap above an input near the bottom
199
+ // of the viewport when the rendered menu is capped at 400px.
200
+ estimatedHeight = Math.min(totalOptions * itemHeight + groups.size * groupHeaderHeight, 400);
134
201
  }
135
202
  const spaceBelow = viewportHeight - rect.bottom;
136
203
  const spaceAbove = rect.top;
@@ -146,33 +213,26 @@
146
213
  const needsScrolling = estimatedHeight > effectiveAvailableSpace;
147
214
  // Calculate the actual height the dropdown will take
148
215
  const actualHeight = needsScrolling ? effectiveAvailableSpace : estimatedHeight;
149
- // Calculate positioning when dropdown should appear above
150
- let topOffset;
151
- if (shouldPositionAbove) {
152
- // Calculate how much space we actually have from top of viewport to top of input
153
- const availableSpaceFromViewportTop = rect.top;
154
- // If dropdown fits comfortably above input, use normal positioning
155
- if (actualHeight <= availableSpaceFromViewportTop) {
156
- topOffset = 12 - actualHeight + (isDropDown ? itemHeight : 0); // Bottom of dropdown aligns with top of input
157
- }
158
- else {
159
- // If dropdown is too tall, position it at the very top of viewport
160
- // This makes the dropdown use all available space from viewport top to input top
161
- topOffset = -availableSpaceFromViewportTop + 5; // 5px margin from viewport top
162
- }
163
- }
164
- else {
165
- topOffset = overlap ? 0 : '100%';
166
- }
167
216
  const styles = {
168
217
  display: 'block',
169
218
  opacity: 1,
170
219
  position: 'absolute',
171
- top: typeof topOffset === 'number' ? `${topOffset}px` : topOffset,
172
220
  left: '0',
173
221
  zIndex: 1000,
174
222
  width: `${rect.width}px`,
175
223
  };
224
+ if (shouldPositionAbove) {
225
+ // This menu is absolutely positioned inside the select wrapper. A negative
226
+ // `top` calculated from viewport coordinates becomes wrong when an ancestor
227
+ // scrolls (as ScenarioSpark's main area does). Anchor the menu to its local
228
+ // containing block instead; its height can then change without detaching it
229
+ // from the input.
230
+ styles.top = 'auto';
231
+ styles.bottom = `${isDropDown ? -18 : inputRef.offsetHeight - 12}px`;
232
+ }
233
+ else {
234
+ styles.top = overlap ? 0 : '100%';
235
+ }
176
236
  // Only add scrolling constraints when necessary
177
237
  if (needsScrolling) {
178
238
  styles.maxHeight = `${actualHeight}px`;
@@ -239,9 +299,17 @@
239
299
  * @param zIndex - Z-index for portal container (default: 1004)
240
300
  */
241
301
  const renderToPortal = (containerId, vnode, zIndex = 1004) => {
242
- const container = getPortalContainer(containerId, zIndex);
302
+ const existingContainer = portalContainers.get(containerId);
303
+ const container = existingContainer ? existingContainer.element : getPortalContainer(containerId, zIndex);
243
304
  m.render(container, vnode);
244
305
  };
306
+ const syncPortalContent = ({ containerId, shouldRender, vnode, zIndex = 1004 }) => {
307
+ if (!shouldRender || vnode === null) {
308
+ clearPortal(containerId);
309
+ return;
310
+ }
311
+ renderToPortal(containerId, vnode, zIndex);
312
+ };
245
313
  /**
246
314
  * Clears portal content and releases container reference.
247
315
  * If this is the last reference, the container will be removed from the DOM.
@@ -256,35 +324,6 @@
256
324
  }
257
325
  };
258
326
 
259
- // import './styles/input.css';
260
- const Mandatory = { view: ({ attrs }) => m('span.mandatory', Object.assign({}, attrs), '*') };
261
- /** Simple label element, used for most components. */
262
- const Label = () => {
263
- return {
264
- view: (_a) => {
265
- var _b = _a.attrs, { label, id, isMandatory, isActive, className, initialValue } = _b, params = __rest(_b, ["label", "id", "isMandatory", "isActive", "className", "initialValue"]);
266
- return label
267
- ? m('label', Object.assign(Object.assign({}, params), { className: [className, isActive ? 'active' : ''].filter(Boolean).join(' ').trim() || undefined, for: id, oncreate: ({ dom }) => {
268
- if (!initialValue)
269
- return;
270
- const labelEl = dom;
271
- labelEl.classList.add('active');
272
- } }), [m.trust(label), isMandatory ? m(Mandatory) : undefined])
273
- : undefined;
274
- },
275
- };
276
- };
277
- /** Create a helper text, often used for displaying a small help text. May be replaced by the validation message. */
278
- const HelperText = () => {
279
- return {
280
- view: ({ attrs: { helperText, dataError, dataSuccess, className } }) => {
281
- return helperText || dataError || dataSuccess
282
- ? m('span.helper-text.left', { className, 'data-error': dataError, 'data-success': dataSuccess }, dataError ? m.trust(dataError) : dataSuccess ? m.trust(dataSuccess) : helperText ? m.trust(helperText) : '')
283
- : undefined;
284
- },
285
- };
286
- };
287
-
288
327
  /** Component to auto complete your text input - Pure Mithril implementation */
289
328
  const Autocomplete = () => {
290
329
  const state = {
@@ -307,6 +346,28 @@
307
346
  .slice(0, limit);
308
347
  return filtered;
309
348
  };
349
+ const highlightMatch = (text, query) => {
350
+ if (!query) {
351
+ return text;
352
+ }
353
+ const lowerText = text.toLowerCase();
354
+ const lowerQuery = query.toLowerCase();
355
+ const nodes = [];
356
+ let start = 0;
357
+ let index = lowerText.indexOf(lowerQuery, start);
358
+ while (index !== -1) {
359
+ if (index > start) {
360
+ nodes.push(text.slice(start, index));
361
+ }
362
+ nodes.push(m('span.highlight', text.slice(index, index + query.length)));
363
+ start = index + query.length;
364
+ index = lowerText.indexOf(lowerQuery, start);
365
+ }
366
+ if (start < text.length) {
367
+ nodes.push(text.slice(start));
368
+ }
369
+ return nodes.length > 0 ? nodes : text;
370
+ };
310
371
  const selectSuggestion = (suggestion, attrs) => {
311
372
  const controlled = isControlled(attrs);
312
373
  // Update internal state for uncontrolled mode
@@ -412,7 +473,6 @@
412
473
  Object.keys(data).some((key) => key.toLowerCase() === currentValue.toLowerCase());
413
474
  // Only open dropdown if there are suggestions and no perfect match
414
475
  state.isOpen = state.suggestions.length > 0 && currentValue.length >= minLength && !hasExactMatch;
415
- const replacer = new RegExp(`(${currentValue})`, 'i');
416
476
  return m('.input-field.autocomplete-wrapper', {
417
477
  className: cn,
418
478
  style,
@@ -493,9 +553,7 @@
493
553
  },
494
554
  }, suggestion.value.replace('icon:', ''))
495
555
  : null,
496
- m('span', suggestion.key
497
- ? m.trust(suggestion.key.replace(replacer, (i) => `<span class="highlight">${i}</span>`))
498
- : ''),
556
+ m('span', suggestion.key ? highlightMatch(suggestion.key, currentValue) : ''),
499
557
  ]))),
500
558
  m(Label, {
501
559
  label,
@@ -722,7 +780,7 @@
722
780
  /**
723
781
  * A factory to create new buttons.
724
782
  *
725
- * @example FlatButton = ButtonFactory('a.waves-effect.waves-teal.btn-flat');
783
+ * @example FlatButton = ButtonFactory('button.waves-effect.waves-teal.btn-flat');
726
784
  */
727
785
  const ButtonFactory = (element, defaultClassNames, type = '') => {
728
786
  return () => {
@@ -745,15 +803,21 @@
745
803
  ontouchend: WavesEffect.onTouchEnd,
746
804
  }
747
805
  : {};
748
- return m(element, Object.assign(Object.assign(Object.assign({}, params), wavesHandlers), { className: cn, 'data-position': tooltip ? position : undefined, 'data-tooltip': tooltip || undefined, type: buttonType }), iconName ? m(Icon, { iconName, className: iconClass !== undefined ? iconClass : 'left' }) : undefined, label ? label : undefined, children);
806
+ const tagName = element.split(/[.#]/)[0] || element;
807
+ const elementSuffix = element.slice(tagName.length);
808
+ const isLink = Boolean(attrs.href);
809
+ const renderedElement = `${isLink ? 'a' : 'button'}${elementSuffix}`;
810
+ return m(renderedElement, Object.assign(Object.assign(Object.assign({}, params), wavesHandlers), { className: cn, 'data-position': tooltip ? position : undefined, 'data-tooltip': tooltip || undefined,
811
+ // Links retain native navigation semantics and must not have a button type.
812
+ type: isLink ? undefined : buttonType }), iconName ? m(Icon, { iconName, className: iconClass !== undefined ? iconClass : 'left' }) : undefined, label ? label : undefined, children);
749
813
  },
750
814
  };
751
815
  };
752
816
  };
753
- const Button = ButtonFactory('a', 'waves-effect waves-light btn', 'button');
754
- const LargeButton = ButtonFactory('a', 'waves-effect waves-light btn-large', 'button');
755
- const SmallButton = ButtonFactory('a', 'waves-effect waves-light btn-small', 'button');
756
- const FlatButton = ButtonFactory('a', 'waves-effect waves-teal btn-flat', 'button');
817
+ const Button = ButtonFactory('button', 'waves-effect waves-light btn', 'button');
818
+ const LargeButton = ButtonFactory('button', 'waves-effect waves-light btn-large', 'button');
819
+ const SmallButton = ButtonFactory('button', 'waves-effect waves-light btn-small', 'button');
820
+ const FlatButton = ButtonFactory('button', 'waves-effect waves-teal btn-flat', 'button');
757
821
  const IconButton = ButtonFactory('button', 'btn-flat btn-icon waves-effect waves-teal', 'button');
758
822
  const RoundIconButton = ButtonFactory('button', 'btn-floating btn-large waves-effect waves-light', 'button');
759
823
  const SubmitButton = ButtonFactory('button', 'btn waves-effect waves-light', 'submit');
@@ -810,7 +874,7 @@
810
874
  // Add square styling for icon-only confirming state
811
875
  const buttonStyle = !label && isConfirming
812
876
  ? Object.assign(Object.assign({}, props.style), { width: '36px', height: '36px', padding: '0', minWidth: '28px', display: 'inline-flex', alignItems: 'center', justifyContent: 'center', borderRadius: '2px' }) : props.style;
813
- return m(ButtonComponent, Object.assign(Object.assign({}, props), { style: buttonStyle, className: `${props.className || ''} ${cn}`, iconName: currentIconName, iconClass: label ? (iconClass || 'left') : '', label, onclick: handleClick }));
877
+ return m(ButtonComponent, Object.assign(Object.assign({}, props), { style: buttonStyle, className: `${props.className || ''} ${cn}`, iconName: currentIconName, iconClass: label ? iconClass || 'left' : '', label, onclick: handleClick }));
814
878
  },
815
879
  };
816
880
  };
@@ -1277,11 +1341,19 @@
1277
1341
  };
1278
1342
  const rotation = (_a = rotationMap[direction]) !== null && _a !== void 0 ? _a : 0;
1279
1343
  const transform = rotation ? `rotate(${rotation}deg)` : undefined;
1344
+ const baseStyle = {
1345
+ display: 'inline-block',
1346
+ verticalAlign: 'middle',
1347
+ transform,
1348
+ };
1349
+ const combinedStyle = typeof style === 'string'
1350
+ ? `display:inline-block;vertical-align:middle;${transform ? `transform:${transform};` : ''}${style}`
1351
+ : Object.assign(Object.assign({}, baseStyle), style);
1280
1352
  const icon = iconPaths[name];
1281
1353
  if (!icon || !Array.isArray(icon)) {
1282
1354
  return m(Icon, Object.assign(Object.assign({}, props), { iconName: name }));
1283
1355
  }
1284
- return m('svg', Object.assign(Object.assign({}, props), { style: Object.assign({ display: 'inline-block', verticalAlign: 'middle', transform }, style), height: '24px', width: '24px', viewBox: '0 0 24 24', xmlns: 'http://www.w3.org/2000/svg' }), icon === null || icon === void 0 ? void 0 : icon.map((d) => m('path', {
1356
+ return m('svg', Object.assign(Object.assign({}, props), { style: combinedStyle, height: '24px', width: '24px', viewBox: '0 0 24 24', xmlns: 'http://www.w3.org/2000/svg' }), icon === null || icon === void 0 ? void 0 : icon.map((d) => m('path', {
1285
1357
  d,
1286
1358
  fill: d.includes('M0 0h24v24H0z') ? 'none' : 'currentColor',
1287
1359
  })));
@@ -1680,6 +1752,106 @@
1680
1752
  };
1681
1753
  };
1682
1754
 
1755
+ const isHandledKey = (key) => key === 'ArrowDown' || key === 'ArrowUp' || key === 'Enter' || key === ' ' || key === 'Escape';
1756
+ const getTotalRows = (optionCount, includeActionRow) => Math.max(0, optionCount) + (includeActionRow ? 1 : 0);
1757
+ const getComboboxKeyResult = ({ key, isOpen, focusedIndex, optionCount, includeActionRow, }) => {
1758
+ const totalRows = getTotalRows(optionCount, includeActionRow);
1759
+ if (!isHandledKey(key)) {
1760
+ return { isOpen, focusedIndex, action: 'none', preventDefault: false };
1761
+ }
1762
+ if (key === 'Escape') {
1763
+ return { isOpen: false, focusedIndex: -1, action: 'close', preventDefault: true };
1764
+ }
1765
+ if (key === 'ArrowDown') {
1766
+ if (!isOpen) {
1767
+ return {
1768
+ isOpen: true,
1769
+ focusedIndex: totalRows > 0 ? 0 : -1,
1770
+ action: 'open',
1771
+ preventDefault: true,
1772
+ };
1773
+ }
1774
+ return {
1775
+ isOpen: true,
1776
+ focusedIndex: totalRows > 0 ? Math.min(focusedIndex + 1, totalRows - 1) : -1,
1777
+ action: 'none',
1778
+ preventDefault: true,
1779
+ };
1780
+ }
1781
+ if (key === 'ArrowUp') {
1782
+ if (!isOpen) {
1783
+ return {
1784
+ isOpen: true,
1785
+ focusedIndex: totalRows > 0 ? 0 : -1,
1786
+ action: 'open',
1787
+ preventDefault: true,
1788
+ };
1789
+ }
1790
+ return {
1791
+ isOpen: true,
1792
+ focusedIndex: totalRows > 0 ? Math.max(focusedIndex - 1, 0) : -1,
1793
+ action: 'none',
1794
+ preventDefault: true,
1795
+ };
1796
+ }
1797
+ if (!isOpen) {
1798
+ return {
1799
+ isOpen: true,
1800
+ focusedIndex: totalRows > 0 ? 0 : -1,
1801
+ action: 'open',
1802
+ preventDefault: true,
1803
+ };
1804
+ }
1805
+ if (focusedIndex < 0) {
1806
+ return { isOpen, focusedIndex, action: 'none', preventDefault: true };
1807
+ }
1808
+ const isActionRow = includeActionRow && focusedIndex === optionCount;
1809
+ return {
1810
+ isOpen,
1811
+ focusedIndex,
1812
+ action: isActionRow ? 'selectAction' : 'selectFocused',
1813
+ preventDefault: true,
1814
+ };
1815
+ };
1816
+ const getComboboxOptionId = (baseId, optionIndex) => `${baseId}-option-${optionIndex}`;
1817
+ const createAsyncComboboxState = (initialOptions = []) => ({
1818
+ options: initialOptions,
1819
+ isLoading: false,
1820
+ error: null,
1821
+ latestRequestId: 0,
1822
+ });
1823
+ const startAsyncComboboxRequest = (state) => {
1824
+ const requestId = state.latestRequestId + 1;
1825
+ return {
1826
+ requestId,
1827
+ nextState: Object.assign(Object.assign({}, state), { isLoading: true, error: null, latestRequestId: requestId }),
1828
+ };
1829
+ };
1830
+ const resolveAsyncComboboxRequest = (state, requestId, options) => {
1831
+ if (requestId !== state.latestRequestId) {
1832
+ return state;
1833
+ }
1834
+ return Object.assign(Object.assign({}, state), { options, isLoading: false, error: null });
1835
+ };
1836
+ const rejectAsyncComboboxRequest = (state, requestId, errorMessage) => {
1837
+ if (requestId !== state.latestRequestId) {
1838
+ return state;
1839
+ }
1840
+ return Object.assign(Object.assign({}, state), { options: [], isLoading: false, error: errorMessage });
1841
+ };
1842
+ const getComboboxViewState = ({ isLoading, error, optionCount, }) => {
1843
+ if (isLoading) {
1844
+ return 'loading';
1845
+ }
1846
+ if (error) {
1847
+ return 'error';
1848
+ }
1849
+ if (optionCount === 0) {
1850
+ return 'empty';
1851
+ }
1852
+ return 'ready';
1853
+ };
1854
+
1683
1855
  exports.CollectionMode = void 0;
1684
1856
  (function (CollectionMode) {
1685
1857
  CollectionMode[CollectionMode["BASIC"] = 0] = "BASIC";
@@ -2379,6 +2551,7 @@
2379
2551
  e.stopPropagation();
2380
2552
  gotoMonth(index);
2381
2553
  state.monthDropdownOpen = false;
2554
+ m.redraw();
2382
2555
  },
2383
2556
  }, monthName))),
2384
2557
  ]),
@@ -2403,6 +2576,7 @@
2403
2576
  e.stopPropagation();
2404
2577
  gotoYear(i);
2405
2578
  state.yearDropdownOpen = false;
2579
+ m.redraw();
2406
2580
  },
2407
2581
  }, i))),
2408
2582
  ]),
@@ -3287,6 +3461,10 @@
3287
3461
  },
3288
3462
  };
3289
3463
 
3464
+ const isReadonly = (attrs) => {
3465
+ const legacyReadonly = attrs.readonly;
3466
+ return Boolean(attrs.readOnly || legacyReadonly);
3467
+ };
3290
3468
  /** Character counter component that tracks text length against maxLength */
3291
3469
  const CharacterCounter = () => {
3292
3470
  return {
@@ -3365,7 +3543,7 @@
3365
3543
  return {
3366
3544
  oninit: ({ attrs }) => {
3367
3545
  const controlled = isControlled(attrs);
3368
- const isNonInteractive = attrs.readonly || attrs.disabled;
3546
+ const isNonInteractive = isReadonly(attrs) || attrs.disabled;
3369
3547
  // Warn developer for improper controlled usage
3370
3548
  if (attrs.value !== undefined && !controlled && !isNonInteractive) {
3371
3549
  console.warn(`TextArea received 'value' prop without 'oninput' or 'onchange' handler. ` +
@@ -3382,7 +3560,7 @@
3382
3560
  var _a, _b, _c, _d;
3383
3561
  const { className = 'col s12', helperText, iconName, id = state.id, value, placeholder, isMandatory, label, maxLength, oninput, onchange, onkeydown, onkeypress, onkeyup, onblur, style } = attrs, params = __rest(attrs, ["className", "helperText", "iconName", "id", "value", "placeholder", "isMandatory", "label", "maxLength", "oninput", "onchange", "onkeydown", "onkeypress", "onkeyup", "onblur", "style"]);
3384
3562
  const controlled = isControlled(attrs);
3385
- const isNonInteractive = attrs.readonly || attrs.disabled;
3563
+ const isNonInteractive = isReadonly(attrs) || attrs.disabled;
3386
3564
  let currentValue;
3387
3565
  if (controlled) {
3388
3566
  currentValue = value || '';
@@ -3498,14 +3676,12 @@
3498
3676
  onkeypress(ev, ev.target.value);
3499
3677
  }
3500
3678
  : undefined })),
3501
- m(Label, {
3679
+ ...renderFieldChrome({
3502
3680
  label,
3503
3681
  id,
3504
3682
  isMandatory,
3505
3683
  isActive: currentValue || placeholder || state.active,
3506
3684
  initialValue: currentValue !== '',
3507
- }),
3508
- m(HelperText, {
3509
3685
  helperText,
3510
3686
  dataError: state.hasInteracted && attrs.dataError ? attrs.dataError : undefined,
3511
3687
  dataSuccess: state.hasInteracted && attrs.dataSuccess ? attrs.dataSuccess : undefined,
@@ -3585,7 +3761,7 @@
3585
3761
  return {
3586
3762
  oninit: ({ attrs }) => {
3587
3763
  const controlled = isControlled(attrs);
3588
- const isNonInteractive = attrs.readonly || attrs.disabled;
3764
+ const isNonInteractive = isReadonly(attrs) || attrs.disabled;
3589
3765
  // Warn developer for improper controlled usage
3590
3766
  if (attrs.value !== undefined && !controlled && !isNonInteractive) {
3591
3767
  console.warn(`${type} input with label '${attrs.label}' received 'value' prop without 'oninput' handler. ` +
@@ -3625,7 +3801,7 @@
3625
3801
  }
3626
3802
  const isNumeric = ['number', 'range'].includes(type);
3627
3803
  const controlled = isControlled(attrs);
3628
- const isNonInteractive = attrs.readonly || attrs.disabled;
3804
+ const isNonInteractive = isReadonly(attrs) || attrs.disabled;
3629
3805
  let value;
3630
3806
  if (controlled) {
3631
3807
  value = attrs.value;
@@ -3644,9 +3820,28 @@
3644
3820
  const rangeType = type === 'range' && !attrs.minmax;
3645
3821
  // Only add validate class if input is interactive and validation is needed
3646
3822
  const shouldValidate = !isNonInteractive && (validate || type === 'email' || type === 'url' || isNumeric);
3823
+ const inputClass = [
3824
+ type === 'number' ? 'number-input' : '',
3825
+ type === 'range' && attrs.vertical ? 'range-slider vertical' : '',
3826
+ shouldValidate ? 'validate' : '',
3827
+ ]
3828
+ .filter(Boolean)
3829
+ .join(' ');
3830
+ const stepNumberInput = (direction) => {
3831
+ const input = state.inputElement;
3832
+ if (!input)
3833
+ return;
3834
+ if (direction === 'up') {
3835
+ input.stepUp();
3836
+ }
3837
+ else {
3838
+ input.stepDown();
3839
+ }
3840
+ input.dispatchEvent(new Event('input', { bubbles: true }));
3841
+ };
3647
3842
  return m('.input-field', { className: cn, style }, [
3648
3843
  iconName ? m('i.material-icons.prefix', iconName) : undefined,
3649
- m('input', Object.assign(Object.assign({ class: type === 'range' && attrs.vertical ? 'range-slider vertical' : shouldValidate ? 'validate' : undefined }, params), { type, tabindex: 0, id,
3844
+ m('input', Object.assign(Object.assign({ class: inputClass || undefined }, params), { type, tabindex: 0, id,
3650
3845
  placeholder, value: controlled ? value : undefined, style: type === 'range' && attrs.vertical
3651
3846
  ? {
3652
3847
  height: attrs.height || '200px',
@@ -3728,7 +3923,7 @@
3728
3923
  const target = e.target;
3729
3924
  state.hasInteracted = true;
3730
3925
  // Skip validation for readonly/disabled inputs
3731
- if (attrs.readonly || attrs.disabled) {
3926
+ if (isReadonly(attrs) || attrs.disabled) {
3732
3927
  // Call original onblur if provided
3733
3928
  if (attrs.onblur) {
3734
3929
  attrs.onblur(e);
@@ -3818,6 +4013,22 @@
3818
4013
  onchange(getValue(state.inputElement));
3819
4014
  }
3820
4015
  } })),
4016
+ type === 'number' && !isNonInteractive
4017
+ ? m('.number-input-controls', [
4018
+ m('button.number-input-control.number-input-control-up[type=button]', {
4019
+ 'aria-label': `Increase ${label || 'value'}`,
4020
+ title: 'Increase value',
4021
+ onmousedown: (event) => event.preventDefault(),
4022
+ onclick: () => stepNumberInput('up'),
4023
+ }, m(MaterialIcon, { name: 'chevron', direction: 'up' })),
4024
+ m('button.number-input-control.number-input-control-down[type=button]', {
4025
+ 'aria-label': `Decrease ${label || 'value'}`,
4026
+ title: 'Decrease value',
4027
+ onmousedown: (event) => event.preventDefault(),
4028
+ onclick: () => stepNumberInput('down'),
4029
+ }, m(MaterialIcon, { name: 'chevron' })),
4030
+ ])
4031
+ : undefined,
3821
4032
  // Clear button - only for text inputs with canClear enabled and has content
3822
4033
  canClear && type === 'text' && ((_f = state.inputElement) === null || _f === void 0 ? void 0 : _f.value)
3823
4034
  ? m(MaterialIcon, {
@@ -3830,14 +4041,12 @@
3830
4041
  },
3831
4042
  })
3832
4043
  : undefined,
3833
- m(Label, {
4044
+ ...renderFieldChrome({
3834
4045
  label,
3835
4046
  id,
3836
4047
  isMandatory,
3837
4048
  isActive,
3838
4049
  initialValue: value !== undefined && value !== '',
3839
- }),
3840
- m(HelperText, {
3841
4050
  helperText,
3842
4051
  dataError: state.hasInteracted && !state.isValid ? dataError : undefined,
3843
4052
  dataSuccess: state.hasInteracted && state.isValid ? dataSuccess : undefined,
@@ -3937,7 +4146,7 @@
3937
4146
  view: ({ attrs: { className = 'col s12', onchange, label, checked, disabled, description, style, inputId } }) => {
3938
4147
  if (!checkboxId)
3939
4148
  checkboxId = inputId || uniqueId();
3940
- return m('div', { className, style }, m('label', { for: checkboxId }, [
4149
+ return m('div', { className, style }, m('label', { for: checkboxId, style: { position: 'relative', display: 'inline-block' } }, [
3941
4150
  m('input[type=checkbox][tabindex=0]', {
3942
4151
  className: disabled ? 'disabled' : undefined,
3943
4152
  id: checkboxId,
@@ -4702,17 +4911,6 @@
4702
4911
  const updatePortalDropdown = (items, selectedLabel, onSelectItem, maxHeight) => {
4703
4912
  if (!state.isInsideModal)
4704
4913
  return;
4705
- // Clean up existing portal
4706
- const existingPortal = document.getElementById(`${state.id}-dropdown`);
4707
- if (existingPortal) {
4708
- existingPortal.remove();
4709
- }
4710
- if (!state.isOpen || !state.inputRef)
4711
- return;
4712
- // Create portal element
4713
- const portalElement = document.createElement('div');
4714
- portalElement.id = `${state.id}-dropdown`;
4715
- document.body.appendChild(portalElement);
4716
4914
  // Create dropdown content
4717
4915
  const availableItems = items.filter((item) => !item.divider && !item.disabled);
4718
4916
  const dropdownContent = items.map((item) => {
@@ -4726,15 +4924,14 @@
4726
4924
  class: `${isSelected ? 'selected' : ''} ${isFocused ? 'focused' : ''}${item.disabled ? ' disabled' : ''}`,
4727
4925
  onclick: item.disabled ? undefined : () => onSelectItem(item),
4728
4926
  }, m('span', {
4927
+ class: 'mm-layout-row mm-layout-row--center',
4729
4928
  style: {
4730
- display: 'flex',
4731
- alignItems: 'center',
4732
4929
  padding: '14px 16px',
4733
4930
  },
4734
4931
  }, [
4735
4932
  item.iconName
4736
4933
  ? m('i.material-icons', {
4737
- style: { marginRight: '32px' },
4934
+ class: 'mm-layout-item-icon',
4738
4935
  }, item.iconName)
4739
4936
  : undefined,
4740
4937
  item.label,
@@ -4751,8 +4948,12 @@
4751
4948
  state.dropdownRef = null;
4752
4949
  },
4753
4950
  }, dropdownContent);
4754
- // Render to portal
4755
- m.render(portalElement, dropdownVnode);
4951
+ syncPortalContent({
4952
+ containerId: `${state.id}-dropdown`,
4953
+ shouldRender: state.isOpen && !!state.inputRef,
4954
+ vnode: dropdownVnode,
4955
+ zIndex: 10000,
4956
+ });
4756
4957
  };
4757
4958
  return {
4758
4959
  oninit: ({ attrs }) => {
@@ -4773,15 +4974,19 @@
4773
4974
  // Cleanup global listener
4774
4975
  document.removeEventListener('click', closeDropdown);
4775
4976
  // Cleanup portal
4776
- const portalElement = document.getElementById(`${state.id}-dropdown`);
4777
- if (portalElement) {
4778
- portalElement.remove();
4779
- }
4977
+ syncPortalContent({ containerId: `${state.id}-dropdown`, shouldRender: false, vnode: null });
4780
4978
  },
4781
4979
  view: ({ attrs }) => {
4782
4980
  const { checkedId, key, label, onchange, disabled = false, items, iconName, helperText, style, className = 'col s12', } = attrs;
4783
4981
  const controlled = isControlled(attrs);
4784
- const currentCheckedId = controlled ? checkedId : state.internalCheckedId;
4982
+ const currentCheckedId = resolveControllableValue({
4983
+ controlled,
4984
+ disabled,
4985
+ controlledValue: checkedId,
4986
+ defaultValue: attrs.defaultCheckedId,
4987
+ internalValue: state.internalCheckedId,
4988
+ fallbackValue: undefined,
4989
+ });
4785
4990
  const handleSelection = (value) => {
4786
4991
  // Update internal state for uncontrolled mode
4787
4992
  if (!controlled) {
@@ -4813,6 +5018,7 @@
4813
5018
  iconName ? m('i.material-icons.prefix', iconName) : undefined,
4814
5019
  m(HelperText, { helperText }),
4815
5020
  m('.select-wrapper', {
5021
+ class: 'mm-layout-row mm-layout-row--center',
4816
5022
  onkeydown: disabled
4817
5023
  ? undefined
4818
5024
  : (e) => {
@@ -4880,15 +5086,14 @@
4880
5086
  handleSelection(value);
4881
5087
  },
4882
5088
  }, m('span', {
5089
+ class: 'mm-layout-row mm-layout-row--center',
4883
5090
  style: {
4884
- display: 'flex',
4885
- alignItems: 'center',
4886
5091
  padding: '14px 16px',
4887
5092
  },
4888
5093
  }, [
4889
5094
  item.iconName
4890
5095
  ? m('i.material-icons', {
4891
- style: { marginRight: '32px' },
5096
+ class: 'mm-layout-item-icon',
4892
5097
  }, item.iconName)
4893
5098
  : undefined,
4894
5099
  item.label,
@@ -6845,9 +7050,9 @@
6845
7050
  };
6846
7051
 
6847
7052
  const RadioButton = () => ({
6848
- view: ({ attrs: { id, groupId, label, onchange, className = 'col s12', checked, disabled, inputId } }) => {
7053
+ view: ({ attrs: { id, groupId, label, onchange, className = 'col s12', checked, disabled, inputId, allowHtml } }) => {
6849
7054
  const radioId = inputId || `${groupId}-${id}`;
6850
- return m('p', { className }, m('label', { for: radioId }, [
7055
+ return m('p', { className }, m('label', { for: radioId, style: { position: 'relative', display: 'inline-block' } }, [
6851
7056
  m('input[type=radio][tabindex=0]', {
6852
7057
  id: radioId,
6853
7058
  name: groupId,
@@ -6855,7 +7060,7 @@
6855
7060
  checked,
6856
7061
  onclick: onchange ? () => onchange(id) : undefined,
6857
7062
  }),
6858
- m('span', m.trust(label)),
7063
+ m('span', allowHtml ? m.trust(label) : label),
6859
7064
  ]));
6860
7065
  },
6861
7066
  });
@@ -6882,8 +7087,9 @@
6882
7087
  }
6883
7088
  },
6884
7089
  view: ({ attrs }) => {
6885
- var _a, _b;
6886
- const { checkedId, newRow, className = 'col s12', label = '', disabled, description, options, isMandatory, checkboxClass, layout = 'vertical', onchange, } = attrs;
7090
+ var _a, _b, _c;
7091
+ const { checkedId, newRow, className = 'col s12', label = '', disabled, description, options, isMandatory, checkboxClass, layout, direction, allowHtml = false, onchange, } = attrs;
7092
+ const resolvedLayout = (_a = layout !== null && layout !== void 0 ? layout : direction) !== null && _a !== void 0 ? _a : 'vertical';
6887
7093
  const { groupId, componentId } = state;
6888
7094
  const controlled = isControlled(attrs);
6889
7095
  // Get current checked ID from props or internal state
@@ -6893,11 +7099,11 @@
6893
7099
  }
6894
7100
  else if (disabled) {
6895
7101
  // Non-interactive components: prefer defaultCheckedId, fallback to checkedId
6896
- currentCheckedId = (_a = attrs.defaultCheckedId) !== null && _a !== void 0 ? _a : checkedId;
7102
+ currentCheckedId = (_b = attrs.defaultCheckedId) !== null && _b !== void 0 ? _b : checkedId;
6897
7103
  }
6898
7104
  else {
6899
7105
  // Interactive uncontrolled: use internal state
6900
- currentCheckedId = (_b = state.internalCheckedId) !== null && _b !== void 0 ? _b : attrs.defaultCheckedId;
7106
+ currentCheckedId = (_c = state.internalCheckedId) !== null && _c !== void 0 ? _c : attrs.defaultCheckedId;
6901
7107
  }
6902
7108
  const handleChange = (id) => {
6903
7109
  // Update internal state for uncontrolled mode
@@ -6912,16 +7118,16 @@
6912
7118
  const cn = [newRow ? 'clear' : '', className].filter(Boolean).join(' ').trim() || undefined;
6913
7119
  const radioItems = options.map((r) => ({
6914
7120
  component: (RadioButton),
6915
- props: Object.assign(Object.assign({}, r), { onchange: handleChange, groupId, disabled: disabled || r.disabled, className: checkboxClass, checked: r.id === currentCheckedId, inputId: `${componentId}-${r.id}` }),
7121
+ props: Object.assign(Object.assign({}, r), { onchange: handleChange, groupId, disabled: disabled || r.disabled, className: checkboxClass, checked: r.id === currentCheckedId, inputId: `${componentId}-${r.id}`, allowHtml }),
6916
7122
  key: r.id,
6917
7123
  }));
6918
7124
  const optionsContent = m(OptionsList, {
6919
7125
  options: radioItems,
6920
- layout,
7126
+ layout: resolvedLayout,
6921
7127
  });
6922
7128
  return m('div', { id: componentId, className: cn }, [
6923
7129
  label && m('h5.form-group-label', label + (isMandatory ? ' *' : '')),
6924
- description && m('p.helper-text', m.trust(description)),
7130
+ description && m('p.helper-text', allowHtml ? m.trust(description) : description),
6925
7131
  m('form', { action: '#' }, optionsContent),
6926
7132
  ]);
6927
7133
  },
@@ -7058,6 +7264,26 @@
7058
7264
  opacity: 1,
7059
7265
  };
7060
7266
  };
7267
+ const formatSelectionSummary = (attrs, selectedOptions, selectedIds, placeholder) => {
7268
+ const mode = attrs.summaryMode || 'labels';
7269
+ const selectedCount = selectedIds.length;
7270
+ const totalCount = attrs.options.filter((option) => !option.disabled).length;
7271
+ if (mode === 'labels') {
7272
+ return selectedOptions.length > 0 ? selectedOptions.map((o) => o.label || o.id).join(', ') : placeholder;
7273
+ }
7274
+ const defaultCountLabel = `${selectedCount}/${totalCount} selected`;
7275
+ const formattedCount = attrs.countLabel ? attrs.countLabel(selectedCount, totalCount) : defaultCountLabel;
7276
+ if (mode === 'count') {
7277
+ return selectedCount > 0 ? formattedCount : placeholder;
7278
+ }
7279
+ if (selectedCount === 0) {
7280
+ return attrs.noneSelectedLabel || placeholder;
7281
+ }
7282
+ if (totalCount > 0 && selectedCount >= totalCount) {
7283
+ return attrs.allSelectedLabel || 'All selected';
7284
+ }
7285
+ return formattedCount;
7286
+ };
7061
7287
  const renderDropdownContent = (attrs, selectedIds, multiple, placeholder) => [
7062
7288
  placeholder && m('li.disabled', { tabindex: 0 }, m('span', placeholder)),
7063
7289
  // Render ungrouped options first
@@ -7126,17 +7352,6 @@
7126
7352
  const updatePortalDropdown = (attrs, selectedIds, multiple, placeholder) => {
7127
7353
  if (!state.isInsideModal)
7128
7354
  return;
7129
- // Clean up existing portal
7130
- const existingPortal = document.getElementById(state.dropdownId);
7131
- if (existingPortal) {
7132
- existingPortal.remove();
7133
- }
7134
- if (!state.isOpen || !state.inputRef)
7135
- return;
7136
- // Create portal element
7137
- const portalElement = document.createElement('div');
7138
- portalElement.id = state.dropdownId;
7139
- document.body.appendChild(portalElement);
7140
7355
  // Create dropdown with proper positioning
7141
7356
  const dropdownVnode = m('ul.dropdown-content.select-dropdown', {
7142
7357
  tabindex: 0,
@@ -7148,8 +7363,12 @@
7148
7363
  state.dropdownRef = null;
7149
7364
  },
7150
7365
  }, renderDropdownContent(attrs, selectedIds, multiple, placeholder));
7151
- // Render to portal
7152
- m.render(portalElement, dropdownVnode);
7366
+ syncPortalContent({
7367
+ containerId: state.dropdownId,
7368
+ shouldRender: state.isOpen && !!state.inputRef,
7369
+ vnode: dropdownVnode,
7370
+ zIndex: 10000,
7371
+ });
7153
7372
  };
7154
7373
  return {
7155
7374
  oninit: ({ attrs }) => {
@@ -7182,35 +7401,29 @@
7182
7401
  document.removeEventListener('click', closeDropdown);
7183
7402
  // Cleanup portaled dropdown if it exists
7184
7403
  if (state.isInsideModal && state.dropdownRef) {
7185
- const portalElement = document.getElementById(state.dropdownId);
7186
- if (portalElement && portalElement.parentNode) {
7187
- portalElement.parentNode.removeChild(portalElement);
7188
- }
7404
+ syncPortalContent({ containerId: state.dropdownId, shouldRender: false, vnode: null });
7189
7405
  }
7190
7406
  },
7191
7407
  view: ({ attrs }) => {
7192
- var _a;
7193
7408
  const controlled = isControlled(attrs);
7194
- const { newRow, className = 'col s12', key, options = [], multiple = false, label, helperText, placeholder = '', isMandatory, iconName, style, disabled, } = attrs;
7409
+ const { newRow, className = 'col s12', key, options = [], multiple = false, label, helperText, placeholder = '', isMandatory, iconName, appearance = 'standard', style, disabled, } = attrs;
7195
7410
  state.isMultiple = multiple;
7196
7411
  // Get selected IDs from props or internal state
7197
- let selectedIds;
7198
- if (controlled) {
7199
- selectedIds =
7200
- attrs.checkedId !== undefined ? (Array.isArray(attrs.checkedId) ? attrs.checkedId : [attrs.checkedId]) : [];
7201
- }
7202
- else if (disabled) {
7203
- // Non-interactive components: prefer defaultCheckedId, fallback to checkedId
7204
- const fallbackId = (_a = attrs.defaultCheckedId) !== null && _a !== void 0 ? _a : attrs.checkedId;
7205
- selectedIds = fallbackId !== undefined ? (Array.isArray(fallbackId) ? fallbackId : [fallbackId]) : [];
7206
- }
7207
- else {
7208
- // Interactive uncontrolled: use internal state
7209
- selectedIds = state.internalSelectedIds;
7210
- }
7211
- const finalClassName = newRow ? `${className} clear` : className;
7412
+ const selectedIds = resolveControllableValue({
7413
+ controlled,
7414
+ disabled,
7415
+ controlledValue: normalizeSelection(attrs.checkedId),
7416
+ defaultValue: normalizeSelection(attrs.defaultCheckedId),
7417
+ internalValue: state.internalSelectedIds,
7418
+ fallbackValue: [],
7419
+ });
7420
+ const layoutClassName = newRow ? `${className} clear` : className;
7421
+ const appearanceClassName = appearance === 'outlined' ? 'select-appearance-outlined' : '';
7422
+ const finalClassName = [layoutClassName, appearanceClassName].filter(Boolean).join(' ');
7423
+ const shouldInlineLabel = appearance === 'outlined' && !!label;
7212
7424
  const selectedOptionsUnsorted = options.filter((opt) => isSelected(opt.id, selectedIds));
7213
7425
  const selectedOptions = sortOptions(selectedOptionsUnsorted, attrs.sortSelected);
7426
+ const triggerValue = formatSelectionSummary(attrs, selectedOptions, selectedIds, placeholder);
7214
7427
  // Update portal dropdown when inside modal
7215
7428
  if (state.isInsideModal) {
7216
7429
  updatePortalDropdown(attrs, selectedIds, multiple, placeholder);
@@ -7230,9 +7443,15 @@
7230
7443
  'aria-controls': state.dropdownId,
7231
7444
  role: 'combobox',
7232
7445
  }, [
7446
+ shouldInlineLabel &&
7447
+ m('span.select-inline-label', [
7448
+ label,
7449
+ isMandatory && m('span.mandatory', { style: { marginLeft: '2px' } }, '*'),
7450
+ ]),
7233
7451
  m('input[type=text][readonly=true].select-dropdown.dropdown-trigger', {
7234
7452
  id: state.id,
7235
- value: selectedOptions.length > 0 ? selectedOptions.map((o) => o.label || o.id).join(', ') : placeholder,
7453
+ value: triggerValue,
7454
+ 'aria-label': label,
7236
7455
  oncreate: ({ dom }) => {
7237
7456
  state.inputRef = dom;
7238
7457
  },
@@ -7264,14 +7483,12 @@
7264
7483
  }),
7265
7484
  ]),
7266
7485
  // Label
7267
- label &&
7268
- m(Label, {
7269
- id: state.id,
7270
- label,
7271
- isMandatory,
7272
- }),
7273
- // Helper text
7274
- helperText && m(HelperText, { helperText }),
7486
+ ...renderFieldChrome({
7487
+ label: shouldInlineLabel ? undefined : label,
7488
+ id: state.id,
7489
+ isMandatory,
7490
+ helperText,
7491
+ }),
7275
7492
  ]);
7276
7493
  },
7277
7494
  };
@@ -7490,59 +7707,65 @@
7490
7707
  };
7491
7708
  };
7492
7709
 
7493
- // Proper components to avoid anonymous closures
7494
- const SelectedChip = () => {
7495
- return {
7496
- view: ({ attrs: { option, onRemove } }) => m('.chip', [
7497
- option.label || option.id.toString(),
7498
- m(MaterialIcon, {
7499
- name: 'close',
7500
- className: 'close',
7501
- onclick: (e) => {
7502
- e.stopPropagation();
7503
- onRemove(option.id);
7504
- },
7505
- }),
7506
- ]),
7507
- };
7508
- };
7509
- const DropdownOption = () => {
7510
- return {
7511
- view: ({ attrs: { option, index, selectedIds, isFocused, onToggle, onMouseOver, showCheckbox } }) => {
7512
- const checkboxId = `search-select-option-${option.id}`;
7513
- const optionLabel = option.label || option.id.toString();
7514
- return m('li', {
7515
- key: option.id,
7516
- onclick: (e) => {
7517
- e.preventDefault();
7710
+ const SelectedChip = ({ option, onRemove, }) => m('.chip', [
7711
+ option.label || option.id.toString(),
7712
+ m(MaterialIcon, {
7713
+ name: 'close',
7714
+ className: 'close',
7715
+ onclick: (e) => {
7716
+ e.stopPropagation();
7717
+ onRemove(option.id);
7718
+ },
7719
+ }),
7720
+ ]);
7721
+ const DropdownOption = ({ option, index, optionId, selectedIds, isFocused, onToggle, onMouseOver, showCheckbox, }) => {
7722
+ const optionLabel = option.label || option.id.toString();
7723
+ return m('li', {
7724
+ id: optionId,
7725
+ role: 'option',
7726
+ 'aria-selected': selectedIds.includes(option.id) ? 'true' : 'false',
7727
+ class: `${option.disabled ? 'disabled' : ''} ${isFocused ? 'active' : ''}`.trim(),
7728
+ onmouseover: () => {
7729
+ if (!option.disabled) {
7730
+ onMouseOver(index);
7731
+ }
7732
+ },
7733
+ }, m('label', {
7734
+ class: 'search-select-option-label',
7735
+ onclick: (e) => {
7736
+ // A single-select row has no native checkbox to emit change.
7737
+ if (!showCheckbox) {
7738
+ e.preventDefault();
7739
+ onToggle(option);
7740
+ }
7741
+ },
7742
+ }, [
7743
+ showCheckbox &&
7744
+ m('input', {
7745
+ type: 'checkbox',
7746
+ checked: selectedIds.includes(option.id),
7747
+ disabled: option.disabled,
7748
+ onchange: (e) => {
7518
7749
  e.stopPropagation();
7519
7750
  onToggle(option);
7520
7751
  },
7521
- class: `${option.disabled ? 'disabled' : ''} ${isFocused ? 'active' : ''}`.trim(),
7522
- onmouseover: () => {
7523
- if (!option.disabled) {
7524
- onMouseOver(index);
7525
- }
7526
- },
7527
- }, m('label', { for: checkboxId, class: 'search-select-option-label' }, [
7528
- showCheckbox &&
7529
- m('input', {
7530
- type: 'checkbox',
7531
- id: checkboxId,
7532
- checked: selectedIds.includes(option.id),
7533
- }),
7534
- m('span', optionLabel),
7535
- ]));
7536
- },
7537
- };
7752
+ }),
7753
+ m('span', optionLabel),
7754
+ ]));
7538
7755
  };
7539
7756
  /**
7540
7757
  * Mithril Factory Component for Multi-Select Dropdown with search
7541
7758
  */
7542
- const SearchSelect = () => {
7759
+ const SearchSelect = (maybeVnode) => {
7760
+ var _a;
7761
+ const cachedInstance = (_a = maybeVnode === null || maybeVnode === void 0 ? void 0 : maybeVnode.state) === null || _a === void 0 ? void 0 : _a.__searchSelectInstance;
7762
+ if (cachedInstance) {
7763
+ return cachedInstance;
7764
+ }
7543
7765
  // State initialization
7544
7766
  const state = {
7545
7767
  id: '',
7768
+ listboxId: '',
7546
7769
  isOpen: false,
7547
7770
  searchTerm: '',
7548
7771
  inputRef: null,
@@ -7550,17 +7773,38 @@
7550
7773
  focusedIndex: -1,
7551
7774
  internalSelectedIds: [],
7552
7775
  createdOptions: [],
7553
- };
7776
+ asyncOptions: [],
7777
+ isLoading: false,
7778
+ loadError: null,
7779
+ latestRequestId: 0,
7780
+ };
7781
+ const updateAsyncState = (nextState) => {
7782
+ state.asyncOptions = nextState.options;
7783
+ state.isLoading = nextState.isLoading;
7784
+ state.loadError = nextState.error;
7785
+ state.latestRequestId = nextState.latestRequestId;
7786
+ };
7787
+ const readAsyncState = () => ({
7788
+ options: state.asyncOptions,
7789
+ isLoading: state.isLoading,
7790
+ error: state.loadError,
7791
+ latestRequestId: state.latestRequestId,
7792
+ });
7554
7793
  const isControlled = (attrs) => attrs.checkedId !== undefined && typeof attrs.onchange === 'function';
7555
7794
  const componentId = uniqueId();
7556
7795
  const searchInputId = `${componentId}-search`;
7557
7796
  // Handle click outside
7558
7797
  const handleClickOutside = (e) => {
7559
7798
  const target = e.target;
7799
+ const targetElement = e.target instanceof Element ? e.target : null;
7560
7800
  if (state.dropdownRef && state.dropdownRef.contains(target)) {
7561
7801
  // Click inside dropdown, do nothing
7562
7802
  return;
7563
7803
  }
7804
+ if (targetElement && targetElement.closest('.chips-container')) {
7805
+ // Click on trigger, do nothing
7806
+ return;
7807
+ }
7564
7808
  if (state.inputRef && state.inputRef.contains(target)) {
7565
7809
  // Click on trigger handled by onclick event
7566
7810
  return;
@@ -7571,40 +7815,51 @@
7571
7815
  }
7572
7816
  m.redraw();
7573
7817
  };
7574
- // Handle keyboard navigation
7575
- const handleKeyDown = (e, filteredOptions, showAddNew) => {
7576
- if (!state.isOpen)
7818
+ // Handle keyboard navigation through shared combobox primitive.
7819
+ const handleKeyDown = (e, optionCount, includeActionRow) => {
7820
+ const result = getComboboxKeyResult({
7821
+ key: e.key,
7822
+ isOpen: state.isOpen,
7823
+ focusedIndex: state.focusedIndex,
7824
+ optionCount,
7825
+ includeActionRow,
7826
+ });
7827
+ if (result.preventDefault) {
7828
+ e.preventDefault();
7829
+ }
7830
+ state.isOpen = result.isOpen;
7831
+ state.focusedIndex = result.focusedIndex;
7832
+ return result.action;
7833
+ };
7834
+ const loadAsyncOptions = async (attrs, query) => {
7835
+ if (!attrs.loadOptions) {
7577
7836
  return;
7578
- const totalOptions = filteredOptions.length + (showAddNew ? 1 : 0);
7579
- switch (e.key) {
7580
- case 'ArrowDown':
7581
- e.preventDefault();
7582
- state.focusedIndex = Math.min(state.focusedIndex + 1, totalOptions - 1);
7583
- break;
7584
- case 'ArrowUp':
7585
- e.preventDefault();
7586
- state.focusedIndex = Math.max(state.focusedIndex - 1, -1);
7587
- break;
7588
- case 'Enter':
7589
- e.preventDefault();
7590
- if (state.focusedIndex >= 0) {
7591
- if (showAddNew && state.focusedIndex === filteredOptions.length) {
7592
- // Handle add new option
7593
- return 'addNew';
7594
- }
7595
- else if (state.focusedIndex < filteredOptions.length) {
7596
- // This will be handled in the view method where attrs are available
7597
- return 'selectOption';
7598
- }
7599
- }
7600
- break;
7601
- case 'Escape':
7602
- e.preventDefault();
7603
- state.isOpen = false;
7837
+ }
7838
+ const started = startAsyncComboboxRequest(readAsyncState());
7839
+ updateAsyncState(started.nextState);
7840
+ m.redraw();
7841
+ try {
7842
+ const loadedOptions = await attrs.loadOptions(query);
7843
+ const resolved = resolveAsyncComboboxRequest(readAsyncState(), started.requestId, loadedOptions);
7844
+ updateAsyncState(resolved);
7845
+ if (started.requestId !== state.latestRequestId) {
7846
+ return;
7847
+ }
7848
+ if (state.focusedIndex >= loadedOptions.length) {
7604
7849
  state.focusedIndex = -1;
7605
- break;
7850
+ }
7851
+ }
7852
+ catch (error) {
7853
+ const rejected = rejectAsyncComboboxRequest(readAsyncState(), started.requestId, error instanceof Error ? error.message : 'Unable to load options');
7854
+ updateAsyncState(rejected);
7855
+ if (started.requestId !== state.latestRequestId) {
7856
+ return;
7857
+ }
7858
+ state.focusedIndex = -1;
7859
+ }
7860
+ finally {
7861
+ m.redraw();
7606
7862
  }
7607
- return null;
7608
7863
  };
7609
7864
  // Create new option and add to state
7610
7865
  const createAndSelectOption = async (attrs) => {
@@ -7685,9 +7940,10 @@
7685
7940
  attrs.onchange(newIds);
7686
7941
  }
7687
7942
  };
7688
- return {
7943
+ const componentInstance = {
7689
7944
  oninit: ({ attrs }) => {
7690
7945
  state.id = attrs.id || uniqueId();
7946
+ state.listboxId = `${state.id}-listbox`;
7691
7947
  // Initialize internal state for uncontrolled mode
7692
7948
  if (!isControlled(attrs)) {
7693
7949
  const defaultIds = attrs.defaultCheckedId !== undefined
@@ -7714,20 +7970,26 @@
7714
7970
  : [attrs.checkedId]
7715
7971
  : []
7716
7972
  : state.internalSelectedIds;
7717
- const { options = [], oncreateNewOption, className, placeholder, searchPlaceholder = 'Search options...', noOptionsFound = 'No options found', label, i18n = {}, maxDisplayedOptions, maxSelectedOptions, maxHeight, } = attrs;
7973
+ const { options = [], loadOptions, oncreateNewOption, className, placeholder, searchPlaceholder = 'Search options...', noOptionsFound = 'No options found', label, i18n = {}, maxDisplayedOptions, maxSelectedOptions, maxHeight, } = attrs;
7718
7974
  // Use i18n values if provided, otherwise use defaults
7719
7975
  const texts = {
7720
7976
  noOptionsFound: i18n.noOptionsFound || noOptionsFound,
7977
+ loadingOptions: i18n.loadingOptions || 'Loading options...',
7978
+ loadingError: i18n.loadingError || 'Unable to load options',
7721
7979
  addNewPrefix: i18n.addNewPrefix || '+',
7722
7980
  showingXofY: i18n.showingXofY || 'Showing {shown} of {total} options',
7723
7981
  maxSelectionsReached: i18n.maxSelectionsReached || 'Maximum {max} selections reached',
7724
7982
  };
7725
7983
  // Check if max selections is reached
7726
7984
  const isMaxSelectionsReached = maxSelectedOptions && selectedIds.length >= maxSelectedOptions;
7727
- // Merge provided options with internally created options
7728
- const allOptions = [...options, ...state.createdOptions];
7985
+ // In async mode, the active list is sourced remotely.
7986
+ const sourceOptions = loadOptions ? state.asyncOptions : options;
7987
+ // Merge active options with internally created options
7988
+ const allOptions = [...sourceOptions, ...state.createdOptions];
7989
+ // Keep selected label lookups stable across static, async, and created sets.
7990
+ const lookupOptions = [...options, ...state.asyncOptions, ...state.createdOptions];
7729
7991
  // Get selected options for display
7730
- const selectedOptionsUnsorted = allOptions.filter((opt) => selectedIds.includes(opt.id));
7992
+ const selectedOptionsUnsorted = lookupOptions.filter((opt) => selectedIds.includes(opt.id));
7731
7993
  const selectedOptions = sortOptions(selectedOptionsUnsorted, attrs.sortSelected);
7732
7994
  // Safely filter options
7733
7995
  const filteredOptions = allOptions.filter((option) => (option.label || option.id.toString()).toLowerCase().includes((state.searchTerm || '').toLowerCase()) &&
@@ -7740,6 +8002,14 @@
7740
8002
  const showAddNew = oncreateNewOption &&
7741
8003
  state.searchTerm &&
7742
8004
  !displayedOptions.some((o) => (o.label || o.id.toString()).toLowerCase() === state.searchTerm.toLowerCase());
8005
+ const activeDescendantId = state.isOpen && state.focusedIndex >= 0 && state.focusedIndex < displayedOptions.length
8006
+ ? getComboboxOptionId(state.id, state.focusedIndex)
8007
+ : undefined;
8008
+ const viewState = getComboboxViewState({
8009
+ isLoading: state.isLoading,
8010
+ error: state.loadError,
8011
+ optionCount: displayedOptions.length,
8012
+ });
7743
8013
  // Render the dropdown
7744
8014
  return m('.input-field.multi-select-dropdown', { className }, [
7745
8015
  m('.chips.chips-initial.chips-container', {
@@ -7750,14 +8020,34 @@
7750
8020
  // console.log('SearchSelect clicked', state.isOpen, e); // Debug log
7751
8021
  e.preventDefault();
7752
8022
  e.stopPropagation();
8023
+ const wasOpen = state.isOpen;
7753
8024
  state.isOpen = !state.isOpen;
8025
+ if (!wasOpen && state.isOpen && loadOptions) {
8026
+ void loadAsyncOptions(attrs, state.searchTerm);
8027
+ }
7754
8028
  // console.log('SearchSelect state changed to', state.isOpen); // Debug log
7755
8029
  },
7756
- class: 'chips chips-container',
8030
+ onkeydown: async (e) => {
8031
+ const action = handleKeyDown(e, displayedOptions.length, !!showAddNew);
8032
+ if (action === 'open' && loadOptions) {
8033
+ await loadAsyncOptions(attrs, state.searchTerm);
8034
+ return;
8035
+ }
8036
+ if (action === 'selectAction' && oncreateNewOption) {
8037
+ await createAndSelectOption(attrs);
8038
+ }
8039
+ if (action === 'selectFocused' && state.focusedIndex < displayedOptions.length) {
8040
+ toggleOption(displayedOptions[state.focusedIndex], attrs);
8041
+ }
8042
+ },
8043
+ class: 'chips chips-container mm-layout-row mm-layout-row--wrap mm-layout-row--align-end',
8044
+ role: 'combobox',
8045
+ tabindex: 0,
8046
+ 'aria-expanded': state.isOpen ? 'true' : 'false',
8047
+ 'aria-haspopup': 'listbox',
8048
+ 'aria-controls': state.isOpen ? state.listboxId : undefined,
8049
+ 'aria-activedescendant': activeDescendantId,
7757
8050
  style: {
7758
- display: 'flex',
7759
- alignItems: 'end',
7760
- flexWrap: 'wrap',
7761
8051
  cursor: 'pointer',
7762
8052
  position: 'relative',
7763
8053
  },
@@ -7770,30 +8060,41 @@
7770
8060
  value: selectedOptions.map((o) => o.label || o.id.toString()).join(', '),
7771
8061
  readonly: true,
7772
8062
  class: 'sr-only',
7773
- style: { position: 'absolute', left: '-9999px', opacity: 0 },
8063
+ style: {
8064
+ position: 'absolute',
8065
+ width: '1px',
8066
+ height: '1px',
8067
+ margin: '-1px',
8068
+ padding: 0,
8069
+ border: 0,
8070
+ overflow: 'hidden',
8071
+ clip: 'rect(0 0 0 0)',
8072
+ clipPath: 'inset(50%)',
8073
+ whiteSpace: 'nowrap',
8074
+ },
7774
8075
  }),
7775
8076
  // Selected Options (chips)
7776
- ...selectedOptions.map((option) => m(SelectedChip(), {
7777
- option,
8077
+ ...selectedOptions.map((option) => SelectedChip({
8078
+ option: option,
7778
8079
  onRemove: (id) => removeOption(id, attrs),
7779
8080
  })),
7780
8081
  // Placeholder when no options selected
7781
8082
  selectedOptions.length === 0 &&
7782
8083
  placeholder &&
7783
8084
  m('span.placeholder', {
8085
+ class: 'mm-layout-grow',
7784
8086
  style: {
7785
8087
  color: 'var(--mm-text-hint, #9e9e9e)',
7786
- flexGrow: 1,
7787
8088
  padding: '8px 0',
7788
8089
  },
7789
8090
  }, placeholder),
7790
8091
  // Spacer to push caret to the right
7791
- m('span.spacer', { style: { flexGrow: 1 } }),
8092
+ m('span.spacer.mm-layout-grow'),
7792
8093
  m(MaterialIcon, {
7793
8094
  name: 'caret',
7794
8095
  direction: state.isOpen ? 'up' : 'down',
7795
- class: 'caret',
7796
- style: { marginLeft: 'auto', cursor: 'pointer' },
8096
+ class: 'caret mm-layout-ml-auto',
8097
+ style: { cursor: 'pointer' },
7797
8098
  }),
7798
8099
  ]),
7799
8100
  // Label
@@ -7805,6 +8106,8 @@
7805
8106
  // Dropdown Menu
7806
8107
  state.isOpen &&
7807
8108
  m('ul.dropdown-content.select-dropdown', {
8109
+ id: state.listboxId,
8110
+ role: 'listbox',
7808
8111
  oncreate: ({ dom }) => {
7809
8112
  state.dropdownRef = dom;
7810
8113
  },
@@ -7829,23 +8132,39 @@
7829
8132
  oninput: (e) => {
7830
8133
  state.searchTerm = e.target.value;
7831
8134
  state.focusedIndex = -1; // Reset focus when typing
8135
+ if (loadOptions) {
8136
+ void loadAsyncOptions(attrs, state.searchTerm);
8137
+ }
7832
8138
  },
7833
8139
  onkeydown: async (e) => {
7834
- const result = handleKeyDown(e, displayedOptions, !!showAddNew);
7835
- if (result === 'addNew' && oncreateNewOption) {
8140
+ const action = handleKeyDown(e, displayedOptions.length, !!showAddNew);
8141
+ if (action === 'open' && loadOptions) {
8142
+ await loadAsyncOptions(attrs, state.searchTerm);
8143
+ }
8144
+ else if (action === 'selectAction' && oncreateNewOption) {
7836
8145
  await createAndSelectOption(attrs);
7837
8146
  }
7838
- else if (result === 'selectOption' && state.focusedIndex < displayedOptions.length) {
8147
+ else if (action === 'selectFocused' && state.focusedIndex < displayedOptions.length) {
7839
8148
  toggleOption(displayedOptions[state.focusedIndex], attrs);
7840
8149
  }
7841
8150
  },
7842
8151
  class: 'search-select-input',
8152
+ 'aria-autocomplete': 'list',
8153
+ 'aria-controls': state.listboxId,
7843
8154
  }),
7844
8155
  ]),
7845
- // No options found message or list of options
7846
- ...(displayedOptions.length === 0 && !showAddNew
7847
- ? [m('li.search-select-no-options', texts.noOptionsFound)]
8156
+ // Async loading status
8157
+ ...(viewState === 'loading'
8158
+ ? [m('li.search-select-loading-info', { role: 'status', 'aria-live': 'polite' }, texts.loadingOptions)]
8159
+ : []),
8160
+ // Async loading error
8161
+ ...(viewState === 'error' && state.loadError
8162
+ ? [
8163
+ m('li.search-select-error-info', { role: 'status', 'aria-live': 'assertive' }, `${texts.loadingError}: ${state.loadError}`),
8164
+ ]
7848
8165
  : []),
8166
+ // No options found message or list of options
8167
+ ...(viewState === 'empty' && !showAddNew ? [m('li.search-select-no-options', texts.noOptionsFound)] : []),
7849
8168
  // Truncation message
7850
8169
  ...(isTruncated
7851
8170
  ? [
@@ -7878,6 +8197,9 @@
7878
8197
  ...(showAddNew
7879
8198
  ? [
7880
8199
  m('li', {
8200
+ id: `${state.listboxId}-action`,
8201
+ role: 'option',
8202
+ 'aria-selected': 'false',
7881
8203
  onclick: async () => {
7882
8204
  await createAndSelectOption(attrs);
7883
8205
  },
@@ -7889,9 +8211,10 @@
7889
8211
  ]
7890
8212
  : []),
7891
8213
  // List of filtered options
7892
- ...displayedOptions.map((option, index) => m(DropdownOption(), {
8214
+ ...displayedOptions.map((option, index) => DropdownOption({
7893
8215
  option,
7894
8216
  index,
8217
+ optionId: getComboboxOptionId(state.id, index),
7895
8218
  selectedIds,
7896
8219
  isFocused: state.focusedIndex === index,
7897
8220
  onToggle: (opt) => toggleOption(opt, attrs),
@@ -7904,6 +8227,10 @@
7904
8227
  ]);
7905
8228
  },
7906
8229
  };
8230
+ if (maybeVnode === null || maybeVnode === void 0 ? void 0 : maybeVnode.state) {
8231
+ maybeVnode.state.__searchSelectInstance = componentInstance;
8232
+ }
8233
+ return componentInstance;
7907
8234
  };
7908
8235
 
7909
8236
  const defaultI18n$2 = {
@@ -9277,6 +9604,53 @@
9277
9604
  };
9278
9605
  };
9279
9606
 
9607
+ /** A semantic native fieldset for related controls. */
9608
+ const Fieldset = () => ({
9609
+ view: ({ attrs, children }) => {
9610
+ const { legend, description, required, disabled, error, className } = attrs, params = __rest(attrs, ["legend", "description", "required", "disabled", "error", "className"]);
9611
+ const descriptionId = params.id ? `${params.id}-description` : undefined;
9612
+ const errorId = params.id ? `${params.id}-error` : undefined;
9613
+ const describedBy = [description && descriptionId, error && errorId].filter(Boolean).join(' ') || undefined;
9614
+ return m('fieldset.mm-fieldset', Object.assign(Object.assign({}, params), { className, disabled, 'aria-describedby': describedBy }), [
9615
+ m('legend.mm-fieldset__legend', [legend, required && m('span.mm-fieldset__required[aria-hidden=true]', ' *')]),
9616
+ description && m('p.mm-fieldset__description', { id: descriptionId }, description),
9617
+ children,
9618
+ error && m('p.mm-fieldset__error[role=alert]', { id: errorId }, error),
9619
+ ]);
9620
+ },
9621
+ });
9622
+ const focusErrorTarget = (fieldId) => {
9623
+ var _a;
9624
+ if (!fieldId || typeof document === 'undefined')
9625
+ return;
9626
+ (_a = document.getElementById(fieldId)) === null || _a === void 0 ? void 0 : _a.focus();
9627
+ };
9628
+ /** A visual form section with an optional accessible validation summary. */
9629
+ const FormSection = () => ({
9630
+ view: ({ attrs, children }) => {
9631
+ const { title, description, errors = [], summaryTitle = 'Please correct the following errors', className } = attrs, params = __rest(attrs, ["title", "description", "errors", "summaryTitle", "className"]);
9632
+ return m('section.mm-form-section', Object.assign(Object.assign({}, params), { className }), [
9633
+ (title || description) &&
9634
+ m('.mm-form-section__header', [
9635
+ title && m('h3.mm-form-section__title', title),
9636
+ description && m('p.mm-form-section__description', description),
9637
+ ]),
9638
+ errors.length > 0 &&
9639
+ m('div.mm-validation-summary[role=alert][aria-live=assertive]', [
9640
+ m('p.mm-validation-summary__title', summaryTitle),
9641
+ m('ul.mm-validation-summary__list', errors.map((error, index) => {
9642
+ var _a, _b, _c;
9643
+ const href = (_a = error.href) !== null && _a !== void 0 ? _a : (error.fieldId ? `#${error.fieldId}` : undefined);
9644
+ return m('li', { key: `${(_c = (_b = error.fieldId) !== null && _b !== void 0 ? _b : href) !== null && _c !== void 0 ? _c : 'error'}-${index}` }, href
9645
+ ? m('a', { href, onclick: () => focusErrorTarget(error.fieldId) }, error.message)
9646
+ : error.message);
9647
+ })),
9648
+ ]),
9649
+ children,
9650
+ ]);
9651
+ },
9652
+ });
9653
+
9280
9654
  // List of MaterialIcon SVG icons that are available
9281
9655
  const materialIconSvgNames = [
9282
9656
  'caret',
@@ -9299,6 +9673,7 @@
9299
9673
  const renderIcon = (icon, style) => {
9300
9674
  if (!icon)
9301
9675
  return null;
9676
+ const objectStyle = typeof style === 'string' ? undefined : style;
9302
9677
  if (typeof icon === 'string') {
9303
9678
  // Check if this is a MaterialIcon SVG name
9304
9679
  if (materialIconSvgNames.includes(icon)) {
@@ -9315,7 +9690,7 @@
9315
9690
  // Image URL
9316
9691
  return m('img', {
9317
9692
  src: icon.content,
9318
- style: Object.assign(Object.assign({}, style), { width: '24px', height: '24px', objectFit: 'contain' }),
9693
+ style: Object.assign(Object.assign({}, objectStyle), { width: '24px', height: '24px', objectFit: 'contain' }),
9319
9694
  });
9320
9695
  }
9321
9696
  return null;
@@ -9326,8 +9701,10 @@
9326
9701
  const SidenavHeaderFooterItem = () => {
9327
9702
  return {
9328
9703
  view: ({ attrs }) => {
9329
- const { text, icon, onclick, href, className = '', _isExpanded = true, _position = 'left' } = attrs;
9704
+ const { text, icon, onclick, href, className = '', title, tooltip, tooltipWhenCollapsedOnly = true, _isExpanded = true, _position = 'left', } = attrs;
9330
9705
  const isRightAligned = _position === 'right';
9706
+ const tooltipText = title || tooltip || text;
9707
+ const shouldShowTooltip = tooltipWhenCollapsedOnly ? !_isExpanded : true;
9331
9708
  const handleClick = (e) => {
9332
9709
  if (onclick) {
9333
9710
  e.preventDefault();
@@ -9336,24 +9713,25 @@
9336
9713
  };
9337
9714
  const content = isRightAligned
9338
9715
  ? [
9339
- _isExpanded &&
9340
- m('span.sidenav-item-text', { style: { flex: '1', 'text-align': 'left', 'margin-right': '8px' } }, text),
9716
+ _isExpanded && m('span.sidenav-item-text.mm-layout-grow.mm-layout-mr-8.left-align', text),
9341
9717
  renderIcon(icon, { 'min-width': '24px', width: '24px' }),
9342
9718
  ]
9343
9719
  : [
9344
9720
  renderIcon(icon, { 'min-width': '24px', width: '24px' }),
9345
- _isExpanded && m('span.sidenav-item-text', { style: { 'margin-left': '8px', flex: '1' } }, text),
9721
+ _isExpanded && m('span.sidenav-item-text.mm-layout-grow.mm-layout-ml-8', text),
9346
9722
  ];
9347
9723
  const linkStyle = {
9348
- display: 'flex',
9349
- 'align-items': 'center',
9350
9724
  padding: _isExpanded ? '12px 16px' : '12px 18px',
9351
9725
  'justify-content': _isExpanded ? (isRightAligned ? 'flex-end' : 'flex-start') : 'center',
9352
9726
  };
9727
+ const linkClass = 'mm-layout-row mm-layout-row--center';
9353
9728
  return m('li', { class: className }, m('a', {
9354
9729
  href: href || '#!',
9355
9730
  onclick: handleClick,
9731
+ class: linkClass,
9356
9732
  style: linkStyle,
9733
+ title: shouldShowTooltip ? tooltipText : undefined,
9734
+ 'aria-label': shouldShowTooltip ? tooltipText : undefined,
9357
9735
  }, content));
9358
9736
  },
9359
9737
  };
@@ -9375,9 +9753,9 @@
9375
9753
  m.redraw();
9376
9754
  }
9377
9755
  };
9378
- const setBodyOverflow = (isOpen, mode) => {
9756
+ const setBodyOverflow = (isOpen, mode, fixed) => {
9379
9757
  if (typeof document !== 'undefined') {
9380
- document.body.style.overflow = isOpen && mode === 'overlay' ? 'hidden' : '';
9758
+ document.body.style.overflow = isOpen && mode === 'overlay' && !fixed ? 'hidden' : '';
9381
9759
  }
9382
9760
  };
9383
9761
  const toggleExpanded = (attrs) => {
@@ -9413,7 +9791,7 @@
9413
9791
  if (wasOpen !== isOpen) {
9414
9792
  state.isOpen = isOpen;
9415
9793
  state.isAnimating = true;
9416
- setBodyOverflow(isOpen, attrs.mode || 'overlay');
9794
+ setBodyOverflow(isOpen, attrs.mode || 'overlay', attrs.fixed || false);
9417
9795
  // Clear animation state after animation completes
9418
9796
  setTimeout(() => {
9419
9797
  state.isAnimating = false;
@@ -9423,7 +9801,7 @@
9423
9801
  },
9424
9802
  onremove: ({ attrs }) => {
9425
9803
  // Clean up
9426
- setBodyOverflow(false, attrs.mode || 'overlay');
9804
+ setBodyOverflow(false, attrs.mode || 'overlay', attrs.fixed || false);
9427
9805
  if (typeof document !== 'undefined' && attrs.closeOnEscape !== false) {
9428
9806
  document.removeEventListener('keydown', (e) => handleEscapeKey(e, attrs));
9429
9807
  }
@@ -9452,7 +9830,9 @@
9452
9830
  class: [
9453
9831
  position === 'right' ? 'right-aligned' : '',
9454
9832
  fixed ? 'sidenav-fixed' : '',
9833
+ mode === 'push' ? 'sidenav-push' : '',
9455
9834
  expandable && !isExpanded ? 'sidenav-collapsed' : '',
9835
+ 'mm-layout-stack',
9456
9836
  className,
9457
9837
  ]
9458
9838
  .filter(Boolean)
@@ -9464,13 +9844,22 @@
9464
9844
  'transition-property': 'transform, width',
9465
9845
  },
9466
9846
  }, [
9847
+ // Header content slot (rendered first, before hamburger)
9848
+ attrs.headerContent &&
9849
+ m('li.sidenav-header-slot', {
9850
+ style: {
9851
+ 'flex-shrink': 0,
9852
+ 'list-style': 'none',
9853
+ height: 'auto',
9854
+ 'line-height': 'normal',
9855
+ padding: 0,
9856
+ },
9857
+ }, attrs.headerContent),
9467
9858
  // Hamburger toggle button (inside sidenav, at the top)
9468
9859
  showHamburger &&
9469
9860
  m('li.sidenav-hamburger-item', {
9861
+ class: `mm-layout-row mm-layout-row--center ${position === 'right' ? 'mm-layout-row--justify-end' : 'mm-layout-row--justify-start'}`,
9470
9862
  style: {
9471
- display: 'flex',
9472
- 'justify-content': position === 'right' ? 'flex-end' : 'flex-start',
9473
- 'align-items': 'center',
9474
9863
  padding: '12px 16px',
9475
9864
  cursor: 'pointer',
9476
9865
  'border-bottom': '1px solid rgba(0,0,0,0.1)',
@@ -9486,10 +9875,8 @@
9486
9875
  // Expand/collapse toggle button (if expandable, right below hamburger)
9487
9876
  expandable &&
9488
9877
  m('li.sidenav-expand-toggle', {
9878
+ class: `mm-layout-row mm-layout-row--center ${position === 'right' ? 'mm-layout-row--justify-end' : 'mm-layout-row--justify-start'}`,
9489
9879
  style: {
9490
- display: 'flex',
9491
- 'justify-content': position === 'right' ? 'flex-end' : 'flex-start',
9492
- 'align-items': 'center',
9493
9880
  padding: '12px 16px',
9494
9881
  cursor: 'pointer',
9495
9882
  'border-bottom': '1px solid rgba(0,0,0,0.1)',
@@ -9517,7 +9904,19 @@
9517
9904
  : children,
9518
9905
  // Footer item (if provided, appears at the bottom)
9519
9906
  attrs.footer &&
9520
- m(SidenavHeaderFooterItem, Object.assign(Object.assign({}, attrs.footer), { _isExpanded: isExpanded, _position: position, className: 'sidenav-footer-item' })),
9907
+ m(SidenavHeaderFooterItem, Object.assign(Object.assign({}, attrs.footer), { _isExpanded: isExpanded, _position: position, className: ['sidenav-footer-item', attrs.footer.className].filter(Boolean).join(' ') })),
9908
+ // Footer content slot (rendered last, pushed to bottom via margin-top: auto)
9909
+ attrs.footerContent &&
9910
+ m('li.sidenav-footer-slot', {
9911
+ style: {
9912
+ 'margin-top': 'auto',
9913
+ 'flex-shrink': 0,
9914
+ 'list-style': 'none',
9915
+ height: 'auto',
9916
+ 'line-height': 'normal',
9917
+ padding: 0,
9918
+ },
9919
+ }, attrs.footerContent),
9521
9920
  ]),
9522
9921
  ];
9523
9922
  },
@@ -9550,7 +9949,7 @@
9550
9949
  const submenuContent = isRightAligned
9551
9950
  ? [
9552
9951
  // Right-aligned: text on left, icons on right
9553
- isExpanded && m('span', { style: { flex: '1', 'text-align': 'left' } }, text),
9952
+ isExpanded && m('span.mm-layout-grow.left-align', text),
9554
9953
  icon && isExpanded && renderIcon(icon, { 'font-size': '18px' }),
9555
9954
  indicatorIcon,
9556
9955
  ]
@@ -9558,18 +9957,22 @@
9558
9957
  // Left-aligned: indicator on left, text and icon on right
9559
9958
  indicatorIcon,
9560
9959
  icon && isExpanded && renderIcon(icon, { 'font-size': '18px', 'margin-left': indicatorIcon ? '8px' : '0' }),
9561
- isExpanded && m('span', { style: { 'margin-left': icon || indicatorIcon ? '8px' : '0' } }, text),
9960
+ isExpanded && m('span', { class: icon || indicatorIcon ? 'mm-layout-ml-8' : undefined }, text),
9562
9961
  ];
9563
9962
  return m('li.sidenav-subitem', {
9564
- class: selected ? 'selected' : '',
9963
+ class: [
9964
+ selected ? 'selected' : '',
9965
+ 'mm-layout-row',
9966
+ 'mm-layout-row--center',
9967
+ 'mm-layout-gap-sm',
9968
+ isRightAligned ? 'mm-layout-row--justify-between' : 'mm-layout-row--justify-start',
9969
+ ]
9970
+ .filter(Boolean)
9971
+ .join(' ') || undefined,
9565
9972
  style: {
9566
9973
  padding: isExpanded ? '0 16px 0 48px' : '0 16px',
9567
9974
  cursor: 'pointer',
9568
- display: 'flex',
9569
- 'align-items': 'center',
9570
- gap: '8px',
9571
9975
  'font-size': '0.9em',
9572
- 'justify-content': isRightAligned ? 'space-between' : 'flex-start',
9573
9976
  height: '48px',
9574
9977
  'min-height': '48px',
9575
9978
  },
@@ -9586,7 +9989,7 @@
9586
9989
  let isSubmenuOpen = false;
9587
9990
  return {
9588
9991
  view: ({ attrs, children }) => {
9589
- const { text, icon, active = false, disabled = false, onclick, href, className = '', divider = false, subheader = false, submenu = [], submenuMode = 'checkbox', } = attrs;
9992
+ const { text, icon, active = false, disabled = false, onclick, href, className = '', divider = false, subheader = false, submenu = [], submenuMode = 'checkbox', title, tooltip, tooltipWhenCollapsedOnly = true, } = attrs;
9590
9993
  if (divider) {
9591
9994
  return m('li.divider');
9592
9995
  }
@@ -9611,39 +10014,45 @@
9611
10014
  const isExpanded = attrs._isExpanded !== false;
9612
10015
  const position = attrs._position || 'left';
9613
10016
  const isRightAligned = position === 'right';
10017
+ const tooltipText = title || tooltip || text;
10018
+ const shouldShowTooltip = tooltipWhenCollapsedOnly ? !isExpanded : true;
9614
10019
  // In expanded mode, icons are at the outside edge
9615
10020
  // In collapsed mode, icons are centered
9616
10021
  const content = isRightAligned
9617
10022
  ? [
9618
10023
  // Right-aligned: text on left, icon on right
9619
- isExpanded &&
9620
- m('span.sidenav-item-text', { style: { flex: '1', 'text-align': 'left', 'margin-right': '8px' } }, text || children),
10024
+ isExpanded && m('span.sidenav-item-text.mm-layout-grow.mm-layout-mr-8.left-align', text || children),
9621
10025
  renderIcon(icon, { 'min-width': '24px', width: '24px' }),
9622
10026
  ]
9623
10027
  : [
9624
10028
  // Left-aligned: icon on left, text on right
9625
10029
  renderIcon(icon, { 'min-width': '24px', width: '24px' }),
9626
- isExpanded && m('span.sidenav-item-text', { style: { 'margin-left': '8px', flex: '1' } }, text || children),
10030
+ isExpanded && m('span.sidenav-item-text.mm-layout-grow.mm-layout-ml-8', text || children),
9627
10031
  ];
9628
10032
  const linkStyle = {
9629
- display: 'flex',
9630
- 'align-items': 'center',
9631
10033
  padding: isExpanded ? '12px 16px' : '12px 18px',
9632
10034
  'justify-content': isExpanded ? (isRightAligned ? 'flex-end' : 'flex-start') : 'center',
9633
10035
  };
10036
+ const linkClass = 'mm-layout-row mm-layout-row--center';
9634
10037
  const mainItem = href && !disabled
9635
10038
  ? m('li', { class: itemClasses }, [
9636
10039
  m('a', {
9637
10040
  href,
9638
10041
  onclick: handleMainClick,
10042
+ class: linkClass,
9639
10043
  style: linkStyle,
10044
+ title: shouldShowTooltip ? tooltipText : undefined,
10045
+ 'aria-label': shouldShowTooltip ? tooltipText : undefined,
9640
10046
  }, content),
9641
10047
  ])
9642
10048
  : m('li', { class: itemClasses }, [
9643
10049
  m('a', {
9644
10050
  onclick: handleMainClick,
9645
10051
  href: '#!',
10052
+ class: linkClass,
9646
10053
  style: linkStyle,
10054
+ title: shouldShowTooltip ? tooltipText : undefined,
10055
+ 'aria-label': shouldShowTooltip ? tooltipText : undefined,
9647
10056
  }, content),
9648
10057
  ]);
9649
10058
  // Return main item with submenu if applicable
@@ -10159,8 +10568,9 @@
10159
10568
  const childIsFocused = ((_e = attrs.treeState) === null || _e === void 0 ? void 0 : _e.focusedNodeId) === child.id;
10160
10569
  // Calculate if this child is last in branch
10161
10570
  const childPath = [...(attrs.currentPath || []), childIndex];
10162
- const childIsLastInBranch = ((_f = attrs.treeAttrs) === null || _f === void 0 ? void 0 : _f.data) ?
10163
- isNodeLastInBranch(childPath, attrs.treeAttrs.data) : false;
10571
+ const childIsLastInBranch = ((_f = attrs.treeAttrs) === null || _f === void 0 ? void 0 : _f.data)
10572
+ ? isNodeLastInBranch(childPath, attrs.treeAttrs.data)
10573
+ : false;
10164
10574
  return m(TreeNodeComponent, {
10165
10575
  key: child.id,
10166
10576
  node: child,
@@ -10293,10 +10703,7 @@
10293
10703
  view: ({ attrs }) => {
10294
10704
  const { data, className, style, id, selectionMode = 'single', showConnectors = true } = attrs;
10295
10705
  return m('div.tree-view', {
10296
- class: [
10297
- className,
10298
- showConnectors && 'show-connectors'
10299
- ].filter(Boolean).join(' ') || undefined,
10706
+ class: [className, showConnectors && 'show-connectors'].filter(Boolean).join(' ') || undefined,
10300
10707
  style,
10301
10708
  id,
10302
10709
  role: selectionMode === 'multiple' ? 'listbox' : 'tree',
@@ -10994,6 +11401,7 @@
10994
11401
  };
10995
11402
  };
10996
11403
 
11404
+ const MOBILE_LAYOUT_BREAKPOINT = 600;
10997
11405
  /** Create a LikertScale component */
10998
11406
  const LikertScale = () => {
10999
11407
  const state = {
@@ -11056,6 +11464,22 @@
11056
11464
  return 'likert-scale--responsive';
11057
11465
  }
11058
11466
  };
11467
+ const isVerticalLayout = (layout) => {
11468
+ if (layout === 'vertical')
11469
+ return true;
11470
+ if (layout === 'horizontal')
11471
+ return false;
11472
+ return typeof window !== 'undefined' ? window.innerWidth <= MOBILE_LAYOUT_BREAKPOINT : false;
11473
+ };
11474
+ const getInlineAnchorLabel = (value, min, max, middleValue, startLabel, middleLabel, endLabel) => {
11475
+ if (value === min && startLabel)
11476
+ return startLabel;
11477
+ if (middleLabel && middleValue !== undefined && value === middleValue)
11478
+ return middleLabel;
11479
+ if (value === max && endLabel)
11480
+ return endLabel;
11481
+ return undefined;
11482
+ };
11059
11483
  const handleChange = (attrs, newValue) => {
11060
11484
  var _a;
11061
11485
  if (attrs.readonly || attrs.disabled)
@@ -11102,7 +11526,7 @@
11102
11526
  const LikertScaleItem = () => {
11103
11527
  return {
11104
11528
  view: ({ attrs }) => {
11105
- const { value, currentValue, showNumber, showTooltip, tooltipLabel, groupId, name, disabled, readonly, onchange, } = attrs;
11529
+ const { value, currentValue, showNumber, showTooltip, tooltipLabel, groupId, name, disabled, readonly, anchorLabel, onchange, } = attrs;
11106
11530
  const radioId = `${groupId}-${value}`;
11107
11531
  const isChecked = currentValue === value;
11108
11532
  return m('.likert-scale__item.no-select', {
@@ -11129,6 +11553,7 @@
11129
11553
  m('label.likert-scale__label', {
11130
11554
  for: radioId,
11131
11555
  }),
11556
+ anchorLabel && m('.likert-scale__item-anchor', anchorLabel),
11132
11557
  // Tooltip (optional)
11133
11558
  showTooltip && tooltipLabel && m('.likert-scale__tooltip', tooltipLabel),
11134
11559
  ]);
@@ -11152,6 +11577,8 @@
11152
11577
  const { min = 1, max = 5, step = 1, size = 'medium', density = 'standard', layout = 'responsive', className = '', style = {}, readonly = false, disabled = false, id = state.id, name, label, description, isMandatory, startLabel, middleLabel, endLabel, showNumbers = false, showTooltips = false, tooltipLabels, alignLabels = false } = attrs, ariaAttrs = __rest(attrs, ["min", "max", "step", "size", "density", "layout", "className", "style", "readonly", "disabled", "id", "name", "label", "description", "isMandatory", "startLabel", "middleLabel", "endLabel", "showNumbers", "showTooltips", "tooltipLabels", "alignLabels"]);
11153
11578
  const currentValue = getCurrentValue(attrs);
11154
11579
  const itemCount = Math.floor((max - min) / step) + 1;
11580
+ const useInlineAnchors = isVerticalLayout(layout);
11581
+ const middleValue = middleLabel ? min + Math.floor((itemCount - 1) / 2) * step : undefined;
11155
11582
  // Generate scale values
11156
11583
  const scaleValues = Array.from({ length: itemCount }, (_, i) => min + i * step);
11157
11584
  return m('.likert-scale', {
@@ -11200,6 +11627,9 @@
11200
11627
  showNumber: showNumbers,
11201
11628
  showTooltip: showTooltips,
11202
11629
  tooltipLabel: tooltipLabels === null || tooltipLabels === void 0 ? void 0 : tooltipLabels[value - min],
11630
+ anchorLabel: useInlineAnchors
11631
+ ? getInlineAnchorLabel(value, min, max, middleValue, startLabel, middleLabel, endLabel)
11632
+ : undefined,
11203
11633
  groupId: state.groupId,
11204
11634
  name,
11205
11635
  disabled,
@@ -11207,7 +11637,8 @@
11207
11637
  onchange: (v) => handleChange(attrs, v),
11208
11638
  }))),
11209
11639
  // Scale anchors
11210
- (startLabel || middleLabel || endLabel) &&
11640
+ !useInlineAnchors &&
11641
+ (startLabel || middleLabel || endLabel) &&
11211
11642
  m('.likert-scale__anchors', [
11212
11643
  startLabel && m('.likert-scale__anchor.likert-scale__anchor--start', startLabel),
11213
11644
  middleLabel && m('.likert-scale__anchor.likert-scale__anchor--middle', middleLabel),
@@ -11392,6 +11823,9 @@
11392
11823
  const { mode = 'indeterminate', value = 0, max = 100, size = 'medium', color = 'teal', colorIntensity, label, showPercentage = false, className = '', style = {}, id = state.id, 'aria-label': ariaLabel, 'aria-valuemin': ariaValueMin = 0, 'aria-valuemax': ariaValueMax = max, 'aria-valuenow': ariaValueNow, 'aria-valuetext': ariaValueText } = attrs, params = __rest(attrs, ["mode", "value", "max", "size", "color", "colorIntensity", "label", "showPercentage", "className", "style", "id", 'aria-label', 'aria-valuemin', 'aria-valuemax', 'aria-valuenow', 'aria-valuetext']);
11393
11824
  const isDeterminate = mode === 'determinate';
11394
11825
  const sizePixels = SIZE_MAP[size];
11826
+ const styleValue = typeof style === 'string'
11827
+ ? `width:${sizePixels}px;height:${sizePixels}px;${style}`
11828
+ : Object.assign({ width: `${sizePixels}px`, height: `${sizePixels}px` }, style);
11395
11829
  const { radius, circumference, strokeDashoffset, percentage } = isDeterminate
11396
11830
  ? calculateStrokeProperties(sizePixels, value, max)
11397
11831
  : { radius: 0, circumference: 0, strokeDashoffset: 0, percentage: 0 };
@@ -11418,7 +11852,7 @@
11418
11852
  : {
11419
11853
  'aria-valuetext': ariaValueText || label || 'Loading',
11420
11854
  };
11421
- return m('.circular-progress', Object.assign(Object.assign(Object.assign({}, params), { className: classNames, style: Object.assign({ width: `${sizePixels}px`, height: `${sizePixels}px` }, style), id, role: 'progressbar', 'aria-label': ariaLabel || (isDeterminate ? `Progress: ${Math.round(percentage)}%` : 'Loading') }), ariaAttrs), [
11855
+ return m('.circular-progress', Object.assign(Object.assign(Object.assign({}, params), { className: classNames, style: styleValue, id, role: 'progressbar', 'aria-label': ariaLabel || (isDeterminate ? `Progress: ${Math.round(percentage)}%` : 'Loading') }), ariaAttrs), [
11422
11856
  // SVG circle
11423
11857
  m('svg.circular-progress__svg', {
11424
11858
  viewBox: `0 0 ${sizePixels} ${sizePixels}`,
@@ -11483,13 +11917,7 @@
11483
11917
  // Determine label content
11484
11918
  const labelContent = label !== undefined ? label : showPercentage && isDeterminate ? `${Math.round(percentage)}%` : '';
11485
11919
  // Build class names
11486
- const classNames = [
11487
- 'linear-progress',
11488
- getColorClass(color, colorIntensity),
11489
- className,
11490
- ]
11491
- .filter(Boolean)
11492
- .join(' ');
11920
+ const classNames = ['linear-progress', getColorClass(color, colorIntensity), className].filter(Boolean).join(' ');
11493
11921
  // ARIA attributes
11494
11922
  const ariaAttrs = isDeterminate
11495
11923
  ? {
@@ -11566,10 +11994,12 @@
11566
11994
  exports.DoubleRangeSlider = DoubleRangeSlider;
11567
11995
  exports.Dropdown = Dropdown;
11568
11996
  exports.EmailInput = EmailInput;
11997
+ exports.Fieldset = Fieldset;
11569
11998
  exports.FileInput = FileInput;
11570
11999
  exports.FileUpload = FileUpload;
11571
12000
  exports.FlatButton = FlatButton;
11572
12001
  exports.FloatingActionButton = FloatingActionButton;
12002
+ exports.FormSection = FormSection;
11573
12003
  exports.HelperText = HelperText;
11574
12004
  exports.Icon = Icon;
11575
12005
  exports.IconButton = IconButton;
@@ -11621,6 +12051,7 @@
11621
12051
  exports.Timeline = Timeline;
11622
12052
  exports.Toast = Toast;
11623
12053
  exports.ToastComponent = ToastComponent;
12054
+ exports.ToggleButton = ToggleButton;
11624
12055
  exports.ToggleGroup = ToggleGroup;
11625
12056
  exports.Tooltip = Tooltip;
11626
12057
  exports.TooltipComponent = TooltipComponent;
@@ -11629,10 +12060,14 @@
11629
12060
  exports.Wizard = Wizard;
11630
12061
  exports.addLeadingZero = addLeadingZero;
11631
12062
  exports.clearPortal = clearPortal;
12063
+ exports.createAsyncComboboxState = createAsyncComboboxState;
11632
12064
  exports.createBreadcrumb = createBreadcrumb;
11633
12065
  exports.formatTime = formatTime;
11634
12066
  exports.generateHourOptions = generateHourOptions;
11635
12067
  exports.generateMinuteOptions = generateMinuteOptions;
12068
+ exports.getComboboxKeyResult = getComboboxKeyResult;
12069
+ exports.getComboboxOptionId = getComboboxOptionId;
12070
+ exports.getComboboxViewState = getComboboxViewState;
11636
12071
  exports.getDropdownStyles = getDropdownStyles;
11637
12072
  exports.getPortalContainer = getPortalContainer;
11638
12073
  exports.initPushpins = initPushpins;
@@ -11641,17 +12076,25 @@
11641
12076
  exports.isTimeDisabled = isTimeDisabled;
11642
12077
  exports.isValidationError = isValidationError;
11643
12078
  exports.isValidationSuccess = isValidationSuccess;
12079
+ exports.normalizeSelection = normalizeSelection;
11644
12080
  exports.padLeft = padLeft;
11645
12081
  exports.parseTime = parseTime;
11646
12082
  exports.range = range;
12083
+ exports.rejectAsyncComboboxRequest = rejectAsyncComboboxRequest;
11647
12084
  exports.releasePortalContainer = releasePortalContainer;
12085
+ exports.renderFieldChrome = renderFieldChrome;
11648
12086
  exports.renderToPortal = renderToPortal;
12087
+ exports.resolveAsyncComboboxRequest = resolveAsyncComboboxRequest;
12088
+ exports.resolveControllableValue = resolveControllableValue;
11649
12089
  exports.scrollToValue = scrollToValue;
11650
12090
  exports.snapToNearestItem = snapToNearestItem;
11651
12091
  exports.sortOptions = sortOptions;
12092
+ exports.startAsyncComboboxRequest = startAsyncComboboxRequest;
12093
+ exports.syncPortalContent = syncPortalContent;
11652
12094
  exports.timeToMinutes = timeToMinutes;
11653
12095
  exports.toast = toast;
11654
12096
  exports.uniqueId = uniqueId;
11655
12097
  exports.uuid4 = uuid4;
11656
12098
 
11657
12099
  }));
12100
+ //# sourceMappingURL=index.umd.js.map