mithril-materialized 2.0.0-beta.9 → 2.0.0-rc.2
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 +353 -10
- package/dist/advanced.css +6 -6
- package/dist/button.d.ts +56 -11
- package/dist/components.css +1674 -6
- package/dist/core.css +13 -13
- package/dist/datatable.d.ts +291 -0
- package/dist/datepicker.d.ts +12 -2
- package/dist/file-upload.d.ts +23 -0
- package/dist/floating-action-button.d.ts +1 -1
- package/dist/forms.css +344 -13
- package/dist/icon.d.ts +2 -2
- package/dist/image-list.d.ts +70 -0
- package/dist/index.css +1940 -20
- package/dist/index.d.ts +8 -0
- package/dist/index.esm.js +2736 -659
- package/dist/index.js +2746 -658
- package/dist/index.min.css +2 -2
- package/dist/index.umd.js +2746 -658
- package/dist/input-options.d.ts +18 -4
- package/dist/input.d.ts +0 -1
- package/dist/masonry.d.ts +17 -0
- package/dist/material-icon.d.ts +3 -0
- package/dist/pickers.css +45 -0
- package/dist/range-slider.d.ts +42 -0
- package/dist/theme-switcher.d.ts +23 -0
- package/dist/timeline.d.ts +43 -0
- package/dist/treeview.d.ts +39 -0
- package/dist/types.d.ts +226 -0
- package/dist/utilities.css +16 -9
- package/package.json +12 -9
- package/sass/components/_cards.scss +10 -3
- package/sass/components/_datatable.scss +417 -0
- package/sass/components/_datepicker.scss +57 -0
- package/sass/components/_global.scss +6 -6
- package/sass/components/_image-list.scss +421 -0
- package/sass/components/_masonry.scss +241 -0
- package/sass/components/_timeline.scss +452 -0
- package/sass/components/_treeview.scss +353 -0
- package/sass/components/forms/_forms.scss +1 -1
- package/sass/components/forms/_range-enhanced.scss +406 -0
- package/sass/components/forms/_range.scss +5 -5
- package/sass/components/forms/_select.scss +1 -1
- package/sass/materialize.scss +6 -0
package/dist/index.umd.js
CHANGED
|
@@ -420,12 +420,18 @@
|
|
|
420
420
|
return () => {
|
|
421
421
|
return {
|
|
422
422
|
view: ({ attrs }) => {
|
|
423
|
-
const { modalId, tooltip,
|
|
424
|
-
|
|
423
|
+
const { modalId, tooltip, tooltipPosition, tooltipPostion, // Keep for backwards compatibility
|
|
424
|
+
iconName, iconClass, label, className, attr, variant } = attrs, params = __rest(attrs, ["modalId", "tooltip", "tooltipPosition", "tooltipPostion", "iconName", "iconClass", "label", "className", "attr", "variant"]);
|
|
425
|
+
// Handle both new variant prop and legacy modalId/type
|
|
426
|
+
const buttonType = (variant === null || variant === void 0 ? void 0 : variant.type) || (modalId ? 'modal' : type || 'button');
|
|
427
|
+
const modalTarget = (variant === null || variant === void 0 ? void 0 : variant.type) === 'modal' ? variant.modalId : modalId;
|
|
428
|
+
const cn = [modalTarget ? 'modal-trigger' : '', tooltip ? 'tooltipped' : '', defaultClassNames, className]
|
|
425
429
|
.filter(Boolean)
|
|
426
430
|
.join(' ')
|
|
427
431
|
.trim();
|
|
428
|
-
|
|
432
|
+
// Use tooltipPosition if available, fallback to legacy tooltipPostion
|
|
433
|
+
const position = tooltipPosition || tooltipPostion || 'top';
|
|
434
|
+
return m(element, Object.assign(Object.assign(Object.assign({}, params), attr), { className: cn, href: modalTarget ? `#${modalTarget}` : undefined, 'data-position': tooltip ? position : undefined, 'data-tooltip': tooltip || undefined, type: buttonType === 'modal' ? 'button' : buttonType }),
|
|
429
435
|
// `${dca}${modalId ? `.modal-trigger[href=#${modalId}]` : ''}${
|
|
430
436
|
// tooltip ? `.tooltipped[data-position=${tooltipPostion || 'top'}][data-tooltip=${tooltip}]` : ''
|
|
431
437
|
// }${toAttributeString(attr)}`, {}
|
|
@@ -834,6 +840,18 @@
|
|
|
834
840
|
'M18.3 5.71a1 1 0 0 0-1.41 0L12 10.59 7.11 5.7A1 1 0 0 0 5.7 7.11L10.59 12l-4.89 4.89a1 1 0 1 0 1.41 1.41L12 13.41l4.89 4.89a1 1 0 0 0 1.41-1.41L13.41 12l4.89-4.89a1 1 0 0 0 0-1.4z',
|
|
835
841
|
'M0 0h24v24H0z',
|
|
836
842
|
],
|
|
843
|
+
chevron: [
|
|
844
|
+
'M16.59 8.59L12 13.17 7.41 8.59 6 10l6 6 6-6z', // chevron down
|
|
845
|
+
'M0 0h24v24H0z', // background
|
|
846
|
+
],
|
|
847
|
+
expand: [
|
|
848
|
+
'M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z', // plus
|
|
849
|
+
'M0 0h24v24H0z', // background
|
|
850
|
+
],
|
|
851
|
+
collapse: [
|
|
852
|
+
'M19 13H5v-2h14v2z', // minus
|
|
853
|
+
'M0 0h24v24H0z', // background
|
|
854
|
+
],
|
|
837
855
|
};
|
|
838
856
|
const MaterialIcon = () => {
|
|
839
857
|
return {
|
|
@@ -843,8 +861,8 @@
|
|
|
843
861
|
const rotationMap = {
|
|
844
862
|
down: 0,
|
|
845
863
|
up: 180,
|
|
846
|
-
left:
|
|
847
|
-
right: 90,
|
|
864
|
+
left: 90,
|
|
865
|
+
right: -90,
|
|
848
866
|
};
|
|
849
867
|
const rotation = (_a = rotationMap[direction]) !== null && _a !== void 0 ? _a : 0;
|
|
850
868
|
const transform = rotation ? `rotate(${rotation}deg)` : undefined;
|
|
@@ -1153,7 +1171,7 @@
|
|
|
1153
1171
|
const lang = language || 'lang-TypeScript';
|
|
1154
1172
|
const label = lang.replace('lang-', '');
|
|
1155
1173
|
const cb = code instanceof Array ? code.join('\n') : code;
|
|
1156
|
-
const cn = [newRow ? 'clear' : '', lang, className].filter(Boolean).join(' ').trim();
|
|
1174
|
+
const cn = [newRow ? 'clear' : '', lang, className].filter(Boolean).join(' ').trim() || undefined;
|
|
1157
1175
|
return m(`pre.codeblock${newRow ? '.clear' : ''}`, attrs, [
|
|
1158
1176
|
m('div', m('label', label)),
|
|
1159
1177
|
m('code', Object.assign(Object.assign({}, params), { className: cn }), cb),
|
|
@@ -1336,7 +1354,7 @@
|
|
|
1336
1354
|
};
|
|
1337
1355
|
};
|
|
1338
1356
|
|
|
1339
|
-
const defaultI18n = {
|
|
1357
|
+
const defaultI18n$2 = {
|
|
1340
1358
|
cancel: 'Cancel',
|
|
1341
1359
|
clear: 'Clear',
|
|
1342
1360
|
done: 'Ok',
|
|
@@ -1444,9 +1462,9 @@
|
|
|
1444
1462
|
else if (attrs.displayFormat) {
|
|
1445
1463
|
finalFormat = attrs.displayFormat;
|
|
1446
1464
|
}
|
|
1447
|
-
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, onSelect: null, onOpen: null, onClose: null }, attrs);
|
|
1465
|
+
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$2, onSelect: null, onOpen: null, onClose: null }, attrs);
|
|
1448
1466
|
// Merge i18n properly
|
|
1449
|
-
merged.i18n = Object.assign(Object.assign({}, defaultI18n), attrs.i18n);
|
|
1467
|
+
merged.i18n = Object.assign(Object.assign({}, defaultI18n$2), attrs.i18n);
|
|
1450
1468
|
return merged;
|
|
1451
1469
|
};
|
|
1452
1470
|
const toString = (date, format) => {
|
|
@@ -1465,6 +1483,42 @@
|
|
|
1465
1483
|
});
|
|
1466
1484
|
return result;
|
|
1467
1485
|
};
|
|
1486
|
+
// New function to format any specific date (not just state.date)
|
|
1487
|
+
const formatDate = (date, format, options) => {
|
|
1488
|
+
if (!date || !isDate(date)) {
|
|
1489
|
+
return '';
|
|
1490
|
+
}
|
|
1491
|
+
// Split format into tokens - match longer patterns first
|
|
1492
|
+
const formatTokens = /(dddd|ddd|dd|d|mmmm|mmm|mm|m|yyyy|yy)/g;
|
|
1493
|
+
let result = format;
|
|
1494
|
+
// Create temporary formats for the specific date
|
|
1495
|
+
const dateFormats = {
|
|
1496
|
+
d: () => date.getDate(),
|
|
1497
|
+
dd: () => {
|
|
1498
|
+
const d = date.getDate();
|
|
1499
|
+
return (d < 10 ? '0' : '') + d;
|
|
1500
|
+
},
|
|
1501
|
+
ddd: () => options.i18n.weekdaysShort[date.getDay()],
|
|
1502
|
+
dddd: () => options.i18n.weekdays[date.getDay()],
|
|
1503
|
+
m: () => date.getMonth() + 1,
|
|
1504
|
+
mm: () => {
|
|
1505
|
+
const m = date.getMonth() + 1;
|
|
1506
|
+
return (m < 10 ? '0' : '') + m;
|
|
1507
|
+
},
|
|
1508
|
+
mmm: () => options.i18n.monthsShort[date.getMonth()],
|
|
1509
|
+
mmmm: () => options.i18n.months[date.getMonth()],
|
|
1510
|
+
yy: () => ('' + date.getFullYear()).slice(2),
|
|
1511
|
+
yyyy: () => date.getFullYear(),
|
|
1512
|
+
};
|
|
1513
|
+
// Replace all format tokens with actual values
|
|
1514
|
+
result = result.replace(formatTokens, (match) => {
|
|
1515
|
+
if (dateFormats[match]) {
|
|
1516
|
+
return String(dateFormats[match]());
|
|
1517
|
+
}
|
|
1518
|
+
return match;
|
|
1519
|
+
});
|
|
1520
|
+
return result;
|
|
1521
|
+
};
|
|
1468
1522
|
const setDate = (date, preventOnSelect = false, options) => {
|
|
1469
1523
|
if (!date) {
|
|
1470
1524
|
state.date = null;
|
|
@@ -1491,6 +1545,55 @@
|
|
|
1491
1545
|
options.onSelect(state.date);
|
|
1492
1546
|
}
|
|
1493
1547
|
};
|
|
1548
|
+
const handleRangeSelection = (date, options) => {
|
|
1549
|
+
setToStartOfDay(date);
|
|
1550
|
+
// First click or reset - set start date
|
|
1551
|
+
if (!state.startDate || (state.startDate && state.endDate)) {
|
|
1552
|
+
state.startDate = new Date(date.getTime());
|
|
1553
|
+
state.endDate = null;
|
|
1554
|
+
state.selectionMode = 'end';
|
|
1555
|
+
state.isSelectingRange = true;
|
|
1556
|
+
}
|
|
1557
|
+
// Second click - set end date
|
|
1558
|
+
else if (state.startDate && !state.endDate) {
|
|
1559
|
+
// Ensure proper order (start <= end)
|
|
1560
|
+
if (date < state.startDate) {
|
|
1561
|
+
state.endDate = new Date(state.startDate.getTime());
|
|
1562
|
+
state.startDate = new Date(date.getTime());
|
|
1563
|
+
}
|
|
1564
|
+
else {
|
|
1565
|
+
state.endDate = new Date(date.getTime());
|
|
1566
|
+
}
|
|
1567
|
+
// Validate range constraints
|
|
1568
|
+
if (options.minDateRange || options.maxDateRange) {
|
|
1569
|
+
const daysDiff = Math.ceil((state.endDate.getTime() - state.startDate.getTime()) / (1000 * 60 * 60 * 24));
|
|
1570
|
+
if (options.minDateRange && daysDiff < options.minDateRange) {
|
|
1571
|
+
// Range too short, reset
|
|
1572
|
+
state.startDate = new Date(date.getTime());
|
|
1573
|
+
state.endDate = null;
|
|
1574
|
+
state.selectionMode = 'end';
|
|
1575
|
+
return;
|
|
1576
|
+
}
|
|
1577
|
+
if (options.maxDateRange && daysDiff > options.maxDateRange) {
|
|
1578
|
+
// Range too long, reset
|
|
1579
|
+
state.startDate = new Date(date.getTime());
|
|
1580
|
+
state.endDate = null;
|
|
1581
|
+
state.selectionMode = 'end';
|
|
1582
|
+
return;
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
state.selectionMode = null;
|
|
1586
|
+
state.isSelectingRange = false;
|
|
1587
|
+
// Call onSelect with both dates
|
|
1588
|
+
if (options.onSelect) {
|
|
1589
|
+
options.onSelect(state.startDate, state.endDate);
|
|
1590
|
+
}
|
|
1591
|
+
// Auto-close if enabled
|
|
1592
|
+
if (options.autoClose) {
|
|
1593
|
+
state.isOpen = false;
|
|
1594
|
+
}
|
|
1595
|
+
}
|
|
1596
|
+
};
|
|
1494
1597
|
const gotoDate = (date) => {
|
|
1495
1598
|
if (!isDate(date)) {
|
|
1496
1599
|
return;
|
|
@@ -1549,6 +1652,21 @@
|
|
|
1549
1652
|
arr.push('is-selected');
|
|
1550
1653
|
ariaSelected = 'true';
|
|
1551
1654
|
}
|
|
1655
|
+
// Range selection states
|
|
1656
|
+
if (opts.isRangeStart) {
|
|
1657
|
+
arr.push('is-range-start');
|
|
1658
|
+
ariaSelected = 'true';
|
|
1659
|
+
}
|
|
1660
|
+
if (opts.isRangeEnd) {
|
|
1661
|
+
arr.push('is-range-end');
|
|
1662
|
+
ariaSelected = 'true';
|
|
1663
|
+
}
|
|
1664
|
+
if (opts.isInRange) {
|
|
1665
|
+
arr.push('is-in-range');
|
|
1666
|
+
}
|
|
1667
|
+
if (opts.isRangePreview) {
|
|
1668
|
+
arr.push('is-range-preview');
|
|
1669
|
+
}
|
|
1552
1670
|
if (opts.hasEvent) {
|
|
1553
1671
|
arr.push('has-event');
|
|
1554
1672
|
}
|
|
@@ -1569,9 +1687,14 @@
|
|
|
1569
1687
|
const month = parseInt(target.getAttribute('data-month') || '0', 10);
|
|
1570
1688
|
const day = parseInt(target.getAttribute('data-day') || '0', 10);
|
|
1571
1689
|
const selectedDate = new Date(year, month, day);
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1690
|
+
if (options.dateRange) {
|
|
1691
|
+
handleRangeSelection(selectedDate, options);
|
|
1692
|
+
}
|
|
1693
|
+
else {
|
|
1694
|
+
setDate(selectedDate, false, options);
|
|
1695
|
+
if (options.autoClose) {
|
|
1696
|
+
state.isOpen = false;
|
|
1697
|
+
}
|
|
1575
1698
|
}
|
|
1576
1699
|
}
|
|
1577
1700
|
},
|
|
@@ -1631,16 +1754,37 @@
|
|
|
1631
1754
|
(options.maxDate && day > options.maxDate) ||
|
|
1632
1755
|
(options.disableWeekends && isWeekend(day)) ||
|
|
1633
1756
|
(options.disableDayFn && options.disableDayFn(day));
|
|
1757
|
+
// Range selection states
|
|
1758
|
+
let isRangeStart = false;
|
|
1759
|
+
let isRangeEnd = false;
|
|
1760
|
+
let isInRange = false;
|
|
1761
|
+
let isRangePreview = false;
|
|
1762
|
+
if (options.dateRange) {
|
|
1763
|
+
if (state.startDate && compareDates(day, state.startDate)) {
|
|
1764
|
+
isRangeStart = true;
|
|
1765
|
+
}
|
|
1766
|
+
if (state.endDate && compareDates(day, state.endDate)) {
|
|
1767
|
+
isRangeEnd = true;
|
|
1768
|
+
}
|
|
1769
|
+
if (state.startDate && state.endDate && day > state.startDate && day < state.endDate) {
|
|
1770
|
+
isInRange = true;
|
|
1771
|
+
}
|
|
1772
|
+
// TODO: Add hover preview logic for range selection
|
|
1773
|
+
}
|
|
1634
1774
|
const dayConfig = {
|
|
1635
1775
|
day: dayNumber,
|
|
1636
1776
|
month: monthNumber,
|
|
1637
1777
|
year: yearNumber,
|
|
1638
1778
|
hasEvent: false,
|
|
1639
|
-
isSelected: isSelected,
|
|
1779
|
+
isSelected: !options.dateRange && isSelected, // Only use isSelected in single date mode
|
|
1640
1780
|
isToday: isToday,
|
|
1641
1781
|
isDisabled: isDisabled,
|
|
1642
1782
|
isEmpty: isEmpty,
|
|
1643
1783
|
showDaysInNextAndPreviousMonths: false,
|
|
1784
|
+
isRangeStart: isRangeStart,
|
|
1785
|
+
isRangeEnd: isRangeEnd,
|
|
1786
|
+
isInRange: isInRange,
|
|
1787
|
+
isRangePreview: isRangePreview,
|
|
1644
1788
|
};
|
|
1645
1789
|
// Add week number cell at the beginning of each row
|
|
1646
1790
|
if (r === 0 && options.showWeekNumbers) {
|
|
@@ -1687,14 +1831,58 @@
|
|
|
1687
1831
|
return {
|
|
1688
1832
|
view: ({ attrs }) => {
|
|
1689
1833
|
const { options } = attrs;
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1834
|
+
if (options.dateRange) {
|
|
1835
|
+
// Range display
|
|
1836
|
+
const startDate = state.startDate;
|
|
1837
|
+
const endDate = state.endDate;
|
|
1838
|
+
if (startDate && endDate) {
|
|
1839
|
+
// Both dates selected
|
|
1840
|
+
const startDay = options.i18n.weekdaysShort[startDate.getDay()];
|
|
1841
|
+
const startMonth = options.i18n.monthsShort[startDate.getMonth()];
|
|
1842
|
+
const endDay = options.i18n.weekdaysShort[endDate.getDay()];
|
|
1843
|
+
const endMonth = options.i18n.monthsShort[endDate.getMonth()];
|
|
1844
|
+
return m('.datepicker-date-display.range-display', [
|
|
1845
|
+
m('span.year-text', startDate.getFullYear()),
|
|
1846
|
+
m('span.date-text', [
|
|
1847
|
+
m('span.start-date', `${startDay}, ${startMonth} ${startDate.getDate()}`),
|
|
1848
|
+
m('span.range-separator', ' - '),
|
|
1849
|
+
m('span.end-date', `${endDay}, ${endMonth} ${endDate.getDate()}`),
|
|
1850
|
+
]),
|
|
1851
|
+
]);
|
|
1852
|
+
}
|
|
1853
|
+
else if (startDate) {
|
|
1854
|
+
// Only start date selected
|
|
1855
|
+
const startDay = options.i18n.weekdaysShort[startDate.getDay()];
|
|
1856
|
+
const startMonth = options.i18n.monthsShort[startDate.getMonth()];
|
|
1857
|
+
return m('.datepicker-date-display.range-display', [
|
|
1858
|
+
m('span.year-text', startDate.getFullYear()),
|
|
1859
|
+
m('span.date-text', [
|
|
1860
|
+
m('span.start-date', `${startDay}, ${startMonth} ${startDate.getDate()}`),
|
|
1861
|
+
m('span.range-separator', ' - '),
|
|
1862
|
+
m('span.end-date.placeholder', 'Select end date'),
|
|
1863
|
+
]),
|
|
1864
|
+
]);
|
|
1865
|
+
}
|
|
1866
|
+
else {
|
|
1867
|
+
// No dates selected
|
|
1868
|
+
const currentDate = new Date();
|
|
1869
|
+
return m('.datepicker-date-display.range-display', [
|
|
1870
|
+
m('span.year-text', currentDate.getFullYear()),
|
|
1871
|
+
m('span.date-text.placeholder', 'Select date range'),
|
|
1872
|
+
]);
|
|
1873
|
+
}
|
|
1874
|
+
}
|
|
1875
|
+
else {
|
|
1876
|
+
// Single date display (original behavior)
|
|
1877
|
+
const displayDate = isDate(state.date) ? state.date : new Date();
|
|
1878
|
+
const day = options.i18n.weekdaysShort[displayDate.getDay()];
|
|
1879
|
+
const month = options.i18n.monthsShort[displayDate.getMonth()];
|
|
1880
|
+
const date = displayDate.getDate();
|
|
1881
|
+
return m('.datepicker-date-display', [
|
|
1882
|
+
m('span.year-text', displayDate.getFullYear()),
|
|
1883
|
+
m('span.date-text', `${day}, ${month} ${date}`),
|
|
1884
|
+
]);
|
|
1885
|
+
}
|
|
1698
1886
|
}
|
|
1699
1887
|
};
|
|
1700
1888
|
};
|
|
@@ -1843,6 +2031,10 @@
|
|
|
1843
2031
|
id: uniqueId(),
|
|
1844
2032
|
isOpen: false,
|
|
1845
2033
|
date: null,
|
|
2034
|
+
startDate: null,
|
|
2035
|
+
endDate: null,
|
|
2036
|
+
selectionMode: null,
|
|
2037
|
+
isSelectingRange: false,
|
|
1846
2038
|
calendars: [{ month: 0, year: 0 }],
|
|
1847
2039
|
monthDropdownOpen: false,
|
|
1848
2040
|
yearDropdownOpen: false,
|
|
@@ -1867,17 +2059,35 @@
|
|
|
1867
2059
|
yyyy: () => { var _a; return ((_a = state.date) === null || _a === void 0 ? void 0 : _a.getFullYear()) || 0; },
|
|
1868
2060
|
},
|
|
1869
2061
|
};
|
|
1870
|
-
// Initialize date
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
2062
|
+
// Initialize date or date range
|
|
2063
|
+
if (options.dateRange) {
|
|
2064
|
+
// Initialize range dates
|
|
2065
|
+
if (attrs.initialStartDate && isDate(attrs.initialStartDate)) {
|
|
2066
|
+
state.startDate = new Date(attrs.initialStartDate.getTime());
|
|
2067
|
+
setToStartOfDay(state.startDate);
|
|
2068
|
+
gotoDate(state.startDate);
|
|
2069
|
+
}
|
|
2070
|
+
if (attrs.initialEndDate && isDate(attrs.initialEndDate)) {
|
|
2071
|
+
state.endDate = new Date(attrs.initialEndDate.getTime());
|
|
2072
|
+
setToStartOfDay(state.endDate);
|
|
2073
|
+
}
|
|
2074
|
+
if (!state.startDate && !state.endDate) {
|
|
2075
|
+
gotoDate(new Date());
|
|
2076
|
+
}
|
|
1878
2077
|
}
|
|
1879
2078
|
else {
|
|
1880
|
-
|
|
2079
|
+
// Single date initialization (original behavior)
|
|
2080
|
+
let defaultDate = attrs.defaultDate;
|
|
2081
|
+
if (!defaultDate && attrs.initialValue) {
|
|
2082
|
+
defaultDate = new Date(attrs.initialValue);
|
|
2083
|
+
}
|
|
2084
|
+
if (isDate(defaultDate)) {
|
|
2085
|
+
// Always set the date if we have initialValue or defaultDate
|
|
2086
|
+
setDate(defaultDate, true, options);
|
|
2087
|
+
}
|
|
2088
|
+
else {
|
|
2089
|
+
gotoDate(new Date());
|
|
2090
|
+
}
|
|
1881
2091
|
}
|
|
1882
2092
|
// Add document click listener to close dropdowns
|
|
1883
2093
|
document.addEventListener('click', handleDocumentClick);
|
|
@@ -1893,19 +2103,61 @@
|
|
|
1893
2103
|
const className = cn1 || cn2 || 'col s12';
|
|
1894
2104
|
// Calculate display value for the input
|
|
1895
2105
|
let displayValue = '';
|
|
1896
|
-
if (
|
|
1897
|
-
|
|
2106
|
+
if (options.dateRange) {
|
|
2107
|
+
// Handle date range display
|
|
2108
|
+
const formatToUse = attrs.displayFormat || options.format;
|
|
2109
|
+
if (state.startDate && state.endDate) {
|
|
2110
|
+
let startStr, endStr;
|
|
2111
|
+
if (attrs.displayFormat) {
|
|
2112
|
+
// Custom display format for date range
|
|
2113
|
+
startStr = attrs.displayFormat
|
|
2114
|
+
.replace(/yyyy/gi, state.startDate.getFullYear().toString())
|
|
2115
|
+
.replace(/mm/gi, (state.startDate.getMonth() + 1).toString().padStart(2, '0'))
|
|
2116
|
+
.replace(/dd/gi, state.startDate.getDate().toString().padStart(2, '0'));
|
|
2117
|
+
endStr = attrs.displayFormat
|
|
2118
|
+
.replace(/yyyy/gi, state.endDate.getFullYear().toString())
|
|
2119
|
+
.replace(/mm/gi, (state.endDate.getMonth() + 1).toString().padStart(2, '0'))
|
|
2120
|
+
.replace(/dd/gi, state.endDate.getDate().toString().padStart(2, '0'));
|
|
2121
|
+
}
|
|
2122
|
+
else {
|
|
2123
|
+
// Standard format
|
|
2124
|
+
startStr = formatDate(state.startDate, formatToUse, options);
|
|
2125
|
+
endStr = formatDate(state.endDate, formatToUse, options);
|
|
2126
|
+
}
|
|
2127
|
+
displayValue = `${startStr} - ${endStr}`;
|
|
2128
|
+
}
|
|
2129
|
+
else if (state.startDate) {
|
|
2130
|
+
let startStr;
|
|
2131
|
+
if (attrs.displayFormat) {
|
|
2132
|
+
// Custom display format for single date
|
|
2133
|
+
startStr = attrs.displayFormat
|
|
2134
|
+
.replace(/yyyy/gi, state.startDate.getFullYear().toString())
|
|
2135
|
+
.replace(/mm/gi, (state.startDate.getMonth() + 1).toString().padStart(2, '0'))
|
|
2136
|
+
.replace(/dd/gi, state.startDate.getDate().toString().padStart(2, '0'));
|
|
2137
|
+
}
|
|
2138
|
+
else {
|
|
2139
|
+
// Standard format
|
|
2140
|
+
startStr = formatDate(state.startDate, formatToUse, options);
|
|
2141
|
+
}
|
|
2142
|
+
displayValue = `${startStr} - `;
|
|
2143
|
+
}
|
|
1898
2144
|
}
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
// const formatRegex = /(yyyy|mm|dd)/gi;
|
|
1902
|
-
let customDisplayValue = attrs.displayFormat;
|
|
2145
|
+
else {
|
|
2146
|
+
// Single date display (original behavior)
|
|
1903
2147
|
if (state.date) {
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
2148
|
+
displayValue = toString(state.date, options.format);
|
|
2149
|
+
}
|
|
2150
|
+
// Custom date format handling
|
|
2151
|
+
if (attrs.displayFormat) {
|
|
2152
|
+
// const formatRegex = /(yyyy|mm|dd)/gi;
|
|
2153
|
+
let customDisplayValue = attrs.displayFormat;
|
|
2154
|
+
if (state.date) {
|
|
2155
|
+
customDisplayValue = customDisplayValue
|
|
2156
|
+
.replace(/yyyy/gi, state.date.getFullYear().toString())
|
|
2157
|
+
.replace(/mm/gi, (state.date.getMonth() + 1).toString().padStart(2, '0'))
|
|
2158
|
+
.replace(/dd/gi, state.date.getDate().toString().padStart(2, '0'));
|
|
2159
|
+
displayValue = customDisplayValue;
|
|
2160
|
+
}
|
|
1909
2161
|
}
|
|
1910
2162
|
}
|
|
1911
2163
|
return m('.input-field', {
|
|
@@ -2009,8 +2261,19 @@
|
|
|
2009
2261
|
type: 'button',
|
|
2010
2262
|
onclick: () => {
|
|
2011
2263
|
state.isOpen = false;
|
|
2012
|
-
if (
|
|
2013
|
-
|
|
2264
|
+
if (options.dateRange) {
|
|
2265
|
+
// Range mode
|
|
2266
|
+
if (state.startDate && state.endDate && onchange) {
|
|
2267
|
+
const startStr = toString(state.startDate, 'yyyy-mm-dd');
|
|
2268
|
+
const endStr = toString(state.endDate, 'yyyy-mm-dd');
|
|
2269
|
+
onchange(`${startStr} - ${endStr}`);
|
|
2270
|
+
}
|
|
2271
|
+
}
|
|
2272
|
+
else {
|
|
2273
|
+
// Single date mode
|
|
2274
|
+
if (state.date && onchange) {
|
|
2275
|
+
onchange(toString(state.date, 'yyyy-mm-dd')); // Always return ISO format
|
|
2276
|
+
}
|
|
2014
2277
|
}
|
|
2015
2278
|
if (options.onClose)
|
|
2016
2279
|
options.onClose();
|
|
@@ -2039,246 +2302,458 @@
|
|
|
2039
2302
|
};
|
|
2040
2303
|
};
|
|
2041
2304
|
|
|
2042
|
-
|
|
2043
|
-
const
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
}
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2305
|
+
// Tooltip component for range sliders
|
|
2306
|
+
const RangeTooltip = {
|
|
2307
|
+
view: ({ attrs: { show, position, value } }) => {
|
|
2308
|
+
return show ? m(`.value-tooltip.${position}`, value.toFixed(0)) : null;
|
|
2309
|
+
},
|
|
2310
|
+
};
|
|
2311
|
+
const DoubleRangeTooltip = {
|
|
2312
|
+
view: ({ attrs: { show, orientation, value } }) => {
|
|
2313
|
+
return show ? m(`.value.${orientation}`, value.toFixed(0)) : null;
|
|
2314
|
+
},
|
|
2315
|
+
};
|
|
2316
|
+
// Utility functions
|
|
2317
|
+
const getPercentage = (value, min, max) => {
|
|
2318
|
+
return ((value - min) / (max - min)) * 100;
|
|
2319
|
+
};
|
|
2320
|
+
const positionToValue = (e, rect, min, max, step, vertical) => {
|
|
2321
|
+
let percentage;
|
|
2322
|
+
if (vertical) {
|
|
2323
|
+
percentage = ((rect.bottom - e.clientY) / rect.height) * 100;
|
|
2324
|
+
}
|
|
2325
|
+
else {
|
|
2326
|
+
percentage = ((e.clientX - rect.left) / rect.width) * 100;
|
|
2327
|
+
}
|
|
2328
|
+
percentage = Math.max(0, Math.min(100, percentage));
|
|
2329
|
+
const value = min + (percentage / 100) * (max - min);
|
|
2330
|
+
return Math.round(value / step) * step;
|
|
2331
|
+
};
|
|
2332
|
+
const handleKeyboardNavigation = (key, currentValue, min, max, step) => {
|
|
2333
|
+
const largeStep = step * 10;
|
|
2334
|
+
switch (key) {
|
|
2335
|
+
case 'ArrowLeft':
|
|
2336
|
+
case 'ArrowDown':
|
|
2337
|
+
return Math.max(min, currentValue - step);
|
|
2338
|
+
case 'ArrowRight':
|
|
2339
|
+
case 'ArrowUp':
|
|
2340
|
+
return Math.min(max, currentValue + step);
|
|
2341
|
+
case 'PageDown':
|
|
2342
|
+
return Math.max(min, currentValue - largeStep);
|
|
2343
|
+
case 'PageUp':
|
|
2344
|
+
return Math.min(max, currentValue + largeStep);
|
|
2345
|
+
case 'Home':
|
|
2346
|
+
return min;
|
|
2347
|
+
case 'End':
|
|
2348
|
+
return max;
|
|
2349
|
+
default:
|
|
2350
|
+
return null;
|
|
2351
|
+
}
|
|
2352
|
+
};
|
|
2353
|
+
const initRangeState = (state, attrs) => {
|
|
2354
|
+
const { min = 0, max = 100, initialValue, minValue, maxValue } = attrs;
|
|
2355
|
+
// Initialize single range value
|
|
2356
|
+
if (initialValue !== undefined) {
|
|
2357
|
+
const currentValue = initialValue;
|
|
2358
|
+
if (state.singleValue === undefined) {
|
|
2359
|
+
state.singleValue = currentValue;
|
|
2360
|
+
}
|
|
2361
|
+
if (state.lastInitialValue !== initialValue && !state.hasUserInteracted) {
|
|
2362
|
+
state.singleValue = initialValue;
|
|
2363
|
+
state.lastInitialValue = initialValue;
|
|
2364
|
+
}
|
|
2365
|
+
}
|
|
2366
|
+
else if (state.singleValue === undefined) {
|
|
2367
|
+
state.singleValue = min;
|
|
2368
|
+
}
|
|
2369
|
+
// Initialize range values
|
|
2370
|
+
const currentMinValue = minValue !== undefined ? minValue : min;
|
|
2371
|
+
const currentMaxValue = maxValue !== undefined ? maxValue : max;
|
|
2372
|
+
if (state.rangeMinValue === undefined || state.rangeMaxValue === undefined) {
|
|
2373
|
+
state.rangeMinValue = currentMinValue;
|
|
2374
|
+
state.rangeMaxValue = currentMaxValue;
|
|
2375
|
+
}
|
|
2376
|
+
if (!state.hasUserInteracted &&
|
|
2377
|
+
((minValue !== undefined && state.lastMinValue !== minValue) ||
|
|
2378
|
+
(maxValue !== undefined && state.lastMaxValue !== maxValue))) {
|
|
2379
|
+
state.rangeMinValue = currentMinValue;
|
|
2380
|
+
state.rangeMaxValue = currentMaxValue;
|
|
2381
|
+
state.lastMinValue = minValue;
|
|
2382
|
+
state.lastMaxValue = maxValue;
|
|
2383
|
+
}
|
|
2384
|
+
// Initialize active thumb if not set
|
|
2385
|
+
if (state.activeThumb === null) {
|
|
2386
|
+
state.activeThumb = 'min';
|
|
2387
|
+
}
|
|
2388
|
+
// Initialize dragging state
|
|
2389
|
+
if (state.isDragging === undefined) {
|
|
2390
|
+
state.isDragging = false;
|
|
2391
|
+
}
|
|
2392
|
+
};
|
|
2393
|
+
const updateRangeValues = (minValue, maxValue, attrs, state, immediate) => {
|
|
2394
|
+
// Ensure min doesn't exceed max and vice versa
|
|
2395
|
+
if (minValue > maxValue)
|
|
2396
|
+
minValue = maxValue;
|
|
2397
|
+
if (maxValue < minValue)
|
|
2398
|
+
maxValue = minValue;
|
|
2399
|
+
state.rangeMinValue = minValue;
|
|
2400
|
+
state.rangeMaxValue = maxValue;
|
|
2401
|
+
state.hasUserInteracted = true;
|
|
2402
|
+
// Call oninput for immediate feedback or onchange for final changes
|
|
2403
|
+
if (immediate && attrs.oninput) {
|
|
2404
|
+
attrs.oninput(minValue, maxValue);
|
|
2405
|
+
}
|
|
2406
|
+
else if (!immediate && attrs.onchange) {
|
|
2407
|
+
attrs.onchange(minValue, maxValue);
|
|
2408
|
+
}
|
|
2409
|
+
};
|
|
2410
|
+
// Single Range Slider Component
|
|
2411
|
+
const SingleRangeSlider = {
|
|
2412
|
+
oninit({ state }) {
|
|
2413
|
+
if (!state.componentInitialized) {
|
|
2414
|
+
state.componentInitialized = true;
|
|
2415
|
+
}
|
|
2416
|
+
},
|
|
2417
|
+
onremove({ state }) {
|
|
2418
|
+
if (state.cleanupMouseEvents) {
|
|
2419
|
+
state.cleanupMouseEvents();
|
|
2420
|
+
state.cleanupMouseEvents = null;
|
|
2421
|
+
}
|
|
2422
|
+
},
|
|
2423
|
+
view({ attrs, state, }) {
|
|
2424
|
+
const { cn, style, iconName, id, label, isMandatory, helperText, min = 0, max = 100, step = 1, vertical = false, showValue = false, valueDisplay, height = '200px', disabled = false, tooltipPos = 'top', oninput, onchange, } = attrs;
|
|
2425
|
+
// Apply fallback logic for valueDisplay if not explicitly set
|
|
2426
|
+
const finalValueDisplay = valueDisplay || (showValue ? 'always' : 'none');
|
|
2427
|
+
// Initialize state
|
|
2428
|
+
initRangeState(state, attrs);
|
|
2429
|
+
const percentage = getPercentage(state.singleValue, min, max);
|
|
2430
|
+
const containerStyle = vertical ? { height } : {};
|
|
2431
|
+
const orientation = vertical ? 'vertical' : 'horizontal';
|
|
2432
|
+
const progressStyle = vertical ? { height: `${percentage}%` } : { width: `${percentage}%` };
|
|
2433
|
+
const thumbStyle = vertical ? { bottom: `${percentage}%` } : { left: `${percentage}%` };
|
|
2434
|
+
// Determine tooltip position for vertical sliders
|
|
2435
|
+
const tooltipPosition = vertical
|
|
2436
|
+
? tooltipPos === 'top' || tooltipPos === 'bottom'
|
|
2437
|
+
? 'right'
|
|
2438
|
+
: tooltipPos
|
|
2439
|
+
: tooltipPos;
|
|
2440
|
+
const updateSingleValue = (newValue, immediate = false) => {
|
|
2441
|
+
state.singleValue = newValue;
|
|
2442
|
+
state.hasUserInteracted = true;
|
|
2443
|
+
if (immediate && oninput) {
|
|
2444
|
+
oninput(newValue);
|
|
2445
|
+
}
|
|
2446
|
+
else if (!immediate && onchange) {
|
|
2447
|
+
onchange(newValue);
|
|
2448
|
+
}
|
|
2449
|
+
};
|
|
2450
|
+
const handleMouseDown = (e) => {
|
|
2451
|
+
if (disabled)
|
|
2452
|
+
return;
|
|
2453
|
+
e.preventDefault();
|
|
2454
|
+
state.isDragging = true;
|
|
2455
|
+
if (finalValueDisplay === 'auto') {
|
|
2456
|
+
m.redraw();
|
|
2457
|
+
}
|
|
2458
|
+
const thumbElement = e.currentTarget;
|
|
2459
|
+
const container = thumbElement.parentElement;
|
|
2460
|
+
if (!container)
|
|
2461
|
+
return;
|
|
2462
|
+
const handleMouseMove = (e) => {
|
|
2463
|
+
if (!state.isDragging || !container)
|
|
2464
|
+
return;
|
|
2465
|
+
const rect = container.getBoundingClientRect();
|
|
2466
|
+
const value = positionToValue(e, rect, min, max, step, vertical);
|
|
2467
|
+
updateSingleValue(value, true);
|
|
2468
|
+
m.redraw();
|
|
2469
|
+
};
|
|
2470
|
+
const handleMouseUp = () => {
|
|
2471
|
+
state.isDragging = false;
|
|
2472
|
+
if (state.cleanupMouseEvents) {
|
|
2473
|
+
state.cleanupMouseEvents();
|
|
2474
|
+
state.cleanupMouseEvents = null;
|
|
2060
2475
|
}
|
|
2061
|
-
|
|
2062
|
-
|
|
2476
|
+
updateSingleValue(state.singleValue, false);
|
|
2477
|
+
if (finalValueDisplay === 'auto') {
|
|
2478
|
+
m.redraw();
|
|
2063
2479
|
}
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2480
|
+
};
|
|
2481
|
+
document.addEventListener('mousemove', handleMouseMove);
|
|
2482
|
+
document.addEventListener('mouseup', handleMouseUp);
|
|
2483
|
+
state.cleanupMouseEvents = () => {
|
|
2484
|
+
document.removeEventListener('mousemove', handleMouseMove);
|
|
2485
|
+
document.removeEventListener('mouseup', handleMouseUp);
|
|
2486
|
+
};
|
|
2487
|
+
};
|
|
2488
|
+
const fieldClass = vertical ? 'range-field vertical' : 'range-field';
|
|
2489
|
+
return m('.input-field', { className: cn, style }, [
|
|
2490
|
+
iconName ? m('i.material-icons.prefix', iconName) : undefined,
|
|
2491
|
+
m('input[type=range]', {
|
|
2492
|
+
id,
|
|
2493
|
+
value: state.singleValue,
|
|
2494
|
+
min,
|
|
2495
|
+
max,
|
|
2496
|
+
step,
|
|
2497
|
+
style: { display: 'none' },
|
|
2498
|
+
disabled,
|
|
2499
|
+
tabindex: -1,
|
|
2500
|
+
}),
|
|
2501
|
+
m('div', { class: fieldClass, style: containerStyle }, [
|
|
2502
|
+
m(`.single-range-slider.${orientation}`, {
|
|
2503
|
+
tabindex: disabled ? -1 : 0,
|
|
2504
|
+
role: 'slider',
|
|
2505
|
+
'aria-valuemin': min,
|
|
2506
|
+
'aria-valuemax': max,
|
|
2507
|
+
'aria-valuenow': state.singleValue,
|
|
2508
|
+
'aria-label': label || 'Range slider',
|
|
2509
|
+
onclick: (e) => {
|
|
2510
|
+
if (disabled)
|
|
2511
|
+
return;
|
|
2512
|
+
const container = e.currentTarget;
|
|
2513
|
+
const rect = container.getBoundingClientRect();
|
|
2514
|
+
const value = positionToValue(e, rect, min, max, step, vertical);
|
|
2515
|
+
updateSingleValue(value, false);
|
|
2516
|
+
e.currentTarget.focus();
|
|
2517
|
+
},
|
|
2518
|
+
onkeydown: (e) => {
|
|
2519
|
+
if (disabled)
|
|
2520
|
+
return;
|
|
2521
|
+
const currentValue = state.singleValue;
|
|
2522
|
+
const newValue = handleKeyboardNavigation(e.key, currentValue, min, max, step);
|
|
2523
|
+
if (newValue !== null) {
|
|
2524
|
+
e.preventDefault();
|
|
2525
|
+
updateSingleValue(newValue, false);
|
|
2526
|
+
}
|
|
2527
|
+
},
|
|
2528
|
+
}, [
|
|
2529
|
+
m(`.track.${orientation}`),
|
|
2530
|
+
m(`.range-progress.${orientation}`, { style: progressStyle }),
|
|
2531
|
+
m(`.thumb.${orientation}`, { style: thumbStyle, onmousedown: handleMouseDown }, m(RangeTooltip, {
|
|
2532
|
+
value: state.singleValue,
|
|
2533
|
+
position: tooltipPosition,
|
|
2534
|
+
show: finalValueDisplay === 'always' || (finalValueDisplay === 'auto' && state.isDragging),
|
|
2535
|
+
})),
|
|
2536
|
+
]),
|
|
2537
|
+
]),
|
|
2538
|
+
label ? m(Label, { label, id, isMandatory, isActive: true }) : null,
|
|
2539
|
+
helperText ? m(HelperText, { helperText }) : null,
|
|
2540
|
+
]);
|
|
2541
|
+
},
|
|
2542
|
+
};
|
|
2543
|
+
// Double Range Slider Component
|
|
2544
|
+
const DoubleRangeSlider = {
|
|
2545
|
+
oninit({ state }) {
|
|
2546
|
+
if (!state.componentInitialized) {
|
|
2547
|
+
state.componentInitialized = true;
|
|
2548
|
+
}
|
|
2549
|
+
},
|
|
2550
|
+
onremove({ state }) {
|
|
2551
|
+
if (state.cleanupMouseEvents) {
|
|
2552
|
+
state.cleanupMouseEvents();
|
|
2553
|
+
state.cleanupMouseEvents = null;
|
|
2554
|
+
}
|
|
2555
|
+
},
|
|
2556
|
+
view({ attrs, state, }) {
|
|
2557
|
+
const { cn, style, iconName, id, label, isMandatory, helperText, min = 0, max = 100, step = 1, vertical = false, showValue = false, valueDisplay, height = '200px', disabled = false, } = attrs;
|
|
2558
|
+
const finalValueDisplay = valueDisplay || (showValue ? 'always' : 'none');
|
|
2559
|
+
initRangeState(state, attrs);
|
|
2560
|
+
const minPercentage = getPercentage(state.rangeMinValue, min, max);
|
|
2561
|
+
const maxPercentage = getPercentage(state.rangeMaxValue, min, max);
|
|
2562
|
+
const containerStyle = vertical ? { height } : {};
|
|
2563
|
+
const orientation = vertical ? 'vertical' : 'horizontal';
|
|
2564
|
+
const rangeStyle = vertical
|
|
2565
|
+
? {
|
|
2566
|
+
bottom: `${minPercentage}%`,
|
|
2567
|
+
height: `${maxPercentage - minPercentage}%`,
|
|
2568
|
+
}
|
|
2569
|
+
: {
|
|
2570
|
+
left: `${minPercentage}%`,
|
|
2571
|
+
width: `${maxPercentage - minPercentage}%`,
|
|
2572
|
+
};
|
|
2573
|
+
const createThumbStyle = (percentage, isActive) => vertical
|
|
2574
|
+
? {
|
|
2575
|
+
bottom: `${percentage}%`,
|
|
2576
|
+
zIndex: isActive ? 10 : 5,
|
|
2577
|
+
}
|
|
2578
|
+
: {
|
|
2579
|
+
left: `${percentage}%`,
|
|
2580
|
+
zIndex: isActive ? 10 : 5,
|
|
2581
|
+
};
|
|
2582
|
+
const handleMouseDown = (thumb) => (e) => {
|
|
2583
|
+
if (disabled)
|
|
2584
|
+
return;
|
|
2585
|
+
e.preventDefault();
|
|
2586
|
+
state.isDragging = true;
|
|
2587
|
+
state.activeThumb = thumb;
|
|
2588
|
+
if (finalValueDisplay === 'auto') {
|
|
2589
|
+
m.redraw();
|
|
2590
|
+
}
|
|
2591
|
+
const thumbElement = e.currentTarget;
|
|
2592
|
+
const container = thumbElement.parentElement;
|
|
2593
|
+
if (!container)
|
|
2594
|
+
return;
|
|
2595
|
+
const handleMouseMove = (e) => {
|
|
2596
|
+
if (!state.isDragging || !container)
|
|
2597
|
+
return;
|
|
2598
|
+
const rect = container.getBoundingClientRect();
|
|
2599
|
+
const steppedValue = positionToValue(e, rect, min, max, step, vertical);
|
|
2600
|
+
if (thumb === 'min') {
|
|
2601
|
+
updateRangeValues(Math.min(steppedValue, state.rangeMaxValue), state.rangeMaxValue, attrs, state, true);
|
|
2069
2602
|
}
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
case ' ':
|
|
2073
|
-
e.preventDefault();
|
|
2074
|
-
if (state.isOpen && state.focusedIndex >= 0 && state.focusedIndex < availableItems.length) {
|
|
2075
|
-
const selectedItem = availableItems[state.focusedIndex];
|
|
2076
|
-
const value = (selectedItem.id || selectedItem.label);
|
|
2077
|
-
state.initialValue = value;
|
|
2078
|
-
state.isOpen = false;
|
|
2079
|
-
state.focusedIndex = -1;
|
|
2080
|
-
if (onchange)
|
|
2081
|
-
onchange(value);
|
|
2603
|
+
else {
|
|
2604
|
+
updateRangeValues(state.rangeMinValue, Math.max(steppedValue, state.rangeMinValue), attrs, state, true);
|
|
2082
2605
|
}
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2606
|
+
m.redraw();
|
|
2607
|
+
};
|
|
2608
|
+
const handleMouseUp = () => {
|
|
2609
|
+
state.isDragging = false;
|
|
2610
|
+
state.activeThumb = null;
|
|
2611
|
+
if (state.cleanupMouseEvents) {
|
|
2612
|
+
state.cleanupMouseEvents();
|
|
2613
|
+
state.cleanupMouseEvents = null;
|
|
2086
2614
|
}
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
},
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2615
|
+
updateRangeValues(state.rangeMinValue, state.rangeMaxValue, attrs, state, false);
|
|
2616
|
+
if (finalValueDisplay === 'auto') {
|
|
2617
|
+
m.redraw();
|
|
2618
|
+
}
|
|
2619
|
+
};
|
|
2620
|
+
document.addEventListener('mousemove', handleMouseMove);
|
|
2621
|
+
document.addEventListener('mouseup', handleMouseUp);
|
|
2622
|
+
state.cleanupMouseEvents = () => {
|
|
2623
|
+
document.removeEventListener('mousemove', handleMouseMove);
|
|
2624
|
+
document.removeEventListener('mouseup', handleMouseUp);
|
|
2625
|
+
};
|
|
2626
|
+
};
|
|
2627
|
+
const fieldClass = vertical ? 'range-field vertical' : 'range-field';
|
|
2628
|
+
return m('.input-field', { className: cn, style }, [
|
|
2629
|
+
iconName ? m('i.material-icons.prefix', iconName) : undefined,
|
|
2630
|
+
m('input[type=range]', {
|
|
2631
|
+
id,
|
|
2632
|
+
value: state.rangeMinValue,
|
|
2633
|
+
min,
|
|
2634
|
+
max,
|
|
2635
|
+
step,
|
|
2636
|
+
style: { display: 'none' },
|
|
2637
|
+
disabled,
|
|
2638
|
+
tabindex: -1,
|
|
2639
|
+
}),
|
|
2640
|
+
m('input[type=range]', {
|
|
2641
|
+
id: `${id}_max`,
|
|
2642
|
+
value: state.rangeMaxValue,
|
|
2643
|
+
min,
|
|
2644
|
+
max,
|
|
2645
|
+
step,
|
|
2646
|
+
style: { display: 'none' },
|
|
2647
|
+
disabled,
|
|
2648
|
+
tabindex: -1,
|
|
2649
|
+
}),
|
|
2650
|
+
m(`div`, { className: fieldClass }, [
|
|
2651
|
+
m(`.double-range-slider.${orientation}`, {
|
|
2652
|
+
style: containerStyle,
|
|
2653
|
+
onclick: (e) => {
|
|
2654
|
+
if (disabled)
|
|
2655
|
+
return;
|
|
2656
|
+
const container = e.currentTarget;
|
|
2657
|
+
const rect = container.getBoundingClientRect();
|
|
2658
|
+
const steppedValue = positionToValue(e, rect, min, max, step, vertical);
|
|
2659
|
+
const distToMin = Math.abs(steppedValue - state.rangeMinValue);
|
|
2660
|
+
const distToMax = Math.abs(steppedValue - state.rangeMaxValue);
|
|
2661
|
+
if (distToMin <= distToMax) {
|
|
2662
|
+
updateRangeValues(Math.min(steppedValue, state.rangeMaxValue), state.rangeMaxValue, attrs, state, false);
|
|
2663
|
+
state.activeThumb = 'min';
|
|
2664
|
+
const minThumb = container.querySelector('.thumb.min-thumb');
|
|
2665
|
+
if (minThumb)
|
|
2666
|
+
minThumb.focus();
|
|
2667
|
+
}
|
|
2668
|
+
else {
|
|
2669
|
+
updateRangeValues(state.rangeMinValue, Math.max(steppedValue, state.rangeMinValue), attrs, state, false);
|
|
2670
|
+
state.activeThumb = 'max';
|
|
2671
|
+
const maxThumb = container.querySelector('.thumb.max-thumb');
|
|
2672
|
+
if (maxThumb)
|
|
2673
|
+
maxThumb.focus();
|
|
2674
|
+
}
|
|
2675
|
+
},
|
|
2123
2676
|
}, [
|
|
2124
|
-
m(
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2677
|
+
m(`.track.${orientation}`),
|
|
2678
|
+
m(`.range.${orientation}`, { style: rangeStyle }),
|
|
2679
|
+
// Min thumb
|
|
2680
|
+
m(`.thumb.${orientation}.min-thumb${state.activeThumb === 'min' ? '.active' : ''}`, {
|
|
2681
|
+
style: createThumbStyle(minPercentage, state.activeThumb === 'min'),
|
|
2682
|
+
tabindex: disabled ? -1 : 0,
|
|
2683
|
+
role: 'slider',
|
|
2684
|
+
'aria-valuemin': min,
|
|
2685
|
+
'aria-valuemax': state.rangeMaxValue,
|
|
2686
|
+
'aria-valuenow': state.rangeMinValue,
|
|
2687
|
+
'aria-label': `Minimum value: ${state.rangeMinValue}`,
|
|
2688
|
+
'aria-orientation': vertical ? 'vertical' : 'horizontal',
|
|
2689
|
+
onmousedown: handleMouseDown('min'),
|
|
2130
2690
|
onclick: (e) => {
|
|
2131
|
-
e.preventDefault();
|
|
2132
2691
|
e.stopPropagation();
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
state.focusedIndex = state.isOpen ? 0 : -1;
|
|
2136
|
-
}
|
|
2692
|
+
state.activeThumb = 'min';
|
|
2693
|
+
e.currentTarget.focus();
|
|
2137
2694
|
},
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
state.
|
|
2150
|
-
},
|
|
2151
|
-
style: getDropdownStyles(state.inputRef, true, items.map((item) => (Object.assign(Object.assign({}, item), {
|
|
2152
|
-
// Convert dropdown items to format expected by getDropdownStyles
|
|
2153
|
-
group: undefined }))), true),
|
|
2154
|
-
}, items.map((item, index) => {
|
|
2155
|
-
if (item.divider) {
|
|
2156
|
-
return m('li.divider', {
|
|
2157
|
-
key: `divider-${index}`,
|
|
2158
|
-
});
|
|
2695
|
+
onfocus: () => {
|
|
2696
|
+
state.activeThumb = 'min';
|
|
2697
|
+
},
|
|
2698
|
+
onkeydown: (e) => {
|
|
2699
|
+
if (disabled)
|
|
2700
|
+
return;
|
|
2701
|
+
const currentValue = state.rangeMinValue;
|
|
2702
|
+
const newValue = handleKeyboardNavigation(e.key, currentValue, min, max, step);
|
|
2703
|
+
if (newValue !== null) {
|
|
2704
|
+
e.preventDefault();
|
|
2705
|
+
const constrainedValue = Math.min(newValue, state.rangeMaxValue);
|
|
2706
|
+
updateRangeValues(constrainedValue, state.rangeMaxValue, attrs, state, false);
|
|
2159
2707
|
}
|
|
2160
|
-
const itemIndex = availableItems.indexOf(item);
|
|
2161
|
-
const isFocused = itemIndex === state.focusedIndex;
|
|
2162
|
-
return m('li', Object.assign({ key: item.id || `item-${index}`, class: [
|
|
2163
|
-
item.disabled ? 'disabled' : '',
|
|
2164
|
-
isFocused ? 'focused' : '',
|
|
2165
|
-
(selectedItem === null || selectedItem === void 0 ? void 0 : selectedItem.id) === item.id || (selectedItem === null || selectedItem === void 0 ? void 0 : selectedItem.label) === item.label ? 'selected' : '',
|
|
2166
|
-
]
|
|
2167
|
-
.filter(Boolean)
|
|
2168
|
-
.join(' ') }, (item.disabled
|
|
2169
|
-
? {}
|
|
2170
|
-
: {
|
|
2171
|
-
onclick: (e) => {
|
|
2172
|
-
e.stopPropagation();
|
|
2173
|
-
const value = (item.id || item.label);
|
|
2174
|
-
state.initialValue = value;
|
|
2175
|
-
state.isOpen = false;
|
|
2176
|
-
state.focusedIndex = -1;
|
|
2177
|
-
if (onchange)
|
|
2178
|
-
onchange(value);
|
|
2179
|
-
},
|
|
2180
|
-
})), m('span', {
|
|
2181
|
-
style: {
|
|
2182
|
-
display: 'flex',
|
|
2183
|
-
alignItems: 'center',
|
|
2184
|
-
padding: '14px 16px',
|
|
2185
|
-
},
|
|
2186
|
-
}, [
|
|
2187
|
-
item.iconName
|
|
2188
|
-
? m('i.material-icons', {
|
|
2189
|
-
style: { marginRight: '32px' },
|
|
2190
|
-
}, item.iconName)
|
|
2191
|
-
: undefined,
|
|
2192
|
-
item.label,
|
|
2193
|
-
]));
|
|
2194
|
-
})),
|
|
2195
|
-
m(MaterialIcon, {
|
|
2196
|
-
name: 'caret',
|
|
2197
|
-
direction: 'down',
|
|
2198
|
-
class: 'caret',
|
|
2199
|
-
}),
|
|
2200
|
-
]),
|
|
2201
|
-
]);
|
|
2202
|
-
},
|
|
2203
|
-
};
|
|
2204
|
-
};
|
|
2205
|
-
|
|
2206
|
-
/**
|
|
2207
|
-
* Floating Action Button
|
|
2208
|
-
*/
|
|
2209
|
-
const FloatingActionButton = () => {
|
|
2210
|
-
const state = {
|
|
2211
|
-
isOpen: false,
|
|
2212
|
-
};
|
|
2213
|
-
const handleClickOutside = (e) => {
|
|
2214
|
-
const target = e.target;
|
|
2215
|
-
if (!target.closest('.fixed-action-btn')) {
|
|
2216
|
-
state.isOpen = false;
|
|
2217
|
-
}
|
|
2218
|
-
};
|
|
2219
|
-
return {
|
|
2220
|
-
oncreate: () => {
|
|
2221
|
-
document.addEventListener('click', handleClickOutside);
|
|
2222
|
-
},
|
|
2223
|
-
onremove: () => {
|
|
2224
|
-
document.removeEventListener('click', handleClickOutside);
|
|
2225
|
-
},
|
|
2226
|
-
view: ({ attrs: { className, iconName, iconClass, position, style = position === 'left' || position === 'inline-left'
|
|
2227
|
-
? 'position: absolute; display: inline-block; left: 24px;'
|
|
2228
|
-
: position === 'right' || position === 'inline-right'
|
|
2229
|
-
? 'position: absolute; display: inline-block; right: 24px;'
|
|
2230
|
-
: undefined, buttons, direction = 'top', hoverEnabled = true, }, }) => {
|
|
2231
|
-
const fabClasses = [
|
|
2232
|
-
'fixed-action-btn',
|
|
2233
|
-
direction ? `direction-${direction}` : '',
|
|
2234
|
-
state.isOpen ? 'active' : '',
|
|
2235
|
-
// hoverEnabled ? 'hover-enabled' : '',
|
|
2236
|
-
]
|
|
2237
|
-
.filter(Boolean)
|
|
2238
|
-
.join(' ');
|
|
2239
|
-
return m('div', {
|
|
2240
|
-
style: position === 'inline-right' || position === 'inline-left' ? 'position: relative; height: 70px;' : undefined,
|
|
2241
|
-
}, m(`.${fabClasses}`, {
|
|
2242
|
-
style,
|
|
2243
|
-
onclick: (e) => {
|
|
2244
|
-
e.stopPropagation();
|
|
2245
|
-
if (buttons && buttons.length > 0) {
|
|
2246
|
-
state.isOpen = !state.isOpen;
|
|
2247
|
-
}
|
|
2248
|
-
},
|
|
2249
|
-
onmouseover: hoverEnabled
|
|
2250
|
-
? () => {
|
|
2251
|
-
if (buttons && buttons.length > 0) {
|
|
2252
|
-
state.isOpen = true;
|
|
2253
|
-
}
|
|
2254
|
-
}
|
|
2255
|
-
: undefined,
|
|
2256
|
-
onmouseleave: hoverEnabled
|
|
2257
|
-
? () => {
|
|
2258
|
-
state.isOpen = false;
|
|
2259
|
-
}
|
|
2260
|
-
: undefined,
|
|
2261
|
-
}, [
|
|
2262
|
-
m('a.btn-floating.btn-large', {
|
|
2263
|
-
className,
|
|
2264
|
-
}, m('i.material-icons', { className: iconClass }, iconName)),
|
|
2265
|
-
buttons &&
|
|
2266
|
-
buttons.length > 0 &&
|
|
2267
|
-
m('ul', buttons.map((button, index) => m('li', m(`a.btn-floating.${button.className || 'red'}`, {
|
|
2268
|
-
style: {
|
|
2269
|
-
opacity: state.isOpen ? '1' : '0',
|
|
2270
|
-
transform: state.isOpen ? 'scale(1)' : 'scale(0.4)',
|
|
2271
|
-
transition: `all 0.3s ease ${index * 40}ms`,
|
|
2272
2708
|
},
|
|
2709
|
+
}, m(DoubleRangeTooltip, {
|
|
2710
|
+
value: state.rangeMinValue,
|
|
2711
|
+
orientation,
|
|
2712
|
+
show: finalValueDisplay === 'always' ||
|
|
2713
|
+
(finalValueDisplay === 'auto' && state.isDragging && state.activeThumb === 'min'),
|
|
2714
|
+
})),
|
|
2715
|
+
// Max thumb
|
|
2716
|
+
m(`.thumb.${orientation}.max-thumb${state.activeThumb === 'max' ? '.active' : ''}`, {
|
|
2717
|
+
style: createThumbStyle(maxPercentage, state.activeThumb === 'max'),
|
|
2718
|
+
tabindex: disabled ? -1 : 0,
|
|
2719
|
+
role: 'slider',
|
|
2720
|
+
'aria-valuemin': state.rangeMinValue,
|
|
2721
|
+
'aria-valuemax': max,
|
|
2722
|
+
'aria-valuenow': state.rangeMaxValue,
|
|
2723
|
+
'aria-label': `Maximum value: ${state.rangeMaxValue}`,
|
|
2724
|
+
'aria-orientation': vertical ? 'vertical' : 'horizontal',
|
|
2725
|
+
onmousedown: handleMouseDown('max'),
|
|
2273
2726
|
onclick: (e) => {
|
|
2274
2727
|
e.stopPropagation();
|
|
2275
|
-
|
|
2276
|
-
|
|
2728
|
+
state.activeThumb = 'max';
|
|
2729
|
+
e.currentTarget.focus();
|
|
2277
2730
|
},
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2731
|
+
onfocus: () => {
|
|
2732
|
+
state.activeThumb = 'max';
|
|
2733
|
+
},
|
|
2734
|
+
onkeydown: (e) => {
|
|
2735
|
+
if (disabled)
|
|
2736
|
+
return;
|
|
2737
|
+
const currentValue = state.rangeMaxValue;
|
|
2738
|
+
const newValue = handleKeyboardNavigation(e.key, currentValue, min, max, step);
|
|
2739
|
+
if (newValue !== null) {
|
|
2740
|
+
e.preventDefault();
|
|
2741
|
+
const constrainedValue = Math.max(newValue, state.rangeMinValue);
|
|
2742
|
+
updateRangeValues(state.rangeMinValue, constrainedValue, attrs, state, false);
|
|
2743
|
+
}
|
|
2744
|
+
},
|
|
2745
|
+
}, m(DoubleRangeTooltip, {
|
|
2746
|
+
value: state.rangeMaxValue,
|
|
2747
|
+
orientation,
|
|
2748
|
+
show: finalValueDisplay === 'always' ||
|
|
2749
|
+
(finalValueDisplay === 'auto' && state.isDragging && state.activeThumb === 'max'),
|
|
2750
|
+
})),
|
|
2751
|
+
]),
|
|
2752
|
+
]),
|
|
2753
|
+
label ? m(Label, { label, id, isMandatory, isActive: true }) : null,
|
|
2754
|
+
helperText ? m(HelperText, { helperText }) : null,
|
|
2755
|
+
]);
|
|
2756
|
+
},
|
|
2282
2757
|
};
|
|
2283
2758
|
|
|
2284
2759
|
/** Character counter component that tracks text length against maxLength */
|
|
@@ -2418,6 +2893,12 @@
|
|
|
2418
2893
|
isValid: true,
|
|
2419
2894
|
active: false,
|
|
2420
2895
|
inputElement: null,
|
|
2896
|
+
// Range-specific state
|
|
2897
|
+
rangeMinValue: undefined,
|
|
2898
|
+
rangeMaxValue: undefined,
|
|
2899
|
+
singleValue: undefined,
|
|
2900
|
+
isDragging: false,
|
|
2901
|
+
activeThumb: null,
|
|
2421
2902
|
};
|
|
2422
2903
|
// let labelManager: { updateLabelState: () => void; cleanup: () => void } | null = null;
|
|
2423
2904
|
// let lengthUpdateHandler: (() => void) | null = null;
|
|
@@ -2443,18 +2924,39 @@
|
|
|
2443
2924
|
state.hasInteracted = length > 0;
|
|
2444
2925
|
}
|
|
2445
2926
|
};
|
|
2927
|
+
// Range slider helper functions
|
|
2928
|
+
// Range slider rendering functions are now in separate module
|
|
2446
2929
|
return {
|
|
2447
2930
|
view: ({ attrs }) => {
|
|
2448
2931
|
var _a;
|
|
2449
2932
|
const { className = 'col s12', dataError, dataSuccess, helperText, iconName, id = state.id, initialValue, placeholder, isMandatory, label, maxLength, newRow, oninput, onchange, onkeydown, onkeypress, onkeyup, style, validate } = attrs, params = __rest(attrs, ["className", "dataError", "dataSuccess", "helperText", "iconName", "id", "initialValue", "placeholder", "isMandatory", "label", "maxLength", "newRow", "oninput", "onchange", "onkeydown", "onkeypress", "onkeyup", "style", "validate"]);
|
|
2450
2933
|
// const attributes = toAttrs(params);
|
|
2451
|
-
const cn = [newRow ? 'clear' : '', defaultClass, className].filter(Boolean).join(' ').trim();
|
|
2934
|
+
const cn = [newRow ? 'clear' : '', defaultClass, className].filter(Boolean).join(' ').trim() || undefined;
|
|
2452
2935
|
const isActive = state.active || ((_a = state.inputElement) === null || _a === void 0 ? void 0 : _a.value) || placeholder || type === 'color' || type === 'range'
|
|
2453
2936
|
? true
|
|
2454
2937
|
: false;
|
|
2938
|
+
// Special rendering for minmax range sliders
|
|
2939
|
+
if (type === 'range' && (attrs.minmax || attrs.valueDisplay)) {
|
|
2940
|
+
return m(attrs.minmax ? DoubleRangeSlider : SingleRangeSlider, Object.assign(Object.assign({}, attrs), { state,
|
|
2941
|
+
cn,
|
|
2942
|
+
style,
|
|
2943
|
+
iconName,
|
|
2944
|
+
id,
|
|
2945
|
+
label,
|
|
2946
|
+
isMandatory,
|
|
2947
|
+
helperText }));
|
|
2948
|
+
}
|
|
2455
2949
|
return m('.input-field', { className: cn, style }, [
|
|
2456
2950
|
iconName ? m('i.material-icons.prefix', iconName) : undefined,
|
|
2457
|
-
m('input.validate', Object.assign(Object.assign({}, params), { type, tabindex: 0, id,
|
|
2951
|
+
m('input.validate', Object.assign(Object.assign({}, params), { type, tabindex: 0, id,
|
|
2952
|
+
placeholder, class: type === 'range' && attrs.vertical ? 'range-slider vertical' : undefined, style: type === 'range' && attrs.vertical
|
|
2953
|
+
? {
|
|
2954
|
+
height: attrs.height || '200px',
|
|
2955
|
+
width: '6px',
|
|
2956
|
+
writingMode: 'vertical-lr',
|
|
2957
|
+
direction: 'rtl',
|
|
2958
|
+
}
|
|
2959
|
+
: undefined,
|
|
2458
2960
|
// attributes,
|
|
2459
2961
|
oncreate: ({ dom }) => {
|
|
2460
2962
|
const input = (state.inputElement = dom);
|
|
@@ -2470,7 +2972,7 @@
|
|
|
2470
2972
|
state.currentLength = input.value.length; // Initial count
|
|
2471
2973
|
}
|
|
2472
2974
|
// Range input functionality
|
|
2473
|
-
if (type === 'range') {
|
|
2975
|
+
if (type === 'range' && !attrs.minmax) {
|
|
2474
2976
|
const updateThumb = () => {
|
|
2475
2977
|
const value = input.value;
|
|
2476
2978
|
const min = input.min || '0';
|
|
@@ -2667,83 +3169,1010 @@
|
|
|
2667
3169
|
};
|
|
2668
3170
|
};
|
|
2669
3171
|
|
|
2670
|
-
/**
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
3172
|
+
/** Component to show a check box */
|
|
3173
|
+
const InputCheckbox = () => {
|
|
3174
|
+
return {
|
|
3175
|
+
view: ({ attrs: { className = 'col s12', onchange, label, checked, disabled, description, style, inputId } }) => {
|
|
3176
|
+
const checkboxId = inputId || uniqueId();
|
|
3177
|
+
return m(`p`, { className, style }, m('label', { for: checkboxId }, [
|
|
3178
|
+
m('input[type=checkbox][tabindex=0]', {
|
|
3179
|
+
id: checkboxId,
|
|
3180
|
+
checked,
|
|
3181
|
+
disabled,
|
|
3182
|
+
onclick: onchange
|
|
3183
|
+
? (e) => {
|
|
3184
|
+
if (e.target && typeof e.target.checked !== 'undefined') {
|
|
3185
|
+
onchange(e.target.checked);
|
|
3186
|
+
}
|
|
3187
|
+
}
|
|
3188
|
+
: undefined,
|
|
3189
|
+
}),
|
|
3190
|
+
label ? (typeof label === 'string' ? m('span', label) : label) : undefined,
|
|
3191
|
+
]), description && m(HelperText, { className: 'input-checkbox-desc', helperText: description }));
|
|
3192
|
+
},
|
|
2680
3193
|
};
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
3194
|
+
};
|
|
3195
|
+
/** A list of checkboxes */
|
|
3196
|
+
const Options = () => {
|
|
3197
|
+
const state = {};
|
|
3198
|
+
const isChecked = (id) => state.checkedIds.indexOf(id) >= 0;
|
|
3199
|
+
const selectAll = (options, callback) => {
|
|
3200
|
+
const allIds = options.map((option) => option.id);
|
|
3201
|
+
state.checkedIds = [...allIds];
|
|
3202
|
+
if (callback)
|
|
3203
|
+
callback(allIds);
|
|
3204
|
+
};
|
|
3205
|
+
const selectNone = (callback) => {
|
|
3206
|
+
state.checkedIds = [];
|
|
3207
|
+
if (callback)
|
|
3208
|
+
callback([]);
|
|
3209
|
+
};
|
|
3210
|
+
return {
|
|
3211
|
+
oninit: ({ attrs: { initialValue, checkedId, id } }) => {
|
|
3212
|
+
const iv = checkedId || initialValue;
|
|
3213
|
+
state.checkedId = checkedId;
|
|
3214
|
+
state.checkedIds = iv ? (iv instanceof Array ? [...iv] : [iv]) : [];
|
|
3215
|
+
state.componentId = id || uniqueId();
|
|
3216
|
+
},
|
|
3217
|
+
view: ({ attrs: { label, options, description, className = 'col s12', style, disabled, checkboxClass, newRow, isMandatory, layout = 'vertical', showSelectAll = false, onchange: callback, }, }) => {
|
|
3218
|
+
const onchange = callback
|
|
3219
|
+
? (propId, checked) => {
|
|
3220
|
+
const checkedIds = state.checkedIds.filter((i) => i !== propId);
|
|
3221
|
+
if (checked) {
|
|
3222
|
+
checkedIds.push(propId);
|
|
3223
|
+
}
|
|
3224
|
+
state.checkedIds = checkedIds;
|
|
3225
|
+
callback(checkedIds);
|
|
3226
|
+
}
|
|
3227
|
+
: undefined;
|
|
3228
|
+
const cn = [newRow ? 'clear' : '', className].filter(Boolean).join(' ').trim() || undefined;
|
|
3229
|
+
const optionsContent = layout === 'horizontal'
|
|
3230
|
+
? m('div.grid-container', options.map((option) => m(InputCheckbox, {
|
|
3231
|
+
disabled: disabled || option.disabled,
|
|
3232
|
+
label: option.label,
|
|
3233
|
+
onchange: onchange ? (v) => onchange(option.id, v) : undefined,
|
|
3234
|
+
className: option.className || checkboxClass,
|
|
3235
|
+
checked: isChecked(option.id),
|
|
3236
|
+
description: option.description,
|
|
3237
|
+
inputId: `${state.componentId}-${option.id}`,
|
|
3238
|
+
})))
|
|
3239
|
+
: options.map((option) => m(InputCheckbox, {
|
|
3240
|
+
disabled: disabled || option.disabled,
|
|
3241
|
+
label: option.label,
|
|
3242
|
+
onchange: onchange ? (v) => onchange(option.id, v) : undefined,
|
|
3243
|
+
className: option.className || checkboxClass,
|
|
3244
|
+
checked: isChecked(option.id),
|
|
3245
|
+
description: option.description,
|
|
3246
|
+
inputId: `${state.componentId}-${option.id}`,
|
|
3247
|
+
}));
|
|
3248
|
+
return m('div', { id: state.componentId, className: cn, style }, [
|
|
3249
|
+
label && m('h5.form-group-label', label + (isMandatory ? ' *' : '')),
|
|
3250
|
+
showSelectAll &&
|
|
3251
|
+
m('div.select-all-controls', { style: 'margin-bottom: 10px;' }, [
|
|
3252
|
+
m('a', {
|
|
3253
|
+
href: '#',
|
|
3254
|
+
onclick: (e) => {
|
|
3255
|
+
e.preventDefault();
|
|
3256
|
+
selectAll(options, callback);
|
|
3257
|
+
},
|
|
3258
|
+
style: 'margin-right: 15px;',
|
|
3259
|
+
}, 'Select All'),
|
|
3260
|
+
m('a', {
|
|
3261
|
+
href: '#',
|
|
3262
|
+
onclick: (e) => {
|
|
3263
|
+
e.preventDefault();
|
|
3264
|
+
selectNone(callback);
|
|
3265
|
+
},
|
|
3266
|
+
}, 'Select None'),
|
|
3267
|
+
]),
|
|
3268
|
+
description && m(HelperText, { helperText: description }),
|
|
3269
|
+
m('form', { action: '#' }, optionsContent),
|
|
3270
|
+
]);
|
|
3271
|
+
},
|
|
3272
|
+
};
|
|
3273
|
+
};
|
|
3274
|
+
|
|
3275
|
+
const GlobalSearch = () => {
|
|
3276
|
+
return {
|
|
3277
|
+
view: ({ attrs }) => {
|
|
3278
|
+
const { searchPlaceholder, onSearch, i18n } = attrs;
|
|
3279
|
+
return m('.datatable-global-search', m(TextInput, {
|
|
3280
|
+
className: 'datatable-search',
|
|
3281
|
+
label: (i18n === null || i18n === void 0 ? void 0 : i18n.search) || 'Search',
|
|
3282
|
+
placeholder: searchPlaceholder || (i18n === null || i18n === void 0 ? void 0 : i18n.searchPlaceholder) || 'Search table...',
|
|
3283
|
+
oninput: onSearch,
|
|
3284
|
+
}));
|
|
3285
|
+
},
|
|
3286
|
+
};
|
|
3287
|
+
};
|
|
3288
|
+
const TableHeader = () => {
|
|
3289
|
+
return {
|
|
3290
|
+
view: ({ attrs }) => {
|
|
3291
|
+
const { columns, selection, sort, allSelected, helpers } = attrs;
|
|
3292
|
+
return m('thead', m('tr', [
|
|
3293
|
+
// Selection column header
|
|
3294
|
+
...(selection && selection.mode !== 'none'
|
|
3295
|
+
? [
|
|
3296
|
+
m('th.selection-checkbox', [
|
|
3297
|
+
selection.mode === 'multiple' &&
|
|
3298
|
+
m(InputCheckbox, {
|
|
3299
|
+
checked: allSelected,
|
|
3300
|
+
onchange: helpers.handleSelectAll,
|
|
3301
|
+
className: '',
|
|
3302
|
+
}),
|
|
3303
|
+
]),
|
|
3304
|
+
]
|
|
3305
|
+
: []),
|
|
3306
|
+
// Regular columns
|
|
3307
|
+
...columns.map((column) => {
|
|
3308
|
+
const isSorted = (sort === null || sort === void 0 ? void 0 : sort.column) === column.key;
|
|
3309
|
+
const sortDirection = isSorted ? sort.direction : null;
|
|
3310
|
+
return m('th', {
|
|
3311
|
+
class: [
|
|
3312
|
+
column.headerClassName,
|
|
3313
|
+
column.sortable ? 'sortable' : '',
|
|
3314
|
+
isSorted ? `sorted-${sortDirection}` : '',
|
|
3315
|
+
]
|
|
3316
|
+
.filter(Boolean)
|
|
3317
|
+
.join(' '),
|
|
3318
|
+
style: column.width ? { width: column.width } : undefined,
|
|
3319
|
+
onclick: column.sortable ? () => helpers.handleSort(column.key) : undefined,
|
|
3320
|
+
}, [
|
|
3321
|
+
column.title,
|
|
3322
|
+
column.sortable &&
|
|
3323
|
+
m('.sort-indicators', [
|
|
3324
|
+
m('span.sort-icon.sort-asc', {
|
|
3325
|
+
className: isSorted && sortDirection === 'asc' ? 'active' : '',
|
|
3326
|
+
}, '▲'),
|
|
3327
|
+
m('span.sort-icon.sort-desc', {
|
|
3328
|
+
className: isSorted && sortDirection === 'desc' ? 'active' : '',
|
|
3329
|
+
}, '▼'),
|
|
3330
|
+
]),
|
|
3331
|
+
]);
|
|
3332
|
+
}),
|
|
3333
|
+
]));
|
|
3334
|
+
},
|
|
3335
|
+
};
|
|
3336
|
+
};
|
|
3337
|
+
const TableRow = () => {
|
|
3338
|
+
return {
|
|
3339
|
+
view: ({ attrs }) => {
|
|
3340
|
+
const { row, index, columns, selection, onRowClick, onRowDoubleClick, getRowClassName, helpers, data } = attrs;
|
|
3341
|
+
// Calculate the original data index for the row key
|
|
3342
|
+
const originalIndex = data.findIndex((originalRow) => originalRow === row);
|
|
3343
|
+
const rowKey = (selection === null || selection === void 0 ? void 0 : selection.getRowKey(row, originalIndex)) || String(originalIndex);
|
|
3344
|
+
const isSelected = (selection === null || selection === void 0 ? void 0 : selection.selectedKeys.includes(rowKey)) || false;
|
|
3345
|
+
return m('tr', {
|
|
3346
|
+
class: [getRowClassName ? getRowClassName(row, index) : '', isSelected ? 'selected' : '']
|
|
3347
|
+
.filter(Boolean)
|
|
3348
|
+
.join(' ') || undefined,
|
|
3349
|
+
onclick: onRowClick ? (e) => onRowClick(row, index, e) : undefined,
|
|
3350
|
+
ondblclick: onRowDoubleClick ? (e) => onRowDoubleClick(row, index, e) : undefined,
|
|
3351
|
+
}, [
|
|
3352
|
+
// Selection column
|
|
3353
|
+
selection &&
|
|
3354
|
+
selection.mode !== 'none' &&
|
|
3355
|
+
m('td.selection-checkbox', [
|
|
3356
|
+
m(InputCheckbox, {
|
|
3357
|
+
checked: isSelected,
|
|
3358
|
+
onchange: (checked) => helpers.handleSelectionChange(rowKey, checked),
|
|
3359
|
+
className: '',
|
|
3360
|
+
}),
|
|
3361
|
+
]),
|
|
3362
|
+
columns.map((column) => {
|
|
3363
|
+
const value = helpers.getCellValue(row, column);
|
|
3364
|
+
let cellContent;
|
|
3365
|
+
if (column.cellRenderer) {
|
|
3366
|
+
cellContent = m(column.cellRenderer, {
|
|
3367
|
+
value,
|
|
3368
|
+
row,
|
|
3369
|
+
index,
|
|
3370
|
+
column,
|
|
3371
|
+
});
|
|
3372
|
+
}
|
|
3373
|
+
else if (column.render) {
|
|
3374
|
+
// Backward compatibility with deprecated render function
|
|
3375
|
+
cellContent = column.render(value, row, index);
|
|
3376
|
+
}
|
|
3377
|
+
else {
|
|
3378
|
+
cellContent = String(value || '');
|
|
3379
|
+
}
|
|
3380
|
+
return m('td', {
|
|
3381
|
+
class: [column.className, column.align ? `align-${column.align}` : ''].filter(Boolean).join(' ') ||
|
|
3382
|
+
undefined,
|
|
3383
|
+
}, cellContent);
|
|
3384
|
+
}),
|
|
3385
|
+
]);
|
|
3386
|
+
},
|
|
3387
|
+
};
|
|
3388
|
+
};
|
|
3389
|
+
/**
|
|
3390
|
+
* Standalone Pagination Controls component
|
|
3391
|
+
*
|
|
3392
|
+
* Provides navigation controls for paginated data with customizable text labels.
|
|
3393
|
+
* Includes first page, previous page, next page, last page buttons and page info display.
|
|
3394
|
+
* Can be used independently of DataTable for any paginated content.
|
|
3395
|
+
*
|
|
3396
|
+
* @example
|
|
3397
|
+
* ```typescript
|
|
3398
|
+
* m(PaginationControls, {
|
|
3399
|
+
* pagination: { page: 0, pageSize: 10, total: 100 },
|
|
3400
|
+
* onPaginationChange: (newPagination) => console.log('Page changed:', newPagination),
|
|
3401
|
+
* i18n: { showing: 'Showing', to: 'to', of: 'of', entries: 'entries', page: 'Page' }
|
|
3402
|
+
* })
|
|
3403
|
+
* ```
|
|
3404
|
+
*/
|
|
3405
|
+
const PaginationControls = () => {
|
|
3406
|
+
return {
|
|
3407
|
+
view: ({ attrs }) => {
|
|
3408
|
+
const { pagination, onPaginationChange, i18n } = attrs;
|
|
3409
|
+
if (!pagination)
|
|
3410
|
+
return null;
|
|
3411
|
+
const { page, pageSize, total } = pagination;
|
|
3412
|
+
const totalPages = Math.ceil(total / pageSize);
|
|
3413
|
+
const startItem = page * pageSize + 1;
|
|
3414
|
+
const endItem = Math.min((page + 1) * pageSize, total);
|
|
3415
|
+
const showingText = (i18n === null || i18n === void 0 ? void 0 : i18n.showing) || 'Showing';
|
|
3416
|
+
const toText = (i18n === null || i18n === void 0 ? void 0 : i18n.to) || 'to';
|
|
3417
|
+
const ofText = (i18n === null || i18n === void 0 ? void 0 : i18n.of) || 'of';
|
|
3418
|
+
const entriesText = (i18n === null || i18n === void 0 ? void 0 : i18n.entries) || 'entries';
|
|
3419
|
+
const pageText = (i18n === null || i18n === void 0 ? void 0 : i18n.page) || 'Page';
|
|
3420
|
+
return m('.datatable-pagination', [
|
|
3421
|
+
m('.pagination-info', `${showingText} ${startItem} ${toText} ${endItem} ${ofText} ${total} ${entriesText}`),
|
|
3422
|
+
m('.pagination-controls', [
|
|
3423
|
+
m('button.btn-flat', {
|
|
3424
|
+
disabled: page === 0,
|
|
3425
|
+
onclick: () => onPaginationChange(Object.assign(Object.assign({}, pagination), { page: 0 })),
|
|
3426
|
+
}, '⏮'),
|
|
3427
|
+
m('button.btn-flat', {
|
|
3428
|
+
disabled: page === 0,
|
|
3429
|
+
onclick: () => onPaginationChange(Object.assign(Object.assign({}, pagination), { page: page - 1 })),
|
|
3430
|
+
}, '◀'),
|
|
3431
|
+
m('span.page-info', `${pageText} ${page + 1} ${ofText} ${totalPages}`),
|
|
3432
|
+
m('button.btn-flat', {
|
|
3433
|
+
disabled: page >= totalPages - 1,
|
|
3434
|
+
onclick: () => onPaginationChange(Object.assign(Object.assign({}, pagination), { page: page + 1 })),
|
|
3435
|
+
}, '▶'),
|
|
3436
|
+
m('button.btn-flat', {
|
|
3437
|
+
disabled: page >= totalPages - 1,
|
|
3438
|
+
onclick: () => onPaginationChange(Object.assign(Object.assign({}, pagination), { page: totalPages - 1 })),
|
|
3439
|
+
}, '⏭'),
|
|
3440
|
+
]),
|
|
3441
|
+
]);
|
|
3442
|
+
},
|
|
3443
|
+
};
|
|
3444
|
+
};
|
|
3445
|
+
const TableContent = () => {
|
|
3446
|
+
return {
|
|
3447
|
+
view: ({ attrs: contentAttrs }) => {
|
|
3448
|
+
const { processedData, tableClasses, columns, selection, internalSort, allSelected, someSelected, helpers, onRowClick, onRowDoubleClick, getRowClassName, data, } = contentAttrs;
|
|
3449
|
+
return m('table', {
|
|
3450
|
+
class: tableClasses,
|
|
3451
|
+
}, m(TableHeader(), {
|
|
3452
|
+
columns,
|
|
3453
|
+
selection,
|
|
3454
|
+
sort: internalSort,
|
|
3455
|
+
allSelected,
|
|
3456
|
+
someSelected,
|
|
3457
|
+
helpers,
|
|
3458
|
+
}), m('tbody', processedData.map((row, index) => m(TableRow(), {
|
|
3459
|
+
key: (selection === null || selection === void 0 ? void 0 : selection.getRowKey(row, data.findIndex((originalRow) => originalRow === row))) || index,
|
|
3460
|
+
row,
|
|
3461
|
+
index,
|
|
3462
|
+
columns,
|
|
3463
|
+
selection,
|
|
3464
|
+
onRowClick,
|
|
3465
|
+
onRowDoubleClick,
|
|
3466
|
+
getRowClassName,
|
|
3467
|
+
helpers,
|
|
3468
|
+
data,
|
|
3469
|
+
}))));
|
|
3470
|
+
},
|
|
3471
|
+
};
|
|
3472
|
+
};
|
|
3473
|
+
/**
|
|
3474
|
+
* A comprehensive data table component with sorting, filtering, pagination, and selection capabilities.
|
|
3475
|
+
*
|
|
3476
|
+
* @template T The type of data objects displayed in each row
|
|
3477
|
+
*
|
|
3478
|
+
* @description
|
|
3479
|
+
* The DataTable component provides a feature-rich interface for displaying and interacting with tabular data.
|
|
3480
|
+
* It supports both controlled and uncontrolled modes for all interactive features.
|
|
3481
|
+
*
|
|
3482
|
+
* **Key Features:**
|
|
3483
|
+
* - Sorting: Click column headers to sort data ascending/descending
|
|
3484
|
+
* - Filtering: Global search across filterable columns
|
|
3485
|
+
* - Pagination: Navigate through large datasets with customizable page sizes
|
|
3486
|
+
* - Selection: Single or multiple row selection with callbacks
|
|
3487
|
+
* - Custom rendering: Use cellRenderer for complex cell content
|
|
3488
|
+
* - Responsive: Adapts to different screen sizes
|
|
3489
|
+
* - Internationalization: Customize all UI text
|
|
3490
|
+
* - Accessibility: Proper ARIA attributes and keyboard navigation
|
|
3491
|
+
*
|
|
3492
|
+
* @example Basic usage
|
|
3493
|
+
* ```typescript
|
|
3494
|
+
* interface User { id: number; name: string; email: string; }
|
|
3495
|
+
* const users: User[] = [...];
|
|
3496
|
+
* const columns: DataTableColumn<User>[] = [
|
|
3497
|
+
* { key: 'name', title: 'Name', field: 'name', sortable: true, filterable: true },
|
|
3498
|
+
* { key: 'email', title: 'Email', field: 'email', sortable: true, filterable: true }
|
|
3499
|
+
* ];
|
|
3500
|
+
*
|
|
3501
|
+
* return m(DataTable<User>, { data: users, columns });
|
|
3502
|
+
* ```
|
|
3503
|
+
*
|
|
3504
|
+
* @example Advanced usage with all features
|
|
3505
|
+
* ```typescript
|
|
3506
|
+
* return m(DataTable<User>, {
|
|
3507
|
+
* data: users,
|
|
3508
|
+
* columns,
|
|
3509
|
+
* title: 'User Management',
|
|
3510
|
+
* striped: true,
|
|
3511
|
+
* hoverable: true,
|
|
3512
|
+
* height: 400,
|
|
3513
|
+
*
|
|
3514
|
+
* // Pagination
|
|
3515
|
+
* pagination: { page: 0, pageSize: 10, total: users.length },
|
|
3516
|
+
* onPaginationChange: (pagination) => console.log('Page changed:', pagination),
|
|
3517
|
+
*
|
|
3518
|
+
* // Selection
|
|
3519
|
+
* selection: {
|
|
3520
|
+
* mode: 'multiple',
|
|
3521
|
+
* selectedKeys: [],
|
|
3522
|
+
* getRowKey: (user) => String(user.id),
|
|
3523
|
+
* onSelectionChange: (keys, selectedUsers) => console.log('Selection:', selectedUsers)
|
|
3524
|
+
* },
|
|
3525
|
+
*
|
|
3526
|
+
* // Search
|
|
3527
|
+
* enableGlobalSearch: true,
|
|
3528
|
+
* searchPlaceholder: 'Search users...',
|
|
3529
|
+
*
|
|
3530
|
+
* // Events
|
|
3531
|
+
* onRowClick: (user, index, event) => console.log('Clicked:', user),
|
|
3532
|
+
* onRowDoubleClick: (user) => editUser(user),
|
|
3533
|
+
*
|
|
3534
|
+
* // Styling
|
|
3535
|
+
* getRowClassName: (user) => user.active ? '' : 'inactive-row'
|
|
3536
|
+
* });
|
|
3537
|
+
* ```
|
|
3538
|
+
*
|
|
3539
|
+
* @returns A Mithril component that renders the data table
|
|
3540
|
+
*/
|
|
3541
|
+
const DataTable = () => {
|
|
3542
|
+
const state = {
|
|
3543
|
+
internalSort: undefined,
|
|
3544
|
+
internalFilter: undefined,
|
|
3545
|
+
internalPagination: undefined,
|
|
3546
|
+
processedData: [],
|
|
3547
|
+
tableId: '',
|
|
3548
|
+
// Performance optimization caches
|
|
3549
|
+
lastProcessedHash: ''};
|
|
3550
|
+
// Helper functions
|
|
3551
|
+
const quickDataHash = (data) => {
|
|
3552
|
+
if (data.length === 0)
|
|
3553
|
+
return '0';
|
|
3554
|
+
if (data.length === 1)
|
|
3555
|
+
return '1';
|
|
3556
|
+
// Sample first, middle, and last items for quick hash
|
|
3557
|
+
const first = JSON.stringify(data[0]);
|
|
3558
|
+
const middle = data.length > 2 ? JSON.stringify(data[Math.floor(data.length / 2)]) : '';
|
|
3559
|
+
const last = JSON.stringify(data[data.length - 1]);
|
|
3560
|
+
return `${data.length}-${first.length}-${middle.length}-${last.length}`;
|
|
3561
|
+
};
|
|
3562
|
+
const getDataHash = (attrs) => {
|
|
3563
|
+
const { data, sort, filter, pagination } = attrs;
|
|
3564
|
+
const { internalSort, internalFilter, internalPagination } = state;
|
|
3565
|
+
const hashInputs = {
|
|
3566
|
+
dataLength: data.length,
|
|
3567
|
+
dataHash: quickDataHash(data),
|
|
3568
|
+
sort: sort || internalSort,
|
|
3569
|
+
filter: filter || internalFilter,
|
|
3570
|
+
pagination: pagination || internalPagination,
|
|
3571
|
+
};
|
|
3572
|
+
return JSON.stringify(hashInputs);
|
|
3573
|
+
};
|
|
3574
|
+
const getCellValue = (row, column) => {
|
|
3575
|
+
if (column.field) {
|
|
3576
|
+
return row[column.field];
|
|
3577
|
+
}
|
|
3578
|
+
return row;
|
|
3579
|
+
};
|
|
3580
|
+
const applyFiltering = (data, filter, columns) => {
|
|
3581
|
+
var _a;
|
|
3582
|
+
if (!filter.searchTerm && !filter.columnFilters)
|
|
3583
|
+
return data;
|
|
3584
|
+
const filterableColumns = columns.filter((col) => col.filterable);
|
|
3585
|
+
if (filterableColumns.length === 0 && !filter.searchTerm)
|
|
3586
|
+
return data;
|
|
3587
|
+
const searchTerm = (_a = filter.searchTerm) === null || _a === void 0 ? void 0 : _a.toLowerCase();
|
|
3588
|
+
const hasColumnFilters = filter.columnFilters &&
|
|
3589
|
+
Object.keys(filter.columnFilters).some((key) => {
|
|
3590
|
+
const value = filter.columnFilters[key];
|
|
3591
|
+
return value !== null && value !== undefined && value !== '';
|
|
3592
|
+
});
|
|
3593
|
+
return data.filter((row) => {
|
|
3594
|
+
// Global search
|
|
3595
|
+
if (searchTerm) {
|
|
3596
|
+
const matchesGlobal = filterableColumns.some((column) => {
|
|
3597
|
+
const value = getCellValue(row, column);
|
|
3598
|
+
if (value == null)
|
|
3599
|
+
return false;
|
|
3600
|
+
return String(value).toLowerCase().includes(searchTerm);
|
|
3601
|
+
});
|
|
3602
|
+
if (!matchesGlobal)
|
|
3603
|
+
return false;
|
|
3604
|
+
}
|
|
3605
|
+
// Column-specific filters
|
|
3606
|
+
if (hasColumnFilters) {
|
|
3607
|
+
const matchesColumnFilters = Object.entries(filter.columnFilters).every(([columnKey, filterValue]) => {
|
|
3608
|
+
if (filterValue === null || filterValue === undefined || filterValue === '')
|
|
3609
|
+
return true;
|
|
3610
|
+
const column = columns.find((col) => col.key === columnKey);
|
|
3611
|
+
if (!column)
|
|
3612
|
+
return true;
|
|
3613
|
+
const value = getCellValue(row, column);
|
|
3614
|
+
if (value == null)
|
|
3615
|
+
return false;
|
|
3616
|
+
return String(value).toLowerCase().includes(String(filterValue).toLowerCase());
|
|
3617
|
+
});
|
|
3618
|
+
if (!matchesColumnFilters)
|
|
3619
|
+
return false;
|
|
3620
|
+
}
|
|
3621
|
+
return true;
|
|
3622
|
+
});
|
|
3623
|
+
};
|
|
3624
|
+
const applySorting = (data, sort, columns) => {
|
|
3625
|
+
const column = columns.find((col) => col.key === sort.column);
|
|
3626
|
+
if (!column || !column.sortable)
|
|
3627
|
+
return data;
|
|
3628
|
+
const multiplier = sort.direction === 'asc' ? 1 : -1;
|
|
3629
|
+
return [...data].sort((a, b) => {
|
|
3630
|
+
const aValue = getCellValue(a, column);
|
|
3631
|
+
const bValue = getCellValue(b, column);
|
|
3632
|
+
// Handle null/undefined values
|
|
3633
|
+
if (aValue == null && bValue == null)
|
|
3634
|
+
return 0;
|
|
3635
|
+
if (aValue == null)
|
|
3636
|
+
return -1 * multiplier;
|
|
3637
|
+
if (bValue == null)
|
|
3638
|
+
return 1 * multiplier;
|
|
3639
|
+
// Type-specific comparisons
|
|
3640
|
+
const aType = typeof aValue;
|
|
3641
|
+
const bType = typeof bValue;
|
|
3642
|
+
if (aType === bType) {
|
|
3643
|
+
if (aType === 'number') {
|
|
3644
|
+
return (aValue - bValue) * multiplier;
|
|
3645
|
+
}
|
|
3646
|
+
if (aType === 'boolean') {
|
|
3647
|
+
return (aValue === bValue ? 0 : aValue ? 1 : -1) * multiplier;
|
|
3648
|
+
}
|
|
3649
|
+
if (aValue instanceof Date && bValue instanceof Date) {
|
|
3650
|
+
return (aValue.getTime() - bValue.getTime()) * multiplier;
|
|
3651
|
+
}
|
|
3652
|
+
}
|
|
3653
|
+
// Fallback to string comparison
|
|
3654
|
+
return String(aValue).localeCompare(String(bValue)) * multiplier;
|
|
3655
|
+
});
|
|
3656
|
+
};
|
|
3657
|
+
const processData = (attrs) => {
|
|
3658
|
+
const { data } = attrs;
|
|
3659
|
+
const { internalSort, internalFilter, internalPagination } = state;
|
|
3660
|
+
let processedData = [...data];
|
|
3661
|
+
// Apply filtering
|
|
3662
|
+
if (internalFilter) {
|
|
3663
|
+
processedData = applyFiltering(processedData, internalFilter, attrs.columns);
|
|
3664
|
+
}
|
|
3665
|
+
// Apply sorting
|
|
3666
|
+
if (internalSort) {
|
|
3667
|
+
processedData = applySorting(processedData, internalSort, attrs.columns);
|
|
3668
|
+
}
|
|
3669
|
+
// Update total count for pagination
|
|
3670
|
+
if (internalPagination) {
|
|
3671
|
+
state.internalPagination = Object.assign(Object.assign({}, internalPagination), { total: processedData.length });
|
|
3672
|
+
}
|
|
3673
|
+
// Apply pagination
|
|
3674
|
+
if (internalPagination) {
|
|
3675
|
+
const { page, pageSize } = internalPagination;
|
|
3676
|
+
const start = page * pageSize;
|
|
3677
|
+
const end = start + pageSize;
|
|
3678
|
+
processedData = processedData.slice(start, end);
|
|
3679
|
+
}
|
|
3680
|
+
state.processedData = processedData;
|
|
3681
|
+
};
|
|
3682
|
+
// Create stable helper functions that don't get recreated on every render
|
|
3683
|
+
const createHelpers = (attrs) => ({
|
|
3684
|
+
getCellValue,
|
|
3685
|
+
handleSort: (columnKey) => {
|
|
3686
|
+
var _a;
|
|
3687
|
+
const column = attrs.columns.find((col) => col.key === columnKey);
|
|
3688
|
+
if (!column || !column.sortable)
|
|
3689
|
+
return;
|
|
3690
|
+
const currentSort = state.internalSort;
|
|
3691
|
+
let newSort;
|
|
3692
|
+
if ((currentSort === null || currentSort === void 0 ? void 0 : currentSort.column) === columnKey) {
|
|
3693
|
+
// Toggle direction
|
|
3694
|
+
if (currentSort.direction === 'asc') {
|
|
3695
|
+
newSort = { column: columnKey, direction: 'desc' };
|
|
3696
|
+
}
|
|
3697
|
+
else {
|
|
3698
|
+
newSort = { column: columnKey, direction: 'asc' };
|
|
3699
|
+
}
|
|
3700
|
+
}
|
|
3701
|
+
else {
|
|
3702
|
+
// New column sort
|
|
3703
|
+
newSort = { column: columnKey, direction: 'asc' };
|
|
3704
|
+
}
|
|
3705
|
+
state.internalSort = newSort;
|
|
3706
|
+
(_a = attrs.onSortChange) === null || _a === void 0 ? void 0 : _a.call(attrs, newSort);
|
|
3707
|
+
},
|
|
3708
|
+
handleGlobalSearch: (searchTerm) => {
|
|
3709
|
+
var _a;
|
|
3710
|
+
const newFilter = Object.assign(Object.assign({}, state.internalFilter), { searchTerm });
|
|
3711
|
+
state.internalFilter = newFilter;
|
|
3712
|
+
// Reset pagination to first page when filtering
|
|
3713
|
+
if (state.internalPagination) {
|
|
3714
|
+
state.internalPagination = Object.assign(Object.assign({}, state.internalPagination), { page: 0 });
|
|
3715
|
+
}
|
|
3716
|
+
(_a = attrs.onFilterChange) === null || _a === void 0 ? void 0 : _a.call(attrs, newFilter);
|
|
3717
|
+
},
|
|
3718
|
+
handleSelectionChange: (rowKey, selected) => {
|
|
3719
|
+
var _a, _b;
|
|
3720
|
+
if (!attrs.selection)
|
|
3721
|
+
return;
|
|
3722
|
+
let newSelectedKeys;
|
|
3723
|
+
if (attrs.selection.mode === 'single') {
|
|
3724
|
+
newSelectedKeys = selected ? [rowKey] : [];
|
|
3725
|
+
}
|
|
3726
|
+
else if (attrs.selection.mode === 'multiple') {
|
|
3727
|
+
if (selected) {
|
|
3728
|
+
newSelectedKeys = [...attrs.selection.selectedKeys, rowKey];
|
|
3729
|
+
}
|
|
3730
|
+
else {
|
|
3731
|
+
newSelectedKeys = attrs.selection.selectedKeys.filter((key) => key !== rowKey);
|
|
3732
|
+
}
|
|
3733
|
+
}
|
|
3734
|
+
else {
|
|
3735
|
+
return; // No selection mode
|
|
3736
|
+
}
|
|
3737
|
+
// Get selected rows
|
|
3738
|
+
const selectedRows = attrs.data.filter((row, index) => {
|
|
3739
|
+
const key = attrs.selection.getRowKey(row, index);
|
|
3740
|
+
return newSelectedKeys.includes(key);
|
|
3741
|
+
});
|
|
3742
|
+
(_b = (_a = attrs.selection).onSelectionChange) === null || _b === void 0 ? void 0 : _b.call(_a, newSelectedKeys, selectedRows);
|
|
3743
|
+
},
|
|
3744
|
+
handleSelectAll: (selected) => {
|
|
3745
|
+
var _a, _b;
|
|
3746
|
+
if (!attrs.selection || attrs.selection.mode !== 'multiple')
|
|
3747
|
+
return;
|
|
3748
|
+
let newSelectedKeys;
|
|
3749
|
+
if (selected) {
|
|
3750
|
+
// Select all visible rows
|
|
3751
|
+
newSelectedKeys = state.processedData.map((row) => {
|
|
3752
|
+
const originalIndex = attrs.data.findIndex((originalRow) => originalRow === row);
|
|
3753
|
+
return attrs.selection.getRowKey(row, originalIndex);
|
|
3754
|
+
});
|
|
3755
|
+
}
|
|
3756
|
+
else {
|
|
3757
|
+
newSelectedKeys = [];
|
|
3758
|
+
}
|
|
3759
|
+
const selectedRows = attrs.data.filter((row, index) => {
|
|
3760
|
+
const key = attrs.selection.getRowKey(row, index);
|
|
3761
|
+
return newSelectedKeys.includes(key);
|
|
3762
|
+
});
|
|
3763
|
+
(_b = (_a = attrs.selection).onSelectionChange) === null || _b === void 0 ? void 0 : _b.call(_a, newSelectedKeys, selectedRows);
|
|
3764
|
+
},
|
|
3765
|
+
});
|
|
3766
|
+
return {
|
|
3767
|
+
oninit(vnodeInit) {
|
|
3768
|
+
const { sort, filter, pagination } = vnodeInit.attrs;
|
|
3769
|
+
state.tableId = uniqueId();
|
|
3770
|
+
state.internalSort = sort || undefined;
|
|
3771
|
+
state.internalFilter = filter || { searchTerm: '', columnFilters: {} };
|
|
3772
|
+
state.internalPagination = pagination || undefined;
|
|
3773
|
+
processData(vnodeInit.attrs);
|
|
3774
|
+
},
|
|
3775
|
+
onbeforeupdate(vnodeUpdate) {
|
|
3776
|
+
// Only reprocess data if inputs have changed
|
|
3777
|
+
const currentHash = getDataHash(vnodeUpdate.attrs);
|
|
3778
|
+
if (currentHash !== state.lastProcessedHash) {
|
|
3779
|
+
processData(vnodeUpdate.attrs);
|
|
3780
|
+
state.lastProcessedHash = currentHash;
|
|
3781
|
+
}
|
|
3782
|
+
},
|
|
3783
|
+
view(vnodeView) {
|
|
3784
|
+
const attrs = vnodeView.attrs;
|
|
3785
|
+
const { loading, emptyMessage, striped, hoverable, responsive, centered, className, id, title, height, enableGlobalSearch, searchPlaceholder, selection, columns, onRowClick, onRowDoubleClick, getRowClassName, data, onPaginationChange, i18n, } = attrs;
|
|
3786
|
+
const { processedData, tableId, internalSort, internalPagination } = state;
|
|
3787
|
+
if (loading) {
|
|
3788
|
+
return m('.datatable-loading', [
|
|
3789
|
+
m('.preloader-wrapper.small.active', m('.spinner-layer.spinner-blue-only', m('.circle-clipper.left', m('.circle')))),
|
|
3790
|
+
m('p', (i18n === null || i18n === void 0 ? void 0 : i18n.loading) || 'Loading...'),
|
|
3791
|
+
]);
|
|
3792
|
+
}
|
|
3793
|
+
// Create stable helpers object using the factory function
|
|
3794
|
+
const helpers = createHelpers(attrs);
|
|
3795
|
+
// Calculate selection state for "select all" checkbox
|
|
3796
|
+
let allSelected = false;
|
|
3797
|
+
let someSelected = false;
|
|
3798
|
+
if (selection && selection.mode === 'multiple') {
|
|
3799
|
+
const visibleRowKeys = processedData.map((row) => {
|
|
3800
|
+
const originalIndex = data.findIndex((originalRow) => originalRow === row);
|
|
3801
|
+
return selection.getRowKey(row, originalIndex);
|
|
3802
|
+
});
|
|
3803
|
+
const selectedVisibleKeys = visibleRowKeys.filter((key) => selection.selectedKeys.includes(key));
|
|
3804
|
+
allSelected = visibleRowKeys.length > 0 && selectedVisibleKeys.length === visibleRowKeys.length;
|
|
3805
|
+
someSelected = selectedVisibleKeys.length > 0 && selectedVisibleKeys.length < visibleRowKeys.length;
|
|
3806
|
+
}
|
|
3807
|
+
const tableClasses = [
|
|
3808
|
+
'datatable',
|
|
3809
|
+
striped ? 'striped' : '',
|
|
3810
|
+
hoverable ? 'highlight' : '',
|
|
3811
|
+
responsive ? 'responsive-table' : '',
|
|
3812
|
+
centered ? 'centered' : '',
|
|
3813
|
+
className || '',
|
|
3814
|
+
]
|
|
3815
|
+
.filter(Boolean)
|
|
3816
|
+
.join(' ');
|
|
3817
|
+
return m('.datatable-container', {
|
|
3818
|
+
id: id || tableId,
|
|
3819
|
+
}, title && m('h5.datatable-title', title), enableGlobalSearch &&
|
|
3820
|
+
m(GlobalSearch, {
|
|
3821
|
+
searchPlaceholder,
|
|
3822
|
+
onSearch: helpers.handleGlobalSearch,
|
|
3823
|
+
i18n,
|
|
3824
|
+
}), m('.datatable-wrapper', {
|
|
3825
|
+
style: {
|
|
3826
|
+
maxHeight: height ? `${height}px` : undefined,
|
|
3827
|
+
overflowY: height ? 'auto' : undefined,
|
|
3828
|
+
},
|
|
3829
|
+
}, processedData.length === 0
|
|
3830
|
+
? m('.datatable-empty', emptyMessage || (i18n === null || i18n === void 0 ? void 0 : i18n.noDataAvailable) || 'No data available')
|
|
3831
|
+
: m(TableContent(), {
|
|
3832
|
+
processedData,
|
|
3833
|
+
height,
|
|
3834
|
+
tableClasses,
|
|
3835
|
+
columns,
|
|
3836
|
+
selection,
|
|
3837
|
+
internalSort,
|
|
3838
|
+
allSelected,
|
|
3839
|
+
someSelected,
|
|
3840
|
+
helpers,
|
|
3841
|
+
onRowClick,
|
|
3842
|
+
onRowDoubleClick,
|
|
3843
|
+
getRowClassName,
|
|
3844
|
+
data,
|
|
3845
|
+
})), m(PaginationControls, {
|
|
3846
|
+
pagination: internalPagination,
|
|
3847
|
+
onPaginationChange: (pagination) => {
|
|
3848
|
+
state.internalPagination = pagination;
|
|
3849
|
+
onPaginationChange === null || onPaginationChange === void 0 ? void 0 : onPaginationChange(pagination);
|
|
3850
|
+
},
|
|
3851
|
+
i18n,
|
|
3852
|
+
}));
|
|
3853
|
+
},
|
|
3854
|
+
};
|
|
3855
|
+
};
|
|
3856
|
+
|
|
3857
|
+
/** Pure TypeScript Dropdown component - no Materialize dependencies */
|
|
3858
|
+
const Dropdown = () => {
|
|
3859
|
+
const state = {
|
|
3860
|
+
isOpen: false,
|
|
3861
|
+
initialValue: undefined,
|
|
3862
|
+
id: '',
|
|
3863
|
+
focusedIndex: -1,
|
|
3864
|
+
inputRef: null,
|
|
3865
|
+
dropdownRef: null,
|
|
3866
|
+
};
|
|
3867
|
+
const handleKeyDown = (e, items, onchange) => {
|
|
3868
|
+
const availableItems = items.filter((item) => !item.divider && !item.disabled);
|
|
3869
|
+
switch (e.key) {
|
|
3870
|
+
case 'ArrowDown':
|
|
3871
|
+
e.preventDefault();
|
|
3872
|
+
if (!state.isOpen) {
|
|
3873
|
+
state.isOpen = true;
|
|
3874
|
+
state.focusedIndex = 0;
|
|
3875
|
+
}
|
|
3876
|
+
else {
|
|
3877
|
+
state.focusedIndex = Math.min(state.focusedIndex + 1, availableItems.length - 1);
|
|
3878
|
+
}
|
|
3879
|
+
break;
|
|
3880
|
+
case 'ArrowUp':
|
|
3881
|
+
e.preventDefault();
|
|
3882
|
+
if (state.isOpen) {
|
|
3883
|
+
state.focusedIndex = Math.max(state.focusedIndex - 1, 0);
|
|
3884
|
+
}
|
|
3885
|
+
break;
|
|
3886
|
+
case 'Enter':
|
|
3887
|
+
case ' ':
|
|
3888
|
+
e.preventDefault();
|
|
3889
|
+
if (state.isOpen && state.focusedIndex >= 0 && state.focusedIndex < availableItems.length) {
|
|
3890
|
+
const selectedItem = availableItems[state.focusedIndex];
|
|
3891
|
+
const value = (selectedItem.id || selectedItem.label);
|
|
3892
|
+
state.initialValue = value;
|
|
3893
|
+
state.isOpen = false;
|
|
3894
|
+
state.focusedIndex = -1;
|
|
3895
|
+
if (onchange)
|
|
3896
|
+
onchange(value);
|
|
3897
|
+
}
|
|
3898
|
+
else if (!state.isOpen) {
|
|
3899
|
+
state.isOpen = true;
|
|
3900
|
+
state.focusedIndex = 0;
|
|
3901
|
+
}
|
|
3902
|
+
break;
|
|
3903
|
+
case 'Escape':
|
|
3904
|
+
e.preventDefault();
|
|
3905
|
+
state.isOpen = false;
|
|
3906
|
+
state.focusedIndex = -1;
|
|
3907
|
+
break;
|
|
3908
|
+
}
|
|
3909
|
+
};
|
|
3910
|
+
return {
|
|
3911
|
+
oninit: ({ attrs: { id = uniqueId(), initialValue, checkedId } }) => {
|
|
3912
|
+
state.id = id;
|
|
3913
|
+
state.initialValue = initialValue || checkedId;
|
|
3914
|
+
// Mithril will handle click events through the component structure
|
|
3915
|
+
},
|
|
3916
|
+
view: ({ attrs: { key, label, onchange, disabled = false, items, iconName, helperText, style, className = 'col s12' }, }) => {
|
|
3917
|
+
const { initialValue } = state;
|
|
3918
|
+
const selectedItem = initialValue
|
|
3919
|
+
? items.filter((i) => (i.id ? i.id === initialValue : i.label === initialValue)).shift()
|
|
3920
|
+
: undefined;
|
|
3921
|
+
const title = selectedItem ? selectedItem.label : label || 'Select';
|
|
3922
|
+
const availableItems = items.filter((item) => !item.divider && !item.disabled);
|
|
3923
|
+
return m('.dropdown-wrapper.input-field', { className, key, style }, [
|
|
3924
|
+
iconName ? m('i.material-icons.prefix', iconName) : undefined,
|
|
3925
|
+
m(HelperText, { helperText }),
|
|
3926
|
+
m('.select-wrapper', {
|
|
3927
|
+
onclick: disabled
|
|
3928
|
+
? undefined
|
|
3929
|
+
: () => {
|
|
3930
|
+
state.isOpen = !state.isOpen;
|
|
3931
|
+
state.focusedIndex = state.isOpen ? 0 : -1;
|
|
3932
|
+
},
|
|
3933
|
+
onkeydown: disabled ? undefined : (e) => handleKeyDown(e, items, onchange),
|
|
3934
|
+
tabindex: disabled ? -1 : 0,
|
|
3935
|
+
'aria-expanded': state.isOpen ? 'true' : 'false',
|
|
3936
|
+
'aria-haspopup': 'listbox',
|
|
3937
|
+
role: 'combobox',
|
|
3938
|
+
}, [
|
|
3939
|
+
m('input[type=text][readonly=true].select-dropdown.dropdown-trigger', {
|
|
3940
|
+
id: state.id,
|
|
3941
|
+
value: title,
|
|
3942
|
+
oncreate: ({ dom }) => {
|
|
3943
|
+
state.inputRef = dom;
|
|
3944
|
+
},
|
|
3945
|
+
onclick: (e) => {
|
|
3946
|
+
e.preventDefault();
|
|
3947
|
+
e.stopPropagation();
|
|
3948
|
+
if (!disabled) {
|
|
3949
|
+
state.isOpen = !state.isOpen;
|
|
3950
|
+
state.focusedIndex = state.isOpen ? 0 : -1;
|
|
3951
|
+
}
|
|
3952
|
+
},
|
|
3953
|
+
}),
|
|
3954
|
+
// Dropdown Menu using Select component's positioning logic
|
|
3955
|
+
state.isOpen &&
|
|
3956
|
+
m('ul.dropdown-content.select-dropdown', {
|
|
3957
|
+
tabindex: 0,
|
|
3958
|
+
role: 'listbox',
|
|
3959
|
+
'aria-labelledby': state.id,
|
|
3960
|
+
oncreate: ({ dom }) => {
|
|
3961
|
+
state.dropdownRef = dom;
|
|
3962
|
+
},
|
|
3963
|
+
onremove: () => {
|
|
3964
|
+
state.dropdownRef = null;
|
|
3965
|
+
},
|
|
3966
|
+
style: getDropdownStyles(state.inputRef, true, items.map((item) => (Object.assign(Object.assign({}, item), {
|
|
3967
|
+
// Convert dropdown items to format expected by getDropdownStyles
|
|
3968
|
+
group: undefined }))), true),
|
|
3969
|
+
}, items.map((item, index) => {
|
|
3970
|
+
if (item.divider) {
|
|
3971
|
+
return m('li.divider', {
|
|
3972
|
+
key: `divider-${index}`,
|
|
3973
|
+
});
|
|
3974
|
+
}
|
|
3975
|
+
const itemIndex = availableItems.indexOf(item);
|
|
3976
|
+
const isFocused = itemIndex === state.focusedIndex;
|
|
3977
|
+
return m('li', Object.assign({ key: item.id || `item-${index}`, class: [
|
|
3978
|
+
item.disabled ? 'disabled' : '',
|
|
3979
|
+
isFocused ? 'focused' : '',
|
|
3980
|
+
(selectedItem === null || selectedItem === void 0 ? void 0 : selectedItem.id) === item.id || (selectedItem === null || selectedItem === void 0 ? void 0 : selectedItem.label) === item.label ? 'selected' : '',
|
|
3981
|
+
]
|
|
3982
|
+
.filter(Boolean)
|
|
3983
|
+
.join(' ') }, (item.disabled
|
|
3984
|
+
? {}
|
|
3985
|
+
: {
|
|
3986
|
+
onclick: (e) => {
|
|
3987
|
+
e.stopPropagation();
|
|
3988
|
+
const value = (item.id || item.label);
|
|
3989
|
+
state.initialValue = value;
|
|
3990
|
+
state.isOpen = false;
|
|
3991
|
+
state.focusedIndex = -1;
|
|
3992
|
+
if (onchange)
|
|
3993
|
+
onchange(value);
|
|
3994
|
+
},
|
|
3995
|
+
})), m('span', {
|
|
3996
|
+
style: {
|
|
3997
|
+
display: 'flex',
|
|
3998
|
+
alignItems: 'center',
|
|
3999
|
+
padding: '14px 16px',
|
|
4000
|
+
},
|
|
4001
|
+
}, [
|
|
4002
|
+
item.iconName
|
|
4003
|
+
? m('i.material-icons', {
|
|
4004
|
+
style: { marginRight: '32px' },
|
|
4005
|
+
}, item.iconName)
|
|
4006
|
+
: undefined,
|
|
4007
|
+
item.label,
|
|
4008
|
+
]));
|
|
4009
|
+
})),
|
|
4010
|
+
m(MaterialIcon, {
|
|
4011
|
+
name: 'caret',
|
|
4012
|
+
direction: 'down',
|
|
4013
|
+
class: 'caret',
|
|
4014
|
+
}),
|
|
4015
|
+
]),
|
|
4016
|
+
]);
|
|
4017
|
+
},
|
|
4018
|
+
};
|
|
4019
|
+
};
|
|
4020
|
+
|
|
4021
|
+
/**
|
|
4022
|
+
* Floating Action Button
|
|
4023
|
+
*/
|
|
4024
|
+
const FloatingActionButton = () => {
|
|
4025
|
+
const state = {
|
|
4026
|
+
isOpen: false,
|
|
4027
|
+
};
|
|
4028
|
+
const handleClickOutside = (e) => {
|
|
4029
|
+
const target = e.target;
|
|
4030
|
+
if (!target.closest('.fixed-action-btn')) {
|
|
4031
|
+
state.isOpen = false;
|
|
4032
|
+
}
|
|
4033
|
+
};
|
|
4034
|
+
return {
|
|
4035
|
+
oncreate: () => {
|
|
4036
|
+
document.addEventListener('click', handleClickOutside);
|
|
4037
|
+
},
|
|
4038
|
+
onremove: () => {
|
|
4039
|
+
document.removeEventListener('click', handleClickOutside);
|
|
4040
|
+
},
|
|
4041
|
+
view: ({ attrs: { className, iconName, iconClass, position, style = position === 'left' || position === 'inline-left'
|
|
4042
|
+
? 'position: absolute; display: inline-block; left: 24px;'
|
|
4043
|
+
: position === 'right' || position === 'inline-right'
|
|
4044
|
+
? 'position: absolute; display: inline-block; right: 24px;'
|
|
4045
|
+
: undefined, buttons, direction = 'top', hoverEnabled = true, }, }) => {
|
|
4046
|
+
const fabClasses = [
|
|
4047
|
+
'fixed-action-btn',
|
|
4048
|
+
direction ? `direction-${direction}` : '',
|
|
4049
|
+
state.isOpen ? 'active' : '',
|
|
4050
|
+
// hoverEnabled ? 'hover-enabled' : '',
|
|
4051
|
+
]
|
|
4052
|
+
.filter(Boolean)
|
|
4053
|
+
.join(' ');
|
|
4054
|
+
return m('div', {
|
|
4055
|
+
style: position === 'inline-right' || position === 'inline-left' ? 'position: relative; height: 70px;' : undefined,
|
|
4056
|
+
}, m(`.${fabClasses}`, {
|
|
4057
|
+
style,
|
|
4058
|
+
onclick: (e) => {
|
|
4059
|
+
e.stopPropagation();
|
|
4060
|
+
if (buttons && buttons.length > 0) {
|
|
4061
|
+
state.isOpen = !state.isOpen;
|
|
4062
|
+
}
|
|
4063
|
+
},
|
|
4064
|
+
onmouseover: hoverEnabled
|
|
4065
|
+
? () => {
|
|
4066
|
+
if (buttons && buttons.length > 0) {
|
|
4067
|
+
state.isOpen = true;
|
|
4068
|
+
}
|
|
4069
|
+
}
|
|
4070
|
+
: undefined,
|
|
4071
|
+
onmouseleave: hoverEnabled
|
|
4072
|
+
? () => {
|
|
4073
|
+
state.isOpen = false;
|
|
4074
|
+
}
|
|
4075
|
+
: undefined,
|
|
4076
|
+
}, [
|
|
4077
|
+
m('a.btn-floating.btn-large', {
|
|
4078
|
+
className,
|
|
4079
|
+
}, m('i.material-icons', { className: iconClass }, iconName)),
|
|
4080
|
+
buttons &&
|
|
4081
|
+
buttons.length > 0 &&
|
|
4082
|
+
m('ul', buttons.map((button, index) => m('li', m(`a.btn-floating.${button.className || 'red'}`, {
|
|
4083
|
+
style: {
|
|
4084
|
+
opacity: state.isOpen ? '1' : '0',
|
|
4085
|
+
transform: state.isOpen ? 'scale(1)' : 'scale(0.4)',
|
|
4086
|
+
transition: `all 0.3s ease ${index * 40}ms`,
|
|
4087
|
+
},
|
|
4088
|
+
onclick: (e) => {
|
|
4089
|
+
e.stopPropagation();
|
|
4090
|
+
if (button.onclick)
|
|
4091
|
+
button.onclick(e);
|
|
4092
|
+
},
|
|
4093
|
+
}, m('i.material-icons', { className: button.iconClass }, button.iconName))))),
|
|
4094
|
+
]));
|
|
4095
|
+
},
|
|
4096
|
+
};
|
|
4097
|
+
};
|
|
4098
|
+
|
|
4099
|
+
/**
|
|
4100
|
+
* Pure TypeScript MaterialBox - creates an image lightbox that fills the screen when clicked
|
|
4101
|
+
* No MaterializeCSS dependencies
|
|
4102
|
+
*/
|
|
4103
|
+
const MaterialBox = () => {
|
|
4104
|
+
const state = {
|
|
4105
|
+
isOpen: false,
|
|
4106
|
+
originalImage: null,
|
|
4107
|
+
overlay: null,
|
|
4108
|
+
overlayImage: null,
|
|
4109
|
+
};
|
|
4110
|
+
const openBox = (img, attrs) => {
|
|
4111
|
+
if (state.isOpen)
|
|
4112
|
+
return;
|
|
4113
|
+
state.isOpen = true;
|
|
4114
|
+
state.originalImage = img;
|
|
4115
|
+
if (attrs.onOpenStart)
|
|
4116
|
+
attrs.onOpenStart();
|
|
4117
|
+
// Create overlay
|
|
4118
|
+
const overlay = document.createElement('div');
|
|
4119
|
+
overlay.className = 'materialbox-overlay';
|
|
4120
|
+
overlay.style.cssText = `
|
|
4121
|
+
position: fixed;
|
|
4122
|
+
top: 0;
|
|
4123
|
+
left: 0;
|
|
4124
|
+
right: 0;
|
|
4125
|
+
bottom: 0;
|
|
4126
|
+
background-color: rgba(0, 0, 0, 0.85);
|
|
4127
|
+
z-index: 1000;
|
|
4128
|
+
opacity: 0;
|
|
4129
|
+
transition: opacity ${attrs.inDuration || 275}ms ease;
|
|
4130
|
+
cursor: zoom-out;
|
|
4131
|
+
`;
|
|
4132
|
+
// Create enlarged image
|
|
4133
|
+
const enlargedImg = document.createElement('img');
|
|
4134
|
+
enlargedImg.src = img.src;
|
|
4135
|
+
enlargedImg.alt = img.alt || '';
|
|
4136
|
+
enlargedImg.className = 'materialbox-image';
|
|
4137
|
+
// Get original image dimensions and position
|
|
4138
|
+
const imgRect = img.getBoundingClientRect();
|
|
4139
|
+
const windowWidth = window.innerWidth;
|
|
4140
|
+
const windowHeight = window.innerHeight;
|
|
4141
|
+
// Calculate final size maintaining aspect ratio
|
|
4142
|
+
const aspectRatio = img.naturalWidth / img.naturalHeight;
|
|
4143
|
+
const maxWidth = windowWidth * 0.9;
|
|
4144
|
+
const maxHeight = windowHeight * 0.9;
|
|
4145
|
+
let finalWidth = maxWidth;
|
|
4146
|
+
let finalHeight = maxWidth / aspectRatio;
|
|
4147
|
+
if (finalHeight > maxHeight) {
|
|
4148
|
+
finalHeight = maxHeight;
|
|
4149
|
+
finalWidth = maxHeight * aspectRatio;
|
|
4150
|
+
}
|
|
4151
|
+
// Set initial position and size (same as original image)
|
|
4152
|
+
enlargedImg.style.cssText = `
|
|
4153
|
+
position: fixed;
|
|
4154
|
+
top: ${imgRect.top}px;
|
|
4155
|
+
left: ${imgRect.left}px;
|
|
4156
|
+
width: ${imgRect.width}px;
|
|
4157
|
+
height: ${imgRect.height}px;
|
|
4158
|
+
transition: all ${attrs.inDuration || 275}ms ease;
|
|
4159
|
+
cursor: zoom-out;
|
|
4160
|
+
max-width: none;
|
|
4161
|
+
z-index: 1001;
|
|
4162
|
+
`;
|
|
4163
|
+
// Add caption if provided
|
|
4164
|
+
let caption = null;
|
|
4165
|
+
if (attrs.caption) {
|
|
4166
|
+
caption = document.createElement('div');
|
|
4167
|
+
caption.className = 'materialbox-caption';
|
|
4168
|
+
caption.textContent = attrs.caption;
|
|
4169
|
+
caption.style.cssText = `
|
|
4170
|
+
position: fixed;
|
|
4171
|
+
bottom: 20px;
|
|
4172
|
+
left: 50%;
|
|
4173
|
+
transform: translateX(-50%);
|
|
4174
|
+
color: white;
|
|
4175
|
+
font-size: 16px;
|
|
2747
4176
|
text-align: center;
|
|
2748
4177
|
opacity: 0;
|
|
2749
4178
|
transition: opacity ${attrs.inDuration || 275}ms ease ${attrs.inDuration || 275}ms;
|
|
@@ -2841,7 +4270,7 @@
|
|
|
2841
4270
|
view: ({ attrs }) => {
|
|
2842
4271
|
const { src, alt, width, height, caption, className, style } = attrs, otherAttrs = __rest(attrs, ["src", "alt", "width", "height", "caption", "className", "style"]);
|
|
2843
4272
|
return m('img.materialboxed', Object.assign(Object.assign({}, otherAttrs), { src, alt: alt || '', width,
|
|
2844
|
-
height, className: ['materialboxed', className].filter(Boolean).join(' '), style: Object.assign({ cursor: 'zoom-in', transition: 'opacity 200ms ease' }, style), onclick: (e) => {
|
|
4273
|
+
height, className: ['materialboxed', className].filter(Boolean).join(' ') || undefined, style: Object.assign({ cursor: 'zoom-in', transition: 'opacity 200ms ease' }, style), onclick: (e) => {
|
|
2845
4274
|
e.preventDefault();
|
|
2846
4275
|
openBox(e.target, attrs);
|
|
2847
4276
|
} }));
|
|
@@ -2923,7 +4352,7 @@
|
|
|
2923
4352
|
.filter(Boolean)
|
|
2924
4353
|
.join(' ')
|
|
2925
4354
|
.trim();
|
|
2926
|
-
const overlayClasses = ['modal-overlay', state.isOpen ? 'active' : ''].filter(Boolean).join(' ').trim();
|
|
4355
|
+
const overlayClasses = ['modal-overlay', state.isOpen ? 'active' : ''].filter(Boolean).join(' ').trim() || undefined;
|
|
2927
4356
|
return m('div', { className: 'modal-container' }, [
|
|
2928
4357
|
// Modal overlay
|
|
2929
4358
|
m('div', {
|
|
@@ -2948,21 +4377,25 @@
|
|
|
2948
4377
|
role: 'dialog',
|
|
2949
4378
|
'aria-labelledby': `${id}-title`,
|
|
2950
4379
|
'aria-describedby': description ? `${id}-desc` : undefined,
|
|
2951
|
-
style: {
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
4380
|
+
style: Object.assign(Object.assign({ display: state.isOpen ? 'flex' : 'none', position: 'fixed' }, (bottomSheet
|
|
4381
|
+
? {
|
|
4382
|
+
// Bottom sheet positioning
|
|
4383
|
+
top: 'auto',
|
|
4384
|
+
bottom: '0',
|
|
4385
|
+
left: '0',
|
|
4386
|
+
right: '0',
|
|
4387
|
+
transform: 'none',
|
|
4388
|
+
maxWidth: '100%',
|
|
4389
|
+
borderRadius: '8px 8px 0 0',
|
|
4390
|
+
}
|
|
4391
|
+
: {
|
|
4392
|
+
// Regular modal positioning
|
|
4393
|
+
top: '50%',
|
|
4394
|
+
left: '50%',
|
|
4395
|
+
transform: 'translate(-50%, -50%)',
|
|
4396
|
+
maxWidth: '75%',
|
|
4397
|
+
borderRadius: '4px',
|
|
4398
|
+
})), { backgroundColor: 'var(--mm-modal-background, #fff)', maxHeight: '85%', overflow: 'auto', zIndex: '1003', padding: '0', flexDirection: 'column', boxShadow: '0 24px 38px 3px rgba(0,0,0,0.14), 0 9px 46px 8px rgba(0,0,0,0.12), 0 11px 15px -7px rgba(0,0,0,0.20)' }),
|
|
2966
4399
|
onclick: (e) => e.stopPropagation(), // Prevent backdrop click when clicking inside modal
|
|
2967
4400
|
}, [
|
|
2968
4401
|
// Close button
|
|
@@ -2982,7 +4415,12 @@
|
|
|
2982
4415
|
}, '×'),
|
|
2983
4416
|
// Modal content
|
|
2984
4417
|
m('.modal-content', {
|
|
2985
|
-
style: {
|
|
4418
|
+
style: {
|
|
4419
|
+
padding: '24px',
|
|
4420
|
+
paddingTop: showCloseButton ? '48px' : '24px',
|
|
4421
|
+
minHeight: 'auto',
|
|
4422
|
+
flex: '1 1 auto',
|
|
4423
|
+
},
|
|
2986
4424
|
}, [
|
|
2987
4425
|
m('h4', { id: `${id}-title`, style: { margin: '0 0 20px 0' } }, title),
|
|
2988
4426
|
description &&
|
|
@@ -2993,119 +4431,16 @@
|
|
|
2993
4431
|
buttons.length > 0 &&
|
|
2994
4432
|
m('.modal-footer', {
|
|
2995
4433
|
style: {
|
|
2996
|
-
padding: '4px 6px',
|
|
2997
|
-
borderTop: '1px solid rgba(160,160,160,0.2)',
|
|
2998
|
-
textAlign: 'right',
|
|
2999
|
-
},
|
|
3000
|
-
}, buttons.map((buttonProps) => m(FlatButton, Object.assign(Object.assign({}, buttonProps), { className: `modal-close ${buttonProps.className || ''}`, onclick: (e) => {
|
|
3001
|
-
if (buttonProps.onclick)
|
|
3002
|
-
buttonProps.onclick(e);
|
|
3003
|
-
closeModal(attrs);
|
|
3004
|
-
} })))),
|
|
3005
|
-
]),
|
|
3006
|
-
]);
|
|
3007
|
-
},
|
|
3008
|
-
};
|
|
3009
|
-
};
|
|
3010
|
-
|
|
3011
|
-
/** Component to show a check box */
|
|
3012
|
-
const InputCheckbox = () => {
|
|
3013
|
-
return {
|
|
3014
|
-
view: ({ attrs: { className = 'col s12', onchange, label, checked, disabled, description, style, inputId } }) => {
|
|
3015
|
-
const checkboxId = inputId || uniqueId();
|
|
3016
|
-
return m(`p`, { className, style }, m('label', { for: checkboxId }, [
|
|
3017
|
-
m('input[type=checkbox][tabindex=0]', {
|
|
3018
|
-
id: checkboxId,
|
|
3019
|
-
checked,
|
|
3020
|
-
disabled,
|
|
3021
|
-
onclick: onchange
|
|
3022
|
-
? (e) => {
|
|
3023
|
-
if (e.target && typeof e.target.checked !== 'undefined') {
|
|
3024
|
-
onchange(e.target.checked);
|
|
3025
|
-
}
|
|
3026
|
-
}
|
|
3027
|
-
: undefined,
|
|
3028
|
-
}),
|
|
3029
|
-
label ? (typeof label === 'string' ? m('span', label) : label) : undefined,
|
|
3030
|
-
]), description && m(HelperText, { className: 'input-checkbox-desc', helperText: description }));
|
|
3031
|
-
},
|
|
3032
|
-
};
|
|
3033
|
-
};
|
|
3034
|
-
/** A list of checkboxes */
|
|
3035
|
-
const Options = () => {
|
|
3036
|
-
const state = {};
|
|
3037
|
-
const isChecked = (id) => state.checkedIds.indexOf(id) >= 0;
|
|
3038
|
-
const selectAll = (options, callback) => {
|
|
3039
|
-
const allIds = options.map((option) => option.id);
|
|
3040
|
-
state.checkedIds = [...allIds];
|
|
3041
|
-
if (callback)
|
|
3042
|
-
callback(allIds);
|
|
3043
|
-
};
|
|
3044
|
-
const selectNone = (callback) => {
|
|
3045
|
-
state.checkedIds = [];
|
|
3046
|
-
if (callback)
|
|
3047
|
-
callback([]);
|
|
3048
|
-
};
|
|
3049
|
-
return {
|
|
3050
|
-
oninit: ({ attrs: { initialValue, checkedId, id } }) => {
|
|
3051
|
-
const iv = checkedId || initialValue;
|
|
3052
|
-
state.checkedId = checkedId;
|
|
3053
|
-
state.checkedIds = iv ? (iv instanceof Array ? [...iv] : [iv]) : [];
|
|
3054
|
-
state.componentId = id || uniqueId();
|
|
3055
|
-
},
|
|
3056
|
-
view: ({ attrs: { label, options, description, className = 'col s12', style, disabled, checkboxClass, newRow, isMandatory, layout = 'vertical', showSelectAll = false, onchange: callback, }, }) => {
|
|
3057
|
-
const onchange = callback
|
|
3058
|
-
? (propId, checked) => {
|
|
3059
|
-
const checkedIds = state.checkedIds.filter((i) => i !== propId);
|
|
3060
|
-
if (checked) {
|
|
3061
|
-
checkedIds.push(propId);
|
|
3062
|
-
}
|
|
3063
|
-
state.checkedIds = checkedIds;
|
|
3064
|
-
callback(checkedIds);
|
|
3065
|
-
}
|
|
3066
|
-
: undefined;
|
|
3067
|
-
const cn = [newRow ? 'clear' : '', className].filter(Boolean).join(' ').trim();
|
|
3068
|
-
const optionsContent = layout === 'horizontal'
|
|
3069
|
-
? m('div.grid-container', options.map((option) => m(InputCheckbox, {
|
|
3070
|
-
disabled: disabled || option.disabled,
|
|
3071
|
-
label: option.label,
|
|
3072
|
-
onchange: onchange ? (v) => onchange(option.id, v) : undefined,
|
|
3073
|
-
className: option.className || checkboxClass,
|
|
3074
|
-
checked: isChecked(option.id),
|
|
3075
|
-
description: option.description,
|
|
3076
|
-
inputId: `${state.componentId}-${option.id}`,
|
|
3077
|
-
})))
|
|
3078
|
-
: options.map((option) => m(InputCheckbox, {
|
|
3079
|
-
disabled: disabled || option.disabled,
|
|
3080
|
-
label: option.label,
|
|
3081
|
-
onchange: onchange ? (v) => onchange(option.id, v) : undefined,
|
|
3082
|
-
className: option.className || checkboxClass,
|
|
3083
|
-
checked: isChecked(option.id),
|
|
3084
|
-
description: option.description,
|
|
3085
|
-
inputId: `${state.componentId}-${option.id}`,
|
|
3086
|
-
}));
|
|
3087
|
-
return m('div', { id: state.componentId, className: cn, style }, [
|
|
3088
|
-
label && m('h5.form-group-label', label + (isMandatory ? ' *' : '')),
|
|
3089
|
-
showSelectAll &&
|
|
3090
|
-
m('div.select-all-controls', { style: 'margin-bottom: 10px;' }, [
|
|
3091
|
-
m('a', {
|
|
3092
|
-
href: '#',
|
|
3093
|
-
onclick: (e) => {
|
|
3094
|
-
e.preventDefault();
|
|
3095
|
-
selectAll(options, callback);
|
|
3096
|
-
},
|
|
3097
|
-
style: 'margin-right: 15px;',
|
|
3098
|
-
}, 'Select All'),
|
|
3099
|
-
m('a', {
|
|
3100
|
-
href: '#',
|
|
3101
|
-
onclick: (e) => {
|
|
3102
|
-
e.preventDefault();
|
|
3103
|
-
selectNone(callback);
|
|
3104
|
-
},
|
|
3105
|
-
}, 'Select None'),
|
|
3106
|
-
]),
|
|
3107
|
-
description && m(HelperText, { helperText: description }),
|
|
3108
|
-
m('form', { action: '#' }, optionsContent),
|
|
4434
|
+
padding: '4px 6px',
|
|
4435
|
+
borderTop: '1px solid var(--mm-border-color, rgba(160,160,160,0.2))',
|
|
4436
|
+
textAlign: 'right',
|
|
4437
|
+
},
|
|
4438
|
+
}, buttons.map((buttonProps) => m(FlatButton, Object.assign(Object.assign({}, buttonProps), { className: `modal-close ${buttonProps.className || ''}`, onclick: (e) => {
|
|
4439
|
+
if (buttonProps.onclick)
|
|
4440
|
+
buttonProps.onclick(e);
|
|
4441
|
+
closeModal(attrs);
|
|
4442
|
+
} })))),
|
|
4443
|
+
]),
|
|
3109
4444
|
]);
|
|
3110
4445
|
},
|
|
3111
4446
|
};
|
|
@@ -3375,12 +4710,15 @@
|
|
|
3375
4710
|
};
|
|
3376
4711
|
const updateTimeFromInput = (inputValue) => {
|
|
3377
4712
|
let value = ((inputValue || options.defaultTime || '') + '').split(':');
|
|
4713
|
+
let amPmWasProvided = false;
|
|
3378
4714
|
if (options.twelveHour && value.length > 1) {
|
|
3379
4715
|
if (value[1].toUpperCase().indexOf('AM') > -1) {
|
|
3380
4716
|
state.amOrPm = 'AM';
|
|
4717
|
+
amPmWasProvided = true;
|
|
3381
4718
|
}
|
|
3382
4719
|
else if (value[1].toUpperCase().indexOf('PM') > -1) {
|
|
3383
4720
|
state.amOrPm = 'PM';
|
|
4721
|
+
amPmWasProvided = true;
|
|
3384
4722
|
}
|
|
3385
4723
|
value[1] = value[1].replace('AM', '').replace('PM', '').trim();
|
|
3386
4724
|
}
|
|
@@ -3389,21 +4727,33 @@
|
|
|
3389
4727
|
value = [now.getHours().toString(), now.getMinutes().toString()];
|
|
3390
4728
|
if (options.twelveHour) {
|
|
3391
4729
|
state.amOrPm = parseInt(value[0]) >= 12 ? 'PM' : 'AM';
|
|
4730
|
+
amPmWasProvided = false; // For 'now', we need to do conversion
|
|
3392
4731
|
}
|
|
3393
4732
|
}
|
|
3394
4733
|
let hours = +value[0] || 0;
|
|
3395
4734
|
let minutes = +value[1] || 0;
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
4735
|
+
if (options.twelveHour) {
|
|
4736
|
+
if (!amPmWasProvided) {
|
|
4737
|
+
// No AM/PM was provided, assume this is 24-hour format input - convert it
|
|
4738
|
+
if (hours >= 12) {
|
|
4739
|
+
state.amOrPm = 'PM';
|
|
4740
|
+
if (hours > 12) {
|
|
4741
|
+
hours = hours - 12;
|
|
4742
|
+
}
|
|
4743
|
+
}
|
|
4744
|
+
else {
|
|
4745
|
+
state.amOrPm = 'AM';
|
|
4746
|
+
if (hours === 0) {
|
|
4747
|
+
hours = 12;
|
|
4748
|
+
}
|
|
4749
|
+
}
|
|
3401
4750
|
}
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
4751
|
+
else {
|
|
4752
|
+
// AM/PM was provided, hours are already in 12-hour format
|
|
4753
|
+
// Just handle midnight/noon edge cases
|
|
4754
|
+
if (hours === 0 && state.amOrPm === 'AM') {
|
|
4755
|
+
hours = 12;
|
|
4756
|
+
}
|
|
3407
4757
|
}
|
|
3408
4758
|
}
|
|
3409
4759
|
state.hours = hours;
|
|
@@ -4083,7 +5433,7 @@
|
|
|
4083
5433
|
callback(propId);
|
|
4084
5434
|
}
|
|
4085
5435
|
};
|
|
4086
|
-
const cn = [newRow ? 'clear' : '', className].filter(Boolean).join(' ').trim();
|
|
5436
|
+
const cn = [newRow ? 'clear' : '', className].filter(Boolean).join(' ').trim() || undefined;
|
|
4087
5437
|
const optionsContent = layout === 'horizontal'
|
|
4088
5438
|
? m('div.grid-container', options.map((r) => m(RadioButton, Object.assign(Object.assign({}, r), { onchange,
|
|
4089
5439
|
groupId, disabled: disabled || r.disabled, className: checkboxClass, checked: r.id === checkedId, inputId: `${componentId}-${r.id}` }))))
|
|
@@ -4356,7 +5706,7 @@
|
|
|
4356
5706
|
view: ({ attrs }) => {
|
|
4357
5707
|
const id = attrs.id || state.id;
|
|
4358
5708
|
const { label, left, right, disabled, newRow, onchange, isMandatory, className = 'col s12' } = attrs, params = __rest(attrs, ["label", "left", "right", "disabled", "newRow", "onchange", "isMandatory", "className"]);
|
|
4359
|
-
const cn = ['input-field', newRow ? 'clear' : '', className].filter(Boolean).join(' ').trim();
|
|
5709
|
+
const cn = ['input-field', newRow ? 'clear' : '', className].filter(Boolean).join(' ').trim() || undefined;
|
|
4360
5710
|
return m('div', {
|
|
4361
5711
|
className: cn,
|
|
4362
5712
|
onclick: (e) => {
|
|
@@ -4507,7 +5857,7 @@
|
|
|
4507
5857
|
},
|
|
4508
5858
|
view: ({ attrs }) => {
|
|
4509
5859
|
const { tabWidth, tabs, className, style, swipeable = false } = attrs;
|
|
4510
|
-
const cn = [tabWidth === 'fill' ? 'tabs-fixed-width' : '', className].filter(Boolean).join(' ').trim();
|
|
5860
|
+
const cn = [tabWidth === 'fill' ? 'tabs-fixed-width' : '', className].filter(Boolean).join(' ').trim() || undefined;
|
|
4511
5861
|
const anchoredTabs = tabs.map(toAnchored());
|
|
4512
5862
|
const activeTab = setActiveTabId(anchoredTabs, attrs.selectedTabId);
|
|
4513
5863
|
updateIndicator();
|
|
@@ -5327,6 +6677,17 @@
|
|
|
5327
6677
|
return tooltips;
|
|
5328
6678
|
};
|
|
5329
6679
|
|
|
6680
|
+
const defaultI18n$1 = {
|
|
6681
|
+
theme: 'Theme:',
|
|
6682
|
+
light: 'Light',
|
|
6683
|
+
dark: 'Dark',
|
|
6684
|
+
auto: 'Auto',
|
|
6685
|
+
lightTitle: 'Light theme',
|
|
6686
|
+
darkTitle: 'Dark theme',
|
|
6687
|
+
autoTitle: 'Auto theme (system preference)',
|
|
6688
|
+
switchToDark: 'Switch to dark theme',
|
|
6689
|
+
switchToLight: 'Switch to light theme',
|
|
6690
|
+
};
|
|
5330
6691
|
/**
|
|
5331
6692
|
* Theme switching utilities and component
|
|
5332
6693
|
*/
|
|
@@ -5409,44 +6770,36 @@
|
|
|
5409
6770
|
ThemeManager.initialize();
|
|
5410
6771
|
}
|
|
5411
6772
|
},
|
|
5412
|
-
view: ({ attrs }) => {
|
|
5413
|
-
const { theme = ThemeManager.getTheme(), onThemeChange, showLabels = true, className
|
|
6773
|
+
view: ({ attrs = {} }) => {
|
|
6774
|
+
const { theme = ThemeManager.getTheme(), onThemeChange, showLabels = true, className, i18n } = attrs;
|
|
6775
|
+
const labels = Object.assign(Object.assign({}, defaultI18n$1), i18n);
|
|
5414
6776
|
const handleThemeChange = (newTheme) => {
|
|
5415
6777
|
ThemeManager.setTheme(newTheme);
|
|
5416
6778
|
if (onThemeChange) {
|
|
5417
6779
|
onThemeChange(newTheme);
|
|
5418
6780
|
}
|
|
5419
6781
|
};
|
|
5420
|
-
return m('.theme-switcher', {
|
|
5421
|
-
showLabels && m('span.theme-switcher-label',
|
|
6782
|
+
return m('.theme-switcher', { className }, [
|
|
6783
|
+
showLabels && m('span.theme-switcher-label', labels.theme),
|
|
5422
6784
|
m('.theme-switcher-buttons', [
|
|
5423
6785
|
m('button.btn-flat', {
|
|
5424
6786
|
class: theme === 'light' ? 'active' : '',
|
|
5425
6787
|
onclick: () => handleThemeChange('light'),
|
|
5426
|
-
title:
|
|
5427
|
-
}, [
|
|
5428
|
-
m('i.material-icons', 'light_mode'),
|
|
5429
|
-
showLabels && m('span', 'Light')
|
|
5430
|
-
]),
|
|
6788
|
+
title: labels.lightTitle,
|
|
6789
|
+
}, [m('i.material-icons', 'light_mode'), showLabels && m('span', labels.light)]),
|
|
5431
6790
|
m('button.btn-flat', {
|
|
5432
6791
|
class: theme === 'auto' ? 'active' : '',
|
|
5433
6792
|
onclick: () => handleThemeChange('auto'),
|
|
5434
|
-
title:
|
|
5435
|
-
}, [
|
|
5436
|
-
m('i.material-icons', 'brightness_auto'),
|
|
5437
|
-
showLabels && m('span', 'Auto')
|
|
5438
|
-
]),
|
|
6793
|
+
title: labels.autoTitle,
|
|
6794
|
+
}, [m('i.material-icons', 'brightness_auto'), showLabels && m('span', labels.auto)]),
|
|
5439
6795
|
m('button.btn-flat', {
|
|
5440
6796
|
class: theme === 'dark' ? 'active' : '',
|
|
5441
6797
|
onclick: () => handleThemeChange('dark'),
|
|
5442
|
-
title:
|
|
5443
|
-
}, [
|
|
5444
|
-
|
|
5445
|
-
showLabels && m('span', 'Dark')
|
|
5446
|
-
])
|
|
5447
|
-
])
|
|
6798
|
+
title: labels.darkTitle,
|
|
6799
|
+
}, [m('i.material-icons', 'dark_mode'), showLabels && m('span', labels.dark)]),
|
|
6800
|
+
]),
|
|
5448
6801
|
]);
|
|
5449
|
-
}
|
|
6802
|
+
},
|
|
5450
6803
|
};
|
|
5451
6804
|
};
|
|
5452
6805
|
/**
|
|
@@ -5460,41 +6813,55 @@
|
|
|
5460
6813
|
ThemeManager.initialize();
|
|
5461
6814
|
}
|
|
5462
6815
|
},
|
|
5463
|
-
view: ({ attrs }) => {
|
|
6816
|
+
view: ({ attrs = {} }) => {
|
|
5464
6817
|
const currentTheme = ThemeManager.getEffectiveTheme();
|
|
6818
|
+
const labels = Object.assign(Object.assign({}, defaultI18n$1), attrs.i18n);
|
|
5465
6819
|
return m('button.btn-flat.theme-toggle', {
|
|
5466
6820
|
class: attrs.className || '',
|
|
5467
6821
|
onclick: () => {
|
|
5468
6822
|
ThemeManager.toggle();
|
|
5469
|
-
m.redraw();
|
|
5470
6823
|
},
|
|
5471
|
-
title:
|
|
5472
|
-
style: 'margin: 0; height: 64px; line-height: 64px; border-radius: 0; min-width: 64px; padding: 0;'
|
|
6824
|
+
title: currentTheme === 'light' ? labels.switchToDark : labels.switchToLight,
|
|
6825
|
+
style: 'margin: 0; height: 64px; line-height: 64px; border-radius: 0; min-width: 64px; padding: 0;',
|
|
5473
6826
|
}, [
|
|
5474
6827
|
m('i.material-icons', {
|
|
5475
|
-
style: 'color: inherit; font-size: 24px;'
|
|
5476
|
-
}, currentTheme === 'light' ? 'dark_mode' : 'light_mode')
|
|
6828
|
+
style: 'color: inherit; font-size: 24px;',
|
|
6829
|
+
}, currentTheme === 'light' ? 'dark_mode' : 'light_mode'),
|
|
5477
6830
|
]);
|
|
5478
|
-
}
|
|
6831
|
+
},
|
|
5479
6832
|
};
|
|
5480
6833
|
};
|
|
5481
6834
|
|
|
6835
|
+
const defaultI18n = {
|
|
6836
|
+
label: 'Choose files or drag them here',
|
|
6837
|
+
selectedFiles: 'Selected Files:',
|
|
6838
|
+
acceptedTypes: 'Accepted:',
|
|
6839
|
+
removeFile: 'Remove file',
|
|
6840
|
+
fileSizeExceeds: 'File size exceeds {maxSize}MB limit',
|
|
6841
|
+
fileTypeNotAccepted: 'File type not accepted. Accepted: {accept}',
|
|
6842
|
+
fileSizeUnits: {
|
|
6843
|
+
bytes: 'B',
|
|
6844
|
+
kb: 'KB',
|
|
6845
|
+
mb: 'MB',
|
|
6846
|
+
gb: 'GB',
|
|
6847
|
+
},
|
|
6848
|
+
};
|
|
5482
6849
|
/**
|
|
5483
6850
|
* File Upload Component with Drag and Drop
|
|
5484
6851
|
* Supports multiple files, file type validation, size limits, and image preview
|
|
5485
6852
|
*/
|
|
5486
6853
|
const FileUpload = () => {
|
|
5487
6854
|
let state;
|
|
5488
|
-
const validateFile = (file, attrs) => {
|
|
6855
|
+
const validateFile = (file, attrs, labels) => {
|
|
5489
6856
|
// Check file size
|
|
5490
6857
|
if (attrs.maxSize && file.size > attrs.maxSize) {
|
|
5491
6858
|
const maxSizeMB = (attrs.maxSize / (1024 * 1024)).toFixed(1);
|
|
5492
|
-
return
|
|
6859
|
+
return labels.fileSizeExceeds.replace('{maxSize}', maxSizeMB);
|
|
5493
6860
|
}
|
|
5494
6861
|
// Check file type
|
|
5495
6862
|
if (attrs.accept) {
|
|
5496
|
-
const acceptedTypes = attrs.accept.split(',').map(type => type.trim());
|
|
5497
|
-
const isAccepted = acceptedTypes.some(acceptedType => {
|
|
6863
|
+
const acceptedTypes = attrs.accept.split(',').map((type) => type.trim());
|
|
6864
|
+
const isAccepted = acceptedTypes.some((acceptedType) => {
|
|
5498
6865
|
if (acceptedType.startsWith('.')) {
|
|
5499
6866
|
// Extension check
|
|
5500
6867
|
return file.name.toLowerCase().endsWith(acceptedType.toLowerCase());
|
|
@@ -5505,7 +6872,7 @@
|
|
|
5505
6872
|
}
|
|
5506
6873
|
});
|
|
5507
6874
|
if (!isAccepted) {
|
|
5508
|
-
return
|
|
6875
|
+
return labels.fileTypeNotAccepted.replace('{accept}', attrs.accept);
|
|
5509
6876
|
}
|
|
5510
6877
|
}
|
|
5511
6878
|
return null;
|
|
@@ -5521,12 +6888,12 @@
|
|
|
5521
6888
|
reader.readAsDataURL(file);
|
|
5522
6889
|
}
|
|
5523
6890
|
};
|
|
5524
|
-
const handleFiles = (fileList, attrs) => {
|
|
6891
|
+
const handleFiles = (fileList, attrs, labels) => {
|
|
5525
6892
|
const newFiles = Array.from(fileList);
|
|
5526
6893
|
const validFiles = [];
|
|
5527
6894
|
// Validate each file
|
|
5528
6895
|
for (const file of newFiles) {
|
|
5529
|
-
const error = validateFile(file, attrs);
|
|
6896
|
+
const error = validateFile(file, attrs, labels);
|
|
5530
6897
|
if (error) {
|
|
5531
6898
|
file.uploadError = error;
|
|
5532
6899
|
}
|
|
@@ -5554,20 +6921,20 @@
|
|
|
5554
6921
|
}
|
|
5555
6922
|
// Notify parent component
|
|
5556
6923
|
if (attrs.onFilesSelected) {
|
|
5557
|
-
attrs.onFilesSelected(state.files.filter(f => !f.uploadError));
|
|
6924
|
+
attrs.onFilesSelected(state.files.filter((f) => !f.uploadError));
|
|
5558
6925
|
}
|
|
5559
6926
|
};
|
|
5560
6927
|
const removeFile = (fileToRemove, attrs) => {
|
|
5561
|
-
state.files = state.files.filter(file => file !== fileToRemove);
|
|
6928
|
+
state.files = state.files.filter((file) => file !== fileToRemove);
|
|
5562
6929
|
if (attrs.onFileRemoved) {
|
|
5563
6930
|
attrs.onFileRemoved(fileToRemove);
|
|
5564
6931
|
}
|
|
5565
6932
|
};
|
|
5566
|
-
const formatFileSize = (bytes) => {
|
|
6933
|
+
const formatFileSize = (bytes, labels) => {
|
|
5567
6934
|
if (bytes === 0)
|
|
5568
|
-
return
|
|
6935
|
+
return `0 ${labels.fileSizeUnits.bytes}`;
|
|
5569
6936
|
const k = 1024;
|
|
5570
|
-
const sizes = [
|
|
6937
|
+
const sizes = [labels.fileSizeUnits.bytes, labels.fileSizeUnits.kb, labels.fileSizeUnits.mb, labels.fileSizeUnits.gb];
|
|
5571
6938
|
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
5572
6939
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i];
|
|
5573
6940
|
};
|
|
@@ -5577,11 +6944,13 @@
|
|
|
5577
6944
|
id: uniqueId(),
|
|
5578
6945
|
files: [],
|
|
5579
6946
|
isDragOver: false,
|
|
5580
|
-
isUploading: false
|
|
6947
|
+
isUploading: false,
|
|
5581
6948
|
};
|
|
5582
6949
|
},
|
|
5583
6950
|
view: ({ attrs }) => {
|
|
5584
|
-
const { accept, multiple = false, disabled = false, label
|
|
6951
|
+
const { accept, multiple = false, disabled = false, label, helperText, showPreview = true, className = '', error, i18n, } = attrs;
|
|
6952
|
+
const labels = Object.assign(Object.assign({}, defaultI18n), i18n);
|
|
6953
|
+
const displayLabel = label || labels.label;
|
|
5585
6954
|
return m('.file-upload-container', { class: className }, [
|
|
5586
6955
|
// Upload area
|
|
5587
6956
|
m('.file-upload-area', {
|
|
@@ -5589,8 +6958,10 @@
|
|
|
5589
6958
|
state.isDragOver ? 'drag-over' : '',
|
|
5590
6959
|
disabled ? 'disabled' : '',
|
|
5591
6960
|
error ? 'error' : '',
|
|
5592
|
-
state.files.length > 0 ? 'has-files' : ''
|
|
5593
|
-
]
|
|
6961
|
+
state.files.length > 0 ? 'has-files' : '',
|
|
6962
|
+
]
|
|
6963
|
+
.filter(Boolean)
|
|
6964
|
+
.join(' ') || undefined,
|
|
5594
6965
|
ondragover: (e) => {
|
|
5595
6966
|
if (disabled)
|
|
5596
6967
|
return;
|
|
@@ -5613,7 +6984,7 @@
|
|
|
5613
6984
|
e.stopPropagation();
|
|
5614
6985
|
state.isDragOver = false;
|
|
5615
6986
|
if ((_a = e.dataTransfer) === null || _a === void 0 ? void 0 : _a.files) {
|
|
5616
|
-
handleFiles(e.dataTransfer.files, attrs);
|
|
6987
|
+
handleFiles(e.dataTransfer.files, attrs, labels);
|
|
5617
6988
|
}
|
|
5618
6989
|
},
|
|
5619
6990
|
onclick: () => {
|
|
@@ -5621,7 +6992,7 @@
|
|
|
5621
6992
|
return;
|
|
5622
6993
|
const input = document.getElementById(state.id);
|
|
5623
6994
|
input === null || input === void 0 ? void 0 : input.click();
|
|
5624
|
-
}
|
|
6995
|
+
},
|
|
5625
6996
|
}, [
|
|
5626
6997
|
m('input[type="file"]', {
|
|
5627
6998
|
id: state.id,
|
|
@@ -5632,59 +7003,57 @@
|
|
|
5632
7003
|
onchange: (e) => {
|
|
5633
7004
|
const target = e.target;
|
|
5634
7005
|
if (target.files) {
|
|
5635
|
-
handleFiles(target.files, attrs);
|
|
7006
|
+
handleFiles(target.files, attrs, labels);
|
|
5636
7007
|
}
|
|
5637
|
-
}
|
|
7008
|
+
},
|
|
5638
7009
|
}),
|
|
5639
7010
|
m('.file-upload-content', [
|
|
5640
7011
|
m('i.material-icons.file-upload-icon', 'cloud_upload'),
|
|
5641
|
-
m('p.file-upload-label',
|
|
7012
|
+
m('p.file-upload-label', displayLabel),
|
|
5642
7013
|
helperText && m('p.file-upload-helper', helperText),
|
|
5643
|
-
accept && m('p.file-upload-types',
|
|
5644
|
-
])
|
|
7014
|
+
accept && m('p.file-upload-types', `${labels.acceptedTypes} ${accept}`),
|
|
7015
|
+
]),
|
|
5645
7016
|
]),
|
|
5646
7017
|
// Error message
|
|
5647
7018
|
error && m('.file-upload-error', error),
|
|
5648
7019
|
// File list
|
|
5649
|
-
state.files.length > 0 &&
|
|
5650
|
-
m('
|
|
5651
|
-
|
|
5652
|
-
|
|
5653
|
-
|
|
5654
|
-
m('img', { src: file.preview, alt: file.name })
|
|
5655
|
-
|
|
5656
|
-
|
|
5657
|
-
|
|
5658
|
-
|
|
5659
|
-
|
|
5660
|
-
|
|
5661
|
-
|
|
5662
|
-
|
|
5663
|
-
|
|
5664
|
-
|
|
5665
|
-
|
|
5666
|
-
|
|
5667
|
-
|
|
5668
|
-
|
|
5669
|
-
|
|
7020
|
+
state.files.length > 0 &&
|
|
7021
|
+
m('.file-upload-list', [
|
|
7022
|
+
m('h6', labels.selectedFiles),
|
|
7023
|
+
state.files.map((file) => m('.file-upload-item', { key: file.name + file.size }, [
|
|
7024
|
+
// Preview thumbnail
|
|
7025
|
+
showPreview && file.preview && m('.file-preview', [m('img', { src: file.preview, alt: file.name })]),
|
|
7026
|
+
// File info
|
|
7027
|
+
m('.file-info', [
|
|
7028
|
+
m('.file-name', file.name),
|
|
7029
|
+
m('.file-details', [
|
|
7030
|
+
m('span.file-size', formatFileSize(file.size, labels)),
|
|
7031
|
+
file.type && m('span.file-type', file.type),
|
|
7032
|
+
]),
|
|
7033
|
+
// Progress bar (if uploading)
|
|
7034
|
+
file.uploadProgress !== undefined &&
|
|
7035
|
+
m('.file-progress', [
|
|
7036
|
+
m('.progress', [
|
|
7037
|
+
m('.determinate', {
|
|
7038
|
+
style: { width: `${file.uploadProgress}%` },
|
|
7039
|
+
}),
|
|
7040
|
+
]),
|
|
7041
|
+
]),
|
|
7042
|
+
// Error message
|
|
7043
|
+
file.uploadError && m('.file-error', file.uploadError),
|
|
5670
7044
|
]),
|
|
5671
|
-
//
|
|
5672
|
-
|
|
5673
|
-
|
|
5674
|
-
|
|
5675
|
-
|
|
5676
|
-
|
|
5677
|
-
|
|
5678
|
-
|
|
5679
|
-
|
|
5680
|
-
|
|
5681
|
-
}, [
|
|
5682
|
-
m('i.material-icons', 'close')
|
|
5683
|
-
])
|
|
5684
|
-
]))
|
|
5685
|
-
])
|
|
7045
|
+
// Remove button
|
|
7046
|
+
m('button.btn-flat.file-remove', {
|
|
7047
|
+
onclick: (e) => {
|
|
7048
|
+
e.stopPropagation();
|
|
7049
|
+
removeFile(file, attrs);
|
|
7050
|
+
},
|
|
7051
|
+
title: labels.removeFile,
|
|
7052
|
+
}, [m('i.material-icons', 'close')]),
|
|
7053
|
+
])),
|
|
7054
|
+
]),
|
|
5686
7055
|
]);
|
|
5687
|
-
}
|
|
7056
|
+
},
|
|
5688
7057
|
};
|
|
5689
7058
|
};
|
|
5690
7059
|
|
|
@@ -5714,7 +7083,7 @@
|
|
|
5714
7083
|
state = {
|
|
5715
7084
|
id: attrs.id || uniqueId(),
|
|
5716
7085
|
isOpen: attrs.isOpen || false,
|
|
5717
|
-
isAnimating: false
|
|
7086
|
+
isAnimating: false,
|
|
5718
7087
|
};
|
|
5719
7088
|
// Set up keyboard listener
|
|
5720
7089
|
if (typeof document !== 'undefined' && attrs.closeOnEscape !== false) {
|
|
@@ -5743,34 +7112,33 @@
|
|
|
5743
7112
|
}
|
|
5744
7113
|
},
|
|
5745
7114
|
view: ({ attrs, children }) => {
|
|
5746
|
-
const { position = 'left', mode = 'overlay', width = 300, className = '', showBackdrop = true, animationDuration = 300, fixed = false } = attrs;
|
|
7115
|
+
const { position = 'left', mode = 'overlay', width = 300, className = '', showBackdrop = true, animationDuration = 300, fixed = false, } = attrs;
|
|
5747
7116
|
const isOpen = state.isOpen;
|
|
5748
7117
|
return [
|
|
5749
7118
|
// Backdrop (using existing materialize class)
|
|
5750
|
-
showBackdrop &&
|
|
5751
|
-
|
|
5752
|
-
|
|
5753
|
-
|
|
5754
|
-
|
|
5755
|
-
|
|
5756
|
-
|
|
7119
|
+
showBackdrop &&
|
|
7120
|
+
mode === 'overlay' &&
|
|
7121
|
+
m('.sidenav-overlay', {
|
|
7122
|
+
style: {
|
|
7123
|
+
display: isOpen ? 'block' : 'none',
|
|
7124
|
+
opacity: isOpen ? '1' : '0',
|
|
7125
|
+
},
|
|
7126
|
+
onclick: () => handleBackdropClick(attrs),
|
|
7127
|
+
}),
|
|
5757
7128
|
// Sidenav (using existing materialize structure)
|
|
5758
7129
|
m('ul.sidenav', {
|
|
5759
7130
|
id: state.id,
|
|
5760
|
-
class: [
|
|
5761
|
-
|
|
5762
|
-
|
|
5763
|
-
className
|
|
5764
|
-
].filter(Boolean).join(' '),
|
|
7131
|
+
class: [position === 'right' ? 'right-aligned' : '', fixed ? 'sidenav-fixed' : '', className]
|
|
7132
|
+
.filter(Boolean)
|
|
7133
|
+
.join(' ') || undefined,
|
|
5765
7134
|
style: {
|
|
5766
7135
|
width: `${width}px`,
|
|
5767
|
-
transform: isOpen ? 'translateX(0)' :
|
|
5768
|
-
|
|
5769
|
-
|
|
5770
|
-
|
|
5771
|
-
}, children)
|
|
7136
|
+
transform: isOpen ? 'translateX(0)' : position === 'left' ? 'translateX(-105%)' : 'translateX(105%)',
|
|
7137
|
+
'transition-duration': `${animationDuration}ms`,
|
|
7138
|
+
},
|
|
7139
|
+
}, children),
|
|
5772
7140
|
];
|
|
5773
|
-
}
|
|
7141
|
+
},
|
|
5774
7142
|
};
|
|
5775
7143
|
};
|
|
5776
7144
|
/**
|
|
@@ -5780,37 +7148,30 @@
|
|
|
5780
7148
|
const SidenavItem = () => {
|
|
5781
7149
|
return {
|
|
5782
7150
|
view: ({ attrs, children }) => {
|
|
5783
|
-
const { text, icon, active = false, disabled = false, onclick, href, className = '', divider = false, subheader = false } = attrs;
|
|
7151
|
+
const { text, icon, active = false, disabled = false, onclick, href, className = '', divider = false, subheader = false, } = attrs;
|
|
5784
7152
|
if (divider) {
|
|
5785
7153
|
return m('li.divider');
|
|
5786
7154
|
}
|
|
5787
7155
|
if (subheader) {
|
|
5788
7156
|
return m('li.subheader', text || children);
|
|
5789
7157
|
}
|
|
5790
|
-
const itemClasses = [
|
|
5791
|
-
|
|
5792
|
-
disabled ? 'disabled' : '',
|
|
5793
|
-
className
|
|
5794
|
-
].filter(Boolean).join(' ');
|
|
5795
|
-
const content = [
|
|
5796
|
-
icon && m('i.material-icons', icon),
|
|
5797
|
-
text || children
|
|
5798
|
-
];
|
|
7158
|
+
const itemClasses = [active ? 'active' : '', disabled ? 'disabled' : '', className].filter(Boolean).join(' ') || undefined;
|
|
7159
|
+
const content = [icon && m('i.material-icons', icon), text || children];
|
|
5799
7160
|
if (href && !disabled) {
|
|
5800
7161
|
return m('li', { class: itemClasses }, [
|
|
5801
7162
|
m('a', {
|
|
5802
7163
|
href,
|
|
5803
|
-
onclick: disabled ? undefined : onclick
|
|
5804
|
-
}, content)
|
|
7164
|
+
onclick: disabled ? undefined : onclick,
|
|
7165
|
+
}, content),
|
|
5805
7166
|
]);
|
|
5806
7167
|
}
|
|
5807
7168
|
return m('li', { class: itemClasses }, [
|
|
5808
7169
|
m('a', {
|
|
5809
7170
|
onclick: disabled ? undefined : onclick,
|
|
5810
|
-
href: '#!'
|
|
5811
|
-
}, content)
|
|
7171
|
+
href: '#!',
|
|
7172
|
+
}, content),
|
|
5812
7173
|
]);
|
|
5813
|
-
}
|
|
7174
|
+
},
|
|
5814
7175
|
};
|
|
5815
7176
|
};
|
|
5816
7177
|
/**
|
|
@@ -5861,7 +7222,7 @@
|
|
|
5861
7222
|
const Breadcrumb = () => {
|
|
5862
7223
|
return {
|
|
5863
7224
|
view: ({ attrs }) => {
|
|
5864
|
-
const { items = [], separator = 'chevron_right', className = '', showIcons = false, maxItems, showHome = false } = attrs;
|
|
7225
|
+
const { items = [], separator = 'chevron_right', className = '', showIcons = false, maxItems, showHome = false, } = attrs;
|
|
5865
7226
|
if (items.length === 0) {
|
|
5866
7227
|
return null;
|
|
5867
7228
|
}
|
|
@@ -5870,52 +7231,46 @@
|
|
|
5870
7231
|
if (maxItems && items.length > maxItems) {
|
|
5871
7232
|
const firstItem = items[0];
|
|
5872
7233
|
const lastItems = items.slice(-(maxItems - 2));
|
|
5873
|
-
displayItems = [
|
|
5874
|
-
firstItem,
|
|
5875
|
-
{ text: '...', disabled: true, className: 'breadcrumb-ellipsis' },
|
|
5876
|
-
...lastItems
|
|
5877
|
-
];
|
|
7234
|
+
displayItems = [firstItem, { text: '...', disabled: true, className: 'breadcrumb-ellipsis' }, ...lastItems];
|
|
5878
7235
|
}
|
|
5879
7236
|
return m('nav.breadcrumb', { class: className }, [
|
|
5880
|
-
m('ol.breadcrumb-list', displayItems
|
|
7237
|
+
m('ol.breadcrumb-list', displayItems
|
|
7238
|
+
.map((item, index) => {
|
|
5881
7239
|
const isLast = index === displayItems.length - 1;
|
|
5882
7240
|
const isFirst = index === 0;
|
|
5883
7241
|
return [
|
|
5884
7242
|
// Breadcrumb item
|
|
5885
7243
|
m('li.breadcrumb-item', {
|
|
5886
|
-
class: [
|
|
5887
|
-
|
|
5888
|
-
|
|
5889
|
-
item.className || ''
|
|
5890
|
-
].filter(Boolean).join(' ')
|
|
7244
|
+
class: [item.active || isLast ? 'active' : '', item.disabled ? 'disabled' : '', item.className || '']
|
|
7245
|
+
.filter(Boolean)
|
|
7246
|
+
.join(' ') || undefined,
|
|
5891
7247
|
}, [
|
|
5892
|
-
item.href && !item.disabled && !isLast
|
|
5893
|
-
// Link item
|
|
5894
|
-
|
|
5895
|
-
|
|
5896
|
-
|
|
5897
|
-
|
|
5898
|
-
|
|
5899
|
-
|
|
5900
|
-
|
|
5901
|
-
|
|
5902
|
-
// Text item (active or disabled)
|
|
5903
|
-
|
|
5904
|
-
|
|
5905
|
-
|
|
5906
|
-
|
|
5907
|
-
|
|
5908
|
-
|
|
5909
|
-
|
|
7248
|
+
item.href && !item.disabled && !isLast
|
|
7249
|
+
? // Link item
|
|
7250
|
+
m('a.breadcrumb-link', {
|
|
7251
|
+
href: item.href,
|
|
7252
|
+
onclick: item.onclick,
|
|
7253
|
+
}, [
|
|
7254
|
+
showIcons && item.icon && m('i.material-icons.breadcrumb-icon', item.icon),
|
|
7255
|
+
showHome && isFirst && !item.icon && m('i.material-icons.breadcrumb-icon', 'home'),
|
|
7256
|
+
m('span.breadcrumb-text', item.text),
|
|
7257
|
+
])
|
|
7258
|
+
: // Text item (active or disabled)
|
|
7259
|
+
m('span.breadcrumb-text', {
|
|
7260
|
+
onclick: item.disabled ? undefined : item.onclick,
|
|
7261
|
+
}, [
|
|
7262
|
+
showIcons && item.icon && m('i.material-icons.breadcrumb-icon', item.icon),
|
|
7263
|
+
showHome && isFirst && !item.icon && m('i.material-icons.breadcrumb-icon', 'home'),
|
|
7264
|
+
item.text,
|
|
7265
|
+
]),
|
|
5910
7266
|
]),
|
|
5911
7267
|
// Separator (except for last item)
|
|
5912
|
-
!isLast && m('li.breadcrumb-separator', [
|
|
5913
|
-
m('i.material-icons', separator)
|
|
5914
|
-
])
|
|
7268
|
+
!isLast && m('li.breadcrumb-separator', [m('i.material-icons', separator)]),
|
|
5915
7269
|
];
|
|
5916
|
-
})
|
|
7270
|
+
})
|
|
7271
|
+
.reduce((acc, val) => acc.concat(val), [])),
|
|
5917
7272
|
]);
|
|
5918
|
-
}
|
|
7273
|
+
},
|
|
5919
7274
|
};
|
|
5920
7275
|
};
|
|
5921
7276
|
/**
|
|
@@ -5928,7 +7283,7 @@
|
|
|
5928
7283
|
items.push({
|
|
5929
7284
|
text: 'Home',
|
|
5930
7285
|
href: basePath,
|
|
5931
|
-
icon: 'home'
|
|
7286
|
+
icon: 'home',
|
|
5932
7287
|
});
|
|
5933
7288
|
// Add path segments
|
|
5934
7289
|
let currentPath = basePath;
|
|
@@ -5938,7 +7293,7 @@
|
|
|
5938
7293
|
items.push({
|
|
5939
7294
|
text: segment.charAt(0).toUpperCase() + segment.slice(1).replace(/-/g, ' '),
|
|
5940
7295
|
href: isLast ? undefined : currentPath,
|
|
5941
|
-
active: isLast
|
|
7296
|
+
active: isLast,
|
|
5942
7297
|
});
|
|
5943
7298
|
});
|
|
5944
7299
|
return items;
|
|
@@ -5957,19 +7312,18 @@
|
|
|
5957
7312
|
items.push({
|
|
5958
7313
|
text: 'Home',
|
|
5959
7314
|
href: '/',
|
|
5960
|
-
icon: 'home'
|
|
7315
|
+
icon: 'home',
|
|
5961
7316
|
});
|
|
5962
7317
|
let currentPath = '';
|
|
5963
7318
|
segments.forEach((segment, index) => {
|
|
5964
7319
|
currentPath += '/' + segment;
|
|
5965
7320
|
const isLast = index === segments.length - 1;
|
|
5966
7321
|
// Use custom text from config or format segment
|
|
5967
|
-
const text = routeConfig[currentPath] ||
|
|
5968
|
-
segment.charAt(0).toUpperCase() + segment.slice(1).replace(/-/g, ' ');
|
|
7322
|
+
const text = routeConfig[currentPath] || segment.charAt(0).toUpperCase() + segment.slice(1).replace(/-/g, ' ');
|
|
5969
7323
|
items.push({
|
|
5970
7324
|
text,
|
|
5971
7325
|
href: isLast ? undefined : currentPath,
|
|
5972
|
-
active: isLast
|
|
7326
|
+
active: isLast,
|
|
5973
7327
|
});
|
|
5974
7328
|
});
|
|
5975
7329
|
return items;
|
|
@@ -5981,7 +7335,7 @@
|
|
|
5981
7335
|
return hierarchy.map((item, index) => ({
|
|
5982
7336
|
text: item[textKey],
|
|
5983
7337
|
href: index === hierarchy.length - 1 ? undefined : item[pathKey],
|
|
5984
|
-
active: index === hierarchy.length - 1
|
|
7338
|
+
active: index === hierarchy.length - 1,
|
|
5985
7339
|
}));
|
|
5986
7340
|
}
|
|
5987
7341
|
}
|
|
@@ -6115,7 +7469,7 @@
|
|
|
6115
7469
|
hasError ? 'error' : '',
|
|
6116
7470
|
step.disabled ? 'disabled' : '',
|
|
6117
7471
|
step.optional ? 'optional' : ''
|
|
6118
|
-
].filter(Boolean).join(' '),
|
|
7472
|
+
].filter(Boolean).join(' ') || undefined,
|
|
6119
7473
|
onclick: allowHeaderNavigation && !step.disabled ?
|
|
6120
7474
|
() => goToStep(index, attrs) : undefined
|
|
6121
7475
|
}, [
|
|
@@ -6189,6 +7543,729 @@
|
|
|
6189
7543
|
};
|
|
6190
7544
|
};
|
|
6191
7545
|
|
|
7546
|
+
// Utility function to check if a node is the last in its branch
|
|
7547
|
+
const isNodeLastInBranch = (nodePath, rootNodes) => {
|
|
7548
|
+
// Navigate to the node's position and check if it's the last child at every level
|
|
7549
|
+
let currentNodes = rootNodes;
|
|
7550
|
+
for (let i = 0; i < nodePath.length; i++) {
|
|
7551
|
+
const index = nodePath[i];
|
|
7552
|
+
const isLastAtThisLevel = index === currentNodes.length - 1;
|
|
7553
|
+
// If this is not the last child at this level, then this node is not last in branch
|
|
7554
|
+
if (!isLastAtThisLevel) {
|
|
7555
|
+
return false;
|
|
7556
|
+
}
|
|
7557
|
+
// Move to the next level if it exists
|
|
7558
|
+
if (i < nodePath.length - 1) {
|
|
7559
|
+
const currentNode = currentNodes[index];
|
|
7560
|
+
if (currentNode.children) {
|
|
7561
|
+
currentNodes = currentNode.children;
|
|
7562
|
+
}
|
|
7563
|
+
}
|
|
7564
|
+
}
|
|
7565
|
+
return true;
|
|
7566
|
+
};
|
|
7567
|
+
const TreeNodeComponent = () => {
|
|
7568
|
+
return {
|
|
7569
|
+
view: ({ attrs }) => {
|
|
7570
|
+
const { node, level, isSelected, isExpanded, isFocused, showConnectors, iconType, selectionMode, onToggleExpand, onToggleSelect, onFocus, } = attrs;
|
|
7571
|
+
const hasChildren = node.children && node.children.length > 0;
|
|
7572
|
+
const indentLevel = level * 24; // 24px per level
|
|
7573
|
+
return m('li.tree-node', {
|
|
7574
|
+
class: [
|
|
7575
|
+
isSelected && 'selected',
|
|
7576
|
+
isFocused && 'focused',
|
|
7577
|
+
node.disabled && 'disabled',
|
|
7578
|
+
hasChildren && 'has-children',
|
|
7579
|
+
attrs.isLastInBranch && 'tree-last-in-branch',
|
|
7580
|
+
]
|
|
7581
|
+
.filter(Boolean)
|
|
7582
|
+
.join(' ') || undefined,
|
|
7583
|
+
'data-node-id': node.id,
|
|
7584
|
+
'data-level': level,
|
|
7585
|
+
}, [
|
|
7586
|
+
// Node content
|
|
7587
|
+
m('.tree-node-content', {
|
|
7588
|
+
style: {
|
|
7589
|
+
paddingLeft: `${indentLevel}px`,
|
|
7590
|
+
},
|
|
7591
|
+
onclick: node.disabled
|
|
7592
|
+
? undefined
|
|
7593
|
+
: () => {
|
|
7594
|
+
if (selectionMode !== 'none') {
|
|
7595
|
+
onToggleSelect(node.id);
|
|
7596
|
+
}
|
|
7597
|
+
onFocus(node.id);
|
|
7598
|
+
},
|
|
7599
|
+
onkeydown: (e) => {
|
|
7600
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
7601
|
+
e.preventDefault();
|
|
7602
|
+
if (!node.disabled && selectionMode !== 'none') {
|
|
7603
|
+
onToggleSelect(node.id);
|
|
7604
|
+
}
|
|
7605
|
+
}
|
|
7606
|
+
},
|
|
7607
|
+
tabindex: node.disabled ? -1 : 0,
|
|
7608
|
+
role: selectionMode === 'multiple' ? 'option' : 'treeitem',
|
|
7609
|
+
'aria-selected': selectionMode !== 'none' ? isSelected.toString() : undefined,
|
|
7610
|
+
'aria-expanded': hasChildren ? isExpanded.toString() : undefined,
|
|
7611
|
+
'aria-disabled': node.disabled ? 'true' : undefined,
|
|
7612
|
+
}, [
|
|
7613
|
+
// Connector lines
|
|
7614
|
+
showConnectors &&
|
|
7615
|
+
level > 0 &&
|
|
7616
|
+
m('.tree-connectors', Array.from({ length: level }, (_, i) => m('.tree-connector', {
|
|
7617
|
+
key: i,
|
|
7618
|
+
style: { left: `${i * 24 + 12}px` },
|
|
7619
|
+
}))),
|
|
7620
|
+
// Expand/collapse icon or spacer
|
|
7621
|
+
hasChildren
|
|
7622
|
+
? m('.tree-expand-icon', {
|
|
7623
|
+
onclick: (e) => {
|
|
7624
|
+
e.stopPropagation();
|
|
7625
|
+
if (!node.disabled) {
|
|
7626
|
+
onToggleExpand(node.id);
|
|
7627
|
+
}
|
|
7628
|
+
},
|
|
7629
|
+
class: iconType,
|
|
7630
|
+
}, [
|
|
7631
|
+
iconType === 'plus-minus'
|
|
7632
|
+
? m('span.tree-plus-minus', isExpanded ? '−' : '+')
|
|
7633
|
+
: iconType === 'triangle'
|
|
7634
|
+
? m('span.tree-triangle', { class: isExpanded ? 'expanded' : undefined }, '▶')
|
|
7635
|
+
: iconType === 'chevron'
|
|
7636
|
+
? m(MaterialIcon, {
|
|
7637
|
+
name: 'chevron',
|
|
7638
|
+
direction: isExpanded ? 'down' : 'right',
|
|
7639
|
+
class: 'tree-chevron-icon',
|
|
7640
|
+
})
|
|
7641
|
+
: m(MaterialIcon, {
|
|
7642
|
+
name: 'caret',
|
|
7643
|
+
direction: isExpanded ? 'down' : 'right',
|
|
7644
|
+
class: 'tree-caret-icon',
|
|
7645
|
+
}),
|
|
7646
|
+
])
|
|
7647
|
+
: m('.tree-expand-spacer'), // Spacer for alignment
|
|
7648
|
+
// Selection indicator for multiple selection
|
|
7649
|
+
selectionMode === 'multiple' &&
|
|
7650
|
+
m('.tree-selection-indicator', [
|
|
7651
|
+
m('input[type=checkbox]', {
|
|
7652
|
+
checked: isSelected,
|
|
7653
|
+
disabled: node.disabled,
|
|
7654
|
+
onchange: () => {
|
|
7655
|
+
if (!node.disabled) {
|
|
7656
|
+
onToggleSelect(node.id);
|
|
7657
|
+
}
|
|
7658
|
+
},
|
|
7659
|
+
onclick: (e) => e.stopPropagation(),
|
|
7660
|
+
}),
|
|
7661
|
+
]),
|
|
7662
|
+
// Node icon (optional)
|
|
7663
|
+
node.icon && m('i.tree-node-icon.material-icons', node.icon),
|
|
7664
|
+
// Node label
|
|
7665
|
+
m('span.tree-node-label', node.label),
|
|
7666
|
+
]),
|
|
7667
|
+
// Children (recursive)
|
|
7668
|
+
hasChildren &&
|
|
7669
|
+
isExpanded &&
|
|
7670
|
+
m('ul.tree-children', {
|
|
7671
|
+
role: 'group',
|
|
7672
|
+
'aria-expanded': 'true',
|
|
7673
|
+
}, node.children.map((child, childIndex) => {
|
|
7674
|
+
var _a, _b, _c, _d, _e, _f;
|
|
7675
|
+
// Calculate state for each child using treeState
|
|
7676
|
+
const childIsSelected = (_b = (_a = attrs.treeState) === null || _a === void 0 ? void 0 : _a.selectedIds.has(child.id)) !== null && _b !== void 0 ? _b : false;
|
|
7677
|
+
const childIsExpanded = (_d = (_c = attrs.treeState) === null || _c === void 0 ? void 0 : _c.expandedIds.has(child.id)) !== null && _d !== void 0 ? _d : false;
|
|
7678
|
+
const childIsFocused = ((_e = attrs.treeState) === null || _e === void 0 ? void 0 : _e.focusedNodeId) === child.id;
|
|
7679
|
+
// Calculate if this child is last in branch
|
|
7680
|
+
const childPath = [...(attrs.currentPath || []), childIndex];
|
|
7681
|
+
const childIsLastInBranch = ((_f = attrs.treeAttrs) === null || _f === void 0 ? void 0 : _f.data) ?
|
|
7682
|
+
isNodeLastInBranch(childPath, attrs.treeAttrs.data) : false;
|
|
7683
|
+
return m(TreeNodeComponent, {
|
|
7684
|
+
key: child.id,
|
|
7685
|
+
node: child,
|
|
7686
|
+
level: level + 1,
|
|
7687
|
+
isSelected: childIsSelected,
|
|
7688
|
+
isExpanded: childIsExpanded,
|
|
7689
|
+
isFocused: childIsFocused,
|
|
7690
|
+
showConnectors,
|
|
7691
|
+
iconType,
|
|
7692
|
+
selectionMode,
|
|
7693
|
+
onToggleExpand,
|
|
7694
|
+
onToggleSelect,
|
|
7695
|
+
onFocus,
|
|
7696
|
+
isLastInBranch: childIsLastInBranch,
|
|
7697
|
+
currentPath: childPath,
|
|
7698
|
+
treeState: attrs.treeState,
|
|
7699
|
+
treeAttrs: attrs.treeAttrs,
|
|
7700
|
+
});
|
|
7701
|
+
})),
|
|
7702
|
+
]);
|
|
7703
|
+
},
|
|
7704
|
+
};
|
|
7705
|
+
};
|
|
7706
|
+
const TreeView = () => {
|
|
7707
|
+
const state = {
|
|
7708
|
+
selectedIds: new Set(),
|
|
7709
|
+
expandedIds: new Set(),
|
|
7710
|
+
focusedNodeId: null,
|
|
7711
|
+
treeMap: new Map(),
|
|
7712
|
+
};
|
|
7713
|
+
const buildTreeMap = (nodes, map) => {
|
|
7714
|
+
nodes.forEach((node) => {
|
|
7715
|
+
map.set(node.id, node);
|
|
7716
|
+
if (node.children) {
|
|
7717
|
+
buildTreeMap(node.children, map);
|
|
7718
|
+
}
|
|
7719
|
+
});
|
|
7720
|
+
};
|
|
7721
|
+
const initializeExpandedNodes = (nodes) => {
|
|
7722
|
+
nodes.forEach((node) => {
|
|
7723
|
+
if (node.expanded) {
|
|
7724
|
+
state.expandedIds.add(node.id);
|
|
7725
|
+
}
|
|
7726
|
+
if (node.children) {
|
|
7727
|
+
initializeExpandedNodes(node.children);
|
|
7728
|
+
}
|
|
7729
|
+
});
|
|
7730
|
+
};
|
|
7731
|
+
const handleToggleExpand = (nodeId, attrs) => {
|
|
7732
|
+
var _a;
|
|
7733
|
+
const isExpanded = state.expandedIds.has(nodeId);
|
|
7734
|
+
if (isExpanded) {
|
|
7735
|
+
state.expandedIds.delete(nodeId);
|
|
7736
|
+
}
|
|
7737
|
+
else {
|
|
7738
|
+
state.expandedIds.add(nodeId);
|
|
7739
|
+
}
|
|
7740
|
+
(_a = attrs.onexpand) === null || _a === void 0 ? void 0 : _a.call(attrs, { nodeId, expanded: !isExpanded });
|
|
7741
|
+
};
|
|
7742
|
+
const handleToggleSelect = (nodeId, attrs) => {
|
|
7743
|
+
var _a;
|
|
7744
|
+
const { selectionMode = 'single' } = attrs;
|
|
7745
|
+
if (selectionMode === 'single') {
|
|
7746
|
+
state.selectedIds.clear();
|
|
7747
|
+
state.selectedIds.add(nodeId);
|
|
7748
|
+
}
|
|
7749
|
+
else if (selectionMode === 'multiple') {
|
|
7750
|
+
if (state.selectedIds.has(nodeId)) {
|
|
7751
|
+
state.selectedIds.delete(nodeId);
|
|
7752
|
+
}
|
|
7753
|
+
else {
|
|
7754
|
+
state.selectedIds.add(nodeId);
|
|
7755
|
+
}
|
|
7756
|
+
}
|
|
7757
|
+
(_a = attrs.onselection) === null || _a === void 0 ? void 0 : _a.call(attrs, Array.from(state.selectedIds));
|
|
7758
|
+
};
|
|
7759
|
+
const handleFocus = (nodeId) => {
|
|
7760
|
+
state.focusedNodeId = nodeId;
|
|
7761
|
+
};
|
|
7762
|
+
const renderNodes = (nodes, attrs, level = 0, parentPath = []) => {
|
|
7763
|
+
return nodes.map((node, index) => {
|
|
7764
|
+
var _a, _b, _c;
|
|
7765
|
+
const isSelected = state.selectedIds.has(node.id);
|
|
7766
|
+
const isExpanded = state.expandedIds.has(node.id);
|
|
7767
|
+
const isFocused = state.focusedNodeId === node.id;
|
|
7768
|
+
const currentPath = [...parentPath, index];
|
|
7769
|
+
const isLastInBranch = isNodeLastInBranch(currentPath, attrs.data);
|
|
7770
|
+
return m(TreeNodeComponent, {
|
|
7771
|
+
key: node.id,
|
|
7772
|
+
node,
|
|
7773
|
+
level,
|
|
7774
|
+
isSelected,
|
|
7775
|
+
isExpanded,
|
|
7776
|
+
isFocused,
|
|
7777
|
+
showConnectors: (_a = attrs.showConnectors) !== null && _a !== void 0 ? _a : true,
|
|
7778
|
+
iconType: (_b = attrs.iconType) !== null && _b !== void 0 ? _b : 'caret',
|
|
7779
|
+
selectionMode: (_c = attrs.selectionMode) !== null && _c !== void 0 ? _c : 'single',
|
|
7780
|
+
onToggleExpand: (nodeId) => handleToggleExpand(nodeId, attrs),
|
|
7781
|
+
onToggleSelect: (nodeId) => handleToggleSelect(nodeId, attrs),
|
|
7782
|
+
onFocus: handleFocus,
|
|
7783
|
+
isLastInBranch,
|
|
7784
|
+
currentPath,
|
|
7785
|
+
// Pass state and attrs for recursive rendering
|
|
7786
|
+
treeState: state,
|
|
7787
|
+
treeAttrs: attrs,
|
|
7788
|
+
});
|
|
7789
|
+
});
|
|
7790
|
+
};
|
|
7791
|
+
return {
|
|
7792
|
+
oninit: ({ attrs }) => {
|
|
7793
|
+
// Build internal tree map for efficient lookups
|
|
7794
|
+
buildTreeMap(attrs.data, state.treeMap);
|
|
7795
|
+
// Initialize expanded nodes from data
|
|
7796
|
+
initializeExpandedNodes(attrs.data);
|
|
7797
|
+
// Initialize selected nodes from props
|
|
7798
|
+
if (attrs.selectedIds) {
|
|
7799
|
+
state.selectedIds = new Set(attrs.selectedIds);
|
|
7800
|
+
}
|
|
7801
|
+
},
|
|
7802
|
+
onupdate: ({ attrs }) => {
|
|
7803
|
+
// Sync selectedIds prop with internal state
|
|
7804
|
+
if (attrs.selectedIds) {
|
|
7805
|
+
const newSelection = new Set(attrs.selectedIds);
|
|
7806
|
+
if (newSelection.size !== state.selectedIds.size ||
|
|
7807
|
+
!Array.from(newSelection).every((id) => state.selectedIds.has(id))) {
|
|
7808
|
+
state.selectedIds = newSelection;
|
|
7809
|
+
}
|
|
7810
|
+
}
|
|
7811
|
+
},
|
|
7812
|
+
view: ({ attrs }) => {
|
|
7813
|
+
const { data, className, style, id, selectionMode = 'single', showConnectors = true } = attrs;
|
|
7814
|
+
return m('div.tree-view', {
|
|
7815
|
+
class: [
|
|
7816
|
+
className,
|
|
7817
|
+
showConnectors && 'show-connectors'
|
|
7818
|
+
].filter(Boolean).join(' ') || undefined,
|
|
7819
|
+
style,
|
|
7820
|
+
id,
|
|
7821
|
+
role: selectionMode === 'multiple' ? 'listbox' : 'tree',
|
|
7822
|
+
'aria-multiselectable': selectionMode === 'multiple' ? 'true' : 'false',
|
|
7823
|
+
}, [
|
|
7824
|
+
m('ul.tree-root', {
|
|
7825
|
+
role: 'group',
|
|
7826
|
+
}, renderNodes(data, attrs)),
|
|
7827
|
+
]);
|
|
7828
|
+
},
|
|
7829
|
+
};
|
|
7830
|
+
};
|
|
7831
|
+
|
|
7832
|
+
/**
|
|
7833
|
+
* Timeline Component
|
|
7834
|
+
* Displays a sequence of events in chronological order with connecting lines
|
|
7835
|
+
* Supports both vertical and horizontal orientations with Material Design styling
|
|
7836
|
+
*/
|
|
7837
|
+
const Timeline = () => {
|
|
7838
|
+
const formatTimestamp = (timestamp) => {
|
|
7839
|
+
if (!timestamp)
|
|
7840
|
+
return '';
|
|
7841
|
+
if (typeof timestamp === 'string')
|
|
7842
|
+
return timestamp;
|
|
7843
|
+
return timestamp.toLocaleDateString();
|
|
7844
|
+
};
|
|
7845
|
+
const getColorClass = (color) => {
|
|
7846
|
+
switch (color) {
|
|
7847
|
+
case 'primary':
|
|
7848
|
+
return 'timeline-primary';
|
|
7849
|
+
case 'secondary':
|
|
7850
|
+
return 'timeline-secondary';
|
|
7851
|
+
case 'success':
|
|
7852
|
+
return 'timeline-success';
|
|
7853
|
+
case 'warning':
|
|
7854
|
+
return 'timeline-warning';
|
|
7855
|
+
case 'error':
|
|
7856
|
+
return 'timeline-error';
|
|
7857
|
+
case 'info':
|
|
7858
|
+
return 'timeline-info';
|
|
7859
|
+
default:
|
|
7860
|
+
return 'timeline-default';
|
|
7861
|
+
}
|
|
7862
|
+
};
|
|
7863
|
+
return {
|
|
7864
|
+
view: ({ attrs }) => {
|
|
7865
|
+
const { items = [], orientation = 'vertical', position = 'right', showConnector = true, className = '', showTimestamps = true, compact = false, } = attrs;
|
|
7866
|
+
const timelineClasses = [
|
|
7867
|
+
'timeline',
|
|
7868
|
+
`timeline-${orientation}`,
|
|
7869
|
+
`timeline-${position}`,
|
|
7870
|
+
showConnector ? 'timeline-connector' : '',
|
|
7871
|
+
compact ? 'timeline-compact' : '',
|
|
7872
|
+
className,
|
|
7873
|
+
]
|
|
7874
|
+
.filter(Boolean)
|
|
7875
|
+
.join(' ') || undefined;
|
|
7876
|
+
return m('div', { className: timelineClasses }, [
|
|
7877
|
+
items.map((item, index) => {
|
|
7878
|
+
const isAlternate = position === 'alternate';
|
|
7879
|
+
const itemPosition = isAlternate ? (index % 2 === 0 ? 'right' : 'left') : position;
|
|
7880
|
+
const itemClasses = [
|
|
7881
|
+
'timeline-item',
|
|
7882
|
+
`timeline-item-${itemPosition}`,
|
|
7883
|
+
getColorClass(item.color),
|
|
7884
|
+
item.disabled ? 'timeline-item-disabled' : '',
|
|
7885
|
+
item.className || '',
|
|
7886
|
+
]
|
|
7887
|
+
.filter(Boolean)
|
|
7888
|
+
.join(' ') || undefined;
|
|
7889
|
+
const handleItemClick = (e) => {
|
|
7890
|
+
if (item.disabled)
|
|
7891
|
+
return;
|
|
7892
|
+
if (item.onclick) {
|
|
7893
|
+
e.preventDefault();
|
|
7894
|
+
item.onclick(item, e);
|
|
7895
|
+
}
|
|
7896
|
+
};
|
|
7897
|
+
const isVertical = orientation === 'vertical';
|
|
7898
|
+
return m('div', {
|
|
7899
|
+
key: item.id || index,
|
|
7900
|
+
className: itemClasses,
|
|
7901
|
+
onclick: item.onclick ? handleItemClick : undefined,
|
|
7902
|
+
role: item.onclick ? 'button' : undefined,
|
|
7903
|
+
tabindex: item.onclick && !item.disabled ? 0 : undefined,
|
|
7904
|
+
'aria-disabled': item.disabled ? 'true' : undefined,
|
|
7905
|
+
}, [
|
|
7906
|
+
// Timestamp (on opposite side of content from bullet)
|
|
7907
|
+
isVertical &&
|
|
7908
|
+
showTimestamps &&
|
|
7909
|
+
item.timestamp &&
|
|
7910
|
+
m(`.timeline-timestamp-separate${!item.icon ? '.timeline-dot-small' : ''}`, formatTimestamp(item.timestamp)),
|
|
7911
|
+
// Timeline separator containing dot and connector
|
|
7912
|
+
m('.timeline-separator', [
|
|
7913
|
+
// Timeline dot/icon
|
|
7914
|
+
m(`.timeline-dot${!item.icon ? '.timeline-dot-small' : ''}`, [
|
|
7915
|
+
item.icon ? m('i.material-icons.timeline-icon', item.icon) : m('.timeline-marker'),
|
|
7916
|
+
]),
|
|
7917
|
+
// Timeline connector (only show if not the last item and connectors are enabled)
|
|
7918
|
+
showConnector && index < items.length - 1 && m('.timeline-connector'),
|
|
7919
|
+
]),
|
|
7920
|
+
// Content container
|
|
7921
|
+
m('.timeline-content', [
|
|
7922
|
+
// Timestamp for horizontal layout or when not shown separately
|
|
7923
|
+
!isVertical &&
|
|
7924
|
+
showTimestamps &&
|
|
7925
|
+
item.timestamp &&
|
|
7926
|
+
m('.timeline-timestamp', formatTimestamp(item.timestamp)),
|
|
7927
|
+
// Main content
|
|
7928
|
+
item.content
|
|
7929
|
+
? item.content
|
|
7930
|
+
: m('.timeline-text', [
|
|
7931
|
+
item.label && m('.timeline-label', item.label),
|
|
7932
|
+
item.description && m('.timeline-description', item.description),
|
|
7933
|
+
]),
|
|
7934
|
+
]),
|
|
7935
|
+
]);
|
|
7936
|
+
}),
|
|
7937
|
+
]);
|
|
7938
|
+
},
|
|
7939
|
+
};
|
|
7940
|
+
};
|
|
7941
|
+
|
|
7942
|
+
const Masonry = () => {
|
|
7943
|
+
let containerRef = null;
|
|
7944
|
+
const itemHeights = []; // measured heights
|
|
7945
|
+
let resizeObserver = null;
|
|
7946
|
+
const defaultBreakpoints = { xs: 1, sm: 2, md: 3, lg: 4, xl: 5 };
|
|
7947
|
+
const getColumnsCount = (columns = 3) => {
|
|
7948
|
+
if (typeof columns === 'number')
|
|
7949
|
+
return columns;
|
|
7950
|
+
const breakpoints = Object.assign(Object.assign({}, defaultBreakpoints), columns);
|
|
7951
|
+
const width = window.innerWidth;
|
|
7952
|
+
if (width >= 1200)
|
|
7953
|
+
return breakpoints.xl || 5;
|
|
7954
|
+
if (width >= 992)
|
|
7955
|
+
return breakpoints.lg || 4;
|
|
7956
|
+
if (width >= 768)
|
|
7957
|
+
return breakpoints.md || 3;
|
|
7958
|
+
if (width >= 576)
|
|
7959
|
+
return breakpoints.sm || 2;
|
|
7960
|
+
return breakpoints.xs || 1;
|
|
7961
|
+
};
|
|
7962
|
+
const setupResizeObserver = () => {
|
|
7963
|
+
if (resizeObserver)
|
|
7964
|
+
return;
|
|
7965
|
+
if (typeof ResizeObserver === 'undefined')
|
|
7966
|
+
return;
|
|
7967
|
+
resizeObserver = new ResizeObserver((entries) => {
|
|
7968
|
+
for (const entry of entries) {
|
|
7969
|
+
const el = entry.target;
|
|
7970
|
+
const index = Number(el.dataset.index);
|
|
7971
|
+
if (!isNaN(index)) {
|
|
7972
|
+
const h = el.offsetHeight;
|
|
7973
|
+
if (itemHeights[index] !== h) {
|
|
7974
|
+
itemHeights[index] = h;
|
|
7975
|
+
m.redraw();
|
|
7976
|
+
}
|
|
7977
|
+
}
|
|
7978
|
+
}
|
|
7979
|
+
});
|
|
7980
|
+
};
|
|
7981
|
+
const cleanup = () => {
|
|
7982
|
+
if (resizeObserver) {
|
|
7983
|
+
resizeObserver.disconnect();
|
|
7984
|
+
resizeObserver = null;
|
|
7985
|
+
}
|
|
7986
|
+
};
|
|
7987
|
+
return {
|
|
7988
|
+
onremove: cleanup,
|
|
7989
|
+
view: ({ attrs, children }) => {
|
|
7990
|
+
const { columns = 3, spacing = 16, className = '', onItemClick, cssOnly = false, animationDelay } = attrs;
|
|
7991
|
+
const gap = typeof spacing === 'number' ? spacing : parseInt(spacing, 10) || 16;
|
|
7992
|
+
const columnsCount = typeof columns === 'number' ? columns : getColumnsCount(columns);
|
|
7993
|
+
const containerClasses = [
|
|
7994
|
+
'masonry',
|
|
7995
|
+
cssOnly ? 'masonry-css' : 'masonry-js',
|
|
7996
|
+
animationDelay ? 'masonry-animated' : '',
|
|
7997
|
+
className,
|
|
7998
|
+
]
|
|
7999
|
+
.filter(Boolean)
|
|
8000
|
+
.join(' ');
|
|
8001
|
+
const containerStyle = { position: 'relative' };
|
|
8002
|
+
// --- CSS-only fallback ---
|
|
8003
|
+
if (cssOnly) {
|
|
8004
|
+
containerStyle.display = 'grid';
|
|
8005
|
+
containerStyle.gridTemplateColumns = `repeat(${columnsCount}, 1fr)`;
|
|
8006
|
+
containerStyle.gap = `${gap}px`;
|
|
8007
|
+
return m('div', { className: containerClasses, style: containerStyle }, children);
|
|
8008
|
+
}
|
|
8009
|
+
// --- JS Masonry ---
|
|
8010
|
+
const containerWidth = (containerRef === null || containerRef === void 0 ? void 0 : containerRef.offsetWidth) || 800;
|
|
8011
|
+
const totalGapWidth = gap * (columnsCount - 1);
|
|
8012
|
+
const columnWidth = (containerWidth - totalGapWidth) / columnsCount;
|
|
8013
|
+
const columnHeights = new Array(columnsCount).fill(0);
|
|
8014
|
+
const positionedChildren = [];
|
|
8015
|
+
(Array.isArray(children) ? children : [children]).forEach((child, index) => {
|
|
8016
|
+
var _a;
|
|
8017
|
+
const shortestColumnIndex = columnHeights.indexOf(Math.min(...columnHeights));
|
|
8018
|
+
const x = shortestColumnIndex * (columnWidth + gap);
|
|
8019
|
+
const y = columnHeights[shortestColumnIndex];
|
|
8020
|
+
const itemHeight = (_a = itemHeights[index]) !== null && _a !== void 0 ? _a : 200; // fallback until measured
|
|
8021
|
+
columnHeights[shortestColumnIndex] += itemHeight + gap;
|
|
8022
|
+
const itemStyle = {
|
|
8023
|
+
position: 'absolute',
|
|
8024
|
+
left: `${x}px`,
|
|
8025
|
+
top: `${y}px`,
|
|
8026
|
+
width: `${columnWidth}px`,
|
|
8027
|
+
transition: 'all 0.3s ease',
|
|
8028
|
+
animationDelay: animationDelay ? `${index * animationDelay}ms` : undefined,
|
|
8029
|
+
};
|
|
8030
|
+
positionedChildren.push(m('div', {
|
|
8031
|
+
key: `masonry-item-${index}`,
|
|
8032
|
+
className: 'masonry-item',
|
|
8033
|
+
style: itemStyle,
|
|
8034
|
+
'data-index': index,
|
|
8035
|
+
onclick: onItemClick ? (e) => onItemClick(index, e) : undefined,
|
|
8036
|
+
oncreate: ({ dom }) => {
|
|
8037
|
+
const el = dom;
|
|
8038
|
+
setupResizeObserver();
|
|
8039
|
+
resizeObserver === null || resizeObserver === void 0 ? void 0 : resizeObserver.observe(el);
|
|
8040
|
+
const h = el.offsetHeight;
|
|
8041
|
+
if (itemHeights[index] !== h) {
|
|
8042
|
+
itemHeights[index] = h;
|
|
8043
|
+
m.redraw();
|
|
8044
|
+
}
|
|
8045
|
+
},
|
|
8046
|
+
onremove: ({ dom }) => {
|
|
8047
|
+
resizeObserver === null || resizeObserver === void 0 ? void 0 : resizeObserver.unobserve(dom);
|
|
8048
|
+
},
|
|
8049
|
+
}, child));
|
|
8050
|
+
});
|
|
8051
|
+
containerStyle.height = `${Math.max(...columnHeights) - gap}px`;
|
|
8052
|
+
return m('div', {
|
|
8053
|
+
className: containerClasses,
|
|
8054
|
+
style: containerStyle,
|
|
8055
|
+
oncreate: ({ dom }) => {
|
|
8056
|
+
containerRef = dom;
|
|
8057
|
+
},
|
|
8058
|
+
}, positionedChildren);
|
|
8059
|
+
},
|
|
8060
|
+
};
|
|
8061
|
+
};
|
|
8062
|
+
|
|
8063
|
+
/**
|
|
8064
|
+
* ImageList Component
|
|
8065
|
+
* Displays a collection of images in various grid layouts
|
|
8066
|
+
* Supports standard grid, quilted (varied sizes), masonry, and woven patterns
|
|
8067
|
+
*/
|
|
8068
|
+
const ImageList = () => {
|
|
8069
|
+
const defaultBreakpoints = {
|
|
8070
|
+
xs: 1,
|
|
8071
|
+
sm: 2,
|
|
8072
|
+
md: 3,
|
|
8073
|
+
lg: 4,
|
|
8074
|
+
xl: 5
|
|
8075
|
+
};
|
|
8076
|
+
const getColumnsCount = (cols = 3) => {
|
|
8077
|
+
if (typeof cols === 'number')
|
|
8078
|
+
return cols;
|
|
8079
|
+
const breakpoints = Object.assign(Object.assign({}, defaultBreakpoints), cols);
|
|
8080
|
+
const width = typeof window !== 'undefined' ? window.innerWidth : 1024;
|
|
8081
|
+
if (width >= 1200)
|
|
8082
|
+
return breakpoints.xl || 5;
|
|
8083
|
+
if (width >= 992)
|
|
8084
|
+
return breakpoints.lg || 4;
|
|
8085
|
+
if (width >= 768)
|
|
8086
|
+
return breakpoints.md || 3;
|
|
8087
|
+
if (width >= 576)
|
|
8088
|
+
return breakpoints.sm || 2;
|
|
8089
|
+
return breakpoints.xs || 1;
|
|
8090
|
+
};
|
|
8091
|
+
const handleImageLoad = (e) => {
|
|
8092
|
+
const img = e.target;
|
|
8093
|
+
img.classList.add('loaded');
|
|
8094
|
+
};
|
|
8095
|
+
const handleImageError = (e) => {
|
|
8096
|
+
const img = e.target;
|
|
8097
|
+
img.classList.add('error');
|
|
8098
|
+
// Could set a placeholder image here
|
|
8099
|
+
img.alt = 'Failed to load image';
|
|
8100
|
+
};
|
|
8101
|
+
const renderImage = (item, index, options) => {
|
|
8102
|
+
const { src, alt = '', title, subtitle, onclick, actionButton, className = '', loading = options.loading || 'lazy', aspectRatio, cols = 1, rows = 1, featured = false } = item;
|
|
8103
|
+
const itemClasses = [
|
|
8104
|
+
'image-list-item',
|
|
8105
|
+
featured ? 'image-list-item-featured' : '',
|
|
8106
|
+
onclick ? 'image-list-item-clickable' : '',
|
|
8107
|
+
className
|
|
8108
|
+
].filter(Boolean).join(' ');
|
|
8109
|
+
const itemStyle = {};
|
|
8110
|
+
// Quilted layout with fixed alternating pattern
|
|
8111
|
+
if (options.variant === 'quilted') {
|
|
8112
|
+
if (featured || index % 7 === 0) {
|
|
8113
|
+
itemStyle.gridColumnEnd = 'span 2';
|
|
8114
|
+
itemStyle.gridRowEnd = 'span 2';
|
|
8115
|
+
}
|
|
8116
|
+
else if (index % 3 === 0) {
|
|
8117
|
+
itemStyle.gridColumnEnd = 'span 2';
|
|
8118
|
+
itemStyle.gridRowEnd = 'span 1';
|
|
8119
|
+
}
|
|
8120
|
+
else {
|
|
8121
|
+
itemStyle.gridColumnEnd = 'span 1';
|
|
8122
|
+
itemStyle.gridRowEnd = 'span 1';
|
|
8123
|
+
}
|
|
8124
|
+
}
|
|
8125
|
+
// Woven layout with varied sizes based on item properties
|
|
8126
|
+
if (options.variant === 'woven') {
|
|
8127
|
+
itemStyle.gridColumnEnd = `span ${cols}`;
|
|
8128
|
+
itemStyle.gridRowEnd = `span ${rows}`;
|
|
8129
|
+
}
|
|
8130
|
+
// Masonry layout - prevent break inside items
|
|
8131
|
+
if (options.variant === 'masonry') {
|
|
8132
|
+
itemStyle.breakInside = 'avoid';
|
|
8133
|
+
itemStyle.marginBottom = typeof options.gap === 'number' ? `${options.gap}px` : options.gap || '4px';
|
|
8134
|
+
itemStyle.display = 'inline-block';
|
|
8135
|
+
itemStyle.width = '100%';
|
|
8136
|
+
}
|
|
8137
|
+
// Custom aspect ratio
|
|
8138
|
+
if (aspectRatio && options.variant !== 'masonry') {
|
|
8139
|
+
itemStyle.aspectRatio = aspectRatio.toString();
|
|
8140
|
+
}
|
|
8141
|
+
const handleItemClick = (e) => {
|
|
8142
|
+
if (onclick) {
|
|
8143
|
+
e.preventDefault();
|
|
8144
|
+
onclick(item, e);
|
|
8145
|
+
}
|
|
8146
|
+
};
|
|
8147
|
+
const handleActionClick = (e) => {
|
|
8148
|
+
e.stopPropagation();
|
|
8149
|
+
if (actionButton === null || actionButton === void 0 ? void 0 : actionButton.onclick) {
|
|
8150
|
+
actionButton.onclick(item, e);
|
|
8151
|
+
}
|
|
8152
|
+
};
|
|
8153
|
+
return m(`.${itemClasses}`, {
|
|
8154
|
+
key: `image-${index}`,
|
|
8155
|
+
style: itemStyle,
|
|
8156
|
+
onclick: onclick ? handleItemClick : undefined,
|
|
8157
|
+
role: onclick ? 'button' : undefined,
|
|
8158
|
+
tabindex: onclick ? 0 : undefined,
|
|
8159
|
+
}, [
|
|
8160
|
+
m('.image-list-item-img', [
|
|
8161
|
+
m('img', {
|
|
8162
|
+
src,
|
|
8163
|
+
alt,
|
|
8164
|
+
loading,
|
|
8165
|
+
onload: handleImageLoad,
|
|
8166
|
+
onerror: handleImageError,
|
|
8167
|
+
draggable: false,
|
|
8168
|
+
}),
|
|
8169
|
+
// Loading placeholder
|
|
8170
|
+
m('.image-list-item-placeholder'),
|
|
8171
|
+
]),
|
|
8172
|
+
// Title overlay
|
|
8173
|
+
(options.showTitles && (title || subtitle)) &&
|
|
8174
|
+
m('.image-list-item-bar', [
|
|
8175
|
+
m('.image-list-item-title-wrap', [
|
|
8176
|
+
title && m('.image-list-item-title', title),
|
|
8177
|
+
subtitle && m('.image-list-item-subtitle', subtitle)
|
|
8178
|
+
])
|
|
8179
|
+
]),
|
|
8180
|
+
// Action button
|
|
8181
|
+
(options.showActions && actionButton) &&
|
|
8182
|
+
m('button.image-list-item-action', {
|
|
8183
|
+
class: `image-list-action-${actionButton.position || 'top-right'}`,
|
|
8184
|
+
onclick: handleActionClick,
|
|
8185
|
+
'aria-label': actionButton.ariaLabel || `Action for ${title || alt}`,
|
|
8186
|
+
}, [
|
|
8187
|
+
m('i.material-icons', actionButton.icon)
|
|
8188
|
+
])
|
|
8189
|
+
]);
|
|
8190
|
+
};
|
|
8191
|
+
return {
|
|
8192
|
+
view: ({ attrs }) => {
|
|
8193
|
+
const { items = [], variant = 'standard', cols = 3, gap = 4, rowHeight = 'auto', className = '', showTitles = false, showActions = false } = attrs;
|
|
8194
|
+
const columnsCount = getColumnsCount(cols);
|
|
8195
|
+
const gapValue = typeof gap === 'number' ? `${gap}px` : gap;
|
|
8196
|
+
const containerClasses = [
|
|
8197
|
+
'image-list',
|
|
8198
|
+
`image-list-${variant}`,
|
|
8199
|
+
showTitles ? 'image-list-with-titles' : '',
|
|
8200
|
+
showActions ? 'image-list-with-actions' : '',
|
|
8201
|
+
className
|
|
8202
|
+
].filter(Boolean).join(' ');
|
|
8203
|
+
const containerStyle = {
|
|
8204
|
+
gap: gapValue
|
|
8205
|
+
};
|
|
8206
|
+
// Set up grid based on variant
|
|
8207
|
+
switch (variant) {
|
|
8208
|
+
case 'standard':
|
|
8209
|
+
containerStyle.display = 'grid';
|
|
8210
|
+
containerStyle.gridTemplateColumns = `repeat(${columnsCount}, 1fr)`;
|
|
8211
|
+
if (rowHeight !== 'auto') {
|
|
8212
|
+
containerStyle.gridAutoRows = typeof rowHeight === 'number' ? `${rowHeight}px` : rowHeight;
|
|
8213
|
+
}
|
|
8214
|
+
break;
|
|
8215
|
+
case 'quilted':
|
|
8216
|
+
// Fixed pattern like woven
|
|
8217
|
+
containerStyle.display = 'grid';
|
|
8218
|
+
containerStyle.gridTemplateColumns = `repeat(${Math.max(columnsCount * 2, 4)}, 1fr)`;
|
|
8219
|
+
containerStyle.gridAutoRows = typeof rowHeight === 'number' ? `${rowHeight}px` : '150px';
|
|
8220
|
+
containerStyle.gridAutoFlow = 'dense';
|
|
8221
|
+
break;
|
|
8222
|
+
case 'masonry':
|
|
8223
|
+
// Use CSS columns for masonry effect
|
|
8224
|
+
containerStyle.display = 'block';
|
|
8225
|
+
containerStyle.columnCount = columnsCount;
|
|
8226
|
+
containerStyle.columnGap = gapValue;
|
|
8227
|
+
containerStyle.columnFill = 'balance';
|
|
8228
|
+
containerStyle.orphans = 1;
|
|
8229
|
+
containerStyle.widows = 1;
|
|
8230
|
+
break;
|
|
8231
|
+
case 'woven':
|
|
8232
|
+
// Varied sizes based on item cols/rows
|
|
8233
|
+
containerStyle.display = 'grid';
|
|
8234
|
+
containerStyle.gridTemplateColumns = `repeat(${columnsCount}, 1fr)`;
|
|
8235
|
+
containerStyle.gridAutoRows = typeof rowHeight === 'number' ? `${rowHeight}px` : '200px';
|
|
8236
|
+
containerStyle.gridAutoFlow = 'dense';
|
|
8237
|
+
break;
|
|
8238
|
+
}
|
|
8239
|
+
return m(`.${containerClasses}`, {
|
|
8240
|
+
style: containerStyle
|
|
8241
|
+
}, [
|
|
8242
|
+
items.map((item, index) => renderImage(item, index, attrs))
|
|
8243
|
+
]);
|
|
8244
|
+
}
|
|
8245
|
+
};
|
|
8246
|
+
};
|
|
8247
|
+
|
|
8248
|
+
/**
|
|
8249
|
+
* @fileoverview Core TypeScript utility types for mithril-materialized library
|
|
8250
|
+
* These types improve type safety and developer experience across all components
|
|
8251
|
+
*/
|
|
8252
|
+
/**
|
|
8253
|
+
* Type guard to check if validation result indicates success
|
|
8254
|
+
* @param result - The validation result to check
|
|
8255
|
+
* @returns True if validation passed
|
|
8256
|
+
*/
|
|
8257
|
+
const isValidationSuccess = (result) => result === true || result === '';
|
|
8258
|
+
/**
|
|
8259
|
+
* Type guard to check if validation result indicates an error
|
|
8260
|
+
* @param result - The validation result to check
|
|
8261
|
+
* @returns True if validation failed
|
|
8262
|
+
*/
|
|
8263
|
+
const isValidationError = (result) => !isValidationSuccess(result);
|
|
8264
|
+
// ============================================================================
|
|
8265
|
+
// EXPORTS
|
|
8266
|
+
// ============================================================================
|
|
8267
|
+
// All types are already exported via individual export declarations above
|
|
8268
|
+
|
|
6192
8269
|
exports.AnchorItem = AnchorItem;
|
|
6193
8270
|
exports.Autocomplete = Autocomplete;
|
|
6194
8271
|
exports.Breadcrumb = Breadcrumb;
|
|
@@ -6203,7 +8280,9 @@
|
|
|
6203
8280
|
exports.CollapsibleItem = CollapsibleItem;
|
|
6204
8281
|
exports.Collection = Collection;
|
|
6205
8282
|
exports.ColorInput = ColorInput;
|
|
8283
|
+
exports.DataTable = DataTable;
|
|
6206
8284
|
exports.DatePicker = DatePicker;
|
|
8285
|
+
exports.DoubleRangeSlider = DoubleRangeSlider;
|
|
6207
8286
|
exports.Dropdown = Dropdown;
|
|
6208
8287
|
exports.EmailInput = EmailInput;
|
|
6209
8288
|
exports.FileInput = FileInput;
|
|
@@ -6212,16 +8291,20 @@
|
|
|
6212
8291
|
exports.FloatingActionButton = FloatingActionButton;
|
|
6213
8292
|
exports.HelperText = HelperText;
|
|
6214
8293
|
exports.Icon = Icon;
|
|
8294
|
+
exports.ImageList = ImageList;
|
|
6215
8295
|
exports.InputCheckbox = InputCheckbox;
|
|
6216
8296
|
exports.Label = Label;
|
|
6217
8297
|
exports.LargeButton = LargeButton;
|
|
6218
8298
|
exports.ListItem = ListItem;
|
|
6219
8299
|
exports.Mandatory = Mandatory;
|
|
8300
|
+
exports.Masonry = Masonry;
|
|
6220
8301
|
exports.MaterialBox = MaterialBox;
|
|
8302
|
+
exports.MaterialIcon = MaterialIcon;
|
|
6221
8303
|
exports.ModalPanel = ModalPanel;
|
|
6222
8304
|
exports.NumberInput = NumberInput;
|
|
6223
8305
|
exports.Options = Options;
|
|
6224
8306
|
exports.Pagination = Pagination;
|
|
8307
|
+
exports.PaginationControls = PaginationControls;
|
|
6225
8308
|
exports.Parallax = Parallax;
|
|
6226
8309
|
exports.PasswordInput = PasswordInput;
|
|
6227
8310
|
exports.Pushpin = Pushpin;
|
|
@@ -6236,6 +8319,7 @@
|
|
|
6236
8319
|
exports.Sidenav = Sidenav;
|
|
6237
8320
|
exports.SidenavItem = SidenavItem;
|
|
6238
8321
|
exports.SidenavManager = SidenavManager;
|
|
8322
|
+
exports.SingleRangeSlider = SingleRangeSlider;
|
|
6239
8323
|
exports.SmallButton = SmallButton;
|
|
6240
8324
|
exports.Stepper = Stepper;
|
|
6241
8325
|
exports.SubmitButton = SubmitButton;
|
|
@@ -6247,10 +8331,12 @@
|
|
|
6247
8331
|
exports.ThemeSwitcher = ThemeSwitcher;
|
|
6248
8332
|
exports.ThemeToggle = ThemeToggle;
|
|
6249
8333
|
exports.TimePicker = TimePicker;
|
|
8334
|
+
exports.Timeline = Timeline;
|
|
6250
8335
|
exports.Toast = Toast;
|
|
6251
8336
|
exports.ToastComponent = ToastComponent;
|
|
6252
8337
|
exports.Tooltip = Tooltip;
|
|
6253
8338
|
exports.TooltipComponent = TooltipComponent;
|
|
8339
|
+
exports.TreeView = TreeView;
|
|
6254
8340
|
exports.UrlInput = UrlInput;
|
|
6255
8341
|
exports.Wizard = Wizard;
|
|
6256
8342
|
exports.createBreadcrumb = createBreadcrumb;
|
|
@@ -6258,6 +8344,8 @@
|
|
|
6258
8344
|
exports.initPushpins = initPushpins;
|
|
6259
8345
|
exports.initTooltips = initTooltips;
|
|
6260
8346
|
exports.isNumeric = isNumeric;
|
|
8347
|
+
exports.isValidationError = isValidationError;
|
|
8348
|
+
exports.isValidationSuccess = isValidationSuccess;
|
|
6261
8349
|
exports.padLeft = padLeft;
|
|
6262
8350
|
exports.range = range;
|
|
6263
8351
|
exports.toast = toast;
|