mithril-materialized 3.1.0 → 3.2.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.
@@ -6,8 +6,16 @@ export interface InputAttrs<T = string> extends Attributes {
6
6
  id?: string;
7
7
  /** Unique key for use of the element in an array. */
8
8
  key?: string | number;
9
- /** Initial value of the input field. */
10
- initialValue?: T;
9
+ /**
10
+ * Current value of the input field. If provided along with `oninput`, the component operates in controlled mode
11
+ * where the parent manages the state. The parent must update this value in response to `oninput` callbacks.
12
+ */
13
+ value?: T;
14
+ /**
15
+ * Initial value for uncontrolled mode. Only used when `value` and `oninput` are not provided.
16
+ * In uncontrolled mode, the component manages its own internal state.
17
+ */
18
+ defaultValue?: T;
11
19
  /**
12
20
  * The autocomplete property sets or returns the value of the autocomplete
13
21
  * attribute in a text field. When autocomplete is on, the browser automatically
package/dist/input.d.ts CHANGED
@@ -26,7 +26,7 @@ export interface FileInputAttrs extends Attributes {
26
26
  /** Displayed on the button, @default File */
27
27
  label?: string;
28
28
  /** Current value of the file input, write only */
29
- initialValue?: string;
29
+ value?: string;
30
30
  /** Adds a placeholder message */
31
31
  placeholder?: string;
32
32
  /** If true, upload multiple files */
package/dist/option.d.ts CHANGED
@@ -39,9 +39,9 @@ export interface OptionsAttrs<T extends string | number> extends Attributes {
39
39
  /** The options that you have */
40
40
  options: InputOption<T>[];
41
41
  /** Event handler that is called when an option is changed */
42
- onchange?: (checkedId: T[]) => void;
43
- /** Selected id or ids (in case of multiple options) */
44
- initialValue?: T | T[];
42
+ onchange?: (checkedIds: T[]) => void;
43
+ /** Currently selected ids. This property controls the component state and should be updated externally to change selection programmatically. */
44
+ checkedId?: T | T[];
45
45
  /** Optional description */
46
46
  description?: string;
47
47
  /** Optional CSS that is added to the input checkbox, e.g. if you add col s4, the items will be put inline */
@@ -56,6 +56,19 @@ export interface OptionsAttrs<T extends string | number> extends Attributes {
56
56
  layout?: 'vertical' | 'horizontal';
57
57
  /** If true, show select all/none buttons */
58
58
  showSelectAll?: boolean;
59
+ /** Text for select all button */
60
+ selectAllText?: string;
61
+ /** Text for select none button */
62
+ selectNoneText?: string;
59
63
  }
64
+ /** Reusable layout component for rendering options horizontally or vertically */
65
+ export declare const OptionsList: Component<{
66
+ options: Array<{
67
+ component: any;
68
+ props: any;
69
+ key: string | number;
70
+ }>;
71
+ layout: 'vertical' | 'horizontal';
72
+ }>;
60
73
  /** A list of checkboxes */
61
74
  export declare const Options: <T extends string | number>() => Component<OptionsAttrs<T>>;
package/dist/radio.d.ts CHANGED
@@ -7,12 +7,18 @@ export interface RadioButtonsAttrs<T extends string | number> extends Attributes
7
7
  label?: string;
8
8
  /** The options that you have */
9
9
  options: InputOption<T>[];
10
- /** Event handler that is called when an option is changed */
11
- onchange: (id: T) => void;
12
- /** Selected id (in oninit lifecycle) */
13
- initialValue?: T;
14
- /** Selected id (in oninit and onupdate lifecycle) */
10
+ /** Event handler that is called when an option is changed. Optional for uncontrolled mode. */
11
+ onchange?: (id: T) => void;
12
+ /**
13
+ * Currently selected id for controlled mode. If provided along with `onchange`, the component operates in controlled mode
14
+ * where the parent manages the state. The parent must update this value in response to `onchange` callbacks.
15
+ */
15
16
  checkedId?: T;
17
+ /**
18
+ * Default selected id for uncontrolled mode. Only used when `checkedId` and `onchange` are not provided.
19
+ * The component will manage its own internal state in uncontrolled mode.
20
+ */
21
+ defaultCheckedId?: T;
16
22
  /** Optional description */
17
23
  description?: string;
18
24
  /** If true, start on a new row */
@@ -1,38 +1,32 @@
1
- import { Attributes, Component } from 'mithril';
2
- export interface Option<T extends string | number> {
3
- id: T;
4
- label?: string;
5
- disabled?: boolean;
1
+ import { Component } from 'mithril';
2
+ import { SelectAttrs } from './select';
3
+ import { InputOption } from './option';
4
+ export interface SearchSelectI18n {
5
+ /** Text shown when no options match the search */
6
+ noOptionsFound?: string;
7
+ /** Prefix for adding new option */
8
+ addNewPrefix?: string;
6
9
  }
7
- export interface SearchSelectAttrs<T extends string | number> extends Attributes {
8
- /** Options to display in the select */
9
- options?: Option<T>[];
10
- /** Initial value */
11
- initialValue?: T[];
12
- /** Callback when user selects or deselects an option */
13
- onchange?: (selectedOptions: T[]) => void | Promise<void>;
10
+ export interface SearchSelectAttrs<T extends string | number> extends SelectAttrs<T> {
14
11
  /** Callback when user creates a new option: should return new ID */
15
- oncreateNewOption?: (term: string) => Option<T> | Promise<Option<T>>;
16
- /** Label for the search select, no default */
17
- label?: string;
18
- /** Placeholder text for the search input, no default */
19
- placeholder?: string;
12
+ oncreateNewOption?: (term: string) => InputOption<T> | Promise<InputOption<T>>;
20
13
  /** Placeholder text for the search input, default 'Search options...' */
21
14
  searchPlaceholder?: string;
22
15
  /** When no options are left, displays this text, default 'No options found' */
23
16
  noOptionsFound?: string;
24
17
  /** Max height of the dropdown menu, default '25rem' */
25
18
  maxHeight?: string;
19
+ /** Internationalization options */
20
+ i18n?: SearchSelectI18n;
26
21
  }
27
22
  interface SearchSelectState<T extends string | number> {
23
+ id: string;
28
24
  isOpen: boolean;
29
- selectedOptions: Option<T>[];
30
25
  searchTerm: string;
31
- options: Option<T>[];
32
26
  inputRef: HTMLElement | null;
33
27
  dropdownRef: HTMLElement | null;
34
28
  focusedIndex: number;
35
- onchange: any;
29
+ internalSelectedIds: T[];
36
30
  }
37
31
  /**
38
32
  * Mithril Factory Component for Multi-Select Dropdown with search
package/dist/select.d.ts CHANGED
@@ -3,15 +3,19 @@ import { InputOption } from './option';
3
3
  export interface SelectAttrs<T extends string | number> extends Attributes {
4
4
  /** Options to select from */
5
5
  options: InputOption<T>[];
6
- /** Called when the value is changed, either contains a single or all selected (checked) ids */
7
- onchange: (checkedIds: T[]) => void;
6
+ /** Called when the selection changes, contains all selected (checked) ids as an array */
7
+ onchange?: (checkedIds: T[]) => void;
8
8
  /**
9
- * Selected id or ids (in case of multiple options). Processed in the oninit and onupdate lifecycle.
10
- * When the checkedId property changes (using a shallow compare), the selections are updated accordingly.
9
+ * Currently selected id or ids. For controlled mode, pass the current selection and provide onchange.
10
+ * For single select, pass a single value or array with one item.
11
+ * For multiple select, pass an array of selected ids.
11
12
  */
12
13
  checkedId?: T | T[];
13
- /** Selected id or ids (in case of multiple options). Only processed in the oninit lifecycle. */
14
- initialValue?: T | T[];
14
+ /**
15
+ * Default selected id or ids for uncontrolled mode. Only used when checkedId and onchange are not provided.
16
+ * The component will manage its own internal state in uncontrolled mode.
17
+ */
18
+ defaultCheckedId?: T | T[];
15
19
  /** Select a single option or multiple options */
16
20
  multiple?: boolean;
17
21
  /** Optional label. */
package/dist/switch.d.ts CHANGED
@@ -5,7 +5,7 @@ export interface SwitchOptions extends Partial<InputAttrs<boolean>> {
5
5
  left?: string;
6
6
  /** Right text label */
7
7
  right?: string;
8
- /** If checked is true, the switch is set in the right position. Only processed in oninit. */
8
+ /** If checked is true, the switch is set in the right position. */
9
9
  checked?: boolean;
10
10
  }
11
11
  /** Component to display a switch with two values. */
package/dist/types.d.ts CHANGED
@@ -122,7 +122,7 @@ export type InputValue<T extends InputType> = T extends 'number' | 'range' ? num
122
122
  /**
123
123
  * Icon class using template literal types for better IntelliSense
124
124
  */
125
- export type IconClass = ComponentSize | MaterialPosition | `${ComponentSize} ${MaterialPosition}`;
125
+ export type IconClass = ComponentSize | MaterialPosition | `${ComponentSize} ${MaterialPosition}` | string;
126
126
  /**
127
127
  * Modal type discriminated union
128
128
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mithril-materialized",
3
- "version": "3.1.0",
3
+ "version": "3.2.0",
4
4
  "description": "A materialize library for mithril.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.esm.js",
@@ -64,6 +64,7 @@
64
64
  "type": "git",
65
65
  "url": "git://github.com/erikvullings/mithril-materialized.git"
66
66
  },
67
+ "homepage": "https://erikvullings.github.io/mithril-materialized/index.html#!/home",
67
68
  "keywords": [
68
69
  "mithril",
69
70
  "materialize-css"
@@ -85,11 +86,11 @@
85
86
  "concurrently": "^9.2.1",
86
87
  "express": "^5.1.0",
87
88
  "identity-obj-proxy": "^3.0.0",
88
- "jest": "^30.0.5",
89
- "jest-environment-jsdom": "^30.0.5",
89
+ "jest": "^30.1.1",
90
+ "jest-environment-jsdom": "^30.1.1",
90
91
  "js-yaml": "^4.1.0",
91
92
  "rimraf": "^6.0.1",
92
- "rollup": "^4.48.1",
93
+ "rollup": "^4.49.0",
93
94
  "rollup-plugin-postcss": "^4.0.2",
94
95
  "sass": "^1.91.0",
95
96
  "ts-jest": "^29.4.1",
@@ -60,6 +60,9 @@
60
60
  width: 24px;
61
61
  }
62
62
 
63
+ &.disabled {
64
+ cursor: not-allowed;
65
+ }
63
66
 
64
67
  clear: both;
65
68
  color: var(--mm-text-primary, variables.$off-black);
@@ -4,15 +4,19 @@
4
4
  ========================================================================== */
5
5
 
6
6
  /* Remove default checkbox */
7
- [type="checkbox"]:not(:checked),
8
- [type="checkbox"]:checked {
7
+ [type=checkbox]:not(:checked),
8
+ [type=checkbox]:checked {
9
9
  position: absolute;
10
10
  opacity: 0;
11
11
  pointer-events: none;
12
12
  }
13
13
 
14
+ [type=checkbox]:disabled + span {
15
+ color: var(--mm-text-disabled, rgba(0, 0, 0, 0.42));
16
+ }
17
+
14
18
  // Checkbox Styles
15
- [type="checkbox"] {
19
+ [type=checkbox] {
16
20
  // Text Label Style
17
21
  + span:not(.lever) {
18
22
  position: relative;
@@ -61,7 +65,7 @@
61
65
  }
62
66
  }
63
67
 
64
- [type="checkbox"]:checked {
68
+ [type=checkbox]:checked {
65
69
  + span:not(.lever):before {
66
70
  top: -4px;
67
71
  left: -5px;
@@ -83,7 +87,7 @@
83
87
  }
84
88
 
85
89
  /* Indeterminate checkbox */
86
- [type="checkbox"]:indeterminate {
90
+ [type=checkbox]:indeterminate {
87
91
  + span:not(.lever):before {
88
92
  top: -11px;
89
93
  left: -12px;
@@ -106,7 +110,7 @@
106
110
  }
107
111
 
108
112
  // Filled in Style
109
- [type="checkbox"].filled-in {
113
+ [type=checkbox].filled-in {
110
114
  // General
111
115
  + span:not(.lever):after {
112
116
  border-radius: 2px;
@@ -7,18 +7,3 @@
7
7
  @use "file-input";
8
8
  @use "range"; // Standard HTML5 range input styles
9
9
  @use "form-groups";
10
-
11
- // Remove Focus Boxes
12
- select:focus {
13
- outline: variables.$select-focus;
14
- }
15
-
16
- button:focus {
17
- outline: none;
18
- background-color: variables.$button-background-focus;
19
- }
20
-
21
- label {
22
- font-size: variables.$label-font-size;
23
- color: variables.$input-border-color;
24
- }
@@ -191,3 +191,167 @@ body.keyboard-focused {
191
191
  padding-left: 1rem;
192
192
  }
193
193
  }
194
+
195
+ /* Dark theme overrides to ensure proper colors */
196
+ [data-theme="dark"] {
197
+ .select-dropdown.dropdown-content li:hover,
198
+ .dropdown-content li:hover,
199
+ .dropdown-content li.active {
200
+ background-color: #444 !important;
201
+ }
202
+
203
+ .chip {
204
+ background-color: #424242 !important;
205
+ color: var(--mm-text-secondary) !important;
206
+ }
207
+ }
208
+
209
+ /* Auto dark mode support */
210
+ @media (prefers-color-scheme: dark) {
211
+ :root:not([data-theme="light"]) {
212
+ .select-dropdown.dropdown-content li:hover,
213
+ .dropdown-content li:hover,
214
+ .dropdown-content li.active {
215
+ background-color: #444 !important;
216
+ }
217
+
218
+ .chip {
219
+ background-color: #424242 !important;
220
+ color: var(--mm-text-secondary) !important;
221
+ }
222
+ }
223
+ }
224
+
225
+ /* SearchSelect Specific Styles
226
+ ========================================================================== */
227
+
228
+ // Search input wrapper
229
+ .search-wrapper {
230
+ padding: 0 16px;
231
+ position: relative;
232
+ // Removed border-bottom that was causing extra divider
233
+ }
234
+
235
+ // Search input field
236
+ .search-select-input {
237
+ width: 100%;
238
+ outline: none;
239
+ font-size: 0.875rem;
240
+ border: none;
241
+ padding: 8px 0;
242
+ border-bottom: 1px solid var(--mm-input-border, #9e9e9e);
243
+ background-color: transparent;
244
+ color: var(--mm-text-primary, inherit);
245
+
246
+ &:focus {
247
+ border-bottom-color: var(--mm-primary-color, #2196F3);
248
+ }
249
+
250
+ &::placeholder {
251
+ color: var(--mm-text-hint, #9e9e9e);
252
+ }
253
+ }
254
+
255
+ // No options found message
256
+ .search-select-no-options {
257
+ padding: 8px 16px;
258
+ color: var(--mm-text-hint, #9e9e9e);
259
+ font-style: italic;
260
+ text-align: center;
261
+ border-bottom: none;
262
+
263
+ &:hover {
264
+ background-color: transparent;
265
+ cursor: default;
266
+ }
267
+ }
268
+
269
+ // Option labels for proper checkbox accessibility
270
+ .search-select-option-label {
271
+ display: flex;
272
+ align-items: center;
273
+ width: 100%;
274
+ cursor: pointer;
275
+ padding: 0;
276
+ margin: 0;
277
+ // Ensure it inherits the same padding as regular dropdown items
278
+ min-height: var(--mm-dropdown-item-height, 50px);
279
+
280
+ input[type="checkbox"] {
281
+ margin-right: 8px;
282
+ position: relative;
283
+ // Reset any default checkbox styles
284
+ appearance: none;
285
+ width: 18px;
286
+ height: 18px;
287
+ border: 2px solid var(--mm-border-color, #9e9e9e);
288
+ border-radius: 2px;
289
+ background-color: transparent;
290
+
291
+ &:checked {
292
+ background-color: var(--mm-primary-color, #2196F3);
293
+ border-color: var(--mm-primary-color, #2196F3);
294
+
295
+ &:after {
296
+ content: '✓';
297
+ color: white;
298
+ font-size: 12px;
299
+ position: absolute;
300
+ top: -2px;
301
+ left: 2px;
302
+ }
303
+ }
304
+
305
+ &:focus {
306
+ outline: 2px solid var(--mm-primary-color, #2196F3);
307
+ outline-offset: 2px;
308
+ }
309
+ }
310
+
311
+ span {
312
+ flex: 1;
313
+ }
314
+ }
315
+
316
+ // Multi-select chips styling improvements
317
+ .multi-select-dropdown {
318
+ .chips-container {
319
+ display: flex;
320
+ align-items: flex-end;
321
+ flex-wrap: wrap;
322
+ cursor: pointer;
323
+ position: relative;
324
+ min-height: var(--mm-input-height, 3rem);
325
+ padding: 4px 0;
326
+
327
+ .chip {
328
+ margin: 2px 4px 2px 0;
329
+ // Let existing chip system handle background colors for theme compatibility
330
+
331
+ .material-icons.close {
332
+ cursor: pointer;
333
+ font-size: 16px;
334
+ margin-left: 4px;
335
+
336
+ &:hover {
337
+ color: var(--mm-error-color, #f44336);
338
+ }
339
+ }
340
+ }
341
+
342
+ .placeholder {
343
+ color: var(--mm-text-hint, #9e9e9e);
344
+ flex-grow: 1;
345
+ padding: 8px 0;
346
+ }
347
+
348
+ .spacer {
349
+ flex-grow: 1;
350
+ }
351
+
352
+ .caret {
353
+ margin-left: auto;
354
+ cursor: pointer;
355
+ }
356
+ }
357
+ }
@@ -8,6 +8,7 @@
8
8
  .switch * {
9
9
  -webkit-tap-highlight-color: transparent;
10
10
  user-select: none;
11
+ margin: 13px 0 17px 0;
11
12
  }
12
13
 
13
14
  .switch label {