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