mithril-materialized 3.16.0 → 3.17.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.
Files changed (46) hide show
  1. package/README.md +32 -2
  2. package/dist/advanced.css +92 -1
  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 +127 -14
  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 +340 -19
  12. package/dist/index.d.ts +3 -2
  13. package/dist/index.esm.js +698 -296
  14. package/dist/index.esm.js.map +1 -0
  15. package/dist/index.js +711 -295
  16. package/dist/index.js.map +1 -0
  17. package/dist/index.min.css +2 -2
  18. package/dist/index.umd.js +711 -295
  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 +6 -0
  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 +16 -19
  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/_theme-variables.scss +22 -1
  40. package/sass/components/_toggle-group.scss +3 -0
  41. package/sass/components/forms/_form-groups.scss +1 -1
  42. package/sass/components/forms/_form-section.scss +63 -0
  43. package/sass/components/forms/_forms.scss +1 -0
  44. package/sass/components/forms/_input-fields.scss +55 -0
  45. package/sass/components/forms/_range-enhanced.scss +3 -3
  46. package/sass/components/forms/_select.scss +85 -0
package/dist/index.js CHANGED
@@ -36,6 +36,35 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
36
36
  return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
37
37
  };
38
38
 
39
+ // import './styles/input.css';
40
+ const Mandatory = { view: ({ attrs }) => m('span.mandatory', Object.assign({}, attrs), '*') };
41
+ /** Simple label element, used for most components. */
42
+ const Label = () => {
43
+ return {
44
+ view: (_a) => {
45
+ var _b = _a.attrs, { label, id, isMandatory, isActive, className, initialValue } = _b, params = __rest(_b, ["label", "id", "isMandatory", "isActive", "className", "initialValue"]);
46
+ return label
47
+ ? m('label', Object.assign(Object.assign({}, params), { className: [className, isActive ? 'active' : ''].filter(Boolean).join(' ').trim() || undefined, for: id, oncreate: ({ dom }) => {
48
+ if (!initialValue)
49
+ return;
50
+ const labelEl = dom;
51
+ labelEl.classList.add('active');
52
+ } }), [m.trust(label), isMandatory ? m(Mandatory) : undefined])
53
+ : undefined;
54
+ },
55
+ };
56
+ };
57
+ /** Create a helper text, often used for displaying a small help text. May be replaced by the validation message. */
58
+ const HelperText = () => {
59
+ return {
60
+ view: ({ attrs: { helperText, dataError, dataSuccess, className } }) => {
61
+ return helperText || dataError || dataSuccess
62
+ ? m('span.helper-text.left', { className, 'data-error': dataError, 'data-success': dataSuccess }, dataError ? m.trust(dataError) : dataSuccess ? m.trust(dataSuccess) : helperText ? m.trust(helperText) : '')
63
+ : undefined;
64
+ },
65
+ };
66
+ };
67
+
39
68
  // Utility functions for the library
40
69
  /**
41
70
  * Create a unique ID
@@ -96,6 +125,42 @@ const sortOptions = (options, sortConfig) => {
96
125
  * @returns
97
126
  */
98
127
  const padLeft = (n, width = 2, z = '0') => String(n).padStart(width, z);
128
+ const normalizeSelection = (value) => {
129
+ if (value === undefined) {
130
+ return [];
131
+ }
132
+ return Array.isArray(value) ? value : [value];
133
+ };
134
+ const resolveControllableValue = ({ controlled, disabled, controlledValue, defaultValue, internalValue, fallbackValue, }) => {
135
+ var _a, _b;
136
+ if (controlled) {
137
+ return controlledValue !== null && controlledValue !== void 0 ? controlledValue : fallbackValue;
138
+ }
139
+ if (disabled) {
140
+ return (_a = defaultValue !== null && defaultValue !== void 0 ? defaultValue : controlledValue) !== null && _a !== void 0 ? _a : fallbackValue;
141
+ }
142
+ return (_b = internalValue !== null && internalValue !== void 0 ? internalValue : defaultValue) !== null && _b !== void 0 ? _b : fallbackValue;
143
+ };
144
+ const renderFieldChrome = ({ label, id, isMandatory, isActive, initialValue, helperText, dataError, dataSuccess, }) => {
145
+ return [
146
+ label
147
+ ? m(Label, {
148
+ label,
149
+ id,
150
+ isMandatory,
151
+ isActive,
152
+ initialValue,
153
+ })
154
+ : undefined,
155
+ helperText || dataError || dataSuccess
156
+ ? m(HelperText, {
157
+ helperText,
158
+ dataError,
159
+ dataSuccess,
160
+ })
161
+ : undefined,
162
+ ].filter(Boolean);
163
+ };
99
164
  // Keep only essential dropdown positioning styles
100
165
  const getDropdownStyles = (inputRef, overlap = false, options, isDropDown = false) => {
101
166
  if (!inputRef) {
@@ -127,8 +192,10 @@ const getDropdownStyles = (inputRef, overlap = false, options, isDropDown = fals
127
192
  groups.add(option.group);
128
193
  }
129
194
  });
130
- // Calculate total height: options + group headers + padding
131
- estimatedHeight = totalOptions * itemHeight + groups.size * groupHeaderHeight;
195
+ // Match the select dropdown's CSS max-height. Positioning with the full
196
+ // option count would otherwise leave a gap above an input near the bottom
197
+ // of the viewport when the rendered menu is capped at 400px.
198
+ estimatedHeight = Math.min(totalOptions * itemHeight + groups.size * groupHeaderHeight, 400);
132
199
  }
133
200
  const spaceBelow = viewportHeight - rect.bottom;
134
201
  const spaceAbove = rect.top;
@@ -144,33 +211,26 @@ const getDropdownStyles = (inputRef, overlap = false, options, isDropDown = fals
144
211
  const needsScrolling = estimatedHeight > effectiveAvailableSpace;
145
212
  // Calculate the actual height the dropdown will take
146
213
  const actualHeight = needsScrolling ? effectiveAvailableSpace : estimatedHeight;
147
- // Calculate positioning when dropdown should appear above
148
- let topOffset;
149
- if (shouldPositionAbove) {
150
- // Calculate how much space we actually have from top of viewport to top of input
151
- const availableSpaceFromViewportTop = rect.top;
152
- // If dropdown fits comfortably above input, use normal positioning
153
- if (actualHeight <= availableSpaceFromViewportTop) {
154
- topOffset = 12 - actualHeight + (isDropDown ? itemHeight : 0); // Bottom of dropdown aligns with top of input
155
- }
156
- else {
157
- // If dropdown is too tall, position it at the very top of viewport
158
- // This makes the dropdown use all available space from viewport top to input top
159
- topOffset = -availableSpaceFromViewportTop + 5; // 5px margin from viewport top
160
- }
161
- }
162
- else {
163
- topOffset = overlap ? 0 : '100%';
164
- }
165
214
  const styles = {
166
215
  display: 'block',
167
216
  opacity: 1,
168
217
  position: 'absolute',
169
- top: typeof topOffset === 'number' ? `${topOffset}px` : topOffset,
170
218
  left: '0',
171
219
  zIndex: 1000,
172
220
  width: `${rect.width}px`,
173
221
  };
222
+ if (shouldPositionAbove) {
223
+ // This menu is absolutely positioned inside the select wrapper. A negative
224
+ // `top` calculated from viewport coordinates becomes wrong when an ancestor
225
+ // scrolls (as ScenarioSpark's main area does). Anchor the menu to its local
226
+ // containing block instead; its height can then change without detaching it
227
+ // from the input.
228
+ styles.top = 'auto';
229
+ styles.bottom = `${isDropDown ? -18 : inputRef.offsetHeight - 12}px`;
230
+ }
231
+ else {
232
+ styles.top = overlap ? 0 : '100%';
233
+ }
174
234
  // Only add scrolling constraints when necessary
175
235
  if (needsScrolling) {
176
236
  styles.maxHeight = `${actualHeight}px`;
@@ -237,9 +297,17 @@ const releasePortalContainer = (id) => {
237
297
  * @param zIndex - Z-index for portal container (default: 1004)
238
298
  */
239
299
  const renderToPortal = (containerId, vnode, zIndex = 1004) => {
240
- const container = getPortalContainer(containerId, zIndex);
300
+ const existingContainer = portalContainers.get(containerId);
301
+ const container = existingContainer ? existingContainer.element : getPortalContainer(containerId, zIndex);
241
302
  m.render(container, vnode);
242
303
  };
304
+ const syncPortalContent = ({ containerId, shouldRender, vnode, zIndex = 1004 }) => {
305
+ if (!shouldRender || vnode === null) {
306
+ clearPortal(containerId);
307
+ return;
308
+ }
309
+ renderToPortal(containerId, vnode, zIndex);
310
+ };
243
311
  /**
244
312
  * Clears portal content and releases container reference.
245
313
  * If this is the last reference, the container will be removed from the DOM.
@@ -254,35 +322,6 @@ const clearPortal = (containerId) => {
254
322
  }
255
323
  };
256
324
 
257
- // import './styles/input.css';
258
- const Mandatory = { view: ({ attrs }) => m('span.mandatory', Object.assign({}, attrs), '*') };
259
- /** Simple label element, used for most components. */
260
- const Label = () => {
261
- return {
262
- view: (_a) => {
263
- var _b = _a.attrs, { label, id, isMandatory, isActive, className, initialValue } = _b, params = __rest(_b, ["label", "id", "isMandatory", "isActive", "className", "initialValue"]);
264
- return label
265
- ? m('label', Object.assign(Object.assign({}, params), { className: [className, isActive ? 'active' : ''].filter(Boolean).join(' ').trim() || undefined, for: id, oncreate: ({ dom }) => {
266
- if (!initialValue)
267
- return;
268
- const labelEl = dom;
269
- labelEl.classList.add('active');
270
- } }), [m.trust(label), isMandatory ? m(Mandatory) : undefined])
271
- : undefined;
272
- },
273
- };
274
- };
275
- /** Create a helper text, often used for displaying a small help text. May be replaced by the validation message. */
276
- const HelperText = () => {
277
- return {
278
- view: ({ attrs: { helperText, dataError, dataSuccess, className } }) => {
279
- return helperText || dataError || dataSuccess
280
- ? m('span.helper-text.left', { className, 'data-error': dataError, 'data-success': dataSuccess }, dataError ? m.trust(dataError) : dataSuccess ? m.trust(dataSuccess) : helperText ? m.trust(helperText) : '')
281
- : undefined;
282
- },
283
- };
284
- };
285
-
286
325
  /** Component to auto complete your text input - Pure Mithril implementation */
287
326
  const Autocomplete = () => {
288
327
  const state = {
@@ -305,6 +344,28 @@ const Autocomplete = () => {
305
344
  .slice(0, limit);
306
345
  return filtered;
307
346
  };
347
+ const highlightMatch = (text, query) => {
348
+ if (!query) {
349
+ return text;
350
+ }
351
+ const lowerText = text.toLowerCase();
352
+ const lowerQuery = query.toLowerCase();
353
+ const nodes = [];
354
+ let start = 0;
355
+ let index = lowerText.indexOf(lowerQuery, start);
356
+ while (index !== -1) {
357
+ if (index > start) {
358
+ nodes.push(text.slice(start, index));
359
+ }
360
+ nodes.push(m('span.highlight', text.slice(index, index + query.length)));
361
+ start = index + query.length;
362
+ index = lowerText.indexOf(lowerQuery, start);
363
+ }
364
+ if (start < text.length) {
365
+ nodes.push(text.slice(start));
366
+ }
367
+ return nodes.length > 0 ? nodes : text;
368
+ };
308
369
  const selectSuggestion = (suggestion, attrs) => {
309
370
  const controlled = isControlled(attrs);
310
371
  // Update internal state for uncontrolled mode
@@ -410,7 +471,6 @@ const Autocomplete = () => {
410
471
  Object.keys(data).some((key) => key.toLowerCase() === currentValue.toLowerCase());
411
472
  // Only open dropdown if there are suggestions and no perfect match
412
473
  state.isOpen = state.suggestions.length > 0 && currentValue.length >= minLength && !hasExactMatch;
413
- const replacer = new RegExp(`(${currentValue})`, 'i');
414
474
  return m('.input-field.autocomplete-wrapper', {
415
475
  className: cn,
416
476
  style,
@@ -491,9 +551,7 @@ const Autocomplete = () => {
491
551
  },
492
552
  }, suggestion.value.replace('icon:', ''))
493
553
  : null,
494
- m('span', suggestion.key
495
- ? m.trust(suggestion.key.replace(replacer, (i) => `<span class="highlight">${i}</span>`))
496
- : ''),
554
+ m('span', suggestion.key ? highlightMatch(suggestion.key, currentValue) : ''),
497
555
  ]))),
498
556
  m(Label, {
499
557
  label,
@@ -720,7 +778,7 @@ WavesEffect.onTouchEnd = (e) => {
720
778
  /**
721
779
  * A factory to create new buttons.
722
780
  *
723
- * @example FlatButton = ButtonFactory('a.waves-effect.waves-teal.btn-flat');
781
+ * @example FlatButton = ButtonFactory('button.waves-effect.waves-teal.btn-flat');
724
782
  */
725
783
  const ButtonFactory = (element, defaultClassNames, type = '') => {
726
784
  return () => {
@@ -743,15 +801,21 @@ const ButtonFactory = (element, defaultClassNames, type = '') => {
743
801
  ontouchend: WavesEffect.onTouchEnd,
744
802
  }
745
803
  : {};
746
- 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);
804
+ const tagName = element.split(/[.#]/)[0] || element;
805
+ const elementSuffix = element.slice(tagName.length);
806
+ const isLink = Boolean(attrs.href);
807
+ const renderedElement = `${isLink ? 'a' : 'button'}${elementSuffix}`;
808
+ return m(renderedElement, Object.assign(Object.assign(Object.assign({}, params), wavesHandlers), { className: cn, 'data-position': tooltip ? position : undefined, 'data-tooltip': tooltip || undefined,
809
+ // Links retain native navigation semantics and must not have a button type.
810
+ type: isLink ? undefined : buttonType }), iconName ? m(Icon, { iconName, className: iconClass !== undefined ? iconClass : 'left' }) : undefined, label ? label : undefined, children);
747
811
  },
748
812
  };
749
813
  };
750
814
  };
751
- const Button = ButtonFactory('a', 'waves-effect waves-light btn', 'button');
752
- const LargeButton = ButtonFactory('a', 'waves-effect waves-light btn-large', 'button');
753
- const SmallButton = ButtonFactory('a', 'waves-effect waves-light btn-small', 'button');
754
- const FlatButton = ButtonFactory('a', 'waves-effect waves-teal btn-flat', 'button');
815
+ const Button = ButtonFactory('button', 'waves-effect waves-light btn', 'button');
816
+ const LargeButton = ButtonFactory('button', 'waves-effect waves-light btn-large', 'button');
817
+ const SmallButton = ButtonFactory('button', 'waves-effect waves-light btn-small', 'button');
818
+ const FlatButton = ButtonFactory('button', 'waves-effect waves-teal btn-flat', 'button');
755
819
  const IconButton = ButtonFactory('button', 'btn-flat btn-icon waves-effect waves-teal', 'button');
756
820
  const RoundIconButton = ButtonFactory('button', 'btn-floating btn-large waves-effect waves-light', 'button');
757
821
  const SubmitButton = ButtonFactory('button', 'btn waves-effect waves-light', 'submit');
@@ -808,7 +872,7 @@ const ConfirmButton = () => {
808
872
  // Add square styling for icon-only confirming state
809
873
  const buttonStyle = !label && isConfirming
810
874
  ? 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;
811
- return m(ButtonComponent, Object.assign(Object.assign({}, props), { style: buttonStyle, className: `${props.className || ''} ${cn}`, iconName: currentIconName, iconClass: label ? (iconClass || 'left') : '', label, onclick: handleClick }));
875
+ return m(ButtonComponent, Object.assign(Object.assign({}, props), { style: buttonStyle, className: `${props.className || ''} ${cn}`, iconName: currentIconName, iconClass: label ? iconClass || 'left' : '', label, onclick: handleClick }));
812
876
  },
813
877
  };
814
878
  };
@@ -1275,11 +1339,19 @@ const MaterialIcon = () => {
1275
1339
  };
1276
1340
  const rotation = (_a = rotationMap[direction]) !== null && _a !== void 0 ? _a : 0;
1277
1341
  const transform = rotation ? `rotate(${rotation}deg)` : undefined;
1342
+ const baseStyle = {
1343
+ display: 'inline-block',
1344
+ verticalAlign: 'middle',
1345
+ transform,
1346
+ };
1347
+ const combinedStyle = typeof style === 'string'
1348
+ ? `display:inline-block;vertical-align:middle;${transform ? `transform:${transform};` : ''}${style}`
1349
+ : Object.assign(Object.assign({}, baseStyle), style);
1278
1350
  const icon = iconPaths[name];
1279
1351
  if (!icon || !Array.isArray(icon)) {
1280
1352
  return m(Icon, Object.assign(Object.assign({}, props), { iconName: name }));
1281
1353
  }
1282
- 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', {
1354
+ 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', {
1283
1355
  d,
1284
1356
  fill: d.includes('M0 0h24v24H0z') ? 'none' : 'currentColor',
1285
1357
  })));
@@ -1678,6 +1750,106 @@ const Collapsible = () => {
1678
1750
  };
1679
1751
  };
1680
1752
 
1753
+ const isHandledKey = (key) => key === 'ArrowDown' || key === 'ArrowUp' || key === 'Enter' || key === ' ' || key === 'Escape';
1754
+ const getTotalRows = (optionCount, includeActionRow) => Math.max(0, optionCount) + (includeActionRow ? 1 : 0);
1755
+ const getComboboxKeyResult = ({ key, isOpen, focusedIndex, optionCount, includeActionRow, }) => {
1756
+ const totalRows = getTotalRows(optionCount, includeActionRow);
1757
+ if (!isHandledKey(key)) {
1758
+ return { isOpen, focusedIndex, action: 'none', preventDefault: false };
1759
+ }
1760
+ if (key === 'Escape') {
1761
+ return { isOpen: false, focusedIndex: -1, action: 'close', preventDefault: true };
1762
+ }
1763
+ if (key === 'ArrowDown') {
1764
+ if (!isOpen) {
1765
+ return {
1766
+ isOpen: true,
1767
+ focusedIndex: totalRows > 0 ? 0 : -1,
1768
+ action: 'open',
1769
+ preventDefault: true,
1770
+ };
1771
+ }
1772
+ return {
1773
+ isOpen: true,
1774
+ focusedIndex: totalRows > 0 ? Math.min(focusedIndex + 1, totalRows - 1) : -1,
1775
+ action: 'none',
1776
+ preventDefault: true,
1777
+ };
1778
+ }
1779
+ if (key === 'ArrowUp') {
1780
+ if (!isOpen) {
1781
+ return {
1782
+ isOpen: true,
1783
+ focusedIndex: totalRows > 0 ? 0 : -1,
1784
+ action: 'open',
1785
+ preventDefault: true,
1786
+ };
1787
+ }
1788
+ return {
1789
+ isOpen: true,
1790
+ focusedIndex: totalRows > 0 ? Math.max(focusedIndex - 1, 0) : -1,
1791
+ action: 'none',
1792
+ preventDefault: true,
1793
+ };
1794
+ }
1795
+ if (!isOpen) {
1796
+ return {
1797
+ isOpen: true,
1798
+ focusedIndex: totalRows > 0 ? 0 : -1,
1799
+ action: 'open',
1800
+ preventDefault: true,
1801
+ };
1802
+ }
1803
+ if (focusedIndex < 0) {
1804
+ return { isOpen, focusedIndex, action: 'none', preventDefault: true };
1805
+ }
1806
+ const isActionRow = includeActionRow && focusedIndex === optionCount;
1807
+ return {
1808
+ isOpen,
1809
+ focusedIndex,
1810
+ action: isActionRow ? 'selectAction' : 'selectFocused',
1811
+ preventDefault: true,
1812
+ };
1813
+ };
1814
+ const getComboboxOptionId = (baseId, optionIndex) => `${baseId}-option-${optionIndex}`;
1815
+ const createAsyncComboboxState = (initialOptions = []) => ({
1816
+ options: initialOptions,
1817
+ isLoading: false,
1818
+ error: null,
1819
+ latestRequestId: 0,
1820
+ });
1821
+ const startAsyncComboboxRequest = (state) => {
1822
+ const requestId = state.latestRequestId + 1;
1823
+ return {
1824
+ requestId,
1825
+ nextState: Object.assign(Object.assign({}, state), { isLoading: true, error: null, latestRequestId: requestId }),
1826
+ };
1827
+ };
1828
+ const resolveAsyncComboboxRequest = (state, requestId, options) => {
1829
+ if (requestId !== state.latestRequestId) {
1830
+ return state;
1831
+ }
1832
+ return Object.assign(Object.assign({}, state), { options, isLoading: false, error: null });
1833
+ };
1834
+ const rejectAsyncComboboxRequest = (state, requestId, errorMessage) => {
1835
+ if (requestId !== state.latestRequestId) {
1836
+ return state;
1837
+ }
1838
+ return Object.assign(Object.assign({}, state), { options: [], isLoading: false, error: errorMessage });
1839
+ };
1840
+ const getComboboxViewState = ({ isLoading, error, optionCount, }) => {
1841
+ if (isLoading) {
1842
+ return 'loading';
1843
+ }
1844
+ if (error) {
1845
+ return 'error';
1846
+ }
1847
+ if (optionCount === 0) {
1848
+ return 'empty';
1849
+ }
1850
+ return 'ready';
1851
+ };
1852
+
1681
1853
  exports.CollectionMode = void 0;
1682
1854
  (function (CollectionMode) {
1683
1855
  CollectionMode[CollectionMode["BASIC"] = 0] = "BASIC";
@@ -3287,6 +3459,10 @@ const DoubleRangeSlider = {
3287
3459
  },
3288
3460
  };
3289
3461
 
3462
+ const isReadonly = (attrs) => {
3463
+ const legacyReadonly = attrs.readonly;
3464
+ return Boolean(attrs.readOnly || legacyReadonly);
3465
+ };
3290
3466
  /** Character counter component that tracks text length against maxLength */
3291
3467
  const CharacterCounter = () => {
3292
3468
  return {
@@ -3365,7 +3541,7 @@ const TextArea = () => {
3365
3541
  return {
3366
3542
  oninit: ({ attrs }) => {
3367
3543
  const controlled = isControlled(attrs);
3368
- const isNonInteractive = attrs.readonly || attrs.disabled;
3544
+ const isNonInteractive = isReadonly(attrs) || attrs.disabled;
3369
3545
  // Warn developer for improper controlled usage
3370
3546
  if (attrs.value !== undefined && !controlled && !isNonInteractive) {
3371
3547
  console.warn(`TextArea received 'value' prop without 'oninput' or 'onchange' handler. ` +
@@ -3382,7 +3558,7 @@ const TextArea = () => {
3382
3558
  var _a, _b, _c, _d;
3383
3559
  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
3560
  const controlled = isControlled(attrs);
3385
- const isNonInteractive = attrs.readonly || attrs.disabled;
3561
+ const isNonInteractive = isReadonly(attrs) || attrs.disabled;
3386
3562
  let currentValue;
3387
3563
  if (controlled) {
3388
3564
  currentValue = value || '';
@@ -3498,14 +3674,12 @@ const TextArea = () => {
3498
3674
  onkeypress(ev, ev.target.value);
3499
3675
  }
3500
3676
  : undefined })),
3501
- m(Label, {
3677
+ ...renderFieldChrome({
3502
3678
  label,
3503
3679
  id,
3504
3680
  isMandatory,
3505
3681
  isActive: currentValue || placeholder || state.active,
3506
3682
  initialValue: currentValue !== '',
3507
- }),
3508
- m(HelperText, {
3509
3683
  helperText,
3510
3684
  dataError: state.hasInteracted && attrs.dataError ? attrs.dataError : undefined,
3511
3685
  dataSuccess: state.hasInteracted && attrs.dataSuccess ? attrs.dataSuccess : undefined,
@@ -3585,7 +3759,7 @@ const InputField = (type, defaultClass = '') => () => {
3585
3759
  return {
3586
3760
  oninit: ({ attrs }) => {
3587
3761
  const controlled = isControlled(attrs);
3588
- const isNonInteractive = attrs.readonly || attrs.disabled;
3762
+ const isNonInteractive = isReadonly(attrs) || attrs.disabled;
3589
3763
  // Warn developer for improper controlled usage
3590
3764
  if (attrs.value !== undefined && !controlled && !isNonInteractive) {
3591
3765
  console.warn(`${type} input with label '${attrs.label}' received 'value' prop without 'oninput' handler. ` +
@@ -3625,7 +3799,7 @@ const InputField = (type, defaultClass = '') => () => {
3625
3799
  }
3626
3800
  const isNumeric = ['number', 'range'].includes(type);
3627
3801
  const controlled = isControlled(attrs);
3628
- const isNonInteractive = attrs.readonly || attrs.disabled;
3802
+ const isNonInteractive = isReadonly(attrs) || attrs.disabled;
3629
3803
  let value;
3630
3804
  if (controlled) {
3631
3805
  value = attrs.value;
@@ -3644,9 +3818,28 @@ const InputField = (type, defaultClass = '') => () => {
3644
3818
  const rangeType = type === 'range' && !attrs.minmax;
3645
3819
  // Only add validate class if input is interactive and validation is needed
3646
3820
  const shouldValidate = !isNonInteractive && (validate || type === 'email' || type === 'url' || isNumeric);
3821
+ const inputClass = [
3822
+ type === 'number' ? 'number-input' : '',
3823
+ type === 'range' && attrs.vertical ? 'range-slider vertical' : '',
3824
+ shouldValidate ? 'validate' : '',
3825
+ ]
3826
+ .filter(Boolean)
3827
+ .join(' ');
3828
+ const stepNumberInput = (direction) => {
3829
+ const input = state.inputElement;
3830
+ if (!input)
3831
+ return;
3832
+ if (direction === 'up') {
3833
+ input.stepUp();
3834
+ }
3835
+ else {
3836
+ input.stepDown();
3837
+ }
3838
+ input.dispatchEvent(new Event('input', { bubbles: true }));
3839
+ };
3647
3840
  return m('.input-field', { className: cn, style }, [
3648
3841
  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,
3842
+ m('input', Object.assign(Object.assign({ class: inputClass || undefined }, params), { type, tabindex: 0, id,
3650
3843
  placeholder, value: controlled ? value : undefined, style: type === 'range' && attrs.vertical
3651
3844
  ? {
3652
3845
  height: attrs.height || '200px',
@@ -3728,7 +3921,7 @@ const InputField = (type, defaultClass = '') => () => {
3728
3921
  const target = e.target;
3729
3922
  state.hasInteracted = true;
3730
3923
  // Skip validation for readonly/disabled inputs
3731
- if (attrs.readonly || attrs.disabled) {
3924
+ if (isReadonly(attrs) || attrs.disabled) {
3732
3925
  // Call original onblur if provided
3733
3926
  if (attrs.onblur) {
3734
3927
  attrs.onblur(e);
@@ -3818,6 +4011,22 @@ const InputField = (type, defaultClass = '') => () => {
3818
4011
  onchange(getValue(state.inputElement));
3819
4012
  }
3820
4013
  } })),
4014
+ type === 'number' && !isNonInteractive
4015
+ ? m('.number-input-controls', [
4016
+ m('button.number-input-control.number-input-control-up[type=button]', {
4017
+ 'aria-label': `Increase ${label || 'value'}`,
4018
+ title: 'Increase value',
4019
+ onmousedown: (event) => event.preventDefault(),
4020
+ onclick: () => stepNumberInput('up'),
4021
+ }, m(MaterialIcon, { name: 'chevron', direction: 'up' })),
4022
+ m('button.number-input-control.number-input-control-down[type=button]', {
4023
+ 'aria-label': `Decrease ${label || 'value'}`,
4024
+ title: 'Decrease value',
4025
+ onmousedown: (event) => event.preventDefault(),
4026
+ onclick: () => stepNumberInput('down'),
4027
+ }, m(MaterialIcon, { name: 'chevron' })),
4028
+ ])
4029
+ : undefined,
3821
4030
  // Clear button - only for text inputs with canClear enabled and has content
3822
4031
  canClear && type === 'text' && ((_f = state.inputElement) === null || _f === void 0 ? void 0 : _f.value)
3823
4032
  ? m(MaterialIcon, {
@@ -3830,14 +4039,12 @@ const InputField = (type, defaultClass = '') => () => {
3830
4039
  },
3831
4040
  })
3832
4041
  : undefined,
3833
- m(Label, {
4042
+ ...renderFieldChrome({
3834
4043
  label,
3835
4044
  id,
3836
4045
  isMandatory,
3837
4046
  isActive,
3838
4047
  initialValue: value !== undefined && value !== '',
3839
- }),
3840
- m(HelperText, {
3841
4048
  helperText,
3842
4049
  dataError: state.hasInteracted && !state.isValid ? dataError : undefined,
3843
4050
  dataSuccess: state.hasInteracted && state.isValid ? dataSuccess : undefined,
@@ -3937,7 +4144,7 @@ const InputCheckbox = () => {
3937
4144
  view: ({ attrs: { className = 'col s12', onchange, label, checked, disabled, description, style, inputId } }) => {
3938
4145
  if (!checkboxId)
3939
4146
  checkboxId = inputId || uniqueId();
3940
- return m('div', { className, style }, m('label', { for: checkboxId }, [
4147
+ return m('div', { className, style }, m('label', { for: checkboxId, style: { position: 'relative', display: 'inline-block' } }, [
3941
4148
  m('input[type=checkbox][tabindex=0]', {
3942
4149
  className: disabled ? 'disabled' : undefined,
3943
4150
  id: checkboxId,
@@ -4702,17 +4909,6 @@ const Dropdown = () => {
4702
4909
  const updatePortalDropdown = (items, selectedLabel, onSelectItem, maxHeight) => {
4703
4910
  if (!state.isInsideModal)
4704
4911
  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
4912
  // Create dropdown content
4717
4913
  const availableItems = items.filter((item) => !item.divider && !item.disabled);
4718
4914
  const dropdownContent = items.map((item) => {
@@ -4726,15 +4922,14 @@ const Dropdown = () => {
4726
4922
  class: `${isSelected ? 'selected' : ''} ${isFocused ? 'focused' : ''}${item.disabled ? ' disabled' : ''}`,
4727
4923
  onclick: item.disabled ? undefined : () => onSelectItem(item),
4728
4924
  }, m('span', {
4925
+ class: 'mm-layout-row mm-layout-row--center',
4729
4926
  style: {
4730
- display: 'flex',
4731
- alignItems: 'center',
4732
4927
  padding: '14px 16px',
4733
4928
  },
4734
4929
  }, [
4735
4930
  item.iconName
4736
4931
  ? m('i.material-icons', {
4737
- style: { marginRight: '32px' },
4932
+ class: 'mm-layout-item-icon',
4738
4933
  }, item.iconName)
4739
4934
  : undefined,
4740
4935
  item.label,
@@ -4751,8 +4946,12 @@ const Dropdown = () => {
4751
4946
  state.dropdownRef = null;
4752
4947
  },
4753
4948
  }, dropdownContent);
4754
- // Render to portal
4755
- m.render(portalElement, dropdownVnode);
4949
+ syncPortalContent({
4950
+ containerId: `${state.id}-dropdown`,
4951
+ shouldRender: state.isOpen && !!state.inputRef,
4952
+ vnode: dropdownVnode,
4953
+ zIndex: 10000,
4954
+ });
4756
4955
  };
4757
4956
  return {
4758
4957
  oninit: ({ attrs }) => {
@@ -4773,15 +4972,19 @@ const Dropdown = () => {
4773
4972
  // Cleanup global listener
4774
4973
  document.removeEventListener('click', closeDropdown);
4775
4974
  // Cleanup portal
4776
- const portalElement = document.getElementById(`${state.id}-dropdown`);
4777
- if (portalElement) {
4778
- portalElement.remove();
4779
- }
4975
+ syncPortalContent({ containerId: `${state.id}-dropdown`, shouldRender: false, vnode: null });
4780
4976
  },
4781
4977
  view: ({ attrs }) => {
4782
4978
  const { checkedId, key, label, onchange, disabled = false, items, iconName, helperText, style, className = 'col s12', } = attrs;
4783
4979
  const controlled = isControlled(attrs);
4784
- const currentCheckedId = controlled ? checkedId : state.internalCheckedId;
4980
+ const currentCheckedId = resolveControllableValue({
4981
+ controlled,
4982
+ disabled,
4983
+ controlledValue: checkedId,
4984
+ defaultValue: attrs.defaultCheckedId,
4985
+ internalValue: state.internalCheckedId,
4986
+ fallbackValue: undefined,
4987
+ });
4785
4988
  const handleSelection = (value) => {
4786
4989
  // Update internal state for uncontrolled mode
4787
4990
  if (!controlled) {
@@ -4813,6 +5016,7 @@ const Dropdown = () => {
4813
5016
  iconName ? m('i.material-icons.prefix', iconName) : undefined,
4814
5017
  m(HelperText, { helperText }),
4815
5018
  m('.select-wrapper', {
5019
+ class: 'mm-layout-row mm-layout-row--center',
4816
5020
  onkeydown: disabled
4817
5021
  ? undefined
4818
5022
  : (e) => {
@@ -4880,15 +5084,14 @@ const Dropdown = () => {
4880
5084
  handleSelection(value);
4881
5085
  },
4882
5086
  }, m('span', {
5087
+ class: 'mm-layout-row mm-layout-row--center',
4883
5088
  style: {
4884
- display: 'flex',
4885
- alignItems: 'center',
4886
5089
  padding: '14px 16px',
4887
5090
  },
4888
5091
  }, [
4889
5092
  item.iconName
4890
5093
  ? m('i.material-icons', {
4891
- style: { marginRight: '32px' },
5094
+ class: 'mm-layout-item-icon',
4892
5095
  }, item.iconName)
4893
5096
  : undefined,
4894
5097
  item.label,
@@ -6845,9 +7048,9 @@ const initPushpins = (selector = '.pushpin', options = {}) => {
6845
7048
  };
6846
7049
 
6847
7050
  const RadioButton = () => ({
6848
- view: ({ attrs: { id, groupId, label, onchange, className = 'col s12', checked, disabled, inputId } }) => {
7051
+ view: ({ attrs: { id, groupId, label, onchange, className = 'col s12', checked, disabled, inputId, allowHtml } }) => {
6849
7052
  const radioId = inputId || `${groupId}-${id}`;
6850
- return m('p', { className }, m('label', { for: radioId }, [
7053
+ return m('p', { className }, m('label', { for: radioId, style: { position: 'relative', display: 'inline-block' } }, [
6851
7054
  m('input[type=radio][tabindex=0]', {
6852
7055
  id: radioId,
6853
7056
  name: groupId,
@@ -6855,7 +7058,7 @@ const RadioButton = () => ({
6855
7058
  checked,
6856
7059
  onclick: onchange ? () => onchange(id) : undefined,
6857
7060
  }),
6858
- m('span', m.trust(label)),
7061
+ m('span', allowHtml ? m.trust(label) : label),
6859
7062
  ]));
6860
7063
  },
6861
7064
  });
@@ -6882,8 +7085,9 @@ const RadioButtons = () => {
6882
7085
  }
6883
7086
  },
6884
7087
  view: ({ attrs }) => {
6885
- var _a, _b;
6886
- const { checkedId, newRow, className = 'col s12', label = '', disabled, description, options, isMandatory, checkboxClass, layout = 'vertical', onchange, } = attrs;
7088
+ var _a, _b, _c;
7089
+ const { checkedId, newRow, className = 'col s12', label = '', disabled, description, options, isMandatory, checkboxClass, layout, direction, allowHtml = false, onchange, } = attrs;
7090
+ const resolvedLayout = (_a = layout !== null && layout !== void 0 ? layout : direction) !== null && _a !== void 0 ? _a : 'vertical';
6887
7091
  const { groupId, componentId } = state;
6888
7092
  const controlled = isControlled(attrs);
6889
7093
  // Get current checked ID from props or internal state
@@ -6893,11 +7097,11 @@ const RadioButtons = () => {
6893
7097
  }
6894
7098
  else if (disabled) {
6895
7099
  // Non-interactive components: prefer defaultCheckedId, fallback to checkedId
6896
- currentCheckedId = (_a = attrs.defaultCheckedId) !== null && _a !== void 0 ? _a : checkedId;
7100
+ currentCheckedId = (_b = attrs.defaultCheckedId) !== null && _b !== void 0 ? _b : checkedId;
6897
7101
  }
6898
7102
  else {
6899
7103
  // Interactive uncontrolled: use internal state
6900
- currentCheckedId = (_b = state.internalCheckedId) !== null && _b !== void 0 ? _b : attrs.defaultCheckedId;
7104
+ currentCheckedId = (_c = state.internalCheckedId) !== null && _c !== void 0 ? _c : attrs.defaultCheckedId;
6901
7105
  }
6902
7106
  const handleChange = (id) => {
6903
7107
  // Update internal state for uncontrolled mode
@@ -6912,16 +7116,16 @@ const RadioButtons = () => {
6912
7116
  const cn = [newRow ? 'clear' : '', className].filter(Boolean).join(' ').trim() || undefined;
6913
7117
  const radioItems = options.map((r) => ({
6914
7118
  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}` }),
7119
+ 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
7120
  key: r.id,
6917
7121
  }));
6918
7122
  const optionsContent = m(OptionsList, {
6919
7123
  options: radioItems,
6920
- layout,
7124
+ layout: resolvedLayout,
6921
7125
  });
6922
7126
  return m('div', { id: componentId, className: cn }, [
6923
7127
  label && m('h5.form-group-label', label + (isMandatory ? ' *' : '')),
6924
- description && m('p.helper-text', m.trust(description)),
7128
+ description && m('p.helper-text', allowHtml ? m.trust(description) : description),
6925
7129
  m('form', { action: '#' }, optionsContent),
6926
7130
  ]);
6927
7131
  },
@@ -7058,6 +7262,26 @@ const Select = () => {
7058
7262
  opacity: 1,
7059
7263
  };
7060
7264
  };
7265
+ const formatSelectionSummary = (attrs, selectedLabels, selectedIds, placeholder) => {
7266
+ const mode = attrs.summaryMode || 'labels';
7267
+ const selectedCount = selectedIds.length;
7268
+ const totalCount = attrs.options.filter((option) => !option.disabled).length;
7269
+ if (mode === 'labels') {
7270
+ return selectedLabels.length > 0 ? selectedLabels.join(', ') : placeholder;
7271
+ }
7272
+ const defaultCountLabel = `${selectedCount}/${totalCount} selected`;
7273
+ const formattedCount = attrs.countLabel ? attrs.countLabel(selectedCount, totalCount) : defaultCountLabel;
7274
+ if (mode === 'count') {
7275
+ return selectedCount > 0 ? formattedCount : placeholder;
7276
+ }
7277
+ if (selectedCount === 0) {
7278
+ return attrs.noneSelectedLabel || placeholder;
7279
+ }
7280
+ if (totalCount > 0 && selectedCount >= totalCount) {
7281
+ return attrs.allSelectedLabel || 'All selected';
7282
+ }
7283
+ return formattedCount;
7284
+ };
7061
7285
  const renderDropdownContent = (attrs, selectedIds, multiple, placeholder) => [
7062
7286
  placeholder && m('li.disabled', { tabindex: 0 }, m('span', placeholder)),
7063
7287
  // Render ungrouped options first
@@ -7126,17 +7350,6 @@ const Select = () => {
7126
7350
  const updatePortalDropdown = (attrs, selectedIds, multiple, placeholder) => {
7127
7351
  if (!state.isInsideModal)
7128
7352
  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
7353
  // Create dropdown with proper positioning
7141
7354
  const dropdownVnode = m('ul.dropdown-content.select-dropdown', {
7142
7355
  tabindex: 0,
@@ -7148,8 +7361,12 @@ const Select = () => {
7148
7361
  state.dropdownRef = null;
7149
7362
  },
7150
7363
  }, renderDropdownContent(attrs, selectedIds, multiple, placeholder));
7151
- // Render to portal
7152
- m.render(portalElement, dropdownVnode);
7364
+ syncPortalContent({
7365
+ containerId: state.dropdownId,
7366
+ shouldRender: state.isOpen && !!state.inputRef,
7367
+ vnode: dropdownVnode,
7368
+ zIndex: 10000,
7369
+ });
7153
7370
  };
7154
7371
  return {
7155
7372
  oninit: ({ attrs }) => {
@@ -7182,35 +7399,30 @@ const Select = () => {
7182
7399
  document.removeEventListener('click', closeDropdown);
7183
7400
  // Cleanup portaled dropdown if it exists
7184
7401
  if (state.isInsideModal && state.dropdownRef) {
7185
- const portalElement = document.getElementById(state.dropdownId);
7186
- if (portalElement && portalElement.parentNode) {
7187
- portalElement.parentNode.removeChild(portalElement);
7188
- }
7402
+ syncPortalContent({ containerId: state.dropdownId, shouldRender: false, vnode: null });
7189
7403
  }
7190
7404
  },
7191
7405
  view: ({ attrs }) => {
7192
- var _a;
7193
7406
  const controlled = isControlled(attrs);
7194
- const { newRow, className = 'col s12', key, options = [], multiple = false, label, helperText, placeholder = '', isMandatory, iconName, style, disabled, } = attrs;
7407
+ const { newRow, className = 'col s12', key, options = [], multiple = false, label, helperText, placeholder = '', isMandatory, iconName, appearance = 'standard', style, disabled, } = attrs;
7195
7408
  state.isMultiple = multiple;
7196
7409
  // 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;
7410
+ const selectedIds = resolveControllableValue({
7411
+ controlled,
7412
+ disabled,
7413
+ controlledValue: normalizeSelection(attrs.checkedId),
7414
+ defaultValue: normalizeSelection(attrs.defaultCheckedId),
7415
+ internalValue: state.internalSelectedIds,
7416
+ fallbackValue: [],
7417
+ });
7418
+ const layoutClassName = newRow ? `${className} clear` : className;
7419
+ const appearanceClassName = appearance === 'outlined' ? 'select-appearance-outlined' : '';
7420
+ const finalClassName = [layoutClassName, appearanceClassName].filter(Boolean).join(' ');
7421
+ const shouldInlineLabel = appearance === 'outlined' && !!label;
7212
7422
  const selectedOptionsUnsorted = options.filter((opt) => isSelected(opt.id, selectedIds));
7213
7423
  const selectedOptions = sortOptions(selectedOptionsUnsorted, attrs.sortSelected);
7424
+ const selectedLabels = selectedOptions.map((o) => o.label || o.id.toString());
7425
+ const triggerValue = formatSelectionSummary(attrs, selectedLabels, selectedIds, placeholder);
7214
7426
  // Update portal dropdown when inside modal
7215
7427
  if (state.isInsideModal) {
7216
7428
  updatePortalDropdown(attrs, selectedIds, multiple, placeholder);
@@ -7230,9 +7442,15 @@ const Select = () => {
7230
7442
  'aria-controls': state.dropdownId,
7231
7443
  role: 'combobox',
7232
7444
  }, [
7445
+ shouldInlineLabel &&
7446
+ m('span.select-inline-label', [
7447
+ label,
7448
+ isMandatory && m('span.mandatory', { style: { marginLeft: '2px' } }, '*'),
7449
+ ]),
7233
7450
  m('input[type=text][readonly=true].select-dropdown.dropdown-trigger', {
7234
7451
  id: state.id,
7235
- value: selectedOptions.length > 0 ? selectedOptions.map((o) => o.label || o.id).join(', ') : placeholder,
7452
+ value: triggerValue,
7453
+ 'aria-label': label,
7236
7454
  oncreate: ({ dom }) => {
7237
7455
  state.inputRef = dom;
7238
7456
  },
@@ -7264,14 +7482,12 @@ const Select = () => {
7264
7482
  }),
7265
7483
  ]),
7266
7484
  // Label
7267
- label &&
7268
- m(Label, {
7269
- id: state.id,
7270
- label,
7271
- isMandatory,
7272
- }),
7273
- // Helper text
7274
- helperText && m(HelperText, { helperText }),
7485
+ ...renderFieldChrome({
7486
+ label: shouldInlineLabel ? undefined : label,
7487
+ id: state.id,
7488
+ isMandatory,
7489
+ helperText,
7490
+ }),
7275
7491
  ]);
7276
7492
  },
7277
7493
  };
@@ -7490,59 +7706,65 @@ const Tabs = () => {
7490
7706
  };
7491
7707
  };
7492
7708
 
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();
7709
+ const SelectedChip = ({ option, onRemove, }) => m('.chip', [
7710
+ option.label || option.id.toString(),
7711
+ m(MaterialIcon, {
7712
+ name: 'close',
7713
+ className: 'close',
7714
+ onclick: (e) => {
7715
+ e.stopPropagation();
7716
+ onRemove(option.id);
7717
+ },
7718
+ }),
7719
+ ]);
7720
+ const DropdownOption = ({ option, index, optionId, selectedIds, isFocused, onToggle, onMouseOver, showCheckbox, }) => {
7721
+ const optionLabel = option.label || option.id.toString();
7722
+ return m('li', {
7723
+ id: optionId,
7724
+ role: 'option',
7725
+ 'aria-selected': selectedIds.includes(option.id) ? 'true' : 'false',
7726
+ class: `${option.disabled ? 'disabled' : ''} ${isFocused ? 'active' : ''}`.trim(),
7727
+ onmouseover: () => {
7728
+ if (!option.disabled) {
7729
+ onMouseOver(index);
7730
+ }
7731
+ },
7732
+ }, m('label', {
7733
+ class: 'search-select-option-label',
7734
+ onclick: (e) => {
7735
+ // A single-select row has no native checkbox to emit change.
7736
+ if (!showCheckbox) {
7737
+ e.preventDefault();
7738
+ onToggle(option);
7739
+ }
7740
+ },
7741
+ }, [
7742
+ showCheckbox &&
7743
+ m('input', {
7744
+ type: 'checkbox',
7745
+ checked: selectedIds.includes(option.id),
7746
+ disabled: option.disabled,
7747
+ onchange: (e) => {
7518
7748
  e.stopPropagation();
7519
7749
  onToggle(option);
7520
7750
  },
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
- };
7751
+ }),
7752
+ m('span', optionLabel),
7753
+ ]));
7538
7754
  };
7539
7755
  /**
7540
7756
  * Mithril Factory Component for Multi-Select Dropdown with search
7541
7757
  */
7542
- const SearchSelect = () => {
7758
+ const SearchSelect = (maybeVnode) => {
7759
+ var _a;
7760
+ const cachedInstance = (_a = maybeVnode === null || maybeVnode === void 0 ? void 0 : maybeVnode.state) === null || _a === void 0 ? void 0 : _a.__searchSelectInstance;
7761
+ if (cachedInstance) {
7762
+ return cachedInstance;
7763
+ }
7543
7764
  // State initialization
7544
7765
  const state = {
7545
7766
  id: '',
7767
+ listboxId: '',
7546
7768
  isOpen: false,
7547
7769
  searchTerm: '',
7548
7770
  inputRef: null,
@@ -7550,17 +7772,38 @@ const SearchSelect = () => {
7550
7772
  focusedIndex: -1,
7551
7773
  internalSelectedIds: [],
7552
7774
  createdOptions: [],
7553
- };
7775
+ asyncOptions: [],
7776
+ isLoading: false,
7777
+ loadError: null,
7778
+ latestRequestId: 0,
7779
+ };
7780
+ const updateAsyncState = (nextState) => {
7781
+ state.asyncOptions = nextState.options;
7782
+ state.isLoading = nextState.isLoading;
7783
+ state.loadError = nextState.error;
7784
+ state.latestRequestId = nextState.latestRequestId;
7785
+ };
7786
+ const readAsyncState = () => ({
7787
+ options: state.asyncOptions,
7788
+ isLoading: state.isLoading,
7789
+ error: state.loadError,
7790
+ latestRequestId: state.latestRequestId,
7791
+ });
7554
7792
  const isControlled = (attrs) => attrs.checkedId !== undefined && typeof attrs.onchange === 'function';
7555
7793
  const componentId = uniqueId();
7556
7794
  const searchInputId = `${componentId}-search`;
7557
7795
  // Handle click outside
7558
7796
  const handleClickOutside = (e) => {
7559
7797
  const target = e.target;
7798
+ const targetElement = e.target instanceof Element ? e.target : null;
7560
7799
  if (state.dropdownRef && state.dropdownRef.contains(target)) {
7561
7800
  // Click inside dropdown, do nothing
7562
7801
  return;
7563
7802
  }
7803
+ if (targetElement && targetElement.closest('.chips-container')) {
7804
+ // Click on trigger, do nothing
7805
+ return;
7806
+ }
7564
7807
  if (state.inputRef && state.inputRef.contains(target)) {
7565
7808
  // Click on trigger handled by onclick event
7566
7809
  return;
@@ -7571,40 +7814,51 @@ const SearchSelect = () => {
7571
7814
  }
7572
7815
  m.redraw();
7573
7816
  };
7574
- // Handle keyboard navigation
7575
- const handleKeyDown = (e, filteredOptions, showAddNew) => {
7576
- if (!state.isOpen)
7817
+ // Handle keyboard navigation through shared combobox primitive.
7818
+ const handleKeyDown = (e, optionCount, includeActionRow) => {
7819
+ const result = getComboboxKeyResult({
7820
+ key: e.key,
7821
+ isOpen: state.isOpen,
7822
+ focusedIndex: state.focusedIndex,
7823
+ optionCount,
7824
+ includeActionRow,
7825
+ });
7826
+ if (result.preventDefault) {
7827
+ e.preventDefault();
7828
+ }
7829
+ state.isOpen = result.isOpen;
7830
+ state.focusedIndex = result.focusedIndex;
7831
+ return result.action;
7832
+ };
7833
+ const loadAsyncOptions = async (attrs, query) => {
7834
+ if (!attrs.loadOptions) {
7577
7835
  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;
7836
+ }
7837
+ const started = startAsyncComboboxRequest(readAsyncState());
7838
+ updateAsyncState(started.nextState);
7839
+ m.redraw();
7840
+ try {
7841
+ const loadedOptions = await attrs.loadOptions(query);
7842
+ const resolved = resolveAsyncComboboxRequest(readAsyncState(), started.requestId, loadedOptions);
7843
+ updateAsyncState(resolved);
7844
+ if (started.requestId !== state.latestRequestId) {
7845
+ return;
7846
+ }
7847
+ if (state.focusedIndex >= loadedOptions.length) {
7604
7848
  state.focusedIndex = -1;
7605
- break;
7849
+ }
7850
+ }
7851
+ catch (error) {
7852
+ const rejected = rejectAsyncComboboxRequest(readAsyncState(), started.requestId, error instanceof Error ? error.message : 'Unable to load options');
7853
+ updateAsyncState(rejected);
7854
+ if (started.requestId !== state.latestRequestId) {
7855
+ return;
7856
+ }
7857
+ state.focusedIndex = -1;
7858
+ }
7859
+ finally {
7860
+ m.redraw();
7606
7861
  }
7607
- return null;
7608
7862
  };
7609
7863
  // Create new option and add to state
7610
7864
  const createAndSelectOption = async (attrs) => {
@@ -7685,9 +7939,10 @@ const SearchSelect = () => {
7685
7939
  attrs.onchange(newIds);
7686
7940
  }
7687
7941
  };
7688
- return {
7942
+ const componentInstance = {
7689
7943
  oninit: ({ attrs }) => {
7690
7944
  state.id = attrs.id || uniqueId();
7945
+ state.listboxId = `${state.id}-listbox`;
7691
7946
  // Initialize internal state for uncontrolled mode
7692
7947
  if (!isControlled(attrs)) {
7693
7948
  const defaultIds = attrs.defaultCheckedId !== undefined
@@ -7714,20 +7969,26 @@ const SearchSelect = () => {
7714
7969
  : [attrs.checkedId]
7715
7970
  : []
7716
7971
  : state.internalSelectedIds;
7717
- const { options = [], oncreateNewOption, className, placeholder, searchPlaceholder = 'Search options...', noOptionsFound = 'No options found', label, i18n = {}, maxDisplayedOptions, maxSelectedOptions, maxHeight, } = attrs;
7972
+ const { options = [], loadOptions, oncreateNewOption, className, placeholder, searchPlaceholder = 'Search options...', noOptionsFound = 'No options found', label, i18n = {}, maxDisplayedOptions, maxSelectedOptions, maxHeight, } = attrs;
7718
7973
  // Use i18n values if provided, otherwise use defaults
7719
7974
  const texts = {
7720
7975
  noOptionsFound: i18n.noOptionsFound || noOptionsFound,
7976
+ loadingOptions: i18n.loadingOptions || 'Loading options...',
7977
+ loadingError: i18n.loadingError || 'Unable to load options',
7721
7978
  addNewPrefix: i18n.addNewPrefix || '+',
7722
7979
  showingXofY: i18n.showingXofY || 'Showing {shown} of {total} options',
7723
7980
  maxSelectionsReached: i18n.maxSelectionsReached || 'Maximum {max} selections reached',
7724
7981
  };
7725
7982
  // Check if max selections is reached
7726
7983
  const isMaxSelectionsReached = maxSelectedOptions && selectedIds.length >= maxSelectedOptions;
7727
- // Merge provided options with internally created options
7728
- const allOptions = [...options, ...state.createdOptions];
7984
+ // In async mode, the active list is sourced remotely.
7985
+ const sourceOptions = loadOptions ? state.asyncOptions : options;
7986
+ // Merge active options with internally created options
7987
+ const allOptions = [...sourceOptions, ...state.createdOptions];
7988
+ // Keep selected label lookups stable across static, async, and created sets.
7989
+ const lookupOptions = [...options, ...state.asyncOptions, ...state.createdOptions];
7729
7990
  // Get selected options for display
7730
- const selectedOptionsUnsorted = allOptions.filter((opt) => selectedIds.includes(opt.id));
7991
+ const selectedOptionsUnsorted = lookupOptions.filter((opt) => selectedIds.includes(opt.id));
7731
7992
  const selectedOptions = sortOptions(selectedOptionsUnsorted, attrs.sortSelected);
7732
7993
  // Safely filter options
7733
7994
  const filteredOptions = allOptions.filter((option) => (option.label || option.id.toString()).toLowerCase().includes((state.searchTerm || '').toLowerCase()) &&
@@ -7740,6 +8001,14 @@ const SearchSelect = () => {
7740
8001
  const showAddNew = oncreateNewOption &&
7741
8002
  state.searchTerm &&
7742
8003
  !displayedOptions.some((o) => (o.label || o.id.toString()).toLowerCase() === state.searchTerm.toLowerCase());
8004
+ const activeDescendantId = state.isOpen && state.focusedIndex >= 0 && state.focusedIndex < displayedOptions.length
8005
+ ? getComboboxOptionId(state.id, state.focusedIndex)
8006
+ : undefined;
8007
+ const viewState = getComboboxViewState({
8008
+ isLoading: state.isLoading,
8009
+ error: state.loadError,
8010
+ optionCount: displayedOptions.length,
8011
+ });
7743
8012
  // Render the dropdown
7744
8013
  return m('.input-field.multi-select-dropdown', { className }, [
7745
8014
  m('.chips.chips-initial.chips-container', {
@@ -7750,14 +8019,34 @@ const SearchSelect = () => {
7750
8019
  // console.log('SearchSelect clicked', state.isOpen, e); // Debug log
7751
8020
  e.preventDefault();
7752
8021
  e.stopPropagation();
8022
+ const wasOpen = state.isOpen;
7753
8023
  state.isOpen = !state.isOpen;
8024
+ if (!wasOpen && state.isOpen && loadOptions) {
8025
+ void loadAsyncOptions(attrs, state.searchTerm);
8026
+ }
7754
8027
  // console.log('SearchSelect state changed to', state.isOpen); // Debug log
7755
8028
  },
7756
- class: 'chips chips-container',
8029
+ onkeydown: async (e) => {
8030
+ const action = handleKeyDown(e, displayedOptions.length, !!showAddNew);
8031
+ if (action === 'open' && loadOptions) {
8032
+ await loadAsyncOptions(attrs, state.searchTerm);
8033
+ return;
8034
+ }
8035
+ if (action === 'selectAction' && oncreateNewOption) {
8036
+ await createAndSelectOption(attrs);
8037
+ }
8038
+ if (action === 'selectFocused' && state.focusedIndex < displayedOptions.length) {
8039
+ toggleOption(displayedOptions[state.focusedIndex], attrs);
8040
+ }
8041
+ },
8042
+ class: 'chips chips-container mm-layout-row mm-layout-row--wrap mm-layout-row--align-end',
8043
+ role: 'combobox',
8044
+ tabindex: 0,
8045
+ 'aria-expanded': state.isOpen ? 'true' : 'false',
8046
+ 'aria-haspopup': 'listbox',
8047
+ 'aria-controls': state.isOpen ? state.listboxId : undefined,
8048
+ 'aria-activedescendant': activeDescendantId,
7757
8049
  style: {
7758
- display: 'flex',
7759
- alignItems: 'end',
7760
- flexWrap: 'wrap',
7761
8050
  cursor: 'pointer',
7762
8051
  position: 'relative',
7763
8052
  },
@@ -7770,30 +8059,41 @@ const SearchSelect = () => {
7770
8059
  value: selectedOptions.map((o) => o.label || o.id.toString()).join(', '),
7771
8060
  readonly: true,
7772
8061
  class: 'sr-only',
7773
- style: { position: 'absolute', left: '-9999px', opacity: 0 },
8062
+ style: {
8063
+ position: 'absolute',
8064
+ width: '1px',
8065
+ height: '1px',
8066
+ margin: '-1px',
8067
+ padding: 0,
8068
+ border: 0,
8069
+ overflow: 'hidden',
8070
+ clip: 'rect(0 0 0 0)',
8071
+ clipPath: 'inset(50%)',
8072
+ whiteSpace: 'nowrap',
8073
+ },
7774
8074
  }),
7775
8075
  // Selected Options (chips)
7776
- ...selectedOptions.map((option) => m(SelectedChip(), {
7777
- option,
8076
+ ...selectedOptions.map((option) => SelectedChip({
8077
+ option: option,
7778
8078
  onRemove: (id) => removeOption(id, attrs),
7779
8079
  })),
7780
8080
  // Placeholder when no options selected
7781
8081
  selectedOptions.length === 0 &&
7782
8082
  placeholder &&
7783
8083
  m('span.placeholder', {
8084
+ class: 'mm-layout-grow',
7784
8085
  style: {
7785
8086
  color: 'var(--mm-text-hint, #9e9e9e)',
7786
- flexGrow: 1,
7787
8087
  padding: '8px 0',
7788
8088
  },
7789
8089
  }, placeholder),
7790
8090
  // Spacer to push caret to the right
7791
- m('span.spacer', { style: { flexGrow: 1 } }),
8091
+ m('span.spacer.mm-layout-grow'),
7792
8092
  m(MaterialIcon, {
7793
8093
  name: 'caret',
7794
8094
  direction: state.isOpen ? 'up' : 'down',
7795
- class: 'caret',
7796
- style: { marginLeft: 'auto', cursor: 'pointer' },
8095
+ class: 'caret mm-layout-ml-auto',
8096
+ style: { cursor: 'pointer' },
7797
8097
  }),
7798
8098
  ]),
7799
8099
  // Label
@@ -7805,6 +8105,8 @@ const SearchSelect = () => {
7805
8105
  // Dropdown Menu
7806
8106
  state.isOpen &&
7807
8107
  m('ul.dropdown-content.select-dropdown', {
8108
+ id: state.listboxId,
8109
+ role: 'listbox',
7808
8110
  oncreate: ({ dom }) => {
7809
8111
  state.dropdownRef = dom;
7810
8112
  },
@@ -7829,23 +8131,39 @@ const SearchSelect = () => {
7829
8131
  oninput: (e) => {
7830
8132
  state.searchTerm = e.target.value;
7831
8133
  state.focusedIndex = -1; // Reset focus when typing
8134
+ if (loadOptions) {
8135
+ void loadAsyncOptions(attrs, state.searchTerm);
8136
+ }
7832
8137
  },
7833
8138
  onkeydown: async (e) => {
7834
- const result = handleKeyDown(e, displayedOptions, !!showAddNew);
7835
- if (result === 'addNew' && oncreateNewOption) {
8139
+ const action = handleKeyDown(e, displayedOptions.length, !!showAddNew);
8140
+ if (action === 'open' && loadOptions) {
8141
+ await loadAsyncOptions(attrs, state.searchTerm);
8142
+ }
8143
+ else if (action === 'selectAction' && oncreateNewOption) {
7836
8144
  await createAndSelectOption(attrs);
7837
8145
  }
7838
- else if (result === 'selectOption' && state.focusedIndex < displayedOptions.length) {
8146
+ else if (action === 'selectFocused' && state.focusedIndex < displayedOptions.length) {
7839
8147
  toggleOption(displayedOptions[state.focusedIndex], attrs);
7840
8148
  }
7841
8149
  },
7842
8150
  class: 'search-select-input',
8151
+ 'aria-autocomplete': 'list',
8152
+ 'aria-controls': state.listboxId,
7843
8153
  }),
7844
8154
  ]),
7845
- // No options found message or list of options
7846
- ...(displayedOptions.length === 0 && !showAddNew
7847
- ? [m('li.search-select-no-options', texts.noOptionsFound)]
8155
+ // Async loading status
8156
+ ...(viewState === 'loading'
8157
+ ? [m('li.search-select-loading-info', { role: 'status', 'aria-live': 'polite' }, texts.loadingOptions)]
8158
+ : []),
8159
+ // Async loading error
8160
+ ...(viewState === 'error' && state.loadError
8161
+ ? [
8162
+ m('li.search-select-error-info', { role: 'status', 'aria-live': 'assertive' }, `${texts.loadingError}: ${state.loadError}`),
8163
+ ]
7848
8164
  : []),
8165
+ // No options found message or list of options
8166
+ ...(viewState === 'empty' && !showAddNew ? [m('li.search-select-no-options', texts.noOptionsFound)] : []),
7849
8167
  // Truncation message
7850
8168
  ...(isTruncated
7851
8169
  ? [
@@ -7878,6 +8196,9 @@ const SearchSelect = () => {
7878
8196
  ...(showAddNew
7879
8197
  ? [
7880
8198
  m('li', {
8199
+ id: `${state.listboxId}-action`,
8200
+ role: 'option',
8201
+ 'aria-selected': 'false',
7881
8202
  onclick: async () => {
7882
8203
  await createAndSelectOption(attrs);
7883
8204
  },
@@ -7889,9 +8210,10 @@ const SearchSelect = () => {
7889
8210
  ]
7890
8211
  : []),
7891
8212
  // List of filtered options
7892
- ...displayedOptions.map((option, index) => m(DropdownOption(), {
8213
+ ...displayedOptions.map((option, index) => DropdownOption({
7893
8214
  option,
7894
8215
  index,
8216
+ optionId: getComboboxOptionId(state.id, index),
7895
8217
  selectedIds,
7896
8218
  isFocused: state.focusedIndex === index,
7897
8219
  onToggle: (opt) => toggleOption(opt, attrs),
@@ -7904,6 +8226,10 @@ const SearchSelect = () => {
7904
8226
  ]);
7905
8227
  },
7906
8228
  };
8229
+ if (maybeVnode === null || maybeVnode === void 0 ? void 0 : maybeVnode.state) {
8230
+ maybeVnode.state.__searchSelectInstance = componentInstance;
8231
+ }
8232
+ return componentInstance;
7907
8233
  };
7908
8234
 
7909
8235
  const defaultI18n$2 = {
@@ -9277,6 +9603,53 @@ const FileUpload = () => {
9277
9603
  };
9278
9604
  };
9279
9605
 
9606
+ /** A semantic native fieldset for related controls. */
9607
+ const Fieldset = () => ({
9608
+ view: ({ attrs, children }) => {
9609
+ const { legend, description, required, disabled, error, className } = attrs, params = __rest(attrs, ["legend", "description", "required", "disabled", "error", "className"]);
9610
+ const descriptionId = params.id ? `${params.id}-description` : undefined;
9611
+ const errorId = params.id ? `${params.id}-error` : undefined;
9612
+ const describedBy = [description && descriptionId, error && errorId].filter(Boolean).join(' ') || undefined;
9613
+ return m('fieldset.mm-fieldset', Object.assign(Object.assign({}, params), { className, disabled, 'aria-describedby': describedBy }), [
9614
+ m('legend.mm-fieldset__legend', [legend, required && m('span.mm-fieldset__required[aria-hidden=true]', ' *')]),
9615
+ description && m('p.mm-fieldset__description', { id: descriptionId }, description),
9616
+ children,
9617
+ error && m('p.mm-fieldset__error[role=alert]', { id: errorId }, error),
9618
+ ]);
9619
+ },
9620
+ });
9621
+ const focusErrorTarget = (fieldId) => {
9622
+ var _a;
9623
+ if (!fieldId || typeof document === 'undefined')
9624
+ return;
9625
+ (_a = document.getElementById(fieldId)) === null || _a === void 0 ? void 0 : _a.focus();
9626
+ };
9627
+ /** A visual form section with an optional accessible validation summary. */
9628
+ const FormSection = () => ({
9629
+ view: ({ attrs, children }) => {
9630
+ const { title, description, errors = [], summaryTitle = 'Please correct the following errors', className } = attrs, params = __rest(attrs, ["title", "description", "errors", "summaryTitle", "className"]);
9631
+ return m('section.mm-form-section', Object.assign(Object.assign({}, params), { className }), [
9632
+ (title || description) &&
9633
+ m('.mm-form-section__header', [
9634
+ title && m('h3.mm-form-section__title', title),
9635
+ description && m('p.mm-form-section__description', description),
9636
+ ]),
9637
+ errors.length > 0 &&
9638
+ m('div.mm-validation-summary[role=alert][aria-live=assertive]', [
9639
+ m('p.mm-validation-summary__title', summaryTitle),
9640
+ m('ul.mm-validation-summary__list', errors.map((error, index) => {
9641
+ var _a, _b, _c;
9642
+ const href = (_a = error.href) !== null && _a !== void 0 ? _a : (error.fieldId ? `#${error.fieldId}` : undefined);
9643
+ return m('li', { key: `${(_c = (_b = error.fieldId) !== null && _b !== void 0 ? _b : href) !== null && _c !== void 0 ? _c : 'error'}-${index}` }, href
9644
+ ? m('a', { href, onclick: () => focusErrorTarget(error.fieldId) }, error.message)
9645
+ : error.message);
9646
+ })),
9647
+ ]),
9648
+ children,
9649
+ ]);
9650
+ },
9651
+ });
9652
+
9280
9653
  // List of MaterialIcon SVG icons that are available
9281
9654
  const materialIconSvgNames = [
9282
9655
  'caret',
@@ -9299,6 +9672,7 @@ const materialIconSvgNames = [
9299
9672
  const renderIcon = (icon, style) => {
9300
9673
  if (!icon)
9301
9674
  return null;
9675
+ const objectStyle = typeof style === 'string' ? undefined : style;
9302
9676
  if (typeof icon === 'string') {
9303
9677
  // Check if this is a MaterialIcon SVG name
9304
9678
  if (materialIconSvgNames.includes(icon)) {
@@ -9315,7 +9689,7 @@ const renderIcon = (icon, style) => {
9315
9689
  // Image URL
9316
9690
  return m('img', {
9317
9691
  src: icon.content,
9318
- style: Object.assign(Object.assign({}, style), { width: '24px', height: '24px', objectFit: 'contain' }),
9692
+ style: Object.assign(Object.assign({}, objectStyle), { width: '24px', height: '24px', objectFit: 'contain' }),
9319
9693
  });
9320
9694
  }
9321
9695
  return null;
@@ -9326,8 +9700,10 @@ const renderIcon = (icon, style) => {
9326
9700
  const SidenavHeaderFooterItem = () => {
9327
9701
  return {
9328
9702
  view: ({ attrs }) => {
9329
- const { text, icon, onclick, href, className = '', _isExpanded = true, _position = 'left' } = attrs;
9703
+ const { text, icon, onclick, href, className = '', title, tooltip, tooltipWhenCollapsedOnly = true, _isExpanded = true, _position = 'left', } = attrs;
9330
9704
  const isRightAligned = _position === 'right';
9705
+ const tooltipText = title || tooltip || text;
9706
+ const shouldShowTooltip = tooltipWhenCollapsedOnly ? !_isExpanded : true;
9331
9707
  const handleClick = (e) => {
9332
9708
  if (onclick) {
9333
9709
  e.preventDefault();
@@ -9336,24 +9712,25 @@ const SidenavHeaderFooterItem = () => {
9336
9712
  };
9337
9713
  const content = isRightAligned
9338
9714
  ? [
9339
- _isExpanded &&
9340
- m('span.sidenav-item-text', { style: { flex: '1', 'text-align': 'left', 'margin-right': '8px' } }, text),
9715
+ _isExpanded && m('span.sidenav-item-text.mm-layout-grow.mm-layout-mr-8.left-align', text),
9341
9716
  renderIcon(icon, { 'min-width': '24px', width: '24px' }),
9342
9717
  ]
9343
9718
  : [
9344
9719
  renderIcon(icon, { 'min-width': '24px', width: '24px' }),
9345
- _isExpanded && m('span.sidenav-item-text', { style: { 'margin-left': '8px', flex: '1' } }, text),
9720
+ _isExpanded && m('span.sidenav-item-text.mm-layout-grow.mm-layout-ml-8', text),
9346
9721
  ];
9347
9722
  const linkStyle = {
9348
- display: 'flex',
9349
- 'align-items': 'center',
9350
9723
  padding: _isExpanded ? '12px 16px' : '12px 18px',
9351
9724
  'justify-content': _isExpanded ? (isRightAligned ? 'flex-end' : 'flex-start') : 'center',
9352
9725
  };
9726
+ const linkClass = 'mm-layout-row mm-layout-row--center';
9353
9727
  return m('li', { class: className }, m('a', {
9354
9728
  href: href || '#!',
9355
9729
  onclick: handleClick,
9730
+ class: linkClass,
9356
9731
  style: linkStyle,
9732
+ title: shouldShowTooltip ? tooltipText : undefined,
9733
+ 'aria-label': shouldShowTooltip ? tooltipText : undefined,
9357
9734
  }, content));
9358
9735
  },
9359
9736
  };
@@ -9454,6 +9831,7 @@ const Sidenav = () => {
9454
9831
  fixed ? 'sidenav-fixed' : '',
9455
9832
  mode === 'push' ? 'sidenav-push' : '',
9456
9833
  expandable && !isExpanded ? 'sidenav-collapsed' : '',
9834
+ 'mm-layout-stack',
9457
9835
  className,
9458
9836
  ]
9459
9837
  .filter(Boolean)
@@ -9463,8 +9841,6 @@ const Sidenav = () => {
9463
9841
  transform: isOpen ? 'translateX(0)' : position === 'left' ? 'translateX(-105%)' : 'translateX(105%)',
9464
9842
  'transition-duration': `${animationDuration}ms`,
9465
9843
  'transition-property': 'transform, width',
9466
- display: 'flex',
9467
- 'flex-direction': 'column',
9468
9844
  },
9469
9845
  }, [
9470
9846
  // Header content slot (rendered first, before hamburger)
@@ -9481,10 +9857,8 @@ const Sidenav = () => {
9481
9857
  // Hamburger toggle button (inside sidenav, at the top)
9482
9858
  showHamburger &&
9483
9859
  m('li.sidenav-hamburger-item', {
9860
+ class: `mm-layout-row mm-layout-row--center ${position === 'right' ? 'mm-layout-row--justify-end' : 'mm-layout-row--justify-start'}`,
9484
9861
  style: {
9485
- display: 'flex',
9486
- 'justify-content': position === 'right' ? 'flex-end' : 'flex-start',
9487
- 'align-items': 'center',
9488
9862
  padding: '12px 16px',
9489
9863
  cursor: 'pointer',
9490
9864
  'border-bottom': '1px solid rgba(0,0,0,0.1)',
@@ -9500,10 +9874,8 @@ const Sidenav = () => {
9500
9874
  // Expand/collapse toggle button (if expandable, right below hamburger)
9501
9875
  expandable &&
9502
9876
  m('li.sidenav-expand-toggle', {
9877
+ class: `mm-layout-row mm-layout-row--center ${position === 'right' ? 'mm-layout-row--justify-end' : 'mm-layout-row--justify-start'}`,
9503
9878
  style: {
9504
- display: 'flex',
9505
- 'justify-content': position === 'right' ? 'flex-end' : 'flex-start',
9506
- 'align-items': 'center',
9507
9879
  padding: '12px 16px',
9508
9880
  cursor: 'pointer',
9509
9881
  'border-bottom': '1px solid rgba(0,0,0,0.1)',
@@ -9576,7 +9948,7 @@ const NavbarSubItem = () => {
9576
9948
  const submenuContent = isRightAligned
9577
9949
  ? [
9578
9950
  // Right-aligned: text on left, icons on right
9579
- isExpanded && m('span', { style: { flex: '1', 'text-align': 'left' } }, text),
9951
+ isExpanded && m('span.mm-layout-grow.left-align', text),
9580
9952
  icon && isExpanded && renderIcon(icon, { 'font-size': '18px' }),
9581
9953
  indicatorIcon,
9582
9954
  ]
@@ -9584,18 +9956,22 @@ const NavbarSubItem = () => {
9584
9956
  // Left-aligned: indicator on left, text and icon on right
9585
9957
  indicatorIcon,
9586
9958
  icon && isExpanded && renderIcon(icon, { 'font-size': '18px', 'margin-left': indicatorIcon ? '8px' : '0' }),
9587
- isExpanded && m('span', { style: { 'margin-left': icon || indicatorIcon ? '8px' : '0' } }, text),
9959
+ isExpanded && m('span', { class: icon || indicatorIcon ? 'mm-layout-ml-8' : undefined }, text),
9588
9960
  ];
9589
9961
  return m('li.sidenav-subitem', {
9590
- class: selected ? 'selected' : '',
9962
+ class: [
9963
+ selected ? 'selected' : '',
9964
+ 'mm-layout-row',
9965
+ 'mm-layout-row--center',
9966
+ 'mm-layout-gap-sm',
9967
+ isRightAligned ? 'mm-layout-row--justify-between' : 'mm-layout-row--justify-start',
9968
+ ]
9969
+ .filter(Boolean)
9970
+ .join(' ') || undefined,
9591
9971
  style: {
9592
9972
  padding: isExpanded ? '0 16px 0 48px' : '0 16px',
9593
9973
  cursor: 'pointer',
9594
- display: 'flex',
9595
- 'align-items': 'center',
9596
- gap: '8px',
9597
9974
  'font-size': '0.9em',
9598
- 'justify-content': isRightAligned ? 'space-between' : 'flex-start',
9599
9975
  height: '48px',
9600
9976
  'min-height': '48px',
9601
9977
  },
@@ -9612,7 +9988,7 @@ const SidenavItem = () => {
9612
9988
  let isSubmenuOpen = false;
9613
9989
  return {
9614
9990
  view: ({ attrs, children }) => {
9615
- const { text, icon, active = false, disabled = false, onclick, href, className = '', divider = false, subheader = false, submenu = [], submenuMode = 'checkbox', } = attrs;
9991
+ const { text, icon, active = false, disabled = false, onclick, href, className = '', divider = false, subheader = false, submenu = [], submenuMode = 'checkbox', title, tooltip, tooltipWhenCollapsedOnly = true, } = attrs;
9616
9992
  if (divider) {
9617
9993
  return m('li.divider');
9618
9994
  }
@@ -9637,39 +10013,45 @@ const SidenavItem = () => {
9637
10013
  const isExpanded = attrs._isExpanded !== false;
9638
10014
  const position = attrs._position || 'left';
9639
10015
  const isRightAligned = position === 'right';
10016
+ const tooltipText = title || tooltip || text;
10017
+ const shouldShowTooltip = tooltipWhenCollapsedOnly ? !isExpanded : true;
9640
10018
  // In expanded mode, icons are at the outside edge
9641
10019
  // In collapsed mode, icons are centered
9642
10020
  const content = isRightAligned
9643
10021
  ? [
9644
10022
  // Right-aligned: text on left, icon on right
9645
- isExpanded &&
9646
- m('span.sidenav-item-text', { style: { flex: '1', 'text-align': 'left', 'margin-right': '8px' } }, text || children),
10023
+ isExpanded && m('span.sidenav-item-text.mm-layout-grow.mm-layout-mr-8.left-align', text || children),
9647
10024
  renderIcon(icon, { 'min-width': '24px', width: '24px' }),
9648
10025
  ]
9649
10026
  : [
9650
10027
  // Left-aligned: icon on left, text on right
9651
10028
  renderIcon(icon, { 'min-width': '24px', width: '24px' }),
9652
- isExpanded && m('span.sidenav-item-text', { style: { 'margin-left': '8px', flex: '1' } }, text || children),
10029
+ isExpanded && m('span.sidenav-item-text.mm-layout-grow.mm-layout-ml-8', text || children),
9653
10030
  ];
9654
10031
  const linkStyle = {
9655
- display: 'flex',
9656
- 'align-items': 'center',
9657
10032
  padding: isExpanded ? '12px 16px' : '12px 18px',
9658
10033
  'justify-content': isExpanded ? (isRightAligned ? 'flex-end' : 'flex-start') : 'center',
9659
10034
  };
10035
+ const linkClass = 'mm-layout-row mm-layout-row--center';
9660
10036
  const mainItem = href && !disabled
9661
10037
  ? m('li', { class: itemClasses }, [
9662
10038
  m('a', {
9663
10039
  href,
9664
10040
  onclick: handleMainClick,
10041
+ class: linkClass,
9665
10042
  style: linkStyle,
10043
+ title: shouldShowTooltip ? tooltipText : undefined,
10044
+ 'aria-label': shouldShowTooltip ? tooltipText : undefined,
9666
10045
  }, content),
9667
10046
  ])
9668
10047
  : m('li', { class: itemClasses }, [
9669
10048
  m('a', {
9670
10049
  onclick: handleMainClick,
9671
10050
  href: '#!',
10051
+ class: linkClass,
9672
10052
  style: linkStyle,
10053
+ title: shouldShowTooltip ? tooltipText : undefined,
10054
+ 'aria-label': shouldShowTooltip ? tooltipText : undefined,
9673
10055
  }, content),
9674
10056
  ]);
9675
10057
  // Return main item with submenu if applicable
@@ -10185,8 +10567,9 @@ const TreeNodeComponent = () => {
10185
10567
  const childIsFocused = ((_e = attrs.treeState) === null || _e === void 0 ? void 0 : _e.focusedNodeId) === child.id;
10186
10568
  // Calculate if this child is last in branch
10187
10569
  const childPath = [...(attrs.currentPath || []), childIndex];
10188
- const childIsLastInBranch = ((_f = attrs.treeAttrs) === null || _f === void 0 ? void 0 : _f.data) ?
10189
- isNodeLastInBranch(childPath, attrs.treeAttrs.data) : false;
10570
+ const childIsLastInBranch = ((_f = attrs.treeAttrs) === null || _f === void 0 ? void 0 : _f.data)
10571
+ ? isNodeLastInBranch(childPath, attrs.treeAttrs.data)
10572
+ : false;
10190
10573
  return m(TreeNodeComponent, {
10191
10574
  key: child.id,
10192
10575
  node: child,
@@ -10319,10 +10702,7 @@ const TreeView = () => {
10319
10702
  view: ({ attrs }) => {
10320
10703
  const { data, className, style, id, selectionMode = 'single', showConnectors = true } = attrs;
10321
10704
  return m('div.tree-view', {
10322
- class: [
10323
- className,
10324
- showConnectors && 'show-connectors'
10325
- ].filter(Boolean).join(' ') || undefined,
10705
+ class: [className, showConnectors && 'show-connectors'].filter(Boolean).join(' ') || undefined,
10326
10706
  style,
10327
10707
  id,
10328
10708
  role: selectionMode === 'multiple' ? 'listbox' : 'tree',
@@ -11020,6 +11400,7 @@ const Rating = () => {
11020
11400
  };
11021
11401
  };
11022
11402
 
11403
+ const MOBILE_LAYOUT_BREAKPOINT = 600;
11023
11404
  /** Create a LikertScale component */
11024
11405
  const LikertScale = () => {
11025
11406
  const state = {
@@ -11082,6 +11463,22 @@ const LikertScale = () => {
11082
11463
  return 'likert-scale--responsive';
11083
11464
  }
11084
11465
  };
11466
+ const isVerticalLayout = (layout) => {
11467
+ if (layout === 'vertical')
11468
+ return true;
11469
+ if (layout === 'horizontal')
11470
+ return false;
11471
+ return typeof window !== 'undefined' ? window.innerWidth <= MOBILE_LAYOUT_BREAKPOINT : false;
11472
+ };
11473
+ const getInlineAnchorLabel = (value, min, max, middleValue, startLabel, middleLabel, endLabel) => {
11474
+ if (value === min && startLabel)
11475
+ return startLabel;
11476
+ if (middleLabel && middleValue !== undefined && value === middleValue)
11477
+ return middleLabel;
11478
+ if (value === max && endLabel)
11479
+ return endLabel;
11480
+ return undefined;
11481
+ };
11085
11482
  const handleChange = (attrs, newValue) => {
11086
11483
  var _a;
11087
11484
  if (attrs.readonly || attrs.disabled)
@@ -11128,7 +11525,7 @@ const LikertScale = () => {
11128
11525
  const LikertScaleItem = () => {
11129
11526
  return {
11130
11527
  view: ({ attrs }) => {
11131
- const { value, currentValue, showNumber, showTooltip, tooltipLabel, groupId, name, disabled, readonly, onchange, } = attrs;
11528
+ const { value, currentValue, showNumber, showTooltip, tooltipLabel, groupId, name, disabled, readonly, anchorLabel, onchange, } = attrs;
11132
11529
  const radioId = `${groupId}-${value}`;
11133
11530
  const isChecked = currentValue === value;
11134
11531
  return m('.likert-scale__item.no-select', {
@@ -11155,6 +11552,7 @@ const LikertScale = () => {
11155
11552
  m('label.likert-scale__label', {
11156
11553
  for: radioId,
11157
11554
  }),
11555
+ anchorLabel && m('.likert-scale__item-anchor', anchorLabel),
11158
11556
  // Tooltip (optional)
11159
11557
  showTooltip && tooltipLabel && m('.likert-scale__tooltip', tooltipLabel),
11160
11558
  ]);
@@ -11178,6 +11576,8 @@ const LikertScale = () => {
11178
11576
  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"]);
11179
11577
  const currentValue = getCurrentValue(attrs);
11180
11578
  const itemCount = Math.floor((max - min) / step) + 1;
11579
+ const useInlineAnchors = isVerticalLayout(layout);
11580
+ const middleValue = middleLabel ? min + Math.floor((itemCount - 1) / 2) * step : undefined;
11181
11581
  // Generate scale values
11182
11582
  const scaleValues = Array.from({ length: itemCount }, (_, i) => min + i * step);
11183
11583
  return m('.likert-scale', {
@@ -11226,6 +11626,9 @@ const LikertScale = () => {
11226
11626
  showNumber: showNumbers,
11227
11627
  showTooltip: showTooltips,
11228
11628
  tooltipLabel: tooltipLabels === null || tooltipLabels === void 0 ? void 0 : tooltipLabels[value - min],
11629
+ anchorLabel: useInlineAnchors
11630
+ ? getInlineAnchorLabel(value, min, max, middleValue, startLabel, middleLabel, endLabel)
11631
+ : undefined,
11229
11632
  groupId: state.groupId,
11230
11633
  name,
11231
11634
  disabled,
@@ -11233,7 +11636,8 @@ const LikertScale = () => {
11233
11636
  onchange: (v) => handleChange(attrs, v),
11234
11637
  }))),
11235
11638
  // Scale anchors
11236
- (startLabel || middleLabel || endLabel) &&
11639
+ !useInlineAnchors &&
11640
+ (startLabel || middleLabel || endLabel) &&
11237
11641
  m('.likert-scale__anchors', [
11238
11642
  startLabel && m('.likert-scale__anchor.likert-scale__anchor--start', startLabel),
11239
11643
  middleLabel && m('.likert-scale__anchor.likert-scale__anchor--middle', middleLabel),
@@ -11418,6 +11822,9 @@ const CircularProgress = () => {
11418
11822
  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']);
11419
11823
  const isDeterminate = mode === 'determinate';
11420
11824
  const sizePixels = SIZE_MAP[size];
11825
+ const styleValue = typeof style === 'string'
11826
+ ? `width:${sizePixels}px;height:${sizePixels}px;${style}`
11827
+ : Object.assign({ width: `${sizePixels}px`, height: `${sizePixels}px` }, style);
11421
11828
  const { radius, circumference, strokeDashoffset, percentage } = isDeterminate
11422
11829
  ? calculateStrokeProperties(sizePixels, value, max)
11423
11830
  : { radius: 0, circumference: 0, strokeDashoffset: 0, percentage: 0 };
@@ -11444,7 +11851,7 @@ const CircularProgress = () => {
11444
11851
  : {
11445
11852
  'aria-valuetext': ariaValueText || label || 'Loading',
11446
11853
  };
11447
- 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), [
11854
+ 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), [
11448
11855
  // SVG circle
11449
11856
  m('svg.circular-progress__svg', {
11450
11857
  viewBox: `0 0 ${sizePixels} ${sizePixels}`,
@@ -11509,13 +11916,7 @@ const LinearProgress = () => {
11509
11916
  // Determine label content
11510
11917
  const labelContent = label !== undefined ? label : showPercentage && isDeterminate ? `${Math.round(percentage)}%` : '';
11511
11918
  // Build class names
11512
- const classNames = [
11513
- 'linear-progress',
11514
- getColorClass(color, colorIntensity),
11515
- className,
11516
- ]
11517
- .filter(Boolean)
11518
- .join(' ');
11919
+ const classNames = ['linear-progress', getColorClass(color, colorIntensity), className].filter(Boolean).join(' ');
11519
11920
  // ARIA attributes
11520
11921
  const ariaAttrs = isDeterminate
11521
11922
  ? {
@@ -11592,10 +11993,12 @@ exports.DigitalClock = DigitalClock;
11592
11993
  exports.DoubleRangeSlider = DoubleRangeSlider;
11593
11994
  exports.Dropdown = Dropdown;
11594
11995
  exports.EmailInput = EmailInput;
11996
+ exports.Fieldset = Fieldset;
11595
11997
  exports.FileInput = FileInput;
11596
11998
  exports.FileUpload = FileUpload;
11597
11999
  exports.FlatButton = FlatButton;
11598
12000
  exports.FloatingActionButton = FloatingActionButton;
12001
+ exports.FormSection = FormSection;
11599
12002
  exports.HelperText = HelperText;
11600
12003
  exports.Icon = Icon;
11601
12004
  exports.IconButton = IconButton;
@@ -11647,6 +12050,7 @@ exports.TimeRangePicker = TimeRangePicker;
11647
12050
  exports.Timeline = Timeline;
11648
12051
  exports.Toast = Toast;
11649
12052
  exports.ToastComponent = ToastComponent;
12053
+ exports.ToggleButton = ToggleButton;
11650
12054
  exports.ToggleGroup = ToggleGroup;
11651
12055
  exports.Tooltip = Tooltip;
11652
12056
  exports.TooltipComponent = TooltipComponent;
@@ -11655,10 +12059,14 @@ exports.UrlInput = UrlInput;
11655
12059
  exports.Wizard = Wizard;
11656
12060
  exports.addLeadingZero = addLeadingZero;
11657
12061
  exports.clearPortal = clearPortal;
12062
+ exports.createAsyncComboboxState = createAsyncComboboxState;
11658
12063
  exports.createBreadcrumb = createBreadcrumb;
11659
12064
  exports.formatTime = formatTime;
11660
12065
  exports.generateHourOptions = generateHourOptions;
11661
12066
  exports.generateMinuteOptions = generateMinuteOptions;
12067
+ exports.getComboboxKeyResult = getComboboxKeyResult;
12068
+ exports.getComboboxOptionId = getComboboxOptionId;
12069
+ exports.getComboboxViewState = getComboboxViewState;
11662
12070
  exports.getDropdownStyles = getDropdownStyles;
11663
12071
  exports.getPortalContainer = getPortalContainer;
11664
12072
  exports.initPushpins = initPushpins;
@@ -11667,15 +12075,23 @@ exports.isNumeric = isNumeric;
11667
12075
  exports.isTimeDisabled = isTimeDisabled;
11668
12076
  exports.isValidationError = isValidationError;
11669
12077
  exports.isValidationSuccess = isValidationSuccess;
12078
+ exports.normalizeSelection = normalizeSelection;
11670
12079
  exports.padLeft = padLeft;
11671
12080
  exports.parseTime = parseTime;
11672
12081
  exports.range = range;
12082
+ exports.rejectAsyncComboboxRequest = rejectAsyncComboboxRequest;
11673
12083
  exports.releasePortalContainer = releasePortalContainer;
12084
+ exports.renderFieldChrome = renderFieldChrome;
11674
12085
  exports.renderToPortal = renderToPortal;
12086
+ exports.resolveAsyncComboboxRequest = resolveAsyncComboboxRequest;
12087
+ exports.resolveControllableValue = resolveControllableValue;
11675
12088
  exports.scrollToValue = scrollToValue;
11676
12089
  exports.snapToNearestItem = snapToNearestItem;
11677
12090
  exports.sortOptions = sortOptions;
12091
+ exports.startAsyncComboboxRequest = startAsyncComboboxRequest;
12092
+ exports.syncPortalContent = syncPortalContent;
11678
12093
  exports.timeToMinutes = timeToMinutes;
11679
12094
  exports.toast = toast;
11680
12095
  exports.uniqueId = uniqueId;
11681
12096
  exports.uuid4 = uuid4;
12097
+ //# sourceMappingURL=index.js.map