mithril-materialized 3.5.10 → 3.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components.css +0 -1
- package/dist/core.css +4 -0
- package/dist/dropdown.d.ts +2 -0
- package/dist/forms.css +4 -0
- package/dist/index.css +4 -1
- package/dist/index.esm.js +129 -34
- package/dist/index.js +129 -33
- package/dist/index.min.css +1 -1
- package/dist/index.umd.js +129 -33
- package/dist/search-select.d.ts +12 -1
- package/dist/select.d.ts +4 -0
- package/dist/utils.d.ts +19 -0
- package/package.json +1 -1
- package/sass/components/_collapsible.scss +0 -1
- package/sass/components/forms/_select.scss +20 -16
package/dist/index.umd.js
CHANGED
|
@@ -66,6 +66,28 @@
|
|
|
66
66
|
};
|
|
67
67
|
/** Check if a string or number is numeric. @see https://stackoverflow.com/a/9716488/319711 */
|
|
68
68
|
const isNumeric = (n) => !isNaN(parseFloat(n)) && isFinite(n);
|
|
69
|
+
/**
|
|
70
|
+
* Sort options array based on sorting configuration
|
|
71
|
+
* @param options - Array of options to sort
|
|
72
|
+
* @param sortConfig - Sort configuration: 'asc', 'desc', 'none', or custom comparator function
|
|
73
|
+
* @returns Sorted array (or original if 'none' or undefined)
|
|
74
|
+
*/
|
|
75
|
+
const sortOptions = (options, sortConfig) => {
|
|
76
|
+
if (!sortConfig || sortConfig === 'none') {
|
|
77
|
+
return options;
|
|
78
|
+
}
|
|
79
|
+
const sorted = [...options]; // Create a copy to avoid mutating original
|
|
80
|
+
if (typeof sortConfig === 'function') {
|
|
81
|
+
return sorted.sort(sortConfig);
|
|
82
|
+
}
|
|
83
|
+
// Sort by label, fallback to id if no label
|
|
84
|
+
return sorted.sort((a, b) => {
|
|
85
|
+
const aLabel = (a.label || a.id.toString()).toLowerCase();
|
|
86
|
+
const bLabel = (b.label || b.id.toString()).toLowerCase();
|
|
87
|
+
const comparison = aLabel.localeCompare(bLabel);
|
|
88
|
+
return sortConfig === 'asc' ? comparison : -comparison;
|
|
89
|
+
});
|
|
90
|
+
};
|
|
69
91
|
/**
|
|
70
92
|
* Pad left, default width 2 with a '0'
|
|
71
93
|
*
|
|
@@ -4543,7 +4565,7 @@
|
|
|
4543
4565
|
// Create dropdown with proper positioning
|
|
4544
4566
|
const dropdownVnode = m('ul.dropdown-content.select-dropdown', {
|
|
4545
4567
|
tabindex: 0,
|
|
4546
|
-
style: getPortalStyles(state.inputRef),
|
|
4568
|
+
style: Object.assign(Object.assign({}, getPortalStyles(state.inputRef)), (attrs.maxHeight ? { maxHeight: attrs.maxHeight } : {})),
|
|
4547
4569
|
oncreate: ({ dom }) => {
|
|
4548
4570
|
state.dropdownRef = dom;
|
|
4549
4571
|
},
|
|
@@ -4655,7 +4677,7 @@
|
|
|
4655
4677
|
onremove: () => {
|
|
4656
4678
|
state.dropdownRef = null;
|
|
4657
4679
|
},
|
|
4658
|
-
style: getDropdownStyles(state.inputRef, true, items, true),
|
|
4680
|
+
style: Object.assign(Object.assign({}, getDropdownStyles(state.inputRef, true, items, true)), (attrs.maxHeight ? { maxHeight: attrs.maxHeight } : {})),
|
|
4659
4681
|
}, items.map((item) => {
|
|
4660
4682
|
if (item.divider) {
|
|
4661
4683
|
return m('li.divider');
|
|
@@ -6429,7 +6451,7 @@
|
|
|
6429
6451
|
// Create dropdown with proper positioning
|
|
6430
6452
|
const dropdownVnode = m('ul.dropdown-content.select-dropdown', {
|
|
6431
6453
|
tabindex: 0,
|
|
6432
|
-
style: getPortalStyles(state.inputRef),
|
|
6454
|
+
style: Object.assign(Object.assign({}, getPortalStyles(state.inputRef)), (attrs.maxHeight ? { maxHeight: attrs.maxHeight } : {})),
|
|
6433
6455
|
oncreate: ({ dom }) => {
|
|
6434
6456
|
state.dropdownRef = dom;
|
|
6435
6457
|
},
|
|
@@ -6498,7 +6520,8 @@
|
|
|
6498
6520
|
selectedIds = state.internalSelectedIds;
|
|
6499
6521
|
}
|
|
6500
6522
|
const finalClassName = newRow ? `${className} clear` : className;
|
|
6501
|
-
const
|
|
6523
|
+
const selectedOptionsUnsorted = options.filter((opt) => isSelected(opt.id, selectedIds));
|
|
6524
|
+
const selectedOptions = sortOptions(selectedOptionsUnsorted, attrs.sortSelected);
|
|
6502
6525
|
// Update portal dropdown when inside modal
|
|
6503
6526
|
if (state.isInsideModal) {
|
|
6504
6527
|
updatePortalDropdown(attrs, selectedIds, multiple, placeholder);
|
|
@@ -6543,7 +6566,7 @@
|
|
|
6543
6566
|
onremove: () => {
|
|
6544
6567
|
state.dropdownRef = null;
|
|
6545
6568
|
},
|
|
6546
|
-
style: getDropdownStyles(state.inputRef, true, options),
|
|
6569
|
+
style: Object.assign(Object.assign({}, getDropdownStyles(state.inputRef, true, options)), (attrs.maxHeight ? { maxHeight: attrs.maxHeight } : {})),
|
|
6547
6570
|
}, renderDropdownContent(attrs, selectedIds, multiple, placeholder)),
|
|
6548
6571
|
m(MaterialIcon, {
|
|
6549
6572
|
name: 'caret',
|
|
@@ -6793,7 +6816,7 @@
|
|
|
6793
6816
|
]),
|
|
6794
6817
|
};
|
|
6795
6818
|
const DropdownOption = {
|
|
6796
|
-
view: ({ attrs: { option, index, selectedIds, isFocused, onToggle, onMouseOver } }) => {
|
|
6819
|
+
view: ({ attrs: { option, index, selectedIds, isFocused, onToggle, onMouseOver, showCheckbox } }) => {
|
|
6797
6820
|
const checkboxId = `search-select-option-${option.id}`;
|
|
6798
6821
|
const optionLabel = option.label || option.id.toString();
|
|
6799
6822
|
return m('li', {
|
|
@@ -6810,11 +6833,12 @@
|
|
|
6810
6833
|
}
|
|
6811
6834
|
},
|
|
6812
6835
|
}, m('label', { for: checkboxId, class: 'search-select-option-label' }, [
|
|
6813
|
-
|
|
6814
|
-
|
|
6815
|
-
|
|
6816
|
-
|
|
6817
|
-
|
|
6836
|
+
showCheckbox &&
|
|
6837
|
+
m('input', {
|
|
6838
|
+
type: 'checkbox',
|
|
6839
|
+
id: checkboxId,
|
|
6840
|
+
checked: selectedIds.includes(option.id),
|
|
6841
|
+
}),
|
|
6818
6842
|
m('span', optionLabel),
|
|
6819
6843
|
]));
|
|
6820
6844
|
},
|
|
@@ -6832,6 +6856,7 @@
|
|
|
6832
6856
|
dropdownRef: null,
|
|
6833
6857
|
focusedIndex: -1,
|
|
6834
6858
|
internalSelectedIds: [],
|
|
6859
|
+
createdOptions: [],
|
|
6835
6860
|
};
|
|
6836
6861
|
const isControlled = (attrs) => attrs.checkedId !== undefined && typeof attrs.onchange === 'function';
|
|
6837
6862
|
const componentId = uniqueId();
|
|
@@ -6874,7 +6899,10 @@
|
|
|
6874
6899
|
// Handle add new option
|
|
6875
6900
|
return 'addNew';
|
|
6876
6901
|
}
|
|
6877
|
-
else if (state.focusedIndex < filteredOptions.length)
|
|
6902
|
+
else if (state.focusedIndex < filteredOptions.length) {
|
|
6903
|
+
// This will be handled in the view method where attrs are available
|
|
6904
|
+
return 'selectOption';
|
|
6905
|
+
}
|
|
6878
6906
|
}
|
|
6879
6907
|
break;
|
|
6880
6908
|
case 'Escape':
|
|
@@ -6885,11 +6913,22 @@
|
|
|
6885
6913
|
}
|
|
6886
6914
|
return null;
|
|
6887
6915
|
};
|
|
6916
|
+
// Create new option and add to state
|
|
6917
|
+
const createAndSelectOption = async (attrs) => {
|
|
6918
|
+
if (!attrs.oncreateNewOption || !state.searchTerm)
|
|
6919
|
+
return;
|
|
6920
|
+
const newOption = await attrs.oncreateNewOption(state.searchTerm);
|
|
6921
|
+
// Store the created option internally
|
|
6922
|
+
state.createdOptions.push(newOption);
|
|
6923
|
+
// Select the new option
|
|
6924
|
+
toggleOption(newOption, attrs);
|
|
6925
|
+
};
|
|
6888
6926
|
// Toggle option selection
|
|
6889
6927
|
const toggleOption = (option, attrs) => {
|
|
6890
6928
|
if (option.disabled)
|
|
6891
6929
|
return;
|
|
6892
6930
|
const controlled = isControlled(attrs);
|
|
6931
|
+
const { maxSelectedOptions } = attrs;
|
|
6893
6932
|
// Get current selected IDs from props or internal state
|
|
6894
6933
|
const currentSelectedIds = controlled
|
|
6895
6934
|
? attrs.checkedId !== undefined
|
|
@@ -6898,9 +6937,29 @@
|
|
|
6898
6937
|
: [attrs.checkedId]
|
|
6899
6938
|
: []
|
|
6900
6939
|
: state.internalSelectedIds;
|
|
6901
|
-
const
|
|
6902
|
-
|
|
6903
|
-
|
|
6940
|
+
const isSelected = currentSelectedIds.includes(option.id);
|
|
6941
|
+
let newIds;
|
|
6942
|
+
if (isSelected) {
|
|
6943
|
+
// Remove if already selected
|
|
6944
|
+
newIds = currentSelectedIds.filter((id) => id !== option.id);
|
|
6945
|
+
}
|
|
6946
|
+
else {
|
|
6947
|
+
// Check if we've reached the max selection limit
|
|
6948
|
+
if (maxSelectedOptions && currentSelectedIds.length >= maxSelectedOptions) {
|
|
6949
|
+
// If max=1, replace the selection
|
|
6950
|
+
if (maxSelectedOptions === 1) {
|
|
6951
|
+
newIds = [option.id];
|
|
6952
|
+
}
|
|
6953
|
+
else {
|
|
6954
|
+
// Otherwise, don't add more
|
|
6955
|
+
return;
|
|
6956
|
+
}
|
|
6957
|
+
}
|
|
6958
|
+
else {
|
|
6959
|
+
// Add to selection
|
|
6960
|
+
newIds = [...currentSelectedIds, option.id];
|
|
6961
|
+
}
|
|
6962
|
+
}
|
|
6904
6963
|
// Update internal state for uncontrolled mode
|
|
6905
6964
|
if (!controlled) {
|
|
6906
6965
|
state.internalSelectedIds = newIds;
|
|
@@ -6962,21 +7021,32 @@
|
|
|
6962
7021
|
: [attrs.checkedId]
|
|
6963
7022
|
: []
|
|
6964
7023
|
: state.internalSelectedIds;
|
|
6965
|
-
const { options = [], oncreateNewOption, className, placeholder, searchPlaceholder = 'Search options...', noOptionsFound = 'No options found', label, i18n = {}, } = attrs;
|
|
7024
|
+
const { options = [], oncreateNewOption, className, placeholder, searchPlaceholder = 'Search options...', noOptionsFound = 'No options found', label, i18n = {}, maxDisplayedOptions, maxSelectedOptions, maxHeight, } = attrs;
|
|
6966
7025
|
// Use i18n values if provided, otherwise use defaults
|
|
6967
7026
|
const texts = {
|
|
6968
7027
|
noOptionsFound: i18n.noOptionsFound || noOptionsFound,
|
|
6969
7028
|
addNewPrefix: i18n.addNewPrefix || '+',
|
|
7029
|
+
showingXofY: i18n.showingXofY || 'Showing {shown} of {total} options',
|
|
7030
|
+
maxSelectionsReached: i18n.maxSelectionsReached || 'Maximum {max} selections reached',
|
|
6970
7031
|
};
|
|
7032
|
+
// Check if max selections is reached
|
|
7033
|
+
const isMaxSelectionsReached = maxSelectedOptions && selectedIds.length >= maxSelectedOptions;
|
|
7034
|
+
// Merge provided options with internally created options
|
|
7035
|
+
const allOptions = [...options, ...state.createdOptions];
|
|
6971
7036
|
// Get selected options for display
|
|
6972
|
-
const
|
|
7037
|
+
const selectedOptionsUnsorted = allOptions.filter((opt) => selectedIds.includes(opt.id));
|
|
7038
|
+
const selectedOptions = sortOptions(selectedOptionsUnsorted, attrs.sortSelected);
|
|
6973
7039
|
// Safely filter options
|
|
6974
|
-
const filteredOptions =
|
|
7040
|
+
const filteredOptions = allOptions.filter((option) => (option.label || option.id.toString()).toLowerCase().includes((state.searchTerm || '').toLowerCase()) &&
|
|
6975
7041
|
!selectedIds.includes(option.id));
|
|
7042
|
+
// Apply display limit if configured
|
|
7043
|
+
const totalFilteredCount = filteredOptions.length;
|
|
7044
|
+
const displayedOptions = maxDisplayedOptions ? filteredOptions.slice(0, maxDisplayedOptions) : filteredOptions;
|
|
7045
|
+
const isTruncated = maxDisplayedOptions && totalFilteredCount > maxDisplayedOptions;
|
|
6976
7046
|
// Check if we should show the "add new option" element
|
|
6977
7047
|
const showAddNew = oncreateNewOption &&
|
|
6978
7048
|
state.searchTerm &&
|
|
6979
|
-
!
|
|
7049
|
+
!displayedOptions.some((o) => (o.label || o.id.toString()).toLowerCase() === state.searchTerm.toLowerCase());
|
|
6980
7050
|
// Render the dropdown
|
|
6981
7051
|
return m('.input-field.multi-select-dropdown', { className }, [
|
|
6982
7052
|
m('.chips.chips-initial.chips-container', {
|
|
@@ -7049,7 +7119,7 @@
|
|
|
7049
7119
|
onremove: () => {
|
|
7050
7120
|
state.dropdownRef = null;
|
|
7051
7121
|
},
|
|
7052
|
-
style: getDropdownStyles(state.inputRef),
|
|
7122
|
+
style: Object.assign(Object.assign({}, getDropdownStyles(state.inputRef)), (maxHeight ? { maxHeight } : {})),
|
|
7053
7123
|
}, [
|
|
7054
7124
|
m('li', // Search Input
|
|
7055
7125
|
{
|
|
@@ -7069,41 +7139,65 @@
|
|
|
7069
7139
|
state.focusedIndex = -1; // Reset focus when typing
|
|
7070
7140
|
},
|
|
7071
7141
|
onkeydown: async (e) => {
|
|
7072
|
-
const result = handleKeyDown(e,
|
|
7142
|
+
const result = handleKeyDown(e, displayedOptions, !!showAddNew);
|
|
7073
7143
|
if (result === 'addNew' && oncreateNewOption) {
|
|
7074
|
-
|
|
7075
|
-
toggleOption(option, attrs);
|
|
7144
|
+
await createAndSelectOption(attrs);
|
|
7076
7145
|
}
|
|
7077
|
-
else if (
|
|
7078
|
-
state.focusedIndex
|
|
7079
|
-
state.focusedIndex < filteredOptions.length) {
|
|
7080
|
-
toggleOption(filteredOptions[state.focusedIndex], attrs);
|
|
7146
|
+
else if (result === 'selectOption' && state.focusedIndex < displayedOptions.length) {
|
|
7147
|
+
toggleOption(displayedOptions[state.focusedIndex], attrs);
|
|
7081
7148
|
}
|
|
7082
7149
|
},
|
|
7083
7150
|
class: 'search-select-input',
|
|
7084
7151
|
}),
|
|
7085
7152
|
]),
|
|
7086
7153
|
// No options found message or list of options
|
|
7087
|
-
...(
|
|
7154
|
+
...(displayedOptions.length === 0 && !showAddNew
|
|
7088
7155
|
? [m('li.search-select-no-options', texts.noOptionsFound)]
|
|
7089
7156
|
: []),
|
|
7157
|
+
// Truncation message
|
|
7158
|
+
...(isTruncated
|
|
7159
|
+
? [
|
|
7160
|
+
m('li.search-select-truncation-info', {
|
|
7161
|
+
style: {
|
|
7162
|
+
fontStyle: 'italic',
|
|
7163
|
+
color: 'var(--mm-text-hint, #9e9e9e)',
|
|
7164
|
+
padding: '8px 16px',
|
|
7165
|
+
cursor: 'default',
|
|
7166
|
+
},
|
|
7167
|
+
}, texts.showingXofY
|
|
7168
|
+
.replace('{shown}', displayedOptions.length.toString())
|
|
7169
|
+
.replace('{total}', totalFilteredCount.toString())),
|
|
7170
|
+
]
|
|
7171
|
+
: []),
|
|
7172
|
+
// Max selections reached message
|
|
7173
|
+
...(isMaxSelectionsReached
|
|
7174
|
+
? [
|
|
7175
|
+
m('li.search-select-max-info', {
|
|
7176
|
+
style: {
|
|
7177
|
+
fontStyle: 'italic',
|
|
7178
|
+
color: 'var(--mm-text-hint, #9e9e9e)',
|
|
7179
|
+
padding: '8px 16px',
|
|
7180
|
+
cursor: 'default',
|
|
7181
|
+
},
|
|
7182
|
+
}, texts.maxSelectionsReached.replace('{max}', maxSelectedOptions.toString())),
|
|
7183
|
+
]
|
|
7184
|
+
: []),
|
|
7090
7185
|
// Add new option item
|
|
7091
7186
|
...(showAddNew
|
|
7092
7187
|
? [
|
|
7093
7188
|
m('li', {
|
|
7094
7189
|
onclick: async () => {
|
|
7095
|
-
|
|
7096
|
-
toggleOption(option, attrs);
|
|
7190
|
+
await createAndSelectOption(attrs);
|
|
7097
7191
|
},
|
|
7098
|
-
class: state.focusedIndex ===
|
|
7192
|
+
class: state.focusedIndex === displayedOptions.length ? 'active' : '',
|
|
7099
7193
|
onmouseover: () => {
|
|
7100
|
-
state.focusedIndex =
|
|
7194
|
+
state.focusedIndex = displayedOptions.length;
|
|
7101
7195
|
},
|
|
7102
7196
|
}, [m('span', `${texts.addNewPrefix} "${state.searchTerm}"`)]),
|
|
7103
7197
|
]
|
|
7104
7198
|
: []),
|
|
7105
7199
|
// List of filtered options
|
|
7106
|
-
...
|
|
7200
|
+
...displayedOptions.map((option, index) => m(DropdownOption, {
|
|
7107
7201
|
// key: option.id,
|
|
7108
7202
|
option,
|
|
7109
7203
|
index,
|
|
@@ -7113,6 +7207,7 @@
|
|
|
7113
7207
|
onMouseOver: (idx) => {
|
|
7114
7208
|
state.focusedIndex = idx;
|
|
7115
7209
|
},
|
|
7210
|
+
showCheckbox: maxSelectedOptions !== 1,
|
|
7116
7211
|
})),
|
|
7117
7212
|
]),
|
|
7118
7213
|
]);
|
|
@@ -9812,6 +9907,7 @@
|
|
|
9812
9907
|
exports.range = range;
|
|
9813
9908
|
exports.releasePortalContainer = releasePortalContainer;
|
|
9814
9909
|
exports.renderToPortal = renderToPortal;
|
|
9910
|
+
exports.sortOptions = sortOptions;
|
|
9815
9911
|
exports.toast = toast;
|
|
9816
9912
|
exports.uniqueId = uniqueId;
|
|
9817
9913
|
exports.uuid4 = uuid4;
|
package/dist/search-select.d.ts
CHANGED
|
@@ -6,6 +6,10 @@ export interface SearchSelectI18n {
|
|
|
6
6
|
noOptionsFound?: string;
|
|
7
7
|
/** Prefix for adding new option */
|
|
8
8
|
addNewPrefix?: string;
|
|
9
|
+
/** Message template for truncated results. Use {shown} and {total} placeholders */
|
|
10
|
+
showingXofY?: string;
|
|
11
|
+
/** Message shown when max selections reached. Use {max} placeholder */
|
|
12
|
+
maxSelectionsReached?: string;
|
|
9
13
|
}
|
|
10
14
|
export interface SearchSelectAttrs<T extends string | number> extends SelectAttrs<T> {
|
|
11
15
|
/** Callback when user creates a new option: should return new ID */
|
|
@@ -14,10 +18,16 @@ export interface SearchSelectAttrs<T extends string | number> extends SelectAttr
|
|
|
14
18
|
searchPlaceholder?: string;
|
|
15
19
|
/** When no options are left, displays this text, default 'No options found' */
|
|
16
20
|
noOptionsFound?: string;
|
|
17
|
-
/** Max height of the dropdown menu, default '
|
|
21
|
+
/** Max height of the dropdown menu, default '400px', use 'none' to disable it */
|
|
18
22
|
maxHeight?: string;
|
|
19
23
|
/** Internationalization options */
|
|
20
24
|
i18n?: SearchSelectI18n;
|
|
25
|
+
/** Maximum number of options to display. When set, limits displayed options to improve performance with large datasets */
|
|
26
|
+
maxDisplayedOptions?: number;
|
|
27
|
+
/** Maximum number of options that can be selected. When max=1, checkboxes are hidden and behaves like single select */
|
|
28
|
+
maxSelectedOptions?: number;
|
|
29
|
+
/** Sort selected items: 'asc' (alphabetically A-Z), 'desc' (Z-A), 'none' (insertion order), or custom sort function */
|
|
30
|
+
sortSelected?: 'asc' | 'desc' | 'none' | ((a: InputOption<T>, b: InputOption<T>) => number);
|
|
21
31
|
}
|
|
22
32
|
interface SearchSelectState<T extends string | number> {
|
|
23
33
|
id: string;
|
|
@@ -27,6 +37,7 @@ interface SearchSelectState<T extends string | number> {
|
|
|
27
37
|
dropdownRef: HTMLElement | null;
|
|
28
38
|
focusedIndex: number;
|
|
29
39
|
internalSelectedIds: T[];
|
|
40
|
+
createdOptions: InputOption<T>[];
|
|
30
41
|
}
|
|
31
42
|
/**
|
|
32
43
|
* Mithril Factory Component for Multi-Select Dropdown with search
|
package/dist/select.d.ts
CHANGED
|
@@ -45,6 +45,10 @@ export interface SelectAttrs<T extends string | number> extends Attributes {
|
|
|
45
45
|
required?: boolean;
|
|
46
46
|
/** Enable the clear icon */
|
|
47
47
|
showClearButton?: boolean;
|
|
48
|
+
/** Max height of the dropdown menu, default '400px' */
|
|
49
|
+
maxHeight?: string;
|
|
50
|
+
/** Sort selected items: 'asc' (alphabetically A-Z), 'desc' (Z-A), 'none' (insertion order), or custom sort function */
|
|
51
|
+
sortSelected?: 'asc' | 'desc' | 'none' | ((a: InputOption<T>, b: InputOption<T>) => number);
|
|
48
52
|
}
|
|
49
53
|
/** Select component */
|
|
50
54
|
export declare const Select: <T extends string | number>() => Component<SelectAttrs<T>>;
|
package/dist/utils.d.ts
CHANGED
|
@@ -14,6 +14,25 @@ export declare const uniqueId: () => string;
|
|
|
14
14
|
export declare const uuid4: () => string;
|
|
15
15
|
/** Check if a string or number is numeric. @see https://stackoverflow.com/a/9716488/319711 */
|
|
16
16
|
export declare const isNumeric: (n: string | number) => boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Sort options array based on sorting configuration
|
|
19
|
+
* @param options - Array of options to sort
|
|
20
|
+
* @param sortConfig - Sort configuration: 'asc', 'desc', 'none', or custom comparator function
|
|
21
|
+
* @returns Sorted array (or original if 'none' or undefined)
|
|
22
|
+
*/
|
|
23
|
+
export declare const sortOptions: <T extends string | number>(options: {
|
|
24
|
+
id: T;
|
|
25
|
+
label?: string;
|
|
26
|
+
}[], sortConfig?: "asc" | "desc" | "none" | ((a: {
|
|
27
|
+
id: T;
|
|
28
|
+
label?: string;
|
|
29
|
+
}, b: {
|
|
30
|
+
id: T;
|
|
31
|
+
label?: string;
|
|
32
|
+
}) => number)) => {
|
|
33
|
+
id: T;
|
|
34
|
+
label?: string;
|
|
35
|
+
}[];
|
|
17
36
|
/**
|
|
18
37
|
* Pad left, default width 2 with a '0'
|
|
19
38
|
*
|
package/package.json
CHANGED
|
@@ -145,6 +145,10 @@ body.keyboard-focused {
|
|
|
145
145
|
}
|
|
146
146
|
|
|
147
147
|
.select-dropdown.dropdown-content {
|
|
148
|
+
// Set a reasonable default max-height to prevent overly tall dropdowns
|
|
149
|
+
max-height: 400px;
|
|
150
|
+
overflow-y: auto;
|
|
151
|
+
|
|
148
152
|
li {
|
|
149
153
|
&:hover {
|
|
150
154
|
background-color: var(--mm-dropdown-hover, variables.$select-option-hover);
|
|
@@ -199,7 +203,7 @@ body.keyboard-focused {
|
|
|
199
203
|
.dropdown-content li.active {
|
|
200
204
|
background-color: #444 !important;
|
|
201
205
|
}
|
|
202
|
-
|
|
206
|
+
|
|
203
207
|
.chip {
|
|
204
208
|
background-color: #424242 !important;
|
|
205
209
|
color: var(--mm-text-secondary) !important;
|
|
@@ -214,7 +218,7 @@ body.keyboard-focused {
|
|
|
214
218
|
.dropdown-content li.active {
|
|
215
219
|
background-color: #444 !important;
|
|
216
220
|
}
|
|
217
|
-
|
|
221
|
+
|
|
218
222
|
.chip {
|
|
219
223
|
background-color: #424242 !important;
|
|
220
224
|
color: var(--mm-text-secondary) !important;
|
|
@@ -242,11 +246,11 @@ body.keyboard-focused {
|
|
|
242
246
|
border-bottom: 1px solid var(--mm-input-border, #9e9e9e);
|
|
243
247
|
background-color: transparent;
|
|
244
248
|
color: var(--mm-text-primary, inherit);
|
|
245
|
-
|
|
249
|
+
|
|
246
250
|
&:focus {
|
|
247
251
|
border-bottom-color: var(--mm-primary-color, #2196F3);
|
|
248
252
|
}
|
|
249
|
-
|
|
253
|
+
|
|
250
254
|
&::placeholder {
|
|
251
255
|
color: var(--mm-text-hint, #9e9e9e);
|
|
252
256
|
}
|
|
@@ -259,7 +263,7 @@ body.keyboard-focused {
|
|
|
259
263
|
font-style: italic;
|
|
260
264
|
text-align: center;
|
|
261
265
|
border-bottom: none;
|
|
262
|
-
|
|
266
|
+
|
|
263
267
|
&:hover {
|
|
264
268
|
background-color: transparent;
|
|
265
269
|
cursor: default;
|
|
@@ -276,7 +280,7 @@ body.keyboard-focused {
|
|
|
276
280
|
margin: 0;
|
|
277
281
|
// Ensure it inherits the same padding as regular dropdown items
|
|
278
282
|
min-height: var(--mm-dropdown-item-height, 50px);
|
|
279
|
-
|
|
283
|
+
|
|
280
284
|
input[type="checkbox"] {
|
|
281
285
|
margin-right: 8px;
|
|
282
286
|
position: relative;
|
|
@@ -287,11 +291,11 @@ body.keyboard-focused {
|
|
|
287
291
|
border: 2px solid var(--mm-border-color, #9e9e9e);
|
|
288
292
|
border-radius: 2px;
|
|
289
293
|
background-color: transparent;
|
|
290
|
-
|
|
294
|
+
|
|
291
295
|
&:checked {
|
|
292
296
|
background-color: var(--mm-primary-color, #2196F3);
|
|
293
297
|
border-color: var(--mm-primary-color, #2196F3);
|
|
294
|
-
|
|
298
|
+
|
|
295
299
|
&:after {
|
|
296
300
|
content: '✓';
|
|
297
301
|
color: white;
|
|
@@ -301,13 +305,13 @@ body.keyboard-focused {
|
|
|
301
305
|
left: 2px;
|
|
302
306
|
}
|
|
303
307
|
}
|
|
304
|
-
|
|
308
|
+
|
|
305
309
|
&:focus {
|
|
306
310
|
outline: 2px solid var(--mm-primary-color, #2196F3);
|
|
307
311
|
outline-offset: 2px;
|
|
308
312
|
}
|
|
309
313
|
}
|
|
310
|
-
|
|
314
|
+
|
|
311
315
|
span {
|
|
312
316
|
flex: 1;
|
|
313
317
|
}
|
|
@@ -323,32 +327,32 @@ body.keyboard-focused {
|
|
|
323
327
|
position: relative;
|
|
324
328
|
min-height: var(--mm-input-height, 3rem);
|
|
325
329
|
padding: 4px 0;
|
|
326
|
-
|
|
330
|
+
|
|
327
331
|
.chip {
|
|
328
332
|
margin: 2px 4px 2px 0;
|
|
329
333
|
// Let existing chip system handle background colors for theme compatibility
|
|
330
|
-
|
|
334
|
+
|
|
331
335
|
.material-icons.close {
|
|
332
336
|
cursor: pointer;
|
|
333
337
|
font-size: 16px;
|
|
334
338
|
margin-left: 4px;
|
|
335
|
-
|
|
339
|
+
|
|
336
340
|
&:hover {
|
|
337
341
|
color: var(--mm-error-color, #f44336);
|
|
338
342
|
}
|
|
339
343
|
}
|
|
340
344
|
}
|
|
341
|
-
|
|
345
|
+
|
|
342
346
|
.placeholder {
|
|
343
347
|
color: var(--mm-text-hint, #9e9e9e);
|
|
344
348
|
flex-grow: 1;
|
|
345
349
|
padding: 8px 0;
|
|
346
350
|
}
|
|
347
|
-
|
|
351
|
+
|
|
348
352
|
.spacer {
|
|
349
353
|
flex-grow: 1;
|
|
350
354
|
}
|
|
351
|
-
|
|
355
|
+
|
|
352
356
|
.caret {
|
|
353
357
|
margin-left: auto;
|
|
354
358
|
cursor: pointer;
|