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.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";
@@ -2377,6 +2549,7 @@ const DatePicker = () => {
2377
2549
  e.stopPropagation();
2378
2550
  gotoMonth(index);
2379
2551
  state.monthDropdownOpen = false;
2552
+ m.redraw();
2380
2553
  },
2381
2554
  }, monthName))),
2382
2555
  ]),
@@ -2401,6 +2574,7 @@ const DatePicker = () => {
2401
2574
  e.stopPropagation();
2402
2575
  gotoYear(i);
2403
2576
  state.yearDropdownOpen = false;
2577
+ m.redraw();
2404
2578
  },
2405
2579
  }, i))),
2406
2580
  ]),
@@ -3285,6 +3459,10 @@ const DoubleRangeSlider = {
3285
3459
  },
3286
3460
  };
3287
3461
 
3462
+ const isReadonly = (attrs) => {
3463
+ const legacyReadonly = attrs.readonly;
3464
+ return Boolean(attrs.readOnly || legacyReadonly);
3465
+ };
3288
3466
  /** Character counter component that tracks text length against maxLength */
3289
3467
  const CharacterCounter = () => {
3290
3468
  return {
@@ -3363,7 +3541,7 @@ const TextArea = () => {
3363
3541
  return {
3364
3542
  oninit: ({ attrs }) => {
3365
3543
  const controlled = isControlled(attrs);
3366
- const isNonInteractive = attrs.readonly || attrs.disabled;
3544
+ const isNonInteractive = isReadonly(attrs) || attrs.disabled;
3367
3545
  // Warn developer for improper controlled usage
3368
3546
  if (attrs.value !== undefined && !controlled && !isNonInteractive) {
3369
3547
  console.warn(`TextArea received 'value' prop without 'oninput' or 'onchange' handler. ` +
@@ -3380,7 +3558,7 @@ const TextArea = () => {
3380
3558
  var _a, _b, _c, _d;
3381
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"]);
3382
3560
  const controlled = isControlled(attrs);
3383
- const isNonInteractive = attrs.readonly || attrs.disabled;
3561
+ const isNonInteractive = isReadonly(attrs) || attrs.disabled;
3384
3562
  let currentValue;
3385
3563
  if (controlled) {
3386
3564
  currentValue = value || '';
@@ -3496,14 +3674,12 @@ const TextArea = () => {
3496
3674
  onkeypress(ev, ev.target.value);
3497
3675
  }
3498
3676
  : undefined })),
3499
- m(Label, {
3677
+ ...renderFieldChrome({
3500
3678
  label,
3501
3679
  id,
3502
3680
  isMandatory,
3503
3681
  isActive: currentValue || placeholder || state.active,
3504
3682
  initialValue: currentValue !== '',
3505
- }),
3506
- m(HelperText, {
3507
3683
  helperText,
3508
3684
  dataError: state.hasInteracted && attrs.dataError ? attrs.dataError : undefined,
3509
3685
  dataSuccess: state.hasInteracted && attrs.dataSuccess ? attrs.dataSuccess : undefined,
@@ -3583,7 +3759,7 @@ const InputField = (type, defaultClass = '') => () => {
3583
3759
  return {
3584
3760
  oninit: ({ attrs }) => {
3585
3761
  const controlled = isControlled(attrs);
3586
- const isNonInteractive = attrs.readonly || attrs.disabled;
3762
+ const isNonInteractive = isReadonly(attrs) || attrs.disabled;
3587
3763
  // Warn developer for improper controlled usage
3588
3764
  if (attrs.value !== undefined && !controlled && !isNonInteractive) {
3589
3765
  console.warn(`${type} input with label '${attrs.label}' received 'value' prop without 'oninput' handler. ` +
@@ -3623,7 +3799,7 @@ const InputField = (type, defaultClass = '') => () => {
3623
3799
  }
3624
3800
  const isNumeric = ['number', 'range'].includes(type);
3625
3801
  const controlled = isControlled(attrs);
3626
- const isNonInteractive = attrs.readonly || attrs.disabled;
3802
+ const isNonInteractive = isReadonly(attrs) || attrs.disabled;
3627
3803
  let value;
3628
3804
  if (controlled) {
3629
3805
  value = attrs.value;
@@ -3642,9 +3818,28 @@ const InputField = (type, defaultClass = '') => () => {
3642
3818
  const rangeType = type === 'range' && !attrs.minmax;
3643
3819
  // Only add validate class if input is interactive and validation is needed
3644
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
+ };
3645
3840
  return m('.input-field', { className: cn, style }, [
3646
3841
  iconName ? m('i.material-icons.prefix', iconName) : undefined,
3647
- 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,
3648
3843
  placeholder, value: controlled ? value : undefined, style: type === 'range' && attrs.vertical
3649
3844
  ? {
3650
3845
  height: attrs.height || '200px',
@@ -3726,7 +3921,7 @@ const InputField = (type, defaultClass = '') => () => {
3726
3921
  const target = e.target;
3727
3922
  state.hasInteracted = true;
3728
3923
  // Skip validation for readonly/disabled inputs
3729
- if (attrs.readonly || attrs.disabled) {
3924
+ if (isReadonly(attrs) || attrs.disabled) {
3730
3925
  // Call original onblur if provided
3731
3926
  if (attrs.onblur) {
3732
3927
  attrs.onblur(e);
@@ -3816,6 +4011,22 @@ const InputField = (type, defaultClass = '') => () => {
3816
4011
  onchange(getValue(state.inputElement));
3817
4012
  }
3818
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,
3819
4030
  // Clear button - only for text inputs with canClear enabled and has content
3820
4031
  canClear && type === 'text' && ((_f = state.inputElement) === null || _f === void 0 ? void 0 : _f.value)
3821
4032
  ? m(MaterialIcon, {
@@ -3828,14 +4039,12 @@ const InputField = (type, defaultClass = '') => () => {
3828
4039
  },
3829
4040
  })
3830
4041
  : undefined,
3831
- m(Label, {
4042
+ ...renderFieldChrome({
3832
4043
  label,
3833
4044
  id,
3834
4045
  isMandatory,
3835
4046
  isActive,
3836
4047
  initialValue: value !== undefined && value !== '',
3837
- }),
3838
- m(HelperText, {
3839
4048
  helperText,
3840
4049
  dataError: state.hasInteracted && !state.isValid ? dataError : undefined,
3841
4050
  dataSuccess: state.hasInteracted && state.isValid ? dataSuccess : undefined,
@@ -3935,7 +4144,7 @@ const InputCheckbox = () => {
3935
4144
  view: ({ attrs: { className = 'col s12', onchange, label, checked, disabled, description, style, inputId } }) => {
3936
4145
  if (!checkboxId)
3937
4146
  checkboxId = inputId || uniqueId();
3938
- 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' } }, [
3939
4148
  m('input[type=checkbox][tabindex=0]', {
3940
4149
  className: disabled ? 'disabled' : undefined,
3941
4150
  id: checkboxId,
@@ -4700,17 +4909,6 @@ const Dropdown = () => {
4700
4909
  const updatePortalDropdown = (items, selectedLabel, onSelectItem, maxHeight) => {
4701
4910
  if (!state.isInsideModal)
4702
4911
  return;
4703
- // Clean up existing portal
4704
- const existingPortal = document.getElementById(`${state.id}-dropdown`);
4705
- if (existingPortal) {
4706
- existingPortal.remove();
4707
- }
4708
- if (!state.isOpen || !state.inputRef)
4709
- return;
4710
- // Create portal element
4711
- const portalElement = document.createElement('div');
4712
- portalElement.id = `${state.id}-dropdown`;
4713
- document.body.appendChild(portalElement);
4714
4912
  // Create dropdown content
4715
4913
  const availableItems = items.filter((item) => !item.divider && !item.disabled);
4716
4914
  const dropdownContent = items.map((item) => {
@@ -4724,15 +4922,14 @@ const Dropdown = () => {
4724
4922
  class: `${isSelected ? 'selected' : ''} ${isFocused ? 'focused' : ''}${item.disabled ? ' disabled' : ''}`,
4725
4923
  onclick: item.disabled ? undefined : () => onSelectItem(item),
4726
4924
  }, m('span', {
4925
+ class: 'mm-layout-row mm-layout-row--center',
4727
4926
  style: {
4728
- display: 'flex',
4729
- alignItems: 'center',
4730
4927
  padding: '14px 16px',
4731
4928
  },
4732
4929
  }, [
4733
4930
  item.iconName
4734
4931
  ? m('i.material-icons', {
4735
- style: { marginRight: '32px' },
4932
+ class: 'mm-layout-item-icon',
4736
4933
  }, item.iconName)
4737
4934
  : undefined,
4738
4935
  item.label,
@@ -4749,8 +4946,12 @@ const Dropdown = () => {
4749
4946
  state.dropdownRef = null;
4750
4947
  },
4751
4948
  }, dropdownContent);
4752
- // Render to portal
4753
- m.render(portalElement, dropdownVnode);
4949
+ syncPortalContent({
4950
+ containerId: `${state.id}-dropdown`,
4951
+ shouldRender: state.isOpen && !!state.inputRef,
4952
+ vnode: dropdownVnode,
4953
+ zIndex: 10000,
4954
+ });
4754
4955
  };
4755
4956
  return {
4756
4957
  oninit: ({ attrs }) => {
@@ -4771,15 +4972,19 @@ const Dropdown = () => {
4771
4972
  // Cleanup global listener
4772
4973
  document.removeEventListener('click', closeDropdown);
4773
4974
  // Cleanup portal
4774
- const portalElement = document.getElementById(`${state.id}-dropdown`);
4775
- if (portalElement) {
4776
- portalElement.remove();
4777
- }
4975
+ syncPortalContent({ containerId: `${state.id}-dropdown`, shouldRender: false, vnode: null });
4778
4976
  },
4779
4977
  view: ({ attrs }) => {
4780
4978
  const { checkedId, key, label, onchange, disabled = false, items, iconName, helperText, style, className = 'col s12', } = attrs;
4781
4979
  const controlled = isControlled(attrs);
4782
- 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
+ });
4783
4988
  const handleSelection = (value) => {
4784
4989
  // Update internal state for uncontrolled mode
4785
4990
  if (!controlled) {
@@ -4811,6 +5016,7 @@ const Dropdown = () => {
4811
5016
  iconName ? m('i.material-icons.prefix', iconName) : undefined,
4812
5017
  m(HelperText, { helperText }),
4813
5018
  m('.select-wrapper', {
5019
+ class: 'mm-layout-row mm-layout-row--center',
4814
5020
  onkeydown: disabled
4815
5021
  ? undefined
4816
5022
  : (e) => {
@@ -4878,15 +5084,14 @@ const Dropdown = () => {
4878
5084
  handleSelection(value);
4879
5085
  },
4880
5086
  }, m('span', {
5087
+ class: 'mm-layout-row mm-layout-row--center',
4881
5088
  style: {
4882
- display: 'flex',
4883
- alignItems: 'center',
4884
5089
  padding: '14px 16px',
4885
5090
  },
4886
5091
  }, [
4887
5092
  item.iconName
4888
5093
  ? m('i.material-icons', {
4889
- style: { marginRight: '32px' },
5094
+ class: 'mm-layout-item-icon',
4890
5095
  }, item.iconName)
4891
5096
  : undefined,
4892
5097
  item.label,
@@ -6843,9 +7048,9 @@ const initPushpins = (selector = '.pushpin', options = {}) => {
6843
7048
  };
6844
7049
 
6845
7050
  const RadioButton = () => ({
6846
- 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 } }) => {
6847
7052
  const radioId = inputId || `${groupId}-${id}`;
6848
- return m('p', { className }, m('label', { for: radioId }, [
7053
+ return m('p', { className }, m('label', { for: radioId, style: { position: 'relative', display: 'inline-block' } }, [
6849
7054
  m('input[type=radio][tabindex=0]', {
6850
7055
  id: radioId,
6851
7056
  name: groupId,
@@ -6853,7 +7058,7 @@ const RadioButton = () => ({
6853
7058
  checked,
6854
7059
  onclick: onchange ? () => onchange(id) : undefined,
6855
7060
  }),
6856
- m('span', m.trust(label)),
7061
+ m('span', allowHtml ? m.trust(label) : label),
6857
7062
  ]));
6858
7063
  },
6859
7064
  });
@@ -6880,8 +7085,9 @@ const RadioButtons = () => {
6880
7085
  }
6881
7086
  },
6882
7087
  view: ({ attrs }) => {
6883
- var _a, _b;
6884
- 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';
6885
7091
  const { groupId, componentId } = state;
6886
7092
  const controlled = isControlled(attrs);
6887
7093
  // Get current checked ID from props or internal state
@@ -6891,11 +7097,11 @@ const RadioButtons = () => {
6891
7097
  }
6892
7098
  else if (disabled) {
6893
7099
  // Non-interactive components: prefer defaultCheckedId, fallback to checkedId
6894
- currentCheckedId = (_a = attrs.defaultCheckedId) !== null && _a !== void 0 ? _a : checkedId;
7100
+ currentCheckedId = (_b = attrs.defaultCheckedId) !== null && _b !== void 0 ? _b : checkedId;
6895
7101
  }
6896
7102
  else {
6897
7103
  // Interactive uncontrolled: use internal state
6898
- currentCheckedId = (_b = state.internalCheckedId) !== null && _b !== void 0 ? _b : attrs.defaultCheckedId;
7104
+ currentCheckedId = (_c = state.internalCheckedId) !== null && _c !== void 0 ? _c : attrs.defaultCheckedId;
6899
7105
  }
6900
7106
  const handleChange = (id) => {
6901
7107
  // Update internal state for uncontrolled mode
@@ -6910,16 +7116,16 @@ const RadioButtons = () => {
6910
7116
  const cn = [newRow ? 'clear' : '', className].filter(Boolean).join(' ').trim() || undefined;
6911
7117
  const radioItems = options.map((r) => ({
6912
7118
  component: (RadioButton),
6913
- 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 }),
6914
7120
  key: r.id,
6915
7121
  }));
6916
7122
  const optionsContent = m(OptionsList, {
6917
7123
  options: radioItems,
6918
- layout,
7124
+ layout: resolvedLayout,
6919
7125
  });
6920
7126
  return m('div', { id: componentId, className: cn }, [
6921
7127
  label && m('h5.form-group-label', label + (isMandatory ? ' *' : '')),
6922
- description && m('p.helper-text', m.trust(description)),
7128
+ description && m('p.helper-text', allowHtml ? m.trust(description) : description),
6923
7129
  m('form', { action: '#' }, optionsContent),
6924
7130
  ]);
6925
7131
  },
@@ -7056,6 +7262,26 @@ const Select = () => {
7056
7262
  opacity: 1,
7057
7263
  };
7058
7264
  };
7265
+ const formatSelectionSummary = (attrs, selectedOptions, 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 selectedOptions.length > 0 ? selectedOptions.map((o) => o.label || o.id).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
+ };
7059
7285
  const renderDropdownContent = (attrs, selectedIds, multiple, placeholder) => [
7060
7286
  placeholder && m('li.disabled', { tabindex: 0 }, m('span', placeholder)),
7061
7287
  // Render ungrouped options first
@@ -7124,17 +7350,6 @@ const Select = () => {
7124
7350
  const updatePortalDropdown = (attrs, selectedIds, multiple, placeholder) => {
7125
7351
  if (!state.isInsideModal)
7126
7352
  return;
7127
- // Clean up existing portal
7128
- const existingPortal = document.getElementById(state.dropdownId);
7129
- if (existingPortal) {
7130
- existingPortal.remove();
7131
- }
7132
- if (!state.isOpen || !state.inputRef)
7133
- return;
7134
- // Create portal element
7135
- const portalElement = document.createElement('div');
7136
- portalElement.id = state.dropdownId;
7137
- document.body.appendChild(portalElement);
7138
7353
  // Create dropdown with proper positioning
7139
7354
  const dropdownVnode = m('ul.dropdown-content.select-dropdown', {
7140
7355
  tabindex: 0,
@@ -7146,8 +7361,12 @@ const Select = () => {
7146
7361
  state.dropdownRef = null;
7147
7362
  },
7148
7363
  }, renderDropdownContent(attrs, selectedIds, multiple, placeholder));
7149
- // Render to portal
7150
- m.render(portalElement, dropdownVnode);
7364
+ syncPortalContent({
7365
+ containerId: state.dropdownId,
7366
+ shouldRender: state.isOpen && !!state.inputRef,
7367
+ vnode: dropdownVnode,
7368
+ zIndex: 10000,
7369
+ });
7151
7370
  };
7152
7371
  return {
7153
7372
  oninit: ({ attrs }) => {
@@ -7180,35 +7399,29 @@ const Select = () => {
7180
7399
  document.removeEventListener('click', closeDropdown);
7181
7400
  // Cleanup portaled dropdown if it exists
7182
7401
  if (state.isInsideModal && state.dropdownRef) {
7183
- const portalElement = document.getElementById(state.dropdownId);
7184
- if (portalElement && portalElement.parentNode) {
7185
- portalElement.parentNode.removeChild(portalElement);
7186
- }
7402
+ syncPortalContent({ containerId: state.dropdownId, shouldRender: false, vnode: null });
7187
7403
  }
7188
7404
  },
7189
7405
  view: ({ attrs }) => {
7190
- var _a;
7191
7406
  const controlled = isControlled(attrs);
7192
- 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;
7193
7408
  state.isMultiple = multiple;
7194
7409
  // Get selected IDs from props or internal state
7195
- let selectedIds;
7196
- if (controlled) {
7197
- selectedIds =
7198
- attrs.checkedId !== undefined ? (Array.isArray(attrs.checkedId) ? attrs.checkedId : [attrs.checkedId]) : [];
7199
- }
7200
- else if (disabled) {
7201
- // Non-interactive components: prefer defaultCheckedId, fallback to checkedId
7202
- const fallbackId = (_a = attrs.defaultCheckedId) !== null && _a !== void 0 ? _a : attrs.checkedId;
7203
- selectedIds = fallbackId !== undefined ? (Array.isArray(fallbackId) ? fallbackId : [fallbackId]) : [];
7204
- }
7205
- else {
7206
- // Interactive uncontrolled: use internal state
7207
- selectedIds = state.internalSelectedIds;
7208
- }
7209
- 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;
7210
7422
  const selectedOptionsUnsorted = options.filter((opt) => isSelected(opt.id, selectedIds));
7211
7423
  const selectedOptions = sortOptions(selectedOptionsUnsorted, attrs.sortSelected);
7424
+ const triggerValue = formatSelectionSummary(attrs, selectedOptions, selectedIds, placeholder);
7212
7425
  // Update portal dropdown when inside modal
7213
7426
  if (state.isInsideModal) {
7214
7427
  updatePortalDropdown(attrs, selectedIds, multiple, placeholder);
@@ -7228,9 +7441,15 @@ const Select = () => {
7228
7441
  'aria-controls': state.dropdownId,
7229
7442
  role: 'combobox',
7230
7443
  }, [
7444
+ shouldInlineLabel &&
7445
+ m('span.select-inline-label', [
7446
+ label,
7447
+ isMandatory && m('span.mandatory', { style: { marginLeft: '2px' } }, '*'),
7448
+ ]),
7231
7449
  m('input[type=text][readonly=true].select-dropdown.dropdown-trigger', {
7232
7450
  id: state.id,
7233
- value: selectedOptions.length > 0 ? selectedOptions.map((o) => o.label || o.id).join(', ') : placeholder,
7451
+ value: triggerValue,
7452
+ 'aria-label': label,
7234
7453
  oncreate: ({ dom }) => {
7235
7454
  state.inputRef = dom;
7236
7455
  },
@@ -7262,14 +7481,12 @@ const Select = () => {
7262
7481
  }),
7263
7482
  ]),
7264
7483
  // Label
7265
- label &&
7266
- m(Label, {
7267
- id: state.id,
7268
- label,
7269
- isMandatory,
7270
- }),
7271
- // Helper text
7272
- helperText && m(HelperText, { helperText }),
7484
+ ...renderFieldChrome({
7485
+ label: shouldInlineLabel ? undefined : label,
7486
+ id: state.id,
7487
+ isMandatory,
7488
+ helperText,
7489
+ }),
7273
7490
  ]);
7274
7491
  },
7275
7492
  };
@@ -7488,59 +7705,65 @@ const Tabs = () => {
7488
7705
  };
7489
7706
  };
7490
7707
 
7491
- // Proper components to avoid anonymous closures
7492
- const SelectedChip = () => {
7493
- return {
7494
- view: ({ attrs: { option, onRemove } }) => m('.chip', [
7495
- option.label || option.id.toString(),
7496
- m(MaterialIcon, {
7497
- name: 'close',
7498
- className: 'close',
7499
- onclick: (e) => {
7500
- e.stopPropagation();
7501
- onRemove(option.id);
7502
- },
7503
- }),
7504
- ]),
7505
- };
7506
- };
7507
- const DropdownOption = () => {
7508
- return {
7509
- view: ({ attrs: { option, index, selectedIds, isFocused, onToggle, onMouseOver, showCheckbox } }) => {
7510
- const checkboxId = `search-select-option-${option.id}`;
7511
- const optionLabel = option.label || option.id.toString();
7512
- return m('li', {
7513
- key: option.id,
7514
- onclick: (e) => {
7515
- e.preventDefault();
7708
+ const SelectedChip = ({ option, onRemove, }) => m('.chip', [
7709
+ option.label || option.id.toString(),
7710
+ m(MaterialIcon, {
7711
+ name: 'close',
7712
+ className: 'close',
7713
+ onclick: (e) => {
7714
+ e.stopPropagation();
7715
+ onRemove(option.id);
7716
+ },
7717
+ }),
7718
+ ]);
7719
+ const DropdownOption = ({ option, index, optionId, selectedIds, isFocused, onToggle, onMouseOver, showCheckbox, }) => {
7720
+ const optionLabel = option.label || option.id.toString();
7721
+ return m('li', {
7722
+ id: optionId,
7723
+ role: 'option',
7724
+ 'aria-selected': selectedIds.includes(option.id) ? 'true' : 'false',
7725
+ class: `${option.disabled ? 'disabled' : ''} ${isFocused ? 'active' : ''}`.trim(),
7726
+ onmouseover: () => {
7727
+ if (!option.disabled) {
7728
+ onMouseOver(index);
7729
+ }
7730
+ },
7731
+ }, m('label', {
7732
+ class: 'search-select-option-label',
7733
+ onclick: (e) => {
7734
+ // A single-select row has no native checkbox to emit change.
7735
+ if (!showCheckbox) {
7736
+ e.preventDefault();
7737
+ onToggle(option);
7738
+ }
7739
+ },
7740
+ }, [
7741
+ showCheckbox &&
7742
+ m('input', {
7743
+ type: 'checkbox',
7744
+ checked: selectedIds.includes(option.id),
7745
+ disabled: option.disabled,
7746
+ onchange: (e) => {
7516
7747
  e.stopPropagation();
7517
7748
  onToggle(option);
7518
7749
  },
7519
- class: `${option.disabled ? 'disabled' : ''} ${isFocused ? 'active' : ''}`.trim(),
7520
- onmouseover: () => {
7521
- if (!option.disabled) {
7522
- onMouseOver(index);
7523
- }
7524
- },
7525
- }, m('label', { for: checkboxId, class: 'search-select-option-label' }, [
7526
- showCheckbox &&
7527
- m('input', {
7528
- type: 'checkbox',
7529
- id: checkboxId,
7530
- checked: selectedIds.includes(option.id),
7531
- }),
7532
- m('span', optionLabel),
7533
- ]));
7534
- },
7535
- };
7750
+ }),
7751
+ m('span', optionLabel),
7752
+ ]));
7536
7753
  };
7537
7754
  /**
7538
7755
  * Mithril Factory Component for Multi-Select Dropdown with search
7539
7756
  */
7540
- const SearchSelect = () => {
7757
+ const SearchSelect = (maybeVnode) => {
7758
+ var _a;
7759
+ const cachedInstance = (_a = maybeVnode === null || maybeVnode === void 0 ? void 0 : maybeVnode.state) === null || _a === void 0 ? void 0 : _a.__searchSelectInstance;
7760
+ if (cachedInstance) {
7761
+ return cachedInstance;
7762
+ }
7541
7763
  // State initialization
7542
7764
  const state = {
7543
7765
  id: '',
7766
+ listboxId: '',
7544
7767
  isOpen: false,
7545
7768
  searchTerm: '',
7546
7769
  inputRef: null,
@@ -7548,17 +7771,38 @@ const SearchSelect = () => {
7548
7771
  focusedIndex: -1,
7549
7772
  internalSelectedIds: [],
7550
7773
  createdOptions: [],
7551
- };
7774
+ asyncOptions: [],
7775
+ isLoading: false,
7776
+ loadError: null,
7777
+ latestRequestId: 0,
7778
+ };
7779
+ const updateAsyncState = (nextState) => {
7780
+ state.asyncOptions = nextState.options;
7781
+ state.isLoading = nextState.isLoading;
7782
+ state.loadError = nextState.error;
7783
+ state.latestRequestId = nextState.latestRequestId;
7784
+ };
7785
+ const readAsyncState = () => ({
7786
+ options: state.asyncOptions,
7787
+ isLoading: state.isLoading,
7788
+ error: state.loadError,
7789
+ latestRequestId: state.latestRequestId,
7790
+ });
7552
7791
  const isControlled = (attrs) => attrs.checkedId !== undefined && typeof attrs.onchange === 'function';
7553
7792
  const componentId = uniqueId();
7554
7793
  const searchInputId = `${componentId}-search`;
7555
7794
  // Handle click outside
7556
7795
  const handleClickOutside = (e) => {
7557
7796
  const target = e.target;
7797
+ const targetElement = e.target instanceof Element ? e.target : null;
7558
7798
  if (state.dropdownRef && state.dropdownRef.contains(target)) {
7559
7799
  // Click inside dropdown, do nothing
7560
7800
  return;
7561
7801
  }
7802
+ if (targetElement && targetElement.closest('.chips-container')) {
7803
+ // Click on trigger, do nothing
7804
+ return;
7805
+ }
7562
7806
  if (state.inputRef && state.inputRef.contains(target)) {
7563
7807
  // Click on trigger handled by onclick event
7564
7808
  return;
@@ -7569,40 +7813,51 @@ const SearchSelect = () => {
7569
7813
  }
7570
7814
  m.redraw();
7571
7815
  };
7572
- // Handle keyboard navigation
7573
- const handleKeyDown = (e, filteredOptions, showAddNew) => {
7574
- if (!state.isOpen)
7816
+ // Handle keyboard navigation through shared combobox primitive.
7817
+ const handleKeyDown = (e, optionCount, includeActionRow) => {
7818
+ const result = getComboboxKeyResult({
7819
+ key: e.key,
7820
+ isOpen: state.isOpen,
7821
+ focusedIndex: state.focusedIndex,
7822
+ optionCount,
7823
+ includeActionRow,
7824
+ });
7825
+ if (result.preventDefault) {
7826
+ e.preventDefault();
7827
+ }
7828
+ state.isOpen = result.isOpen;
7829
+ state.focusedIndex = result.focusedIndex;
7830
+ return result.action;
7831
+ };
7832
+ const loadAsyncOptions = async (attrs, query) => {
7833
+ if (!attrs.loadOptions) {
7575
7834
  return;
7576
- const totalOptions = filteredOptions.length + (showAddNew ? 1 : 0);
7577
- switch (e.key) {
7578
- case 'ArrowDown':
7579
- e.preventDefault();
7580
- state.focusedIndex = Math.min(state.focusedIndex + 1, totalOptions - 1);
7581
- break;
7582
- case 'ArrowUp':
7583
- e.preventDefault();
7584
- state.focusedIndex = Math.max(state.focusedIndex - 1, -1);
7585
- break;
7586
- case 'Enter':
7587
- e.preventDefault();
7588
- if (state.focusedIndex >= 0) {
7589
- if (showAddNew && state.focusedIndex === filteredOptions.length) {
7590
- // Handle add new option
7591
- return 'addNew';
7592
- }
7593
- else if (state.focusedIndex < filteredOptions.length) {
7594
- // This will be handled in the view method where attrs are available
7595
- return 'selectOption';
7596
- }
7597
- }
7598
- break;
7599
- case 'Escape':
7600
- e.preventDefault();
7601
- state.isOpen = false;
7835
+ }
7836
+ const started = startAsyncComboboxRequest(readAsyncState());
7837
+ updateAsyncState(started.nextState);
7838
+ m.redraw();
7839
+ try {
7840
+ const loadedOptions = await attrs.loadOptions(query);
7841
+ const resolved = resolveAsyncComboboxRequest(readAsyncState(), started.requestId, loadedOptions);
7842
+ updateAsyncState(resolved);
7843
+ if (started.requestId !== state.latestRequestId) {
7844
+ return;
7845
+ }
7846
+ if (state.focusedIndex >= loadedOptions.length) {
7602
7847
  state.focusedIndex = -1;
7603
- break;
7848
+ }
7849
+ }
7850
+ catch (error) {
7851
+ const rejected = rejectAsyncComboboxRequest(readAsyncState(), started.requestId, error instanceof Error ? error.message : 'Unable to load options');
7852
+ updateAsyncState(rejected);
7853
+ if (started.requestId !== state.latestRequestId) {
7854
+ return;
7855
+ }
7856
+ state.focusedIndex = -1;
7857
+ }
7858
+ finally {
7859
+ m.redraw();
7604
7860
  }
7605
- return null;
7606
7861
  };
7607
7862
  // Create new option and add to state
7608
7863
  const createAndSelectOption = async (attrs) => {
@@ -7683,9 +7938,10 @@ const SearchSelect = () => {
7683
7938
  attrs.onchange(newIds);
7684
7939
  }
7685
7940
  };
7686
- return {
7941
+ const componentInstance = {
7687
7942
  oninit: ({ attrs }) => {
7688
7943
  state.id = attrs.id || uniqueId();
7944
+ state.listboxId = `${state.id}-listbox`;
7689
7945
  // Initialize internal state for uncontrolled mode
7690
7946
  if (!isControlled(attrs)) {
7691
7947
  const defaultIds = attrs.defaultCheckedId !== undefined
@@ -7712,20 +7968,26 @@ const SearchSelect = () => {
7712
7968
  : [attrs.checkedId]
7713
7969
  : []
7714
7970
  : state.internalSelectedIds;
7715
- const { options = [], oncreateNewOption, className, placeholder, searchPlaceholder = 'Search options...', noOptionsFound = 'No options found', label, i18n = {}, maxDisplayedOptions, maxSelectedOptions, maxHeight, } = attrs;
7971
+ const { options = [], loadOptions, oncreateNewOption, className, placeholder, searchPlaceholder = 'Search options...', noOptionsFound = 'No options found', label, i18n = {}, maxDisplayedOptions, maxSelectedOptions, maxHeight, } = attrs;
7716
7972
  // Use i18n values if provided, otherwise use defaults
7717
7973
  const texts = {
7718
7974
  noOptionsFound: i18n.noOptionsFound || noOptionsFound,
7975
+ loadingOptions: i18n.loadingOptions || 'Loading options...',
7976
+ loadingError: i18n.loadingError || 'Unable to load options',
7719
7977
  addNewPrefix: i18n.addNewPrefix || '+',
7720
7978
  showingXofY: i18n.showingXofY || 'Showing {shown} of {total} options',
7721
7979
  maxSelectionsReached: i18n.maxSelectionsReached || 'Maximum {max} selections reached',
7722
7980
  };
7723
7981
  // Check if max selections is reached
7724
7982
  const isMaxSelectionsReached = maxSelectedOptions && selectedIds.length >= maxSelectedOptions;
7725
- // Merge provided options with internally created options
7726
- const allOptions = [...options, ...state.createdOptions];
7983
+ // In async mode, the active list is sourced remotely.
7984
+ const sourceOptions = loadOptions ? state.asyncOptions : options;
7985
+ // Merge active options with internally created options
7986
+ const allOptions = [...sourceOptions, ...state.createdOptions];
7987
+ // Keep selected label lookups stable across static, async, and created sets.
7988
+ const lookupOptions = [...options, ...state.asyncOptions, ...state.createdOptions];
7727
7989
  // Get selected options for display
7728
- const selectedOptionsUnsorted = allOptions.filter((opt) => selectedIds.includes(opt.id));
7990
+ const selectedOptionsUnsorted = lookupOptions.filter((opt) => selectedIds.includes(opt.id));
7729
7991
  const selectedOptions = sortOptions(selectedOptionsUnsorted, attrs.sortSelected);
7730
7992
  // Safely filter options
7731
7993
  const filteredOptions = allOptions.filter((option) => (option.label || option.id.toString()).toLowerCase().includes((state.searchTerm || '').toLowerCase()) &&
@@ -7738,6 +8000,14 @@ const SearchSelect = () => {
7738
8000
  const showAddNew = oncreateNewOption &&
7739
8001
  state.searchTerm &&
7740
8002
  !displayedOptions.some((o) => (o.label || o.id.toString()).toLowerCase() === state.searchTerm.toLowerCase());
8003
+ const activeDescendantId = state.isOpen && state.focusedIndex >= 0 && state.focusedIndex < displayedOptions.length
8004
+ ? getComboboxOptionId(state.id, state.focusedIndex)
8005
+ : undefined;
8006
+ const viewState = getComboboxViewState({
8007
+ isLoading: state.isLoading,
8008
+ error: state.loadError,
8009
+ optionCount: displayedOptions.length,
8010
+ });
7741
8011
  // Render the dropdown
7742
8012
  return m('.input-field.multi-select-dropdown', { className }, [
7743
8013
  m('.chips.chips-initial.chips-container', {
@@ -7748,14 +8018,34 @@ const SearchSelect = () => {
7748
8018
  // console.log('SearchSelect clicked', state.isOpen, e); // Debug log
7749
8019
  e.preventDefault();
7750
8020
  e.stopPropagation();
8021
+ const wasOpen = state.isOpen;
7751
8022
  state.isOpen = !state.isOpen;
8023
+ if (!wasOpen && state.isOpen && loadOptions) {
8024
+ void loadAsyncOptions(attrs, state.searchTerm);
8025
+ }
7752
8026
  // console.log('SearchSelect state changed to', state.isOpen); // Debug log
7753
8027
  },
7754
- class: 'chips chips-container',
8028
+ onkeydown: async (e) => {
8029
+ const action = handleKeyDown(e, displayedOptions.length, !!showAddNew);
8030
+ if (action === 'open' && loadOptions) {
8031
+ await loadAsyncOptions(attrs, state.searchTerm);
8032
+ return;
8033
+ }
8034
+ if (action === 'selectAction' && oncreateNewOption) {
8035
+ await createAndSelectOption(attrs);
8036
+ }
8037
+ if (action === 'selectFocused' && state.focusedIndex < displayedOptions.length) {
8038
+ toggleOption(displayedOptions[state.focusedIndex], attrs);
8039
+ }
8040
+ },
8041
+ class: 'chips chips-container mm-layout-row mm-layout-row--wrap mm-layout-row--align-end',
8042
+ role: 'combobox',
8043
+ tabindex: 0,
8044
+ 'aria-expanded': state.isOpen ? 'true' : 'false',
8045
+ 'aria-haspopup': 'listbox',
8046
+ 'aria-controls': state.isOpen ? state.listboxId : undefined,
8047
+ 'aria-activedescendant': activeDescendantId,
7755
8048
  style: {
7756
- display: 'flex',
7757
- alignItems: 'end',
7758
- flexWrap: 'wrap',
7759
8049
  cursor: 'pointer',
7760
8050
  position: 'relative',
7761
8051
  },
@@ -7768,30 +8058,41 @@ const SearchSelect = () => {
7768
8058
  value: selectedOptions.map((o) => o.label || o.id.toString()).join(', '),
7769
8059
  readonly: true,
7770
8060
  class: 'sr-only',
7771
- style: { position: 'absolute', left: '-9999px', opacity: 0 },
8061
+ style: {
8062
+ position: 'absolute',
8063
+ width: '1px',
8064
+ height: '1px',
8065
+ margin: '-1px',
8066
+ padding: 0,
8067
+ border: 0,
8068
+ overflow: 'hidden',
8069
+ clip: 'rect(0 0 0 0)',
8070
+ clipPath: 'inset(50%)',
8071
+ whiteSpace: 'nowrap',
8072
+ },
7772
8073
  }),
7773
8074
  // Selected Options (chips)
7774
- ...selectedOptions.map((option) => m(SelectedChip(), {
7775
- option,
8075
+ ...selectedOptions.map((option) => SelectedChip({
8076
+ option: option,
7776
8077
  onRemove: (id) => removeOption(id, attrs),
7777
8078
  })),
7778
8079
  // Placeholder when no options selected
7779
8080
  selectedOptions.length === 0 &&
7780
8081
  placeholder &&
7781
8082
  m('span.placeholder', {
8083
+ class: 'mm-layout-grow',
7782
8084
  style: {
7783
8085
  color: 'var(--mm-text-hint, #9e9e9e)',
7784
- flexGrow: 1,
7785
8086
  padding: '8px 0',
7786
8087
  },
7787
8088
  }, placeholder),
7788
8089
  // Spacer to push caret to the right
7789
- m('span.spacer', { style: { flexGrow: 1 } }),
8090
+ m('span.spacer.mm-layout-grow'),
7790
8091
  m(MaterialIcon, {
7791
8092
  name: 'caret',
7792
8093
  direction: state.isOpen ? 'up' : 'down',
7793
- class: 'caret',
7794
- style: { marginLeft: 'auto', cursor: 'pointer' },
8094
+ class: 'caret mm-layout-ml-auto',
8095
+ style: { cursor: 'pointer' },
7795
8096
  }),
7796
8097
  ]),
7797
8098
  // Label
@@ -7803,6 +8104,8 @@ const SearchSelect = () => {
7803
8104
  // Dropdown Menu
7804
8105
  state.isOpen &&
7805
8106
  m('ul.dropdown-content.select-dropdown', {
8107
+ id: state.listboxId,
8108
+ role: 'listbox',
7806
8109
  oncreate: ({ dom }) => {
7807
8110
  state.dropdownRef = dom;
7808
8111
  },
@@ -7827,23 +8130,39 @@ const SearchSelect = () => {
7827
8130
  oninput: (e) => {
7828
8131
  state.searchTerm = e.target.value;
7829
8132
  state.focusedIndex = -1; // Reset focus when typing
8133
+ if (loadOptions) {
8134
+ void loadAsyncOptions(attrs, state.searchTerm);
8135
+ }
7830
8136
  },
7831
8137
  onkeydown: async (e) => {
7832
- const result = handleKeyDown(e, displayedOptions, !!showAddNew);
7833
- if (result === 'addNew' && oncreateNewOption) {
8138
+ const action = handleKeyDown(e, displayedOptions.length, !!showAddNew);
8139
+ if (action === 'open' && loadOptions) {
8140
+ await loadAsyncOptions(attrs, state.searchTerm);
8141
+ }
8142
+ else if (action === 'selectAction' && oncreateNewOption) {
7834
8143
  await createAndSelectOption(attrs);
7835
8144
  }
7836
- else if (result === 'selectOption' && state.focusedIndex < displayedOptions.length) {
8145
+ else if (action === 'selectFocused' && state.focusedIndex < displayedOptions.length) {
7837
8146
  toggleOption(displayedOptions[state.focusedIndex], attrs);
7838
8147
  }
7839
8148
  },
7840
8149
  class: 'search-select-input',
8150
+ 'aria-autocomplete': 'list',
8151
+ 'aria-controls': state.listboxId,
7841
8152
  }),
7842
8153
  ]),
7843
- // No options found message or list of options
7844
- ...(displayedOptions.length === 0 && !showAddNew
7845
- ? [m('li.search-select-no-options', texts.noOptionsFound)]
8154
+ // Async loading status
8155
+ ...(viewState === 'loading'
8156
+ ? [m('li.search-select-loading-info', { role: 'status', 'aria-live': 'polite' }, texts.loadingOptions)]
8157
+ : []),
8158
+ // Async loading error
8159
+ ...(viewState === 'error' && state.loadError
8160
+ ? [
8161
+ m('li.search-select-error-info', { role: 'status', 'aria-live': 'assertive' }, `${texts.loadingError}: ${state.loadError}`),
8162
+ ]
7846
8163
  : []),
8164
+ // No options found message or list of options
8165
+ ...(viewState === 'empty' && !showAddNew ? [m('li.search-select-no-options', texts.noOptionsFound)] : []),
7847
8166
  // Truncation message
7848
8167
  ...(isTruncated
7849
8168
  ? [
@@ -7876,6 +8195,9 @@ const SearchSelect = () => {
7876
8195
  ...(showAddNew
7877
8196
  ? [
7878
8197
  m('li', {
8198
+ id: `${state.listboxId}-action`,
8199
+ role: 'option',
8200
+ 'aria-selected': 'false',
7879
8201
  onclick: async () => {
7880
8202
  await createAndSelectOption(attrs);
7881
8203
  },
@@ -7887,9 +8209,10 @@ const SearchSelect = () => {
7887
8209
  ]
7888
8210
  : []),
7889
8211
  // List of filtered options
7890
- ...displayedOptions.map((option, index) => m(DropdownOption(), {
8212
+ ...displayedOptions.map((option, index) => DropdownOption({
7891
8213
  option,
7892
8214
  index,
8215
+ optionId: getComboboxOptionId(state.id, index),
7893
8216
  selectedIds,
7894
8217
  isFocused: state.focusedIndex === index,
7895
8218
  onToggle: (opt) => toggleOption(opt, attrs),
@@ -7902,6 +8225,10 @@ const SearchSelect = () => {
7902
8225
  ]);
7903
8226
  },
7904
8227
  };
8228
+ if (maybeVnode === null || maybeVnode === void 0 ? void 0 : maybeVnode.state) {
8229
+ maybeVnode.state.__searchSelectInstance = componentInstance;
8230
+ }
8231
+ return componentInstance;
7905
8232
  };
7906
8233
 
7907
8234
  const defaultI18n$2 = {
@@ -9275,6 +9602,53 @@ const FileUpload = () => {
9275
9602
  };
9276
9603
  };
9277
9604
 
9605
+ /** A semantic native fieldset for related controls. */
9606
+ const Fieldset = () => ({
9607
+ view: ({ attrs, children }) => {
9608
+ const { legend, description, required, disabled, error, className } = attrs, params = __rest(attrs, ["legend", "description", "required", "disabled", "error", "className"]);
9609
+ const descriptionId = params.id ? `${params.id}-description` : undefined;
9610
+ const errorId = params.id ? `${params.id}-error` : undefined;
9611
+ const describedBy = [description && descriptionId, error && errorId].filter(Boolean).join(' ') || undefined;
9612
+ return m('fieldset.mm-fieldset', Object.assign(Object.assign({}, params), { className, disabled, 'aria-describedby': describedBy }), [
9613
+ m('legend.mm-fieldset__legend', [legend, required && m('span.mm-fieldset__required[aria-hidden=true]', ' *')]),
9614
+ description && m('p.mm-fieldset__description', { id: descriptionId }, description),
9615
+ children,
9616
+ error && m('p.mm-fieldset__error[role=alert]', { id: errorId }, error),
9617
+ ]);
9618
+ },
9619
+ });
9620
+ const focusErrorTarget = (fieldId) => {
9621
+ var _a;
9622
+ if (!fieldId || typeof document === 'undefined')
9623
+ return;
9624
+ (_a = document.getElementById(fieldId)) === null || _a === void 0 ? void 0 : _a.focus();
9625
+ };
9626
+ /** A visual form section with an optional accessible validation summary. */
9627
+ const FormSection = () => ({
9628
+ view: ({ attrs, children }) => {
9629
+ const { title, description, errors = [], summaryTitle = 'Please correct the following errors', className } = attrs, params = __rest(attrs, ["title", "description", "errors", "summaryTitle", "className"]);
9630
+ return m('section.mm-form-section', Object.assign(Object.assign({}, params), { className }), [
9631
+ (title || description) &&
9632
+ m('.mm-form-section__header', [
9633
+ title && m('h3.mm-form-section__title', title),
9634
+ description && m('p.mm-form-section__description', description),
9635
+ ]),
9636
+ errors.length > 0 &&
9637
+ m('div.mm-validation-summary[role=alert][aria-live=assertive]', [
9638
+ m('p.mm-validation-summary__title', summaryTitle),
9639
+ m('ul.mm-validation-summary__list', errors.map((error, index) => {
9640
+ var _a, _b, _c;
9641
+ const href = (_a = error.href) !== null && _a !== void 0 ? _a : (error.fieldId ? `#${error.fieldId}` : undefined);
9642
+ return m('li', { key: `${(_c = (_b = error.fieldId) !== null && _b !== void 0 ? _b : href) !== null && _c !== void 0 ? _c : 'error'}-${index}` }, href
9643
+ ? m('a', { href, onclick: () => focusErrorTarget(error.fieldId) }, error.message)
9644
+ : error.message);
9645
+ })),
9646
+ ]),
9647
+ children,
9648
+ ]);
9649
+ },
9650
+ });
9651
+
9278
9652
  // List of MaterialIcon SVG icons that are available
9279
9653
  const materialIconSvgNames = [
9280
9654
  'caret',
@@ -9297,6 +9671,7 @@ const materialIconSvgNames = [
9297
9671
  const renderIcon = (icon, style) => {
9298
9672
  if (!icon)
9299
9673
  return null;
9674
+ const objectStyle = typeof style === 'string' ? undefined : style;
9300
9675
  if (typeof icon === 'string') {
9301
9676
  // Check if this is a MaterialIcon SVG name
9302
9677
  if (materialIconSvgNames.includes(icon)) {
@@ -9313,7 +9688,7 @@ const renderIcon = (icon, style) => {
9313
9688
  // Image URL
9314
9689
  return m('img', {
9315
9690
  src: icon.content,
9316
- style: Object.assign(Object.assign({}, style), { width: '24px', height: '24px', objectFit: 'contain' }),
9691
+ style: Object.assign(Object.assign({}, objectStyle), { width: '24px', height: '24px', objectFit: 'contain' }),
9317
9692
  });
9318
9693
  }
9319
9694
  return null;
@@ -9324,8 +9699,10 @@ const renderIcon = (icon, style) => {
9324
9699
  const SidenavHeaderFooterItem = () => {
9325
9700
  return {
9326
9701
  view: ({ attrs }) => {
9327
- const { text, icon, onclick, href, className = '', _isExpanded = true, _position = 'left' } = attrs;
9702
+ const { text, icon, onclick, href, className = '', title, tooltip, tooltipWhenCollapsedOnly = true, _isExpanded = true, _position = 'left', } = attrs;
9328
9703
  const isRightAligned = _position === 'right';
9704
+ const tooltipText = title || tooltip || text;
9705
+ const shouldShowTooltip = tooltipWhenCollapsedOnly ? !_isExpanded : true;
9329
9706
  const handleClick = (e) => {
9330
9707
  if (onclick) {
9331
9708
  e.preventDefault();
@@ -9334,24 +9711,25 @@ const SidenavHeaderFooterItem = () => {
9334
9711
  };
9335
9712
  const content = isRightAligned
9336
9713
  ? [
9337
- _isExpanded &&
9338
- m('span.sidenav-item-text', { style: { flex: '1', 'text-align': 'left', 'margin-right': '8px' } }, text),
9714
+ _isExpanded && m('span.sidenav-item-text.mm-layout-grow.mm-layout-mr-8.left-align', text),
9339
9715
  renderIcon(icon, { 'min-width': '24px', width: '24px' }),
9340
9716
  ]
9341
9717
  : [
9342
9718
  renderIcon(icon, { 'min-width': '24px', width: '24px' }),
9343
- _isExpanded && m('span.sidenav-item-text', { style: { 'margin-left': '8px', flex: '1' } }, text),
9719
+ _isExpanded && m('span.sidenav-item-text.mm-layout-grow.mm-layout-ml-8', text),
9344
9720
  ];
9345
9721
  const linkStyle = {
9346
- display: 'flex',
9347
- 'align-items': 'center',
9348
9722
  padding: _isExpanded ? '12px 16px' : '12px 18px',
9349
9723
  'justify-content': _isExpanded ? (isRightAligned ? 'flex-end' : 'flex-start') : 'center',
9350
9724
  };
9725
+ const linkClass = 'mm-layout-row mm-layout-row--center';
9351
9726
  return m('li', { class: className }, m('a', {
9352
9727
  href: href || '#!',
9353
9728
  onclick: handleClick,
9729
+ class: linkClass,
9354
9730
  style: linkStyle,
9731
+ title: shouldShowTooltip ? tooltipText : undefined,
9732
+ 'aria-label': shouldShowTooltip ? tooltipText : undefined,
9355
9733
  }, content));
9356
9734
  },
9357
9735
  };
@@ -9373,9 +9751,9 @@ const Sidenav = () => {
9373
9751
  m.redraw();
9374
9752
  }
9375
9753
  };
9376
- const setBodyOverflow = (isOpen, mode) => {
9754
+ const setBodyOverflow = (isOpen, mode, fixed) => {
9377
9755
  if (typeof document !== 'undefined') {
9378
- document.body.style.overflow = isOpen && mode === 'overlay' ? 'hidden' : '';
9756
+ document.body.style.overflow = isOpen && mode === 'overlay' && !fixed ? 'hidden' : '';
9379
9757
  }
9380
9758
  };
9381
9759
  const toggleExpanded = (attrs) => {
@@ -9411,7 +9789,7 @@ const Sidenav = () => {
9411
9789
  if (wasOpen !== isOpen) {
9412
9790
  state.isOpen = isOpen;
9413
9791
  state.isAnimating = true;
9414
- setBodyOverflow(isOpen, attrs.mode || 'overlay');
9792
+ setBodyOverflow(isOpen, attrs.mode || 'overlay', attrs.fixed || false);
9415
9793
  // Clear animation state after animation completes
9416
9794
  setTimeout(() => {
9417
9795
  state.isAnimating = false;
@@ -9421,7 +9799,7 @@ const Sidenav = () => {
9421
9799
  },
9422
9800
  onremove: ({ attrs }) => {
9423
9801
  // Clean up
9424
- setBodyOverflow(false, attrs.mode || 'overlay');
9802
+ setBodyOverflow(false, attrs.mode || 'overlay', attrs.fixed || false);
9425
9803
  if (typeof document !== 'undefined' && attrs.closeOnEscape !== false) {
9426
9804
  document.removeEventListener('keydown', (e) => handleEscapeKey(e, attrs));
9427
9805
  }
@@ -9450,7 +9828,9 @@ const Sidenav = () => {
9450
9828
  class: [
9451
9829
  position === 'right' ? 'right-aligned' : '',
9452
9830
  fixed ? 'sidenav-fixed' : '',
9831
+ mode === 'push' ? 'sidenav-push' : '',
9453
9832
  expandable && !isExpanded ? 'sidenav-collapsed' : '',
9833
+ 'mm-layout-stack',
9454
9834
  className,
9455
9835
  ]
9456
9836
  .filter(Boolean)
@@ -9462,13 +9842,22 @@ const Sidenav = () => {
9462
9842
  'transition-property': 'transform, width',
9463
9843
  },
9464
9844
  }, [
9845
+ // Header content slot (rendered first, before hamburger)
9846
+ attrs.headerContent &&
9847
+ m('li.sidenav-header-slot', {
9848
+ style: {
9849
+ 'flex-shrink': 0,
9850
+ 'list-style': 'none',
9851
+ height: 'auto',
9852
+ 'line-height': 'normal',
9853
+ padding: 0,
9854
+ },
9855
+ }, attrs.headerContent),
9465
9856
  // Hamburger toggle button (inside sidenav, at the top)
9466
9857
  showHamburger &&
9467
9858
  m('li.sidenav-hamburger-item', {
9859
+ class: `mm-layout-row mm-layout-row--center ${position === 'right' ? 'mm-layout-row--justify-end' : 'mm-layout-row--justify-start'}`,
9468
9860
  style: {
9469
- display: 'flex',
9470
- 'justify-content': position === 'right' ? 'flex-end' : 'flex-start',
9471
- 'align-items': 'center',
9472
9861
  padding: '12px 16px',
9473
9862
  cursor: 'pointer',
9474
9863
  'border-bottom': '1px solid rgba(0,0,0,0.1)',
@@ -9484,10 +9873,8 @@ const Sidenav = () => {
9484
9873
  // Expand/collapse toggle button (if expandable, right below hamburger)
9485
9874
  expandable &&
9486
9875
  m('li.sidenav-expand-toggle', {
9876
+ class: `mm-layout-row mm-layout-row--center ${position === 'right' ? 'mm-layout-row--justify-end' : 'mm-layout-row--justify-start'}`,
9487
9877
  style: {
9488
- display: 'flex',
9489
- 'justify-content': position === 'right' ? 'flex-end' : 'flex-start',
9490
- 'align-items': 'center',
9491
9878
  padding: '12px 16px',
9492
9879
  cursor: 'pointer',
9493
9880
  'border-bottom': '1px solid rgba(0,0,0,0.1)',
@@ -9515,7 +9902,19 @@ const Sidenav = () => {
9515
9902
  : children,
9516
9903
  // Footer item (if provided, appears at the bottom)
9517
9904
  attrs.footer &&
9518
- m(SidenavHeaderFooterItem, Object.assign(Object.assign({}, attrs.footer), { _isExpanded: isExpanded, _position: position, className: 'sidenav-footer-item' })),
9905
+ m(SidenavHeaderFooterItem, Object.assign(Object.assign({}, attrs.footer), { _isExpanded: isExpanded, _position: position, className: ['sidenav-footer-item', attrs.footer.className].filter(Boolean).join(' ') })),
9906
+ // Footer content slot (rendered last, pushed to bottom via margin-top: auto)
9907
+ attrs.footerContent &&
9908
+ m('li.sidenav-footer-slot', {
9909
+ style: {
9910
+ 'margin-top': 'auto',
9911
+ 'flex-shrink': 0,
9912
+ 'list-style': 'none',
9913
+ height: 'auto',
9914
+ 'line-height': 'normal',
9915
+ padding: 0,
9916
+ },
9917
+ }, attrs.footerContent),
9519
9918
  ]),
9520
9919
  ];
9521
9920
  },
@@ -9548,7 +9947,7 @@ const NavbarSubItem = () => {
9548
9947
  const submenuContent = isRightAligned
9549
9948
  ? [
9550
9949
  // Right-aligned: text on left, icons on right
9551
- isExpanded && m('span', { style: { flex: '1', 'text-align': 'left' } }, text),
9950
+ isExpanded && m('span.mm-layout-grow.left-align', text),
9552
9951
  icon && isExpanded && renderIcon(icon, { 'font-size': '18px' }),
9553
9952
  indicatorIcon,
9554
9953
  ]
@@ -9556,18 +9955,22 @@ const NavbarSubItem = () => {
9556
9955
  // Left-aligned: indicator on left, text and icon on right
9557
9956
  indicatorIcon,
9558
9957
  icon && isExpanded && renderIcon(icon, { 'font-size': '18px', 'margin-left': indicatorIcon ? '8px' : '0' }),
9559
- isExpanded && m('span', { style: { 'margin-left': icon || indicatorIcon ? '8px' : '0' } }, text),
9958
+ isExpanded && m('span', { class: icon || indicatorIcon ? 'mm-layout-ml-8' : undefined }, text),
9560
9959
  ];
9561
9960
  return m('li.sidenav-subitem', {
9562
- class: selected ? 'selected' : '',
9961
+ class: [
9962
+ selected ? 'selected' : '',
9963
+ 'mm-layout-row',
9964
+ 'mm-layout-row--center',
9965
+ 'mm-layout-gap-sm',
9966
+ isRightAligned ? 'mm-layout-row--justify-between' : 'mm-layout-row--justify-start',
9967
+ ]
9968
+ .filter(Boolean)
9969
+ .join(' ') || undefined,
9563
9970
  style: {
9564
9971
  padding: isExpanded ? '0 16px 0 48px' : '0 16px',
9565
9972
  cursor: 'pointer',
9566
- display: 'flex',
9567
- 'align-items': 'center',
9568
- gap: '8px',
9569
9973
  'font-size': '0.9em',
9570
- 'justify-content': isRightAligned ? 'space-between' : 'flex-start',
9571
9974
  height: '48px',
9572
9975
  'min-height': '48px',
9573
9976
  },
@@ -9584,7 +9987,7 @@ const SidenavItem = () => {
9584
9987
  let isSubmenuOpen = false;
9585
9988
  return {
9586
9989
  view: ({ attrs, children }) => {
9587
- const { text, icon, active = false, disabled = false, onclick, href, className = '', divider = false, subheader = false, submenu = [], submenuMode = 'checkbox', } = attrs;
9990
+ const { text, icon, active = false, disabled = false, onclick, href, className = '', divider = false, subheader = false, submenu = [], submenuMode = 'checkbox', title, tooltip, tooltipWhenCollapsedOnly = true, } = attrs;
9588
9991
  if (divider) {
9589
9992
  return m('li.divider');
9590
9993
  }
@@ -9609,39 +10012,45 @@ const SidenavItem = () => {
9609
10012
  const isExpanded = attrs._isExpanded !== false;
9610
10013
  const position = attrs._position || 'left';
9611
10014
  const isRightAligned = position === 'right';
10015
+ const tooltipText = title || tooltip || text;
10016
+ const shouldShowTooltip = tooltipWhenCollapsedOnly ? !isExpanded : true;
9612
10017
  // In expanded mode, icons are at the outside edge
9613
10018
  // In collapsed mode, icons are centered
9614
10019
  const content = isRightAligned
9615
10020
  ? [
9616
10021
  // Right-aligned: text on left, icon on right
9617
- isExpanded &&
9618
- m('span.sidenav-item-text', { style: { flex: '1', 'text-align': 'left', 'margin-right': '8px' } }, text || children),
10022
+ isExpanded && m('span.sidenav-item-text.mm-layout-grow.mm-layout-mr-8.left-align', text || children),
9619
10023
  renderIcon(icon, { 'min-width': '24px', width: '24px' }),
9620
10024
  ]
9621
10025
  : [
9622
10026
  // Left-aligned: icon on left, text on right
9623
10027
  renderIcon(icon, { 'min-width': '24px', width: '24px' }),
9624
- isExpanded && m('span.sidenav-item-text', { style: { 'margin-left': '8px', flex: '1' } }, text || children),
10028
+ isExpanded && m('span.sidenav-item-text.mm-layout-grow.mm-layout-ml-8', text || children),
9625
10029
  ];
9626
10030
  const linkStyle = {
9627
- display: 'flex',
9628
- 'align-items': 'center',
9629
10031
  padding: isExpanded ? '12px 16px' : '12px 18px',
9630
10032
  'justify-content': isExpanded ? (isRightAligned ? 'flex-end' : 'flex-start') : 'center',
9631
10033
  };
10034
+ const linkClass = 'mm-layout-row mm-layout-row--center';
9632
10035
  const mainItem = href && !disabled
9633
10036
  ? m('li', { class: itemClasses }, [
9634
10037
  m('a', {
9635
10038
  href,
9636
10039
  onclick: handleMainClick,
10040
+ class: linkClass,
9637
10041
  style: linkStyle,
10042
+ title: shouldShowTooltip ? tooltipText : undefined,
10043
+ 'aria-label': shouldShowTooltip ? tooltipText : undefined,
9638
10044
  }, content),
9639
10045
  ])
9640
10046
  : m('li', { class: itemClasses }, [
9641
10047
  m('a', {
9642
10048
  onclick: handleMainClick,
9643
10049
  href: '#!',
10050
+ class: linkClass,
9644
10051
  style: linkStyle,
10052
+ title: shouldShowTooltip ? tooltipText : undefined,
10053
+ 'aria-label': shouldShowTooltip ? tooltipText : undefined,
9645
10054
  }, content),
9646
10055
  ]);
9647
10056
  // Return main item with submenu if applicable
@@ -10157,8 +10566,9 @@ const TreeNodeComponent = () => {
10157
10566
  const childIsFocused = ((_e = attrs.treeState) === null || _e === void 0 ? void 0 : _e.focusedNodeId) === child.id;
10158
10567
  // Calculate if this child is last in branch
10159
10568
  const childPath = [...(attrs.currentPath || []), childIndex];
10160
- const childIsLastInBranch = ((_f = attrs.treeAttrs) === null || _f === void 0 ? void 0 : _f.data) ?
10161
- isNodeLastInBranch(childPath, attrs.treeAttrs.data) : false;
10569
+ const childIsLastInBranch = ((_f = attrs.treeAttrs) === null || _f === void 0 ? void 0 : _f.data)
10570
+ ? isNodeLastInBranch(childPath, attrs.treeAttrs.data)
10571
+ : false;
10162
10572
  return m(TreeNodeComponent, {
10163
10573
  key: child.id,
10164
10574
  node: child,
@@ -10291,10 +10701,7 @@ const TreeView = () => {
10291
10701
  view: ({ attrs }) => {
10292
10702
  const { data, className, style, id, selectionMode = 'single', showConnectors = true } = attrs;
10293
10703
  return m('div.tree-view', {
10294
- class: [
10295
- className,
10296
- showConnectors && 'show-connectors'
10297
- ].filter(Boolean).join(' ') || undefined,
10704
+ class: [className, showConnectors && 'show-connectors'].filter(Boolean).join(' ') || undefined,
10298
10705
  style,
10299
10706
  id,
10300
10707
  role: selectionMode === 'multiple' ? 'listbox' : 'tree',
@@ -10992,6 +11399,7 @@ const Rating = () => {
10992
11399
  };
10993
11400
  };
10994
11401
 
11402
+ const MOBILE_LAYOUT_BREAKPOINT = 600;
10995
11403
  /** Create a LikertScale component */
10996
11404
  const LikertScale = () => {
10997
11405
  const state = {
@@ -11054,6 +11462,22 @@ const LikertScale = () => {
11054
11462
  return 'likert-scale--responsive';
11055
11463
  }
11056
11464
  };
11465
+ const isVerticalLayout = (layout) => {
11466
+ if (layout === 'vertical')
11467
+ return true;
11468
+ if (layout === 'horizontal')
11469
+ return false;
11470
+ return typeof window !== 'undefined' ? window.innerWidth <= MOBILE_LAYOUT_BREAKPOINT : false;
11471
+ };
11472
+ const getInlineAnchorLabel = (value, min, max, middleValue, startLabel, middleLabel, endLabel) => {
11473
+ if (value === min && startLabel)
11474
+ return startLabel;
11475
+ if (middleLabel && middleValue !== undefined && value === middleValue)
11476
+ return middleLabel;
11477
+ if (value === max && endLabel)
11478
+ return endLabel;
11479
+ return undefined;
11480
+ };
11057
11481
  const handleChange = (attrs, newValue) => {
11058
11482
  var _a;
11059
11483
  if (attrs.readonly || attrs.disabled)
@@ -11100,7 +11524,7 @@ const LikertScale = () => {
11100
11524
  const LikertScaleItem = () => {
11101
11525
  return {
11102
11526
  view: ({ attrs }) => {
11103
- const { value, currentValue, showNumber, showTooltip, tooltipLabel, groupId, name, disabled, readonly, onchange, } = attrs;
11527
+ const { value, currentValue, showNumber, showTooltip, tooltipLabel, groupId, name, disabled, readonly, anchorLabel, onchange, } = attrs;
11104
11528
  const radioId = `${groupId}-${value}`;
11105
11529
  const isChecked = currentValue === value;
11106
11530
  return m('.likert-scale__item.no-select', {
@@ -11127,6 +11551,7 @@ const LikertScale = () => {
11127
11551
  m('label.likert-scale__label', {
11128
11552
  for: radioId,
11129
11553
  }),
11554
+ anchorLabel && m('.likert-scale__item-anchor', anchorLabel),
11130
11555
  // Tooltip (optional)
11131
11556
  showTooltip && tooltipLabel && m('.likert-scale__tooltip', tooltipLabel),
11132
11557
  ]);
@@ -11150,6 +11575,8 @@ const LikertScale = () => {
11150
11575
  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"]);
11151
11576
  const currentValue = getCurrentValue(attrs);
11152
11577
  const itemCount = Math.floor((max - min) / step) + 1;
11578
+ const useInlineAnchors = isVerticalLayout(layout);
11579
+ const middleValue = middleLabel ? min + Math.floor((itemCount - 1) / 2) * step : undefined;
11153
11580
  // Generate scale values
11154
11581
  const scaleValues = Array.from({ length: itemCount }, (_, i) => min + i * step);
11155
11582
  return m('.likert-scale', {
@@ -11198,6 +11625,9 @@ const LikertScale = () => {
11198
11625
  showNumber: showNumbers,
11199
11626
  showTooltip: showTooltips,
11200
11627
  tooltipLabel: tooltipLabels === null || tooltipLabels === void 0 ? void 0 : tooltipLabels[value - min],
11628
+ anchorLabel: useInlineAnchors
11629
+ ? getInlineAnchorLabel(value, min, max, middleValue, startLabel, middleLabel, endLabel)
11630
+ : undefined,
11201
11631
  groupId: state.groupId,
11202
11632
  name,
11203
11633
  disabled,
@@ -11205,7 +11635,8 @@ const LikertScale = () => {
11205
11635
  onchange: (v) => handleChange(attrs, v),
11206
11636
  }))),
11207
11637
  // Scale anchors
11208
- (startLabel || middleLabel || endLabel) &&
11638
+ !useInlineAnchors &&
11639
+ (startLabel || middleLabel || endLabel) &&
11209
11640
  m('.likert-scale__anchors', [
11210
11641
  startLabel && m('.likert-scale__anchor.likert-scale__anchor--start', startLabel),
11211
11642
  middleLabel && m('.likert-scale__anchor.likert-scale__anchor--middle', middleLabel),
@@ -11390,6 +11821,9 @@ const CircularProgress = () => {
11390
11821
  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']);
11391
11822
  const isDeterminate = mode === 'determinate';
11392
11823
  const sizePixels = SIZE_MAP[size];
11824
+ const styleValue = typeof style === 'string'
11825
+ ? `width:${sizePixels}px;height:${sizePixels}px;${style}`
11826
+ : Object.assign({ width: `${sizePixels}px`, height: `${sizePixels}px` }, style);
11393
11827
  const { radius, circumference, strokeDashoffset, percentage } = isDeterminate
11394
11828
  ? calculateStrokeProperties(sizePixels, value, max)
11395
11829
  : { radius: 0, circumference: 0, strokeDashoffset: 0, percentage: 0 };
@@ -11416,7 +11850,7 @@ const CircularProgress = () => {
11416
11850
  : {
11417
11851
  'aria-valuetext': ariaValueText || label || 'Loading',
11418
11852
  };
11419
- 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), [
11853
+ 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), [
11420
11854
  // SVG circle
11421
11855
  m('svg.circular-progress__svg', {
11422
11856
  viewBox: `0 0 ${sizePixels} ${sizePixels}`,
@@ -11481,13 +11915,7 @@ const LinearProgress = () => {
11481
11915
  // Determine label content
11482
11916
  const labelContent = label !== undefined ? label : showPercentage && isDeterminate ? `${Math.round(percentage)}%` : '';
11483
11917
  // Build class names
11484
- const classNames = [
11485
- 'linear-progress',
11486
- getColorClass(color, colorIntensity),
11487
- className,
11488
- ]
11489
- .filter(Boolean)
11490
- .join(' ');
11918
+ const classNames = ['linear-progress', getColorClass(color, colorIntensity), className].filter(Boolean).join(' ');
11491
11919
  // ARIA attributes
11492
11920
  const ariaAttrs = isDeterminate
11493
11921
  ? {
@@ -11564,10 +11992,12 @@ exports.DigitalClock = DigitalClock;
11564
11992
  exports.DoubleRangeSlider = DoubleRangeSlider;
11565
11993
  exports.Dropdown = Dropdown;
11566
11994
  exports.EmailInput = EmailInput;
11995
+ exports.Fieldset = Fieldset;
11567
11996
  exports.FileInput = FileInput;
11568
11997
  exports.FileUpload = FileUpload;
11569
11998
  exports.FlatButton = FlatButton;
11570
11999
  exports.FloatingActionButton = FloatingActionButton;
12000
+ exports.FormSection = FormSection;
11571
12001
  exports.HelperText = HelperText;
11572
12002
  exports.Icon = Icon;
11573
12003
  exports.IconButton = IconButton;
@@ -11619,6 +12049,7 @@ exports.TimeRangePicker = TimeRangePicker;
11619
12049
  exports.Timeline = Timeline;
11620
12050
  exports.Toast = Toast;
11621
12051
  exports.ToastComponent = ToastComponent;
12052
+ exports.ToggleButton = ToggleButton;
11622
12053
  exports.ToggleGroup = ToggleGroup;
11623
12054
  exports.Tooltip = Tooltip;
11624
12055
  exports.TooltipComponent = TooltipComponent;
@@ -11627,10 +12058,14 @@ exports.UrlInput = UrlInput;
11627
12058
  exports.Wizard = Wizard;
11628
12059
  exports.addLeadingZero = addLeadingZero;
11629
12060
  exports.clearPortal = clearPortal;
12061
+ exports.createAsyncComboboxState = createAsyncComboboxState;
11630
12062
  exports.createBreadcrumb = createBreadcrumb;
11631
12063
  exports.formatTime = formatTime;
11632
12064
  exports.generateHourOptions = generateHourOptions;
11633
12065
  exports.generateMinuteOptions = generateMinuteOptions;
12066
+ exports.getComboboxKeyResult = getComboboxKeyResult;
12067
+ exports.getComboboxOptionId = getComboboxOptionId;
12068
+ exports.getComboboxViewState = getComboboxViewState;
11634
12069
  exports.getDropdownStyles = getDropdownStyles;
11635
12070
  exports.getPortalContainer = getPortalContainer;
11636
12071
  exports.initPushpins = initPushpins;
@@ -11639,15 +12074,23 @@ exports.isNumeric = isNumeric;
11639
12074
  exports.isTimeDisabled = isTimeDisabled;
11640
12075
  exports.isValidationError = isValidationError;
11641
12076
  exports.isValidationSuccess = isValidationSuccess;
12077
+ exports.normalizeSelection = normalizeSelection;
11642
12078
  exports.padLeft = padLeft;
11643
12079
  exports.parseTime = parseTime;
11644
12080
  exports.range = range;
12081
+ exports.rejectAsyncComboboxRequest = rejectAsyncComboboxRequest;
11645
12082
  exports.releasePortalContainer = releasePortalContainer;
12083
+ exports.renderFieldChrome = renderFieldChrome;
11646
12084
  exports.renderToPortal = renderToPortal;
12085
+ exports.resolveAsyncComboboxRequest = resolveAsyncComboboxRequest;
12086
+ exports.resolveControllableValue = resolveControllableValue;
11647
12087
  exports.scrollToValue = scrollToValue;
11648
12088
  exports.snapToNearestItem = snapToNearestItem;
11649
12089
  exports.sortOptions = sortOptions;
12090
+ exports.startAsyncComboboxRequest = startAsyncComboboxRequest;
12091
+ exports.syncPortalContent = syncPortalContent;
11650
12092
  exports.timeToMinutes = timeToMinutes;
11651
12093
  exports.toast = toast;
11652
12094
  exports.uniqueId = uniqueId;
11653
12095
  exports.uuid4 = uuid4;
12096
+ //# sourceMappingURL=index.js.map