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.
- package/README.md +32 -2
- package/dist/advanced.css +109 -4
- package/dist/button.d.ts +3 -1
- package/dist/circular-progress.d.ts +2 -2
- package/dist/combobox.d.ts +35 -0
- package/dist/components.css +144 -17
- package/dist/core.css +278 -3
- package/dist/floating-action-button.d.ts +2 -1
- package/dist/form-section.d.ts +35 -0
- package/dist/forms.css +280 -5
- package/dist/index.css +357 -22
- package/dist/index.d.ts +3 -2
- package/dist/index.esm.js +728 -299
- package/dist/index.esm.js.map +1 -0
- package/dist/index.js +741 -298
- package/dist/index.js.map +1 -0
- package/dist/index.min.css +2 -2
- package/dist/index.umd.js +741 -298
- package/dist/index.umd.js.map +1 -0
- package/dist/input-options.d.ts +4 -1
- package/dist/likert-scale.d.ts +2 -1
- package/dist/linear-progress.d.ts +2 -2
- package/dist/material-icon.d.ts +2 -1
- package/dist/radio.d.ts +9 -0
- package/dist/range-slider.d.ts +3 -2
- package/dist/rating.d.ts +2 -1
- package/dist/search-select.d.ts +15 -2
- package/dist/select.d.ts +14 -1
- package/dist/sidenav.d.ts +11 -1
- package/dist/treeview.d.ts +2 -2
- package/dist/types.d.ts +5 -0
- package/dist/utilities.css +93 -2
- package/dist/utils.d.ts +30 -1
- package/package.json +20 -20
- package/sass/components/_badge-component.scss +8 -0
- package/sass/components/_buttons.scss +9 -9
- package/sass/components/_global.scss +93 -0
- package/sass/components/_likert-scale.scss +24 -0
- package/sass/components/_sidenav.scss +21 -3
- package/sass/components/_theme-variables.scss +22 -1
- package/sass/components/_toggle-group.scss +3 -0
- package/sass/components/forms/_form-groups.scss +1 -1
- package/sass/components/forms/_form-section.scss +63 -0
- package/sass/components/forms/_forms.scss +1 -0
- package/sass/components/forms/_input-fields.scss +55 -0
- package/sass/components/forms/_range-enhanced.scss +3 -3
- package/sass/components/forms/_select.scss +85 -0
package/dist/index.esm.js
CHANGED
|
@@ -34,6 +34,35 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
|
|
|
34
34
|
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
35
35
|
};
|
|
36
36
|
|
|
37
|
+
// import './styles/input.css';
|
|
38
|
+
const Mandatory = { view: ({ attrs }) => m('span.mandatory', Object.assign({}, attrs), '*') };
|
|
39
|
+
/** Simple label element, used for most components. */
|
|
40
|
+
const Label = () => {
|
|
41
|
+
return {
|
|
42
|
+
view: (_a) => {
|
|
43
|
+
var _b = _a.attrs, { label, id, isMandatory, isActive, className, initialValue } = _b, params = __rest(_b, ["label", "id", "isMandatory", "isActive", "className", "initialValue"]);
|
|
44
|
+
return label
|
|
45
|
+
? m('label', Object.assign(Object.assign({}, params), { className: [className, isActive ? 'active' : ''].filter(Boolean).join(' ').trim() || undefined, for: id, oncreate: ({ dom }) => {
|
|
46
|
+
if (!initialValue)
|
|
47
|
+
return;
|
|
48
|
+
const labelEl = dom;
|
|
49
|
+
labelEl.classList.add('active');
|
|
50
|
+
} }), [m.trust(label), isMandatory ? m(Mandatory) : undefined])
|
|
51
|
+
: undefined;
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
/** Create a helper text, often used for displaying a small help text. May be replaced by the validation message. */
|
|
56
|
+
const HelperText = () => {
|
|
57
|
+
return {
|
|
58
|
+
view: ({ attrs: { helperText, dataError, dataSuccess, className } }) => {
|
|
59
|
+
return helperText || dataError || dataSuccess
|
|
60
|
+
? m('span.helper-text.left', { className, 'data-error': dataError, 'data-success': dataSuccess }, dataError ? m.trust(dataError) : dataSuccess ? m.trust(dataSuccess) : helperText ? m.trust(helperText) : '')
|
|
61
|
+
: undefined;
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
|
|
37
66
|
// Utility functions for the library
|
|
38
67
|
/**
|
|
39
68
|
* Create a unique ID
|
|
@@ -94,6 +123,42 @@ const sortOptions = (options, sortConfig) => {
|
|
|
94
123
|
* @returns
|
|
95
124
|
*/
|
|
96
125
|
const padLeft = (n, width = 2, z = '0') => String(n).padStart(width, z);
|
|
126
|
+
const normalizeSelection = (value) => {
|
|
127
|
+
if (value === undefined) {
|
|
128
|
+
return [];
|
|
129
|
+
}
|
|
130
|
+
return Array.isArray(value) ? value : [value];
|
|
131
|
+
};
|
|
132
|
+
const resolveControllableValue = ({ controlled, disabled, controlledValue, defaultValue, internalValue, fallbackValue, }) => {
|
|
133
|
+
var _a, _b;
|
|
134
|
+
if (controlled) {
|
|
135
|
+
return controlledValue !== null && controlledValue !== void 0 ? controlledValue : fallbackValue;
|
|
136
|
+
}
|
|
137
|
+
if (disabled) {
|
|
138
|
+
return (_a = defaultValue !== null && defaultValue !== void 0 ? defaultValue : controlledValue) !== null && _a !== void 0 ? _a : fallbackValue;
|
|
139
|
+
}
|
|
140
|
+
return (_b = internalValue !== null && internalValue !== void 0 ? internalValue : defaultValue) !== null && _b !== void 0 ? _b : fallbackValue;
|
|
141
|
+
};
|
|
142
|
+
const renderFieldChrome = ({ label, id, isMandatory, isActive, initialValue, helperText, dataError, dataSuccess, }) => {
|
|
143
|
+
return [
|
|
144
|
+
label
|
|
145
|
+
? m(Label, {
|
|
146
|
+
label,
|
|
147
|
+
id,
|
|
148
|
+
isMandatory,
|
|
149
|
+
isActive,
|
|
150
|
+
initialValue,
|
|
151
|
+
})
|
|
152
|
+
: undefined,
|
|
153
|
+
helperText || dataError || dataSuccess
|
|
154
|
+
? m(HelperText, {
|
|
155
|
+
helperText,
|
|
156
|
+
dataError,
|
|
157
|
+
dataSuccess,
|
|
158
|
+
})
|
|
159
|
+
: undefined,
|
|
160
|
+
].filter(Boolean);
|
|
161
|
+
};
|
|
97
162
|
// Keep only essential dropdown positioning styles
|
|
98
163
|
const getDropdownStyles = (inputRef, overlap = false, options, isDropDown = false) => {
|
|
99
164
|
if (!inputRef) {
|
|
@@ -125,8 +190,10 @@ const getDropdownStyles = (inputRef, overlap = false, options, isDropDown = fals
|
|
|
125
190
|
groups.add(option.group);
|
|
126
191
|
}
|
|
127
192
|
});
|
|
128
|
-
//
|
|
129
|
-
|
|
193
|
+
// Match the select dropdown's CSS max-height. Positioning with the full
|
|
194
|
+
// option count would otherwise leave a gap above an input near the bottom
|
|
195
|
+
// of the viewport when the rendered menu is capped at 400px.
|
|
196
|
+
estimatedHeight = Math.min(totalOptions * itemHeight + groups.size * groupHeaderHeight, 400);
|
|
130
197
|
}
|
|
131
198
|
const spaceBelow = viewportHeight - rect.bottom;
|
|
132
199
|
const spaceAbove = rect.top;
|
|
@@ -142,33 +209,26 @@ const getDropdownStyles = (inputRef, overlap = false, options, isDropDown = fals
|
|
|
142
209
|
const needsScrolling = estimatedHeight > effectiveAvailableSpace;
|
|
143
210
|
// Calculate the actual height the dropdown will take
|
|
144
211
|
const actualHeight = needsScrolling ? effectiveAvailableSpace : estimatedHeight;
|
|
145
|
-
// Calculate positioning when dropdown should appear above
|
|
146
|
-
let topOffset;
|
|
147
|
-
if (shouldPositionAbove) {
|
|
148
|
-
// Calculate how much space we actually have from top of viewport to top of input
|
|
149
|
-
const availableSpaceFromViewportTop = rect.top;
|
|
150
|
-
// If dropdown fits comfortably above input, use normal positioning
|
|
151
|
-
if (actualHeight <= availableSpaceFromViewportTop) {
|
|
152
|
-
topOffset = 12 - actualHeight + (isDropDown ? itemHeight : 0); // Bottom of dropdown aligns with top of input
|
|
153
|
-
}
|
|
154
|
-
else {
|
|
155
|
-
// If dropdown is too tall, position it at the very top of viewport
|
|
156
|
-
// This makes the dropdown use all available space from viewport top to input top
|
|
157
|
-
topOffset = -availableSpaceFromViewportTop + 5; // 5px margin from viewport top
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
else {
|
|
161
|
-
topOffset = overlap ? 0 : '100%';
|
|
162
|
-
}
|
|
163
212
|
const styles = {
|
|
164
213
|
display: 'block',
|
|
165
214
|
opacity: 1,
|
|
166
215
|
position: 'absolute',
|
|
167
|
-
top: typeof topOffset === 'number' ? `${topOffset}px` : topOffset,
|
|
168
216
|
left: '0',
|
|
169
217
|
zIndex: 1000,
|
|
170
218
|
width: `${rect.width}px`,
|
|
171
219
|
};
|
|
220
|
+
if (shouldPositionAbove) {
|
|
221
|
+
// This menu is absolutely positioned inside the select wrapper. A negative
|
|
222
|
+
// `top` calculated from viewport coordinates becomes wrong when an ancestor
|
|
223
|
+
// scrolls (as ScenarioSpark's main area does). Anchor the menu to its local
|
|
224
|
+
// containing block instead; its height can then change without detaching it
|
|
225
|
+
// from the input.
|
|
226
|
+
styles.top = 'auto';
|
|
227
|
+
styles.bottom = `${isDropDown ? -18 : inputRef.offsetHeight - 12}px`;
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
styles.top = overlap ? 0 : '100%';
|
|
231
|
+
}
|
|
172
232
|
// Only add scrolling constraints when necessary
|
|
173
233
|
if (needsScrolling) {
|
|
174
234
|
styles.maxHeight = `${actualHeight}px`;
|
|
@@ -235,9 +295,17 @@ const releasePortalContainer = (id) => {
|
|
|
235
295
|
* @param zIndex - Z-index for portal container (default: 1004)
|
|
236
296
|
*/
|
|
237
297
|
const renderToPortal = (containerId, vnode, zIndex = 1004) => {
|
|
238
|
-
const
|
|
298
|
+
const existingContainer = portalContainers.get(containerId);
|
|
299
|
+
const container = existingContainer ? existingContainer.element : getPortalContainer(containerId, zIndex);
|
|
239
300
|
m.render(container, vnode);
|
|
240
301
|
};
|
|
302
|
+
const syncPortalContent = ({ containerId, shouldRender, vnode, zIndex = 1004 }) => {
|
|
303
|
+
if (!shouldRender || vnode === null) {
|
|
304
|
+
clearPortal(containerId);
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
renderToPortal(containerId, vnode, zIndex);
|
|
308
|
+
};
|
|
241
309
|
/**
|
|
242
310
|
* Clears portal content and releases container reference.
|
|
243
311
|
* If this is the last reference, the container will be removed from the DOM.
|
|
@@ -252,35 +320,6 @@ const clearPortal = (containerId) => {
|
|
|
252
320
|
}
|
|
253
321
|
};
|
|
254
322
|
|
|
255
|
-
// import './styles/input.css';
|
|
256
|
-
const Mandatory = { view: ({ attrs }) => m('span.mandatory', Object.assign({}, attrs), '*') };
|
|
257
|
-
/** Simple label element, used for most components. */
|
|
258
|
-
const Label = () => {
|
|
259
|
-
return {
|
|
260
|
-
view: (_a) => {
|
|
261
|
-
var _b = _a.attrs, { label, id, isMandatory, isActive, className, initialValue } = _b, params = __rest(_b, ["label", "id", "isMandatory", "isActive", "className", "initialValue"]);
|
|
262
|
-
return label
|
|
263
|
-
? m('label', Object.assign(Object.assign({}, params), { className: [className, isActive ? 'active' : ''].filter(Boolean).join(' ').trim() || undefined, for: id, oncreate: ({ dom }) => {
|
|
264
|
-
if (!initialValue)
|
|
265
|
-
return;
|
|
266
|
-
const labelEl = dom;
|
|
267
|
-
labelEl.classList.add('active');
|
|
268
|
-
} }), [m.trust(label), isMandatory ? m(Mandatory) : undefined])
|
|
269
|
-
: undefined;
|
|
270
|
-
},
|
|
271
|
-
};
|
|
272
|
-
};
|
|
273
|
-
/** Create a helper text, often used for displaying a small help text. May be replaced by the validation message. */
|
|
274
|
-
const HelperText = () => {
|
|
275
|
-
return {
|
|
276
|
-
view: ({ attrs: { helperText, dataError, dataSuccess, className } }) => {
|
|
277
|
-
return helperText || dataError || dataSuccess
|
|
278
|
-
? m('span.helper-text.left', { className, 'data-error': dataError, 'data-success': dataSuccess }, dataError ? m.trust(dataError) : dataSuccess ? m.trust(dataSuccess) : helperText ? m.trust(helperText) : '')
|
|
279
|
-
: undefined;
|
|
280
|
-
},
|
|
281
|
-
};
|
|
282
|
-
};
|
|
283
|
-
|
|
284
323
|
/** Component to auto complete your text input - Pure Mithril implementation */
|
|
285
324
|
const Autocomplete = () => {
|
|
286
325
|
const state = {
|
|
@@ -303,6 +342,28 @@ const Autocomplete = () => {
|
|
|
303
342
|
.slice(0, limit);
|
|
304
343
|
return filtered;
|
|
305
344
|
};
|
|
345
|
+
const highlightMatch = (text, query) => {
|
|
346
|
+
if (!query) {
|
|
347
|
+
return text;
|
|
348
|
+
}
|
|
349
|
+
const lowerText = text.toLowerCase();
|
|
350
|
+
const lowerQuery = query.toLowerCase();
|
|
351
|
+
const nodes = [];
|
|
352
|
+
let start = 0;
|
|
353
|
+
let index = lowerText.indexOf(lowerQuery, start);
|
|
354
|
+
while (index !== -1) {
|
|
355
|
+
if (index > start) {
|
|
356
|
+
nodes.push(text.slice(start, index));
|
|
357
|
+
}
|
|
358
|
+
nodes.push(m('span.highlight', text.slice(index, index + query.length)));
|
|
359
|
+
start = index + query.length;
|
|
360
|
+
index = lowerText.indexOf(lowerQuery, start);
|
|
361
|
+
}
|
|
362
|
+
if (start < text.length) {
|
|
363
|
+
nodes.push(text.slice(start));
|
|
364
|
+
}
|
|
365
|
+
return nodes.length > 0 ? nodes : text;
|
|
366
|
+
};
|
|
306
367
|
const selectSuggestion = (suggestion, attrs) => {
|
|
307
368
|
const controlled = isControlled(attrs);
|
|
308
369
|
// Update internal state for uncontrolled mode
|
|
@@ -408,7 +469,6 @@ const Autocomplete = () => {
|
|
|
408
469
|
Object.keys(data).some((key) => key.toLowerCase() === currentValue.toLowerCase());
|
|
409
470
|
// Only open dropdown if there are suggestions and no perfect match
|
|
410
471
|
state.isOpen = state.suggestions.length > 0 && currentValue.length >= minLength && !hasExactMatch;
|
|
411
|
-
const replacer = new RegExp(`(${currentValue})`, 'i');
|
|
412
472
|
return m('.input-field.autocomplete-wrapper', {
|
|
413
473
|
className: cn,
|
|
414
474
|
style,
|
|
@@ -489,9 +549,7 @@ const Autocomplete = () => {
|
|
|
489
549
|
},
|
|
490
550
|
}, suggestion.value.replace('icon:', ''))
|
|
491
551
|
: null,
|
|
492
|
-
m('span', suggestion.key
|
|
493
|
-
? m.trust(suggestion.key.replace(replacer, (i) => `<span class="highlight">${i}</span>`))
|
|
494
|
-
: ''),
|
|
552
|
+
m('span', suggestion.key ? highlightMatch(suggestion.key, currentValue) : ''),
|
|
495
553
|
]))),
|
|
496
554
|
m(Label, {
|
|
497
555
|
label,
|
|
@@ -718,7 +776,7 @@ WavesEffect.onTouchEnd = (e) => {
|
|
|
718
776
|
/**
|
|
719
777
|
* A factory to create new buttons.
|
|
720
778
|
*
|
|
721
|
-
* @example FlatButton = ButtonFactory('
|
|
779
|
+
* @example FlatButton = ButtonFactory('button.waves-effect.waves-teal.btn-flat');
|
|
722
780
|
*/
|
|
723
781
|
const ButtonFactory = (element, defaultClassNames, type = '') => {
|
|
724
782
|
return () => {
|
|
@@ -741,15 +799,21 @@ const ButtonFactory = (element, defaultClassNames, type = '') => {
|
|
|
741
799
|
ontouchend: WavesEffect.onTouchEnd,
|
|
742
800
|
}
|
|
743
801
|
: {};
|
|
744
|
-
|
|
802
|
+
const tagName = element.split(/[.#]/)[0] || element;
|
|
803
|
+
const elementSuffix = element.slice(tagName.length);
|
|
804
|
+
const isLink = Boolean(attrs.href);
|
|
805
|
+
const renderedElement = `${isLink ? 'a' : 'button'}${elementSuffix}`;
|
|
806
|
+
return m(renderedElement, Object.assign(Object.assign(Object.assign({}, params), wavesHandlers), { className: cn, 'data-position': tooltip ? position : undefined, 'data-tooltip': tooltip || undefined,
|
|
807
|
+
// Links retain native navigation semantics and must not have a button type.
|
|
808
|
+
type: isLink ? undefined : buttonType }), iconName ? m(Icon, { iconName, className: iconClass !== undefined ? iconClass : 'left' }) : undefined, label ? label : undefined, children);
|
|
745
809
|
},
|
|
746
810
|
};
|
|
747
811
|
};
|
|
748
812
|
};
|
|
749
|
-
const Button = ButtonFactory('
|
|
750
|
-
const LargeButton = ButtonFactory('
|
|
751
|
-
const SmallButton = ButtonFactory('
|
|
752
|
-
const FlatButton = ButtonFactory('
|
|
813
|
+
const Button = ButtonFactory('button', 'waves-effect waves-light btn', 'button');
|
|
814
|
+
const LargeButton = ButtonFactory('button', 'waves-effect waves-light btn-large', 'button');
|
|
815
|
+
const SmallButton = ButtonFactory('button', 'waves-effect waves-light btn-small', 'button');
|
|
816
|
+
const FlatButton = ButtonFactory('button', 'waves-effect waves-teal btn-flat', 'button');
|
|
753
817
|
const IconButton = ButtonFactory('button', 'btn-flat btn-icon waves-effect waves-teal', 'button');
|
|
754
818
|
const RoundIconButton = ButtonFactory('button', 'btn-floating btn-large waves-effect waves-light', 'button');
|
|
755
819
|
const SubmitButton = ButtonFactory('button', 'btn waves-effect waves-light', 'submit');
|
|
@@ -806,7 +870,7 @@ const ConfirmButton = () => {
|
|
|
806
870
|
// Add square styling for icon-only confirming state
|
|
807
871
|
const buttonStyle = !label && isConfirming
|
|
808
872
|
? 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;
|
|
809
|
-
return m(ButtonComponent, Object.assign(Object.assign({}, props), { style: buttonStyle, className: `${props.className || ''} ${cn}`, iconName: currentIconName, iconClass: label ?
|
|
873
|
+
return m(ButtonComponent, Object.assign(Object.assign({}, props), { style: buttonStyle, className: `${props.className || ''} ${cn}`, iconName: currentIconName, iconClass: label ? iconClass || 'left' : '', label, onclick: handleClick }));
|
|
810
874
|
},
|
|
811
875
|
};
|
|
812
876
|
};
|
|
@@ -1273,11 +1337,19 @@ const MaterialIcon = () => {
|
|
|
1273
1337
|
};
|
|
1274
1338
|
const rotation = (_a = rotationMap[direction]) !== null && _a !== void 0 ? _a : 0;
|
|
1275
1339
|
const transform = rotation ? `rotate(${rotation}deg)` : undefined;
|
|
1340
|
+
const baseStyle = {
|
|
1341
|
+
display: 'inline-block',
|
|
1342
|
+
verticalAlign: 'middle',
|
|
1343
|
+
transform,
|
|
1344
|
+
};
|
|
1345
|
+
const combinedStyle = typeof style === 'string'
|
|
1346
|
+
? `display:inline-block;vertical-align:middle;${transform ? `transform:${transform};` : ''}${style}`
|
|
1347
|
+
: Object.assign(Object.assign({}, baseStyle), style);
|
|
1276
1348
|
const icon = iconPaths[name];
|
|
1277
1349
|
if (!icon || !Array.isArray(icon)) {
|
|
1278
1350
|
return m(Icon, Object.assign(Object.assign({}, props), { iconName: name }));
|
|
1279
1351
|
}
|
|
1280
|
-
return m('svg', Object.assign(Object.assign({}, props), { style:
|
|
1352
|
+
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', {
|
|
1281
1353
|
d,
|
|
1282
1354
|
fill: d.includes('M0 0h24v24H0z') ? 'none' : 'currentColor',
|
|
1283
1355
|
})));
|
|
@@ -1676,6 +1748,106 @@ const Collapsible = () => {
|
|
|
1676
1748
|
};
|
|
1677
1749
|
};
|
|
1678
1750
|
|
|
1751
|
+
const isHandledKey = (key) => key === 'ArrowDown' || key === 'ArrowUp' || key === 'Enter' || key === ' ' || key === 'Escape';
|
|
1752
|
+
const getTotalRows = (optionCount, includeActionRow) => Math.max(0, optionCount) + (includeActionRow ? 1 : 0);
|
|
1753
|
+
const getComboboxKeyResult = ({ key, isOpen, focusedIndex, optionCount, includeActionRow, }) => {
|
|
1754
|
+
const totalRows = getTotalRows(optionCount, includeActionRow);
|
|
1755
|
+
if (!isHandledKey(key)) {
|
|
1756
|
+
return { isOpen, focusedIndex, action: 'none', preventDefault: false };
|
|
1757
|
+
}
|
|
1758
|
+
if (key === 'Escape') {
|
|
1759
|
+
return { isOpen: false, focusedIndex: -1, action: 'close', preventDefault: true };
|
|
1760
|
+
}
|
|
1761
|
+
if (key === 'ArrowDown') {
|
|
1762
|
+
if (!isOpen) {
|
|
1763
|
+
return {
|
|
1764
|
+
isOpen: true,
|
|
1765
|
+
focusedIndex: totalRows > 0 ? 0 : -1,
|
|
1766
|
+
action: 'open',
|
|
1767
|
+
preventDefault: true,
|
|
1768
|
+
};
|
|
1769
|
+
}
|
|
1770
|
+
return {
|
|
1771
|
+
isOpen: true,
|
|
1772
|
+
focusedIndex: totalRows > 0 ? Math.min(focusedIndex + 1, totalRows - 1) : -1,
|
|
1773
|
+
action: 'none',
|
|
1774
|
+
preventDefault: true,
|
|
1775
|
+
};
|
|
1776
|
+
}
|
|
1777
|
+
if (key === 'ArrowUp') {
|
|
1778
|
+
if (!isOpen) {
|
|
1779
|
+
return {
|
|
1780
|
+
isOpen: true,
|
|
1781
|
+
focusedIndex: totalRows > 0 ? 0 : -1,
|
|
1782
|
+
action: 'open',
|
|
1783
|
+
preventDefault: true,
|
|
1784
|
+
};
|
|
1785
|
+
}
|
|
1786
|
+
return {
|
|
1787
|
+
isOpen: true,
|
|
1788
|
+
focusedIndex: totalRows > 0 ? Math.max(focusedIndex - 1, 0) : -1,
|
|
1789
|
+
action: 'none',
|
|
1790
|
+
preventDefault: true,
|
|
1791
|
+
};
|
|
1792
|
+
}
|
|
1793
|
+
if (!isOpen) {
|
|
1794
|
+
return {
|
|
1795
|
+
isOpen: true,
|
|
1796
|
+
focusedIndex: totalRows > 0 ? 0 : -1,
|
|
1797
|
+
action: 'open',
|
|
1798
|
+
preventDefault: true,
|
|
1799
|
+
};
|
|
1800
|
+
}
|
|
1801
|
+
if (focusedIndex < 0) {
|
|
1802
|
+
return { isOpen, focusedIndex, action: 'none', preventDefault: true };
|
|
1803
|
+
}
|
|
1804
|
+
const isActionRow = includeActionRow && focusedIndex === optionCount;
|
|
1805
|
+
return {
|
|
1806
|
+
isOpen,
|
|
1807
|
+
focusedIndex,
|
|
1808
|
+
action: isActionRow ? 'selectAction' : 'selectFocused',
|
|
1809
|
+
preventDefault: true,
|
|
1810
|
+
};
|
|
1811
|
+
};
|
|
1812
|
+
const getComboboxOptionId = (baseId, optionIndex) => `${baseId}-option-${optionIndex}`;
|
|
1813
|
+
const createAsyncComboboxState = (initialOptions = []) => ({
|
|
1814
|
+
options: initialOptions,
|
|
1815
|
+
isLoading: false,
|
|
1816
|
+
error: null,
|
|
1817
|
+
latestRequestId: 0,
|
|
1818
|
+
});
|
|
1819
|
+
const startAsyncComboboxRequest = (state) => {
|
|
1820
|
+
const requestId = state.latestRequestId + 1;
|
|
1821
|
+
return {
|
|
1822
|
+
requestId,
|
|
1823
|
+
nextState: Object.assign(Object.assign({}, state), { isLoading: true, error: null, latestRequestId: requestId }),
|
|
1824
|
+
};
|
|
1825
|
+
};
|
|
1826
|
+
const resolveAsyncComboboxRequest = (state, requestId, options) => {
|
|
1827
|
+
if (requestId !== state.latestRequestId) {
|
|
1828
|
+
return state;
|
|
1829
|
+
}
|
|
1830
|
+
return Object.assign(Object.assign({}, state), { options, isLoading: false, error: null });
|
|
1831
|
+
};
|
|
1832
|
+
const rejectAsyncComboboxRequest = (state, requestId, errorMessage) => {
|
|
1833
|
+
if (requestId !== state.latestRequestId) {
|
|
1834
|
+
return state;
|
|
1835
|
+
}
|
|
1836
|
+
return Object.assign(Object.assign({}, state), { options: [], isLoading: false, error: errorMessage });
|
|
1837
|
+
};
|
|
1838
|
+
const getComboboxViewState = ({ isLoading, error, optionCount, }) => {
|
|
1839
|
+
if (isLoading) {
|
|
1840
|
+
return 'loading';
|
|
1841
|
+
}
|
|
1842
|
+
if (error) {
|
|
1843
|
+
return 'error';
|
|
1844
|
+
}
|
|
1845
|
+
if (optionCount === 0) {
|
|
1846
|
+
return 'empty';
|
|
1847
|
+
}
|
|
1848
|
+
return 'ready';
|
|
1849
|
+
};
|
|
1850
|
+
|
|
1679
1851
|
var CollectionMode;
|
|
1680
1852
|
(function (CollectionMode) {
|
|
1681
1853
|
CollectionMode[CollectionMode["BASIC"] = 0] = "BASIC";
|
|
@@ -2375,6 +2547,7 @@ const DatePicker = () => {
|
|
|
2375
2547
|
e.stopPropagation();
|
|
2376
2548
|
gotoMonth(index);
|
|
2377
2549
|
state.monthDropdownOpen = false;
|
|
2550
|
+
m.redraw();
|
|
2378
2551
|
},
|
|
2379
2552
|
}, monthName))),
|
|
2380
2553
|
]),
|
|
@@ -2399,6 +2572,7 @@ const DatePicker = () => {
|
|
|
2399
2572
|
e.stopPropagation();
|
|
2400
2573
|
gotoYear(i);
|
|
2401
2574
|
state.yearDropdownOpen = false;
|
|
2575
|
+
m.redraw();
|
|
2402
2576
|
},
|
|
2403
2577
|
}, i))),
|
|
2404
2578
|
]),
|
|
@@ -3283,6 +3457,10 @@ const DoubleRangeSlider = {
|
|
|
3283
3457
|
},
|
|
3284
3458
|
};
|
|
3285
3459
|
|
|
3460
|
+
const isReadonly = (attrs) => {
|
|
3461
|
+
const legacyReadonly = attrs.readonly;
|
|
3462
|
+
return Boolean(attrs.readOnly || legacyReadonly);
|
|
3463
|
+
};
|
|
3286
3464
|
/** Character counter component that tracks text length against maxLength */
|
|
3287
3465
|
const CharacterCounter = () => {
|
|
3288
3466
|
return {
|
|
@@ -3361,7 +3539,7 @@ const TextArea = () => {
|
|
|
3361
3539
|
return {
|
|
3362
3540
|
oninit: ({ attrs }) => {
|
|
3363
3541
|
const controlled = isControlled(attrs);
|
|
3364
|
-
const isNonInteractive = attrs
|
|
3542
|
+
const isNonInteractive = isReadonly(attrs) || attrs.disabled;
|
|
3365
3543
|
// Warn developer for improper controlled usage
|
|
3366
3544
|
if (attrs.value !== undefined && !controlled && !isNonInteractive) {
|
|
3367
3545
|
console.warn(`TextArea received 'value' prop without 'oninput' or 'onchange' handler. ` +
|
|
@@ -3378,7 +3556,7 @@ const TextArea = () => {
|
|
|
3378
3556
|
var _a, _b, _c, _d;
|
|
3379
3557
|
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"]);
|
|
3380
3558
|
const controlled = isControlled(attrs);
|
|
3381
|
-
const isNonInteractive = attrs
|
|
3559
|
+
const isNonInteractive = isReadonly(attrs) || attrs.disabled;
|
|
3382
3560
|
let currentValue;
|
|
3383
3561
|
if (controlled) {
|
|
3384
3562
|
currentValue = value || '';
|
|
@@ -3494,14 +3672,12 @@ const TextArea = () => {
|
|
|
3494
3672
|
onkeypress(ev, ev.target.value);
|
|
3495
3673
|
}
|
|
3496
3674
|
: undefined })),
|
|
3497
|
-
|
|
3675
|
+
...renderFieldChrome({
|
|
3498
3676
|
label,
|
|
3499
3677
|
id,
|
|
3500
3678
|
isMandatory,
|
|
3501
3679
|
isActive: currentValue || placeholder || state.active,
|
|
3502
3680
|
initialValue: currentValue !== '',
|
|
3503
|
-
}),
|
|
3504
|
-
m(HelperText, {
|
|
3505
3681
|
helperText,
|
|
3506
3682
|
dataError: state.hasInteracted && attrs.dataError ? attrs.dataError : undefined,
|
|
3507
3683
|
dataSuccess: state.hasInteracted && attrs.dataSuccess ? attrs.dataSuccess : undefined,
|
|
@@ -3581,7 +3757,7 @@ const InputField = (type, defaultClass = '') => () => {
|
|
|
3581
3757
|
return {
|
|
3582
3758
|
oninit: ({ attrs }) => {
|
|
3583
3759
|
const controlled = isControlled(attrs);
|
|
3584
|
-
const isNonInteractive = attrs
|
|
3760
|
+
const isNonInteractive = isReadonly(attrs) || attrs.disabled;
|
|
3585
3761
|
// Warn developer for improper controlled usage
|
|
3586
3762
|
if (attrs.value !== undefined && !controlled && !isNonInteractive) {
|
|
3587
3763
|
console.warn(`${type} input with label '${attrs.label}' received 'value' prop without 'oninput' handler. ` +
|
|
@@ -3621,7 +3797,7 @@ const InputField = (type, defaultClass = '') => () => {
|
|
|
3621
3797
|
}
|
|
3622
3798
|
const isNumeric = ['number', 'range'].includes(type);
|
|
3623
3799
|
const controlled = isControlled(attrs);
|
|
3624
|
-
const isNonInteractive = attrs
|
|
3800
|
+
const isNonInteractive = isReadonly(attrs) || attrs.disabled;
|
|
3625
3801
|
let value;
|
|
3626
3802
|
if (controlled) {
|
|
3627
3803
|
value = attrs.value;
|
|
@@ -3640,9 +3816,28 @@ const InputField = (type, defaultClass = '') => () => {
|
|
|
3640
3816
|
const rangeType = type === 'range' && !attrs.minmax;
|
|
3641
3817
|
// Only add validate class if input is interactive and validation is needed
|
|
3642
3818
|
const shouldValidate = !isNonInteractive && (validate || type === 'email' || type === 'url' || isNumeric);
|
|
3819
|
+
const inputClass = [
|
|
3820
|
+
type === 'number' ? 'number-input' : '',
|
|
3821
|
+
type === 'range' && attrs.vertical ? 'range-slider vertical' : '',
|
|
3822
|
+
shouldValidate ? 'validate' : '',
|
|
3823
|
+
]
|
|
3824
|
+
.filter(Boolean)
|
|
3825
|
+
.join(' ');
|
|
3826
|
+
const stepNumberInput = (direction) => {
|
|
3827
|
+
const input = state.inputElement;
|
|
3828
|
+
if (!input)
|
|
3829
|
+
return;
|
|
3830
|
+
if (direction === 'up') {
|
|
3831
|
+
input.stepUp();
|
|
3832
|
+
}
|
|
3833
|
+
else {
|
|
3834
|
+
input.stepDown();
|
|
3835
|
+
}
|
|
3836
|
+
input.dispatchEvent(new Event('input', { bubbles: true }));
|
|
3837
|
+
};
|
|
3643
3838
|
return m('.input-field', { className: cn, style }, [
|
|
3644
3839
|
iconName ? m('i.material-icons.prefix', iconName) : undefined,
|
|
3645
|
-
m('input', Object.assign(Object.assign({ class:
|
|
3840
|
+
m('input', Object.assign(Object.assign({ class: inputClass || undefined }, params), { type, tabindex: 0, id,
|
|
3646
3841
|
placeholder, value: controlled ? value : undefined, style: type === 'range' && attrs.vertical
|
|
3647
3842
|
? {
|
|
3648
3843
|
height: attrs.height || '200px',
|
|
@@ -3724,7 +3919,7 @@ const InputField = (type, defaultClass = '') => () => {
|
|
|
3724
3919
|
const target = e.target;
|
|
3725
3920
|
state.hasInteracted = true;
|
|
3726
3921
|
// Skip validation for readonly/disabled inputs
|
|
3727
|
-
if (attrs
|
|
3922
|
+
if (isReadonly(attrs) || attrs.disabled) {
|
|
3728
3923
|
// Call original onblur if provided
|
|
3729
3924
|
if (attrs.onblur) {
|
|
3730
3925
|
attrs.onblur(e);
|
|
@@ -3814,6 +4009,22 @@ const InputField = (type, defaultClass = '') => () => {
|
|
|
3814
4009
|
onchange(getValue(state.inputElement));
|
|
3815
4010
|
}
|
|
3816
4011
|
} })),
|
|
4012
|
+
type === 'number' && !isNonInteractive
|
|
4013
|
+
? m('.number-input-controls', [
|
|
4014
|
+
m('button.number-input-control.number-input-control-up[type=button]', {
|
|
4015
|
+
'aria-label': `Increase ${label || 'value'}`,
|
|
4016
|
+
title: 'Increase value',
|
|
4017
|
+
onmousedown: (event) => event.preventDefault(),
|
|
4018
|
+
onclick: () => stepNumberInput('up'),
|
|
4019
|
+
}, m(MaterialIcon, { name: 'chevron', direction: 'up' })),
|
|
4020
|
+
m('button.number-input-control.number-input-control-down[type=button]', {
|
|
4021
|
+
'aria-label': `Decrease ${label || 'value'}`,
|
|
4022
|
+
title: 'Decrease value',
|
|
4023
|
+
onmousedown: (event) => event.preventDefault(),
|
|
4024
|
+
onclick: () => stepNumberInput('down'),
|
|
4025
|
+
}, m(MaterialIcon, { name: 'chevron' })),
|
|
4026
|
+
])
|
|
4027
|
+
: undefined,
|
|
3817
4028
|
// Clear button - only for text inputs with canClear enabled and has content
|
|
3818
4029
|
canClear && type === 'text' && ((_f = state.inputElement) === null || _f === void 0 ? void 0 : _f.value)
|
|
3819
4030
|
? m(MaterialIcon, {
|
|
@@ -3826,14 +4037,12 @@ const InputField = (type, defaultClass = '') => () => {
|
|
|
3826
4037
|
},
|
|
3827
4038
|
})
|
|
3828
4039
|
: undefined,
|
|
3829
|
-
|
|
4040
|
+
...renderFieldChrome({
|
|
3830
4041
|
label,
|
|
3831
4042
|
id,
|
|
3832
4043
|
isMandatory,
|
|
3833
4044
|
isActive,
|
|
3834
4045
|
initialValue: value !== undefined && value !== '',
|
|
3835
|
-
}),
|
|
3836
|
-
m(HelperText, {
|
|
3837
4046
|
helperText,
|
|
3838
4047
|
dataError: state.hasInteracted && !state.isValid ? dataError : undefined,
|
|
3839
4048
|
dataSuccess: state.hasInteracted && state.isValid ? dataSuccess : undefined,
|
|
@@ -3933,7 +4142,7 @@ const InputCheckbox = () => {
|
|
|
3933
4142
|
view: ({ attrs: { className = 'col s12', onchange, label, checked, disabled, description, style, inputId } }) => {
|
|
3934
4143
|
if (!checkboxId)
|
|
3935
4144
|
checkboxId = inputId || uniqueId();
|
|
3936
|
-
return m('div', { className, style }, m('label', { for: checkboxId }, [
|
|
4145
|
+
return m('div', { className, style }, m('label', { for: checkboxId, style: { position: 'relative', display: 'inline-block' } }, [
|
|
3937
4146
|
m('input[type=checkbox][tabindex=0]', {
|
|
3938
4147
|
className: disabled ? 'disabled' : undefined,
|
|
3939
4148
|
id: checkboxId,
|
|
@@ -4698,17 +4907,6 @@ const Dropdown = () => {
|
|
|
4698
4907
|
const updatePortalDropdown = (items, selectedLabel, onSelectItem, maxHeight) => {
|
|
4699
4908
|
if (!state.isInsideModal)
|
|
4700
4909
|
return;
|
|
4701
|
-
// Clean up existing portal
|
|
4702
|
-
const existingPortal = document.getElementById(`${state.id}-dropdown`);
|
|
4703
|
-
if (existingPortal) {
|
|
4704
|
-
existingPortal.remove();
|
|
4705
|
-
}
|
|
4706
|
-
if (!state.isOpen || !state.inputRef)
|
|
4707
|
-
return;
|
|
4708
|
-
// Create portal element
|
|
4709
|
-
const portalElement = document.createElement('div');
|
|
4710
|
-
portalElement.id = `${state.id}-dropdown`;
|
|
4711
|
-
document.body.appendChild(portalElement);
|
|
4712
4910
|
// Create dropdown content
|
|
4713
4911
|
const availableItems = items.filter((item) => !item.divider && !item.disabled);
|
|
4714
4912
|
const dropdownContent = items.map((item) => {
|
|
@@ -4722,15 +4920,14 @@ const Dropdown = () => {
|
|
|
4722
4920
|
class: `${isSelected ? 'selected' : ''} ${isFocused ? 'focused' : ''}${item.disabled ? ' disabled' : ''}`,
|
|
4723
4921
|
onclick: item.disabled ? undefined : () => onSelectItem(item),
|
|
4724
4922
|
}, m('span', {
|
|
4923
|
+
class: 'mm-layout-row mm-layout-row--center',
|
|
4725
4924
|
style: {
|
|
4726
|
-
display: 'flex',
|
|
4727
|
-
alignItems: 'center',
|
|
4728
4925
|
padding: '14px 16px',
|
|
4729
4926
|
},
|
|
4730
4927
|
}, [
|
|
4731
4928
|
item.iconName
|
|
4732
4929
|
? m('i.material-icons', {
|
|
4733
|
-
|
|
4930
|
+
class: 'mm-layout-item-icon',
|
|
4734
4931
|
}, item.iconName)
|
|
4735
4932
|
: undefined,
|
|
4736
4933
|
item.label,
|
|
@@ -4747,8 +4944,12 @@ const Dropdown = () => {
|
|
|
4747
4944
|
state.dropdownRef = null;
|
|
4748
4945
|
},
|
|
4749
4946
|
}, dropdownContent);
|
|
4750
|
-
|
|
4751
|
-
|
|
4947
|
+
syncPortalContent({
|
|
4948
|
+
containerId: `${state.id}-dropdown`,
|
|
4949
|
+
shouldRender: state.isOpen && !!state.inputRef,
|
|
4950
|
+
vnode: dropdownVnode,
|
|
4951
|
+
zIndex: 10000,
|
|
4952
|
+
});
|
|
4752
4953
|
};
|
|
4753
4954
|
return {
|
|
4754
4955
|
oninit: ({ attrs }) => {
|
|
@@ -4769,15 +4970,19 @@ const Dropdown = () => {
|
|
|
4769
4970
|
// Cleanup global listener
|
|
4770
4971
|
document.removeEventListener('click', closeDropdown);
|
|
4771
4972
|
// Cleanup portal
|
|
4772
|
-
|
|
4773
|
-
if (portalElement) {
|
|
4774
|
-
portalElement.remove();
|
|
4775
|
-
}
|
|
4973
|
+
syncPortalContent({ containerId: `${state.id}-dropdown`, shouldRender: false, vnode: null });
|
|
4776
4974
|
},
|
|
4777
4975
|
view: ({ attrs }) => {
|
|
4778
4976
|
const { checkedId, key, label, onchange, disabled = false, items, iconName, helperText, style, className = 'col s12', } = attrs;
|
|
4779
4977
|
const controlled = isControlled(attrs);
|
|
4780
|
-
const currentCheckedId =
|
|
4978
|
+
const currentCheckedId = resolveControllableValue({
|
|
4979
|
+
controlled,
|
|
4980
|
+
disabled,
|
|
4981
|
+
controlledValue: checkedId,
|
|
4982
|
+
defaultValue: attrs.defaultCheckedId,
|
|
4983
|
+
internalValue: state.internalCheckedId,
|
|
4984
|
+
fallbackValue: undefined,
|
|
4985
|
+
});
|
|
4781
4986
|
const handleSelection = (value) => {
|
|
4782
4987
|
// Update internal state for uncontrolled mode
|
|
4783
4988
|
if (!controlled) {
|
|
@@ -4809,6 +5014,7 @@ const Dropdown = () => {
|
|
|
4809
5014
|
iconName ? m('i.material-icons.prefix', iconName) : undefined,
|
|
4810
5015
|
m(HelperText, { helperText }),
|
|
4811
5016
|
m('.select-wrapper', {
|
|
5017
|
+
class: 'mm-layout-row mm-layout-row--center',
|
|
4812
5018
|
onkeydown: disabled
|
|
4813
5019
|
? undefined
|
|
4814
5020
|
: (e) => {
|
|
@@ -4876,15 +5082,14 @@ const Dropdown = () => {
|
|
|
4876
5082
|
handleSelection(value);
|
|
4877
5083
|
},
|
|
4878
5084
|
}, m('span', {
|
|
5085
|
+
class: 'mm-layout-row mm-layout-row--center',
|
|
4879
5086
|
style: {
|
|
4880
|
-
display: 'flex',
|
|
4881
|
-
alignItems: 'center',
|
|
4882
5087
|
padding: '14px 16px',
|
|
4883
5088
|
},
|
|
4884
5089
|
}, [
|
|
4885
5090
|
item.iconName
|
|
4886
5091
|
? m('i.material-icons', {
|
|
4887
|
-
|
|
5092
|
+
class: 'mm-layout-item-icon',
|
|
4888
5093
|
}, item.iconName)
|
|
4889
5094
|
: undefined,
|
|
4890
5095
|
item.label,
|
|
@@ -6841,9 +7046,9 @@ const initPushpins = (selector = '.pushpin', options = {}) => {
|
|
|
6841
7046
|
};
|
|
6842
7047
|
|
|
6843
7048
|
const RadioButton = () => ({
|
|
6844
|
-
view: ({ attrs: { id, groupId, label, onchange, className = 'col s12', checked, disabled, inputId } }) => {
|
|
7049
|
+
view: ({ attrs: { id, groupId, label, onchange, className = 'col s12', checked, disabled, inputId, allowHtml } }) => {
|
|
6845
7050
|
const radioId = inputId || `${groupId}-${id}`;
|
|
6846
|
-
return m('p', { className }, m('label', { for: radioId }, [
|
|
7051
|
+
return m('p', { className }, m('label', { for: radioId, style: { position: 'relative', display: 'inline-block' } }, [
|
|
6847
7052
|
m('input[type=radio][tabindex=0]', {
|
|
6848
7053
|
id: radioId,
|
|
6849
7054
|
name: groupId,
|
|
@@ -6851,7 +7056,7 @@ const RadioButton = () => ({
|
|
|
6851
7056
|
checked,
|
|
6852
7057
|
onclick: onchange ? () => onchange(id) : undefined,
|
|
6853
7058
|
}),
|
|
6854
|
-
m('span', m.trust(label)),
|
|
7059
|
+
m('span', allowHtml ? m.trust(label) : label),
|
|
6855
7060
|
]));
|
|
6856
7061
|
},
|
|
6857
7062
|
});
|
|
@@ -6878,8 +7083,9 @@ const RadioButtons = () => {
|
|
|
6878
7083
|
}
|
|
6879
7084
|
},
|
|
6880
7085
|
view: ({ attrs }) => {
|
|
6881
|
-
var _a, _b;
|
|
6882
|
-
const { checkedId, newRow, className = 'col s12', label = '', disabled, description, options, isMandatory, checkboxClass, layout =
|
|
7086
|
+
var _a, _b, _c;
|
|
7087
|
+
const { checkedId, newRow, className = 'col s12', label = '', disabled, description, options, isMandatory, checkboxClass, layout, direction, allowHtml = false, onchange, } = attrs;
|
|
7088
|
+
const resolvedLayout = (_a = layout !== null && layout !== void 0 ? layout : direction) !== null && _a !== void 0 ? _a : 'vertical';
|
|
6883
7089
|
const { groupId, componentId } = state;
|
|
6884
7090
|
const controlled = isControlled(attrs);
|
|
6885
7091
|
// Get current checked ID from props or internal state
|
|
@@ -6889,11 +7095,11 @@ const RadioButtons = () => {
|
|
|
6889
7095
|
}
|
|
6890
7096
|
else if (disabled) {
|
|
6891
7097
|
// Non-interactive components: prefer defaultCheckedId, fallback to checkedId
|
|
6892
|
-
currentCheckedId = (
|
|
7098
|
+
currentCheckedId = (_b = attrs.defaultCheckedId) !== null && _b !== void 0 ? _b : checkedId;
|
|
6893
7099
|
}
|
|
6894
7100
|
else {
|
|
6895
7101
|
// Interactive uncontrolled: use internal state
|
|
6896
|
-
currentCheckedId = (
|
|
7102
|
+
currentCheckedId = (_c = state.internalCheckedId) !== null && _c !== void 0 ? _c : attrs.defaultCheckedId;
|
|
6897
7103
|
}
|
|
6898
7104
|
const handleChange = (id) => {
|
|
6899
7105
|
// Update internal state for uncontrolled mode
|
|
@@ -6908,16 +7114,16 @@ const RadioButtons = () => {
|
|
|
6908
7114
|
const cn = [newRow ? 'clear' : '', className].filter(Boolean).join(' ').trim() || undefined;
|
|
6909
7115
|
const radioItems = options.map((r) => ({
|
|
6910
7116
|
component: (RadioButton),
|
|
6911
|
-
props: Object.assign(Object.assign({}, r), { onchange: handleChange, groupId, disabled: disabled || r.disabled, className: checkboxClass, checked: r.id === currentCheckedId, inputId: `${componentId}-${r.id}
|
|
7117
|
+
props: Object.assign(Object.assign({}, r), { onchange: handleChange, groupId, disabled: disabled || r.disabled, className: checkboxClass, checked: r.id === currentCheckedId, inputId: `${componentId}-${r.id}`, allowHtml }),
|
|
6912
7118
|
key: r.id,
|
|
6913
7119
|
}));
|
|
6914
7120
|
const optionsContent = m(OptionsList, {
|
|
6915
7121
|
options: radioItems,
|
|
6916
|
-
layout,
|
|
7122
|
+
layout: resolvedLayout,
|
|
6917
7123
|
});
|
|
6918
7124
|
return m('div', { id: componentId, className: cn }, [
|
|
6919
7125
|
label && m('h5.form-group-label', label + (isMandatory ? ' *' : '')),
|
|
6920
|
-
description && m('p.helper-text', m.trust(description)),
|
|
7126
|
+
description && m('p.helper-text', allowHtml ? m.trust(description) : description),
|
|
6921
7127
|
m('form', { action: '#' }, optionsContent),
|
|
6922
7128
|
]);
|
|
6923
7129
|
},
|
|
@@ -7054,6 +7260,26 @@ const Select = () => {
|
|
|
7054
7260
|
opacity: 1,
|
|
7055
7261
|
};
|
|
7056
7262
|
};
|
|
7263
|
+
const formatSelectionSummary = (attrs, selectedOptions, selectedIds, placeholder) => {
|
|
7264
|
+
const mode = attrs.summaryMode || 'labels';
|
|
7265
|
+
const selectedCount = selectedIds.length;
|
|
7266
|
+
const totalCount = attrs.options.filter((option) => !option.disabled).length;
|
|
7267
|
+
if (mode === 'labels') {
|
|
7268
|
+
return selectedOptions.length > 0 ? selectedOptions.map((o) => o.label || o.id).join(', ') : placeholder;
|
|
7269
|
+
}
|
|
7270
|
+
const defaultCountLabel = `${selectedCount}/${totalCount} selected`;
|
|
7271
|
+
const formattedCount = attrs.countLabel ? attrs.countLabel(selectedCount, totalCount) : defaultCountLabel;
|
|
7272
|
+
if (mode === 'count') {
|
|
7273
|
+
return selectedCount > 0 ? formattedCount : placeholder;
|
|
7274
|
+
}
|
|
7275
|
+
if (selectedCount === 0) {
|
|
7276
|
+
return attrs.noneSelectedLabel || placeholder;
|
|
7277
|
+
}
|
|
7278
|
+
if (totalCount > 0 && selectedCount >= totalCount) {
|
|
7279
|
+
return attrs.allSelectedLabel || 'All selected';
|
|
7280
|
+
}
|
|
7281
|
+
return formattedCount;
|
|
7282
|
+
};
|
|
7057
7283
|
const renderDropdownContent = (attrs, selectedIds, multiple, placeholder) => [
|
|
7058
7284
|
placeholder && m('li.disabled', { tabindex: 0 }, m('span', placeholder)),
|
|
7059
7285
|
// Render ungrouped options first
|
|
@@ -7122,17 +7348,6 @@ const Select = () => {
|
|
|
7122
7348
|
const updatePortalDropdown = (attrs, selectedIds, multiple, placeholder) => {
|
|
7123
7349
|
if (!state.isInsideModal)
|
|
7124
7350
|
return;
|
|
7125
|
-
// Clean up existing portal
|
|
7126
|
-
const existingPortal = document.getElementById(state.dropdownId);
|
|
7127
|
-
if (existingPortal) {
|
|
7128
|
-
existingPortal.remove();
|
|
7129
|
-
}
|
|
7130
|
-
if (!state.isOpen || !state.inputRef)
|
|
7131
|
-
return;
|
|
7132
|
-
// Create portal element
|
|
7133
|
-
const portalElement = document.createElement('div');
|
|
7134
|
-
portalElement.id = state.dropdownId;
|
|
7135
|
-
document.body.appendChild(portalElement);
|
|
7136
7351
|
// Create dropdown with proper positioning
|
|
7137
7352
|
const dropdownVnode = m('ul.dropdown-content.select-dropdown', {
|
|
7138
7353
|
tabindex: 0,
|
|
@@ -7144,8 +7359,12 @@ const Select = () => {
|
|
|
7144
7359
|
state.dropdownRef = null;
|
|
7145
7360
|
},
|
|
7146
7361
|
}, renderDropdownContent(attrs, selectedIds, multiple, placeholder));
|
|
7147
|
-
|
|
7148
|
-
|
|
7362
|
+
syncPortalContent({
|
|
7363
|
+
containerId: state.dropdownId,
|
|
7364
|
+
shouldRender: state.isOpen && !!state.inputRef,
|
|
7365
|
+
vnode: dropdownVnode,
|
|
7366
|
+
zIndex: 10000,
|
|
7367
|
+
});
|
|
7149
7368
|
};
|
|
7150
7369
|
return {
|
|
7151
7370
|
oninit: ({ attrs }) => {
|
|
@@ -7178,35 +7397,29 @@ const Select = () => {
|
|
|
7178
7397
|
document.removeEventListener('click', closeDropdown);
|
|
7179
7398
|
// Cleanup portaled dropdown if it exists
|
|
7180
7399
|
if (state.isInsideModal && state.dropdownRef) {
|
|
7181
|
-
|
|
7182
|
-
if (portalElement && portalElement.parentNode) {
|
|
7183
|
-
portalElement.parentNode.removeChild(portalElement);
|
|
7184
|
-
}
|
|
7400
|
+
syncPortalContent({ containerId: state.dropdownId, shouldRender: false, vnode: null });
|
|
7185
7401
|
}
|
|
7186
7402
|
},
|
|
7187
7403
|
view: ({ attrs }) => {
|
|
7188
|
-
var _a;
|
|
7189
7404
|
const controlled = isControlled(attrs);
|
|
7190
|
-
const { newRow, className = 'col s12', key, options = [], multiple = false, label, helperText, placeholder = '', isMandatory, iconName, style, disabled, } = attrs;
|
|
7405
|
+
const { newRow, className = 'col s12', key, options = [], multiple = false, label, helperText, placeholder = '', isMandatory, iconName, appearance = 'standard', style, disabled, } = attrs;
|
|
7191
7406
|
state.isMultiple = multiple;
|
|
7192
7407
|
// Get selected IDs from props or internal state
|
|
7193
|
-
|
|
7194
|
-
|
|
7195
|
-
|
|
7196
|
-
|
|
7197
|
-
|
|
7198
|
-
|
|
7199
|
-
|
|
7200
|
-
|
|
7201
|
-
|
|
7202
|
-
|
|
7203
|
-
|
|
7204
|
-
|
|
7205
|
-
selectedIds = state.internalSelectedIds;
|
|
7206
|
-
}
|
|
7207
|
-
const finalClassName = newRow ? `${className} clear` : className;
|
|
7408
|
+
const selectedIds = resolveControllableValue({
|
|
7409
|
+
controlled,
|
|
7410
|
+
disabled,
|
|
7411
|
+
controlledValue: normalizeSelection(attrs.checkedId),
|
|
7412
|
+
defaultValue: normalizeSelection(attrs.defaultCheckedId),
|
|
7413
|
+
internalValue: state.internalSelectedIds,
|
|
7414
|
+
fallbackValue: [],
|
|
7415
|
+
});
|
|
7416
|
+
const layoutClassName = newRow ? `${className} clear` : className;
|
|
7417
|
+
const appearanceClassName = appearance === 'outlined' ? 'select-appearance-outlined' : '';
|
|
7418
|
+
const finalClassName = [layoutClassName, appearanceClassName].filter(Boolean).join(' ');
|
|
7419
|
+
const shouldInlineLabel = appearance === 'outlined' && !!label;
|
|
7208
7420
|
const selectedOptionsUnsorted = options.filter((opt) => isSelected(opt.id, selectedIds));
|
|
7209
7421
|
const selectedOptions = sortOptions(selectedOptionsUnsorted, attrs.sortSelected);
|
|
7422
|
+
const triggerValue = formatSelectionSummary(attrs, selectedOptions, selectedIds, placeholder);
|
|
7210
7423
|
// Update portal dropdown when inside modal
|
|
7211
7424
|
if (state.isInsideModal) {
|
|
7212
7425
|
updatePortalDropdown(attrs, selectedIds, multiple, placeholder);
|
|
@@ -7226,9 +7439,15 @@ const Select = () => {
|
|
|
7226
7439
|
'aria-controls': state.dropdownId,
|
|
7227
7440
|
role: 'combobox',
|
|
7228
7441
|
}, [
|
|
7442
|
+
shouldInlineLabel &&
|
|
7443
|
+
m('span.select-inline-label', [
|
|
7444
|
+
label,
|
|
7445
|
+
isMandatory && m('span.mandatory', { style: { marginLeft: '2px' } }, '*'),
|
|
7446
|
+
]),
|
|
7229
7447
|
m('input[type=text][readonly=true].select-dropdown.dropdown-trigger', {
|
|
7230
7448
|
id: state.id,
|
|
7231
|
-
value:
|
|
7449
|
+
value: triggerValue,
|
|
7450
|
+
'aria-label': label,
|
|
7232
7451
|
oncreate: ({ dom }) => {
|
|
7233
7452
|
state.inputRef = dom;
|
|
7234
7453
|
},
|
|
@@ -7260,14 +7479,12 @@ const Select = () => {
|
|
|
7260
7479
|
}),
|
|
7261
7480
|
]),
|
|
7262
7481
|
// Label
|
|
7263
|
-
|
|
7264
|
-
|
|
7265
|
-
|
|
7266
|
-
|
|
7267
|
-
|
|
7268
|
-
|
|
7269
|
-
// Helper text
|
|
7270
|
-
helperText && m(HelperText, { helperText }),
|
|
7482
|
+
...renderFieldChrome({
|
|
7483
|
+
label: shouldInlineLabel ? undefined : label,
|
|
7484
|
+
id: state.id,
|
|
7485
|
+
isMandatory,
|
|
7486
|
+
helperText,
|
|
7487
|
+
}),
|
|
7271
7488
|
]);
|
|
7272
7489
|
},
|
|
7273
7490
|
};
|
|
@@ -7486,59 +7703,65 @@ const Tabs = () => {
|
|
|
7486
7703
|
};
|
|
7487
7704
|
};
|
|
7488
7705
|
|
|
7489
|
-
|
|
7490
|
-
|
|
7491
|
-
|
|
7492
|
-
|
|
7493
|
-
|
|
7494
|
-
|
|
7495
|
-
|
|
7496
|
-
|
|
7497
|
-
|
|
7498
|
-
|
|
7499
|
-
|
|
7500
|
-
|
|
7501
|
-
|
|
7502
|
-
|
|
7503
|
-
|
|
7504
|
-
|
|
7505
|
-
|
|
7506
|
-
|
|
7507
|
-
|
|
7508
|
-
|
|
7509
|
-
|
|
7510
|
-
|
|
7511
|
-
|
|
7512
|
-
|
|
7513
|
-
|
|
7706
|
+
const SelectedChip = ({ option, onRemove, }) => m('.chip', [
|
|
7707
|
+
option.label || option.id.toString(),
|
|
7708
|
+
m(MaterialIcon, {
|
|
7709
|
+
name: 'close',
|
|
7710
|
+
className: 'close',
|
|
7711
|
+
onclick: (e) => {
|
|
7712
|
+
e.stopPropagation();
|
|
7713
|
+
onRemove(option.id);
|
|
7714
|
+
},
|
|
7715
|
+
}),
|
|
7716
|
+
]);
|
|
7717
|
+
const DropdownOption = ({ option, index, optionId, selectedIds, isFocused, onToggle, onMouseOver, showCheckbox, }) => {
|
|
7718
|
+
const optionLabel = option.label || option.id.toString();
|
|
7719
|
+
return m('li', {
|
|
7720
|
+
id: optionId,
|
|
7721
|
+
role: 'option',
|
|
7722
|
+
'aria-selected': selectedIds.includes(option.id) ? 'true' : 'false',
|
|
7723
|
+
class: `${option.disabled ? 'disabled' : ''} ${isFocused ? 'active' : ''}`.trim(),
|
|
7724
|
+
onmouseover: () => {
|
|
7725
|
+
if (!option.disabled) {
|
|
7726
|
+
onMouseOver(index);
|
|
7727
|
+
}
|
|
7728
|
+
},
|
|
7729
|
+
}, m('label', {
|
|
7730
|
+
class: 'search-select-option-label',
|
|
7731
|
+
onclick: (e) => {
|
|
7732
|
+
// A single-select row has no native checkbox to emit change.
|
|
7733
|
+
if (!showCheckbox) {
|
|
7734
|
+
e.preventDefault();
|
|
7735
|
+
onToggle(option);
|
|
7736
|
+
}
|
|
7737
|
+
},
|
|
7738
|
+
}, [
|
|
7739
|
+
showCheckbox &&
|
|
7740
|
+
m('input', {
|
|
7741
|
+
type: 'checkbox',
|
|
7742
|
+
checked: selectedIds.includes(option.id),
|
|
7743
|
+
disabled: option.disabled,
|
|
7744
|
+
onchange: (e) => {
|
|
7514
7745
|
e.stopPropagation();
|
|
7515
7746
|
onToggle(option);
|
|
7516
7747
|
},
|
|
7517
|
-
|
|
7518
|
-
|
|
7519
|
-
|
|
7520
|
-
onMouseOver(index);
|
|
7521
|
-
}
|
|
7522
|
-
},
|
|
7523
|
-
}, m('label', { for: checkboxId, class: 'search-select-option-label' }, [
|
|
7524
|
-
showCheckbox &&
|
|
7525
|
-
m('input', {
|
|
7526
|
-
type: 'checkbox',
|
|
7527
|
-
id: checkboxId,
|
|
7528
|
-
checked: selectedIds.includes(option.id),
|
|
7529
|
-
}),
|
|
7530
|
-
m('span', optionLabel),
|
|
7531
|
-
]));
|
|
7532
|
-
},
|
|
7533
|
-
};
|
|
7748
|
+
}),
|
|
7749
|
+
m('span', optionLabel),
|
|
7750
|
+
]));
|
|
7534
7751
|
};
|
|
7535
7752
|
/**
|
|
7536
7753
|
* Mithril Factory Component for Multi-Select Dropdown with search
|
|
7537
7754
|
*/
|
|
7538
|
-
const SearchSelect = () => {
|
|
7755
|
+
const SearchSelect = (maybeVnode) => {
|
|
7756
|
+
var _a;
|
|
7757
|
+
const cachedInstance = (_a = maybeVnode === null || maybeVnode === void 0 ? void 0 : maybeVnode.state) === null || _a === void 0 ? void 0 : _a.__searchSelectInstance;
|
|
7758
|
+
if (cachedInstance) {
|
|
7759
|
+
return cachedInstance;
|
|
7760
|
+
}
|
|
7539
7761
|
// State initialization
|
|
7540
7762
|
const state = {
|
|
7541
7763
|
id: '',
|
|
7764
|
+
listboxId: '',
|
|
7542
7765
|
isOpen: false,
|
|
7543
7766
|
searchTerm: '',
|
|
7544
7767
|
inputRef: null,
|
|
@@ -7546,17 +7769,38 @@ const SearchSelect = () => {
|
|
|
7546
7769
|
focusedIndex: -1,
|
|
7547
7770
|
internalSelectedIds: [],
|
|
7548
7771
|
createdOptions: [],
|
|
7549
|
-
|
|
7772
|
+
asyncOptions: [],
|
|
7773
|
+
isLoading: false,
|
|
7774
|
+
loadError: null,
|
|
7775
|
+
latestRequestId: 0,
|
|
7776
|
+
};
|
|
7777
|
+
const updateAsyncState = (nextState) => {
|
|
7778
|
+
state.asyncOptions = nextState.options;
|
|
7779
|
+
state.isLoading = nextState.isLoading;
|
|
7780
|
+
state.loadError = nextState.error;
|
|
7781
|
+
state.latestRequestId = nextState.latestRequestId;
|
|
7782
|
+
};
|
|
7783
|
+
const readAsyncState = () => ({
|
|
7784
|
+
options: state.asyncOptions,
|
|
7785
|
+
isLoading: state.isLoading,
|
|
7786
|
+
error: state.loadError,
|
|
7787
|
+
latestRequestId: state.latestRequestId,
|
|
7788
|
+
});
|
|
7550
7789
|
const isControlled = (attrs) => attrs.checkedId !== undefined && typeof attrs.onchange === 'function';
|
|
7551
7790
|
const componentId = uniqueId();
|
|
7552
7791
|
const searchInputId = `${componentId}-search`;
|
|
7553
7792
|
// Handle click outside
|
|
7554
7793
|
const handleClickOutside = (e) => {
|
|
7555
7794
|
const target = e.target;
|
|
7795
|
+
const targetElement = e.target instanceof Element ? e.target : null;
|
|
7556
7796
|
if (state.dropdownRef && state.dropdownRef.contains(target)) {
|
|
7557
7797
|
// Click inside dropdown, do nothing
|
|
7558
7798
|
return;
|
|
7559
7799
|
}
|
|
7800
|
+
if (targetElement && targetElement.closest('.chips-container')) {
|
|
7801
|
+
// Click on trigger, do nothing
|
|
7802
|
+
return;
|
|
7803
|
+
}
|
|
7560
7804
|
if (state.inputRef && state.inputRef.contains(target)) {
|
|
7561
7805
|
// Click on trigger handled by onclick event
|
|
7562
7806
|
return;
|
|
@@ -7567,40 +7811,51 @@ const SearchSelect = () => {
|
|
|
7567
7811
|
}
|
|
7568
7812
|
m.redraw();
|
|
7569
7813
|
};
|
|
7570
|
-
// Handle keyboard navigation
|
|
7571
|
-
const handleKeyDown = (e,
|
|
7572
|
-
|
|
7814
|
+
// Handle keyboard navigation through shared combobox primitive.
|
|
7815
|
+
const handleKeyDown = (e, optionCount, includeActionRow) => {
|
|
7816
|
+
const result = getComboboxKeyResult({
|
|
7817
|
+
key: e.key,
|
|
7818
|
+
isOpen: state.isOpen,
|
|
7819
|
+
focusedIndex: state.focusedIndex,
|
|
7820
|
+
optionCount,
|
|
7821
|
+
includeActionRow,
|
|
7822
|
+
});
|
|
7823
|
+
if (result.preventDefault) {
|
|
7824
|
+
e.preventDefault();
|
|
7825
|
+
}
|
|
7826
|
+
state.isOpen = result.isOpen;
|
|
7827
|
+
state.focusedIndex = result.focusedIndex;
|
|
7828
|
+
return result.action;
|
|
7829
|
+
};
|
|
7830
|
+
const loadAsyncOptions = async (attrs, query) => {
|
|
7831
|
+
if (!attrs.loadOptions) {
|
|
7573
7832
|
return;
|
|
7574
|
-
|
|
7575
|
-
|
|
7576
|
-
|
|
7577
|
-
|
|
7578
|
-
|
|
7579
|
-
|
|
7580
|
-
|
|
7581
|
-
|
|
7582
|
-
|
|
7583
|
-
|
|
7584
|
-
|
|
7585
|
-
|
|
7586
|
-
if (state.focusedIndex >= 0) {
|
|
7587
|
-
if (showAddNew && state.focusedIndex === filteredOptions.length) {
|
|
7588
|
-
// Handle add new option
|
|
7589
|
-
return 'addNew';
|
|
7590
|
-
}
|
|
7591
|
-
else if (state.focusedIndex < filteredOptions.length) {
|
|
7592
|
-
// This will be handled in the view method where attrs are available
|
|
7593
|
-
return 'selectOption';
|
|
7594
|
-
}
|
|
7595
|
-
}
|
|
7596
|
-
break;
|
|
7597
|
-
case 'Escape':
|
|
7598
|
-
e.preventDefault();
|
|
7599
|
-
state.isOpen = false;
|
|
7833
|
+
}
|
|
7834
|
+
const started = startAsyncComboboxRequest(readAsyncState());
|
|
7835
|
+
updateAsyncState(started.nextState);
|
|
7836
|
+
m.redraw();
|
|
7837
|
+
try {
|
|
7838
|
+
const loadedOptions = await attrs.loadOptions(query);
|
|
7839
|
+
const resolved = resolveAsyncComboboxRequest(readAsyncState(), started.requestId, loadedOptions);
|
|
7840
|
+
updateAsyncState(resolved);
|
|
7841
|
+
if (started.requestId !== state.latestRequestId) {
|
|
7842
|
+
return;
|
|
7843
|
+
}
|
|
7844
|
+
if (state.focusedIndex >= loadedOptions.length) {
|
|
7600
7845
|
state.focusedIndex = -1;
|
|
7601
|
-
|
|
7846
|
+
}
|
|
7847
|
+
}
|
|
7848
|
+
catch (error) {
|
|
7849
|
+
const rejected = rejectAsyncComboboxRequest(readAsyncState(), started.requestId, error instanceof Error ? error.message : 'Unable to load options');
|
|
7850
|
+
updateAsyncState(rejected);
|
|
7851
|
+
if (started.requestId !== state.latestRequestId) {
|
|
7852
|
+
return;
|
|
7853
|
+
}
|
|
7854
|
+
state.focusedIndex = -1;
|
|
7855
|
+
}
|
|
7856
|
+
finally {
|
|
7857
|
+
m.redraw();
|
|
7602
7858
|
}
|
|
7603
|
-
return null;
|
|
7604
7859
|
};
|
|
7605
7860
|
// Create new option and add to state
|
|
7606
7861
|
const createAndSelectOption = async (attrs) => {
|
|
@@ -7681,9 +7936,10 @@ const SearchSelect = () => {
|
|
|
7681
7936
|
attrs.onchange(newIds);
|
|
7682
7937
|
}
|
|
7683
7938
|
};
|
|
7684
|
-
|
|
7939
|
+
const componentInstance = {
|
|
7685
7940
|
oninit: ({ attrs }) => {
|
|
7686
7941
|
state.id = attrs.id || uniqueId();
|
|
7942
|
+
state.listboxId = `${state.id}-listbox`;
|
|
7687
7943
|
// Initialize internal state for uncontrolled mode
|
|
7688
7944
|
if (!isControlled(attrs)) {
|
|
7689
7945
|
const defaultIds = attrs.defaultCheckedId !== undefined
|
|
@@ -7710,20 +7966,26 @@ const SearchSelect = () => {
|
|
|
7710
7966
|
: [attrs.checkedId]
|
|
7711
7967
|
: []
|
|
7712
7968
|
: state.internalSelectedIds;
|
|
7713
|
-
const { options = [], oncreateNewOption, className, placeholder, searchPlaceholder = 'Search options...', noOptionsFound = 'No options found', label, i18n = {}, maxDisplayedOptions, maxSelectedOptions, maxHeight, } = attrs;
|
|
7969
|
+
const { options = [], loadOptions, oncreateNewOption, className, placeholder, searchPlaceholder = 'Search options...', noOptionsFound = 'No options found', label, i18n = {}, maxDisplayedOptions, maxSelectedOptions, maxHeight, } = attrs;
|
|
7714
7970
|
// Use i18n values if provided, otherwise use defaults
|
|
7715
7971
|
const texts = {
|
|
7716
7972
|
noOptionsFound: i18n.noOptionsFound || noOptionsFound,
|
|
7973
|
+
loadingOptions: i18n.loadingOptions || 'Loading options...',
|
|
7974
|
+
loadingError: i18n.loadingError || 'Unable to load options',
|
|
7717
7975
|
addNewPrefix: i18n.addNewPrefix || '+',
|
|
7718
7976
|
showingXofY: i18n.showingXofY || 'Showing {shown} of {total} options',
|
|
7719
7977
|
maxSelectionsReached: i18n.maxSelectionsReached || 'Maximum {max} selections reached',
|
|
7720
7978
|
};
|
|
7721
7979
|
// Check if max selections is reached
|
|
7722
7980
|
const isMaxSelectionsReached = maxSelectedOptions && selectedIds.length >= maxSelectedOptions;
|
|
7723
|
-
//
|
|
7724
|
-
const
|
|
7981
|
+
// In async mode, the active list is sourced remotely.
|
|
7982
|
+
const sourceOptions = loadOptions ? state.asyncOptions : options;
|
|
7983
|
+
// Merge active options with internally created options
|
|
7984
|
+
const allOptions = [...sourceOptions, ...state.createdOptions];
|
|
7985
|
+
// Keep selected label lookups stable across static, async, and created sets.
|
|
7986
|
+
const lookupOptions = [...options, ...state.asyncOptions, ...state.createdOptions];
|
|
7725
7987
|
// Get selected options for display
|
|
7726
|
-
const selectedOptionsUnsorted =
|
|
7988
|
+
const selectedOptionsUnsorted = lookupOptions.filter((opt) => selectedIds.includes(opt.id));
|
|
7727
7989
|
const selectedOptions = sortOptions(selectedOptionsUnsorted, attrs.sortSelected);
|
|
7728
7990
|
// Safely filter options
|
|
7729
7991
|
const filteredOptions = allOptions.filter((option) => (option.label || option.id.toString()).toLowerCase().includes((state.searchTerm || '').toLowerCase()) &&
|
|
@@ -7736,6 +7998,14 @@ const SearchSelect = () => {
|
|
|
7736
7998
|
const showAddNew = oncreateNewOption &&
|
|
7737
7999
|
state.searchTerm &&
|
|
7738
8000
|
!displayedOptions.some((o) => (o.label || o.id.toString()).toLowerCase() === state.searchTerm.toLowerCase());
|
|
8001
|
+
const activeDescendantId = state.isOpen && state.focusedIndex >= 0 && state.focusedIndex < displayedOptions.length
|
|
8002
|
+
? getComboboxOptionId(state.id, state.focusedIndex)
|
|
8003
|
+
: undefined;
|
|
8004
|
+
const viewState = getComboboxViewState({
|
|
8005
|
+
isLoading: state.isLoading,
|
|
8006
|
+
error: state.loadError,
|
|
8007
|
+
optionCount: displayedOptions.length,
|
|
8008
|
+
});
|
|
7739
8009
|
// Render the dropdown
|
|
7740
8010
|
return m('.input-field.multi-select-dropdown', { className }, [
|
|
7741
8011
|
m('.chips.chips-initial.chips-container', {
|
|
@@ -7746,14 +8016,34 @@ const SearchSelect = () => {
|
|
|
7746
8016
|
// console.log('SearchSelect clicked', state.isOpen, e); // Debug log
|
|
7747
8017
|
e.preventDefault();
|
|
7748
8018
|
e.stopPropagation();
|
|
8019
|
+
const wasOpen = state.isOpen;
|
|
7749
8020
|
state.isOpen = !state.isOpen;
|
|
8021
|
+
if (!wasOpen && state.isOpen && loadOptions) {
|
|
8022
|
+
void loadAsyncOptions(attrs, state.searchTerm);
|
|
8023
|
+
}
|
|
7750
8024
|
// console.log('SearchSelect state changed to', state.isOpen); // Debug log
|
|
7751
8025
|
},
|
|
7752
|
-
|
|
8026
|
+
onkeydown: async (e) => {
|
|
8027
|
+
const action = handleKeyDown(e, displayedOptions.length, !!showAddNew);
|
|
8028
|
+
if (action === 'open' && loadOptions) {
|
|
8029
|
+
await loadAsyncOptions(attrs, state.searchTerm);
|
|
8030
|
+
return;
|
|
8031
|
+
}
|
|
8032
|
+
if (action === 'selectAction' && oncreateNewOption) {
|
|
8033
|
+
await createAndSelectOption(attrs);
|
|
8034
|
+
}
|
|
8035
|
+
if (action === 'selectFocused' && state.focusedIndex < displayedOptions.length) {
|
|
8036
|
+
toggleOption(displayedOptions[state.focusedIndex], attrs);
|
|
8037
|
+
}
|
|
8038
|
+
},
|
|
8039
|
+
class: 'chips chips-container mm-layout-row mm-layout-row--wrap mm-layout-row--align-end',
|
|
8040
|
+
role: 'combobox',
|
|
8041
|
+
tabindex: 0,
|
|
8042
|
+
'aria-expanded': state.isOpen ? 'true' : 'false',
|
|
8043
|
+
'aria-haspopup': 'listbox',
|
|
8044
|
+
'aria-controls': state.isOpen ? state.listboxId : undefined,
|
|
8045
|
+
'aria-activedescendant': activeDescendantId,
|
|
7753
8046
|
style: {
|
|
7754
|
-
display: 'flex',
|
|
7755
|
-
alignItems: 'end',
|
|
7756
|
-
flexWrap: 'wrap',
|
|
7757
8047
|
cursor: 'pointer',
|
|
7758
8048
|
position: 'relative',
|
|
7759
8049
|
},
|
|
@@ -7766,30 +8056,41 @@ const SearchSelect = () => {
|
|
|
7766
8056
|
value: selectedOptions.map((o) => o.label || o.id.toString()).join(', '),
|
|
7767
8057
|
readonly: true,
|
|
7768
8058
|
class: 'sr-only',
|
|
7769
|
-
style: {
|
|
8059
|
+
style: {
|
|
8060
|
+
position: 'absolute',
|
|
8061
|
+
width: '1px',
|
|
8062
|
+
height: '1px',
|
|
8063
|
+
margin: '-1px',
|
|
8064
|
+
padding: 0,
|
|
8065
|
+
border: 0,
|
|
8066
|
+
overflow: 'hidden',
|
|
8067
|
+
clip: 'rect(0 0 0 0)',
|
|
8068
|
+
clipPath: 'inset(50%)',
|
|
8069
|
+
whiteSpace: 'nowrap',
|
|
8070
|
+
},
|
|
7770
8071
|
}),
|
|
7771
8072
|
// Selected Options (chips)
|
|
7772
|
-
...selectedOptions.map((option) =>
|
|
7773
|
-
option,
|
|
8073
|
+
...selectedOptions.map((option) => SelectedChip({
|
|
8074
|
+
option: option,
|
|
7774
8075
|
onRemove: (id) => removeOption(id, attrs),
|
|
7775
8076
|
})),
|
|
7776
8077
|
// Placeholder when no options selected
|
|
7777
8078
|
selectedOptions.length === 0 &&
|
|
7778
8079
|
placeholder &&
|
|
7779
8080
|
m('span.placeholder', {
|
|
8081
|
+
class: 'mm-layout-grow',
|
|
7780
8082
|
style: {
|
|
7781
8083
|
color: 'var(--mm-text-hint, #9e9e9e)',
|
|
7782
|
-
flexGrow: 1,
|
|
7783
8084
|
padding: '8px 0',
|
|
7784
8085
|
},
|
|
7785
8086
|
}, placeholder),
|
|
7786
8087
|
// Spacer to push caret to the right
|
|
7787
|
-
m('span.spacer'
|
|
8088
|
+
m('span.spacer.mm-layout-grow'),
|
|
7788
8089
|
m(MaterialIcon, {
|
|
7789
8090
|
name: 'caret',
|
|
7790
8091
|
direction: state.isOpen ? 'up' : 'down',
|
|
7791
|
-
class: 'caret',
|
|
7792
|
-
style: {
|
|
8092
|
+
class: 'caret mm-layout-ml-auto',
|
|
8093
|
+
style: { cursor: 'pointer' },
|
|
7793
8094
|
}),
|
|
7794
8095
|
]),
|
|
7795
8096
|
// Label
|
|
@@ -7801,6 +8102,8 @@ const SearchSelect = () => {
|
|
|
7801
8102
|
// Dropdown Menu
|
|
7802
8103
|
state.isOpen &&
|
|
7803
8104
|
m('ul.dropdown-content.select-dropdown', {
|
|
8105
|
+
id: state.listboxId,
|
|
8106
|
+
role: 'listbox',
|
|
7804
8107
|
oncreate: ({ dom }) => {
|
|
7805
8108
|
state.dropdownRef = dom;
|
|
7806
8109
|
},
|
|
@@ -7825,23 +8128,39 @@ const SearchSelect = () => {
|
|
|
7825
8128
|
oninput: (e) => {
|
|
7826
8129
|
state.searchTerm = e.target.value;
|
|
7827
8130
|
state.focusedIndex = -1; // Reset focus when typing
|
|
8131
|
+
if (loadOptions) {
|
|
8132
|
+
void loadAsyncOptions(attrs, state.searchTerm);
|
|
8133
|
+
}
|
|
7828
8134
|
},
|
|
7829
8135
|
onkeydown: async (e) => {
|
|
7830
|
-
const
|
|
7831
|
-
if (
|
|
8136
|
+
const action = handleKeyDown(e, displayedOptions.length, !!showAddNew);
|
|
8137
|
+
if (action === 'open' && loadOptions) {
|
|
8138
|
+
await loadAsyncOptions(attrs, state.searchTerm);
|
|
8139
|
+
}
|
|
8140
|
+
else if (action === 'selectAction' && oncreateNewOption) {
|
|
7832
8141
|
await createAndSelectOption(attrs);
|
|
7833
8142
|
}
|
|
7834
|
-
else if (
|
|
8143
|
+
else if (action === 'selectFocused' && state.focusedIndex < displayedOptions.length) {
|
|
7835
8144
|
toggleOption(displayedOptions[state.focusedIndex], attrs);
|
|
7836
8145
|
}
|
|
7837
8146
|
},
|
|
7838
8147
|
class: 'search-select-input',
|
|
8148
|
+
'aria-autocomplete': 'list',
|
|
8149
|
+
'aria-controls': state.listboxId,
|
|
7839
8150
|
}),
|
|
7840
8151
|
]),
|
|
7841
|
-
//
|
|
7842
|
-
...(
|
|
7843
|
-
? [m('li.search-select-
|
|
8152
|
+
// Async loading status
|
|
8153
|
+
...(viewState === 'loading'
|
|
8154
|
+
? [m('li.search-select-loading-info', { role: 'status', 'aria-live': 'polite' }, texts.loadingOptions)]
|
|
8155
|
+
: []),
|
|
8156
|
+
// Async loading error
|
|
8157
|
+
...(viewState === 'error' && state.loadError
|
|
8158
|
+
? [
|
|
8159
|
+
m('li.search-select-error-info', { role: 'status', 'aria-live': 'assertive' }, `${texts.loadingError}: ${state.loadError}`),
|
|
8160
|
+
]
|
|
7844
8161
|
: []),
|
|
8162
|
+
// No options found message or list of options
|
|
8163
|
+
...(viewState === 'empty' && !showAddNew ? [m('li.search-select-no-options', texts.noOptionsFound)] : []),
|
|
7845
8164
|
// Truncation message
|
|
7846
8165
|
...(isTruncated
|
|
7847
8166
|
? [
|
|
@@ -7874,6 +8193,9 @@ const SearchSelect = () => {
|
|
|
7874
8193
|
...(showAddNew
|
|
7875
8194
|
? [
|
|
7876
8195
|
m('li', {
|
|
8196
|
+
id: `${state.listboxId}-action`,
|
|
8197
|
+
role: 'option',
|
|
8198
|
+
'aria-selected': 'false',
|
|
7877
8199
|
onclick: async () => {
|
|
7878
8200
|
await createAndSelectOption(attrs);
|
|
7879
8201
|
},
|
|
@@ -7885,9 +8207,10 @@ const SearchSelect = () => {
|
|
|
7885
8207
|
]
|
|
7886
8208
|
: []),
|
|
7887
8209
|
// List of filtered options
|
|
7888
|
-
...displayedOptions.map((option, index) =>
|
|
8210
|
+
...displayedOptions.map((option, index) => DropdownOption({
|
|
7889
8211
|
option,
|
|
7890
8212
|
index,
|
|
8213
|
+
optionId: getComboboxOptionId(state.id, index),
|
|
7891
8214
|
selectedIds,
|
|
7892
8215
|
isFocused: state.focusedIndex === index,
|
|
7893
8216
|
onToggle: (opt) => toggleOption(opt, attrs),
|
|
@@ -7900,6 +8223,10 @@ const SearchSelect = () => {
|
|
|
7900
8223
|
]);
|
|
7901
8224
|
},
|
|
7902
8225
|
};
|
|
8226
|
+
if (maybeVnode === null || maybeVnode === void 0 ? void 0 : maybeVnode.state) {
|
|
8227
|
+
maybeVnode.state.__searchSelectInstance = componentInstance;
|
|
8228
|
+
}
|
|
8229
|
+
return componentInstance;
|
|
7903
8230
|
};
|
|
7904
8231
|
|
|
7905
8232
|
const defaultI18n$2 = {
|
|
@@ -9273,6 +9600,53 @@ const FileUpload = () => {
|
|
|
9273
9600
|
};
|
|
9274
9601
|
};
|
|
9275
9602
|
|
|
9603
|
+
/** A semantic native fieldset for related controls. */
|
|
9604
|
+
const Fieldset = () => ({
|
|
9605
|
+
view: ({ attrs, children }) => {
|
|
9606
|
+
const { legend, description, required, disabled, error, className } = attrs, params = __rest(attrs, ["legend", "description", "required", "disabled", "error", "className"]);
|
|
9607
|
+
const descriptionId = params.id ? `${params.id}-description` : undefined;
|
|
9608
|
+
const errorId = params.id ? `${params.id}-error` : undefined;
|
|
9609
|
+
const describedBy = [description && descriptionId, error && errorId].filter(Boolean).join(' ') || undefined;
|
|
9610
|
+
return m('fieldset.mm-fieldset', Object.assign(Object.assign({}, params), { className, disabled, 'aria-describedby': describedBy }), [
|
|
9611
|
+
m('legend.mm-fieldset__legend', [legend, required && m('span.mm-fieldset__required[aria-hidden=true]', ' *')]),
|
|
9612
|
+
description && m('p.mm-fieldset__description', { id: descriptionId }, description),
|
|
9613
|
+
children,
|
|
9614
|
+
error && m('p.mm-fieldset__error[role=alert]', { id: errorId }, error),
|
|
9615
|
+
]);
|
|
9616
|
+
},
|
|
9617
|
+
});
|
|
9618
|
+
const focusErrorTarget = (fieldId) => {
|
|
9619
|
+
var _a;
|
|
9620
|
+
if (!fieldId || typeof document === 'undefined')
|
|
9621
|
+
return;
|
|
9622
|
+
(_a = document.getElementById(fieldId)) === null || _a === void 0 ? void 0 : _a.focus();
|
|
9623
|
+
};
|
|
9624
|
+
/** A visual form section with an optional accessible validation summary. */
|
|
9625
|
+
const FormSection = () => ({
|
|
9626
|
+
view: ({ attrs, children }) => {
|
|
9627
|
+
const { title, description, errors = [], summaryTitle = 'Please correct the following errors', className } = attrs, params = __rest(attrs, ["title", "description", "errors", "summaryTitle", "className"]);
|
|
9628
|
+
return m('section.mm-form-section', Object.assign(Object.assign({}, params), { className }), [
|
|
9629
|
+
(title || description) &&
|
|
9630
|
+
m('.mm-form-section__header', [
|
|
9631
|
+
title && m('h3.mm-form-section__title', title),
|
|
9632
|
+
description && m('p.mm-form-section__description', description),
|
|
9633
|
+
]),
|
|
9634
|
+
errors.length > 0 &&
|
|
9635
|
+
m('div.mm-validation-summary[role=alert][aria-live=assertive]', [
|
|
9636
|
+
m('p.mm-validation-summary__title', summaryTitle),
|
|
9637
|
+
m('ul.mm-validation-summary__list', errors.map((error, index) => {
|
|
9638
|
+
var _a, _b, _c;
|
|
9639
|
+
const href = (_a = error.href) !== null && _a !== void 0 ? _a : (error.fieldId ? `#${error.fieldId}` : undefined);
|
|
9640
|
+
return m('li', { key: `${(_c = (_b = error.fieldId) !== null && _b !== void 0 ? _b : href) !== null && _c !== void 0 ? _c : 'error'}-${index}` }, href
|
|
9641
|
+
? m('a', { href, onclick: () => focusErrorTarget(error.fieldId) }, error.message)
|
|
9642
|
+
: error.message);
|
|
9643
|
+
})),
|
|
9644
|
+
]),
|
|
9645
|
+
children,
|
|
9646
|
+
]);
|
|
9647
|
+
},
|
|
9648
|
+
});
|
|
9649
|
+
|
|
9276
9650
|
// List of MaterialIcon SVG icons that are available
|
|
9277
9651
|
const materialIconSvgNames = [
|
|
9278
9652
|
'caret',
|
|
@@ -9295,6 +9669,7 @@ const materialIconSvgNames = [
|
|
|
9295
9669
|
const renderIcon = (icon, style) => {
|
|
9296
9670
|
if (!icon)
|
|
9297
9671
|
return null;
|
|
9672
|
+
const objectStyle = typeof style === 'string' ? undefined : style;
|
|
9298
9673
|
if (typeof icon === 'string') {
|
|
9299
9674
|
// Check if this is a MaterialIcon SVG name
|
|
9300
9675
|
if (materialIconSvgNames.includes(icon)) {
|
|
@@ -9311,7 +9686,7 @@ const renderIcon = (icon, style) => {
|
|
|
9311
9686
|
// Image URL
|
|
9312
9687
|
return m('img', {
|
|
9313
9688
|
src: icon.content,
|
|
9314
|
-
style: Object.assign(Object.assign({},
|
|
9689
|
+
style: Object.assign(Object.assign({}, objectStyle), { width: '24px', height: '24px', objectFit: 'contain' }),
|
|
9315
9690
|
});
|
|
9316
9691
|
}
|
|
9317
9692
|
return null;
|
|
@@ -9322,8 +9697,10 @@ const renderIcon = (icon, style) => {
|
|
|
9322
9697
|
const SidenavHeaderFooterItem = () => {
|
|
9323
9698
|
return {
|
|
9324
9699
|
view: ({ attrs }) => {
|
|
9325
|
-
const { text, icon, onclick, href, className = '', _isExpanded = true, _position = 'left' } = attrs;
|
|
9700
|
+
const { text, icon, onclick, href, className = '', title, tooltip, tooltipWhenCollapsedOnly = true, _isExpanded = true, _position = 'left', } = attrs;
|
|
9326
9701
|
const isRightAligned = _position === 'right';
|
|
9702
|
+
const tooltipText = title || tooltip || text;
|
|
9703
|
+
const shouldShowTooltip = tooltipWhenCollapsedOnly ? !_isExpanded : true;
|
|
9327
9704
|
const handleClick = (e) => {
|
|
9328
9705
|
if (onclick) {
|
|
9329
9706
|
e.preventDefault();
|
|
@@ -9332,24 +9709,25 @@ const SidenavHeaderFooterItem = () => {
|
|
|
9332
9709
|
};
|
|
9333
9710
|
const content = isRightAligned
|
|
9334
9711
|
? [
|
|
9335
|
-
_isExpanded &&
|
|
9336
|
-
m('span.sidenav-item-text', { style: { flex: '1', 'text-align': 'left', 'margin-right': '8px' } }, text),
|
|
9712
|
+
_isExpanded && m('span.sidenav-item-text.mm-layout-grow.mm-layout-mr-8.left-align', text),
|
|
9337
9713
|
renderIcon(icon, { 'min-width': '24px', width: '24px' }),
|
|
9338
9714
|
]
|
|
9339
9715
|
: [
|
|
9340
9716
|
renderIcon(icon, { 'min-width': '24px', width: '24px' }),
|
|
9341
|
-
_isExpanded && m('span.sidenav-item-text
|
|
9717
|
+
_isExpanded && m('span.sidenav-item-text.mm-layout-grow.mm-layout-ml-8', text),
|
|
9342
9718
|
];
|
|
9343
9719
|
const linkStyle = {
|
|
9344
|
-
display: 'flex',
|
|
9345
|
-
'align-items': 'center',
|
|
9346
9720
|
padding: _isExpanded ? '12px 16px' : '12px 18px',
|
|
9347
9721
|
'justify-content': _isExpanded ? (isRightAligned ? 'flex-end' : 'flex-start') : 'center',
|
|
9348
9722
|
};
|
|
9723
|
+
const linkClass = 'mm-layout-row mm-layout-row--center';
|
|
9349
9724
|
return m('li', { class: className }, m('a', {
|
|
9350
9725
|
href: href || '#!',
|
|
9351
9726
|
onclick: handleClick,
|
|
9727
|
+
class: linkClass,
|
|
9352
9728
|
style: linkStyle,
|
|
9729
|
+
title: shouldShowTooltip ? tooltipText : undefined,
|
|
9730
|
+
'aria-label': shouldShowTooltip ? tooltipText : undefined,
|
|
9353
9731
|
}, content));
|
|
9354
9732
|
},
|
|
9355
9733
|
};
|
|
@@ -9371,9 +9749,9 @@ const Sidenav = () => {
|
|
|
9371
9749
|
m.redraw();
|
|
9372
9750
|
}
|
|
9373
9751
|
};
|
|
9374
|
-
const setBodyOverflow = (isOpen, mode) => {
|
|
9752
|
+
const setBodyOverflow = (isOpen, mode, fixed) => {
|
|
9375
9753
|
if (typeof document !== 'undefined') {
|
|
9376
|
-
document.body.style.overflow = isOpen && mode === 'overlay' ? 'hidden' : '';
|
|
9754
|
+
document.body.style.overflow = isOpen && mode === 'overlay' && !fixed ? 'hidden' : '';
|
|
9377
9755
|
}
|
|
9378
9756
|
};
|
|
9379
9757
|
const toggleExpanded = (attrs) => {
|
|
@@ -9409,7 +9787,7 @@ const Sidenav = () => {
|
|
|
9409
9787
|
if (wasOpen !== isOpen) {
|
|
9410
9788
|
state.isOpen = isOpen;
|
|
9411
9789
|
state.isAnimating = true;
|
|
9412
|
-
setBodyOverflow(isOpen, attrs.mode || 'overlay');
|
|
9790
|
+
setBodyOverflow(isOpen, attrs.mode || 'overlay', attrs.fixed || false);
|
|
9413
9791
|
// Clear animation state after animation completes
|
|
9414
9792
|
setTimeout(() => {
|
|
9415
9793
|
state.isAnimating = false;
|
|
@@ -9419,7 +9797,7 @@ const Sidenav = () => {
|
|
|
9419
9797
|
},
|
|
9420
9798
|
onremove: ({ attrs }) => {
|
|
9421
9799
|
// Clean up
|
|
9422
|
-
setBodyOverflow(false, attrs.mode || 'overlay');
|
|
9800
|
+
setBodyOverflow(false, attrs.mode || 'overlay', attrs.fixed || false);
|
|
9423
9801
|
if (typeof document !== 'undefined' && attrs.closeOnEscape !== false) {
|
|
9424
9802
|
document.removeEventListener('keydown', (e) => handleEscapeKey(e, attrs));
|
|
9425
9803
|
}
|
|
@@ -9448,7 +9826,9 @@ const Sidenav = () => {
|
|
|
9448
9826
|
class: [
|
|
9449
9827
|
position === 'right' ? 'right-aligned' : '',
|
|
9450
9828
|
fixed ? 'sidenav-fixed' : '',
|
|
9829
|
+
mode === 'push' ? 'sidenav-push' : '',
|
|
9451
9830
|
expandable && !isExpanded ? 'sidenav-collapsed' : '',
|
|
9831
|
+
'mm-layout-stack',
|
|
9452
9832
|
className,
|
|
9453
9833
|
]
|
|
9454
9834
|
.filter(Boolean)
|
|
@@ -9460,13 +9840,22 @@ const Sidenav = () => {
|
|
|
9460
9840
|
'transition-property': 'transform, width',
|
|
9461
9841
|
},
|
|
9462
9842
|
}, [
|
|
9843
|
+
// Header content slot (rendered first, before hamburger)
|
|
9844
|
+
attrs.headerContent &&
|
|
9845
|
+
m('li.sidenav-header-slot', {
|
|
9846
|
+
style: {
|
|
9847
|
+
'flex-shrink': 0,
|
|
9848
|
+
'list-style': 'none',
|
|
9849
|
+
height: 'auto',
|
|
9850
|
+
'line-height': 'normal',
|
|
9851
|
+
padding: 0,
|
|
9852
|
+
},
|
|
9853
|
+
}, attrs.headerContent),
|
|
9463
9854
|
// Hamburger toggle button (inside sidenav, at the top)
|
|
9464
9855
|
showHamburger &&
|
|
9465
9856
|
m('li.sidenav-hamburger-item', {
|
|
9857
|
+
class: `mm-layout-row mm-layout-row--center ${position === 'right' ? 'mm-layout-row--justify-end' : 'mm-layout-row--justify-start'}`,
|
|
9466
9858
|
style: {
|
|
9467
|
-
display: 'flex',
|
|
9468
|
-
'justify-content': position === 'right' ? 'flex-end' : 'flex-start',
|
|
9469
|
-
'align-items': 'center',
|
|
9470
9859
|
padding: '12px 16px',
|
|
9471
9860
|
cursor: 'pointer',
|
|
9472
9861
|
'border-bottom': '1px solid rgba(0,0,0,0.1)',
|
|
@@ -9482,10 +9871,8 @@ const Sidenav = () => {
|
|
|
9482
9871
|
// Expand/collapse toggle button (if expandable, right below hamburger)
|
|
9483
9872
|
expandable &&
|
|
9484
9873
|
m('li.sidenav-expand-toggle', {
|
|
9874
|
+
class: `mm-layout-row mm-layout-row--center ${position === 'right' ? 'mm-layout-row--justify-end' : 'mm-layout-row--justify-start'}`,
|
|
9485
9875
|
style: {
|
|
9486
|
-
display: 'flex',
|
|
9487
|
-
'justify-content': position === 'right' ? 'flex-end' : 'flex-start',
|
|
9488
|
-
'align-items': 'center',
|
|
9489
9876
|
padding: '12px 16px',
|
|
9490
9877
|
cursor: 'pointer',
|
|
9491
9878
|
'border-bottom': '1px solid rgba(0,0,0,0.1)',
|
|
@@ -9513,7 +9900,19 @@ const Sidenav = () => {
|
|
|
9513
9900
|
: children,
|
|
9514
9901
|
// Footer item (if provided, appears at the bottom)
|
|
9515
9902
|
attrs.footer &&
|
|
9516
|
-
m(SidenavHeaderFooterItem, Object.assign(Object.assign({}, attrs.footer), { _isExpanded: isExpanded, _position: position, className: 'sidenav-footer-item' })),
|
|
9903
|
+
m(SidenavHeaderFooterItem, Object.assign(Object.assign({}, attrs.footer), { _isExpanded: isExpanded, _position: position, className: ['sidenav-footer-item', attrs.footer.className].filter(Boolean).join(' ') })),
|
|
9904
|
+
// Footer content slot (rendered last, pushed to bottom via margin-top: auto)
|
|
9905
|
+
attrs.footerContent &&
|
|
9906
|
+
m('li.sidenav-footer-slot', {
|
|
9907
|
+
style: {
|
|
9908
|
+
'margin-top': 'auto',
|
|
9909
|
+
'flex-shrink': 0,
|
|
9910
|
+
'list-style': 'none',
|
|
9911
|
+
height: 'auto',
|
|
9912
|
+
'line-height': 'normal',
|
|
9913
|
+
padding: 0,
|
|
9914
|
+
},
|
|
9915
|
+
}, attrs.footerContent),
|
|
9517
9916
|
]),
|
|
9518
9917
|
];
|
|
9519
9918
|
},
|
|
@@ -9546,7 +9945,7 @@ const NavbarSubItem = () => {
|
|
|
9546
9945
|
const submenuContent = isRightAligned
|
|
9547
9946
|
? [
|
|
9548
9947
|
// Right-aligned: text on left, icons on right
|
|
9549
|
-
isExpanded && m('span
|
|
9948
|
+
isExpanded && m('span.mm-layout-grow.left-align', text),
|
|
9550
9949
|
icon && isExpanded && renderIcon(icon, { 'font-size': '18px' }),
|
|
9551
9950
|
indicatorIcon,
|
|
9552
9951
|
]
|
|
@@ -9554,18 +9953,22 @@ const NavbarSubItem = () => {
|
|
|
9554
9953
|
// Left-aligned: indicator on left, text and icon on right
|
|
9555
9954
|
indicatorIcon,
|
|
9556
9955
|
icon && isExpanded && renderIcon(icon, { 'font-size': '18px', 'margin-left': indicatorIcon ? '8px' : '0' }),
|
|
9557
|
-
isExpanded && m('span', {
|
|
9956
|
+
isExpanded && m('span', { class: icon || indicatorIcon ? 'mm-layout-ml-8' : undefined }, text),
|
|
9558
9957
|
];
|
|
9559
9958
|
return m('li.sidenav-subitem', {
|
|
9560
|
-
class:
|
|
9959
|
+
class: [
|
|
9960
|
+
selected ? 'selected' : '',
|
|
9961
|
+
'mm-layout-row',
|
|
9962
|
+
'mm-layout-row--center',
|
|
9963
|
+
'mm-layout-gap-sm',
|
|
9964
|
+
isRightAligned ? 'mm-layout-row--justify-between' : 'mm-layout-row--justify-start',
|
|
9965
|
+
]
|
|
9966
|
+
.filter(Boolean)
|
|
9967
|
+
.join(' ') || undefined,
|
|
9561
9968
|
style: {
|
|
9562
9969
|
padding: isExpanded ? '0 16px 0 48px' : '0 16px',
|
|
9563
9970
|
cursor: 'pointer',
|
|
9564
|
-
display: 'flex',
|
|
9565
|
-
'align-items': 'center',
|
|
9566
|
-
gap: '8px',
|
|
9567
9971
|
'font-size': '0.9em',
|
|
9568
|
-
'justify-content': isRightAligned ? 'space-between' : 'flex-start',
|
|
9569
9972
|
height: '48px',
|
|
9570
9973
|
'min-height': '48px',
|
|
9571
9974
|
},
|
|
@@ -9582,7 +9985,7 @@ const SidenavItem = () => {
|
|
|
9582
9985
|
let isSubmenuOpen = false;
|
|
9583
9986
|
return {
|
|
9584
9987
|
view: ({ attrs, children }) => {
|
|
9585
|
-
const { text, icon, active = false, disabled = false, onclick, href, className = '', divider = false, subheader = false, submenu = [], submenuMode = 'checkbox', } = attrs;
|
|
9988
|
+
const { text, icon, active = false, disabled = false, onclick, href, className = '', divider = false, subheader = false, submenu = [], submenuMode = 'checkbox', title, tooltip, tooltipWhenCollapsedOnly = true, } = attrs;
|
|
9586
9989
|
if (divider) {
|
|
9587
9990
|
return m('li.divider');
|
|
9588
9991
|
}
|
|
@@ -9607,39 +10010,45 @@ const SidenavItem = () => {
|
|
|
9607
10010
|
const isExpanded = attrs._isExpanded !== false;
|
|
9608
10011
|
const position = attrs._position || 'left';
|
|
9609
10012
|
const isRightAligned = position === 'right';
|
|
10013
|
+
const tooltipText = title || tooltip || text;
|
|
10014
|
+
const shouldShowTooltip = tooltipWhenCollapsedOnly ? !isExpanded : true;
|
|
9610
10015
|
// In expanded mode, icons are at the outside edge
|
|
9611
10016
|
// In collapsed mode, icons are centered
|
|
9612
10017
|
const content = isRightAligned
|
|
9613
10018
|
? [
|
|
9614
10019
|
// Right-aligned: text on left, icon on right
|
|
9615
|
-
isExpanded &&
|
|
9616
|
-
m('span.sidenav-item-text', { style: { flex: '1', 'text-align': 'left', 'margin-right': '8px' } }, text || children),
|
|
10020
|
+
isExpanded && m('span.sidenav-item-text.mm-layout-grow.mm-layout-mr-8.left-align', text || children),
|
|
9617
10021
|
renderIcon(icon, { 'min-width': '24px', width: '24px' }),
|
|
9618
10022
|
]
|
|
9619
10023
|
: [
|
|
9620
10024
|
// Left-aligned: icon on left, text on right
|
|
9621
10025
|
renderIcon(icon, { 'min-width': '24px', width: '24px' }),
|
|
9622
|
-
isExpanded && m('span.sidenav-item-text
|
|
10026
|
+
isExpanded && m('span.sidenav-item-text.mm-layout-grow.mm-layout-ml-8', text || children),
|
|
9623
10027
|
];
|
|
9624
10028
|
const linkStyle = {
|
|
9625
|
-
display: 'flex',
|
|
9626
|
-
'align-items': 'center',
|
|
9627
10029
|
padding: isExpanded ? '12px 16px' : '12px 18px',
|
|
9628
10030
|
'justify-content': isExpanded ? (isRightAligned ? 'flex-end' : 'flex-start') : 'center',
|
|
9629
10031
|
};
|
|
10032
|
+
const linkClass = 'mm-layout-row mm-layout-row--center';
|
|
9630
10033
|
const mainItem = href && !disabled
|
|
9631
10034
|
? m('li', { class: itemClasses }, [
|
|
9632
10035
|
m('a', {
|
|
9633
10036
|
href,
|
|
9634
10037
|
onclick: handleMainClick,
|
|
10038
|
+
class: linkClass,
|
|
9635
10039
|
style: linkStyle,
|
|
10040
|
+
title: shouldShowTooltip ? tooltipText : undefined,
|
|
10041
|
+
'aria-label': shouldShowTooltip ? tooltipText : undefined,
|
|
9636
10042
|
}, content),
|
|
9637
10043
|
])
|
|
9638
10044
|
: m('li', { class: itemClasses }, [
|
|
9639
10045
|
m('a', {
|
|
9640
10046
|
onclick: handleMainClick,
|
|
9641
10047
|
href: '#!',
|
|
10048
|
+
class: linkClass,
|
|
9642
10049
|
style: linkStyle,
|
|
10050
|
+
title: shouldShowTooltip ? tooltipText : undefined,
|
|
10051
|
+
'aria-label': shouldShowTooltip ? tooltipText : undefined,
|
|
9643
10052
|
}, content),
|
|
9644
10053
|
]);
|
|
9645
10054
|
// Return main item with submenu if applicable
|
|
@@ -10155,8 +10564,9 @@ const TreeNodeComponent = () => {
|
|
|
10155
10564
|
const childIsFocused = ((_e = attrs.treeState) === null || _e === void 0 ? void 0 : _e.focusedNodeId) === child.id;
|
|
10156
10565
|
// Calculate if this child is last in branch
|
|
10157
10566
|
const childPath = [...(attrs.currentPath || []), childIndex];
|
|
10158
|
-
const childIsLastInBranch = ((_f = attrs.treeAttrs) === null || _f === void 0 ? void 0 : _f.data)
|
|
10159
|
-
isNodeLastInBranch(childPath, attrs.treeAttrs.data)
|
|
10567
|
+
const childIsLastInBranch = ((_f = attrs.treeAttrs) === null || _f === void 0 ? void 0 : _f.data)
|
|
10568
|
+
? isNodeLastInBranch(childPath, attrs.treeAttrs.data)
|
|
10569
|
+
: false;
|
|
10160
10570
|
return m(TreeNodeComponent, {
|
|
10161
10571
|
key: child.id,
|
|
10162
10572
|
node: child,
|
|
@@ -10289,10 +10699,7 @@ const TreeView = () => {
|
|
|
10289
10699
|
view: ({ attrs }) => {
|
|
10290
10700
|
const { data, className, style, id, selectionMode = 'single', showConnectors = true } = attrs;
|
|
10291
10701
|
return m('div.tree-view', {
|
|
10292
|
-
class: [
|
|
10293
|
-
className,
|
|
10294
|
-
showConnectors && 'show-connectors'
|
|
10295
|
-
].filter(Boolean).join(' ') || undefined,
|
|
10702
|
+
class: [className, showConnectors && 'show-connectors'].filter(Boolean).join(' ') || undefined,
|
|
10296
10703
|
style,
|
|
10297
10704
|
id,
|
|
10298
10705
|
role: selectionMode === 'multiple' ? 'listbox' : 'tree',
|
|
@@ -10990,6 +11397,7 @@ const Rating = () => {
|
|
|
10990
11397
|
};
|
|
10991
11398
|
};
|
|
10992
11399
|
|
|
11400
|
+
const MOBILE_LAYOUT_BREAKPOINT = 600;
|
|
10993
11401
|
/** Create a LikertScale component */
|
|
10994
11402
|
const LikertScale = () => {
|
|
10995
11403
|
const state = {
|
|
@@ -11052,6 +11460,22 @@ const LikertScale = () => {
|
|
|
11052
11460
|
return 'likert-scale--responsive';
|
|
11053
11461
|
}
|
|
11054
11462
|
};
|
|
11463
|
+
const isVerticalLayout = (layout) => {
|
|
11464
|
+
if (layout === 'vertical')
|
|
11465
|
+
return true;
|
|
11466
|
+
if (layout === 'horizontal')
|
|
11467
|
+
return false;
|
|
11468
|
+
return typeof window !== 'undefined' ? window.innerWidth <= MOBILE_LAYOUT_BREAKPOINT : false;
|
|
11469
|
+
};
|
|
11470
|
+
const getInlineAnchorLabel = (value, min, max, middleValue, startLabel, middleLabel, endLabel) => {
|
|
11471
|
+
if (value === min && startLabel)
|
|
11472
|
+
return startLabel;
|
|
11473
|
+
if (middleLabel && middleValue !== undefined && value === middleValue)
|
|
11474
|
+
return middleLabel;
|
|
11475
|
+
if (value === max && endLabel)
|
|
11476
|
+
return endLabel;
|
|
11477
|
+
return undefined;
|
|
11478
|
+
};
|
|
11055
11479
|
const handleChange = (attrs, newValue) => {
|
|
11056
11480
|
var _a;
|
|
11057
11481
|
if (attrs.readonly || attrs.disabled)
|
|
@@ -11098,7 +11522,7 @@ const LikertScale = () => {
|
|
|
11098
11522
|
const LikertScaleItem = () => {
|
|
11099
11523
|
return {
|
|
11100
11524
|
view: ({ attrs }) => {
|
|
11101
|
-
const { value, currentValue, showNumber, showTooltip, tooltipLabel, groupId, name, disabled, readonly, onchange, } = attrs;
|
|
11525
|
+
const { value, currentValue, showNumber, showTooltip, tooltipLabel, groupId, name, disabled, readonly, anchorLabel, onchange, } = attrs;
|
|
11102
11526
|
const radioId = `${groupId}-${value}`;
|
|
11103
11527
|
const isChecked = currentValue === value;
|
|
11104
11528
|
return m('.likert-scale__item.no-select', {
|
|
@@ -11125,6 +11549,7 @@ const LikertScale = () => {
|
|
|
11125
11549
|
m('label.likert-scale__label', {
|
|
11126
11550
|
for: radioId,
|
|
11127
11551
|
}),
|
|
11552
|
+
anchorLabel && m('.likert-scale__item-anchor', anchorLabel),
|
|
11128
11553
|
// Tooltip (optional)
|
|
11129
11554
|
showTooltip && tooltipLabel && m('.likert-scale__tooltip', tooltipLabel),
|
|
11130
11555
|
]);
|
|
@@ -11148,6 +11573,8 @@ const LikertScale = () => {
|
|
|
11148
11573
|
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"]);
|
|
11149
11574
|
const currentValue = getCurrentValue(attrs);
|
|
11150
11575
|
const itemCount = Math.floor((max - min) / step) + 1;
|
|
11576
|
+
const useInlineAnchors = isVerticalLayout(layout);
|
|
11577
|
+
const middleValue = middleLabel ? min + Math.floor((itemCount - 1) / 2) * step : undefined;
|
|
11151
11578
|
// Generate scale values
|
|
11152
11579
|
const scaleValues = Array.from({ length: itemCount }, (_, i) => min + i * step);
|
|
11153
11580
|
return m('.likert-scale', {
|
|
@@ -11196,6 +11623,9 @@ const LikertScale = () => {
|
|
|
11196
11623
|
showNumber: showNumbers,
|
|
11197
11624
|
showTooltip: showTooltips,
|
|
11198
11625
|
tooltipLabel: tooltipLabels === null || tooltipLabels === void 0 ? void 0 : tooltipLabels[value - min],
|
|
11626
|
+
anchorLabel: useInlineAnchors
|
|
11627
|
+
? getInlineAnchorLabel(value, min, max, middleValue, startLabel, middleLabel, endLabel)
|
|
11628
|
+
: undefined,
|
|
11199
11629
|
groupId: state.groupId,
|
|
11200
11630
|
name,
|
|
11201
11631
|
disabled,
|
|
@@ -11203,7 +11633,8 @@ const LikertScale = () => {
|
|
|
11203
11633
|
onchange: (v) => handleChange(attrs, v),
|
|
11204
11634
|
}))),
|
|
11205
11635
|
// Scale anchors
|
|
11206
|
-
|
|
11636
|
+
!useInlineAnchors &&
|
|
11637
|
+
(startLabel || middleLabel || endLabel) &&
|
|
11207
11638
|
m('.likert-scale__anchors', [
|
|
11208
11639
|
startLabel && m('.likert-scale__anchor.likert-scale__anchor--start', startLabel),
|
|
11209
11640
|
middleLabel && m('.likert-scale__anchor.likert-scale__anchor--middle', middleLabel),
|
|
@@ -11388,6 +11819,9 @@ const CircularProgress = () => {
|
|
|
11388
11819
|
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']);
|
|
11389
11820
|
const isDeterminate = mode === 'determinate';
|
|
11390
11821
|
const sizePixels = SIZE_MAP[size];
|
|
11822
|
+
const styleValue = typeof style === 'string'
|
|
11823
|
+
? `width:${sizePixels}px;height:${sizePixels}px;${style}`
|
|
11824
|
+
: Object.assign({ width: `${sizePixels}px`, height: `${sizePixels}px` }, style);
|
|
11391
11825
|
const { radius, circumference, strokeDashoffset, percentage } = isDeterminate
|
|
11392
11826
|
? calculateStrokeProperties(sizePixels, value, max)
|
|
11393
11827
|
: { radius: 0, circumference: 0, strokeDashoffset: 0, percentage: 0 };
|
|
@@ -11414,7 +11848,7 @@ const CircularProgress = () => {
|
|
|
11414
11848
|
: {
|
|
11415
11849
|
'aria-valuetext': ariaValueText || label || 'Loading',
|
|
11416
11850
|
};
|
|
11417
|
-
return m('.circular-progress', Object.assign(Object.assign(Object.assign({}, params), { className: classNames, style:
|
|
11851
|
+
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), [
|
|
11418
11852
|
// SVG circle
|
|
11419
11853
|
m('svg.circular-progress__svg', {
|
|
11420
11854
|
viewBox: `0 0 ${sizePixels} ${sizePixels}`,
|
|
@@ -11479,13 +11913,7 @@ const LinearProgress = () => {
|
|
|
11479
11913
|
// Determine label content
|
|
11480
11914
|
const labelContent = label !== undefined ? label : showPercentage && isDeterminate ? `${Math.round(percentage)}%` : '';
|
|
11481
11915
|
// Build class names
|
|
11482
|
-
const classNames = [
|
|
11483
|
-
'linear-progress',
|
|
11484
|
-
getColorClass(color, colorIntensity),
|
|
11485
|
-
className,
|
|
11486
|
-
]
|
|
11487
|
-
.filter(Boolean)
|
|
11488
|
-
.join(' ');
|
|
11916
|
+
const classNames = ['linear-progress', getColorClass(color, colorIntensity), className].filter(Boolean).join(' ');
|
|
11489
11917
|
// ARIA attributes
|
|
11490
11918
|
const ariaAttrs = isDeterminate
|
|
11491
11919
|
? {
|
|
@@ -11538,4 +11966,5 @@ const isValidationError = (result) => !isValidationSuccess(result);
|
|
|
11538
11966
|
// ============================================================================
|
|
11539
11967
|
// All types are already exported via individual export declarations above
|
|
11540
11968
|
|
|
11541
|
-
export { AnalogClock, AnchorItem, Autocomplete, Badge, Breadcrumb, BreadcrumbManager, Button, ButtonFactory, Carousel, CharacterCounter, Chips, CircularProgress, CodeBlock, Collapsible, CollapsibleItem, Collection, CollectionMode, ColorInput, ConfirmButton, DataTable, DatePicker, DigitalClock, DoubleRangeSlider, Dropdown, EmailInput, FileInput, FileUpload, FlatButton, FloatingActionButton, HelperText, Icon, IconButton, ImageList, InputCheckbox, Label, LargeButton, LikertScale, LinearProgress, ListItem, Mandatory, Masonry, MaterialBox, MaterialIcon, ModalPanel, NumberInput, Options, OptionsList, Pagination, PaginationControls, Parallax, PasswordInput, Pushpin, PushpinComponent, RadioButton, RadioButtons, RangeInput, Rating, RoundIconButton, SearchSelect, SecondaryContent, Select, Sidenav, SidenavItem, SidenavManager, SingleRangeSlider, SmallButton, Stepper, SubmitButton, Switch, Tabs, TextArea, TextInput, ThemeManager, ThemeSwitcher, ThemeToggle, TimePicker, TimeRangePicker, Timeline, Toast, ToastComponent, ToggleGroup, Tooltip, TooltipComponent, TreeView, UrlInput, Wizard, addLeadingZero, clearPortal, createBreadcrumb, formatTime, generateHourOptions, generateMinuteOptions, getDropdownStyles, getPortalContainer, initPushpins, initTooltips, isNumeric, isTimeDisabled, isValidationError, isValidationSuccess, padLeft, parseTime, range, releasePortalContainer, renderToPortal, scrollToValue, snapToNearestItem, sortOptions, timeToMinutes, toast, uniqueId, uuid4 };
|
|
11969
|
+
export { AnalogClock, AnchorItem, Autocomplete, Badge, Breadcrumb, BreadcrumbManager, Button, ButtonFactory, Carousel, CharacterCounter, Chips, CircularProgress, CodeBlock, Collapsible, CollapsibleItem, Collection, CollectionMode, ColorInput, ConfirmButton, DataTable, DatePicker, DigitalClock, DoubleRangeSlider, Dropdown, EmailInput, Fieldset, FileInput, FileUpload, FlatButton, FloatingActionButton, FormSection, HelperText, Icon, IconButton, ImageList, InputCheckbox, Label, LargeButton, LikertScale, LinearProgress, ListItem, Mandatory, Masonry, MaterialBox, MaterialIcon, ModalPanel, NumberInput, Options, OptionsList, Pagination, PaginationControls, Parallax, PasswordInput, Pushpin, PushpinComponent, RadioButton, RadioButtons, RangeInput, Rating, RoundIconButton, SearchSelect, SecondaryContent, Select, Sidenav, SidenavItem, SidenavManager, SingleRangeSlider, SmallButton, Stepper, SubmitButton, Switch, Tabs, TextArea, TextInput, ThemeManager, ThemeSwitcher, ThemeToggle, TimePicker, TimeRangePicker, Timeline, Toast, ToastComponent, ToggleButton, ToggleGroup, Tooltip, TooltipComponent, TreeView, UrlInput, Wizard, addLeadingZero, clearPortal, createAsyncComboboxState, createBreadcrumb, formatTime, generateHourOptions, generateMinuteOptions, getComboboxKeyResult, getComboboxOptionId, getComboboxViewState, getDropdownStyles, getPortalContainer, initPushpins, initTooltips, isNumeric, isTimeDisabled, isValidationError, isValidationSuccess, normalizeSelection, padLeft, parseTime, range, rejectAsyncComboboxRequest, releasePortalContainer, renderFieldChrome, renderToPortal, resolveAsyncComboboxRequest, resolveControllableValue, scrollToValue, snapToNearestItem, sortOptions, startAsyncComboboxRequest, syncPortalContent, timeToMinutes, toast, uniqueId, uuid4 };
|
|
11970
|
+
//# sourceMappingURL=index.esm.js.map
|