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