mithril-materialized 2.0.0-beta.10 → 2.0.0-beta.12
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 +41 -20
- package/dist/advanced.css +6 -6
- package/dist/button.d.ts +56 -11
- package/dist/components.css +601 -6
- package/dist/core.css +13 -13
- package/dist/datatable.d.ts +291 -0
- package/dist/forms.css +13 -13
- package/dist/icon.d.ts +2 -2
- package/dist/index.css +622 -16
- package/dist/index.d.ts +4 -0
- package/dist/index.esm.js +1435 -518
- package/dist/index.js +1440 -517
- package/dist/index.min.css +2 -2
- package/dist/index.umd.js +1440 -517
- package/dist/input.d.ts +0 -1
- package/dist/material-icon.d.ts +3 -0
- package/dist/treeview.d.ts +39 -0
- package/dist/types.d.ts +226 -0
- package/dist/utilities.css +16 -9
- package/package.json +6 -3
- package/sass/components/_cards.scss +10 -3
- package/sass/components/_datatable.scss +417 -0
- package/sass/components/_global.scss +6 -6
- package/sass/components/_treeview.scss +353 -0
- package/sass/components/forms/_range.scss +5 -5
- package/sass/components/forms/_select.scss +1 -1
- package/sass/materialize.scss +2 -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),
|
|
@@ -2039,248 +2057,6 @@
|
|
|
2039
2057
|
};
|
|
2040
2058
|
};
|
|
2041
2059
|
|
|
2042
|
-
/** Pure TypeScript Dropdown component - no Materialize dependencies */
|
|
2043
|
-
const Dropdown = () => {
|
|
2044
|
-
const state = {
|
|
2045
|
-
isOpen: false,
|
|
2046
|
-
initialValue: undefined,
|
|
2047
|
-
id: '',
|
|
2048
|
-
focusedIndex: -1,
|
|
2049
|
-
inputRef: null,
|
|
2050
|
-
dropdownRef: null,
|
|
2051
|
-
};
|
|
2052
|
-
const handleKeyDown = (e, items, onchange) => {
|
|
2053
|
-
const availableItems = items.filter((item) => !item.divider && !item.disabled);
|
|
2054
|
-
switch (e.key) {
|
|
2055
|
-
case 'ArrowDown':
|
|
2056
|
-
e.preventDefault();
|
|
2057
|
-
if (!state.isOpen) {
|
|
2058
|
-
state.isOpen = true;
|
|
2059
|
-
state.focusedIndex = 0;
|
|
2060
|
-
}
|
|
2061
|
-
else {
|
|
2062
|
-
state.focusedIndex = Math.min(state.focusedIndex + 1, availableItems.length - 1);
|
|
2063
|
-
}
|
|
2064
|
-
break;
|
|
2065
|
-
case 'ArrowUp':
|
|
2066
|
-
e.preventDefault();
|
|
2067
|
-
if (state.isOpen) {
|
|
2068
|
-
state.focusedIndex = Math.max(state.focusedIndex - 1, 0);
|
|
2069
|
-
}
|
|
2070
|
-
break;
|
|
2071
|
-
case 'Enter':
|
|
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);
|
|
2082
|
-
}
|
|
2083
|
-
else if (!state.isOpen) {
|
|
2084
|
-
state.isOpen = true;
|
|
2085
|
-
state.focusedIndex = 0;
|
|
2086
|
-
}
|
|
2087
|
-
break;
|
|
2088
|
-
case 'Escape':
|
|
2089
|
-
e.preventDefault();
|
|
2090
|
-
state.isOpen = false;
|
|
2091
|
-
state.focusedIndex = -1;
|
|
2092
|
-
break;
|
|
2093
|
-
}
|
|
2094
|
-
};
|
|
2095
|
-
return {
|
|
2096
|
-
oninit: ({ attrs: { id = uniqueId(), initialValue, checkedId } }) => {
|
|
2097
|
-
state.id = id;
|
|
2098
|
-
state.initialValue = initialValue || checkedId;
|
|
2099
|
-
// Mithril will handle click events through the component structure
|
|
2100
|
-
},
|
|
2101
|
-
view: ({ attrs: { key, label, onchange, disabled = false, items, iconName, helperText, style, className = 'col s12' }, }) => {
|
|
2102
|
-
const { initialValue } = state;
|
|
2103
|
-
const selectedItem = initialValue
|
|
2104
|
-
? items.filter((i) => (i.id ? i.id === initialValue : i.label === initialValue)).shift()
|
|
2105
|
-
: undefined;
|
|
2106
|
-
const title = selectedItem ? selectedItem.label : label || 'Select';
|
|
2107
|
-
const availableItems = items.filter((item) => !item.divider && !item.disabled);
|
|
2108
|
-
return m('.dropdown-wrapper.input-field', { className, key, style }, [
|
|
2109
|
-
iconName ? m('i.material-icons.prefix', iconName) : undefined,
|
|
2110
|
-
m(HelperText, { helperText }),
|
|
2111
|
-
m('.select-wrapper', {
|
|
2112
|
-
onclick: disabled
|
|
2113
|
-
? undefined
|
|
2114
|
-
: () => {
|
|
2115
|
-
state.isOpen = !state.isOpen;
|
|
2116
|
-
state.focusedIndex = state.isOpen ? 0 : -1;
|
|
2117
|
-
},
|
|
2118
|
-
onkeydown: disabled ? undefined : (e) => handleKeyDown(e, items, onchange),
|
|
2119
|
-
tabindex: disabled ? -1 : 0,
|
|
2120
|
-
'aria-expanded': state.isOpen ? 'true' : 'false',
|
|
2121
|
-
'aria-haspopup': 'listbox',
|
|
2122
|
-
role: 'combobox',
|
|
2123
|
-
}, [
|
|
2124
|
-
m('input[type=text][readonly=true].select-dropdown.dropdown-trigger', {
|
|
2125
|
-
id: state.id,
|
|
2126
|
-
value: title,
|
|
2127
|
-
oncreate: ({ dom }) => {
|
|
2128
|
-
state.inputRef = dom;
|
|
2129
|
-
},
|
|
2130
|
-
onclick: (e) => {
|
|
2131
|
-
e.preventDefault();
|
|
2132
|
-
e.stopPropagation();
|
|
2133
|
-
if (!disabled) {
|
|
2134
|
-
state.isOpen = !state.isOpen;
|
|
2135
|
-
state.focusedIndex = state.isOpen ? 0 : -1;
|
|
2136
|
-
}
|
|
2137
|
-
},
|
|
2138
|
-
}),
|
|
2139
|
-
// Dropdown Menu using Select component's positioning logic
|
|
2140
|
-
state.isOpen &&
|
|
2141
|
-
m('ul.dropdown-content.select-dropdown', {
|
|
2142
|
-
tabindex: 0,
|
|
2143
|
-
role: 'listbox',
|
|
2144
|
-
'aria-labelledby': state.id,
|
|
2145
|
-
oncreate: ({ dom }) => {
|
|
2146
|
-
state.dropdownRef = dom;
|
|
2147
|
-
},
|
|
2148
|
-
onremove: () => {
|
|
2149
|
-
state.dropdownRef = null;
|
|
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
|
-
});
|
|
2159
|
-
}
|
|
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
|
-
},
|
|
2273
|
-
onclick: (e) => {
|
|
2274
|
-
e.stopPropagation();
|
|
2275
|
-
if (button.onClick)
|
|
2276
|
-
button.onClick(e);
|
|
2277
|
-
},
|
|
2278
|
-
}, m('i.material-icons', { className: button.iconClass }, button.iconName))))),
|
|
2279
|
-
]));
|
|
2280
|
-
},
|
|
2281
|
-
};
|
|
2282
|
-
};
|
|
2283
|
-
|
|
2284
2060
|
/** Character counter component that tracks text length against maxLength */
|
|
2285
2061
|
const CharacterCounter = () => {
|
|
2286
2062
|
return {
|
|
@@ -2448,13 +2224,14 @@
|
|
|
2448
2224
|
var _a;
|
|
2449
2225
|
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
2226
|
// const attributes = toAttrs(params);
|
|
2451
|
-
const cn = [newRow ? 'clear' : '', defaultClass, className].filter(Boolean).join(' ').trim();
|
|
2227
|
+
const cn = [newRow ? 'clear' : '', defaultClass, className].filter(Boolean).join(' ').trim() || undefined;
|
|
2452
2228
|
const isActive = state.active || ((_a = state.inputElement) === null || _a === void 0 ? void 0 : _a.value) || placeholder || type === 'color' || type === 'range'
|
|
2453
2229
|
? true
|
|
2454
2230
|
: false;
|
|
2455
2231
|
return m('.input-field', { className: cn, style }, [
|
|
2456
2232
|
iconName ? m('i.material-icons.prefix', iconName) : undefined,
|
|
2457
|
-
m('input.validate', Object.assign(Object.assign({}, params), { type, tabindex: 0, id,
|
|
2233
|
+
m('input.validate', Object.assign(Object.assign({}, params), { type, tabindex: 0, id,
|
|
2234
|
+
placeholder,
|
|
2458
2235
|
// attributes,
|
|
2459
2236
|
oncreate: ({ dom }) => {
|
|
2460
2237
|
const input = (state.inputElement = dom);
|
|
@@ -2658,11 +2435,938 @@
|
|
|
2658
2435
|
i.value = '';
|
|
2659
2436
|
onchange && onchange({});
|
|
2660
2437
|
},
|
|
2661
|
-
}, m(MaterialIcon, {
|
|
2662
|
-
name: 'close',
|
|
2663
|
-
className: 'close',
|
|
2664
|
-
})),
|
|
2665
|
-
]);
|
|
2438
|
+
}, m(MaterialIcon, {
|
|
2439
|
+
name: 'close',
|
|
2440
|
+
className: 'close',
|
|
2441
|
+
})),
|
|
2442
|
+
]);
|
|
2443
|
+
},
|
|
2444
|
+
};
|
|
2445
|
+
};
|
|
2446
|
+
|
|
2447
|
+
/** Component to show a check box */
|
|
2448
|
+
const InputCheckbox = () => {
|
|
2449
|
+
return {
|
|
2450
|
+
view: ({ attrs: { className = 'col s12', onchange, label, checked, disabled, description, style, inputId } }) => {
|
|
2451
|
+
const checkboxId = inputId || uniqueId();
|
|
2452
|
+
return m(`p`, { className, style }, m('label', { for: checkboxId }, [
|
|
2453
|
+
m('input[type=checkbox][tabindex=0]', {
|
|
2454
|
+
id: checkboxId,
|
|
2455
|
+
checked,
|
|
2456
|
+
disabled,
|
|
2457
|
+
onclick: onchange
|
|
2458
|
+
? (e) => {
|
|
2459
|
+
if (e.target && typeof e.target.checked !== 'undefined') {
|
|
2460
|
+
onchange(e.target.checked);
|
|
2461
|
+
}
|
|
2462
|
+
}
|
|
2463
|
+
: undefined,
|
|
2464
|
+
}),
|
|
2465
|
+
label ? (typeof label === 'string' ? m('span', label) : label) : undefined,
|
|
2466
|
+
]), description && m(HelperText, { className: 'input-checkbox-desc', helperText: description }));
|
|
2467
|
+
},
|
|
2468
|
+
};
|
|
2469
|
+
};
|
|
2470
|
+
/** A list of checkboxes */
|
|
2471
|
+
const Options = () => {
|
|
2472
|
+
const state = {};
|
|
2473
|
+
const isChecked = (id) => state.checkedIds.indexOf(id) >= 0;
|
|
2474
|
+
const selectAll = (options, callback) => {
|
|
2475
|
+
const allIds = options.map((option) => option.id);
|
|
2476
|
+
state.checkedIds = [...allIds];
|
|
2477
|
+
if (callback)
|
|
2478
|
+
callback(allIds);
|
|
2479
|
+
};
|
|
2480
|
+
const selectNone = (callback) => {
|
|
2481
|
+
state.checkedIds = [];
|
|
2482
|
+
if (callback)
|
|
2483
|
+
callback([]);
|
|
2484
|
+
};
|
|
2485
|
+
return {
|
|
2486
|
+
oninit: ({ attrs: { initialValue, checkedId, id } }) => {
|
|
2487
|
+
const iv = checkedId || initialValue;
|
|
2488
|
+
state.checkedId = checkedId;
|
|
2489
|
+
state.checkedIds = iv ? (iv instanceof Array ? [...iv] : [iv]) : [];
|
|
2490
|
+
state.componentId = id || uniqueId();
|
|
2491
|
+
},
|
|
2492
|
+
view: ({ attrs: { label, options, description, className = 'col s12', style, disabled, checkboxClass, newRow, isMandatory, layout = 'vertical', showSelectAll = false, onchange: callback, }, }) => {
|
|
2493
|
+
const onchange = callback
|
|
2494
|
+
? (propId, checked) => {
|
|
2495
|
+
const checkedIds = state.checkedIds.filter((i) => i !== propId);
|
|
2496
|
+
if (checked) {
|
|
2497
|
+
checkedIds.push(propId);
|
|
2498
|
+
}
|
|
2499
|
+
state.checkedIds = checkedIds;
|
|
2500
|
+
callback(checkedIds);
|
|
2501
|
+
}
|
|
2502
|
+
: undefined;
|
|
2503
|
+
const cn = [newRow ? 'clear' : '', className].filter(Boolean).join(' ').trim() || undefined;
|
|
2504
|
+
const optionsContent = layout === 'horizontal'
|
|
2505
|
+
? m('div.grid-container', options.map((option) => m(InputCheckbox, {
|
|
2506
|
+
disabled: disabled || option.disabled,
|
|
2507
|
+
label: option.label,
|
|
2508
|
+
onchange: onchange ? (v) => onchange(option.id, v) : undefined,
|
|
2509
|
+
className: option.className || checkboxClass,
|
|
2510
|
+
checked: isChecked(option.id),
|
|
2511
|
+
description: option.description,
|
|
2512
|
+
inputId: `${state.componentId}-${option.id}`,
|
|
2513
|
+
})))
|
|
2514
|
+
: options.map((option) => m(InputCheckbox, {
|
|
2515
|
+
disabled: disabled || option.disabled,
|
|
2516
|
+
label: option.label,
|
|
2517
|
+
onchange: onchange ? (v) => onchange(option.id, v) : undefined,
|
|
2518
|
+
className: option.className || checkboxClass,
|
|
2519
|
+
checked: isChecked(option.id),
|
|
2520
|
+
description: option.description,
|
|
2521
|
+
inputId: `${state.componentId}-${option.id}`,
|
|
2522
|
+
}));
|
|
2523
|
+
return m('div', { id: state.componentId, className: cn, style }, [
|
|
2524
|
+
label && m('h5.form-group-label', label + (isMandatory ? ' *' : '')),
|
|
2525
|
+
showSelectAll &&
|
|
2526
|
+
m('div.select-all-controls', { style: 'margin-bottom: 10px;' }, [
|
|
2527
|
+
m('a', {
|
|
2528
|
+
href: '#',
|
|
2529
|
+
onclick: (e) => {
|
|
2530
|
+
e.preventDefault();
|
|
2531
|
+
selectAll(options, callback);
|
|
2532
|
+
},
|
|
2533
|
+
style: 'margin-right: 15px;',
|
|
2534
|
+
}, 'Select All'),
|
|
2535
|
+
m('a', {
|
|
2536
|
+
href: '#',
|
|
2537
|
+
onclick: (e) => {
|
|
2538
|
+
e.preventDefault();
|
|
2539
|
+
selectNone(callback);
|
|
2540
|
+
},
|
|
2541
|
+
}, 'Select None'),
|
|
2542
|
+
]),
|
|
2543
|
+
description && m(HelperText, { helperText: description }),
|
|
2544
|
+
m('form', { action: '#' }, optionsContent),
|
|
2545
|
+
]);
|
|
2546
|
+
},
|
|
2547
|
+
};
|
|
2548
|
+
};
|
|
2549
|
+
|
|
2550
|
+
const GlobalSearch = () => {
|
|
2551
|
+
return {
|
|
2552
|
+
view: ({ attrs }) => {
|
|
2553
|
+
const { searchPlaceholder, onSearch, i18n } = attrs;
|
|
2554
|
+
return m('.datatable-global-search', m(TextInput, {
|
|
2555
|
+
className: 'datatable-search',
|
|
2556
|
+
label: (i18n === null || i18n === void 0 ? void 0 : i18n.search) || 'Search',
|
|
2557
|
+
placeholder: searchPlaceholder || (i18n === null || i18n === void 0 ? void 0 : i18n.searchPlaceholder) || 'Search table...',
|
|
2558
|
+
oninput: onSearch,
|
|
2559
|
+
}));
|
|
2560
|
+
},
|
|
2561
|
+
};
|
|
2562
|
+
};
|
|
2563
|
+
const TableHeader = () => {
|
|
2564
|
+
return {
|
|
2565
|
+
view: ({ attrs }) => {
|
|
2566
|
+
const { columns, selection, sort, allSelected, helpers } = attrs;
|
|
2567
|
+
return m('thead', m('tr', [
|
|
2568
|
+
// Selection column header
|
|
2569
|
+
...(selection && selection.mode !== 'none'
|
|
2570
|
+
? [
|
|
2571
|
+
m('th.selection-checkbox', [
|
|
2572
|
+
selection.mode === 'multiple' &&
|
|
2573
|
+
m(InputCheckbox, {
|
|
2574
|
+
checked: allSelected,
|
|
2575
|
+
onchange: helpers.handleSelectAll,
|
|
2576
|
+
className: '',
|
|
2577
|
+
}),
|
|
2578
|
+
]),
|
|
2579
|
+
]
|
|
2580
|
+
: []),
|
|
2581
|
+
// Regular columns
|
|
2582
|
+
...columns.map((column) => {
|
|
2583
|
+
const isSorted = (sort === null || sort === void 0 ? void 0 : sort.column) === column.key;
|
|
2584
|
+
const sortDirection = isSorted ? sort.direction : null;
|
|
2585
|
+
return m('th', {
|
|
2586
|
+
class: [
|
|
2587
|
+
column.headerClassName,
|
|
2588
|
+
column.sortable ? 'sortable' : '',
|
|
2589
|
+
isSorted ? `sorted-${sortDirection}` : '',
|
|
2590
|
+
]
|
|
2591
|
+
.filter(Boolean)
|
|
2592
|
+
.join(' '),
|
|
2593
|
+
style: column.width ? { width: column.width } : undefined,
|
|
2594
|
+
onclick: column.sortable ? () => helpers.handleSort(column.key) : undefined,
|
|
2595
|
+
}, [
|
|
2596
|
+
column.title,
|
|
2597
|
+
column.sortable &&
|
|
2598
|
+
m('.sort-indicators', [
|
|
2599
|
+
m('span.sort-icon.sort-asc', {
|
|
2600
|
+
className: isSorted && sortDirection === 'asc' ? 'active' : '',
|
|
2601
|
+
}, '▲'),
|
|
2602
|
+
m('span.sort-icon.sort-desc', {
|
|
2603
|
+
className: isSorted && sortDirection === 'desc' ? 'active' : '',
|
|
2604
|
+
}, '▼'),
|
|
2605
|
+
]),
|
|
2606
|
+
]);
|
|
2607
|
+
}),
|
|
2608
|
+
]));
|
|
2609
|
+
},
|
|
2610
|
+
};
|
|
2611
|
+
};
|
|
2612
|
+
const TableRow = () => {
|
|
2613
|
+
return {
|
|
2614
|
+
view: ({ attrs }) => {
|
|
2615
|
+
const { row, index, columns, selection, onRowClick, onRowDoubleClick, getRowClassName, helpers, data } = attrs;
|
|
2616
|
+
// Calculate the original data index for the row key
|
|
2617
|
+
const originalIndex = data.findIndex((originalRow) => originalRow === row);
|
|
2618
|
+
const rowKey = (selection === null || selection === void 0 ? void 0 : selection.getRowKey(row, originalIndex)) || String(originalIndex);
|
|
2619
|
+
const isSelected = (selection === null || selection === void 0 ? void 0 : selection.selectedKeys.includes(rowKey)) || false;
|
|
2620
|
+
return m('tr', {
|
|
2621
|
+
class: [getRowClassName ? getRowClassName(row, index) : '', isSelected ? 'selected' : '']
|
|
2622
|
+
.filter(Boolean)
|
|
2623
|
+
.join(' ') || undefined,
|
|
2624
|
+
onclick: onRowClick ? (e) => onRowClick(row, index, e) : undefined,
|
|
2625
|
+
ondblclick: onRowDoubleClick ? (e) => onRowDoubleClick(row, index, e) : undefined,
|
|
2626
|
+
}, [
|
|
2627
|
+
// Selection column
|
|
2628
|
+
selection &&
|
|
2629
|
+
selection.mode !== 'none' &&
|
|
2630
|
+
m('td.selection-checkbox', [
|
|
2631
|
+
m(InputCheckbox, {
|
|
2632
|
+
checked: isSelected,
|
|
2633
|
+
onchange: (checked) => helpers.handleSelectionChange(rowKey, checked),
|
|
2634
|
+
className: '',
|
|
2635
|
+
}),
|
|
2636
|
+
]),
|
|
2637
|
+
columns.map((column) => {
|
|
2638
|
+
const value = helpers.getCellValue(row, column);
|
|
2639
|
+
let cellContent;
|
|
2640
|
+
if (column.cellRenderer) {
|
|
2641
|
+
cellContent = m(column.cellRenderer, {
|
|
2642
|
+
value,
|
|
2643
|
+
row,
|
|
2644
|
+
index,
|
|
2645
|
+
column,
|
|
2646
|
+
});
|
|
2647
|
+
}
|
|
2648
|
+
else if (column.render) {
|
|
2649
|
+
// Backward compatibility with deprecated render function
|
|
2650
|
+
cellContent = column.render(value, row, index);
|
|
2651
|
+
}
|
|
2652
|
+
else {
|
|
2653
|
+
cellContent = String(value || '');
|
|
2654
|
+
}
|
|
2655
|
+
return m('td', {
|
|
2656
|
+
class: [column.className, column.align ? `align-${column.align}` : ''].filter(Boolean).join(' ') ||
|
|
2657
|
+
undefined,
|
|
2658
|
+
}, cellContent);
|
|
2659
|
+
}),
|
|
2660
|
+
]);
|
|
2661
|
+
},
|
|
2662
|
+
};
|
|
2663
|
+
};
|
|
2664
|
+
/**
|
|
2665
|
+
* Standalone Pagination Controls component
|
|
2666
|
+
*
|
|
2667
|
+
* Provides navigation controls for paginated data with customizable text labels.
|
|
2668
|
+
* Includes first page, previous page, next page, last page buttons and page info display.
|
|
2669
|
+
* Can be used independently of DataTable for any paginated content.
|
|
2670
|
+
*
|
|
2671
|
+
* @example
|
|
2672
|
+
* ```typescript
|
|
2673
|
+
* m(PaginationControls, {
|
|
2674
|
+
* pagination: { page: 0, pageSize: 10, total: 100 },
|
|
2675
|
+
* onPaginationChange: (newPagination) => console.log('Page changed:', newPagination),
|
|
2676
|
+
* i18n: { showing: 'Showing', to: 'to', of: 'of', entries: 'entries', page: 'Page' }
|
|
2677
|
+
* })
|
|
2678
|
+
* ```
|
|
2679
|
+
*/
|
|
2680
|
+
const PaginationControls = () => {
|
|
2681
|
+
return {
|
|
2682
|
+
view: ({ attrs }) => {
|
|
2683
|
+
const { pagination, onPaginationChange, i18n } = attrs;
|
|
2684
|
+
if (!pagination)
|
|
2685
|
+
return null;
|
|
2686
|
+
const { page, pageSize, total } = pagination;
|
|
2687
|
+
const totalPages = Math.ceil(total / pageSize);
|
|
2688
|
+
const startItem = page * pageSize + 1;
|
|
2689
|
+
const endItem = Math.min((page + 1) * pageSize, total);
|
|
2690
|
+
const showingText = (i18n === null || i18n === void 0 ? void 0 : i18n.showing) || 'Showing';
|
|
2691
|
+
const toText = (i18n === null || i18n === void 0 ? void 0 : i18n.to) || 'to';
|
|
2692
|
+
const ofText = (i18n === null || i18n === void 0 ? void 0 : i18n.of) || 'of';
|
|
2693
|
+
const entriesText = (i18n === null || i18n === void 0 ? void 0 : i18n.entries) || 'entries';
|
|
2694
|
+
const pageText = (i18n === null || i18n === void 0 ? void 0 : i18n.page) || 'Page';
|
|
2695
|
+
return m('.datatable-pagination', [
|
|
2696
|
+
m('.pagination-info', `${showingText} ${startItem} ${toText} ${endItem} ${ofText} ${total} ${entriesText}`),
|
|
2697
|
+
m('.pagination-controls', [
|
|
2698
|
+
m('button.btn-flat', {
|
|
2699
|
+
disabled: page === 0,
|
|
2700
|
+
onclick: () => onPaginationChange(Object.assign(Object.assign({}, pagination), { page: 0 })),
|
|
2701
|
+
}, '⏮'),
|
|
2702
|
+
m('button.btn-flat', {
|
|
2703
|
+
disabled: page === 0,
|
|
2704
|
+
onclick: () => onPaginationChange(Object.assign(Object.assign({}, pagination), { page: page - 1 })),
|
|
2705
|
+
}, '◀'),
|
|
2706
|
+
m('span.page-info', `${pageText} ${page + 1} ${ofText} ${totalPages}`),
|
|
2707
|
+
m('button.btn-flat', {
|
|
2708
|
+
disabled: page >= totalPages - 1,
|
|
2709
|
+
onclick: () => onPaginationChange(Object.assign(Object.assign({}, pagination), { page: page + 1 })),
|
|
2710
|
+
}, '▶'),
|
|
2711
|
+
m('button.btn-flat', {
|
|
2712
|
+
disabled: page >= totalPages - 1,
|
|
2713
|
+
onclick: () => onPaginationChange(Object.assign(Object.assign({}, pagination), { page: totalPages - 1 })),
|
|
2714
|
+
}, '⏭'),
|
|
2715
|
+
]),
|
|
2716
|
+
]);
|
|
2717
|
+
},
|
|
2718
|
+
};
|
|
2719
|
+
};
|
|
2720
|
+
const TableContent = () => {
|
|
2721
|
+
return {
|
|
2722
|
+
view: ({ attrs: contentAttrs }) => {
|
|
2723
|
+
const { processedData, tableClasses, columns, selection, internalSort, allSelected, someSelected, helpers, onRowClick, onRowDoubleClick, getRowClassName, data, } = contentAttrs;
|
|
2724
|
+
return m('table', {
|
|
2725
|
+
class: tableClasses,
|
|
2726
|
+
}, m(TableHeader(), {
|
|
2727
|
+
columns,
|
|
2728
|
+
selection,
|
|
2729
|
+
sort: internalSort,
|
|
2730
|
+
allSelected,
|
|
2731
|
+
someSelected,
|
|
2732
|
+
helpers,
|
|
2733
|
+
}), m('tbody', processedData.map((row, index) => m(TableRow(), {
|
|
2734
|
+
key: (selection === null || selection === void 0 ? void 0 : selection.getRowKey(row, data.findIndex((originalRow) => originalRow === row))) || index,
|
|
2735
|
+
row,
|
|
2736
|
+
index,
|
|
2737
|
+
columns,
|
|
2738
|
+
selection,
|
|
2739
|
+
onRowClick,
|
|
2740
|
+
onRowDoubleClick,
|
|
2741
|
+
getRowClassName,
|
|
2742
|
+
helpers,
|
|
2743
|
+
data,
|
|
2744
|
+
}))));
|
|
2745
|
+
},
|
|
2746
|
+
};
|
|
2747
|
+
};
|
|
2748
|
+
/**
|
|
2749
|
+
* A comprehensive data table component with sorting, filtering, pagination, and selection capabilities.
|
|
2750
|
+
*
|
|
2751
|
+
* @template T The type of data objects displayed in each row
|
|
2752
|
+
*
|
|
2753
|
+
* @description
|
|
2754
|
+
* The DataTable component provides a feature-rich interface for displaying and interacting with tabular data.
|
|
2755
|
+
* It supports both controlled and uncontrolled modes for all interactive features.
|
|
2756
|
+
*
|
|
2757
|
+
* **Key Features:**
|
|
2758
|
+
* - Sorting: Click column headers to sort data ascending/descending
|
|
2759
|
+
* - Filtering: Global search across filterable columns
|
|
2760
|
+
* - Pagination: Navigate through large datasets with customizable page sizes
|
|
2761
|
+
* - Selection: Single or multiple row selection with callbacks
|
|
2762
|
+
* - Custom rendering: Use cellRenderer for complex cell content
|
|
2763
|
+
* - Responsive: Adapts to different screen sizes
|
|
2764
|
+
* - Internationalization: Customize all UI text
|
|
2765
|
+
* - Accessibility: Proper ARIA attributes and keyboard navigation
|
|
2766
|
+
*
|
|
2767
|
+
* @example Basic usage
|
|
2768
|
+
* ```typescript
|
|
2769
|
+
* interface User { id: number; name: string; email: string; }
|
|
2770
|
+
* const users: User[] = [...];
|
|
2771
|
+
* const columns: DataTableColumn<User>[] = [
|
|
2772
|
+
* { key: 'name', title: 'Name', field: 'name', sortable: true, filterable: true },
|
|
2773
|
+
* { key: 'email', title: 'Email', field: 'email', sortable: true, filterable: true }
|
|
2774
|
+
* ];
|
|
2775
|
+
*
|
|
2776
|
+
* return m(DataTable<User>, { data: users, columns });
|
|
2777
|
+
* ```
|
|
2778
|
+
*
|
|
2779
|
+
* @example Advanced usage with all features
|
|
2780
|
+
* ```typescript
|
|
2781
|
+
* return m(DataTable<User>, {
|
|
2782
|
+
* data: users,
|
|
2783
|
+
* columns,
|
|
2784
|
+
* title: 'User Management',
|
|
2785
|
+
* striped: true,
|
|
2786
|
+
* hoverable: true,
|
|
2787
|
+
* height: 400,
|
|
2788
|
+
*
|
|
2789
|
+
* // Pagination
|
|
2790
|
+
* pagination: { page: 0, pageSize: 10, total: users.length },
|
|
2791
|
+
* onPaginationChange: (pagination) => console.log('Page changed:', pagination),
|
|
2792
|
+
*
|
|
2793
|
+
* // Selection
|
|
2794
|
+
* selection: {
|
|
2795
|
+
* mode: 'multiple',
|
|
2796
|
+
* selectedKeys: [],
|
|
2797
|
+
* getRowKey: (user) => String(user.id),
|
|
2798
|
+
* onSelectionChange: (keys, selectedUsers) => console.log('Selection:', selectedUsers)
|
|
2799
|
+
* },
|
|
2800
|
+
*
|
|
2801
|
+
* // Search
|
|
2802
|
+
* enableGlobalSearch: true,
|
|
2803
|
+
* searchPlaceholder: 'Search users...',
|
|
2804
|
+
*
|
|
2805
|
+
* // Events
|
|
2806
|
+
* onRowClick: (user, index, event) => console.log('Clicked:', user),
|
|
2807
|
+
* onRowDoubleClick: (user) => editUser(user),
|
|
2808
|
+
*
|
|
2809
|
+
* // Styling
|
|
2810
|
+
* getRowClassName: (user) => user.active ? '' : 'inactive-row'
|
|
2811
|
+
* });
|
|
2812
|
+
* ```
|
|
2813
|
+
*
|
|
2814
|
+
* @returns A Mithril component that renders the data table
|
|
2815
|
+
*/
|
|
2816
|
+
const DataTable = () => {
|
|
2817
|
+
const state = {
|
|
2818
|
+
internalSort: undefined,
|
|
2819
|
+
internalFilter: undefined,
|
|
2820
|
+
internalPagination: undefined,
|
|
2821
|
+
processedData: [],
|
|
2822
|
+
tableId: '',
|
|
2823
|
+
// Performance optimization caches
|
|
2824
|
+
lastProcessedHash: ''};
|
|
2825
|
+
// Helper functions
|
|
2826
|
+
const quickDataHash = (data) => {
|
|
2827
|
+
if (data.length === 0)
|
|
2828
|
+
return '0';
|
|
2829
|
+
if (data.length === 1)
|
|
2830
|
+
return '1';
|
|
2831
|
+
// Sample first, middle, and last items for quick hash
|
|
2832
|
+
const first = JSON.stringify(data[0]);
|
|
2833
|
+
const middle = data.length > 2 ? JSON.stringify(data[Math.floor(data.length / 2)]) : '';
|
|
2834
|
+
const last = JSON.stringify(data[data.length - 1]);
|
|
2835
|
+
return `${data.length}-${first.length}-${middle.length}-${last.length}`;
|
|
2836
|
+
};
|
|
2837
|
+
const getDataHash = (attrs) => {
|
|
2838
|
+
const { data, sort, filter, pagination } = attrs;
|
|
2839
|
+
const { internalSort, internalFilter, internalPagination } = state;
|
|
2840
|
+
const hashInputs = {
|
|
2841
|
+
dataLength: data.length,
|
|
2842
|
+
dataHash: quickDataHash(data),
|
|
2843
|
+
sort: sort || internalSort,
|
|
2844
|
+
filter: filter || internalFilter,
|
|
2845
|
+
pagination: pagination || internalPagination,
|
|
2846
|
+
};
|
|
2847
|
+
return JSON.stringify(hashInputs);
|
|
2848
|
+
};
|
|
2849
|
+
const getCellValue = (row, column) => {
|
|
2850
|
+
if (column.field) {
|
|
2851
|
+
return row[column.field];
|
|
2852
|
+
}
|
|
2853
|
+
return row;
|
|
2854
|
+
};
|
|
2855
|
+
const applyFiltering = (data, filter, columns) => {
|
|
2856
|
+
var _a;
|
|
2857
|
+
if (!filter.searchTerm && !filter.columnFilters)
|
|
2858
|
+
return data;
|
|
2859
|
+
const filterableColumns = columns.filter((col) => col.filterable);
|
|
2860
|
+
if (filterableColumns.length === 0 && !filter.searchTerm)
|
|
2861
|
+
return data;
|
|
2862
|
+
const searchTerm = (_a = filter.searchTerm) === null || _a === void 0 ? void 0 : _a.toLowerCase();
|
|
2863
|
+
const hasColumnFilters = filter.columnFilters &&
|
|
2864
|
+
Object.keys(filter.columnFilters).some((key) => {
|
|
2865
|
+
const value = filter.columnFilters[key];
|
|
2866
|
+
return value !== null && value !== undefined && value !== '';
|
|
2867
|
+
});
|
|
2868
|
+
return data.filter((row) => {
|
|
2869
|
+
// Global search
|
|
2870
|
+
if (searchTerm) {
|
|
2871
|
+
const matchesGlobal = filterableColumns.some((column) => {
|
|
2872
|
+
const value = getCellValue(row, column);
|
|
2873
|
+
if (value == null)
|
|
2874
|
+
return false;
|
|
2875
|
+
return String(value).toLowerCase().includes(searchTerm);
|
|
2876
|
+
});
|
|
2877
|
+
if (!matchesGlobal)
|
|
2878
|
+
return false;
|
|
2879
|
+
}
|
|
2880
|
+
// Column-specific filters
|
|
2881
|
+
if (hasColumnFilters) {
|
|
2882
|
+
const matchesColumnFilters = Object.entries(filter.columnFilters).every(([columnKey, filterValue]) => {
|
|
2883
|
+
if (filterValue === null || filterValue === undefined || filterValue === '')
|
|
2884
|
+
return true;
|
|
2885
|
+
const column = columns.find((col) => col.key === columnKey);
|
|
2886
|
+
if (!column)
|
|
2887
|
+
return true;
|
|
2888
|
+
const value = getCellValue(row, column);
|
|
2889
|
+
if (value == null)
|
|
2890
|
+
return false;
|
|
2891
|
+
return String(value).toLowerCase().includes(String(filterValue).toLowerCase());
|
|
2892
|
+
});
|
|
2893
|
+
if (!matchesColumnFilters)
|
|
2894
|
+
return false;
|
|
2895
|
+
}
|
|
2896
|
+
return true;
|
|
2897
|
+
});
|
|
2898
|
+
};
|
|
2899
|
+
const applySorting = (data, sort, columns) => {
|
|
2900
|
+
const column = columns.find((col) => col.key === sort.column);
|
|
2901
|
+
if (!column || !column.sortable)
|
|
2902
|
+
return data;
|
|
2903
|
+
const multiplier = sort.direction === 'asc' ? 1 : -1;
|
|
2904
|
+
return [...data].sort((a, b) => {
|
|
2905
|
+
const aValue = getCellValue(a, column);
|
|
2906
|
+
const bValue = getCellValue(b, column);
|
|
2907
|
+
// Handle null/undefined values
|
|
2908
|
+
if (aValue == null && bValue == null)
|
|
2909
|
+
return 0;
|
|
2910
|
+
if (aValue == null)
|
|
2911
|
+
return -1 * multiplier;
|
|
2912
|
+
if (bValue == null)
|
|
2913
|
+
return 1 * multiplier;
|
|
2914
|
+
// Type-specific comparisons
|
|
2915
|
+
const aType = typeof aValue;
|
|
2916
|
+
const bType = typeof bValue;
|
|
2917
|
+
if (aType === bType) {
|
|
2918
|
+
if (aType === 'number') {
|
|
2919
|
+
return (aValue - bValue) * multiplier;
|
|
2920
|
+
}
|
|
2921
|
+
if (aType === 'boolean') {
|
|
2922
|
+
return (aValue === bValue ? 0 : aValue ? 1 : -1) * multiplier;
|
|
2923
|
+
}
|
|
2924
|
+
if (aValue instanceof Date && bValue instanceof Date) {
|
|
2925
|
+
return (aValue.getTime() - bValue.getTime()) * multiplier;
|
|
2926
|
+
}
|
|
2927
|
+
}
|
|
2928
|
+
// Fallback to string comparison
|
|
2929
|
+
return String(aValue).localeCompare(String(bValue)) * multiplier;
|
|
2930
|
+
});
|
|
2931
|
+
};
|
|
2932
|
+
const processData = (attrs) => {
|
|
2933
|
+
const { data } = attrs;
|
|
2934
|
+
const { internalSort, internalFilter, internalPagination } = state;
|
|
2935
|
+
let processedData = [...data];
|
|
2936
|
+
// Apply filtering
|
|
2937
|
+
if (internalFilter) {
|
|
2938
|
+
processedData = applyFiltering(processedData, internalFilter, attrs.columns);
|
|
2939
|
+
}
|
|
2940
|
+
// Apply sorting
|
|
2941
|
+
if (internalSort) {
|
|
2942
|
+
processedData = applySorting(processedData, internalSort, attrs.columns);
|
|
2943
|
+
}
|
|
2944
|
+
// Update total count for pagination
|
|
2945
|
+
if (internalPagination) {
|
|
2946
|
+
state.internalPagination = Object.assign(Object.assign({}, internalPagination), { total: processedData.length });
|
|
2947
|
+
}
|
|
2948
|
+
// Apply pagination
|
|
2949
|
+
if (internalPagination) {
|
|
2950
|
+
const { page, pageSize } = internalPagination;
|
|
2951
|
+
const start = page * pageSize;
|
|
2952
|
+
const end = start + pageSize;
|
|
2953
|
+
processedData = processedData.slice(start, end);
|
|
2954
|
+
}
|
|
2955
|
+
state.processedData = processedData;
|
|
2956
|
+
};
|
|
2957
|
+
// Create stable helper functions that don't get recreated on every render
|
|
2958
|
+
const createHelpers = (attrs) => ({
|
|
2959
|
+
getCellValue,
|
|
2960
|
+
handleSort: (columnKey) => {
|
|
2961
|
+
var _a;
|
|
2962
|
+
const column = attrs.columns.find((col) => col.key === columnKey);
|
|
2963
|
+
if (!column || !column.sortable)
|
|
2964
|
+
return;
|
|
2965
|
+
const currentSort = state.internalSort;
|
|
2966
|
+
let newSort;
|
|
2967
|
+
if ((currentSort === null || currentSort === void 0 ? void 0 : currentSort.column) === columnKey) {
|
|
2968
|
+
// Toggle direction
|
|
2969
|
+
if (currentSort.direction === 'asc') {
|
|
2970
|
+
newSort = { column: columnKey, direction: 'desc' };
|
|
2971
|
+
}
|
|
2972
|
+
else {
|
|
2973
|
+
newSort = { column: columnKey, direction: 'asc' };
|
|
2974
|
+
}
|
|
2975
|
+
}
|
|
2976
|
+
else {
|
|
2977
|
+
// New column sort
|
|
2978
|
+
newSort = { column: columnKey, direction: 'asc' };
|
|
2979
|
+
}
|
|
2980
|
+
state.internalSort = newSort;
|
|
2981
|
+
(_a = attrs.onSortChange) === null || _a === void 0 ? void 0 : _a.call(attrs, newSort);
|
|
2982
|
+
},
|
|
2983
|
+
handleGlobalSearch: (searchTerm) => {
|
|
2984
|
+
var _a;
|
|
2985
|
+
const newFilter = Object.assign(Object.assign({}, state.internalFilter), { searchTerm });
|
|
2986
|
+
state.internalFilter = newFilter;
|
|
2987
|
+
// Reset pagination to first page when filtering
|
|
2988
|
+
if (state.internalPagination) {
|
|
2989
|
+
state.internalPagination = Object.assign(Object.assign({}, state.internalPagination), { page: 0 });
|
|
2990
|
+
}
|
|
2991
|
+
(_a = attrs.onFilterChange) === null || _a === void 0 ? void 0 : _a.call(attrs, newFilter);
|
|
2992
|
+
},
|
|
2993
|
+
handleSelectionChange: (rowKey, selected) => {
|
|
2994
|
+
var _a, _b;
|
|
2995
|
+
if (!attrs.selection)
|
|
2996
|
+
return;
|
|
2997
|
+
let newSelectedKeys;
|
|
2998
|
+
if (attrs.selection.mode === 'single') {
|
|
2999
|
+
newSelectedKeys = selected ? [rowKey] : [];
|
|
3000
|
+
}
|
|
3001
|
+
else if (attrs.selection.mode === 'multiple') {
|
|
3002
|
+
if (selected) {
|
|
3003
|
+
newSelectedKeys = [...attrs.selection.selectedKeys, rowKey];
|
|
3004
|
+
}
|
|
3005
|
+
else {
|
|
3006
|
+
newSelectedKeys = attrs.selection.selectedKeys.filter((key) => key !== rowKey);
|
|
3007
|
+
}
|
|
3008
|
+
}
|
|
3009
|
+
else {
|
|
3010
|
+
return; // No selection mode
|
|
3011
|
+
}
|
|
3012
|
+
// Get selected rows
|
|
3013
|
+
const selectedRows = attrs.data.filter((row, index) => {
|
|
3014
|
+
const key = attrs.selection.getRowKey(row, index);
|
|
3015
|
+
return newSelectedKeys.includes(key);
|
|
3016
|
+
});
|
|
3017
|
+
(_b = (_a = attrs.selection).onSelectionChange) === null || _b === void 0 ? void 0 : _b.call(_a, newSelectedKeys, selectedRows);
|
|
3018
|
+
},
|
|
3019
|
+
handleSelectAll: (selected) => {
|
|
3020
|
+
var _a, _b;
|
|
3021
|
+
if (!attrs.selection || attrs.selection.mode !== 'multiple')
|
|
3022
|
+
return;
|
|
3023
|
+
let newSelectedKeys;
|
|
3024
|
+
if (selected) {
|
|
3025
|
+
// Select all visible rows
|
|
3026
|
+
newSelectedKeys = state.processedData.map((row) => {
|
|
3027
|
+
const originalIndex = attrs.data.findIndex((originalRow) => originalRow === row);
|
|
3028
|
+
return attrs.selection.getRowKey(row, originalIndex);
|
|
3029
|
+
});
|
|
3030
|
+
}
|
|
3031
|
+
else {
|
|
3032
|
+
newSelectedKeys = [];
|
|
3033
|
+
}
|
|
3034
|
+
const selectedRows = attrs.data.filter((row, index) => {
|
|
3035
|
+
const key = attrs.selection.getRowKey(row, index);
|
|
3036
|
+
return newSelectedKeys.includes(key);
|
|
3037
|
+
});
|
|
3038
|
+
(_b = (_a = attrs.selection).onSelectionChange) === null || _b === void 0 ? void 0 : _b.call(_a, newSelectedKeys, selectedRows);
|
|
3039
|
+
},
|
|
3040
|
+
});
|
|
3041
|
+
return {
|
|
3042
|
+
oninit(vnodeInit) {
|
|
3043
|
+
const { sort, filter, pagination } = vnodeInit.attrs;
|
|
3044
|
+
state.tableId = uniqueId();
|
|
3045
|
+
state.internalSort = sort || undefined;
|
|
3046
|
+
state.internalFilter = filter || { searchTerm: '', columnFilters: {} };
|
|
3047
|
+
state.internalPagination = pagination || undefined;
|
|
3048
|
+
processData(vnodeInit.attrs);
|
|
3049
|
+
},
|
|
3050
|
+
onbeforeupdate(vnodeUpdate) {
|
|
3051
|
+
// Only reprocess data if inputs have changed
|
|
3052
|
+
const currentHash = getDataHash(vnodeUpdate.attrs);
|
|
3053
|
+
if (currentHash !== state.lastProcessedHash) {
|
|
3054
|
+
processData(vnodeUpdate.attrs);
|
|
3055
|
+
state.lastProcessedHash = currentHash;
|
|
3056
|
+
}
|
|
3057
|
+
},
|
|
3058
|
+
view(vnodeView) {
|
|
3059
|
+
const attrs = vnodeView.attrs;
|
|
3060
|
+
const { loading, emptyMessage, striped, hoverable, responsive, centered, className, id, title, height, enableGlobalSearch, searchPlaceholder, selection, columns, onRowClick, onRowDoubleClick, getRowClassName, data, onPaginationChange, i18n, } = attrs;
|
|
3061
|
+
const { processedData, tableId, internalSort, internalPagination } = state;
|
|
3062
|
+
if (loading) {
|
|
3063
|
+
return m('.datatable-loading', [
|
|
3064
|
+
m('.preloader-wrapper.small.active', m('.spinner-layer.spinner-blue-only', m('.circle-clipper.left', m('.circle')))),
|
|
3065
|
+
m('p', (i18n === null || i18n === void 0 ? void 0 : i18n.loading) || 'Loading...'),
|
|
3066
|
+
]);
|
|
3067
|
+
}
|
|
3068
|
+
// Create stable helpers object using the factory function
|
|
3069
|
+
const helpers = createHelpers(attrs);
|
|
3070
|
+
// Calculate selection state for "select all" checkbox
|
|
3071
|
+
let allSelected = false;
|
|
3072
|
+
let someSelected = false;
|
|
3073
|
+
if (selection && selection.mode === 'multiple') {
|
|
3074
|
+
const visibleRowKeys = processedData.map((row) => {
|
|
3075
|
+
const originalIndex = data.findIndex((originalRow) => originalRow === row);
|
|
3076
|
+
return selection.getRowKey(row, originalIndex);
|
|
3077
|
+
});
|
|
3078
|
+
const selectedVisibleKeys = visibleRowKeys.filter((key) => selection.selectedKeys.includes(key));
|
|
3079
|
+
allSelected = visibleRowKeys.length > 0 && selectedVisibleKeys.length === visibleRowKeys.length;
|
|
3080
|
+
someSelected = selectedVisibleKeys.length > 0 && selectedVisibleKeys.length < visibleRowKeys.length;
|
|
3081
|
+
}
|
|
3082
|
+
const tableClasses = [
|
|
3083
|
+
'datatable',
|
|
3084
|
+
striped ? 'striped' : '',
|
|
3085
|
+
hoverable ? 'highlight' : '',
|
|
3086
|
+
responsive ? 'responsive-table' : '',
|
|
3087
|
+
centered ? 'centered' : '',
|
|
3088
|
+
className || '',
|
|
3089
|
+
]
|
|
3090
|
+
.filter(Boolean)
|
|
3091
|
+
.join(' ');
|
|
3092
|
+
return m('.datatable-container', {
|
|
3093
|
+
id: id || tableId,
|
|
3094
|
+
}, title && m('h5.datatable-title', title), enableGlobalSearch &&
|
|
3095
|
+
m(GlobalSearch, {
|
|
3096
|
+
searchPlaceholder,
|
|
3097
|
+
onSearch: helpers.handleGlobalSearch,
|
|
3098
|
+
i18n,
|
|
3099
|
+
}), m('.datatable-wrapper', {
|
|
3100
|
+
style: {
|
|
3101
|
+
maxHeight: height ? `${height}px` : undefined,
|
|
3102
|
+
overflowY: height ? 'auto' : undefined,
|
|
3103
|
+
},
|
|
3104
|
+
}, processedData.length === 0
|
|
3105
|
+
? m('.datatable-empty', emptyMessage || (i18n === null || i18n === void 0 ? void 0 : i18n.noDataAvailable) || 'No data available')
|
|
3106
|
+
: m(TableContent(), {
|
|
3107
|
+
processedData,
|
|
3108
|
+
height,
|
|
3109
|
+
tableClasses,
|
|
3110
|
+
columns,
|
|
3111
|
+
selection,
|
|
3112
|
+
internalSort,
|
|
3113
|
+
allSelected,
|
|
3114
|
+
someSelected,
|
|
3115
|
+
helpers,
|
|
3116
|
+
onRowClick,
|
|
3117
|
+
onRowDoubleClick,
|
|
3118
|
+
getRowClassName,
|
|
3119
|
+
data,
|
|
3120
|
+
})), m(PaginationControls, {
|
|
3121
|
+
pagination: internalPagination,
|
|
3122
|
+
onPaginationChange: (pagination) => {
|
|
3123
|
+
state.internalPagination = pagination;
|
|
3124
|
+
onPaginationChange === null || onPaginationChange === void 0 ? void 0 : onPaginationChange(pagination);
|
|
3125
|
+
},
|
|
3126
|
+
i18n,
|
|
3127
|
+
}));
|
|
3128
|
+
},
|
|
3129
|
+
};
|
|
3130
|
+
};
|
|
3131
|
+
|
|
3132
|
+
/** Pure TypeScript Dropdown component - no Materialize dependencies */
|
|
3133
|
+
const Dropdown = () => {
|
|
3134
|
+
const state = {
|
|
3135
|
+
isOpen: false,
|
|
3136
|
+
initialValue: undefined,
|
|
3137
|
+
id: '',
|
|
3138
|
+
focusedIndex: -1,
|
|
3139
|
+
inputRef: null,
|
|
3140
|
+
dropdownRef: null,
|
|
3141
|
+
};
|
|
3142
|
+
const handleKeyDown = (e, items, onchange) => {
|
|
3143
|
+
const availableItems = items.filter((item) => !item.divider && !item.disabled);
|
|
3144
|
+
switch (e.key) {
|
|
3145
|
+
case 'ArrowDown':
|
|
3146
|
+
e.preventDefault();
|
|
3147
|
+
if (!state.isOpen) {
|
|
3148
|
+
state.isOpen = true;
|
|
3149
|
+
state.focusedIndex = 0;
|
|
3150
|
+
}
|
|
3151
|
+
else {
|
|
3152
|
+
state.focusedIndex = Math.min(state.focusedIndex + 1, availableItems.length - 1);
|
|
3153
|
+
}
|
|
3154
|
+
break;
|
|
3155
|
+
case 'ArrowUp':
|
|
3156
|
+
e.preventDefault();
|
|
3157
|
+
if (state.isOpen) {
|
|
3158
|
+
state.focusedIndex = Math.max(state.focusedIndex - 1, 0);
|
|
3159
|
+
}
|
|
3160
|
+
break;
|
|
3161
|
+
case 'Enter':
|
|
3162
|
+
case ' ':
|
|
3163
|
+
e.preventDefault();
|
|
3164
|
+
if (state.isOpen && state.focusedIndex >= 0 && state.focusedIndex < availableItems.length) {
|
|
3165
|
+
const selectedItem = availableItems[state.focusedIndex];
|
|
3166
|
+
const value = (selectedItem.id || selectedItem.label);
|
|
3167
|
+
state.initialValue = value;
|
|
3168
|
+
state.isOpen = false;
|
|
3169
|
+
state.focusedIndex = -1;
|
|
3170
|
+
if (onchange)
|
|
3171
|
+
onchange(value);
|
|
3172
|
+
}
|
|
3173
|
+
else if (!state.isOpen) {
|
|
3174
|
+
state.isOpen = true;
|
|
3175
|
+
state.focusedIndex = 0;
|
|
3176
|
+
}
|
|
3177
|
+
break;
|
|
3178
|
+
case 'Escape':
|
|
3179
|
+
e.preventDefault();
|
|
3180
|
+
state.isOpen = false;
|
|
3181
|
+
state.focusedIndex = -1;
|
|
3182
|
+
break;
|
|
3183
|
+
}
|
|
3184
|
+
};
|
|
3185
|
+
return {
|
|
3186
|
+
oninit: ({ attrs: { id = uniqueId(), initialValue, checkedId } }) => {
|
|
3187
|
+
state.id = id;
|
|
3188
|
+
state.initialValue = initialValue || checkedId;
|
|
3189
|
+
// Mithril will handle click events through the component structure
|
|
3190
|
+
},
|
|
3191
|
+
view: ({ attrs: { key, label, onchange, disabled = false, items, iconName, helperText, style, className = 'col s12' }, }) => {
|
|
3192
|
+
const { initialValue } = state;
|
|
3193
|
+
const selectedItem = initialValue
|
|
3194
|
+
? items.filter((i) => (i.id ? i.id === initialValue : i.label === initialValue)).shift()
|
|
3195
|
+
: undefined;
|
|
3196
|
+
const title = selectedItem ? selectedItem.label : label || 'Select';
|
|
3197
|
+
const availableItems = items.filter((item) => !item.divider && !item.disabled);
|
|
3198
|
+
return m('.dropdown-wrapper.input-field', { className, key, style }, [
|
|
3199
|
+
iconName ? m('i.material-icons.prefix', iconName) : undefined,
|
|
3200
|
+
m(HelperText, { helperText }),
|
|
3201
|
+
m('.select-wrapper', {
|
|
3202
|
+
onclick: disabled
|
|
3203
|
+
? undefined
|
|
3204
|
+
: () => {
|
|
3205
|
+
state.isOpen = !state.isOpen;
|
|
3206
|
+
state.focusedIndex = state.isOpen ? 0 : -1;
|
|
3207
|
+
},
|
|
3208
|
+
onkeydown: disabled ? undefined : (e) => handleKeyDown(e, items, onchange),
|
|
3209
|
+
tabindex: disabled ? -1 : 0,
|
|
3210
|
+
'aria-expanded': state.isOpen ? 'true' : 'false',
|
|
3211
|
+
'aria-haspopup': 'listbox',
|
|
3212
|
+
role: 'combobox',
|
|
3213
|
+
}, [
|
|
3214
|
+
m('input[type=text][readonly=true].select-dropdown.dropdown-trigger', {
|
|
3215
|
+
id: state.id,
|
|
3216
|
+
value: title,
|
|
3217
|
+
oncreate: ({ dom }) => {
|
|
3218
|
+
state.inputRef = dom;
|
|
3219
|
+
},
|
|
3220
|
+
onclick: (e) => {
|
|
3221
|
+
e.preventDefault();
|
|
3222
|
+
e.stopPropagation();
|
|
3223
|
+
if (!disabled) {
|
|
3224
|
+
state.isOpen = !state.isOpen;
|
|
3225
|
+
state.focusedIndex = state.isOpen ? 0 : -1;
|
|
3226
|
+
}
|
|
3227
|
+
},
|
|
3228
|
+
}),
|
|
3229
|
+
// Dropdown Menu using Select component's positioning logic
|
|
3230
|
+
state.isOpen &&
|
|
3231
|
+
m('ul.dropdown-content.select-dropdown', {
|
|
3232
|
+
tabindex: 0,
|
|
3233
|
+
role: 'listbox',
|
|
3234
|
+
'aria-labelledby': state.id,
|
|
3235
|
+
oncreate: ({ dom }) => {
|
|
3236
|
+
state.dropdownRef = dom;
|
|
3237
|
+
},
|
|
3238
|
+
onremove: () => {
|
|
3239
|
+
state.dropdownRef = null;
|
|
3240
|
+
},
|
|
3241
|
+
style: getDropdownStyles(state.inputRef, true, items.map((item) => (Object.assign(Object.assign({}, item), {
|
|
3242
|
+
// Convert dropdown items to format expected by getDropdownStyles
|
|
3243
|
+
group: undefined }))), true),
|
|
3244
|
+
}, items.map((item, index) => {
|
|
3245
|
+
if (item.divider) {
|
|
3246
|
+
return m('li.divider', {
|
|
3247
|
+
key: `divider-${index}`,
|
|
3248
|
+
});
|
|
3249
|
+
}
|
|
3250
|
+
const itemIndex = availableItems.indexOf(item);
|
|
3251
|
+
const isFocused = itemIndex === state.focusedIndex;
|
|
3252
|
+
return m('li', Object.assign({ key: item.id || `item-${index}`, class: [
|
|
3253
|
+
item.disabled ? 'disabled' : '',
|
|
3254
|
+
isFocused ? 'focused' : '',
|
|
3255
|
+
(selectedItem === null || selectedItem === void 0 ? void 0 : selectedItem.id) === item.id || (selectedItem === null || selectedItem === void 0 ? void 0 : selectedItem.label) === item.label ? 'selected' : '',
|
|
3256
|
+
]
|
|
3257
|
+
.filter(Boolean)
|
|
3258
|
+
.join(' ') }, (item.disabled
|
|
3259
|
+
? {}
|
|
3260
|
+
: {
|
|
3261
|
+
onclick: (e) => {
|
|
3262
|
+
e.stopPropagation();
|
|
3263
|
+
const value = (item.id || item.label);
|
|
3264
|
+
state.initialValue = value;
|
|
3265
|
+
state.isOpen = false;
|
|
3266
|
+
state.focusedIndex = -1;
|
|
3267
|
+
if (onchange)
|
|
3268
|
+
onchange(value);
|
|
3269
|
+
},
|
|
3270
|
+
})), m('span', {
|
|
3271
|
+
style: {
|
|
3272
|
+
display: 'flex',
|
|
3273
|
+
alignItems: 'center',
|
|
3274
|
+
padding: '14px 16px',
|
|
3275
|
+
},
|
|
3276
|
+
}, [
|
|
3277
|
+
item.iconName
|
|
3278
|
+
? m('i.material-icons', {
|
|
3279
|
+
style: { marginRight: '32px' },
|
|
3280
|
+
}, item.iconName)
|
|
3281
|
+
: undefined,
|
|
3282
|
+
item.label,
|
|
3283
|
+
]));
|
|
3284
|
+
})),
|
|
3285
|
+
m(MaterialIcon, {
|
|
3286
|
+
name: 'caret',
|
|
3287
|
+
direction: 'down',
|
|
3288
|
+
class: 'caret',
|
|
3289
|
+
}),
|
|
3290
|
+
]),
|
|
3291
|
+
]);
|
|
3292
|
+
},
|
|
3293
|
+
};
|
|
3294
|
+
};
|
|
3295
|
+
|
|
3296
|
+
/**
|
|
3297
|
+
* Floating Action Button
|
|
3298
|
+
*/
|
|
3299
|
+
const FloatingActionButton = () => {
|
|
3300
|
+
const state = {
|
|
3301
|
+
isOpen: false,
|
|
3302
|
+
};
|
|
3303
|
+
const handleClickOutside = (e) => {
|
|
3304
|
+
const target = e.target;
|
|
3305
|
+
if (!target.closest('.fixed-action-btn')) {
|
|
3306
|
+
state.isOpen = false;
|
|
3307
|
+
}
|
|
3308
|
+
};
|
|
3309
|
+
return {
|
|
3310
|
+
oncreate: () => {
|
|
3311
|
+
document.addEventListener('click', handleClickOutside);
|
|
3312
|
+
},
|
|
3313
|
+
onremove: () => {
|
|
3314
|
+
document.removeEventListener('click', handleClickOutside);
|
|
3315
|
+
},
|
|
3316
|
+
view: ({ attrs: { className, iconName, iconClass, position, style = position === 'left' || position === 'inline-left'
|
|
3317
|
+
? 'position: absolute; display: inline-block; left: 24px;'
|
|
3318
|
+
: position === 'right' || position === 'inline-right'
|
|
3319
|
+
? 'position: absolute; display: inline-block; right: 24px;'
|
|
3320
|
+
: undefined, buttons, direction = 'top', hoverEnabled = true, }, }) => {
|
|
3321
|
+
const fabClasses = [
|
|
3322
|
+
'fixed-action-btn',
|
|
3323
|
+
direction ? `direction-${direction}` : '',
|
|
3324
|
+
state.isOpen ? 'active' : '',
|
|
3325
|
+
// hoverEnabled ? 'hover-enabled' : '',
|
|
3326
|
+
]
|
|
3327
|
+
.filter(Boolean)
|
|
3328
|
+
.join(' ');
|
|
3329
|
+
return m('div', {
|
|
3330
|
+
style: position === 'inline-right' || position === 'inline-left' ? 'position: relative; height: 70px;' : undefined,
|
|
3331
|
+
}, m(`.${fabClasses}`, {
|
|
3332
|
+
style,
|
|
3333
|
+
onclick: (e) => {
|
|
3334
|
+
e.stopPropagation();
|
|
3335
|
+
if (buttons && buttons.length > 0) {
|
|
3336
|
+
state.isOpen = !state.isOpen;
|
|
3337
|
+
}
|
|
3338
|
+
},
|
|
3339
|
+
onmouseover: hoverEnabled
|
|
3340
|
+
? () => {
|
|
3341
|
+
if (buttons && buttons.length > 0) {
|
|
3342
|
+
state.isOpen = true;
|
|
3343
|
+
}
|
|
3344
|
+
}
|
|
3345
|
+
: undefined,
|
|
3346
|
+
onmouseleave: hoverEnabled
|
|
3347
|
+
? () => {
|
|
3348
|
+
state.isOpen = false;
|
|
3349
|
+
}
|
|
3350
|
+
: undefined,
|
|
3351
|
+
}, [
|
|
3352
|
+
m('a.btn-floating.btn-large', {
|
|
3353
|
+
className,
|
|
3354
|
+
}, m('i.material-icons', { className: iconClass }, iconName)),
|
|
3355
|
+
buttons &&
|
|
3356
|
+
buttons.length > 0 &&
|
|
3357
|
+
m('ul', buttons.map((button, index) => m('li', m(`a.btn-floating.${button.className || 'red'}`, {
|
|
3358
|
+
style: {
|
|
3359
|
+
opacity: state.isOpen ? '1' : '0',
|
|
3360
|
+
transform: state.isOpen ? 'scale(1)' : 'scale(0.4)',
|
|
3361
|
+
transition: `all 0.3s ease ${index * 40}ms`,
|
|
3362
|
+
},
|
|
3363
|
+
onclick: (e) => {
|
|
3364
|
+
e.stopPropagation();
|
|
3365
|
+
if (button.onClick)
|
|
3366
|
+
button.onClick(e);
|
|
3367
|
+
},
|
|
3368
|
+
}, m('i.material-icons', { className: button.iconClass }, button.iconName))))),
|
|
3369
|
+
]));
|
|
2666
3370
|
},
|
|
2667
3371
|
};
|
|
2668
3372
|
};
|
|
@@ -2841,7 +3545,7 @@
|
|
|
2841
3545
|
view: ({ attrs }) => {
|
|
2842
3546
|
const { src, alt, width, height, caption, className, style } = attrs, otherAttrs = __rest(attrs, ["src", "alt", "width", "height", "caption", "className", "style"]);
|
|
2843
3547
|
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) => {
|
|
3548
|
+
height, className: ['materialboxed', className].filter(Boolean).join(' ') || undefined, style: Object.assign({ cursor: 'zoom-in', transition: 'opacity 200ms ease' }, style), onclick: (e) => {
|
|
2845
3549
|
e.preventDefault();
|
|
2846
3550
|
openBox(e.target, attrs);
|
|
2847
3551
|
} }));
|
|
@@ -2923,7 +3627,7 @@
|
|
|
2923
3627
|
.filter(Boolean)
|
|
2924
3628
|
.join(' ')
|
|
2925
3629
|
.trim();
|
|
2926
|
-
const overlayClasses = ['modal-overlay', state.isOpen ? 'active' : ''].filter(Boolean).join(' ').trim();
|
|
3630
|
+
const overlayClasses = ['modal-overlay', state.isOpen ? 'active' : ''].filter(Boolean).join(' ').trim() || undefined;
|
|
2927
3631
|
return m('div', { className: 'modal-container' }, [
|
|
2928
3632
|
// Modal overlay
|
|
2929
3633
|
m('div', {
|
|
@@ -2948,21 +3652,25 @@
|
|
|
2948
3652
|
role: 'dialog',
|
|
2949
3653
|
'aria-labelledby': `${id}-title`,
|
|
2950
3654
|
'aria-describedby': description ? `${id}-desc` : undefined,
|
|
2951
|
-
style: {
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
3655
|
+
style: Object.assign(Object.assign({ display: state.isOpen ? 'flex' : 'none', position: 'fixed' }, (bottomSheet
|
|
3656
|
+
? {
|
|
3657
|
+
// Bottom sheet positioning
|
|
3658
|
+
top: 'auto',
|
|
3659
|
+
bottom: '0',
|
|
3660
|
+
left: '0',
|
|
3661
|
+
right: '0',
|
|
3662
|
+
transform: 'none',
|
|
3663
|
+
maxWidth: '100%',
|
|
3664
|
+
borderRadius: '8px 8px 0 0',
|
|
3665
|
+
}
|
|
3666
|
+
: {
|
|
3667
|
+
// Regular modal positioning
|
|
3668
|
+
top: '50%',
|
|
3669
|
+
left: '50%',
|
|
3670
|
+
transform: 'translate(-50%, -50%)',
|
|
3671
|
+
maxWidth: '75%',
|
|
3672
|
+
borderRadius: '4px',
|
|
3673
|
+
})), { 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
3674
|
onclick: (e) => e.stopPropagation(), // Prevent backdrop click when clicking inside modal
|
|
2967
3675
|
}, [
|
|
2968
3676
|
// Close button
|
|
@@ -2982,7 +3690,12 @@
|
|
|
2982
3690
|
}, '×'),
|
|
2983
3691
|
// Modal content
|
|
2984
3692
|
m('.modal-content', {
|
|
2985
|
-
style: {
|
|
3693
|
+
style: {
|
|
3694
|
+
padding: '24px',
|
|
3695
|
+
paddingTop: showCloseButton ? '48px' : '24px',
|
|
3696
|
+
minHeight: 'auto',
|
|
3697
|
+
flex: '1 1 auto',
|
|
3698
|
+
},
|
|
2986
3699
|
}, [
|
|
2987
3700
|
m('h4', { id: `${id}-title`, style: { margin: '0 0 20px 0' } }, title),
|
|
2988
3701
|
description &&
|
|
@@ -2994,7 +3707,7 @@
|
|
|
2994
3707
|
m('.modal-footer', {
|
|
2995
3708
|
style: {
|
|
2996
3709
|
padding: '4px 6px',
|
|
2997
|
-
borderTop: '1px solid rgba(160,160,160,0.2)',
|
|
3710
|
+
borderTop: '1px solid var(--mm-border-color, rgba(160,160,160,0.2))',
|
|
2998
3711
|
textAlign: 'right',
|
|
2999
3712
|
},
|
|
3000
3713
|
}, buttons.map((buttonProps) => m(FlatButton, Object.assign(Object.assign({}, buttonProps), { className: `modal-close ${buttonProps.className || ''}`, onclick: (e) => {
|
|
@@ -3008,109 +3721,6 @@
|
|
|
3008
3721
|
};
|
|
3009
3722
|
};
|
|
3010
3723
|
|
|
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),
|
|
3109
|
-
]);
|
|
3110
|
-
},
|
|
3111
|
-
};
|
|
3112
|
-
};
|
|
3113
|
-
|
|
3114
3724
|
const PaginationItem = () => ({
|
|
3115
3725
|
view: ({ attrs: { title, href, active, disabled } }) => m('li', { className: active ? 'active' : disabled ? 'disabled' : 'waves-effect' }, typeof title === 'number' ? m(m.route.Link, { href }, title) : title),
|
|
3116
3726
|
});
|
|
@@ -3375,12 +3985,15 @@
|
|
|
3375
3985
|
};
|
|
3376
3986
|
const updateTimeFromInput = (inputValue) => {
|
|
3377
3987
|
let value = ((inputValue || options.defaultTime || '') + '').split(':');
|
|
3988
|
+
let amPmWasProvided = false;
|
|
3378
3989
|
if (options.twelveHour && value.length > 1) {
|
|
3379
3990
|
if (value[1].toUpperCase().indexOf('AM') > -1) {
|
|
3380
3991
|
state.amOrPm = 'AM';
|
|
3992
|
+
amPmWasProvided = true;
|
|
3381
3993
|
}
|
|
3382
3994
|
else if (value[1].toUpperCase().indexOf('PM') > -1) {
|
|
3383
3995
|
state.amOrPm = 'PM';
|
|
3996
|
+
amPmWasProvided = true;
|
|
3384
3997
|
}
|
|
3385
3998
|
value[1] = value[1].replace('AM', '').replace('PM', '').trim();
|
|
3386
3999
|
}
|
|
@@ -3389,21 +4002,33 @@
|
|
|
3389
4002
|
value = [now.getHours().toString(), now.getMinutes().toString()];
|
|
3390
4003
|
if (options.twelveHour) {
|
|
3391
4004
|
state.amOrPm = parseInt(value[0]) >= 12 ? 'PM' : 'AM';
|
|
4005
|
+
amPmWasProvided = false; // For 'now', we need to do conversion
|
|
3392
4006
|
}
|
|
3393
4007
|
}
|
|
3394
4008
|
let hours = +value[0] || 0;
|
|
3395
4009
|
let minutes = +value[1] || 0;
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
4010
|
+
if (options.twelveHour) {
|
|
4011
|
+
if (!amPmWasProvided) {
|
|
4012
|
+
// No AM/PM was provided, assume this is 24-hour format input - convert it
|
|
4013
|
+
if (hours >= 12) {
|
|
4014
|
+
state.amOrPm = 'PM';
|
|
4015
|
+
if (hours > 12) {
|
|
4016
|
+
hours = hours - 12;
|
|
4017
|
+
}
|
|
4018
|
+
}
|
|
4019
|
+
else {
|
|
4020
|
+
state.amOrPm = 'AM';
|
|
4021
|
+
if (hours === 0) {
|
|
4022
|
+
hours = 12;
|
|
4023
|
+
}
|
|
4024
|
+
}
|
|
3401
4025
|
}
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
4026
|
+
else {
|
|
4027
|
+
// AM/PM was provided, hours are already in 12-hour format
|
|
4028
|
+
// Just handle midnight/noon edge cases
|
|
4029
|
+
if (hours === 0 && state.amOrPm === 'AM') {
|
|
4030
|
+
hours = 12;
|
|
4031
|
+
}
|
|
3407
4032
|
}
|
|
3408
4033
|
}
|
|
3409
4034
|
state.hours = hours;
|
|
@@ -4083,7 +4708,7 @@
|
|
|
4083
4708
|
callback(propId);
|
|
4084
4709
|
}
|
|
4085
4710
|
};
|
|
4086
|
-
const cn = [newRow ? 'clear' : '', className].filter(Boolean).join(' ').trim();
|
|
4711
|
+
const cn = [newRow ? 'clear' : '', className].filter(Boolean).join(' ').trim() || undefined;
|
|
4087
4712
|
const optionsContent = layout === 'horizontal'
|
|
4088
4713
|
? m('div.grid-container', options.map((r) => m(RadioButton, Object.assign(Object.assign({}, r), { onchange,
|
|
4089
4714
|
groupId, disabled: disabled || r.disabled, className: checkboxClass, checked: r.id === checkedId, inputId: `${componentId}-${r.id}` }))))
|
|
@@ -4356,7 +4981,7 @@
|
|
|
4356
4981
|
view: ({ attrs }) => {
|
|
4357
4982
|
const id = attrs.id || state.id;
|
|
4358
4983
|
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();
|
|
4984
|
+
const cn = ['input-field', newRow ? 'clear' : '', className].filter(Boolean).join(' ').trim() || undefined;
|
|
4360
4985
|
return m('div', {
|
|
4361
4986
|
className: cn,
|
|
4362
4987
|
onclick: (e) => {
|
|
@@ -4507,7 +5132,7 @@
|
|
|
4507
5132
|
},
|
|
4508
5133
|
view: ({ attrs }) => {
|
|
4509
5134
|
const { tabWidth, tabs, className, style, swipeable = false } = attrs;
|
|
4510
|
-
const cn = [tabWidth === 'fill' ? 'tabs-fixed-width' : '', className].filter(Boolean).join(' ').trim();
|
|
5135
|
+
const cn = [tabWidth === 'fill' ? 'tabs-fixed-width' : '', className].filter(Boolean).join(' ').trim() || undefined;
|
|
4511
5136
|
const anchoredTabs = tabs.map(toAnchored());
|
|
4512
5137
|
const activeTab = setActiveTabId(anchoredTabs, attrs.selectedTabId);
|
|
4513
5138
|
updateIndicator();
|
|
@@ -5493,8 +6118,8 @@
|
|
|
5493
6118
|
}
|
|
5494
6119
|
// Check file type
|
|
5495
6120
|
if (attrs.accept) {
|
|
5496
|
-
const acceptedTypes = attrs.accept.split(',').map(type => type.trim());
|
|
5497
|
-
const isAccepted = acceptedTypes.some(acceptedType => {
|
|
6121
|
+
const acceptedTypes = attrs.accept.split(',').map((type) => type.trim());
|
|
6122
|
+
const isAccepted = acceptedTypes.some((acceptedType) => {
|
|
5498
6123
|
if (acceptedType.startsWith('.')) {
|
|
5499
6124
|
// Extension check
|
|
5500
6125
|
return file.name.toLowerCase().endsWith(acceptedType.toLowerCase());
|
|
@@ -5554,11 +6179,11 @@
|
|
|
5554
6179
|
}
|
|
5555
6180
|
// Notify parent component
|
|
5556
6181
|
if (attrs.onFilesSelected) {
|
|
5557
|
-
attrs.onFilesSelected(state.files.filter(f => !f.uploadError));
|
|
6182
|
+
attrs.onFilesSelected(state.files.filter((f) => !f.uploadError));
|
|
5558
6183
|
}
|
|
5559
6184
|
};
|
|
5560
6185
|
const removeFile = (fileToRemove, attrs) => {
|
|
5561
|
-
state.files = state.files.filter(file => file !== fileToRemove);
|
|
6186
|
+
state.files = state.files.filter((file) => file !== fileToRemove);
|
|
5562
6187
|
if (attrs.onFileRemoved) {
|
|
5563
6188
|
attrs.onFileRemoved(fileToRemove);
|
|
5564
6189
|
}
|
|
@@ -5577,11 +6202,11 @@
|
|
|
5577
6202
|
id: uniqueId(),
|
|
5578
6203
|
files: [],
|
|
5579
6204
|
isDragOver: false,
|
|
5580
|
-
isUploading: false
|
|
6205
|
+
isUploading: false,
|
|
5581
6206
|
};
|
|
5582
6207
|
},
|
|
5583
6208
|
view: ({ attrs }) => {
|
|
5584
|
-
const { accept, multiple = false, disabled = false, label = 'Choose files or drag them here', helperText, showPreview = true, className = '', error } = attrs;
|
|
6209
|
+
const { accept, multiple = false, disabled = false, label = 'Choose files or drag them here', helperText, showPreview = true, className = '', error, } = attrs;
|
|
5585
6210
|
return m('.file-upload-container', { class: className }, [
|
|
5586
6211
|
// Upload area
|
|
5587
6212
|
m('.file-upload-area', {
|
|
@@ -5589,8 +6214,10 @@
|
|
|
5589
6214
|
state.isDragOver ? 'drag-over' : '',
|
|
5590
6215
|
disabled ? 'disabled' : '',
|
|
5591
6216
|
error ? 'error' : '',
|
|
5592
|
-
state.files.length > 0 ? 'has-files' : ''
|
|
5593
|
-
]
|
|
6217
|
+
state.files.length > 0 ? 'has-files' : '',
|
|
6218
|
+
]
|
|
6219
|
+
.filter(Boolean)
|
|
6220
|
+
.join(' ') || undefined,
|
|
5594
6221
|
ondragover: (e) => {
|
|
5595
6222
|
if (disabled)
|
|
5596
6223
|
return;
|
|
@@ -5621,7 +6248,7 @@
|
|
|
5621
6248
|
return;
|
|
5622
6249
|
const input = document.getElementById(state.id);
|
|
5623
6250
|
input === null || input === void 0 ? void 0 : input.click();
|
|
5624
|
-
}
|
|
6251
|
+
},
|
|
5625
6252
|
}, [
|
|
5626
6253
|
m('input[type="file"]', {
|
|
5627
6254
|
id: state.id,
|
|
@@ -5634,57 +6261,55 @@
|
|
|
5634
6261
|
if (target.files) {
|
|
5635
6262
|
handleFiles(target.files, attrs);
|
|
5636
6263
|
}
|
|
5637
|
-
}
|
|
6264
|
+
},
|
|
5638
6265
|
}),
|
|
5639
6266
|
m('.file-upload-content', [
|
|
5640
6267
|
m('i.material-icons.file-upload-icon', 'cloud_upload'),
|
|
5641
6268
|
m('p.file-upload-label', label),
|
|
5642
6269
|
helperText && m('p.file-upload-helper', helperText),
|
|
5643
|
-
accept && m('p.file-upload-types', `Accepted: ${accept}`)
|
|
5644
|
-
])
|
|
6270
|
+
accept && m('p.file-upload-types', `Accepted: ${accept}`),
|
|
6271
|
+
]),
|
|
5645
6272
|
]),
|
|
5646
6273
|
// Error message
|
|
5647
6274
|
error && m('.file-upload-error', error),
|
|
5648
6275
|
// 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
|
-
|
|
6276
|
+
state.files.length > 0 &&
|
|
6277
|
+
m('.file-upload-list', [
|
|
6278
|
+
m('h6', 'Selected Files:'),
|
|
6279
|
+
state.files.map((file) => m('.file-upload-item', { key: file.name + file.size }, [
|
|
6280
|
+
// Preview thumbnail
|
|
6281
|
+
showPreview && file.preview && m('.file-preview', [m('img', { src: file.preview, alt: file.name })]),
|
|
6282
|
+
// File info
|
|
6283
|
+
m('.file-info', [
|
|
6284
|
+
m('.file-name', file.name),
|
|
6285
|
+
m('.file-details', [
|
|
6286
|
+
m('span.file-size', formatFileSize(file.size)),
|
|
6287
|
+
file.type && m('span.file-type', file.type),
|
|
6288
|
+
]),
|
|
6289
|
+
// Progress bar (if uploading)
|
|
6290
|
+
file.uploadProgress !== undefined &&
|
|
6291
|
+
m('.file-progress', [
|
|
6292
|
+
m('.progress', [
|
|
6293
|
+
m('.determinate', {
|
|
6294
|
+
style: { width: `${file.uploadProgress}%` },
|
|
6295
|
+
}),
|
|
6296
|
+
]),
|
|
6297
|
+
]),
|
|
6298
|
+
// Error message
|
|
6299
|
+
file.uploadError && m('.file-error', file.uploadError),
|
|
5670
6300
|
]),
|
|
5671
|
-
//
|
|
5672
|
-
|
|
5673
|
-
|
|
5674
|
-
|
|
5675
|
-
|
|
5676
|
-
|
|
5677
|
-
|
|
5678
|
-
|
|
5679
|
-
|
|
5680
|
-
|
|
5681
|
-
}, [
|
|
5682
|
-
m('i.material-icons', 'close')
|
|
5683
|
-
])
|
|
5684
|
-
]))
|
|
5685
|
-
])
|
|
6301
|
+
// Remove button
|
|
6302
|
+
m('button.btn-flat.file-remove', {
|
|
6303
|
+
onclick: (e) => {
|
|
6304
|
+
e.stopPropagation();
|
|
6305
|
+
removeFile(file, attrs);
|
|
6306
|
+
},
|
|
6307
|
+
title: 'Remove file',
|
|
6308
|
+
}, [m('i.material-icons', 'close')]),
|
|
6309
|
+
])),
|
|
6310
|
+
]),
|
|
5686
6311
|
]);
|
|
5687
|
-
}
|
|
6312
|
+
},
|
|
5688
6313
|
};
|
|
5689
6314
|
};
|
|
5690
6315
|
|
|
@@ -5714,7 +6339,7 @@
|
|
|
5714
6339
|
state = {
|
|
5715
6340
|
id: attrs.id || uniqueId(),
|
|
5716
6341
|
isOpen: attrs.isOpen || false,
|
|
5717
|
-
isAnimating: false
|
|
6342
|
+
isAnimating: false,
|
|
5718
6343
|
};
|
|
5719
6344
|
// Set up keyboard listener
|
|
5720
6345
|
if (typeof document !== 'undefined' && attrs.closeOnEscape !== false) {
|
|
@@ -5743,34 +6368,33 @@
|
|
|
5743
6368
|
}
|
|
5744
6369
|
},
|
|
5745
6370
|
view: ({ attrs, children }) => {
|
|
5746
|
-
const { position = 'left', mode = 'overlay', width = 300, className = '', showBackdrop = true, animationDuration = 300, fixed = false } = attrs;
|
|
6371
|
+
const { position = 'left', mode = 'overlay', width = 300, className = '', showBackdrop = true, animationDuration = 300, fixed = false, } = attrs;
|
|
5747
6372
|
const isOpen = state.isOpen;
|
|
5748
6373
|
return [
|
|
5749
6374
|
// Backdrop (using existing materialize class)
|
|
5750
|
-
showBackdrop &&
|
|
5751
|
-
|
|
5752
|
-
|
|
5753
|
-
|
|
5754
|
-
|
|
5755
|
-
|
|
5756
|
-
|
|
6375
|
+
showBackdrop &&
|
|
6376
|
+
mode === 'overlay' &&
|
|
6377
|
+
m('.sidenav-overlay', {
|
|
6378
|
+
style: {
|
|
6379
|
+
display: isOpen ? 'block' : 'none',
|
|
6380
|
+
opacity: isOpen ? '1' : '0',
|
|
6381
|
+
},
|
|
6382
|
+
onclick: () => handleBackdropClick(attrs),
|
|
6383
|
+
}),
|
|
5757
6384
|
// Sidenav (using existing materialize structure)
|
|
5758
6385
|
m('ul.sidenav', {
|
|
5759
6386
|
id: state.id,
|
|
5760
|
-
class: [
|
|
5761
|
-
|
|
5762
|
-
|
|
5763
|
-
className
|
|
5764
|
-
].filter(Boolean).join(' '),
|
|
6387
|
+
class: [position === 'right' ? 'right-aligned' : '', fixed ? 'sidenav-fixed' : '', className]
|
|
6388
|
+
.filter(Boolean)
|
|
6389
|
+
.join(' ') || undefined,
|
|
5765
6390
|
style: {
|
|
5766
6391
|
width: `${width}px`,
|
|
5767
|
-
transform: isOpen ? 'translateX(0)' :
|
|
5768
|
-
|
|
5769
|
-
|
|
5770
|
-
|
|
5771
|
-
}, children)
|
|
6392
|
+
transform: isOpen ? 'translateX(0)' : position === 'left' ? 'translateX(-105%)' : 'translateX(105%)',
|
|
6393
|
+
'transition-duration': `${animationDuration}ms`,
|
|
6394
|
+
},
|
|
6395
|
+
}, children),
|
|
5772
6396
|
];
|
|
5773
|
-
}
|
|
6397
|
+
},
|
|
5774
6398
|
};
|
|
5775
6399
|
};
|
|
5776
6400
|
/**
|
|
@@ -5780,37 +6404,30 @@
|
|
|
5780
6404
|
const SidenavItem = () => {
|
|
5781
6405
|
return {
|
|
5782
6406
|
view: ({ attrs, children }) => {
|
|
5783
|
-
const { text, icon, active = false, disabled = false, onclick, href, className = '', divider = false, subheader = false } = attrs;
|
|
6407
|
+
const { text, icon, active = false, disabled = false, onclick, href, className = '', divider = false, subheader = false, } = attrs;
|
|
5784
6408
|
if (divider) {
|
|
5785
6409
|
return m('li.divider');
|
|
5786
6410
|
}
|
|
5787
6411
|
if (subheader) {
|
|
5788
6412
|
return m('li.subheader', text || children);
|
|
5789
6413
|
}
|
|
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
|
-
];
|
|
6414
|
+
const itemClasses = [active ? 'active' : '', disabled ? 'disabled' : '', className].filter(Boolean).join(' ') || undefined;
|
|
6415
|
+
const content = [icon && m('i.material-icons', icon), text || children];
|
|
5799
6416
|
if (href && !disabled) {
|
|
5800
6417
|
return m('li', { class: itemClasses }, [
|
|
5801
6418
|
m('a', {
|
|
5802
6419
|
href,
|
|
5803
|
-
onclick: disabled ? undefined : onclick
|
|
5804
|
-
}, content)
|
|
6420
|
+
onclick: disabled ? undefined : onclick,
|
|
6421
|
+
}, content),
|
|
5805
6422
|
]);
|
|
5806
6423
|
}
|
|
5807
6424
|
return m('li', { class: itemClasses }, [
|
|
5808
6425
|
m('a', {
|
|
5809
6426
|
onclick: disabled ? undefined : onclick,
|
|
5810
|
-
href: '#!'
|
|
5811
|
-
}, content)
|
|
6427
|
+
href: '#!',
|
|
6428
|
+
}, content),
|
|
5812
6429
|
]);
|
|
5813
|
-
}
|
|
6430
|
+
},
|
|
5814
6431
|
};
|
|
5815
6432
|
};
|
|
5816
6433
|
/**
|
|
@@ -5861,7 +6478,7 @@
|
|
|
5861
6478
|
const Breadcrumb = () => {
|
|
5862
6479
|
return {
|
|
5863
6480
|
view: ({ attrs }) => {
|
|
5864
|
-
const { items = [], separator = 'chevron_right', className = '', showIcons = false, maxItems, showHome = false } = attrs;
|
|
6481
|
+
const { items = [], separator = 'chevron_right', className = '', showIcons = false, maxItems, showHome = false, } = attrs;
|
|
5865
6482
|
if (items.length === 0) {
|
|
5866
6483
|
return null;
|
|
5867
6484
|
}
|
|
@@ -5870,52 +6487,46 @@
|
|
|
5870
6487
|
if (maxItems && items.length > maxItems) {
|
|
5871
6488
|
const firstItem = items[0];
|
|
5872
6489
|
const lastItems = items.slice(-(maxItems - 2));
|
|
5873
|
-
displayItems = [
|
|
5874
|
-
firstItem,
|
|
5875
|
-
{ text: '...', disabled: true, className: 'breadcrumb-ellipsis' },
|
|
5876
|
-
...lastItems
|
|
5877
|
-
];
|
|
6490
|
+
displayItems = [firstItem, { text: '...', disabled: true, className: 'breadcrumb-ellipsis' }, ...lastItems];
|
|
5878
6491
|
}
|
|
5879
6492
|
return m('nav.breadcrumb', { class: className }, [
|
|
5880
|
-
m('ol.breadcrumb-list', displayItems
|
|
6493
|
+
m('ol.breadcrumb-list', displayItems
|
|
6494
|
+
.map((item, index) => {
|
|
5881
6495
|
const isLast = index === displayItems.length - 1;
|
|
5882
6496
|
const isFirst = index === 0;
|
|
5883
6497
|
return [
|
|
5884
6498
|
// Breadcrumb item
|
|
5885
6499
|
m('li.breadcrumb-item', {
|
|
5886
|
-
class: [
|
|
5887
|
-
|
|
5888
|
-
|
|
5889
|
-
item.className || ''
|
|
5890
|
-
].filter(Boolean).join(' ')
|
|
6500
|
+
class: [item.active || isLast ? 'active' : '', item.disabled ? 'disabled' : '', item.className || '']
|
|
6501
|
+
.filter(Boolean)
|
|
6502
|
+
.join(' ') || undefined,
|
|
5891
6503
|
}, [
|
|
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
|
-
|
|
6504
|
+
item.href && !item.disabled && !isLast
|
|
6505
|
+
? // Link item
|
|
6506
|
+
m('a.breadcrumb-link', {
|
|
6507
|
+
href: item.href,
|
|
6508
|
+
onclick: item.onclick,
|
|
6509
|
+
}, [
|
|
6510
|
+
showIcons && item.icon && m('i.material-icons.breadcrumb-icon', item.icon),
|
|
6511
|
+
showHome && isFirst && !item.icon && m('i.material-icons.breadcrumb-icon', 'home'),
|
|
6512
|
+
m('span.breadcrumb-text', item.text),
|
|
6513
|
+
])
|
|
6514
|
+
: // Text item (active or disabled)
|
|
6515
|
+
m('span.breadcrumb-text', {
|
|
6516
|
+
onclick: item.disabled ? undefined : item.onclick,
|
|
6517
|
+
}, [
|
|
6518
|
+
showIcons && item.icon && m('i.material-icons.breadcrumb-icon', item.icon),
|
|
6519
|
+
showHome && isFirst && !item.icon && m('i.material-icons.breadcrumb-icon', 'home'),
|
|
6520
|
+
item.text,
|
|
6521
|
+
]),
|
|
5910
6522
|
]),
|
|
5911
6523
|
// Separator (except for last item)
|
|
5912
|
-
!isLast && m('li.breadcrumb-separator', [
|
|
5913
|
-
m('i.material-icons', separator)
|
|
5914
|
-
])
|
|
6524
|
+
!isLast && m('li.breadcrumb-separator', [m('i.material-icons', separator)]),
|
|
5915
6525
|
];
|
|
5916
|
-
})
|
|
6526
|
+
})
|
|
6527
|
+
.reduce((acc, val) => acc.concat(val), [])),
|
|
5917
6528
|
]);
|
|
5918
|
-
}
|
|
6529
|
+
},
|
|
5919
6530
|
};
|
|
5920
6531
|
};
|
|
5921
6532
|
/**
|
|
@@ -5928,7 +6539,7 @@
|
|
|
5928
6539
|
items.push({
|
|
5929
6540
|
text: 'Home',
|
|
5930
6541
|
href: basePath,
|
|
5931
|
-
icon: 'home'
|
|
6542
|
+
icon: 'home',
|
|
5932
6543
|
});
|
|
5933
6544
|
// Add path segments
|
|
5934
6545
|
let currentPath = basePath;
|
|
@@ -5938,7 +6549,7 @@
|
|
|
5938
6549
|
items.push({
|
|
5939
6550
|
text: segment.charAt(0).toUpperCase() + segment.slice(1).replace(/-/g, ' '),
|
|
5940
6551
|
href: isLast ? undefined : currentPath,
|
|
5941
|
-
active: isLast
|
|
6552
|
+
active: isLast,
|
|
5942
6553
|
});
|
|
5943
6554
|
});
|
|
5944
6555
|
return items;
|
|
@@ -5957,19 +6568,18 @@
|
|
|
5957
6568
|
items.push({
|
|
5958
6569
|
text: 'Home',
|
|
5959
6570
|
href: '/',
|
|
5960
|
-
icon: 'home'
|
|
6571
|
+
icon: 'home',
|
|
5961
6572
|
});
|
|
5962
6573
|
let currentPath = '';
|
|
5963
6574
|
segments.forEach((segment, index) => {
|
|
5964
6575
|
currentPath += '/' + segment;
|
|
5965
6576
|
const isLast = index === segments.length - 1;
|
|
5966
6577
|
// Use custom text from config or format segment
|
|
5967
|
-
const text = routeConfig[currentPath] ||
|
|
5968
|
-
segment.charAt(0).toUpperCase() + segment.slice(1).replace(/-/g, ' ');
|
|
6578
|
+
const text = routeConfig[currentPath] || segment.charAt(0).toUpperCase() + segment.slice(1).replace(/-/g, ' ');
|
|
5969
6579
|
items.push({
|
|
5970
6580
|
text,
|
|
5971
6581
|
href: isLast ? undefined : currentPath,
|
|
5972
|
-
active: isLast
|
|
6582
|
+
active: isLast,
|
|
5973
6583
|
});
|
|
5974
6584
|
});
|
|
5975
6585
|
return items;
|
|
@@ -5981,7 +6591,7 @@
|
|
|
5981
6591
|
return hierarchy.map((item, index) => ({
|
|
5982
6592
|
text: item[textKey],
|
|
5983
6593
|
href: index === hierarchy.length - 1 ? undefined : item[pathKey],
|
|
5984
|
-
active: index === hierarchy.length - 1
|
|
6594
|
+
active: index === hierarchy.length - 1,
|
|
5985
6595
|
}));
|
|
5986
6596
|
}
|
|
5987
6597
|
}
|
|
@@ -6115,7 +6725,7 @@
|
|
|
6115
6725
|
hasError ? 'error' : '',
|
|
6116
6726
|
step.disabled ? 'disabled' : '',
|
|
6117
6727
|
step.optional ? 'optional' : ''
|
|
6118
|
-
].filter(Boolean).join(' '),
|
|
6728
|
+
].filter(Boolean).join(' ') || undefined,
|
|
6119
6729
|
onclick: allowHeaderNavigation && !step.disabled ?
|
|
6120
6730
|
() => goToStep(index, attrs) : undefined
|
|
6121
6731
|
}, [
|
|
@@ -6189,6 +6799,313 @@
|
|
|
6189
6799
|
};
|
|
6190
6800
|
};
|
|
6191
6801
|
|
|
6802
|
+
// Utility function to check if a node is the last in its branch
|
|
6803
|
+
const isNodeLastInBranch = (nodePath, rootNodes) => {
|
|
6804
|
+
// Navigate to the node's position and check if it's the last child at every level
|
|
6805
|
+
let currentNodes = rootNodes;
|
|
6806
|
+
for (let i = 0; i < nodePath.length; i++) {
|
|
6807
|
+
const index = nodePath[i];
|
|
6808
|
+
const isLastAtThisLevel = index === currentNodes.length - 1;
|
|
6809
|
+
// If this is not the last child at this level, then this node is not last in branch
|
|
6810
|
+
if (!isLastAtThisLevel) {
|
|
6811
|
+
return false;
|
|
6812
|
+
}
|
|
6813
|
+
// Move to the next level if it exists
|
|
6814
|
+
if (i < nodePath.length - 1) {
|
|
6815
|
+
const currentNode = currentNodes[index];
|
|
6816
|
+
if (currentNode.children) {
|
|
6817
|
+
currentNodes = currentNode.children;
|
|
6818
|
+
}
|
|
6819
|
+
}
|
|
6820
|
+
}
|
|
6821
|
+
return true;
|
|
6822
|
+
};
|
|
6823
|
+
const TreeNodeComponent = () => {
|
|
6824
|
+
return {
|
|
6825
|
+
view: ({ attrs }) => {
|
|
6826
|
+
const { node, level, isSelected, isExpanded, isFocused, showConnectors, iconType, selectionMode, onToggleExpand, onToggleSelect, onFocus, } = attrs;
|
|
6827
|
+
const hasChildren = node.children && node.children.length > 0;
|
|
6828
|
+
const indentLevel = level * 24; // 24px per level
|
|
6829
|
+
return m('li.tree-node', {
|
|
6830
|
+
class: [
|
|
6831
|
+
isSelected && 'selected',
|
|
6832
|
+
isFocused && 'focused',
|
|
6833
|
+
node.disabled && 'disabled',
|
|
6834
|
+
hasChildren && 'has-children',
|
|
6835
|
+
attrs.isLastInBranch && 'tree-last-in-branch',
|
|
6836
|
+
]
|
|
6837
|
+
.filter(Boolean)
|
|
6838
|
+
.join(' ') || undefined,
|
|
6839
|
+
'data-node-id': node.id,
|
|
6840
|
+
'data-level': level,
|
|
6841
|
+
}, [
|
|
6842
|
+
// Node content
|
|
6843
|
+
m('.tree-node-content', {
|
|
6844
|
+
style: {
|
|
6845
|
+
paddingLeft: `${indentLevel}px`,
|
|
6846
|
+
},
|
|
6847
|
+
onclick: node.disabled
|
|
6848
|
+
? undefined
|
|
6849
|
+
: () => {
|
|
6850
|
+
if (selectionMode !== 'none') {
|
|
6851
|
+
onToggleSelect(node.id);
|
|
6852
|
+
}
|
|
6853
|
+
onFocus(node.id);
|
|
6854
|
+
},
|
|
6855
|
+
onkeydown: (e) => {
|
|
6856
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
6857
|
+
e.preventDefault();
|
|
6858
|
+
if (!node.disabled && selectionMode !== 'none') {
|
|
6859
|
+
onToggleSelect(node.id);
|
|
6860
|
+
}
|
|
6861
|
+
}
|
|
6862
|
+
},
|
|
6863
|
+
tabindex: node.disabled ? -1 : 0,
|
|
6864
|
+
role: selectionMode === 'multiple' ? 'option' : 'treeitem',
|
|
6865
|
+
'aria-selected': selectionMode !== 'none' ? isSelected.toString() : undefined,
|
|
6866
|
+
'aria-expanded': hasChildren ? isExpanded.toString() : undefined,
|
|
6867
|
+
'aria-disabled': node.disabled ? 'true' : undefined,
|
|
6868
|
+
}, [
|
|
6869
|
+
// Connector lines
|
|
6870
|
+
showConnectors &&
|
|
6871
|
+
level > 0 &&
|
|
6872
|
+
m('.tree-connectors', Array.from({ length: level }, (_, i) => m('.tree-connector', {
|
|
6873
|
+
key: i,
|
|
6874
|
+
style: { left: `${i * 24 + 12}px` },
|
|
6875
|
+
}))),
|
|
6876
|
+
// Expand/collapse icon or spacer
|
|
6877
|
+
hasChildren
|
|
6878
|
+
? m('.tree-expand-icon', {
|
|
6879
|
+
onclick: (e) => {
|
|
6880
|
+
e.stopPropagation();
|
|
6881
|
+
if (!node.disabled) {
|
|
6882
|
+
onToggleExpand(node.id);
|
|
6883
|
+
}
|
|
6884
|
+
},
|
|
6885
|
+
class: iconType,
|
|
6886
|
+
}, [
|
|
6887
|
+
iconType === 'plus-minus'
|
|
6888
|
+
? m('span.tree-plus-minus', isExpanded ? '−' : '+')
|
|
6889
|
+
: iconType === 'triangle'
|
|
6890
|
+
? m('span.tree-triangle', { class: isExpanded ? 'expanded' : undefined }, '▶')
|
|
6891
|
+
: iconType === 'chevron'
|
|
6892
|
+
? m(MaterialIcon, {
|
|
6893
|
+
name: 'chevron',
|
|
6894
|
+
direction: isExpanded ? 'down' : 'right',
|
|
6895
|
+
class: 'tree-chevron-icon',
|
|
6896
|
+
})
|
|
6897
|
+
: m(MaterialIcon, {
|
|
6898
|
+
name: 'caret',
|
|
6899
|
+
direction: isExpanded ? 'down' : 'right',
|
|
6900
|
+
class: 'tree-caret-icon',
|
|
6901
|
+
}),
|
|
6902
|
+
])
|
|
6903
|
+
: m('.tree-expand-spacer'), // Spacer for alignment
|
|
6904
|
+
// Selection indicator for multiple selection
|
|
6905
|
+
selectionMode === 'multiple' &&
|
|
6906
|
+
m('.tree-selection-indicator', [
|
|
6907
|
+
m('input[type=checkbox]', {
|
|
6908
|
+
checked: isSelected,
|
|
6909
|
+
disabled: node.disabled,
|
|
6910
|
+
onchange: () => {
|
|
6911
|
+
if (!node.disabled) {
|
|
6912
|
+
onToggleSelect(node.id);
|
|
6913
|
+
}
|
|
6914
|
+
},
|
|
6915
|
+
onclick: (e) => e.stopPropagation(),
|
|
6916
|
+
}),
|
|
6917
|
+
]),
|
|
6918
|
+
// Node icon (optional)
|
|
6919
|
+
node.icon && m('i.tree-node-icon.material-icons', node.icon),
|
|
6920
|
+
// Node label
|
|
6921
|
+
m('span.tree-node-label', node.label),
|
|
6922
|
+
]),
|
|
6923
|
+
// Children (recursive)
|
|
6924
|
+
hasChildren &&
|
|
6925
|
+
isExpanded &&
|
|
6926
|
+
m('ul.tree-children', {
|
|
6927
|
+
role: 'group',
|
|
6928
|
+
'aria-expanded': 'true',
|
|
6929
|
+
}, node.children.map((child, childIndex) => {
|
|
6930
|
+
var _a, _b, _c, _d, _e, _f;
|
|
6931
|
+
// Calculate state for each child using treeState
|
|
6932
|
+
const childIsSelected = (_b = (_a = attrs.treeState) === null || _a === void 0 ? void 0 : _a.selectedIds.has(child.id)) !== null && _b !== void 0 ? _b : false;
|
|
6933
|
+
const childIsExpanded = (_d = (_c = attrs.treeState) === null || _c === void 0 ? void 0 : _c.expandedIds.has(child.id)) !== null && _d !== void 0 ? _d : false;
|
|
6934
|
+
const childIsFocused = ((_e = attrs.treeState) === null || _e === void 0 ? void 0 : _e.focusedNodeId) === child.id;
|
|
6935
|
+
// Calculate if this child is last in branch
|
|
6936
|
+
const childPath = [...(attrs.currentPath || []), childIndex];
|
|
6937
|
+
const childIsLastInBranch = ((_f = attrs.treeAttrs) === null || _f === void 0 ? void 0 : _f.data) ?
|
|
6938
|
+
isNodeLastInBranch(childPath, attrs.treeAttrs.data) : false;
|
|
6939
|
+
return m(TreeNodeComponent, {
|
|
6940
|
+
key: child.id,
|
|
6941
|
+
node: child,
|
|
6942
|
+
level: level + 1,
|
|
6943
|
+
isSelected: childIsSelected,
|
|
6944
|
+
isExpanded: childIsExpanded,
|
|
6945
|
+
isFocused: childIsFocused,
|
|
6946
|
+
showConnectors,
|
|
6947
|
+
iconType,
|
|
6948
|
+
selectionMode,
|
|
6949
|
+
onToggleExpand,
|
|
6950
|
+
onToggleSelect,
|
|
6951
|
+
onFocus,
|
|
6952
|
+
isLastInBranch: childIsLastInBranch,
|
|
6953
|
+
currentPath: childPath,
|
|
6954
|
+
treeState: attrs.treeState,
|
|
6955
|
+
treeAttrs: attrs.treeAttrs,
|
|
6956
|
+
});
|
|
6957
|
+
})),
|
|
6958
|
+
]);
|
|
6959
|
+
},
|
|
6960
|
+
};
|
|
6961
|
+
};
|
|
6962
|
+
const TreeView = () => {
|
|
6963
|
+
const state = {
|
|
6964
|
+
selectedIds: new Set(),
|
|
6965
|
+
expandedIds: new Set(),
|
|
6966
|
+
focusedNodeId: null,
|
|
6967
|
+
treeMap: new Map(),
|
|
6968
|
+
};
|
|
6969
|
+
const buildTreeMap = (nodes, map) => {
|
|
6970
|
+
nodes.forEach((node) => {
|
|
6971
|
+
map.set(node.id, node);
|
|
6972
|
+
if (node.children) {
|
|
6973
|
+
buildTreeMap(node.children, map);
|
|
6974
|
+
}
|
|
6975
|
+
});
|
|
6976
|
+
};
|
|
6977
|
+
const initializeExpandedNodes = (nodes) => {
|
|
6978
|
+
nodes.forEach((node) => {
|
|
6979
|
+
if (node.expanded) {
|
|
6980
|
+
state.expandedIds.add(node.id);
|
|
6981
|
+
}
|
|
6982
|
+
if (node.children) {
|
|
6983
|
+
initializeExpandedNodes(node.children);
|
|
6984
|
+
}
|
|
6985
|
+
});
|
|
6986
|
+
};
|
|
6987
|
+
const handleToggleExpand = (nodeId, attrs) => {
|
|
6988
|
+
var _a;
|
|
6989
|
+
const isExpanded = state.expandedIds.has(nodeId);
|
|
6990
|
+
if (isExpanded) {
|
|
6991
|
+
state.expandedIds.delete(nodeId);
|
|
6992
|
+
}
|
|
6993
|
+
else {
|
|
6994
|
+
state.expandedIds.add(nodeId);
|
|
6995
|
+
}
|
|
6996
|
+
(_a = attrs.onexpand) === null || _a === void 0 ? void 0 : _a.call(attrs, { nodeId, expanded: !isExpanded });
|
|
6997
|
+
};
|
|
6998
|
+
const handleToggleSelect = (nodeId, attrs) => {
|
|
6999
|
+
var _a;
|
|
7000
|
+
const { selectionMode = 'single' } = attrs;
|
|
7001
|
+
if (selectionMode === 'single') {
|
|
7002
|
+
state.selectedIds.clear();
|
|
7003
|
+
state.selectedIds.add(nodeId);
|
|
7004
|
+
}
|
|
7005
|
+
else if (selectionMode === 'multiple') {
|
|
7006
|
+
if (state.selectedIds.has(nodeId)) {
|
|
7007
|
+
state.selectedIds.delete(nodeId);
|
|
7008
|
+
}
|
|
7009
|
+
else {
|
|
7010
|
+
state.selectedIds.add(nodeId);
|
|
7011
|
+
}
|
|
7012
|
+
}
|
|
7013
|
+
(_a = attrs.onselection) === null || _a === void 0 ? void 0 : _a.call(attrs, Array.from(state.selectedIds));
|
|
7014
|
+
};
|
|
7015
|
+
const handleFocus = (nodeId) => {
|
|
7016
|
+
state.focusedNodeId = nodeId;
|
|
7017
|
+
};
|
|
7018
|
+
const renderNodes = (nodes, attrs, level = 0, parentPath = []) => {
|
|
7019
|
+
return nodes.map((node, index) => {
|
|
7020
|
+
var _a, _b, _c;
|
|
7021
|
+
const isSelected = state.selectedIds.has(node.id);
|
|
7022
|
+
const isExpanded = state.expandedIds.has(node.id);
|
|
7023
|
+
const isFocused = state.focusedNodeId === node.id;
|
|
7024
|
+
const currentPath = [...parentPath, index];
|
|
7025
|
+
const isLastInBranch = isNodeLastInBranch(currentPath, attrs.data);
|
|
7026
|
+
return m(TreeNodeComponent, {
|
|
7027
|
+
key: node.id,
|
|
7028
|
+
node,
|
|
7029
|
+
level,
|
|
7030
|
+
isSelected,
|
|
7031
|
+
isExpanded,
|
|
7032
|
+
isFocused,
|
|
7033
|
+
showConnectors: (_a = attrs.showConnectors) !== null && _a !== void 0 ? _a : true,
|
|
7034
|
+
iconType: (_b = attrs.iconType) !== null && _b !== void 0 ? _b : 'caret',
|
|
7035
|
+
selectionMode: (_c = attrs.selectionMode) !== null && _c !== void 0 ? _c : 'single',
|
|
7036
|
+
onToggleExpand: (nodeId) => handleToggleExpand(nodeId, attrs),
|
|
7037
|
+
onToggleSelect: (nodeId) => handleToggleSelect(nodeId, attrs),
|
|
7038
|
+
onFocus: handleFocus,
|
|
7039
|
+
isLastInBranch,
|
|
7040
|
+
currentPath,
|
|
7041
|
+
// Pass state and attrs for recursive rendering
|
|
7042
|
+
treeState: state,
|
|
7043
|
+
treeAttrs: attrs,
|
|
7044
|
+
});
|
|
7045
|
+
});
|
|
7046
|
+
};
|
|
7047
|
+
return {
|
|
7048
|
+
oninit: ({ attrs }) => {
|
|
7049
|
+
// Build internal tree map for efficient lookups
|
|
7050
|
+
buildTreeMap(attrs.data, state.treeMap);
|
|
7051
|
+
// Initialize expanded nodes from data
|
|
7052
|
+
initializeExpandedNodes(attrs.data);
|
|
7053
|
+
// Initialize selected nodes from props
|
|
7054
|
+
if (attrs.selectedIds) {
|
|
7055
|
+
state.selectedIds = new Set(attrs.selectedIds);
|
|
7056
|
+
}
|
|
7057
|
+
},
|
|
7058
|
+
onupdate: ({ attrs }) => {
|
|
7059
|
+
// Sync selectedIds prop with internal state
|
|
7060
|
+
if (attrs.selectedIds) {
|
|
7061
|
+
const newSelection = new Set(attrs.selectedIds);
|
|
7062
|
+
if (newSelection.size !== state.selectedIds.size ||
|
|
7063
|
+
!Array.from(newSelection).every((id) => state.selectedIds.has(id))) {
|
|
7064
|
+
state.selectedIds = newSelection;
|
|
7065
|
+
}
|
|
7066
|
+
}
|
|
7067
|
+
},
|
|
7068
|
+
view: ({ attrs }) => {
|
|
7069
|
+
const { data, className, style, id, selectionMode = 'single', showConnectors = true } = attrs;
|
|
7070
|
+
return m('div.tree-view', {
|
|
7071
|
+
class: [
|
|
7072
|
+
className,
|
|
7073
|
+
showConnectors && 'show-connectors'
|
|
7074
|
+
].filter(Boolean).join(' ') || undefined,
|
|
7075
|
+
style,
|
|
7076
|
+
id,
|
|
7077
|
+
role: selectionMode === 'multiple' ? 'listbox' : 'tree',
|
|
7078
|
+
'aria-multiselectable': selectionMode === 'multiple' ? 'true' : 'false',
|
|
7079
|
+
}, [
|
|
7080
|
+
m('ul.tree-root', {
|
|
7081
|
+
role: 'group',
|
|
7082
|
+
}, renderNodes(data, attrs)),
|
|
7083
|
+
]);
|
|
7084
|
+
},
|
|
7085
|
+
};
|
|
7086
|
+
};
|
|
7087
|
+
|
|
7088
|
+
/**
|
|
7089
|
+
* @fileoverview Core TypeScript utility types for mithril-materialized library
|
|
7090
|
+
* These types improve type safety and developer experience across all components
|
|
7091
|
+
*/
|
|
7092
|
+
/**
|
|
7093
|
+
* Type guard to check if validation result indicates success
|
|
7094
|
+
* @param result - The validation result to check
|
|
7095
|
+
* @returns True if validation passed
|
|
7096
|
+
*/
|
|
7097
|
+
const isValidationSuccess = (result) => result === true || result === '';
|
|
7098
|
+
/**
|
|
7099
|
+
* Type guard to check if validation result indicates an error
|
|
7100
|
+
* @param result - The validation result to check
|
|
7101
|
+
* @returns True if validation failed
|
|
7102
|
+
*/
|
|
7103
|
+
const isValidationError = (result) => !isValidationSuccess(result);
|
|
7104
|
+
// ============================================================================
|
|
7105
|
+
// EXPORTS
|
|
7106
|
+
// ============================================================================
|
|
7107
|
+
// All types are already exported via individual export declarations above
|
|
7108
|
+
|
|
6192
7109
|
exports.AnchorItem = AnchorItem;
|
|
6193
7110
|
exports.Autocomplete = Autocomplete;
|
|
6194
7111
|
exports.Breadcrumb = Breadcrumb;
|
|
@@ -6203,6 +7120,7 @@
|
|
|
6203
7120
|
exports.CollapsibleItem = CollapsibleItem;
|
|
6204
7121
|
exports.Collection = Collection;
|
|
6205
7122
|
exports.ColorInput = ColorInput;
|
|
7123
|
+
exports.DataTable = DataTable;
|
|
6206
7124
|
exports.DatePicker = DatePicker;
|
|
6207
7125
|
exports.Dropdown = Dropdown;
|
|
6208
7126
|
exports.EmailInput = EmailInput;
|
|
@@ -6218,10 +7136,12 @@
|
|
|
6218
7136
|
exports.ListItem = ListItem;
|
|
6219
7137
|
exports.Mandatory = Mandatory;
|
|
6220
7138
|
exports.MaterialBox = MaterialBox;
|
|
7139
|
+
exports.MaterialIcon = MaterialIcon;
|
|
6221
7140
|
exports.ModalPanel = ModalPanel;
|
|
6222
7141
|
exports.NumberInput = NumberInput;
|
|
6223
7142
|
exports.Options = Options;
|
|
6224
7143
|
exports.Pagination = Pagination;
|
|
7144
|
+
exports.PaginationControls = PaginationControls;
|
|
6225
7145
|
exports.Parallax = Parallax;
|
|
6226
7146
|
exports.PasswordInput = PasswordInput;
|
|
6227
7147
|
exports.Pushpin = Pushpin;
|
|
@@ -6251,6 +7171,7 @@
|
|
|
6251
7171
|
exports.ToastComponent = ToastComponent;
|
|
6252
7172
|
exports.Tooltip = Tooltip;
|
|
6253
7173
|
exports.TooltipComponent = TooltipComponent;
|
|
7174
|
+
exports.TreeView = TreeView;
|
|
6254
7175
|
exports.UrlInput = UrlInput;
|
|
6255
7176
|
exports.Wizard = Wizard;
|
|
6256
7177
|
exports.createBreadcrumb = createBreadcrumb;
|
|
@@ -6258,6 +7179,8 @@
|
|
|
6258
7179
|
exports.initPushpins = initPushpins;
|
|
6259
7180
|
exports.initTooltips = initTooltips;
|
|
6260
7181
|
exports.isNumeric = isNumeric;
|
|
7182
|
+
exports.isValidationError = isValidationError;
|
|
7183
|
+
exports.isValidationSuccess = isValidationSuccess;
|
|
6261
7184
|
exports.padLeft = padLeft;
|
|
6262
7185
|
exports.range = range;
|
|
6263
7186
|
exports.toast = toast;
|