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.js
CHANGED
|
@@ -64,6 +64,28 @@ const uuid4 = () => {
|
|
|
64
64
|
};
|
|
65
65
|
/** Check if a string or number is numeric. @see https://stackoverflow.com/a/9716488/319711 */
|
|
66
66
|
const isNumeric = (n) => !isNaN(parseFloat(n)) && isFinite(n);
|
|
67
|
+
/**
|
|
68
|
+
* Sort options array based on sorting configuration
|
|
69
|
+
* @param options - Array of options to sort
|
|
70
|
+
* @param sortConfig - Sort configuration: 'asc', 'desc', 'none', or custom comparator function
|
|
71
|
+
* @returns Sorted array (or original if 'none' or undefined)
|
|
72
|
+
*/
|
|
73
|
+
const sortOptions = (options, sortConfig) => {
|
|
74
|
+
if (!sortConfig || sortConfig === 'none') {
|
|
75
|
+
return options;
|
|
76
|
+
}
|
|
77
|
+
const sorted = [...options]; // Create a copy to avoid mutating original
|
|
78
|
+
if (typeof sortConfig === 'function') {
|
|
79
|
+
return sorted.sort(sortConfig);
|
|
80
|
+
}
|
|
81
|
+
// Sort by label, fallback to id if no label
|
|
82
|
+
return sorted.sort((a, b) => {
|
|
83
|
+
const aLabel = (a.label || a.id.toString()).toLowerCase();
|
|
84
|
+
const bLabel = (b.label || b.id.toString()).toLowerCase();
|
|
85
|
+
const comparison = aLabel.localeCompare(bLabel);
|
|
86
|
+
return sortConfig === 'asc' ? comparison : -comparison;
|
|
87
|
+
});
|
|
88
|
+
};
|
|
67
89
|
/**
|
|
68
90
|
* Pad left, default width 2 with a '0'
|
|
69
91
|
*
|
|
@@ -1411,19 +1433,18 @@ const CollapsibleItem = () => {
|
|
|
1411
1433
|
onclick: onToggle,
|
|
1412
1434
|
}, [
|
|
1413
1435
|
iconName ? m('i.material-icons', iconName) : undefined,
|
|
1414
|
-
header
|
|
1436
|
+
header
|
|
1437
|
+
? typeof header === 'string'
|
|
1438
|
+
? m('span.collapsible-header-text', header)
|
|
1439
|
+
: m('.collapsible-header-content', header)
|
|
1440
|
+
: undefined,
|
|
1415
1441
|
])
|
|
1416
1442
|
: undefined,
|
|
1417
1443
|
m('.collapsible-body', {
|
|
1418
1444
|
style: {
|
|
1419
1445
|
display: isActive ? 'block' : 'none',
|
|
1420
|
-
transition: 'display 0.3s ease',
|
|
1421
1446
|
},
|
|
1422
|
-
},
|
|
1423
|
-
m('.collapsible-body-content', {
|
|
1424
|
-
style: { padding: '2rem' },
|
|
1425
|
-
}, body ? (typeof body === 'string' ? m('div', { innerHTML: body }) : body) : undefined),
|
|
1426
|
-
]),
|
|
1447
|
+
}, body ? (typeof body === 'string' ? m('div', { innerHTML: body }) : body) : undefined),
|
|
1427
1448
|
]);
|
|
1428
1449
|
},
|
|
1429
1450
|
};
|
|
@@ -1446,7 +1467,7 @@ const Collapsible = () => {
|
|
|
1446
1467
|
});
|
|
1447
1468
|
},
|
|
1448
1469
|
view: ({ attrs }) => {
|
|
1449
|
-
const { items, accordion = true, class: c, className, style, id } = attrs;
|
|
1470
|
+
const { items, header, accordion = true, class: c, className, style, id } = attrs;
|
|
1450
1471
|
const toggleItem = (index) => {
|
|
1451
1472
|
if (accordion) {
|
|
1452
1473
|
// Accordion mode: only one item can be active
|
|
@@ -1468,12 +1489,22 @@ const Collapsible = () => {
|
|
|
1468
1489
|
}
|
|
1469
1490
|
}
|
|
1470
1491
|
};
|
|
1492
|
+
const collapsibleItems = items.map((item, index) => m(CollapsibleItem, Object.assign(Object.assign({}, item), { key: index, isActive: state.activeItems.has(index), onToggle: () => toggleItem(index) })));
|
|
1471
1493
|
return items && items.length > 0
|
|
1472
|
-
?
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1494
|
+
? header
|
|
1495
|
+
? m('ul.collapsible.with-header', {
|
|
1496
|
+
class: c || className,
|
|
1497
|
+
style,
|
|
1498
|
+
id,
|
|
1499
|
+
}, [
|
|
1500
|
+
m('li.collapsible-main-header', m('h4', typeof header === 'string' ? header : header)),
|
|
1501
|
+
collapsibleItems,
|
|
1502
|
+
])
|
|
1503
|
+
: m('ul.collapsible', {
|
|
1504
|
+
class: c || className,
|
|
1505
|
+
style,
|
|
1506
|
+
id,
|
|
1507
|
+
}, collapsibleItems)
|
|
1477
1508
|
: undefined;
|
|
1478
1509
|
},
|
|
1479
1510
|
};
|
|
@@ -1577,7 +1608,7 @@ const Collection = () => {
|
|
|
1577
1608
|
};
|
|
1578
1609
|
};
|
|
1579
1610
|
|
|
1580
|
-
const defaultI18n$
|
|
1611
|
+
const defaultI18n$3 = {
|
|
1581
1612
|
cancel: 'Cancel',
|
|
1582
1613
|
clear: 'Clear',
|
|
1583
1614
|
done: 'Ok',
|
|
@@ -1685,9 +1716,9 @@ const DatePicker = () => {
|
|
|
1685
1716
|
else if (attrs.displayFormat) {
|
|
1686
1717
|
finalFormat = attrs.displayFormat;
|
|
1687
1718
|
}
|
|
1688
|
-
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$
|
|
1719
|
+
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);
|
|
1689
1720
|
// Merge i18n properly
|
|
1690
|
-
merged.i18n = Object.assign(Object.assign({}, defaultI18n$
|
|
1721
|
+
merged.i18n = Object.assign(Object.assign({}, defaultI18n$3), attrs.i18n);
|
|
1691
1722
|
return merged;
|
|
1692
1723
|
};
|
|
1693
1724
|
const toString = (date, format) => {
|
|
@@ -2138,11 +2169,11 @@ const DatePicker = () => {
|
|
|
2138
2169
|
prevMonth();
|
|
2139
2170
|
},
|
|
2140
2171
|
}, m('svg', {
|
|
2141
|
-
fill: '#000000',
|
|
2142
2172
|
height: '24',
|
|
2143
2173
|
viewBox: '0 0 24 24',
|
|
2144
2174
|
width: '24',
|
|
2145
2175
|
xmlns: 'http://www.w3.org/2000/svg',
|
|
2176
|
+
style: 'fill: var(--mm-text-primary, rgba(0, 0, 0, 0.87));',
|
|
2146
2177
|
}, [
|
|
2147
2178
|
m('path', { d: 'M15.41 16.09l-4.58-4.59 4.58-4.59L14 5.5l-6 6 6 6z' }),
|
|
2148
2179
|
m('path', { d: 'M0-.5h24v24H0z', fill: 'none' }),
|
|
@@ -2204,11 +2235,11 @@ const DatePicker = () => {
|
|
|
2204
2235
|
nextMonth();
|
|
2205
2236
|
},
|
|
2206
2237
|
}, m('svg', {
|
|
2207
|
-
fill: '#000000',
|
|
2208
2238
|
height: '24',
|
|
2209
2239
|
viewBox: '0 0 24 24',
|
|
2210
2240
|
width: '24',
|
|
2211
2241
|
xmlns: 'http://www.w3.org/2000/svg',
|
|
2242
|
+
style: 'fill: var(--mm-text-primary, rgba(0, 0, 0, 0.87));',
|
|
2212
2243
|
}, [
|
|
2213
2244
|
m('path', { d: 'M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z' }),
|
|
2214
2245
|
m('path', { d: 'M0-.25h24v24H0z', fill: 'none' }),
|
|
@@ -3221,19 +3252,29 @@ const TextArea = () => {
|
|
|
3221
3252
|
if (!controlled && attrs.defaultValue !== undefined) {
|
|
3222
3253
|
textarea.value = String(attrs.defaultValue);
|
|
3223
3254
|
}
|
|
3224
|
-
// Height will be calculated by hidden div
|
|
3225
3255
|
// Update character count state for counter component
|
|
3226
3256
|
if (maxLength) {
|
|
3227
3257
|
state.currentLength = textarea.value.length;
|
|
3228
3258
|
}
|
|
3259
|
+
// Calculate initial height after DOM is fully ready
|
|
3260
|
+
// Use requestAnimationFrame to ensure layout is complete
|
|
3261
|
+
if (state.hiddenDiv) {
|
|
3262
|
+
requestAnimationFrame(() => {
|
|
3263
|
+
if (state.hiddenDiv) {
|
|
3264
|
+
updateHeight(textarea, state.hiddenDiv);
|
|
3265
|
+
m.redraw();
|
|
3266
|
+
}
|
|
3267
|
+
});
|
|
3268
|
+
}
|
|
3229
3269
|
}, onupdate: ({ dom }) => {
|
|
3230
3270
|
const textarea = dom;
|
|
3231
|
-
|
|
3232
|
-
textarea.style.height = state.height;
|
|
3233
|
-
// Trigger height recalculation when value changes programmatically
|
|
3271
|
+
// Recalculate and apply height
|
|
3234
3272
|
if (state.hiddenDiv) {
|
|
3235
3273
|
updateHeight(textarea, state.hiddenDiv);
|
|
3236
3274
|
}
|
|
3275
|
+
if (state.height) {
|
|
3276
|
+
textarea.style.height = state.height;
|
|
3277
|
+
}
|
|
3237
3278
|
}, onfocus: () => {
|
|
3238
3279
|
state.active = true;
|
|
3239
3280
|
}, oninput: (e) => {
|
|
@@ -4522,7 +4563,7 @@ const Dropdown = () => {
|
|
|
4522
4563
|
// Create dropdown with proper positioning
|
|
4523
4564
|
const dropdownVnode = m('ul.dropdown-content.select-dropdown', {
|
|
4524
4565
|
tabindex: 0,
|
|
4525
|
-
style: getPortalStyles(state.inputRef),
|
|
4566
|
+
style: Object.assign(Object.assign({}, getPortalStyles(state.inputRef)), (attrs.maxHeight ? { maxHeight: attrs.maxHeight } : {})),
|
|
4526
4567
|
oncreate: ({ dom }) => {
|
|
4527
4568
|
state.dropdownRef = dom;
|
|
4528
4569
|
},
|
|
@@ -4634,7 +4675,7 @@ const Dropdown = () => {
|
|
|
4634
4675
|
onremove: () => {
|
|
4635
4676
|
state.dropdownRef = null;
|
|
4636
4677
|
},
|
|
4637
|
-
style: getDropdownStyles(state.inputRef, true, items, true),
|
|
4678
|
+
style: Object.assign(Object.assign({}, getDropdownStyles(state.inputRef, true, items, true)), (attrs.maxHeight ? { maxHeight: attrs.maxHeight } : {})),
|
|
4638
4679
|
}, items.map((item) => {
|
|
4639
4680
|
if (item.divider) {
|
|
4640
4681
|
return m('li.divider');
|
|
@@ -4774,7 +4815,7 @@ const FloatingActionButton = () => {
|
|
|
4774
4815
|
|
|
4775
4816
|
/**
|
|
4776
4817
|
* Pure TypeScript MaterialBox - creates an image lightbox that fills the screen when clicked
|
|
4777
|
-
*
|
|
4818
|
+
* Uses CSS classes from _materialbox.scss
|
|
4778
4819
|
*/
|
|
4779
4820
|
const MaterialBox = () => {
|
|
4780
4821
|
const state = {
|
|
@@ -4790,21 +4831,11 @@ const MaterialBox = () => {
|
|
|
4790
4831
|
state.originalImage = img;
|
|
4791
4832
|
if (attrs.onOpenStart)
|
|
4792
4833
|
attrs.onOpenStart();
|
|
4834
|
+
const inDuration = attrs.inDuration || 275;
|
|
4793
4835
|
// Create overlay
|
|
4794
4836
|
const overlay = document.createElement('div');
|
|
4795
4837
|
overlay.className = 'materialbox-overlay';
|
|
4796
|
-
overlay.style.
|
|
4797
|
-
position: fixed;
|
|
4798
|
-
top: 0;
|
|
4799
|
-
left: 0;
|
|
4800
|
-
right: 0;
|
|
4801
|
-
bottom: 0;
|
|
4802
|
-
background-color: rgba(0, 0, 0, 0.85);
|
|
4803
|
-
z-index: 1000;
|
|
4804
|
-
opacity: 0;
|
|
4805
|
-
transition: opacity ${attrs.inDuration || 275}ms ease;
|
|
4806
|
-
cursor: zoom-out;
|
|
4807
|
-
`;
|
|
4838
|
+
overlay.style.transition = `opacity ${inDuration}ms ease`;
|
|
4808
4839
|
// Create enlarged image
|
|
4809
4840
|
const enlargedImg = document.createElement('img');
|
|
4810
4841
|
enlargedImg.src = img.src;
|
|
@@ -4825,36 +4856,18 @@ const MaterialBox = () => {
|
|
|
4825
4856
|
finalWidth = maxHeight * aspectRatio;
|
|
4826
4857
|
}
|
|
4827
4858
|
// Set initial position and size (same as original image)
|
|
4828
|
-
enlargedImg.style.
|
|
4829
|
-
|
|
4830
|
-
|
|
4831
|
-
|
|
4832
|
-
|
|
4833
|
-
height: ${imgRect.height}px;
|
|
4834
|
-
transition: all ${attrs.inDuration || 275}ms ease;
|
|
4835
|
-
cursor: zoom-out;
|
|
4836
|
-
max-width: none;
|
|
4837
|
-
z-index: 1001;
|
|
4838
|
-
`;
|
|
4859
|
+
enlargedImg.style.top = `${imgRect.top}px`;
|
|
4860
|
+
enlargedImg.style.left = `${imgRect.left}px`;
|
|
4861
|
+
enlargedImg.style.width = `${imgRect.width}px`;
|
|
4862
|
+
enlargedImg.style.height = `${imgRect.height}px`;
|
|
4863
|
+
enlargedImg.style.transition = `all ${inDuration}ms ease`;
|
|
4839
4864
|
// Add caption if provided
|
|
4840
4865
|
let caption = null;
|
|
4841
4866
|
if (attrs.caption) {
|
|
4842
4867
|
caption = document.createElement('div');
|
|
4843
4868
|
caption.className = 'materialbox-caption';
|
|
4844
4869
|
caption.textContent = attrs.caption;
|
|
4845
|
-
caption.style.
|
|
4846
|
-
position: fixed;
|
|
4847
|
-
bottom: 20px;
|
|
4848
|
-
left: 50%;
|
|
4849
|
-
transform: translateX(-50%);
|
|
4850
|
-
color: white;
|
|
4851
|
-
font-size: 16px;
|
|
4852
|
-
text-align: center;
|
|
4853
|
-
opacity: 0;
|
|
4854
|
-
transition: opacity ${attrs.inDuration || 275}ms ease ${attrs.inDuration || 275}ms;
|
|
4855
|
-
z-index: 1002;
|
|
4856
|
-
pointer-events: none;
|
|
4857
|
-
`;
|
|
4870
|
+
caption.style.transition = `opacity ${inDuration}ms ease ${inDuration}ms`;
|
|
4858
4871
|
}
|
|
4859
4872
|
// Add to DOM
|
|
4860
4873
|
document.body.appendChild(overlay);
|
|
@@ -4889,7 +4902,7 @@ const MaterialBox = () => {
|
|
|
4889
4902
|
setTimeout(() => {
|
|
4890
4903
|
if (attrs.onOpenEnd)
|
|
4891
4904
|
attrs.onOpenEnd();
|
|
4892
|
-
},
|
|
4905
|
+
}, inDuration);
|
|
4893
4906
|
};
|
|
4894
4907
|
const closeBox = (attrs) => {
|
|
4895
4908
|
if (!state.isOpen || !state.originalImage || !state.overlay || !state.overlayImage)
|
|
@@ -4946,8 +4959,14 @@ const MaterialBox = () => {
|
|
|
4946
4959
|
},
|
|
4947
4960
|
view: ({ attrs }) => {
|
|
4948
4961
|
const { src, alt, width, height, caption, className, style } = attrs, otherAttrs = __rest(attrs, ["src", "alt", "width", "height", "caption", "className", "style"]);
|
|
4962
|
+
// Build style attribute - handle both string and object styles
|
|
4963
|
+
let imgStyle = style || {};
|
|
4964
|
+
if (typeof style !== 'string') {
|
|
4965
|
+
// If style is an object or undefined, add default styles as object
|
|
4966
|
+
imgStyle = Object.assign({ cursor: 'zoom-in', transition: 'opacity 200ms ease' }, (style || {}));
|
|
4967
|
+
}
|
|
4949
4968
|
return m('img.materialboxed', Object.assign(Object.assign({}, otherAttrs), { src, alt: alt || '', width,
|
|
4950
|
-
height, className: ['materialboxed', className].filter(Boolean).join(' ') || undefined, style:
|
|
4969
|
+
height, className: ['materialboxed', className].filter(Boolean).join(' ') || undefined, style: imgStyle, onclick: (e) => {
|
|
4951
4970
|
e.preventDefault();
|
|
4952
4971
|
openBox(e.target, attrs);
|
|
4953
4972
|
} }));
|
|
@@ -5277,6 +5296,11 @@ const Parallax = () => {
|
|
|
5277
5296
|
};
|
|
5278
5297
|
};
|
|
5279
5298
|
|
|
5299
|
+
const defaultI18n$2 = {
|
|
5300
|
+
cancel: 'Cancel',
|
|
5301
|
+
clear: 'Clear',
|
|
5302
|
+
done: 'Ok',
|
|
5303
|
+
};
|
|
5280
5304
|
const defaultOptions = {
|
|
5281
5305
|
dialRadius: 135,
|
|
5282
5306
|
outerRadius: 105,
|
|
@@ -5287,11 +5311,7 @@ const defaultOptions = {
|
|
|
5287
5311
|
defaultTime: 'now',
|
|
5288
5312
|
fromNow: 0,
|
|
5289
5313
|
showClearBtn: false,
|
|
5290
|
-
i18n:
|
|
5291
|
-
cancel: 'Cancel',
|
|
5292
|
-
clear: 'Clear',
|
|
5293
|
-
done: 'Ok',
|
|
5294
|
-
},
|
|
5314
|
+
i18n: defaultI18n$2,
|
|
5295
5315
|
autoClose: false,
|
|
5296
5316
|
twelveHour: true,
|
|
5297
5317
|
vibrate: true,
|
|
@@ -5441,7 +5461,7 @@ const TimePicker = () => {
|
|
|
5441
5461
|
state.hours = hours;
|
|
5442
5462
|
state.minutes = minutes;
|
|
5443
5463
|
if (state.spanHours) {
|
|
5444
|
-
state.spanHours.innerHTML = state.hours
|
|
5464
|
+
state.spanHours.innerHTML = addLeadingZero(state.hours);
|
|
5445
5465
|
}
|
|
5446
5466
|
if (state.spanMinutes) {
|
|
5447
5467
|
state.spanMinutes.innerHTML = addLeadingZero(state.minutes);
|
|
@@ -5549,7 +5569,7 @@ const TimePicker = () => {
|
|
|
5549
5569
|
}
|
|
5550
5570
|
state[state.currentView] = value;
|
|
5551
5571
|
if (isHours && state.spanHours) {
|
|
5552
|
-
state.spanHours.innerHTML = value
|
|
5572
|
+
state.spanHours.innerHTML = addLeadingZero(value);
|
|
5553
5573
|
}
|
|
5554
5574
|
else if (!isHours && state.spanMinutes) {
|
|
5555
5575
|
state.spanMinutes.innerHTML = addLeadingZero(value);
|
|
@@ -5683,7 +5703,7 @@ const TimePicker = () => {
|
|
|
5683
5703
|
const TimepickerModal = () => {
|
|
5684
5704
|
return {
|
|
5685
5705
|
view: ({ attrs }) => {
|
|
5686
|
-
const {
|
|
5706
|
+
const { i18n, showClearBtn } = attrs;
|
|
5687
5707
|
return [
|
|
5688
5708
|
m('.modal-content.timepicker-container', [
|
|
5689
5709
|
m('.timepicker-digital-display', [
|
|
@@ -5695,7 +5715,7 @@ const TimePicker = () => {
|
|
|
5695
5715
|
oncreate: (vnode) => {
|
|
5696
5716
|
state.spanHours = vnode.dom;
|
|
5697
5717
|
},
|
|
5698
|
-
}, state.hours
|
|
5718
|
+
}, addLeadingZero(state.hours)),
|
|
5699
5719
|
':',
|
|
5700
5720
|
m('span.timepicker-span-minutes', {
|
|
5701
5721
|
class: state.currentView === 'minutes' ? 'text-primary' : '',
|
|
@@ -5775,18 +5795,18 @@ const TimePicker = () => {
|
|
|
5775
5795
|
tabindex: options.twelveHour ? '3' : '1',
|
|
5776
5796
|
style: showClearBtn ? '' : 'visibility: hidden;',
|
|
5777
5797
|
onclick: () => clear(),
|
|
5778
|
-
},
|
|
5798
|
+
}, i18n.clear),
|
|
5779
5799
|
m('.confirmation-btns', [
|
|
5780
5800
|
m('button.btn-flat.timepicker-close.waves-effect', {
|
|
5781
5801
|
type: 'button',
|
|
5782
5802
|
tabindex: options.twelveHour ? '3' : '1',
|
|
5783
5803
|
onclick: () => close(),
|
|
5784
|
-
},
|
|
5804
|
+
}, i18n.cancel),
|
|
5785
5805
|
m('button.btn-flat.timepicker-close.waves-effect', {
|
|
5786
5806
|
type: 'button',
|
|
5787
5807
|
tabindex: options.twelveHour ? '3' : '1',
|
|
5788
5808
|
onclick: () => done(),
|
|
5789
|
-
},
|
|
5809
|
+
}, i18n.done),
|
|
5790
5810
|
]),
|
|
5791
5811
|
]),
|
|
5792
5812
|
]),
|
|
@@ -5803,7 +5823,6 @@ const TimePicker = () => {
|
|
|
5803
5823
|
}
|
|
5804
5824
|
};
|
|
5805
5825
|
const renderPickerToPortal = (attrs) => {
|
|
5806
|
-
const { showClearBtn = false, clearLabel = 'Clear', closeLabel = 'Cancel' } = attrs;
|
|
5807
5826
|
const pickerModal = m('.timepicker-modal-wrapper', {
|
|
5808
5827
|
style: {
|
|
5809
5828
|
position: 'fixed',
|
|
@@ -5846,10 +5865,8 @@ const TimePicker = () => {
|
|
|
5846
5865
|
},
|
|
5847
5866
|
}, [
|
|
5848
5867
|
m(TimepickerModal, {
|
|
5849
|
-
|
|
5850
|
-
|
|
5851
|
-
closeLabel,
|
|
5852
|
-
doneLabel: 'OK',
|
|
5868
|
+
i18n: options.i18n,
|
|
5869
|
+
showClearBtn: options.showClearBtn,
|
|
5853
5870
|
}),
|
|
5854
5871
|
]),
|
|
5855
5872
|
]);
|
|
@@ -5909,7 +5926,7 @@ const TimePicker = () => {
|
|
|
5909
5926
|
}
|
|
5910
5927
|
},
|
|
5911
5928
|
view: ({ attrs }) => {
|
|
5912
|
-
const { id = state.id, label, placeholder, disabled, readonly, required, iconName, helperText, onchange, oninput, useModal = true,
|
|
5929
|
+
const { id = state.id, label, placeholder, disabled, readonly, required, iconName, helperText, onchange, oninput, useModal = true, twelveHour, className: cn1, class: cn2, } = attrs;
|
|
5913
5930
|
const className = cn1 || cn2 || 'col s12';
|
|
5914
5931
|
// Format time value for display
|
|
5915
5932
|
const formatTime = (hours, minutes, use12Hour) => {
|
|
@@ -6355,7 +6372,7 @@ const Select = () => {
|
|
|
6355
6372
|
// Render ungrouped options first
|
|
6356
6373
|
attrs.options
|
|
6357
6374
|
.filter((option) => !option.group)
|
|
6358
|
-
.map((option) => m('li', Object.assign({ class: option.disabled ? 'disabled' : state.focusedIndex === attrs.options.indexOf(option) ? 'focused' : '' }, (option.disabled
|
|
6375
|
+
.map((option) => m('li', Object.assign({ class: `${option.disabled ? 'disabled' : ''}${isSelected(option.id, selectedIds) ? ' selected' : ''}${state.focusedIndex === attrs.options.indexOf(option) ? ' focused' : ''}` }, (option.disabled
|
|
6359
6376
|
? {}
|
|
6360
6377
|
: {
|
|
6361
6378
|
onclick: () => {
|
|
@@ -6432,7 +6449,7 @@ const Select = () => {
|
|
|
6432
6449
|
// Create dropdown with proper positioning
|
|
6433
6450
|
const dropdownVnode = m('ul.dropdown-content.select-dropdown', {
|
|
6434
6451
|
tabindex: 0,
|
|
6435
|
-
style: getPortalStyles(state.inputRef),
|
|
6452
|
+
style: Object.assign(Object.assign({}, getPortalStyles(state.inputRef)), (attrs.maxHeight ? { maxHeight: attrs.maxHeight } : {})),
|
|
6436
6453
|
oncreate: ({ dom }) => {
|
|
6437
6454
|
state.dropdownRef = dom;
|
|
6438
6455
|
},
|
|
@@ -6501,7 +6518,8 @@ const Select = () => {
|
|
|
6501
6518
|
selectedIds = state.internalSelectedIds;
|
|
6502
6519
|
}
|
|
6503
6520
|
const finalClassName = newRow ? `${className} clear` : className;
|
|
6504
|
-
const
|
|
6521
|
+
const selectedOptionsUnsorted = options.filter((opt) => isSelected(opt.id, selectedIds));
|
|
6522
|
+
const selectedOptions = sortOptions(selectedOptionsUnsorted, attrs.sortSelected);
|
|
6505
6523
|
// Update portal dropdown when inside modal
|
|
6506
6524
|
if (state.isInsideModal) {
|
|
6507
6525
|
updatePortalDropdown(attrs, selectedIds, multiple, placeholder);
|
|
@@ -6546,7 +6564,7 @@ const Select = () => {
|
|
|
6546
6564
|
onremove: () => {
|
|
6547
6565
|
state.dropdownRef = null;
|
|
6548
6566
|
},
|
|
6549
|
-
style: getDropdownStyles(state.inputRef, true, options),
|
|
6567
|
+
style: Object.assign(Object.assign({}, getDropdownStyles(state.inputRef, true, options)), (attrs.maxHeight ? { maxHeight: attrs.maxHeight } : {})),
|
|
6550
6568
|
}, renderDropdownContent(attrs, selectedIds, multiple, placeholder)),
|
|
6551
6569
|
m(MaterialIcon, {
|
|
6552
6570
|
name: 'caret',
|
|
@@ -6796,7 +6814,7 @@ const SelectedChip = {
|
|
|
6796
6814
|
]),
|
|
6797
6815
|
};
|
|
6798
6816
|
const DropdownOption = {
|
|
6799
|
-
view: ({ attrs: { option, index, selectedIds, isFocused, onToggle, onMouseOver } }) => {
|
|
6817
|
+
view: ({ attrs: { option, index, selectedIds, isFocused, onToggle, onMouseOver, showCheckbox } }) => {
|
|
6800
6818
|
const checkboxId = `search-select-option-${option.id}`;
|
|
6801
6819
|
const optionLabel = option.label || option.id.toString();
|
|
6802
6820
|
return m('li', {
|
|
@@ -6813,11 +6831,12 @@ const DropdownOption = {
|
|
|
6813
6831
|
}
|
|
6814
6832
|
},
|
|
6815
6833
|
}, m('label', { for: checkboxId, class: 'search-select-option-label' }, [
|
|
6816
|
-
|
|
6817
|
-
|
|
6818
|
-
|
|
6819
|
-
|
|
6820
|
-
|
|
6834
|
+
showCheckbox &&
|
|
6835
|
+
m('input', {
|
|
6836
|
+
type: 'checkbox',
|
|
6837
|
+
id: checkboxId,
|
|
6838
|
+
checked: selectedIds.includes(option.id),
|
|
6839
|
+
}),
|
|
6821
6840
|
m('span', optionLabel),
|
|
6822
6841
|
]));
|
|
6823
6842
|
},
|
|
@@ -6835,6 +6854,7 @@ const SearchSelect = () => {
|
|
|
6835
6854
|
dropdownRef: null,
|
|
6836
6855
|
focusedIndex: -1,
|
|
6837
6856
|
internalSelectedIds: [],
|
|
6857
|
+
createdOptions: [],
|
|
6838
6858
|
};
|
|
6839
6859
|
const isControlled = (attrs) => attrs.checkedId !== undefined && typeof attrs.onchange === 'function';
|
|
6840
6860
|
const componentId = uniqueId();
|
|
@@ -6877,7 +6897,10 @@ const SearchSelect = () => {
|
|
|
6877
6897
|
// Handle add new option
|
|
6878
6898
|
return 'addNew';
|
|
6879
6899
|
}
|
|
6880
|
-
else if (state.focusedIndex < filteredOptions.length)
|
|
6900
|
+
else if (state.focusedIndex < filteredOptions.length) {
|
|
6901
|
+
// This will be handled in the view method where attrs are available
|
|
6902
|
+
return 'selectOption';
|
|
6903
|
+
}
|
|
6881
6904
|
}
|
|
6882
6905
|
break;
|
|
6883
6906
|
case 'Escape':
|
|
@@ -6888,11 +6911,22 @@ const SearchSelect = () => {
|
|
|
6888
6911
|
}
|
|
6889
6912
|
return null;
|
|
6890
6913
|
};
|
|
6914
|
+
// Create new option and add to state
|
|
6915
|
+
const createAndSelectOption = async (attrs) => {
|
|
6916
|
+
if (!attrs.oncreateNewOption || !state.searchTerm)
|
|
6917
|
+
return;
|
|
6918
|
+
const newOption = await attrs.oncreateNewOption(state.searchTerm);
|
|
6919
|
+
// Store the created option internally
|
|
6920
|
+
state.createdOptions.push(newOption);
|
|
6921
|
+
// Select the new option
|
|
6922
|
+
toggleOption(newOption, attrs);
|
|
6923
|
+
};
|
|
6891
6924
|
// Toggle option selection
|
|
6892
6925
|
const toggleOption = (option, attrs) => {
|
|
6893
6926
|
if (option.disabled)
|
|
6894
6927
|
return;
|
|
6895
6928
|
const controlled = isControlled(attrs);
|
|
6929
|
+
const { maxSelectedOptions } = attrs;
|
|
6896
6930
|
// Get current selected IDs from props or internal state
|
|
6897
6931
|
const currentSelectedIds = controlled
|
|
6898
6932
|
? attrs.checkedId !== undefined
|
|
@@ -6901,9 +6935,29 @@ const SearchSelect = () => {
|
|
|
6901
6935
|
: [attrs.checkedId]
|
|
6902
6936
|
: []
|
|
6903
6937
|
: state.internalSelectedIds;
|
|
6904
|
-
const
|
|
6905
|
-
|
|
6906
|
-
|
|
6938
|
+
const isSelected = currentSelectedIds.includes(option.id);
|
|
6939
|
+
let newIds;
|
|
6940
|
+
if (isSelected) {
|
|
6941
|
+
// Remove if already selected
|
|
6942
|
+
newIds = currentSelectedIds.filter((id) => id !== option.id);
|
|
6943
|
+
}
|
|
6944
|
+
else {
|
|
6945
|
+
// Check if we've reached the max selection limit
|
|
6946
|
+
if (maxSelectedOptions && currentSelectedIds.length >= maxSelectedOptions) {
|
|
6947
|
+
// If max=1, replace the selection
|
|
6948
|
+
if (maxSelectedOptions === 1) {
|
|
6949
|
+
newIds = [option.id];
|
|
6950
|
+
}
|
|
6951
|
+
else {
|
|
6952
|
+
// Otherwise, don't add more
|
|
6953
|
+
return;
|
|
6954
|
+
}
|
|
6955
|
+
}
|
|
6956
|
+
else {
|
|
6957
|
+
// Add to selection
|
|
6958
|
+
newIds = [...currentSelectedIds, option.id];
|
|
6959
|
+
}
|
|
6960
|
+
}
|
|
6907
6961
|
// Update internal state for uncontrolled mode
|
|
6908
6962
|
if (!controlled) {
|
|
6909
6963
|
state.internalSelectedIds = newIds;
|
|
@@ -6965,21 +7019,32 @@ const SearchSelect = () => {
|
|
|
6965
7019
|
: [attrs.checkedId]
|
|
6966
7020
|
: []
|
|
6967
7021
|
: state.internalSelectedIds;
|
|
6968
|
-
const { options = [], oncreateNewOption, className, placeholder, searchPlaceholder = 'Search options...', noOptionsFound = 'No options found', label, i18n = {}, } = attrs;
|
|
7022
|
+
const { options = [], oncreateNewOption, className, placeholder, searchPlaceholder = 'Search options...', noOptionsFound = 'No options found', label, i18n = {}, maxDisplayedOptions, maxSelectedOptions, maxHeight, } = attrs;
|
|
6969
7023
|
// Use i18n values if provided, otherwise use defaults
|
|
6970
7024
|
const texts = {
|
|
6971
7025
|
noOptionsFound: i18n.noOptionsFound || noOptionsFound,
|
|
6972
7026
|
addNewPrefix: i18n.addNewPrefix || '+',
|
|
7027
|
+
showingXofY: i18n.showingXofY || 'Showing {shown} of {total} options',
|
|
7028
|
+
maxSelectionsReached: i18n.maxSelectionsReached || 'Maximum {max} selections reached',
|
|
6973
7029
|
};
|
|
7030
|
+
// Check if max selections is reached
|
|
7031
|
+
const isMaxSelectionsReached = maxSelectedOptions && selectedIds.length >= maxSelectedOptions;
|
|
7032
|
+
// Merge provided options with internally created options
|
|
7033
|
+
const allOptions = [...options, ...state.createdOptions];
|
|
6974
7034
|
// Get selected options for display
|
|
6975
|
-
const
|
|
7035
|
+
const selectedOptionsUnsorted = allOptions.filter((opt) => selectedIds.includes(opt.id));
|
|
7036
|
+
const selectedOptions = sortOptions(selectedOptionsUnsorted, attrs.sortSelected);
|
|
6976
7037
|
// Safely filter options
|
|
6977
|
-
const filteredOptions =
|
|
7038
|
+
const filteredOptions = allOptions.filter((option) => (option.label || option.id.toString()).toLowerCase().includes((state.searchTerm || '').toLowerCase()) &&
|
|
6978
7039
|
!selectedIds.includes(option.id));
|
|
7040
|
+
// Apply display limit if configured
|
|
7041
|
+
const totalFilteredCount = filteredOptions.length;
|
|
7042
|
+
const displayedOptions = maxDisplayedOptions ? filteredOptions.slice(0, maxDisplayedOptions) : filteredOptions;
|
|
7043
|
+
const isTruncated = maxDisplayedOptions && totalFilteredCount > maxDisplayedOptions;
|
|
6979
7044
|
// Check if we should show the "add new option" element
|
|
6980
7045
|
const showAddNew = oncreateNewOption &&
|
|
6981
7046
|
state.searchTerm &&
|
|
6982
|
-
!
|
|
7047
|
+
!displayedOptions.some((o) => (o.label || o.id.toString()).toLowerCase() === state.searchTerm.toLowerCase());
|
|
6983
7048
|
// Render the dropdown
|
|
6984
7049
|
return m('.input-field.multi-select-dropdown', { className }, [
|
|
6985
7050
|
m('.chips.chips-initial.chips-container', {
|
|
@@ -7052,7 +7117,7 @@ const SearchSelect = () => {
|
|
|
7052
7117
|
onremove: () => {
|
|
7053
7118
|
state.dropdownRef = null;
|
|
7054
7119
|
},
|
|
7055
|
-
style: getDropdownStyles(state.inputRef),
|
|
7120
|
+
style: Object.assign(Object.assign({}, getDropdownStyles(state.inputRef)), (maxHeight ? { maxHeight } : {})),
|
|
7056
7121
|
}, [
|
|
7057
7122
|
m('li', // Search Input
|
|
7058
7123
|
{
|
|
@@ -7072,41 +7137,65 @@ const SearchSelect = () => {
|
|
|
7072
7137
|
state.focusedIndex = -1; // Reset focus when typing
|
|
7073
7138
|
},
|
|
7074
7139
|
onkeydown: async (e) => {
|
|
7075
|
-
const result = handleKeyDown(e,
|
|
7140
|
+
const result = handleKeyDown(e, displayedOptions, !!showAddNew);
|
|
7076
7141
|
if (result === 'addNew' && oncreateNewOption) {
|
|
7077
|
-
|
|
7078
|
-
toggleOption(option, attrs);
|
|
7142
|
+
await createAndSelectOption(attrs);
|
|
7079
7143
|
}
|
|
7080
|
-
else if (
|
|
7081
|
-
state.focusedIndex
|
|
7082
|
-
state.focusedIndex < filteredOptions.length) {
|
|
7083
|
-
toggleOption(filteredOptions[state.focusedIndex], attrs);
|
|
7144
|
+
else if (result === 'selectOption' && state.focusedIndex < displayedOptions.length) {
|
|
7145
|
+
toggleOption(displayedOptions[state.focusedIndex], attrs);
|
|
7084
7146
|
}
|
|
7085
7147
|
},
|
|
7086
7148
|
class: 'search-select-input',
|
|
7087
7149
|
}),
|
|
7088
7150
|
]),
|
|
7089
7151
|
// No options found message or list of options
|
|
7090
|
-
...(
|
|
7152
|
+
...(displayedOptions.length === 0 && !showAddNew
|
|
7091
7153
|
? [m('li.search-select-no-options', texts.noOptionsFound)]
|
|
7092
7154
|
: []),
|
|
7155
|
+
// Truncation message
|
|
7156
|
+
...(isTruncated
|
|
7157
|
+
? [
|
|
7158
|
+
m('li.search-select-truncation-info', {
|
|
7159
|
+
style: {
|
|
7160
|
+
fontStyle: 'italic',
|
|
7161
|
+
color: 'var(--mm-text-hint, #9e9e9e)',
|
|
7162
|
+
padding: '8px 16px',
|
|
7163
|
+
cursor: 'default',
|
|
7164
|
+
},
|
|
7165
|
+
}, texts.showingXofY
|
|
7166
|
+
.replace('{shown}', displayedOptions.length.toString())
|
|
7167
|
+
.replace('{total}', totalFilteredCount.toString())),
|
|
7168
|
+
]
|
|
7169
|
+
: []),
|
|
7170
|
+
// Max selections reached message
|
|
7171
|
+
...(isMaxSelectionsReached
|
|
7172
|
+
? [
|
|
7173
|
+
m('li.search-select-max-info', {
|
|
7174
|
+
style: {
|
|
7175
|
+
fontStyle: 'italic',
|
|
7176
|
+
color: 'var(--mm-text-hint, #9e9e9e)',
|
|
7177
|
+
padding: '8px 16px',
|
|
7178
|
+
cursor: 'default',
|
|
7179
|
+
},
|
|
7180
|
+
}, texts.maxSelectionsReached.replace('{max}', maxSelectedOptions.toString())),
|
|
7181
|
+
]
|
|
7182
|
+
: []),
|
|
7093
7183
|
// Add new option item
|
|
7094
7184
|
...(showAddNew
|
|
7095
7185
|
? [
|
|
7096
7186
|
m('li', {
|
|
7097
7187
|
onclick: async () => {
|
|
7098
|
-
|
|
7099
|
-
toggleOption(option, attrs);
|
|
7188
|
+
await createAndSelectOption(attrs);
|
|
7100
7189
|
},
|
|
7101
|
-
class: state.focusedIndex ===
|
|
7190
|
+
class: state.focusedIndex === displayedOptions.length ? 'active' : '',
|
|
7102
7191
|
onmouseover: () => {
|
|
7103
|
-
state.focusedIndex =
|
|
7192
|
+
state.focusedIndex = displayedOptions.length;
|
|
7104
7193
|
},
|
|
7105
7194
|
}, [m('span', `${texts.addNewPrefix} "${state.searchTerm}"`)]),
|
|
7106
7195
|
]
|
|
7107
7196
|
: []),
|
|
7108
7197
|
// List of filtered options
|
|
7109
|
-
...
|
|
7198
|
+
...displayedOptions.map((option, index) => m(DropdownOption, {
|
|
7110
7199
|
// key: option.id,
|
|
7111
7200
|
option,
|
|
7112
7201
|
index,
|
|
@@ -7116,6 +7205,7 @@ const SearchSelect = () => {
|
|
|
7116
7205
|
onMouseOver: (idx) => {
|
|
7117
7206
|
state.focusedIndex = idx;
|
|
7118
7207
|
},
|
|
7208
|
+
showCheckbox: maxSelectedOptions !== 1,
|
|
7119
7209
|
})),
|
|
7120
7210
|
]),
|
|
7121
7211
|
]);
|
|
@@ -9815,6 +9905,7 @@ exports.padLeft = padLeft;
|
|
|
9815
9905
|
exports.range = range;
|
|
9816
9906
|
exports.releasePortalContainer = releasePortalContainer;
|
|
9817
9907
|
exports.renderToPortal = renderToPortal;
|
|
9908
|
+
exports.sortOptions = sortOptions;
|
|
9818
9909
|
exports.toast = toast;
|
|
9819
9910
|
exports.uniqueId = uniqueId;
|
|
9820
9911
|
exports.uuid4 = uuid4;
|