mithril-materialized 3.2.1 → 3.3.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.
@@ -0,0 +1,65 @@
1
+ import m, { FactoryComponent, Attributes } from 'mithril';
2
+ /** Rating component size options */
3
+ export type RatingSize = 'small' | 'medium' | 'large';
4
+ /** Rating component density options */
5
+ export type RatingDensity = 'compact' | 'standard' | 'comfortable';
6
+ /** Custom icon configuration */
7
+ export interface RatingIconConfig {
8
+ /** Custom filled icon component or string */
9
+ filled?: string | m.Component;
10
+ /** Custom empty icon component or string */
11
+ empty?: string | m.Component;
12
+ /** Custom half-filled icon component or string (for fractional ratings) */
13
+ half?: string | m.Component;
14
+ }
15
+ /** Rating component attributes */
16
+ export interface RatingAttrs extends Attributes {
17
+ /** Current rating value */
18
+ value?: number;
19
+ /** Maximum rating value (default: 5) */
20
+ max?: number;
21
+ /** Step size for rating increments (default: 1, can be 0.5 for half-steps) */
22
+ step?: number;
23
+ /** Initial value for uncontrolled mode */
24
+ defaultValue?: number;
25
+ /** Whether the rating is read-only */
26
+ readonly?: boolean;
27
+ /** Whether the rating is disabled */
28
+ disabled?: boolean;
29
+ /** Whether the rating can be cleared/reset to 0 */
30
+ clearable?: boolean;
31
+ /** Size variant */
32
+ size?: RatingSize;
33
+ /** Density variant */
34
+ density?: RatingDensity;
35
+ /** Custom icon configuration */
36
+ icon?: RatingIconConfig;
37
+ /** Class name for the container */
38
+ className?: string;
39
+ /** Additional CSS styles */
40
+ style?: any;
41
+ /** Callback when rating changes */
42
+ onchange?: (value: number) => void;
43
+ /** Callback when rating is hovered (preview mode) */
44
+ onmouseover?: (value: number) => void;
45
+ /** Function to get label text for accessibility */
46
+ getLabelText?: (value: number, max: number) => string;
47
+ /** Whether to show tooltips on hover */
48
+ showTooltips?: boolean;
49
+ /** Custom tooltip labels for each rating value */
50
+ tooltipLabels?: string[];
51
+ /** Whether to allow fractional display (e.g., 3.5 stars) */
52
+ allowHalfSteps?: boolean;
53
+ /** Name for form submission */
54
+ name?: string;
55
+ /** HTML ID for the component */
56
+ id?: string;
57
+ /** ARIA label for the component */
58
+ 'aria-label'?: string;
59
+ /** ARIA label for the component (camelCase alternative) */
60
+ ariaLabel?: string;
61
+ /** ARIA labelledby reference */
62
+ 'aria-labelledby'?: string;
63
+ }
64
+ /** Create a Rating component */
65
+ export declare const Rating: FactoryComponent<RatingAttrs>;
package/dist/switch.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { FactoryComponent } from 'mithril';
2
2
  import { InputAttrs } from './input-options';
3
- export interface SwitchOptions extends Partial<InputAttrs<boolean>> {
3
+ export interface SwitchAttrs extends Pick<InputAttrs<boolean>, 'label' | 'disabled' | 'id' | 'className' | 'onchange' | 'newRow' | 'isMandatory'> {
4
4
  /** Left text label */
5
5
  left?: string;
6
6
  /** Right text label */
@@ -9,4 +9,4 @@ export interface SwitchOptions extends Partial<InputAttrs<boolean>> {
9
9
  checked?: boolean;
10
10
  }
11
11
  /** Component to display a switch with two values. */
12
- export declare const Switch: FactoryComponent<SwitchOptions>;
12
+ export declare const Switch: FactoryComponent<SwitchAttrs>;
@@ -2630,7 +2630,7 @@ table.striped tr {
2630
2630
  border-bottom: none;
2631
2631
  }
2632
2632
  table.striped > tbody > tr:nth-child(odd) {
2633
- background-color: rgba(242, 242, 242, 0.5);
2633
+ background-color: var(--mm-table-striped-color, rgba(55, 55, 55, 0.5));
2634
2634
  }
2635
2635
  table.striped > tbody > tr > td {
2636
2636
  border-radius: 0;
@@ -2639,7 +2639,7 @@ table.highlight > tbody > tr {
2639
2639
  transition: background-color 0.25s ease;
2640
2640
  }
2641
2641
  table.highlight > tbody > tr:hover {
2642
- background-color: rgba(242, 242, 242, 0.5);
2642
+ background-color: var(--mm-table-striped-color, rgba(55, 55, 55, 0.5));
2643
2643
  }
2644
2644
  table.centered thead tr th, table.centered tbody tr td {
2645
2645
  text-align: center;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mithril-materialized",
3
- "version": "3.2.1",
3
+ "version": "3.3.0",
4
4
  "description": "A materialize library for mithril.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.esm.js",
@@ -86,16 +86,16 @@
86
86
  "concurrently": "^9.2.1",
87
87
  "express": "^5.1.0",
88
88
  "identity-obj-proxy": "^3.0.0",
89
- "jest": "^30.1.1",
90
- "jest-environment-jsdom": "^30.1.1",
89
+ "jest": "^30.1.2",
90
+ "jest-environment-jsdom": "^30.1.2",
91
91
  "js-yaml": "^4.1.0",
92
92
  "rimraf": "^6.0.1",
93
- "rollup": "^4.49.0",
93
+ "rollup": "^4.50.0",
94
94
  "rollup-plugin-postcss": "^4.0.2",
95
95
  "sass": "^1.91.0",
96
96
  "ts-jest": "^29.4.1",
97
97
  "tslib": "^2.8.1",
98
- "typedoc": "^0.28.11",
98
+ "typedoc": "^0.28.12",
99
99
  "typescript": "^5.9.2"
100
100
  }
101
101
  }
@@ -373,7 +373,7 @@
373
373
  }
374
374
 
375
375
  .datatable.striped tbody tr:nth-child(odd) {
376
- background-color: rgba(255, 255, 255, 0.05);
376
+ background-color: variables.$table-striped-color;
377
377
  }
378
378
 
379
379
  .datatable.fixed-header thead th {
@@ -0,0 +1,341 @@
1
+ @use 'sass:color';
2
+ @use "variables";
3
+
4
+ /* Rating Component
5
+ ========================================================================== */
6
+
7
+ // Rating component variables
8
+ $rating-color-filled: var(--mm-primary-color, variables.$primary-color) !default;
9
+ $rating-color-empty: var(--mm-text-hint, #9e9e9e) !default;
10
+ $rating-color-hover: var(--mm-primary-color-light, color.adjust(variables.$primary-color, $lightness: 10%)) !default;
11
+ $rating-color-disabled: var(--mm-text-disabled, rgba(0, 0, 0, 0.38)) !default;
12
+ $rating-color-preview: var(--mm-secondary-color, variables.$secondary-color) !default;
13
+
14
+ // Size variants
15
+ $rating-size-small: 16px !default;
16
+ $rating-size-medium: 24px !default;
17
+ $rating-size-large: 32px !default;
18
+
19
+ // Density spacing
20
+ $rating-gap-compact: 2px !default;
21
+ $rating-gap-standard: 4px !default;
22
+ $rating-gap-comfortable: 8px !default;
23
+
24
+ // Tooltip
25
+ $rating-tooltip-bg: rgba(0, 0, 0, 0.8) !default;
26
+ $rating-tooltip-text: white !default;
27
+
28
+ .rating {
29
+ position: relative;
30
+ display: inline-flex;
31
+ align-items: center;
32
+ outline: none;
33
+ cursor: pointer;
34
+ transition: all 0.2s ease;
35
+
36
+ // Focus state
37
+ &:focus-visible {
38
+ outline: 2px solid var(--mm-primary-color, #{variables.$primary-color});
39
+ outline-offset: 2px;
40
+ border-radius: 4px;
41
+ }
42
+
43
+ // Read-only state
44
+ &--read-only {
45
+ cursor: default;
46
+
47
+ .rating__item {
48
+ cursor: default;
49
+ }
50
+ }
51
+
52
+ // Disabled state
53
+ &--disabled {
54
+ cursor: not-allowed;
55
+ opacity: 0.6;
56
+
57
+ .rating__item {
58
+ cursor: not-allowed;
59
+ }
60
+ }
61
+
62
+ // Focused state for enhanced visual feedback
63
+ &--focused:not(&--read-only):not(&--disabled) {
64
+ .rating__items {
65
+ transform: scale(1.05);
66
+ }
67
+ }
68
+ }
69
+
70
+ .rating__items {
71
+ display: flex !important;
72
+ flex-direction: row !important;
73
+ align-items: center;
74
+ transition: transform 0.15s ease;
75
+ }
76
+
77
+ .rating__item {
78
+ display: flex;
79
+ flex-direction: row;
80
+ align-items: center;
81
+
82
+ > .rating__item {
83
+ display: flex; // force flex child
84
+ }
85
+
86
+ // Hover effect
87
+ &:hover:not(.rating__item--disabled) {
88
+ transform: scale(1.1);
89
+ }
90
+
91
+ // Filled state
92
+ &--filled {
93
+ .rating__icon--filled {
94
+ color: $rating-color-filled;
95
+ }
96
+ }
97
+
98
+ // Active state
99
+ &--active {
100
+ .rating__icon--filled {
101
+ color: $rating-color-filled;
102
+ }
103
+ }
104
+
105
+ // Preview state (on hover)
106
+ &--preview {
107
+ .rating__icon--filled {
108
+ color: $rating-color-preview;
109
+ }
110
+ }
111
+
112
+ // Half-filled state
113
+ &--half {
114
+ .rating__icon--filled {
115
+ color: $rating-color-filled;
116
+ }
117
+ }
118
+
119
+ // Disabled state
120
+ &--disabled {
121
+ cursor: not-allowed;
122
+ opacity: 0.6;
123
+
124
+ .rating__icon--empty {
125
+ color: $rating-color-disabled;
126
+ }
127
+
128
+ // Keep filled icons visible but muted
129
+ &.rating__item--filled .rating__icon--filled,
130
+ &.rating__item--active .rating__icon--filled {
131
+ color: $rating-color-filled;
132
+ opacity: 0.7;
133
+ }
134
+ }
135
+ }
136
+
137
+ .rating__icon {
138
+ position: relative;
139
+ display: flex;
140
+ align-items: center;
141
+ justify-content: center;
142
+ transition: color 0.15s ease;
143
+ line-height: 1;
144
+ width: $rating-size-medium;
145
+ height: $rating-size-medium;
146
+ font-size: $rating-size-medium;
147
+
148
+ // Empty icon (background layer)
149
+ &--empty {
150
+ color: $rating-color-empty;
151
+ }
152
+
153
+ // Filled icon (foreground layer)
154
+ &--filled {
155
+ position: absolute;
156
+ top: 0;
157
+ width: 100%;
158
+ height: 100%;
159
+ display: flex;
160
+ align-items: center;
161
+ justify-content: center;
162
+ color: transparent;
163
+ overflow: hidden;
164
+ transition: color 0.15s ease;
165
+ }
166
+ }
167
+
168
+ // Size variants
169
+ .rating--small {
170
+ .rating__icon {
171
+ width: $rating-size-small;
172
+ height: $rating-size-small;
173
+ font-size: $rating-size-small;
174
+ }
175
+ }
176
+
177
+ .rating--medium {
178
+ .rating__icon {
179
+ width: $rating-size-medium;
180
+ height: $rating-size-medium;
181
+ font-size: $rating-size-medium;
182
+ }
183
+ }
184
+
185
+ .rating--large {
186
+ .rating__icon {
187
+ width: $rating-size-large;
188
+ height: $rating-size-large;
189
+ font-size: $rating-size-large;
190
+ }
191
+ }
192
+
193
+ // Density variants
194
+ .rating--compact {
195
+ .rating__items {
196
+ gap: $rating-gap-compact;
197
+ }
198
+ }
199
+
200
+ .rating--standard {
201
+ .rating__items {
202
+ gap: $rating-gap-standard;
203
+ }
204
+ }
205
+
206
+ .rating--comfortable {
207
+ .rating__items {
208
+ gap: $rating-gap-comfortable;
209
+ }
210
+ }
211
+
212
+ // Tooltip
213
+ .rating__tooltip {
214
+ position: absolute;
215
+ bottom: calc(100% + 8px);
216
+ left: 50%;
217
+ transform: translateX(-50%);
218
+ padding: 4px 8px;
219
+ background: $rating-tooltip-bg;
220
+ color: $rating-tooltip-text;
221
+ font-size: 12px;
222
+ border-radius: 4px;
223
+ white-space: nowrap;
224
+ opacity: 0;
225
+ pointer-events: none;
226
+ transition: opacity 0.2s ease;
227
+ z-index: 1000;
228
+
229
+ // Arrow
230
+ &::after {
231
+ content: '';
232
+ position: absolute;
233
+ top: 100%;
234
+ left: 50%;
235
+ transform: translateX(-50%);
236
+ border: 4px solid transparent;
237
+ border-top-color: $rating-tooltip-bg;
238
+ }
239
+ }
240
+
241
+ .rating__item:hover .rating__tooltip {
242
+ opacity: 1;
243
+ }
244
+
245
+ // Screen reader only content
246
+ .rating__sr-only {
247
+ position: absolute;
248
+ width: 1px;
249
+ height: 1px;
250
+ padding: 0;
251
+ margin: -1px;
252
+ overflow: hidden;
253
+ clip: rect(0, 0, 0, 0);
254
+ white-space: nowrap;
255
+ border: 0;
256
+ }
257
+
258
+ // Material Design touch target (minimum 48x48dp)
259
+ @media (pointer: coarse) {
260
+ .rating__item {
261
+ min-width: 48px;
262
+ min-height: 48px;
263
+ }
264
+ }
265
+
266
+ // RTL support
267
+ [dir="rtl"] {
268
+ .rating__tooltip {
269
+ &::after {
270
+ transform: translateX(50%);
271
+ }
272
+ }
273
+
274
+ .rating__item--half {
275
+ .rating__icon--filled {
276
+ clip-path: inset(0 0 0 50%) !important;
277
+ }
278
+ }
279
+ }
280
+
281
+ // High contrast mode support
282
+ @media (prefers-contrast: high) {
283
+ .rating__icon--empty {
284
+ color: currentColor;
285
+ }
286
+
287
+ .rating__item--active .rating__icon--filled {
288
+ color: currentColor;
289
+ }
290
+ }
291
+
292
+ // Dark theme adjustments
293
+ [data-theme="dark"] {
294
+ .rating__tooltip {
295
+ background: rgba(255, 255, 255, 0.9);
296
+ color: rgba(0, 0, 0, 0.87);
297
+
298
+ &::after {
299
+ border-top-color: rgba(255, 255, 255, 0.9);
300
+ }
301
+ }
302
+ }
303
+
304
+ // Animation for rating changes
305
+ @keyframes rating-fill {
306
+ 0% {
307
+ transform: scale(0.8);
308
+ opacity: 0.5;
309
+ }
310
+ 50% {
311
+ transform: scale(1.2);
312
+ }
313
+ 100% {
314
+ transform: scale(1);
315
+ opacity: 1;
316
+ }
317
+ }
318
+
319
+ .rating__item--filled .rating__icon--filled {
320
+ animation: rating-fill 0.2s ease-out;
321
+ }
322
+
323
+ // Reduced motion support
324
+ @media (prefers-reduced-motion: reduce) {
325
+ .rating,
326
+ .rating__item,
327
+ .rating__icon,
328
+ .rating__tooltip,
329
+ .rating__items {
330
+ transition: none !important;
331
+ animation: none !important;
332
+ }
333
+
334
+ .rating__item:hover:not(.rating__item--disabled) {
335
+ transform: none;
336
+ }
337
+
338
+ .rating--focused:not(.rating--read-only):not(.rating--disabled) .rating__items {
339
+ transform: none;
340
+ }
341
+ }
@@ -68,6 +68,9 @@
68
68
  --mm-switch-unchecked-thumb: #f5f5f5;
69
69
  --mm-switch-disabled-track: rgba(0, 0, 0, 0.12);
70
70
  --mm-switch-disabled-thumb: #bdbdbd;
71
+
72
+ // Table colors
73
+ --mm-table-striped-color: rgba(0, 0, 0, 0.05);
71
74
  }
72
75
 
73
76
  // Apply theme variables to body and main containers
@@ -148,6 +151,9 @@ body {
148
151
  --mm-switch-unchecked-thumb: #616161;
149
152
  --mm-switch-disabled-track: rgba(255, 255, 255, 0.12);
150
153
  --mm-switch-disabled-thumb: #424242;
154
+
155
+ // Table colors (dark theme)
156
+ --mm-table-striped-color: rgba(255, 255, 255, 0.05);
151
157
  }
152
158
 
153
159
  // Auto dark mode based on user preference
@@ -201,5 +207,8 @@ body {
201
207
  --mm-switch-unchecked-thumb: #616161;
202
208
  --mm-switch-disabled-track: rgba(255, 255, 255, 0.12);
203
209
  --mm-switch-disabled-thumb: #424242;
210
+
211
+ // Table colors (auto dark theme)
212
+ --mm-table-striped-color: rgba(255, 255, 255, 0.05);
204
213
  }
205
214
  }
@@ -291,7 +291,8 @@ $tabs-bg-color: #fff !default;
291
291
  // ==========================================================================
292
292
 
293
293
  $table-border-color: rgba(0,0,0,.12) !default;
294
- $table-striped-color: rgba(242, 242, 242, 0.5) !default;
294
+ // Use CSS custom property for table striped color to enable better theming
295
+ $table-striped-color: var(--mm-table-striped-color, rgba(55, 55, 55, 0.5)) !default;
295
296
 
296
297
 
297
298
  // 19. Toasts
@@ -380,6 +380,9 @@ textarea {
380
380
  /* Character Counter */
381
381
  .character-counter {
382
382
  min-height: 18px;
383
+ font-size: 12px;
384
+ display: block;
385
+ text-align: right;
383
386
  }
384
387
 
385
388
  /* Input Clear Button */
@@ -51,4 +51,5 @@
51
51
  @use "components/treeview";
52
52
  @use "components/timeline";
53
53
  @use "components/masonry";
54
- @use "components/image-list";
54
+ @use "components/image-list";
55
+ @use 'components/rating';