mithril-materialized 3.16.0 → 3.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +32 -2
- package/dist/advanced.css +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 +697 -296
- package/dist/index.esm.js.map +1 -0
- package/dist/index.js +710 -295
- package/dist/index.js.map +1 -0
- package/dist/index.min.css +2 -2
- package/dist/index.umd.js +710 -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, selectedOptions, selectedIds, placeholder) => {
|
|
7266
|
+
const mode = attrs.summaryMode || 'labels';
|
|
7267
|
+
const selectedCount = selectedIds.length;
|
|
7268
|
+
const totalCount = attrs.options.filter((option) => !option.disabled).length;
|
|
7269
|
+
if (mode === 'labels') {
|
|
7270
|
+
return selectedOptions.length > 0 ? selectedOptions.map((o) => o.label || o.id).join(', ') : placeholder;
|
|
7271
|
+
}
|
|
7272
|
+
const defaultCountLabel = `${selectedCount}/${totalCount} selected`;
|
|
7273
|
+
const formattedCount = attrs.countLabel ? attrs.countLabel(selectedCount, totalCount) : defaultCountLabel;
|
|
7274
|
+
if (mode === 'count') {
|
|
7275
|
+
return selectedCount > 0 ? formattedCount : placeholder;
|
|
7276
|
+
}
|
|
7277
|
+
if (selectedCount === 0) {
|
|
7278
|
+
return attrs.noneSelectedLabel || placeholder;
|
|
7279
|
+
}
|
|
7280
|
+
if (totalCount > 0 && selectedCount >= totalCount) {
|
|
7281
|
+
return attrs.allSelectedLabel || 'All selected';
|
|
7282
|
+
}
|
|
7283
|
+
return formattedCount;
|
|
7284
|
+
};
|
|
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,29 @@ 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 triggerValue = formatSelectionSummary(attrs, selectedOptions, selectedIds, placeholder);
|
|
7214
7425
|
// Update portal dropdown when inside modal
|
|
7215
7426
|
if (state.isInsideModal) {
|
|
7216
7427
|
updatePortalDropdown(attrs, selectedIds, multiple, placeholder);
|
|
@@ -7230,9 +7441,15 @@ const Select = () => {
|
|
|
7230
7441
|
'aria-controls': state.dropdownId,
|
|
7231
7442
|
role: 'combobox',
|
|
7232
7443
|
}, [
|
|
7444
|
+
shouldInlineLabel &&
|
|
7445
|
+
m('span.select-inline-label', [
|
|
7446
|
+
label,
|
|
7447
|
+
isMandatory && m('span.mandatory', { style: { marginLeft: '2px' } }, '*'),
|
|
7448
|
+
]),
|
|
7233
7449
|
m('input[type=text][readonly=true].select-dropdown.dropdown-trigger', {
|
|
7234
7450
|
id: state.id,
|
|
7235
|
-
value:
|
|
7451
|
+
value: triggerValue,
|
|
7452
|
+
'aria-label': label,
|
|
7236
7453
|
oncreate: ({ dom }) => {
|
|
7237
7454
|
state.inputRef = dom;
|
|
7238
7455
|
},
|
|
@@ -7264,14 +7481,12 @@ const Select = () => {
|
|
|
7264
7481
|
}),
|
|
7265
7482
|
]),
|
|
7266
7483
|
// Label
|
|
7267
|
-
|
|
7268
|
-
|
|
7269
|
-
|
|
7270
|
-
|
|
7271
|
-
|
|
7272
|
-
|
|
7273
|
-
// Helper text
|
|
7274
|
-
helperText && m(HelperText, { helperText }),
|
|
7484
|
+
...renderFieldChrome({
|
|
7485
|
+
label: shouldInlineLabel ? undefined : label,
|
|
7486
|
+
id: state.id,
|
|
7487
|
+
isMandatory,
|
|
7488
|
+
helperText,
|
|
7489
|
+
}),
|
|
7275
7490
|
]);
|
|
7276
7491
|
},
|
|
7277
7492
|
};
|
|
@@ -7490,59 +7705,65 @@ const Tabs = () => {
|
|
|
7490
7705
|
};
|
|
7491
7706
|
};
|
|
7492
7707
|
|
|
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
|
-
|
|
7708
|
+
const SelectedChip = ({ option, onRemove, }) => m('.chip', [
|
|
7709
|
+
option.label || option.id.toString(),
|
|
7710
|
+
m(MaterialIcon, {
|
|
7711
|
+
name: 'close',
|
|
7712
|
+
className: 'close',
|
|
7713
|
+
onclick: (e) => {
|
|
7714
|
+
e.stopPropagation();
|
|
7715
|
+
onRemove(option.id);
|
|
7716
|
+
},
|
|
7717
|
+
}),
|
|
7718
|
+
]);
|
|
7719
|
+
const DropdownOption = ({ option, index, optionId, selectedIds, isFocused, onToggle, onMouseOver, showCheckbox, }) => {
|
|
7720
|
+
const optionLabel = option.label || option.id.toString();
|
|
7721
|
+
return m('li', {
|
|
7722
|
+
id: optionId,
|
|
7723
|
+
role: 'option',
|
|
7724
|
+
'aria-selected': selectedIds.includes(option.id) ? 'true' : 'false',
|
|
7725
|
+
class: `${option.disabled ? 'disabled' : ''} ${isFocused ? 'active' : ''}`.trim(),
|
|
7726
|
+
onmouseover: () => {
|
|
7727
|
+
if (!option.disabled) {
|
|
7728
|
+
onMouseOver(index);
|
|
7729
|
+
}
|
|
7730
|
+
},
|
|
7731
|
+
}, m('label', {
|
|
7732
|
+
class: 'search-select-option-label',
|
|
7733
|
+
onclick: (e) => {
|
|
7734
|
+
// A single-select row has no native checkbox to emit change.
|
|
7735
|
+
if (!showCheckbox) {
|
|
7736
|
+
e.preventDefault();
|
|
7737
|
+
onToggle(option);
|
|
7738
|
+
}
|
|
7739
|
+
},
|
|
7740
|
+
}, [
|
|
7741
|
+
showCheckbox &&
|
|
7742
|
+
m('input', {
|
|
7743
|
+
type: 'checkbox',
|
|
7744
|
+
checked: selectedIds.includes(option.id),
|
|
7745
|
+
disabled: option.disabled,
|
|
7746
|
+
onchange: (e) => {
|
|
7518
7747
|
e.stopPropagation();
|
|
7519
7748
|
onToggle(option);
|
|
7520
7749
|
},
|
|
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
|
-
};
|
|
7750
|
+
}),
|
|
7751
|
+
m('span', optionLabel),
|
|
7752
|
+
]));
|
|
7538
7753
|
};
|
|
7539
7754
|
/**
|
|
7540
7755
|
* Mithril Factory Component for Multi-Select Dropdown with search
|
|
7541
7756
|
*/
|
|
7542
|
-
const SearchSelect = () => {
|
|
7757
|
+
const SearchSelect = (maybeVnode) => {
|
|
7758
|
+
var _a;
|
|
7759
|
+
const cachedInstance = (_a = maybeVnode === null || maybeVnode === void 0 ? void 0 : maybeVnode.state) === null || _a === void 0 ? void 0 : _a.__searchSelectInstance;
|
|
7760
|
+
if (cachedInstance) {
|
|
7761
|
+
return cachedInstance;
|
|
7762
|
+
}
|
|
7543
7763
|
// State initialization
|
|
7544
7764
|
const state = {
|
|
7545
7765
|
id: '',
|
|
7766
|
+
listboxId: '',
|
|
7546
7767
|
isOpen: false,
|
|
7547
7768
|
searchTerm: '',
|
|
7548
7769
|
inputRef: null,
|
|
@@ -7550,17 +7771,38 @@ const SearchSelect = () => {
|
|
|
7550
7771
|
focusedIndex: -1,
|
|
7551
7772
|
internalSelectedIds: [],
|
|
7552
7773
|
createdOptions: [],
|
|
7553
|
-
|
|
7774
|
+
asyncOptions: [],
|
|
7775
|
+
isLoading: false,
|
|
7776
|
+
loadError: null,
|
|
7777
|
+
latestRequestId: 0,
|
|
7778
|
+
};
|
|
7779
|
+
const updateAsyncState = (nextState) => {
|
|
7780
|
+
state.asyncOptions = nextState.options;
|
|
7781
|
+
state.isLoading = nextState.isLoading;
|
|
7782
|
+
state.loadError = nextState.error;
|
|
7783
|
+
state.latestRequestId = nextState.latestRequestId;
|
|
7784
|
+
};
|
|
7785
|
+
const readAsyncState = () => ({
|
|
7786
|
+
options: state.asyncOptions,
|
|
7787
|
+
isLoading: state.isLoading,
|
|
7788
|
+
error: state.loadError,
|
|
7789
|
+
latestRequestId: state.latestRequestId,
|
|
7790
|
+
});
|
|
7554
7791
|
const isControlled = (attrs) => attrs.checkedId !== undefined && typeof attrs.onchange === 'function';
|
|
7555
7792
|
const componentId = uniqueId();
|
|
7556
7793
|
const searchInputId = `${componentId}-search`;
|
|
7557
7794
|
// Handle click outside
|
|
7558
7795
|
const handleClickOutside = (e) => {
|
|
7559
7796
|
const target = e.target;
|
|
7797
|
+
const targetElement = e.target instanceof Element ? e.target : null;
|
|
7560
7798
|
if (state.dropdownRef && state.dropdownRef.contains(target)) {
|
|
7561
7799
|
// Click inside dropdown, do nothing
|
|
7562
7800
|
return;
|
|
7563
7801
|
}
|
|
7802
|
+
if (targetElement && targetElement.closest('.chips-container')) {
|
|
7803
|
+
// Click on trigger, do nothing
|
|
7804
|
+
return;
|
|
7805
|
+
}
|
|
7564
7806
|
if (state.inputRef && state.inputRef.contains(target)) {
|
|
7565
7807
|
// Click on trigger handled by onclick event
|
|
7566
7808
|
return;
|
|
@@ -7571,40 +7813,51 @@ const SearchSelect = () => {
|
|
|
7571
7813
|
}
|
|
7572
7814
|
m.redraw();
|
|
7573
7815
|
};
|
|
7574
|
-
// Handle keyboard navigation
|
|
7575
|
-
const handleKeyDown = (e,
|
|
7576
|
-
|
|
7816
|
+
// Handle keyboard navigation through shared combobox primitive.
|
|
7817
|
+
const handleKeyDown = (e, optionCount, includeActionRow) => {
|
|
7818
|
+
const result = getComboboxKeyResult({
|
|
7819
|
+
key: e.key,
|
|
7820
|
+
isOpen: state.isOpen,
|
|
7821
|
+
focusedIndex: state.focusedIndex,
|
|
7822
|
+
optionCount,
|
|
7823
|
+
includeActionRow,
|
|
7824
|
+
});
|
|
7825
|
+
if (result.preventDefault) {
|
|
7826
|
+
e.preventDefault();
|
|
7827
|
+
}
|
|
7828
|
+
state.isOpen = result.isOpen;
|
|
7829
|
+
state.focusedIndex = result.focusedIndex;
|
|
7830
|
+
return result.action;
|
|
7831
|
+
};
|
|
7832
|
+
const loadAsyncOptions = async (attrs, query) => {
|
|
7833
|
+
if (!attrs.loadOptions) {
|
|
7577
7834
|
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;
|
|
7835
|
+
}
|
|
7836
|
+
const started = startAsyncComboboxRequest(readAsyncState());
|
|
7837
|
+
updateAsyncState(started.nextState);
|
|
7838
|
+
m.redraw();
|
|
7839
|
+
try {
|
|
7840
|
+
const loadedOptions = await attrs.loadOptions(query);
|
|
7841
|
+
const resolved = resolveAsyncComboboxRequest(readAsyncState(), started.requestId, loadedOptions);
|
|
7842
|
+
updateAsyncState(resolved);
|
|
7843
|
+
if (started.requestId !== state.latestRequestId) {
|
|
7844
|
+
return;
|
|
7845
|
+
}
|
|
7846
|
+
if (state.focusedIndex >= loadedOptions.length) {
|
|
7604
7847
|
state.focusedIndex = -1;
|
|
7605
|
-
|
|
7848
|
+
}
|
|
7849
|
+
}
|
|
7850
|
+
catch (error) {
|
|
7851
|
+
const rejected = rejectAsyncComboboxRequest(readAsyncState(), started.requestId, error instanceof Error ? error.message : 'Unable to load options');
|
|
7852
|
+
updateAsyncState(rejected);
|
|
7853
|
+
if (started.requestId !== state.latestRequestId) {
|
|
7854
|
+
return;
|
|
7855
|
+
}
|
|
7856
|
+
state.focusedIndex = -1;
|
|
7857
|
+
}
|
|
7858
|
+
finally {
|
|
7859
|
+
m.redraw();
|
|
7606
7860
|
}
|
|
7607
|
-
return null;
|
|
7608
7861
|
};
|
|
7609
7862
|
// Create new option and add to state
|
|
7610
7863
|
const createAndSelectOption = async (attrs) => {
|
|
@@ -7685,9 +7938,10 @@ const SearchSelect = () => {
|
|
|
7685
7938
|
attrs.onchange(newIds);
|
|
7686
7939
|
}
|
|
7687
7940
|
};
|
|
7688
|
-
|
|
7941
|
+
const componentInstance = {
|
|
7689
7942
|
oninit: ({ attrs }) => {
|
|
7690
7943
|
state.id = attrs.id || uniqueId();
|
|
7944
|
+
state.listboxId = `${state.id}-listbox`;
|
|
7691
7945
|
// Initialize internal state for uncontrolled mode
|
|
7692
7946
|
if (!isControlled(attrs)) {
|
|
7693
7947
|
const defaultIds = attrs.defaultCheckedId !== undefined
|
|
@@ -7714,20 +7968,26 @@ const SearchSelect = () => {
|
|
|
7714
7968
|
: [attrs.checkedId]
|
|
7715
7969
|
: []
|
|
7716
7970
|
: state.internalSelectedIds;
|
|
7717
|
-
const { options = [], oncreateNewOption, className, placeholder, searchPlaceholder = 'Search options...', noOptionsFound = 'No options found', label, i18n = {}, maxDisplayedOptions, maxSelectedOptions, maxHeight, } = attrs;
|
|
7971
|
+
const { options = [], loadOptions, oncreateNewOption, className, placeholder, searchPlaceholder = 'Search options...', noOptionsFound = 'No options found', label, i18n = {}, maxDisplayedOptions, maxSelectedOptions, maxHeight, } = attrs;
|
|
7718
7972
|
// Use i18n values if provided, otherwise use defaults
|
|
7719
7973
|
const texts = {
|
|
7720
7974
|
noOptionsFound: i18n.noOptionsFound || noOptionsFound,
|
|
7975
|
+
loadingOptions: i18n.loadingOptions || 'Loading options...',
|
|
7976
|
+
loadingError: i18n.loadingError || 'Unable to load options',
|
|
7721
7977
|
addNewPrefix: i18n.addNewPrefix || '+',
|
|
7722
7978
|
showingXofY: i18n.showingXofY || 'Showing {shown} of {total} options',
|
|
7723
7979
|
maxSelectionsReached: i18n.maxSelectionsReached || 'Maximum {max} selections reached',
|
|
7724
7980
|
};
|
|
7725
7981
|
// Check if max selections is reached
|
|
7726
7982
|
const isMaxSelectionsReached = maxSelectedOptions && selectedIds.length >= maxSelectedOptions;
|
|
7727
|
-
//
|
|
7728
|
-
const
|
|
7983
|
+
// In async mode, the active list is sourced remotely.
|
|
7984
|
+
const sourceOptions = loadOptions ? state.asyncOptions : options;
|
|
7985
|
+
// Merge active options with internally created options
|
|
7986
|
+
const allOptions = [...sourceOptions, ...state.createdOptions];
|
|
7987
|
+
// Keep selected label lookups stable across static, async, and created sets.
|
|
7988
|
+
const lookupOptions = [...options, ...state.asyncOptions, ...state.createdOptions];
|
|
7729
7989
|
// Get selected options for display
|
|
7730
|
-
const selectedOptionsUnsorted =
|
|
7990
|
+
const selectedOptionsUnsorted = lookupOptions.filter((opt) => selectedIds.includes(opt.id));
|
|
7731
7991
|
const selectedOptions = sortOptions(selectedOptionsUnsorted, attrs.sortSelected);
|
|
7732
7992
|
// Safely filter options
|
|
7733
7993
|
const filteredOptions = allOptions.filter((option) => (option.label || option.id.toString()).toLowerCase().includes((state.searchTerm || '').toLowerCase()) &&
|
|
@@ -7740,6 +8000,14 @@ const SearchSelect = () => {
|
|
|
7740
8000
|
const showAddNew = oncreateNewOption &&
|
|
7741
8001
|
state.searchTerm &&
|
|
7742
8002
|
!displayedOptions.some((o) => (o.label || o.id.toString()).toLowerCase() === state.searchTerm.toLowerCase());
|
|
8003
|
+
const activeDescendantId = state.isOpen && state.focusedIndex >= 0 && state.focusedIndex < displayedOptions.length
|
|
8004
|
+
? getComboboxOptionId(state.id, state.focusedIndex)
|
|
8005
|
+
: undefined;
|
|
8006
|
+
const viewState = getComboboxViewState({
|
|
8007
|
+
isLoading: state.isLoading,
|
|
8008
|
+
error: state.loadError,
|
|
8009
|
+
optionCount: displayedOptions.length,
|
|
8010
|
+
});
|
|
7743
8011
|
// Render the dropdown
|
|
7744
8012
|
return m('.input-field.multi-select-dropdown', { className }, [
|
|
7745
8013
|
m('.chips.chips-initial.chips-container', {
|
|
@@ -7750,14 +8018,34 @@ const SearchSelect = () => {
|
|
|
7750
8018
|
// console.log('SearchSelect clicked', state.isOpen, e); // Debug log
|
|
7751
8019
|
e.preventDefault();
|
|
7752
8020
|
e.stopPropagation();
|
|
8021
|
+
const wasOpen = state.isOpen;
|
|
7753
8022
|
state.isOpen = !state.isOpen;
|
|
8023
|
+
if (!wasOpen && state.isOpen && loadOptions) {
|
|
8024
|
+
void loadAsyncOptions(attrs, state.searchTerm);
|
|
8025
|
+
}
|
|
7754
8026
|
// console.log('SearchSelect state changed to', state.isOpen); // Debug log
|
|
7755
8027
|
},
|
|
7756
|
-
|
|
8028
|
+
onkeydown: async (e) => {
|
|
8029
|
+
const action = handleKeyDown(e, displayedOptions.length, !!showAddNew);
|
|
8030
|
+
if (action === 'open' && loadOptions) {
|
|
8031
|
+
await loadAsyncOptions(attrs, state.searchTerm);
|
|
8032
|
+
return;
|
|
8033
|
+
}
|
|
8034
|
+
if (action === 'selectAction' && oncreateNewOption) {
|
|
8035
|
+
await createAndSelectOption(attrs);
|
|
8036
|
+
}
|
|
8037
|
+
if (action === 'selectFocused' && state.focusedIndex < displayedOptions.length) {
|
|
8038
|
+
toggleOption(displayedOptions[state.focusedIndex], attrs);
|
|
8039
|
+
}
|
|
8040
|
+
},
|
|
8041
|
+
class: 'chips chips-container mm-layout-row mm-layout-row--wrap mm-layout-row--align-end',
|
|
8042
|
+
role: 'combobox',
|
|
8043
|
+
tabindex: 0,
|
|
8044
|
+
'aria-expanded': state.isOpen ? 'true' : 'false',
|
|
8045
|
+
'aria-haspopup': 'listbox',
|
|
8046
|
+
'aria-controls': state.isOpen ? state.listboxId : undefined,
|
|
8047
|
+
'aria-activedescendant': activeDescendantId,
|
|
7757
8048
|
style: {
|
|
7758
|
-
display: 'flex',
|
|
7759
|
-
alignItems: 'end',
|
|
7760
|
-
flexWrap: 'wrap',
|
|
7761
8049
|
cursor: 'pointer',
|
|
7762
8050
|
position: 'relative',
|
|
7763
8051
|
},
|
|
@@ -7770,30 +8058,41 @@ const SearchSelect = () => {
|
|
|
7770
8058
|
value: selectedOptions.map((o) => o.label || o.id.toString()).join(', '),
|
|
7771
8059
|
readonly: true,
|
|
7772
8060
|
class: 'sr-only',
|
|
7773
|
-
style: {
|
|
8061
|
+
style: {
|
|
8062
|
+
position: 'absolute',
|
|
8063
|
+
width: '1px',
|
|
8064
|
+
height: '1px',
|
|
8065
|
+
margin: '-1px',
|
|
8066
|
+
padding: 0,
|
|
8067
|
+
border: 0,
|
|
8068
|
+
overflow: 'hidden',
|
|
8069
|
+
clip: 'rect(0 0 0 0)',
|
|
8070
|
+
clipPath: 'inset(50%)',
|
|
8071
|
+
whiteSpace: 'nowrap',
|
|
8072
|
+
},
|
|
7774
8073
|
}),
|
|
7775
8074
|
// Selected Options (chips)
|
|
7776
|
-
...selectedOptions.map((option) =>
|
|
7777
|
-
option,
|
|
8075
|
+
...selectedOptions.map((option) => SelectedChip({
|
|
8076
|
+
option: option,
|
|
7778
8077
|
onRemove: (id) => removeOption(id, attrs),
|
|
7779
8078
|
})),
|
|
7780
8079
|
// Placeholder when no options selected
|
|
7781
8080
|
selectedOptions.length === 0 &&
|
|
7782
8081
|
placeholder &&
|
|
7783
8082
|
m('span.placeholder', {
|
|
8083
|
+
class: 'mm-layout-grow',
|
|
7784
8084
|
style: {
|
|
7785
8085
|
color: 'var(--mm-text-hint, #9e9e9e)',
|
|
7786
|
-
flexGrow: 1,
|
|
7787
8086
|
padding: '8px 0',
|
|
7788
8087
|
},
|
|
7789
8088
|
}, placeholder),
|
|
7790
8089
|
// Spacer to push caret to the right
|
|
7791
|
-
m('span.spacer'
|
|
8090
|
+
m('span.spacer.mm-layout-grow'),
|
|
7792
8091
|
m(MaterialIcon, {
|
|
7793
8092
|
name: 'caret',
|
|
7794
8093
|
direction: state.isOpen ? 'up' : 'down',
|
|
7795
|
-
class: 'caret',
|
|
7796
|
-
style: {
|
|
8094
|
+
class: 'caret mm-layout-ml-auto',
|
|
8095
|
+
style: { cursor: 'pointer' },
|
|
7797
8096
|
}),
|
|
7798
8097
|
]),
|
|
7799
8098
|
// Label
|
|
@@ -7805,6 +8104,8 @@ const SearchSelect = () => {
|
|
|
7805
8104
|
// Dropdown Menu
|
|
7806
8105
|
state.isOpen &&
|
|
7807
8106
|
m('ul.dropdown-content.select-dropdown', {
|
|
8107
|
+
id: state.listboxId,
|
|
8108
|
+
role: 'listbox',
|
|
7808
8109
|
oncreate: ({ dom }) => {
|
|
7809
8110
|
state.dropdownRef = dom;
|
|
7810
8111
|
},
|
|
@@ -7829,23 +8130,39 @@ const SearchSelect = () => {
|
|
|
7829
8130
|
oninput: (e) => {
|
|
7830
8131
|
state.searchTerm = e.target.value;
|
|
7831
8132
|
state.focusedIndex = -1; // Reset focus when typing
|
|
8133
|
+
if (loadOptions) {
|
|
8134
|
+
void loadAsyncOptions(attrs, state.searchTerm);
|
|
8135
|
+
}
|
|
7832
8136
|
},
|
|
7833
8137
|
onkeydown: async (e) => {
|
|
7834
|
-
const
|
|
7835
|
-
if (
|
|
8138
|
+
const action = handleKeyDown(e, displayedOptions.length, !!showAddNew);
|
|
8139
|
+
if (action === 'open' && loadOptions) {
|
|
8140
|
+
await loadAsyncOptions(attrs, state.searchTerm);
|
|
8141
|
+
}
|
|
8142
|
+
else if (action === 'selectAction' && oncreateNewOption) {
|
|
7836
8143
|
await createAndSelectOption(attrs);
|
|
7837
8144
|
}
|
|
7838
|
-
else if (
|
|
8145
|
+
else if (action === 'selectFocused' && state.focusedIndex < displayedOptions.length) {
|
|
7839
8146
|
toggleOption(displayedOptions[state.focusedIndex], attrs);
|
|
7840
8147
|
}
|
|
7841
8148
|
},
|
|
7842
8149
|
class: 'search-select-input',
|
|
8150
|
+
'aria-autocomplete': 'list',
|
|
8151
|
+
'aria-controls': state.listboxId,
|
|
7843
8152
|
}),
|
|
7844
8153
|
]),
|
|
7845
|
-
//
|
|
7846
|
-
...(
|
|
7847
|
-
? [m('li.search-select-
|
|
8154
|
+
// Async loading status
|
|
8155
|
+
...(viewState === 'loading'
|
|
8156
|
+
? [m('li.search-select-loading-info', { role: 'status', 'aria-live': 'polite' }, texts.loadingOptions)]
|
|
8157
|
+
: []),
|
|
8158
|
+
// Async loading error
|
|
8159
|
+
...(viewState === 'error' && state.loadError
|
|
8160
|
+
? [
|
|
8161
|
+
m('li.search-select-error-info', { role: 'status', 'aria-live': 'assertive' }, `${texts.loadingError}: ${state.loadError}`),
|
|
8162
|
+
]
|
|
7848
8163
|
: []),
|
|
8164
|
+
// No options found message or list of options
|
|
8165
|
+
...(viewState === 'empty' && !showAddNew ? [m('li.search-select-no-options', texts.noOptionsFound)] : []),
|
|
7849
8166
|
// Truncation message
|
|
7850
8167
|
...(isTruncated
|
|
7851
8168
|
? [
|
|
@@ -7878,6 +8195,9 @@ const SearchSelect = () => {
|
|
|
7878
8195
|
...(showAddNew
|
|
7879
8196
|
? [
|
|
7880
8197
|
m('li', {
|
|
8198
|
+
id: `${state.listboxId}-action`,
|
|
8199
|
+
role: 'option',
|
|
8200
|
+
'aria-selected': 'false',
|
|
7881
8201
|
onclick: async () => {
|
|
7882
8202
|
await createAndSelectOption(attrs);
|
|
7883
8203
|
},
|
|
@@ -7889,9 +8209,10 @@ const SearchSelect = () => {
|
|
|
7889
8209
|
]
|
|
7890
8210
|
: []),
|
|
7891
8211
|
// List of filtered options
|
|
7892
|
-
...displayedOptions.map((option, index) =>
|
|
8212
|
+
...displayedOptions.map((option, index) => DropdownOption({
|
|
7893
8213
|
option,
|
|
7894
8214
|
index,
|
|
8215
|
+
optionId: getComboboxOptionId(state.id, index),
|
|
7895
8216
|
selectedIds,
|
|
7896
8217
|
isFocused: state.focusedIndex === index,
|
|
7897
8218
|
onToggle: (opt) => toggleOption(opt, attrs),
|
|
@@ -7904,6 +8225,10 @@ const SearchSelect = () => {
|
|
|
7904
8225
|
]);
|
|
7905
8226
|
},
|
|
7906
8227
|
};
|
|
8228
|
+
if (maybeVnode === null || maybeVnode === void 0 ? void 0 : maybeVnode.state) {
|
|
8229
|
+
maybeVnode.state.__searchSelectInstance = componentInstance;
|
|
8230
|
+
}
|
|
8231
|
+
return componentInstance;
|
|
7907
8232
|
};
|
|
7908
8233
|
|
|
7909
8234
|
const defaultI18n$2 = {
|
|
@@ -9277,6 +9602,53 @@ const FileUpload = () => {
|
|
|
9277
9602
|
};
|
|
9278
9603
|
};
|
|
9279
9604
|
|
|
9605
|
+
/** A semantic native fieldset for related controls. */
|
|
9606
|
+
const Fieldset = () => ({
|
|
9607
|
+
view: ({ attrs, children }) => {
|
|
9608
|
+
const { legend, description, required, disabled, error, className } = attrs, params = __rest(attrs, ["legend", "description", "required", "disabled", "error", "className"]);
|
|
9609
|
+
const descriptionId = params.id ? `${params.id}-description` : undefined;
|
|
9610
|
+
const errorId = params.id ? `${params.id}-error` : undefined;
|
|
9611
|
+
const describedBy = [description && descriptionId, error && errorId].filter(Boolean).join(' ') || undefined;
|
|
9612
|
+
return m('fieldset.mm-fieldset', Object.assign(Object.assign({}, params), { className, disabled, 'aria-describedby': describedBy }), [
|
|
9613
|
+
m('legend.mm-fieldset__legend', [legend, required && m('span.mm-fieldset__required[aria-hidden=true]', ' *')]),
|
|
9614
|
+
description && m('p.mm-fieldset__description', { id: descriptionId }, description),
|
|
9615
|
+
children,
|
|
9616
|
+
error && m('p.mm-fieldset__error[role=alert]', { id: errorId }, error),
|
|
9617
|
+
]);
|
|
9618
|
+
},
|
|
9619
|
+
});
|
|
9620
|
+
const focusErrorTarget = (fieldId) => {
|
|
9621
|
+
var _a;
|
|
9622
|
+
if (!fieldId || typeof document === 'undefined')
|
|
9623
|
+
return;
|
|
9624
|
+
(_a = document.getElementById(fieldId)) === null || _a === void 0 ? void 0 : _a.focus();
|
|
9625
|
+
};
|
|
9626
|
+
/** A visual form section with an optional accessible validation summary. */
|
|
9627
|
+
const FormSection = () => ({
|
|
9628
|
+
view: ({ attrs, children }) => {
|
|
9629
|
+
const { title, description, errors = [], summaryTitle = 'Please correct the following errors', className } = attrs, params = __rest(attrs, ["title", "description", "errors", "summaryTitle", "className"]);
|
|
9630
|
+
return m('section.mm-form-section', Object.assign(Object.assign({}, params), { className }), [
|
|
9631
|
+
(title || description) &&
|
|
9632
|
+
m('.mm-form-section__header', [
|
|
9633
|
+
title && m('h3.mm-form-section__title', title),
|
|
9634
|
+
description && m('p.mm-form-section__description', description),
|
|
9635
|
+
]),
|
|
9636
|
+
errors.length > 0 &&
|
|
9637
|
+
m('div.mm-validation-summary[role=alert][aria-live=assertive]', [
|
|
9638
|
+
m('p.mm-validation-summary__title', summaryTitle),
|
|
9639
|
+
m('ul.mm-validation-summary__list', errors.map((error, index) => {
|
|
9640
|
+
var _a, _b, _c;
|
|
9641
|
+
const href = (_a = error.href) !== null && _a !== void 0 ? _a : (error.fieldId ? `#${error.fieldId}` : undefined);
|
|
9642
|
+
return m('li', { key: `${(_c = (_b = error.fieldId) !== null && _b !== void 0 ? _b : href) !== null && _c !== void 0 ? _c : 'error'}-${index}` }, href
|
|
9643
|
+
? m('a', { href, onclick: () => focusErrorTarget(error.fieldId) }, error.message)
|
|
9644
|
+
: error.message);
|
|
9645
|
+
})),
|
|
9646
|
+
]),
|
|
9647
|
+
children,
|
|
9648
|
+
]);
|
|
9649
|
+
},
|
|
9650
|
+
});
|
|
9651
|
+
|
|
9280
9652
|
// List of MaterialIcon SVG icons that are available
|
|
9281
9653
|
const materialIconSvgNames = [
|
|
9282
9654
|
'caret',
|
|
@@ -9299,6 +9671,7 @@ const materialIconSvgNames = [
|
|
|
9299
9671
|
const renderIcon = (icon, style) => {
|
|
9300
9672
|
if (!icon)
|
|
9301
9673
|
return null;
|
|
9674
|
+
const objectStyle = typeof style === 'string' ? undefined : style;
|
|
9302
9675
|
if (typeof icon === 'string') {
|
|
9303
9676
|
// Check if this is a MaterialIcon SVG name
|
|
9304
9677
|
if (materialIconSvgNames.includes(icon)) {
|
|
@@ -9315,7 +9688,7 @@ const renderIcon = (icon, style) => {
|
|
|
9315
9688
|
// Image URL
|
|
9316
9689
|
return m('img', {
|
|
9317
9690
|
src: icon.content,
|
|
9318
|
-
style: Object.assign(Object.assign({},
|
|
9691
|
+
style: Object.assign(Object.assign({}, objectStyle), { width: '24px', height: '24px', objectFit: 'contain' }),
|
|
9319
9692
|
});
|
|
9320
9693
|
}
|
|
9321
9694
|
return null;
|
|
@@ -9326,8 +9699,10 @@ const renderIcon = (icon, style) => {
|
|
|
9326
9699
|
const SidenavHeaderFooterItem = () => {
|
|
9327
9700
|
return {
|
|
9328
9701
|
view: ({ attrs }) => {
|
|
9329
|
-
const { text, icon, onclick, href, className = '', _isExpanded = true, _position = 'left' } = attrs;
|
|
9702
|
+
const { text, icon, onclick, href, className = '', title, tooltip, tooltipWhenCollapsedOnly = true, _isExpanded = true, _position = 'left', } = attrs;
|
|
9330
9703
|
const isRightAligned = _position === 'right';
|
|
9704
|
+
const tooltipText = title || tooltip || text;
|
|
9705
|
+
const shouldShowTooltip = tooltipWhenCollapsedOnly ? !_isExpanded : true;
|
|
9331
9706
|
const handleClick = (e) => {
|
|
9332
9707
|
if (onclick) {
|
|
9333
9708
|
e.preventDefault();
|
|
@@ -9336,24 +9711,25 @@ const SidenavHeaderFooterItem = () => {
|
|
|
9336
9711
|
};
|
|
9337
9712
|
const content = isRightAligned
|
|
9338
9713
|
? [
|
|
9339
|
-
_isExpanded &&
|
|
9340
|
-
m('span.sidenav-item-text', { style: { flex: '1', 'text-align': 'left', 'margin-right': '8px' } }, text),
|
|
9714
|
+
_isExpanded && m('span.sidenav-item-text.mm-layout-grow.mm-layout-mr-8.left-align', text),
|
|
9341
9715
|
renderIcon(icon, { 'min-width': '24px', width: '24px' }),
|
|
9342
9716
|
]
|
|
9343
9717
|
: [
|
|
9344
9718
|
renderIcon(icon, { 'min-width': '24px', width: '24px' }),
|
|
9345
|
-
_isExpanded && m('span.sidenav-item-text
|
|
9719
|
+
_isExpanded && m('span.sidenav-item-text.mm-layout-grow.mm-layout-ml-8', text),
|
|
9346
9720
|
];
|
|
9347
9721
|
const linkStyle = {
|
|
9348
|
-
display: 'flex',
|
|
9349
|
-
'align-items': 'center',
|
|
9350
9722
|
padding: _isExpanded ? '12px 16px' : '12px 18px',
|
|
9351
9723
|
'justify-content': _isExpanded ? (isRightAligned ? 'flex-end' : 'flex-start') : 'center',
|
|
9352
9724
|
};
|
|
9725
|
+
const linkClass = 'mm-layout-row mm-layout-row--center';
|
|
9353
9726
|
return m('li', { class: className }, m('a', {
|
|
9354
9727
|
href: href || '#!',
|
|
9355
9728
|
onclick: handleClick,
|
|
9729
|
+
class: linkClass,
|
|
9356
9730
|
style: linkStyle,
|
|
9731
|
+
title: shouldShowTooltip ? tooltipText : undefined,
|
|
9732
|
+
'aria-label': shouldShowTooltip ? tooltipText : undefined,
|
|
9357
9733
|
}, content));
|
|
9358
9734
|
},
|
|
9359
9735
|
};
|
|
@@ -9454,6 +9830,7 @@ const Sidenav = () => {
|
|
|
9454
9830
|
fixed ? 'sidenav-fixed' : '',
|
|
9455
9831
|
mode === 'push' ? 'sidenav-push' : '',
|
|
9456
9832
|
expandable && !isExpanded ? 'sidenav-collapsed' : '',
|
|
9833
|
+
'mm-layout-stack',
|
|
9457
9834
|
className,
|
|
9458
9835
|
]
|
|
9459
9836
|
.filter(Boolean)
|
|
@@ -9463,8 +9840,6 @@ const Sidenav = () => {
|
|
|
9463
9840
|
transform: isOpen ? 'translateX(0)' : position === 'left' ? 'translateX(-105%)' : 'translateX(105%)',
|
|
9464
9841
|
'transition-duration': `${animationDuration}ms`,
|
|
9465
9842
|
'transition-property': 'transform, width',
|
|
9466
|
-
display: 'flex',
|
|
9467
|
-
'flex-direction': 'column',
|
|
9468
9843
|
},
|
|
9469
9844
|
}, [
|
|
9470
9845
|
// Header content slot (rendered first, before hamburger)
|
|
@@ -9481,10 +9856,8 @@ const Sidenav = () => {
|
|
|
9481
9856
|
// Hamburger toggle button (inside sidenav, at the top)
|
|
9482
9857
|
showHamburger &&
|
|
9483
9858
|
m('li.sidenav-hamburger-item', {
|
|
9859
|
+
class: `mm-layout-row mm-layout-row--center ${position === 'right' ? 'mm-layout-row--justify-end' : 'mm-layout-row--justify-start'}`,
|
|
9484
9860
|
style: {
|
|
9485
|
-
display: 'flex',
|
|
9486
|
-
'justify-content': position === 'right' ? 'flex-end' : 'flex-start',
|
|
9487
|
-
'align-items': 'center',
|
|
9488
9861
|
padding: '12px 16px',
|
|
9489
9862
|
cursor: 'pointer',
|
|
9490
9863
|
'border-bottom': '1px solid rgba(0,0,0,0.1)',
|
|
@@ -9500,10 +9873,8 @@ const Sidenav = () => {
|
|
|
9500
9873
|
// Expand/collapse toggle button (if expandable, right below hamburger)
|
|
9501
9874
|
expandable &&
|
|
9502
9875
|
m('li.sidenav-expand-toggle', {
|
|
9876
|
+
class: `mm-layout-row mm-layout-row--center ${position === 'right' ? 'mm-layout-row--justify-end' : 'mm-layout-row--justify-start'}`,
|
|
9503
9877
|
style: {
|
|
9504
|
-
display: 'flex',
|
|
9505
|
-
'justify-content': position === 'right' ? 'flex-end' : 'flex-start',
|
|
9506
|
-
'align-items': 'center',
|
|
9507
9878
|
padding: '12px 16px',
|
|
9508
9879
|
cursor: 'pointer',
|
|
9509
9880
|
'border-bottom': '1px solid rgba(0,0,0,0.1)',
|
|
@@ -9576,7 +9947,7 @@ const NavbarSubItem = () => {
|
|
|
9576
9947
|
const submenuContent = isRightAligned
|
|
9577
9948
|
? [
|
|
9578
9949
|
// Right-aligned: text on left, icons on right
|
|
9579
|
-
isExpanded && m('span
|
|
9950
|
+
isExpanded && m('span.mm-layout-grow.left-align', text),
|
|
9580
9951
|
icon && isExpanded && renderIcon(icon, { 'font-size': '18px' }),
|
|
9581
9952
|
indicatorIcon,
|
|
9582
9953
|
]
|
|
@@ -9584,18 +9955,22 @@ const NavbarSubItem = () => {
|
|
|
9584
9955
|
// Left-aligned: indicator on left, text and icon on right
|
|
9585
9956
|
indicatorIcon,
|
|
9586
9957
|
icon && isExpanded && renderIcon(icon, { 'font-size': '18px', 'margin-left': indicatorIcon ? '8px' : '0' }),
|
|
9587
|
-
isExpanded && m('span', {
|
|
9958
|
+
isExpanded && m('span', { class: icon || indicatorIcon ? 'mm-layout-ml-8' : undefined }, text),
|
|
9588
9959
|
];
|
|
9589
9960
|
return m('li.sidenav-subitem', {
|
|
9590
|
-
class:
|
|
9961
|
+
class: [
|
|
9962
|
+
selected ? 'selected' : '',
|
|
9963
|
+
'mm-layout-row',
|
|
9964
|
+
'mm-layout-row--center',
|
|
9965
|
+
'mm-layout-gap-sm',
|
|
9966
|
+
isRightAligned ? 'mm-layout-row--justify-between' : 'mm-layout-row--justify-start',
|
|
9967
|
+
]
|
|
9968
|
+
.filter(Boolean)
|
|
9969
|
+
.join(' ') || undefined,
|
|
9591
9970
|
style: {
|
|
9592
9971
|
padding: isExpanded ? '0 16px 0 48px' : '0 16px',
|
|
9593
9972
|
cursor: 'pointer',
|
|
9594
|
-
display: 'flex',
|
|
9595
|
-
'align-items': 'center',
|
|
9596
|
-
gap: '8px',
|
|
9597
9973
|
'font-size': '0.9em',
|
|
9598
|
-
'justify-content': isRightAligned ? 'space-between' : 'flex-start',
|
|
9599
9974
|
height: '48px',
|
|
9600
9975
|
'min-height': '48px',
|
|
9601
9976
|
},
|
|
@@ -9612,7 +9987,7 @@ const SidenavItem = () => {
|
|
|
9612
9987
|
let isSubmenuOpen = false;
|
|
9613
9988
|
return {
|
|
9614
9989
|
view: ({ attrs, children }) => {
|
|
9615
|
-
const { text, icon, active = false, disabled = false, onclick, href, className = '', divider = false, subheader = false, submenu = [], submenuMode = 'checkbox', } = attrs;
|
|
9990
|
+
const { text, icon, active = false, disabled = false, onclick, href, className = '', divider = false, subheader = false, submenu = [], submenuMode = 'checkbox', title, tooltip, tooltipWhenCollapsedOnly = true, } = attrs;
|
|
9616
9991
|
if (divider) {
|
|
9617
9992
|
return m('li.divider');
|
|
9618
9993
|
}
|
|
@@ -9637,39 +10012,45 @@ const SidenavItem = () => {
|
|
|
9637
10012
|
const isExpanded = attrs._isExpanded !== false;
|
|
9638
10013
|
const position = attrs._position || 'left';
|
|
9639
10014
|
const isRightAligned = position === 'right';
|
|
10015
|
+
const tooltipText = title || tooltip || text;
|
|
10016
|
+
const shouldShowTooltip = tooltipWhenCollapsedOnly ? !isExpanded : true;
|
|
9640
10017
|
// In expanded mode, icons are at the outside edge
|
|
9641
10018
|
// In collapsed mode, icons are centered
|
|
9642
10019
|
const content = isRightAligned
|
|
9643
10020
|
? [
|
|
9644
10021
|
// 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),
|
|
10022
|
+
isExpanded && m('span.sidenav-item-text.mm-layout-grow.mm-layout-mr-8.left-align', text || children),
|
|
9647
10023
|
renderIcon(icon, { 'min-width': '24px', width: '24px' }),
|
|
9648
10024
|
]
|
|
9649
10025
|
: [
|
|
9650
10026
|
// Left-aligned: icon on left, text on right
|
|
9651
10027
|
renderIcon(icon, { 'min-width': '24px', width: '24px' }),
|
|
9652
|
-
isExpanded && m('span.sidenav-item-text
|
|
10028
|
+
isExpanded && m('span.sidenav-item-text.mm-layout-grow.mm-layout-ml-8', text || children),
|
|
9653
10029
|
];
|
|
9654
10030
|
const linkStyle = {
|
|
9655
|
-
display: 'flex',
|
|
9656
|
-
'align-items': 'center',
|
|
9657
10031
|
padding: isExpanded ? '12px 16px' : '12px 18px',
|
|
9658
10032
|
'justify-content': isExpanded ? (isRightAligned ? 'flex-end' : 'flex-start') : 'center',
|
|
9659
10033
|
};
|
|
10034
|
+
const linkClass = 'mm-layout-row mm-layout-row--center';
|
|
9660
10035
|
const mainItem = href && !disabled
|
|
9661
10036
|
? m('li', { class: itemClasses }, [
|
|
9662
10037
|
m('a', {
|
|
9663
10038
|
href,
|
|
9664
10039
|
onclick: handleMainClick,
|
|
10040
|
+
class: linkClass,
|
|
9665
10041
|
style: linkStyle,
|
|
10042
|
+
title: shouldShowTooltip ? tooltipText : undefined,
|
|
10043
|
+
'aria-label': shouldShowTooltip ? tooltipText : undefined,
|
|
9666
10044
|
}, content),
|
|
9667
10045
|
])
|
|
9668
10046
|
: m('li', { class: itemClasses }, [
|
|
9669
10047
|
m('a', {
|
|
9670
10048
|
onclick: handleMainClick,
|
|
9671
10049
|
href: '#!',
|
|
10050
|
+
class: linkClass,
|
|
9672
10051
|
style: linkStyle,
|
|
10052
|
+
title: shouldShowTooltip ? tooltipText : undefined,
|
|
10053
|
+
'aria-label': shouldShowTooltip ? tooltipText : undefined,
|
|
9673
10054
|
}, content),
|
|
9674
10055
|
]);
|
|
9675
10056
|
// Return main item with submenu if applicable
|
|
@@ -10185,8 +10566,9 @@ const TreeNodeComponent = () => {
|
|
|
10185
10566
|
const childIsFocused = ((_e = attrs.treeState) === null || _e === void 0 ? void 0 : _e.focusedNodeId) === child.id;
|
|
10186
10567
|
// Calculate if this child is last in branch
|
|
10187
10568
|
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)
|
|
10569
|
+
const childIsLastInBranch = ((_f = attrs.treeAttrs) === null || _f === void 0 ? void 0 : _f.data)
|
|
10570
|
+
? isNodeLastInBranch(childPath, attrs.treeAttrs.data)
|
|
10571
|
+
: false;
|
|
10190
10572
|
return m(TreeNodeComponent, {
|
|
10191
10573
|
key: child.id,
|
|
10192
10574
|
node: child,
|
|
@@ -10319,10 +10701,7 @@ const TreeView = () => {
|
|
|
10319
10701
|
view: ({ attrs }) => {
|
|
10320
10702
|
const { data, className, style, id, selectionMode = 'single', showConnectors = true } = attrs;
|
|
10321
10703
|
return m('div.tree-view', {
|
|
10322
|
-
class: [
|
|
10323
|
-
className,
|
|
10324
|
-
showConnectors && 'show-connectors'
|
|
10325
|
-
].filter(Boolean).join(' ') || undefined,
|
|
10704
|
+
class: [className, showConnectors && 'show-connectors'].filter(Boolean).join(' ') || undefined,
|
|
10326
10705
|
style,
|
|
10327
10706
|
id,
|
|
10328
10707
|
role: selectionMode === 'multiple' ? 'listbox' : 'tree',
|
|
@@ -11020,6 +11399,7 @@ const Rating = () => {
|
|
|
11020
11399
|
};
|
|
11021
11400
|
};
|
|
11022
11401
|
|
|
11402
|
+
const MOBILE_LAYOUT_BREAKPOINT = 600;
|
|
11023
11403
|
/** Create a LikertScale component */
|
|
11024
11404
|
const LikertScale = () => {
|
|
11025
11405
|
const state = {
|
|
@@ -11082,6 +11462,22 @@ const LikertScale = () => {
|
|
|
11082
11462
|
return 'likert-scale--responsive';
|
|
11083
11463
|
}
|
|
11084
11464
|
};
|
|
11465
|
+
const isVerticalLayout = (layout) => {
|
|
11466
|
+
if (layout === 'vertical')
|
|
11467
|
+
return true;
|
|
11468
|
+
if (layout === 'horizontal')
|
|
11469
|
+
return false;
|
|
11470
|
+
return typeof window !== 'undefined' ? window.innerWidth <= MOBILE_LAYOUT_BREAKPOINT : false;
|
|
11471
|
+
};
|
|
11472
|
+
const getInlineAnchorLabel = (value, min, max, middleValue, startLabel, middleLabel, endLabel) => {
|
|
11473
|
+
if (value === min && startLabel)
|
|
11474
|
+
return startLabel;
|
|
11475
|
+
if (middleLabel && middleValue !== undefined && value === middleValue)
|
|
11476
|
+
return middleLabel;
|
|
11477
|
+
if (value === max && endLabel)
|
|
11478
|
+
return endLabel;
|
|
11479
|
+
return undefined;
|
|
11480
|
+
};
|
|
11085
11481
|
const handleChange = (attrs, newValue) => {
|
|
11086
11482
|
var _a;
|
|
11087
11483
|
if (attrs.readonly || attrs.disabled)
|
|
@@ -11128,7 +11524,7 @@ const LikertScale = () => {
|
|
|
11128
11524
|
const LikertScaleItem = () => {
|
|
11129
11525
|
return {
|
|
11130
11526
|
view: ({ attrs }) => {
|
|
11131
|
-
const { value, currentValue, showNumber, showTooltip, tooltipLabel, groupId, name, disabled, readonly, onchange, } = attrs;
|
|
11527
|
+
const { value, currentValue, showNumber, showTooltip, tooltipLabel, groupId, name, disabled, readonly, anchorLabel, onchange, } = attrs;
|
|
11132
11528
|
const radioId = `${groupId}-${value}`;
|
|
11133
11529
|
const isChecked = currentValue === value;
|
|
11134
11530
|
return m('.likert-scale__item.no-select', {
|
|
@@ -11155,6 +11551,7 @@ const LikertScale = () => {
|
|
|
11155
11551
|
m('label.likert-scale__label', {
|
|
11156
11552
|
for: radioId,
|
|
11157
11553
|
}),
|
|
11554
|
+
anchorLabel && m('.likert-scale__item-anchor', anchorLabel),
|
|
11158
11555
|
// Tooltip (optional)
|
|
11159
11556
|
showTooltip && tooltipLabel && m('.likert-scale__tooltip', tooltipLabel),
|
|
11160
11557
|
]);
|
|
@@ -11178,6 +11575,8 @@ const LikertScale = () => {
|
|
|
11178
11575
|
const { min = 1, max = 5, step = 1, size = 'medium', density = 'standard', layout = 'responsive', className = '', style = {}, readonly = false, disabled = false, id = state.id, name, label, description, isMandatory, startLabel, middleLabel, endLabel, showNumbers = false, showTooltips = false, tooltipLabels, alignLabels = false } = attrs, ariaAttrs = __rest(attrs, ["min", "max", "step", "size", "density", "layout", "className", "style", "readonly", "disabled", "id", "name", "label", "description", "isMandatory", "startLabel", "middleLabel", "endLabel", "showNumbers", "showTooltips", "tooltipLabels", "alignLabels"]);
|
|
11179
11576
|
const currentValue = getCurrentValue(attrs);
|
|
11180
11577
|
const itemCount = Math.floor((max - min) / step) + 1;
|
|
11578
|
+
const useInlineAnchors = isVerticalLayout(layout);
|
|
11579
|
+
const middleValue = middleLabel ? min + Math.floor((itemCount - 1) / 2) * step : undefined;
|
|
11181
11580
|
// Generate scale values
|
|
11182
11581
|
const scaleValues = Array.from({ length: itemCount }, (_, i) => min + i * step);
|
|
11183
11582
|
return m('.likert-scale', {
|
|
@@ -11226,6 +11625,9 @@ const LikertScale = () => {
|
|
|
11226
11625
|
showNumber: showNumbers,
|
|
11227
11626
|
showTooltip: showTooltips,
|
|
11228
11627
|
tooltipLabel: tooltipLabels === null || tooltipLabels === void 0 ? void 0 : tooltipLabels[value - min],
|
|
11628
|
+
anchorLabel: useInlineAnchors
|
|
11629
|
+
? getInlineAnchorLabel(value, min, max, middleValue, startLabel, middleLabel, endLabel)
|
|
11630
|
+
: undefined,
|
|
11229
11631
|
groupId: state.groupId,
|
|
11230
11632
|
name,
|
|
11231
11633
|
disabled,
|
|
@@ -11233,7 +11635,8 @@ const LikertScale = () => {
|
|
|
11233
11635
|
onchange: (v) => handleChange(attrs, v),
|
|
11234
11636
|
}))),
|
|
11235
11637
|
// Scale anchors
|
|
11236
|
-
|
|
11638
|
+
!useInlineAnchors &&
|
|
11639
|
+
(startLabel || middleLabel || endLabel) &&
|
|
11237
11640
|
m('.likert-scale__anchors', [
|
|
11238
11641
|
startLabel && m('.likert-scale__anchor.likert-scale__anchor--start', startLabel),
|
|
11239
11642
|
middleLabel && m('.likert-scale__anchor.likert-scale__anchor--middle', middleLabel),
|
|
@@ -11418,6 +11821,9 @@ const CircularProgress = () => {
|
|
|
11418
11821
|
const { mode = 'indeterminate', value = 0, max = 100, size = 'medium', color = 'teal', colorIntensity, label, showPercentage = false, className = '', style = {}, id = state.id, 'aria-label': ariaLabel, 'aria-valuemin': ariaValueMin = 0, 'aria-valuemax': ariaValueMax = max, 'aria-valuenow': ariaValueNow, 'aria-valuetext': ariaValueText } = attrs, params = __rest(attrs, ["mode", "value", "max", "size", "color", "colorIntensity", "label", "showPercentage", "className", "style", "id", 'aria-label', 'aria-valuemin', 'aria-valuemax', 'aria-valuenow', 'aria-valuetext']);
|
|
11419
11822
|
const isDeterminate = mode === 'determinate';
|
|
11420
11823
|
const sizePixels = SIZE_MAP[size];
|
|
11824
|
+
const styleValue = typeof style === 'string'
|
|
11825
|
+
? `width:${sizePixels}px;height:${sizePixels}px;${style}`
|
|
11826
|
+
: Object.assign({ width: `${sizePixels}px`, height: `${sizePixels}px` }, style);
|
|
11421
11827
|
const { radius, circumference, strokeDashoffset, percentage } = isDeterminate
|
|
11422
11828
|
? calculateStrokeProperties(sizePixels, value, max)
|
|
11423
11829
|
: { radius: 0, circumference: 0, strokeDashoffset: 0, percentage: 0 };
|
|
@@ -11444,7 +11850,7 @@ const CircularProgress = () => {
|
|
|
11444
11850
|
: {
|
|
11445
11851
|
'aria-valuetext': ariaValueText || label || 'Loading',
|
|
11446
11852
|
};
|
|
11447
|
-
return m('.circular-progress', Object.assign(Object.assign(Object.assign({}, params), { className: classNames, style:
|
|
11853
|
+
return m('.circular-progress', Object.assign(Object.assign(Object.assign({}, params), { className: classNames, style: styleValue, id, role: 'progressbar', 'aria-label': ariaLabel || (isDeterminate ? `Progress: ${Math.round(percentage)}%` : 'Loading') }), ariaAttrs), [
|
|
11448
11854
|
// SVG circle
|
|
11449
11855
|
m('svg.circular-progress__svg', {
|
|
11450
11856
|
viewBox: `0 0 ${sizePixels} ${sizePixels}`,
|
|
@@ -11509,13 +11915,7 @@ const LinearProgress = () => {
|
|
|
11509
11915
|
// Determine label content
|
|
11510
11916
|
const labelContent = label !== undefined ? label : showPercentage && isDeterminate ? `${Math.round(percentage)}%` : '';
|
|
11511
11917
|
// Build class names
|
|
11512
|
-
const classNames = [
|
|
11513
|
-
'linear-progress',
|
|
11514
|
-
getColorClass(color, colorIntensity),
|
|
11515
|
-
className,
|
|
11516
|
-
]
|
|
11517
|
-
.filter(Boolean)
|
|
11518
|
-
.join(' ');
|
|
11918
|
+
const classNames = ['linear-progress', getColorClass(color, colorIntensity), className].filter(Boolean).join(' ');
|
|
11519
11919
|
// ARIA attributes
|
|
11520
11920
|
const ariaAttrs = isDeterminate
|
|
11521
11921
|
? {
|
|
@@ -11592,10 +11992,12 @@ exports.DigitalClock = DigitalClock;
|
|
|
11592
11992
|
exports.DoubleRangeSlider = DoubleRangeSlider;
|
|
11593
11993
|
exports.Dropdown = Dropdown;
|
|
11594
11994
|
exports.EmailInput = EmailInput;
|
|
11995
|
+
exports.Fieldset = Fieldset;
|
|
11595
11996
|
exports.FileInput = FileInput;
|
|
11596
11997
|
exports.FileUpload = FileUpload;
|
|
11597
11998
|
exports.FlatButton = FlatButton;
|
|
11598
11999
|
exports.FloatingActionButton = FloatingActionButton;
|
|
12000
|
+
exports.FormSection = FormSection;
|
|
11599
12001
|
exports.HelperText = HelperText;
|
|
11600
12002
|
exports.Icon = Icon;
|
|
11601
12003
|
exports.IconButton = IconButton;
|
|
@@ -11647,6 +12049,7 @@ exports.TimeRangePicker = TimeRangePicker;
|
|
|
11647
12049
|
exports.Timeline = Timeline;
|
|
11648
12050
|
exports.Toast = Toast;
|
|
11649
12051
|
exports.ToastComponent = ToastComponent;
|
|
12052
|
+
exports.ToggleButton = ToggleButton;
|
|
11650
12053
|
exports.ToggleGroup = ToggleGroup;
|
|
11651
12054
|
exports.Tooltip = Tooltip;
|
|
11652
12055
|
exports.TooltipComponent = TooltipComponent;
|
|
@@ -11655,10 +12058,14 @@ exports.UrlInput = UrlInput;
|
|
|
11655
12058
|
exports.Wizard = Wizard;
|
|
11656
12059
|
exports.addLeadingZero = addLeadingZero;
|
|
11657
12060
|
exports.clearPortal = clearPortal;
|
|
12061
|
+
exports.createAsyncComboboxState = createAsyncComboboxState;
|
|
11658
12062
|
exports.createBreadcrumb = createBreadcrumb;
|
|
11659
12063
|
exports.formatTime = formatTime;
|
|
11660
12064
|
exports.generateHourOptions = generateHourOptions;
|
|
11661
12065
|
exports.generateMinuteOptions = generateMinuteOptions;
|
|
12066
|
+
exports.getComboboxKeyResult = getComboboxKeyResult;
|
|
12067
|
+
exports.getComboboxOptionId = getComboboxOptionId;
|
|
12068
|
+
exports.getComboboxViewState = getComboboxViewState;
|
|
11662
12069
|
exports.getDropdownStyles = getDropdownStyles;
|
|
11663
12070
|
exports.getPortalContainer = getPortalContainer;
|
|
11664
12071
|
exports.initPushpins = initPushpins;
|
|
@@ -11667,15 +12074,23 @@ exports.isNumeric = isNumeric;
|
|
|
11667
12074
|
exports.isTimeDisabled = isTimeDisabled;
|
|
11668
12075
|
exports.isValidationError = isValidationError;
|
|
11669
12076
|
exports.isValidationSuccess = isValidationSuccess;
|
|
12077
|
+
exports.normalizeSelection = normalizeSelection;
|
|
11670
12078
|
exports.padLeft = padLeft;
|
|
11671
12079
|
exports.parseTime = parseTime;
|
|
11672
12080
|
exports.range = range;
|
|
12081
|
+
exports.rejectAsyncComboboxRequest = rejectAsyncComboboxRequest;
|
|
11673
12082
|
exports.releasePortalContainer = releasePortalContainer;
|
|
12083
|
+
exports.renderFieldChrome = renderFieldChrome;
|
|
11674
12084
|
exports.renderToPortal = renderToPortal;
|
|
12085
|
+
exports.resolveAsyncComboboxRequest = resolveAsyncComboboxRequest;
|
|
12086
|
+
exports.resolveControllableValue = resolveControllableValue;
|
|
11675
12087
|
exports.scrollToValue = scrollToValue;
|
|
11676
12088
|
exports.snapToNearestItem = snapToNearestItem;
|
|
11677
12089
|
exports.sortOptions = sortOptions;
|
|
12090
|
+
exports.startAsyncComboboxRequest = startAsyncComboboxRequest;
|
|
12091
|
+
exports.syncPortalContent = syncPortalContent;
|
|
11678
12092
|
exports.timeToMinutes = timeToMinutes;
|
|
11679
12093
|
exports.toast = toast;
|
|
11680
12094
|
exports.uniqueId = uniqueId;
|
|
11681
12095
|
exports.uuid4 = uuid4;
|
|
12096
|
+
//# sourceMappingURL=index.js.map
|