mithril-materialized 3.5.9 → 3.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -33,6 +33,6 @@ export interface MaterialBoxAttrs extends MaterialBoxOptions, Attributes {
33
33
  }
34
34
  /**
35
35
  * Pure TypeScript MaterialBox - creates an image lightbox that fills the screen when clicked
36
- * No MaterializeCSS dependencies
36
+ * Uses CSS classes from _materialbox.scss
37
37
  */
38
38
  export declare const MaterialBox: FactoryComponent<MaterialBoxAttrs>;
package/dist/pickers.css CHANGED
@@ -371,6 +371,8 @@
371
371
  background-color: #26a69a;
372
372
  padding: 10px;
373
373
  font-weight: 300;
374
+ min-width: 200px;
375
+ position: relative;
374
376
  }
375
377
 
376
378
  .timepicker-text-container {
@@ -381,6 +383,8 @@
381
383
  font-weight: 400;
382
384
  position: relative;
383
385
  user-select: none;
386
+ min-width: 180px;
387
+ width: 100%;
384
388
  }
385
389
 
386
390
  .timepicker-span-hours,
@@ -391,10 +395,16 @@
391
395
 
392
396
  .timepicker-span-hours {
393
397
  margin-right: 3px;
398
+ display: inline-block;
399
+ min-width: 2ch;
400
+ text-align: right;
394
401
  }
395
402
 
396
403
  .timepicker-span-minutes {
397
404
  margin-left: 3px;
405
+ display: inline-block;
406
+ min-width: 2ch;
407
+ text-align: left;
398
408
  }
399
409
 
400
410
  .timepicker-display-am-pm {
@@ -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 '25rem' */
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>>;
@@ -32,10 +32,6 @@ export interface TimePickerAttrs extends InputAttrs<string>, TimepickerOptions {
32
32
  useModal?: boolean;
33
33
  /** Allow format toggle between 12h/24h (for inline mode) */
34
34
  allowFormatToggle?: boolean;
35
- /** Clear button label */
36
- clearLabel?: string;
37
- /** Close button label */
38
- closeLabel?: string;
39
35
  }
40
36
  /**
41
37
  * TimePicker component based on original Materialize CSS timepicker
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mithril-materialized",
3
- "version": "3.5.9",
3
+ "version": "3.6.0",
4
4
  "description": "A materialize library for mithril.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.esm.js",
@@ -26,8 +26,7 @@
26
26
  "./pickers.css": "./dist/pickers.css",
27
27
  "./advanced.css": "./dist/advanced.css",
28
28
  "./utilities.css": "./dist/utilities.css",
29
- "./sass/*": "./sass/*",
30
- "./src/*": "./src/*"
29
+ "./sass/*": "./sass/*"
31
30
  },
32
31
  "sideEffects": false,
33
32
  "files": [
@@ -2,42 +2,94 @@
2
2
  @use "global";
3
3
 
4
4
  .collapsible {
5
- border-top: 1px solid var(--mm-border-color, variables.$collapsible-border-color);
6
- border-right: 1px solid var(--mm-border-color, variables.$collapsible-border-color);
7
- border-left: 1px solid var(--mm-border-color, variables.$collapsible-border-color);
5
+ border: 1px solid var(--mm-border-color, variables.$collection-border-color);
6
+ background-color: var(--mm-surface-color, variables.$collection-bg-color);
8
7
  margin: variables.$element-top-margin 0 variables.$element-bottom-margin 0;
9
- @extend .z-depth-1 !optional;
10
- }
8
+ overflow: hidden;
11
9
 
12
- .collapsible-header {
13
- &:focus {
14
- outline: 0
10
+ li {
11
+ list-style-type: none;
15
12
  }
13
+ }
16
14
 
15
+ // Main header (top-level header for the whole collapsible)
16
+ .collapsible-main-header {
17
+ background-color: var(--mm-surface-color, variables.$collection-bg-color);
18
+ border-bottom: 1px solid var(--mm-border-color, variables.$collection-border-color);
19
+ padding: 10px 20px;
20
+ // Don't override h4 styling - let it use default typography
21
+ // This ensures consistent height with Collection headers
22
+ }
23
+
24
+ // Item header (collapsible item header)
25
+ .collapsible-header {
17
26
  display: flex;
27
+ align-items: center;
28
+ justify-content: flex-start;
29
+ min-height: 3rem;
30
+ line-height: 1.5rem;
31
+ padding: 1rem;
32
+ background-color: var(--mm-surface-color, variables.$collection-bg-color);
33
+ border-bottom: 1px solid var(--mm-border-color, variables.$collection-border-color);
18
34
  cursor: pointer;
19
35
  -webkit-tap-highlight-color: transparent;
20
- line-height: 1.5;
21
- padding: 1rem;
22
- border-bottom: 1px solid var(--mm-border-color, variables.$collapsible-border-color);
36
+ transition: background-color .25s;
37
+
38
+ &:hover {
39
+ background-color: var(--mm-row-hover, variables.$collection-hover-bg-color);
40
+ }
41
+
42
+ &:focus {
43
+ outline: 0;
44
+ }
23
45
 
24
- i {
46
+ i.material-icons {
25
47
  width: 2rem;
26
48
  font-size: 1.6rem;
27
- display: inline-block;
28
- text-align: center;
49
+ line-height: 1.5rem;
50
+ display: block;
51
+ flex-shrink: 0;
29
52
  margin-right: 1rem;
30
53
  }
54
+
55
+ // Wrapper for header content - takes remaining space
56
+ .collapsible-header-text,
57
+ .collapsible-header-content {
58
+ flex: 1;
59
+ align-items: center;
60
+ gap: 1rem;
61
+ }
31
62
  }
63
+
32
64
  .keyboard-focused .collapsible-header:focus {
33
- background-color: var(--mm-border-color, rgba(0, 0, 0, 0.05));
65
+ background-color: var(--mm-row-hover, variables.$collection-hover-bg-color);
34
66
  }
35
67
 
36
68
  .collapsible-body {
37
69
  display: none;
38
- border-bottom: 1px solid var(--mm-border-color, variables.$collapsible-border-color);
70
+ border-bottom: 1px solid var(--mm-border-color, variables.$collection-border-color);
71
+ background-color: var(--mm-surface-color, variables.$collection-bg-color);
39
72
  box-sizing: border-box;
40
73
  padding: 2rem;
74
+
75
+ // Remove bottom border from last item
76
+ li:last-child & {
77
+ border-bottom: none;
78
+ }
79
+ }
80
+
81
+ // Active state
82
+ li.active {
83
+ .collapsible-body {
84
+ display: block;
85
+ }
86
+ }
87
+
88
+ // Collapsible with header
89
+ .collapsible.with-header {
90
+ .collapsible-main-header:first-child {
91
+ border-top: none;
92
+ }
41
93
  }
42
94
 
43
95
  // Sidenav collapsible styling
@@ -65,7 +117,7 @@
65
117
  .collapsible-body {
66
118
  border: 0;
67
119
  background-color: var(--mm-surface-color, variables.$collapsible-header-color);
68
- color: var(--mm-text-primary, rgba(0, 0, 0, 0.87));
120
+ color: var(--mm-text-primary, rgba(0, 0, 0, 0.87));
69
121
 
70
122
  li a {
71
123
  padding: 0 (7.5px + variables.$sidenav-padding)
@@ -93,7 +93,7 @@
93
93
  transition: background-color 0.2s ease;
94
94
 
95
95
  &:hover {
96
- background-color: var(--mm-dropdown-hover);
96
+ background-color: var(--mm-row-hover);
97
97
  }
98
98
 
99
99
  .sort-indicators {
@@ -147,27 +147,27 @@
147
147
  cursor: pointer;
148
148
 
149
149
  &:hover {
150
- background-color: var(--mm-dropdown-hover);
150
+ background-color: var(--mm-row-hover);
151
151
 
152
152
  td {
153
- background-color: var(--mm-dropdown-hover);
153
+ background-color: var(--mm-row-hover);
154
154
  }
155
155
  }
156
156
  }
157
157
 
158
158
  // Enhanced striped rows for dark theme
159
159
  &.striped tbody tr:nth-child(odd) {
160
- background-color: var(--mm-dropdown-focus);
160
+ background-color: var(--mm-row-stripe, var(--mm-table-striped-color));
161
161
 
162
162
  td {
163
- background-color: var(--mm-dropdown-focus);
163
+ background-color: var(--mm-row-stripe, var(--mm-table-striped-color));
164
164
  }
165
165
 
166
166
  &:hover {
167
- background-color: var(--mm-dropdown-hover);
167
+ background-color: var(--mm-row-hover);
168
168
 
169
169
  td {
170
- background-color: var(--mm-dropdown-hover);
170
+ background-color: var(--mm-row-hover);
171
171
  }
172
172
  }
173
173
  }
@@ -364,18 +364,6 @@
364
364
  @media (prefers-color-scheme: dark) {
365
365
  :root:not([data-theme]) .datatable-container,
366
366
  [data-theme="dark"] .datatable-container {
367
- .datatable thead th.sortable:hover {
368
- background-color: var(--mm-dropdown-hover);
369
- }
370
-
371
- .datatable tbody tr:hover {
372
- background-color: var(--mm-dropdown-hover);
373
- }
374
-
375
- .datatable.striped tbody tr:nth-child(odd) {
376
- background-color: variables.$table-striped-color;
377
- }
378
-
379
367
  .datatable.fixed-header thead th {
380
368
  border-bottom: 1px solid var(--mm-border-color);
381
369
  }
@@ -16,28 +16,37 @@
16
16
  }
17
17
  }
18
18
 
19
- #materialbox-overlay {
20
- position:fixed;
19
+ .materialbox-overlay {
20
+ position: fixed;
21
21
  top: 0;
22
+ left: 0;
22
23
  right: 0;
23
24
  bottom: 0;
24
- left: 0;
25
- background-color: #292929;
25
+ background-color: rgba(0, 0, 0, 0.85);
26
26
  z-index: 1000;
27
+ opacity: 0;
28
+ cursor: zoom-out;
27
29
  will-change: opacity;
28
30
  }
29
31
 
32
+ .materialbox-image {
33
+ position: fixed;
34
+ cursor: zoom-out;
35
+ max-width: none;
36
+ z-index: 1001;
37
+ will-change: top, left, width, height;
38
+ }
39
+
30
40
  .materialbox-caption {
31
41
  position: fixed;
32
- display: none;
33
- color: #fff;
34
- line-height: 50px;
35
- bottom: 0;
36
- left: 0;
37
- width: 100%;
42
+ bottom: 20px;
43
+ left: 50%;
44
+ transform: translateX(-50%);
45
+ color: white;
46
+ font-size: 16px;
38
47
  text-align: center;
39
- padding: 0% 15%;
40
- height: 50px;
41
- z-index: 1000;
48
+ opacity: 0;
49
+ z-index: 1002;
50
+ pointer-events: none;
42
51
  -webkit-font-smoothing: antialiased;
43
- }
52
+ }
@@ -57,6 +57,9 @@
57
57
  --mm-dropdown-hover: #eee;
58
58
  --mm-dropdown-focus: #ddd;
59
59
  --mm-dropdown-selected: #e3f2fd;
60
+
61
+ // Collection/Table hover (light theme)
62
+ --mm-row-hover: rgba(0, 0, 0, 0.04);
60
63
  --mm-shadow-umbra: rgba(0, 0, 0, 0.2);
61
64
  --mm-shadow-penumbra: rgba(0, 0, 0, 0.14);
62
65
  --mm-shadow-ambient: rgba(0, 0, 0, 0.12);
@@ -144,6 +147,10 @@ body {
144
147
  --mm-dropdown-focus: #555;
145
148
  --mm-dropdown-selected: #1e3a8a;
146
149
 
150
+ // Collection/Table hover (dark theme) - more subtle
151
+ --mm-row-hover: rgba(255, 255, 255, 0.04);
152
+ --mm-row-stripe: rgba(255, 255, 255, 0.02);
153
+
147
154
  // Switch colors (dark theme)
148
155
  --mm-switch-checked-track: rgba(128, 203, 196, 0.3);
149
156
  --mm-switch-checked-thumb: #80cbc4;
@@ -100,6 +100,8 @@
100
100
  background-color: variables.$secondary-color;
101
101
  padding: 10px;
102
102
  font-weight: 300;
103
+ min-width: 200px;
104
+ position: relative;
103
105
  }
104
106
 
105
107
  .timepicker-text-container {
@@ -110,6 +112,8 @@
110
112
  font-weight: 400;
111
113
  position: relative;
112
114
  user-select: none;
115
+ min-width: 180px;
116
+ width: 100%;
113
117
  }
114
118
 
115
119
  .timepicker-span-hours,
@@ -120,10 +124,16 @@
120
124
 
121
125
  .timepicker-span-hours {
122
126
  margin-right: 3px;
127
+ display: inline-block;
128
+ min-width: 2ch;
129
+ text-align: right;
123
130
  }
124
131
 
125
132
  .timepicker-span-minutes {
126
133
  margin-left: 3px;
134
+ display: inline-block;
135
+ min-width: 2ch;
136
+ text-align: left;
127
137
  }
128
138
 
129
139
  .timepicker-display-am-pm {
@@ -131,7 +131,7 @@ textarea.materialize-textarea {
131
131
  &:-ms-input-placeholder {
132
132
  color: var(--mm-text-hint, variables.$placeholder-text-color) !important;
133
133
  }
134
-
134
+
135
135
  &::-ms-input-placeholder {
136
136
  color: var(--mm-text-hint, variables.$placeholder-text-color) !important;
137
137
  }
@@ -340,12 +340,15 @@ textarea {
340
340
  background-color: transparent;
341
341
 
342
342
  &.materialize-textarea {
343
+ // Override base styles to match TextInput alignment
344
+ padding-top: 10px;
345
+ padding-bottom: 0;
346
+ height: 35px;
347
+ margin-bottom: 2.5px;
343
348
  line-height: 1.5;
344
349
  overflow-y: hidden; /* prevents scroll bar flash */
345
- padding: .8rem 0 .8rem 0; /* prevents text jump on Enter keypress */
346
350
  resize: none;
347
- min-height: variables.$input-height;
348
- box-sizing: border-box;
351
+ min-height: calc(variables.$input-height - 10px);
349
352
  }
350
353
  }
351
354
 
@@ -355,7 +358,9 @@ textarea {
355
358
  white-space: pre-wrap;
356
359
  word-wrap: break-word;
357
360
  overflow-wrap: break-word; /* future version of deprecated 'word-wrap' */
358
- padding-top: 1.2rem; /* prevents text jump on Enter keypress */
361
+ padding-top: 13px; /* match textarea padding-top */
362
+ padding-bottom: 0; /* match textarea padding-bottom */
363
+ line-height: 1.5; /* match textarea line-height */
359
364
 
360
365
  // Reduces repaints
361
366
  position: absolute;
@@ -145,14 +145,18 @@ 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);
151
155
  }
152
156
 
153
- // &.selected {
154
- // background-color: var(--mm-dropdown-selected, variables.$select-option-selected);
155
- // }
157
+ &.selected {
158
+ background-color: var(--mm-row-hover, variables.$select-option-hover);
159
+ }
156
160
  }
157
161
  }
158
162
 
@@ -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;