mithril-materialized 3.5.9 → 3.6.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/dist/collapsible.d.ts +2 -0
- package/dist/components.css +81 -45
- package/dist/core.css +15 -4
- package/dist/dropdown.d.ts +2 -0
- package/dist/forms.css +15 -4
- package/dist/index.css +106 -49
- package/dist/index.esm.js +204 -114
- package/dist/index.js +204 -113
- package/dist/index.min.css +2 -2
- package/dist/index.umd.js +204 -113
- package/dist/material-box.d.ts +1 -1
- package/dist/pickers.css +10 -0
- package/dist/search-select.d.ts +12 -1
- package/dist/select.d.ts +4 -0
- package/dist/timepicker.d.ts +0 -4
- package/dist/utils.d.ts +19 -0
- package/package.json +2 -3
- package/sass/components/_collapsible.scss +69 -17
- package/sass/components/_datatable.scss +7 -19
- package/sass/components/_materialbox.scss +23 -14
- package/sass/components/_theme-variables.scss +7 -0
- package/sass/components/_timepicker.scss +10 -0
- package/sass/components/forms/_input-fields.scss +10 -5
- package/sass/components/forms/_select.scss +23 -19
package/dist/index.esm.js
CHANGED
|
@@ -62,6 +62,28 @@ const uuid4 = () => {
|
|
|
62
62
|
};
|
|
63
63
|
/** Check if a string or number is numeric. @see https://stackoverflow.com/a/9716488/319711 */
|
|
64
64
|
const isNumeric = (n) => !isNaN(parseFloat(n)) && isFinite(n);
|
|
65
|
+
/**
|
|
66
|
+
* Sort options array based on sorting configuration
|
|
67
|
+
* @param options - Array of options to sort
|
|
68
|
+
* @param sortConfig - Sort configuration: 'asc', 'desc', 'none', or custom comparator function
|
|
69
|
+
* @returns Sorted array (or original if 'none' or undefined)
|
|
70
|
+
*/
|
|
71
|
+
const sortOptions = (options, sortConfig) => {
|
|
72
|
+
if (!sortConfig || sortConfig === 'none') {
|
|
73
|
+
return options;
|
|
74
|
+
}
|
|
75
|
+
const sorted = [...options]; // Create a copy to avoid mutating original
|
|
76
|
+
if (typeof sortConfig === 'function') {
|
|
77
|
+
return sorted.sort(sortConfig);
|
|
78
|
+
}
|
|
79
|
+
// Sort by label, fallback to id if no label
|
|
80
|
+
return sorted.sort((a, b) => {
|
|
81
|
+
const aLabel = (a.label || a.id.toString()).toLowerCase();
|
|
82
|
+
const bLabel = (b.label || b.id.toString()).toLowerCase();
|
|
83
|
+
const comparison = aLabel.localeCompare(bLabel);
|
|
84
|
+
return sortConfig === 'asc' ? comparison : -comparison;
|
|
85
|
+
});
|
|
86
|
+
};
|
|
65
87
|
/**
|
|
66
88
|
* Pad left, default width 2 with a '0'
|
|
67
89
|
*
|
|
@@ -1409,19 +1431,18 @@ const CollapsibleItem = () => {
|
|
|
1409
1431
|
onclick: onToggle,
|
|
1410
1432
|
}, [
|
|
1411
1433
|
iconName ? m('i.material-icons', iconName) : undefined,
|
|
1412
|
-
header
|
|
1434
|
+
header
|
|
1435
|
+
? typeof header === 'string'
|
|
1436
|
+
? m('span.collapsible-header-text', header)
|
|
1437
|
+
: m('.collapsible-header-content', header)
|
|
1438
|
+
: undefined,
|
|
1413
1439
|
])
|
|
1414
1440
|
: undefined,
|
|
1415
1441
|
m('.collapsible-body', {
|
|
1416
1442
|
style: {
|
|
1417
1443
|
display: isActive ? 'block' : 'none',
|
|
1418
|
-
transition: 'display 0.3s ease',
|
|
1419
1444
|
},
|
|
1420
|
-
},
|
|
1421
|
-
m('.collapsible-body-content', {
|
|
1422
|
-
style: { padding: '2rem' },
|
|
1423
|
-
}, body ? (typeof body === 'string' ? m('div', { innerHTML: body }) : body) : undefined),
|
|
1424
|
-
]),
|
|
1445
|
+
}, body ? (typeof body === 'string' ? m('div', { innerHTML: body }) : body) : undefined),
|
|
1425
1446
|
]);
|
|
1426
1447
|
},
|
|
1427
1448
|
};
|
|
@@ -1444,7 +1465,7 @@ const Collapsible = () => {
|
|
|
1444
1465
|
});
|
|
1445
1466
|
},
|
|
1446
1467
|
view: ({ attrs }) => {
|
|
1447
|
-
const { items, accordion = true, class: c, className, style, id } = attrs;
|
|
1468
|
+
const { items, header, accordion = true, class: c, className, style, id } = attrs;
|
|
1448
1469
|
const toggleItem = (index) => {
|
|
1449
1470
|
if (accordion) {
|
|
1450
1471
|
// Accordion mode: only one item can be active
|
|
@@ -1466,12 +1487,22 @@ const Collapsible = () => {
|
|
|
1466
1487
|
}
|
|
1467
1488
|
}
|
|
1468
1489
|
};
|
|
1490
|
+
const collapsibleItems = items.map((item, index) => m(CollapsibleItem, Object.assign(Object.assign({}, item), { key: index, isActive: state.activeItems.has(index), onToggle: () => toggleItem(index) })));
|
|
1469
1491
|
return items && items.length > 0
|
|
1470
|
-
?
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1492
|
+
? header
|
|
1493
|
+
? m('ul.collapsible.with-header', {
|
|
1494
|
+
class: c || className,
|
|
1495
|
+
style,
|
|
1496
|
+
id,
|
|
1497
|
+
}, [
|
|
1498
|
+
m('li.collapsible-main-header', m('h4', typeof header === 'string' ? header : header)),
|
|
1499
|
+
collapsibleItems,
|
|
1500
|
+
])
|
|
1501
|
+
: m('ul.collapsible', {
|
|
1502
|
+
class: c || className,
|
|
1503
|
+
style,
|
|
1504
|
+
id,
|
|
1505
|
+
}, collapsibleItems)
|
|
1475
1506
|
: undefined;
|
|
1476
1507
|
},
|
|
1477
1508
|
};
|
|
@@ -1575,7 +1606,7 @@ const Collection = () => {
|
|
|
1575
1606
|
};
|
|
1576
1607
|
};
|
|
1577
1608
|
|
|
1578
|
-
const defaultI18n$
|
|
1609
|
+
const defaultI18n$3 = {
|
|
1579
1610
|
cancel: 'Cancel',
|
|
1580
1611
|
clear: 'Clear',
|
|
1581
1612
|
done: 'Ok',
|
|
@@ -1683,9 +1714,9 @@ const DatePicker = () => {
|
|
|
1683
1714
|
else if (attrs.displayFormat) {
|
|
1684
1715
|
finalFormat = attrs.displayFormat;
|
|
1685
1716
|
}
|
|
1686
|
-
const merged = Object.assign({ autoClose: false, format: finalFormat, parse: null, defaultDate: null, setDefaultDate: false, disableWeekends: false, disableDayFn: null, firstDay: 0, minDate: null, maxDate: null, yearRange, showClearBtn: false, showWeekNumbers: false, weekNumbering: 'iso', i18n: defaultI18n$
|
|
1717
|
+
const merged = Object.assign({ autoClose: false, format: finalFormat, parse: null, defaultDate: null, setDefaultDate: false, disableWeekends: false, disableDayFn: null, firstDay: 0, minDate: null, maxDate: null, yearRange, showClearBtn: false, showWeekNumbers: false, weekNumbering: 'iso', i18n: defaultI18n$3, onSelect: null, onOpen: null, onClose: null }, attrs);
|
|
1687
1718
|
// Merge i18n properly
|
|
1688
|
-
merged.i18n = Object.assign(Object.assign({}, defaultI18n$
|
|
1719
|
+
merged.i18n = Object.assign(Object.assign({}, defaultI18n$3), attrs.i18n);
|
|
1689
1720
|
return merged;
|
|
1690
1721
|
};
|
|
1691
1722
|
const toString = (date, format) => {
|
|
@@ -2136,11 +2167,11 @@ const DatePicker = () => {
|
|
|
2136
2167
|
prevMonth();
|
|
2137
2168
|
},
|
|
2138
2169
|
}, m('svg', {
|
|
2139
|
-
fill: '#000000',
|
|
2140
2170
|
height: '24',
|
|
2141
2171
|
viewBox: '0 0 24 24',
|
|
2142
2172
|
width: '24',
|
|
2143
2173
|
xmlns: 'http://www.w3.org/2000/svg',
|
|
2174
|
+
style: 'fill: var(--mm-text-primary, rgba(0, 0, 0, 0.87));',
|
|
2144
2175
|
}, [
|
|
2145
2176
|
m('path', { d: 'M15.41 16.09l-4.58-4.59 4.58-4.59L14 5.5l-6 6 6 6z' }),
|
|
2146
2177
|
m('path', { d: 'M0-.5h24v24H0z', fill: 'none' }),
|
|
@@ -2202,11 +2233,11 @@ const DatePicker = () => {
|
|
|
2202
2233
|
nextMonth();
|
|
2203
2234
|
},
|
|
2204
2235
|
}, m('svg', {
|
|
2205
|
-
fill: '#000000',
|
|
2206
2236
|
height: '24',
|
|
2207
2237
|
viewBox: '0 0 24 24',
|
|
2208
2238
|
width: '24',
|
|
2209
2239
|
xmlns: 'http://www.w3.org/2000/svg',
|
|
2240
|
+
style: 'fill: var(--mm-text-primary, rgba(0, 0, 0, 0.87));',
|
|
2210
2241
|
}, [
|
|
2211
2242
|
m('path', { d: 'M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z' }),
|
|
2212
2243
|
m('path', { d: 'M0-.25h24v24H0z', fill: 'none' }),
|
|
@@ -3219,19 +3250,29 @@ const TextArea = () => {
|
|
|
3219
3250
|
if (!controlled && attrs.defaultValue !== undefined) {
|
|
3220
3251
|
textarea.value = String(attrs.defaultValue);
|
|
3221
3252
|
}
|
|
3222
|
-
// Height will be calculated by hidden div
|
|
3223
3253
|
// Update character count state for counter component
|
|
3224
3254
|
if (maxLength) {
|
|
3225
3255
|
state.currentLength = textarea.value.length;
|
|
3226
3256
|
}
|
|
3257
|
+
// Calculate initial height after DOM is fully ready
|
|
3258
|
+
// Use requestAnimationFrame to ensure layout is complete
|
|
3259
|
+
if (state.hiddenDiv) {
|
|
3260
|
+
requestAnimationFrame(() => {
|
|
3261
|
+
if (state.hiddenDiv) {
|
|
3262
|
+
updateHeight(textarea, state.hiddenDiv);
|
|
3263
|
+
m.redraw();
|
|
3264
|
+
}
|
|
3265
|
+
});
|
|
3266
|
+
}
|
|
3227
3267
|
}, onupdate: ({ dom }) => {
|
|
3228
3268
|
const textarea = dom;
|
|
3229
|
-
|
|
3230
|
-
textarea.style.height = state.height;
|
|
3231
|
-
// Trigger height recalculation when value changes programmatically
|
|
3269
|
+
// Recalculate and apply height
|
|
3232
3270
|
if (state.hiddenDiv) {
|
|
3233
3271
|
updateHeight(textarea, state.hiddenDiv);
|
|
3234
3272
|
}
|
|
3273
|
+
if (state.height) {
|
|
3274
|
+
textarea.style.height = state.height;
|
|
3275
|
+
}
|
|
3235
3276
|
}, onfocus: () => {
|
|
3236
3277
|
state.active = true;
|
|
3237
3278
|
}, oninput: (e) => {
|
|
@@ -4520,7 +4561,7 @@ const Dropdown = () => {
|
|
|
4520
4561
|
// Create dropdown with proper positioning
|
|
4521
4562
|
const dropdownVnode = m('ul.dropdown-content.select-dropdown', {
|
|
4522
4563
|
tabindex: 0,
|
|
4523
|
-
style: getPortalStyles(state.inputRef),
|
|
4564
|
+
style: Object.assign(Object.assign({}, getPortalStyles(state.inputRef)), (attrs.maxHeight ? { maxHeight: attrs.maxHeight } : {})),
|
|
4524
4565
|
oncreate: ({ dom }) => {
|
|
4525
4566
|
state.dropdownRef = dom;
|
|
4526
4567
|
},
|
|
@@ -4632,7 +4673,7 @@ const Dropdown = () => {
|
|
|
4632
4673
|
onremove: () => {
|
|
4633
4674
|
state.dropdownRef = null;
|
|
4634
4675
|
},
|
|
4635
|
-
style: getDropdownStyles(state.inputRef, true, items, true),
|
|
4676
|
+
style: Object.assign(Object.assign({}, getDropdownStyles(state.inputRef, true, items, true)), (attrs.maxHeight ? { maxHeight: attrs.maxHeight } : {})),
|
|
4636
4677
|
}, items.map((item) => {
|
|
4637
4678
|
if (item.divider) {
|
|
4638
4679
|
return m('li.divider');
|
|
@@ -4772,7 +4813,7 @@ const FloatingActionButton = () => {
|
|
|
4772
4813
|
|
|
4773
4814
|
/**
|
|
4774
4815
|
* Pure TypeScript MaterialBox - creates an image lightbox that fills the screen when clicked
|
|
4775
|
-
*
|
|
4816
|
+
* Uses CSS classes from _materialbox.scss
|
|
4776
4817
|
*/
|
|
4777
4818
|
const MaterialBox = () => {
|
|
4778
4819
|
const state = {
|
|
@@ -4788,21 +4829,11 @@ const MaterialBox = () => {
|
|
|
4788
4829
|
state.originalImage = img;
|
|
4789
4830
|
if (attrs.onOpenStart)
|
|
4790
4831
|
attrs.onOpenStart();
|
|
4832
|
+
const inDuration = attrs.inDuration || 275;
|
|
4791
4833
|
// Create overlay
|
|
4792
4834
|
const overlay = document.createElement('div');
|
|
4793
4835
|
overlay.className = 'materialbox-overlay';
|
|
4794
|
-
overlay.style.
|
|
4795
|
-
position: fixed;
|
|
4796
|
-
top: 0;
|
|
4797
|
-
left: 0;
|
|
4798
|
-
right: 0;
|
|
4799
|
-
bottom: 0;
|
|
4800
|
-
background-color: rgba(0, 0, 0, 0.85);
|
|
4801
|
-
z-index: 1000;
|
|
4802
|
-
opacity: 0;
|
|
4803
|
-
transition: opacity ${attrs.inDuration || 275}ms ease;
|
|
4804
|
-
cursor: zoom-out;
|
|
4805
|
-
`;
|
|
4836
|
+
overlay.style.transition = `opacity ${inDuration}ms ease`;
|
|
4806
4837
|
// Create enlarged image
|
|
4807
4838
|
const enlargedImg = document.createElement('img');
|
|
4808
4839
|
enlargedImg.src = img.src;
|
|
@@ -4823,36 +4854,18 @@ const MaterialBox = () => {
|
|
|
4823
4854
|
finalWidth = maxHeight * aspectRatio;
|
|
4824
4855
|
}
|
|
4825
4856
|
// Set initial position and size (same as original image)
|
|
4826
|
-
enlargedImg.style.
|
|
4827
|
-
|
|
4828
|
-
|
|
4829
|
-
|
|
4830
|
-
|
|
4831
|
-
height: ${imgRect.height}px;
|
|
4832
|
-
transition: all ${attrs.inDuration || 275}ms ease;
|
|
4833
|
-
cursor: zoom-out;
|
|
4834
|
-
max-width: none;
|
|
4835
|
-
z-index: 1001;
|
|
4836
|
-
`;
|
|
4857
|
+
enlargedImg.style.top = `${imgRect.top}px`;
|
|
4858
|
+
enlargedImg.style.left = `${imgRect.left}px`;
|
|
4859
|
+
enlargedImg.style.width = `${imgRect.width}px`;
|
|
4860
|
+
enlargedImg.style.height = `${imgRect.height}px`;
|
|
4861
|
+
enlargedImg.style.transition = `all ${inDuration}ms ease`;
|
|
4837
4862
|
// Add caption if provided
|
|
4838
4863
|
let caption = null;
|
|
4839
4864
|
if (attrs.caption) {
|
|
4840
4865
|
caption = document.createElement('div');
|
|
4841
4866
|
caption.className = 'materialbox-caption';
|
|
4842
4867
|
caption.textContent = attrs.caption;
|
|
4843
|
-
caption.style.
|
|
4844
|
-
position: fixed;
|
|
4845
|
-
bottom: 20px;
|
|
4846
|
-
left: 50%;
|
|
4847
|
-
transform: translateX(-50%);
|
|
4848
|
-
color: white;
|
|
4849
|
-
font-size: 16px;
|
|
4850
|
-
text-align: center;
|
|
4851
|
-
opacity: 0;
|
|
4852
|
-
transition: opacity ${attrs.inDuration || 275}ms ease ${attrs.inDuration || 275}ms;
|
|
4853
|
-
z-index: 1002;
|
|
4854
|
-
pointer-events: none;
|
|
4855
|
-
`;
|
|
4868
|
+
caption.style.transition = `opacity ${inDuration}ms ease ${inDuration}ms`;
|
|
4856
4869
|
}
|
|
4857
4870
|
// Add to DOM
|
|
4858
4871
|
document.body.appendChild(overlay);
|
|
@@ -4887,7 +4900,7 @@ const MaterialBox = () => {
|
|
|
4887
4900
|
setTimeout(() => {
|
|
4888
4901
|
if (attrs.onOpenEnd)
|
|
4889
4902
|
attrs.onOpenEnd();
|
|
4890
|
-
},
|
|
4903
|
+
}, inDuration);
|
|
4891
4904
|
};
|
|
4892
4905
|
const closeBox = (attrs) => {
|
|
4893
4906
|
if (!state.isOpen || !state.originalImage || !state.overlay || !state.overlayImage)
|
|
@@ -4944,8 +4957,14 @@ const MaterialBox = () => {
|
|
|
4944
4957
|
},
|
|
4945
4958
|
view: ({ attrs }) => {
|
|
4946
4959
|
const { src, alt, width, height, caption, className, style } = attrs, otherAttrs = __rest(attrs, ["src", "alt", "width", "height", "caption", "className", "style"]);
|
|
4960
|
+
// Build style attribute - handle both string and object styles
|
|
4961
|
+
let imgStyle = style || {};
|
|
4962
|
+
if (typeof style !== 'string') {
|
|
4963
|
+
// If style is an object or undefined, add default styles as object
|
|
4964
|
+
imgStyle = Object.assign({ cursor: 'zoom-in', transition: 'opacity 200ms ease' }, (style || {}));
|
|
4965
|
+
}
|
|
4947
4966
|
return m('img.materialboxed', Object.assign(Object.assign({}, otherAttrs), { src, alt: alt || '', width,
|
|
4948
|
-
height, className: ['materialboxed', className].filter(Boolean).join(' ') || undefined, style:
|
|
4967
|
+
height, className: ['materialboxed', className].filter(Boolean).join(' ') || undefined, style: imgStyle, onclick: (e) => {
|
|
4949
4968
|
e.preventDefault();
|
|
4950
4969
|
openBox(e.target, attrs);
|
|
4951
4970
|
} }));
|
|
@@ -5275,6 +5294,11 @@ const Parallax = () => {
|
|
|
5275
5294
|
};
|
|
5276
5295
|
};
|
|
5277
5296
|
|
|
5297
|
+
const defaultI18n$2 = {
|
|
5298
|
+
cancel: 'Cancel',
|
|
5299
|
+
clear: 'Clear',
|
|
5300
|
+
done: 'Ok',
|
|
5301
|
+
};
|
|
5278
5302
|
const defaultOptions = {
|
|
5279
5303
|
dialRadius: 135,
|
|
5280
5304
|
outerRadius: 105,
|
|
@@ -5285,11 +5309,7 @@ const defaultOptions = {
|
|
|
5285
5309
|
defaultTime: 'now',
|
|
5286
5310
|
fromNow: 0,
|
|
5287
5311
|
showClearBtn: false,
|
|
5288
|
-
i18n:
|
|
5289
|
-
cancel: 'Cancel',
|
|
5290
|
-
clear: 'Clear',
|
|
5291
|
-
done: 'Ok',
|
|
5292
|
-
},
|
|
5312
|
+
i18n: defaultI18n$2,
|
|
5293
5313
|
autoClose: false,
|
|
5294
5314
|
twelveHour: true,
|
|
5295
5315
|
vibrate: true,
|
|
@@ -5439,7 +5459,7 @@ const TimePicker = () => {
|
|
|
5439
5459
|
state.hours = hours;
|
|
5440
5460
|
state.minutes = minutes;
|
|
5441
5461
|
if (state.spanHours) {
|
|
5442
|
-
state.spanHours.innerHTML = state.hours
|
|
5462
|
+
state.spanHours.innerHTML = addLeadingZero(state.hours);
|
|
5443
5463
|
}
|
|
5444
5464
|
if (state.spanMinutes) {
|
|
5445
5465
|
state.spanMinutes.innerHTML = addLeadingZero(state.minutes);
|
|
@@ -5547,7 +5567,7 @@ const TimePicker = () => {
|
|
|
5547
5567
|
}
|
|
5548
5568
|
state[state.currentView] = value;
|
|
5549
5569
|
if (isHours && state.spanHours) {
|
|
5550
|
-
state.spanHours.innerHTML = value
|
|
5570
|
+
state.spanHours.innerHTML = addLeadingZero(value);
|
|
5551
5571
|
}
|
|
5552
5572
|
else if (!isHours && state.spanMinutes) {
|
|
5553
5573
|
state.spanMinutes.innerHTML = addLeadingZero(value);
|
|
@@ -5681,7 +5701,7 @@ const TimePicker = () => {
|
|
|
5681
5701
|
const TimepickerModal = () => {
|
|
5682
5702
|
return {
|
|
5683
5703
|
view: ({ attrs }) => {
|
|
5684
|
-
const {
|
|
5704
|
+
const { i18n, showClearBtn } = attrs;
|
|
5685
5705
|
return [
|
|
5686
5706
|
m('.modal-content.timepicker-container', [
|
|
5687
5707
|
m('.timepicker-digital-display', [
|
|
@@ -5693,7 +5713,7 @@ const TimePicker = () => {
|
|
|
5693
5713
|
oncreate: (vnode) => {
|
|
5694
5714
|
state.spanHours = vnode.dom;
|
|
5695
5715
|
},
|
|
5696
|
-
}, state.hours
|
|
5716
|
+
}, addLeadingZero(state.hours)),
|
|
5697
5717
|
':',
|
|
5698
5718
|
m('span.timepicker-span-minutes', {
|
|
5699
5719
|
class: state.currentView === 'minutes' ? 'text-primary' : '',
|
|
@@ -5773,18 +5793,18 @@ const TimePicker = () => {
|
|
|
5773
5793
|
tabindex: options.twelveHour ? '3' : '1',
|
|
5774
5794
|
style: showClearBtn ? '' : 'visibility: hidden;',
|
|
5775
5795
|
onclick: () => clear(),
|
|
5776
|
-
},
|
|
5796
|
+
}, i18n.clear),
|
|
5777
5797
|
m('.confirmation-btns', [
|
|
5778
5798
|
m('button.btn-flat.timepicker-close.waves-effect', {
|
|
5779
5799
|
type: 'button',
|
|
5780
5800
|
tabindex: options.twelveHour ? '3' : '1',
|
|
5781
5801
|
onclick: () => close(),
|
|
5782
|
-
},
|
|
5802
|
+
}, i18n.cancel),
|
|
5783
5803
|
m('button.btn-flat.timepicker-close.waves-effect', {
|
|
5784
5804
|
type: 'button',
|
|
5785
5805
|
tabindex: options.twelveHour ? '3' : '1',
|
|
5786
5806
|
onclick: () => done(),
|
|
5787
|
-
},
|
|
5807
|
+
}, i18n.done),
|
|
5788
5808
|
]),
|
|
5789
5809
|
]),
|
|
5790
5810
|
]),
|
|
@@ -5801,7 +5821,6 @@ const TimePicker = () => {
|
|
|
5801
5821
|
}
|
|
5802
5822
|
};
|
|
5803
5823
|
const renderPickerToPortal = (attrs) => {
|
|
5804
|
-
const { showClearBtn = false, clearLabel = 'Clear', closeLabel = 'Cancel' } = attrs;
|
|
5805
5824
|
const pickerModal = m('.timepicker-modal-wrapper', {
|
|
5806
5825
|
style: {
|
|
5807
5826
|
position: 'fixed',
|
|
@@ -5844,10 +5863,8 @@ const TimePicker = () => {
|
|
|
5844
5863
|
},
|
|
5845
5864
|
}, [
|
|
5846
5865
|
m(TimepickerModal, {
|
|
5847
|
-
|
|
5848
|
-
|
|
5849
|
-
closeLabel,
|
|
5850
|
-
doneLabel: 'OK',
|
|
5866
|
+
i18n: options.i18n,
|
|
5867
|
+
showClearBtn: options.showClearBtn,
|
|
5851
5868
|
}),
|
|
5852
5869
|
]),
|
|
5853
5870
|
]);
|
|
@@ -5907,7 +5924,7 @@ const TimePicker = () => {
|
|
|
5907
5924
|
}
|
|
5908
5925
|
},
|
|
5909
5926
|
view: ({ attrs }) => {
|
|
5910
|
-
const { id = state.id, label, placeholder, disabled, readonly, required, iconName, helperText, onchange, oninput, useModal = true,
|
|
5927
|
+
const { id = state.id, label, placeholder, disabled, readonly, required, iconName, helperText, onchange, oninput, useModal = true, twelveHour, className: cn1, class: cn2, } = attrs;
|
|
5911
5928
|
const className = cn1 || cn2 || 'col s12';
|
|
5912
5929
|
// Format time value for display
|
|
5913
5930
|
const formatTime = (hours, minutes, use12Hour) => {
|
|
@@ -6353,7 +6370,7 @@ const Select = () => {
|
|
|
6353
6370
|
// Render ungrouped options first
|
|
6354
6371
|
attrs.options
|
|
6355
6372
|
.filter((option) => !option.group)
|
|
6356
|
-
.map((option) => m('li', Object.assign({ class: option.disabled ? 'disabled' : state.focusedIndex === attrs.options.indexOf(option) ? 'focused' : '' }, (option.disabled
|
|
6373
|
+
.map((option) => m('li', Object.assign({ class: `${option.disabled ? 'disabled' : ''}${isSelected(option.id, selectedIds) ? ' selected' : ''}${state.focusedIndex === attrs.options.indexOf(option) ? ' focused' : ''}` }, (option.disabled
|
|
6357
6374
|
? {}
|
|
6358
6375
|
: {
|
|
6359
6376
|
onclick: () => {
|
|
@@ -6430,7 +6447,7 @@ const Select = () => {
|
|
|
6430
6447
|
// Create dropdown with proper positioning
|
|
6431
6448
|
const dropdownVnode = m('ul.dropdown-content.select-dropdown', {
|
|
6432
6449
|
tabindex: 0,
|
|
6433
|
-
style: getPortalStyles(state.inputRef),
|
|
6450
|
+
style: Object.assign(Object.assign({}, getPortalStyles(state.inputRef)), (attrs.maxHeight ? { maxHeight: attrs.maxHeight } : {})),
|
|
6434
6451
|
oncreate: ({ dom }) => {
|
|
6435
6452
|
state.dropdownRef = dom;
|
|
6436
6453
|
},
|
|
@@ -6499,7 +6516,8 @@ const Select = () => {
|
|
|
6499
6516
|
selectedIds = state.internalSelectedIds;
|
|
6500
6517
|
}
|
|
6501
6518
|
const finalClassName = newRow ? `${className} clear` : className;
|
|
6502
|
-
const
|
|
6519
|
+
const selectedOptionsUnsorted = options.filter((opt) => isSelected(opt.id, selectedIds));
|
|
6520
|
+
const selectedOptions = sortOptions(selectedOptionsUnsorted, attrs.sortSelected);
|
|
6503
6521
|
// Update portal dropdown when inside modal
|
|
6504
6522
|
if (state.isInsideModal) {
|
|
6505
6523
|
updatePortalDropdown(attrs, selectedIds, multiple, placeholder);
|
|
@@ -6544,7 +6562,7 @@ const Select = () => {
|
|
|
6544
6562
|
onremove: () => {
|
|
6545
6563
|
state.dropdownRef = null;
|
|
6546
6564
|
},
|
|
6547
|
-
style: getDropdownStyles(state.inputRef, true, options),
|
|
6565
|
+
style: Object.assign(Object.assign({}, getDropdownStyles(state.inputRef, true, options)), (attrs.maxHeight ? { maxHeight: attrs.maxHeight } : {})),
|
|
6548
6566
|
}, renderDropdownContent(attrs, selectedIds, multiple, placeholder)),
|
|
6549
6567
|
m(MaterialIcon, {
|
|
6550
6568
|
name: 'caret',
|
|
@@ -6794,7 +6812,7 @@ const SelectedChip = {
|
|
|
6794
6812
|
]),
|
|
6795
6813
|
};
|
|
6796
6814
|
const DropdownOption = {
|
|
6797
|
-
view: ({ attrs: { option, index, selectedIds, isFocused, onToggle, onMouseOver } }) => {
|
|
6815
|
+
view: ({ attrs: { option, index, selectedIds, isFocused, onToggle, onMouseOver, showCheckbox } }) => {
|
|
6798
6816
|
const checkboxId = `search-select-option-${option.id}`;
|
|
6799
6817
|
const optionLabel = option.label || option.id.toString();
|
|
6800
6818
|
return m('li', {
|
|
@@ -6811,11 +6829,12 @@ const DropdownOption = {
|
|
|
6811
6829
|
}
|
|
6812
6830
|
},
|
|
6813
6831
|
}, m('label', { for: checkboxId, class: 'search-select-option-label' }, [
|
|
6814
|
-
|
|
6815
|
-
|
|
6816
|
-
|
|
6817
|
-
|
|
6818
|
-
|
|
6832
|
+
showCheckbox &&
|
|
6833
|
+
m('input', {
|
|
6834
|
+
type: 'checkbox',
|
|
6835
|
+
id: checkboxId,
|
|
6836
|
+
checked: selectedIds.includes(option.id),
|
|
6837
|
+
}),
|
|
6819
6838
|
m('span', optionLabel),
|
|
6820
6839
|
]));
|
|
6821
6840
|
},
|
|
@@ -6833,6 +6852,7 @@ const SearchSelect = () => {
|
|
|
6833
6852
|
dropdownRef: null,
|
|
6834
6853
|
focusedIndex: -1,
|
|
6835
6854
|
internalSelectedIds: [],
|
|
6855
|
+
createdOptions: [],
|
|
6836
6856
|
};
|
|
6837
6857
|
const isControlled = (attrs) => attrs.checkedId !== undefined && typeof attrs.onchange === 'function';
|
|
6838
6858
|
const componentId = uniqueId();
|
|
@@ -6875,7 +6895,10 @@ const SearchSelect = () => {
|
|
|
6875
6895
|
// Handle add new option
|
|
6876
6896
|
return 'addNew';
|
|
6877
6897
|
}
|
|
6878
|
-
else if (state.focusedIndex < filteredOptions.length)
|
|
6898
|
+
else if (state.focusedIndex < filteredOptions.length) {
|
|
6899
|
+
// This will be handled in the view method where attrs are available
|
|
6900
|
+
return 'selectOption';
|
|
6901
|
+
}
|
|
6879
6902
|
}
|
|
6880
6903
|
break;
|
|
6881
6904
|
case 'Escape':
|
|
@@ -6886,11 +6909,22 @@ const SearchSelect = () => {
|
|
|
6886
6909
|
}
|
|
6887
6910
|
return null;
|
|
6888
6911
|
};
|
|
6912
|
+
// Create new option and add to state
|
|
6913
|
+
const createAndSelectOption = async (attrs) => {
|
|
6914
|
+
if (!attrs.oncreateNewOption || !state.searchTerm)
|
|
6915
|
+
return;
|
|
6916
|
+
const newOption = await attrs.oncreateNewOption(state.searchTerm);
|
|
6917
|
+
// Store the created option internally
|
|
6918
|
+
state.createdOptions.push(newOption);
|
|
6919
|
+
// Select the new option
|
|
6920
|
+
toggleOption(newOption, attrs);
|
|
6921
|
+
};
|
|
6889
6922
|
// Toggle option selection
|
|
6890
6923
|
const toggleOption = (option, attrs) => {
|
|
6891
6924
|
if (option.disabled)
|
|
6892
6925
|
return;
|
|
6893
6926
|
const controlled = isControlled(attrs);
|
|
6927
|
+
const { maxSelectedOptions } = attrs;
|
|
6894
6928
|
// Get current selected IDs from props or internal state
|
|
6895
6929
|
const currentSelectedIds = controlled
|
|
6896
6930
|
? attrs.checkedId !== undefined
|
|
@@ -6899,9 +6933,29 @@ const SearchSelect = () => {
|
|
|
6899
6933
|
: [attrs.checkedId]
|
|
6900
6934
|
: []
|
|
6901
6935
|
: state.internalSelectedIds;
|
|
6902
|
-
const
|
|
6903
|
-
|
|
6904
|
-
|
|
6936
|
+
const isSelected = currentSelectedIds.includes(option.id);
|
|
6937
|
+
let newIds;
|
|
6938
|
+
if (isSelected) {
|
|
6939
|
+
// Remove if already selected
|
|
6940
|
+
newIds = currentSelectedIds.filter((id) => id !== option.id);
|
|
6941
|
+
}
|
|
6942
|
+
else {
|
|
6943
|
+
// Check if we've reached the max selection limit
|
|
6944
|
+
if (maxSelectedOptions && currentSelectedIds.length >= maxSelectedOptions) {
|
|
6945
|
+
// If max=1, replace the selection
|
|
6946
|
+
if (maxSelectedOptions === 1) {
|
|
6947
|
+
newIds = [option.id];
|
|
6948
|
+
}
|
|
6949
|
+
else {
|
|
6950
|
+
// Otherwise, don't add more
|
|
6951
|
+
return;
|
|
6952
|
+
}
|
|
6953
|
+
}
|
|
6954
|
+
else {
|
|
6955
|
+
// Add to selection
|
|
6956
|
+
newIds = [...currentSelectedIds, option.id];
|
|
6957
|
+
}
|
|
6958
|
+
}
|
|
6905
6959
|
// Update internal state for uncontrolled mode
|
|
6906
6960
|
if (!controlled) {
|
|
6907
6961
|
state.internalSelectedIds = newIds;
|
|
@@ -6963,21 +7017,32 @@ const SearchSelect = () => {
|
|
|
6963
7017
|
: [attrs.checkedId]
|
|
6964
7018
|
: []
|
|
6965
7019
|
: state.internalSelectedIds;
|
|
6966
|
-
const { options = [], oncreateNewOption, className, placeholder, searchPlaceholder = 'Search options...', noOptionsFound = 'No options found', label, i18n = {}, } = attrs;
|
|
7020
|
+
const { options = [], oncreateNewOption, className, placeholder, searchPlaceholder = 'Search options...', noOptionsFound = 'No options found', label, i18n = {}, maxDisplayedOptions, maxSelectedOptions, maxHeight, } = attrs;
|
|
6967
7021
|
// Use i18n values if provided, otherwise use defaults
|
|
6968
7022
|
const texts = {
|
|
6969
7023
|
noOptionsFound: i18n.noOptionsFound || noOptionsFound,
|
|
6970
7024
|
addNewPrefix: i18n.addNewPrefix || '+',
|
|
7025
|
+
showingXofY: i18n.showingXofY || 'Showing {shown} of {total} options',
|
|
7026
|
+
maxSelectionsReached: i18n.maxSelectionsReached || 'Maximum {max} selections reached',
|
|
6971
7027
|
};
|
|
7028
|
+
// Check if max selections is reached
|
|
7029
|
+
const isMaxSelectionsReached = maxSelectedOptions && selectedIds.length >= maxSelectedOptions;
|
|
7030
|
+
// Merge provided options with internally created options
|
|
7031
|
+
const allOptions = [...options, ...state.createdOptions];
|
|
6972
7032
|
// Get selected options for display
|
|
6973
|
-
const
|
|
7033
|
+
const selectedOptionsUnsorted = allOptions.filter((opt) => selectedIds.includes(opt.id));
|
|
7034
|
+
const selectedOptions = sortOptions(selectedOptionsUnsorted, attrs.sortSelected);
|
|
6974
7035
|
// Safely filter options
|
|
6975
|
-
const filteredOptions =
|
|
7036
|
+
const filteredOptions = allOptions.filter((option) => (option.label || option.id.toString()).toLowerCase().includes((state.searchTerm || '').toLowerCase()) &&
|
|
6976
7037
|
!selectedIds.includes(option.id));
|
|
7038
|
+
// Apply display limit if configured
|
|
7039
|
+
const totalFilteredCount = filteredOptions.length;
|
|
7040
|
+
const displayedOptions = maxDisplayedOptions ? filteredOptions.slice(0, maxDisplayedOptions) : filteredOptions;
|
|
7041
|
+
const isTruncated = maxDisplayedOptions && totalFilteredCount > maxDisplayedOptions;
|
|
6977
7042
|
// Check if we should show the "add new option" element
|
|
6978
7043
|
const showAddNew = oncreateNewOption &&
|
|
6979
7044
|
state.searchTerm &&
|
|
6980
|
-
!
|
|
7045
|
+
!displayedOptions.some((o) => (o.label || o.id.toString()).toLowerCase() === state.searchTerm.toLowerCase());
|
|
6981
7046
|
// Render the dropdown
|
|
6982
7047
|
return m('.input-field.multi-select-dropdown', { className }, [
|
|
6983
7048
|
m('.chips.chips-initial.chips-container', {
|
|
@@ -7050,7 +7115,7 @@ const SearchSelect = () => {
|
|
|
7050
7115
|
onremove: () => {
|
|
7051
7116
|
state.dropdownRef = null;
|
|
7052
7117
|
},
|
|
7053
|
-
style: getDropdownStyles(state.inputRef),
|
|
7118
|
+
style: Object.assign(Object.assign({}, getDropdownStyles(state.inputRef)), (maxHeight ? { maxHeight } : {})),
|
|
7054
7119
|
}, [
|
|
7055
7120
|
m('li', // Search Input
|
|
7056
7121
|
{
|
|
@@ -7070,41 +7135,65 @@ const SearchSelect = () => {
|
|
|
7070
7135
|
state.focusedIndex = -1; // Reset focus when typing
|
|
7071
7136
|
},
|
|
7072
7137
|
onkeydown: async (e) => {
|
|
7073
|
-
const result = handleKeyDown(e,
|
|
7138
|
+
const result = handleKeyDown(e, displayedOptions, !!showAddNew);
|
|
7074
7139
|
if (result === 'addNew' && oncreateNewOption) {
|
|
7075
|
-
|
|
7076
|
-
toggleOption(option, attrs);
|
|
7140
|
+
await createAndSelectOption(attrs);
|
|
7077
7141
|
}
|
|
7078
|
-
else if (
|
|
7079
|
-
state.focusedIndex
|
|
7080
|
-
state.focusedIndex < filteredOptions.length) {
|
|
7081
|
-
toggleOption(filteredOptions[state.focusedIndex], attrs);
|
|
7142
|
+
else if (result === 'selectOption' && state.focusedIndex < displayedOptions.length) {
|
|
7143
|
+
toggleOption(displayedOptions[state.focusedIndex], attrs);
|
|
7082
7144
|
}
|
|
7083
7145
|
},
|
|
7084
7146
|
class: 'search-select-input',
|
|
7085
7147
|
}),
|
|
7086
7148
|
]),
|
|
7087
7149
|
// No options found message or list of options
|
|
7088
|
-
...(
|
|
7150
|
+
...(displayedOptions.length === 0 && !showAddNew
|
|
7089
7151
|
? [m('li.search-select-no-options', texts.noOptionsFound)]
|
|
7090
7152
|
: []),
|
|
7153
|
+
// Truncation message
|
|
7154
|
+
...(isTruncated
|
|
7155
|
+
? [
|
|
7156
|
+
m('li.search-select-truncation-info', {
|
|
7157
|
+
style: {
|
|
7158
|
+
fontStyle: 'italic',
|
|
7159
|
+
color: 'var(--mm-text-hint, #9e9e9e)',
|
|
7160
|
+
padding: '8px 16px',
|
|
7161
|
+
cursor: 'default',
|
|
7162
|
+
},
|
|
7163
|
+
}, texts.showingXofY
|
|
7164
|
+
.replace('{shown}', displayedOptions.length.toString())
|
|
7165
|
+
.replace('{total}', totalFilteredCount.toString())),
|
|
7166
|
+
]
|
|
7167
|
+
: []),
|
|
7168
|
+
// Max selections reached message
|
|
7169
|
+
...(isMaxSelectionsReached
|
|
7170
|
+
? [
|
|
7171
|
+
m('li.search-select-max-info', {
|
|
7172
|
+
style: {
|
|
7173
|
+
fontStyle: 'italic',
|
|
7174
|
+
color: 'var(--mm-text-hint, #9e9e9e)',
|
|
7175
|
+
padding: '8px 16px',
|
|
7176
|
+
cursor: 'default',
|
|
7177
|
+
},
|
|
7178
|
+
}, texts.maxSelectionsReached.replace('{max}', maxSelectedOptions.toString())),
|
|
7179
|
+
]
|
|
7180
|
+
: []),
|
|
7091
7181
|
// Add new option item
|
|
7092
7182
|
...(showAddNew
|
|
7093
7183
|
? [
|
|
7094
7184
|
m('li', {
|
|
7095
7185
|
onclick: async () => {
|
|
7096
|
-
|
|
7097
|
-
toggleOption(option, attrs);
|
|
7186
|
+
await createAndSelectOption(attrs);
|
|
7098
7187
|
},
|
|
7099
|
-
class: state.focusedIndex ===
|
|
7188
|
+
class: state.focusedIndex === displayedOptions.length ? 'active' : '',
|
|
7100
7189
|
onmouseover: () => {
|
|
7101
|
-
state.focusedIndex =
|
|
7190
|
+
state.focusedIndex = displayedOptions.length;
|
|
7102
7191
|
},
|
|
7103
7192
|
}, [m('span', `${texts.addNewPrefix} "${state.searchTerm}"`)]),
|
|
7104
7193
|
]
|
|
7105
7194
|
: []),
|
|
7106
7195
|
// List of filtered options
|
|
7107
|
-
...
|
|
7196
|
+
...displayedOptions.map((option, index) => m(DropdownOption, {
|
|
7108
7197
|
// key: option.id,
|
|
7109
7198
|
option,
|
|
7110
7199
|
index,
|
|
@@ -7114,6 +7203,7 @@ const SearchSelect = () => {
|
|
|
7114
7203
|
onMouseOver: (idx) => {
|
|
7115
7204
|
state.focusedIndex = idx;
|
|
7116
7205
|
},
|
|
7206
|
+
showCheckbox: maxSelectedOptions !== 1,
|
|
7117
7207
|
})),
|
|
7118
7208
|
]),
|
|
7119
7209
|
]);
|
|
@@ -9724,4 +9814,4 @@ const isValidationError = (result) => !isValidationSuccess(result);
|
|
|
9724
9814
|
// ============================================================================
|
|
9725
9815
|
// All types are already exported via individual export declarations above
|
|
9726
9816
|
|
|
9727
|
-
export { AnchorItem, Autocomplete, Breadcrumb, BreadcrumbManager, Button, ButtonFactory, Carousel, CharacterCounter, Chips, CodeBlock, Collapsible, CollapsibleItem, Collection, CollectionMode, ColorInput, DataTable, DatePicker, DoubleRangeSlider, Dropdown, EmailInput, FileInput, FileUpload, FlatButton, FloatingActionButton, HelperText, Icon, IconButton, ImageList, InputCheckbox, Label, LargeButton, ListItem, Mandatory, Masonry, MaterialBox, MaterialIcon, ModalPanel, NumberInput, Options, OptionsList, Pagination, PaginationControls, Parallax, PasswordInput, Pushpin, PushpinComponent, RadioButton, RadioButtons, RangeInput, Rating, RoundIconButton, SearchSelect, SecondaryContent, Select, Sidenav, SidenavItem, SidenavManager, SingleRangeSlider, SmallButton, Stepper, SubmitButton, Switch, Tabs, TextArea, TextInput, ThemeManager, ThemeSwitcher, ThemeToggle, TimePicker, Timeline, Toast, ToastComponent, Tooltip, TooltipComponent, TreeView, UrlInput, Wizard, clearPortal, createBreadcrumb, getDropdownStyles, getPortalContainer, initPushpins, initTooltips, isNumeric, isValidationError, isValidationSuccess, padLeft, range, releasePortalContainer, renderToPortal, toast, uniqueId, uuid4 };
|
|
9817
|
+
export { AnchorItem, Autocomplete, Breadcrumb, BreadcrumbManager, Button, ButtonFactory, Carousel, CharacterCounter, Chips, CodeBlock, Collapsible, CollapsibleItem, Collection, CollectionMode, ColorInput, DataTable, DatePicker, DoubleRangeSlider, Dropdown, EmailInput, FileInput, FileUpload, FlatButton, FloatingActionButton, HelperText, Icon, IconButton, ImageList, InputCheckbox, Label, LargeButton, ListItem, Mandatory, Masonry, MaterialBox, MaterialIcon, ModalPanel, NumberInput, Options, OptionsList, Pagination, PaginationControls, Parallax, PasswordInput, Pushpin, PushpinComponent, RadioButton, RadioButtons, RangeInput, Rating, RoundIconButton, SearchSelect, SecondaryContent, Select, Sidenav, SidenavItem, SidenavManager, SingleRangeSlider, SmallButton, Stepper, SubmitButton, Switch, Tabs, TextArea, TextInput, ThemeManager, ThemeSwitcher, ThemeToggle, TimePicker, Timeline, Toast, ToastComponent, Tooltip, TooltipComponent, TreeView, UrlInput, Wizard, clearPortal, createBreadcrumb, getDropdownStyles, getPortalContainer, initPushpins, initTooltips, isNumeric, isValidationError, isValidationSuccess, padLeft, range, releasePortalContainer, renderToPortal, sortOptions, toast, uniqueId, uuid4 };
|