mithril-materialized 2.0.0-beta.11 → 2.0.0-beta.14

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/index.esm.js CHANGED
@@ -836,6 +836,18 @@ const iconPaths = {
836
836
  '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',
837
837
  'M0 0h24v24H0z',
838
838
  ],
839
+ chevron: [
840
+ 'M16.59 8.59L12 13.17 7.41 8.59 6 10l6 6 6-6z', // chevron down
841
+ 'M0 0h24v24H0z', // background
842
+ ],
843
+ expand: [
844
+ 'M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z', // plus
845
+ 'M0 0h24v24H0z', // background
846
+ ],
847
+ collapse: [
848
+ 'M19 13H5v-2h14v2z', // minus
849
+ 'M0 0h24v24H0z', // background
850
+ ],
839
851
  };
840
852
  const MaterialIcon = () => {
841
853
  return {
@@ -845,8 +857,8 @@ const MaterialIcon = () => {
845
857
  const rotationMap = {
846
858
  down: 0,
847
859
  up: 180,
848
- left: -90,
849
- right: 90,
860
+ left: 90,
861
+ right: -90,
850
862
  };
851
863
  const rotation = (_a = rotationMap[direction]) !== null && _a !== void 0 ? _a : 0;
852
864
  const transform = rotation ? `rotate(${rotation}deg)` : undefined;
@@ -1155,7 +1167,7 @@ const CodeBlock = () => ({
1155
1167
  const lang = language || 'lang-TypeScript';
1156
1168
  const label = lang.replace('lang-', '');
1157
1169
  const cb = code instanceof Array ? code.join('\n') : code;
1158
- const cn = [newRow ? 'clear' : '', lang, className].filter(Boolean).join(' ').trim();
1170
+ const cn = [newRow ? 'clear' : '', lang, className].filter(Boolean).join(' ').trim() || undefined;
1159
1171
  return m(`pre.codeblock${newRow ? '.clear' : ''}`, attrs, [
1160
1172
  m('div', m('label', label)),
1161
1173
  m('code', Object.assign(Object.assign({}, params), { className: cn }), cb),
@@ -2178,6 +2190,12 @@ const InputField = (type, defaultClass = '') => () => {
2178
2190
  isValid: true,
2179
2191
  active: false,
2180
2192
  inputElement: null,
2193
+ // Range-specific state
2194
+ rangeMinValue: undefined,
2195
+ rangeMaxValue: undefined,
2196
+ singleValue: undefined,
2197
+ isDragging: false,
2198
+ activeThumb: null,
2181
2199
  };
2182
2200
  // let labelManager: { updateLabelState: () => void; cleanup: () => void } | null = null;
2183
2201
  // let lengthUpdateHandler: (() => void) | null = null;
@@ -2203,19 +2221,425 @@ const InputField = (type, defaultClass = '') => () => {
2203
2221
  state.hasInteracted = length > 0;
2204
2222
  }
2205
2223
  };
2224
+ // Range slider helper functions
2225
+ const getPercentage = (value, min, max) => {
2226
+ return ((value - min) / (max - min)) * 100;
2227
+ };
2228
+ const updateRangeValues = (minValue, maxValue, attrs, immediate = false) => {
2229
+ // Ensure min doesn't exceed max and vice versa
2230
+ if (minValue > maxValue)
2231
+ minValue = maxValue;
2232
+ if (maxValue < minValue)
2233
+ maxValue = minValue;
2234
+ state.rangeMinValue = minValue;
2235
+ state.rangeMaxValue = maxValue;
2236
+ // Call oninput for immediate feedback or onchange for final changes
2237
+ if (immediate && attrs.oninput) {
2238
+ attrs.oninput(minValue, maxValue);
2239
+ }
2240
+ else if (!immediate && attrs.onchange) {
2241
+ attrs.onchange(minValue, maxValue);
2242
+ }
2243
+ };
2244
+ // Render function for single range slider with tooltip
2245
+ const renderSingleRangeWithTooltip = (attrs, state, cn, style, iconName, id, label, isMandatory, helperText) => {
2246
+ const { min = 0, max = 100, step = 1, initialValue, vertical = false, showValue = false, height = '200px', disabled = false, oninput, onchange, } = attrs;
2247
+ // Initialize single range value
2248
+ const currentValue = initialValue !== undefined ? initialValue : state.singleValue || min;
2249
+ if (state.singleValue === undefined) {
2250
+ state.singleValue = currentValue;
2251
+ }
2252
+ const percentage = getPercentage(state.singleValue, min, max);
2253
+ // Only keep dynamic styles as inline, use CSS classes for static styles
2254
+ const containerStyle = vertical ? { height } : {};
2255
+ const progressStyle = vertical
2256
+ ? {
2257
+ height: `${percentage}%`,
2258
+ }
2259
+ : {
2260
+ width: `${percentage}%`,
2261
+ };
2262
+ const thumbStyle = vertical
2263
+ ? {
2264
+ bottom: `${percentage}%`,
2265
+ }
2266
+ : {
2267
+ left: `${percentage}%`,
2268
+ marginLeft: '-10px', // Half of thumb size (20px)
2269
+ };
2270
+ const updateSingleValue = (newValue, immediate = false) => {
2271
+ state.singleValue = newValue;
2272
+ if (immediate && oninput) {
2273
+ oninput(newValue);
2274
+ }
2275
+ else if (!immediate && onchange) {
2276
+ onchange(newValue);
2277
+ }
2278
+ };
2279
+ const handleMouseDown = (e) => {
2280
+ if (disabled)
2281
+ return;
2282
+ e.preventDefault();
2283
+ state.isDragging = true;
2284
+ // Get container reference from the current target's parent
2285
+ const thumbElement = e.currentTarget;
2286
+ const container = thumbElement.parentElement;
2287
+ if (!container)
2288
+ return;
2289
+ const handleMouseMove = (e) => {
2290
+ if (!state.isDragging || !container)
2291
+ return;
2292
+ const rect = container.getBoundingClientRect();
2293
+ let percentage;
2294
+ if (vertical) {
2295
+ percentage = ((rect.bottom - e.clientY) / rect.height) * 100;
2296
+ }
2297
+ else {
2298
+ percentage = ((e.clientX - rect.left) / rect.width) * 100;
2299
+ }
2300
+ percentage = Math.max(0, Math.min(100, percentage));
2301
+ const value = min + (percentage / 100) * (max - min);
2302
+ const steppedValue = Math.round(value / step) * step;
2303
+ updateSingleValue(steppedValue, true);
2304
+ // Redraw to update the UI
2305
+ m.redraw();
2306
+ };
2307
+ const handleMouseUp = () => {
2308
+ state.isDragging = false;
2309
+ document.removeEventListener('mousemove', handleMouseMove);
2310
+ document.removeEventListener('mouseup', handleMouseUp);
2311
+ // Fire onchange when dragging ends
2312
+ updateSingleValue(state.singleValue, false);
2313
+ };
2314
+ document.addEventListener('mousemove', handleMouseMove);
2315
+ document.addEventListener('mouseup', handleMouseUp);
2316
+ };
2317
+ const fieldClass = vertical ? 'range-field vertical' : 'range-field';
2318
+ const orientation = vertical ? 'vertical' : 'horizontal';
2319
+ return m('.input-field', { className: cn, style }, [
2320
+ iconName ? m('i.material-icons.prefix', iconName) : undefined,
2321
+ // Hidden input for label association and accessibility
2322
+ m('input[type=range]', {
2323
+ id,
2324
+ value: state.singleValue,
2325
+ min,
2326
+ max,
2327
+ step,
2328
+ style: { display: 'none' },
2329
+ disabled,
2330
+ tabindex: -1,
2331
+ }),
2332
+ m('div', { class: fieldClass, style: containerStyle }, [
2333
+ m(`.single-range-slider.${orientation}`, {
2334
+ tabindex: disabled ? -1 : 0,
2335
+ role: 'slider',
2336
+ 'aria-valuemin': min,
2337
+ 'aria-valuemax': max,
2338
+ 'aria-valuenow': state.singleValue,
2339
+ 'aria-label': label || 'Range slider',
2340
+ onclick: (e) => {
2341
+ // Focus the slider when clicked
2342
+ e.currentTarget.focus();
2343
+ },
2344
+ onkeydown: (e) => {
2345
+ if (disabled)
2346
+ return;
2347
+ let newValue = state.singleValue;
2348
+ switch (e.key) {
2349
+ case 'ArrowLeft':
2350
+ case 'ArrowDown':
2351
+ e.preventDefault();
2352
+ newValue = Math.max(min, newValue - step);
2353
+ updateSingleValue(newValue, false);
2354
+ m.redraw();
2355
+ break;
2356
+ case 'ArrowRight':
2357
+ case 'ArrowUp':
2358
+ e.preventDefault();
2359
+ newValue = Math.min(max, newValue + step);
2360
+ updateSingleValue(newValue, false);
2361
+ m.redraw();
2362
+ break;
2363
+ case 'Home':
2364
+ e.preventDefault();
2365
+ updateSingleValue(min, false);
2366
+ m.redraw();
2367
+ break;
2368
+ case 'End':
2369
+ e.preventDefault();
2370
+ updateSingleValue(max, false);
2371
+ m.redraw();
2372
+ break;
2373
+ }
2374
+ },
2375
+ }, [
2376
+ // Track
2377
+ m(`.track.${orientation}`),
2378
+ // Progress
2379
+ m(`.range-progress.${orientation}`, { style: progressStyle }),
2380
+ // Thumb
2381
+ m(`.thumb.${orientation}`, {
2382
+ style: thumbStyle,
2383
+ onmousedown: handleMouseDown,
2384
+ }, showValue
2385
+ ? m(`.value-tooltip.${vertical ? 'right' : 'top'}`, state.singleValue.toFixed(0))
2386
+ : null),
2387
+ ]),
2388
+ ]),
2389
+ label
2390
+ ? m(Label, {
2391
+ label,
2392
+ id,
2393
+ isMandatory,
2394
+ isActive: true, // Range sliders always have active labels
2395
+ })
2396
+ : null,
2397
+ helperText ? m(HelperText, { helperText }) : null,
2398
+ ]);
2399
+ };
2400
+ // Render function for minmax range slider
2401
+ const renderMinMaxRange = (attrs, state, cn, style, iconName, id, label, isMandatory, helperText) => {
2402
+ const { min = 0, max = 100, step = 1, minValue, maxValue, vertical = false, showValue = false, height = '200px', disabled = false, } = attrs;
2403
+ // Initialize range values
2404
+ const currentMinValue = minValue !== undefined ? minValue : attrs.minValue || min;
2405
+ const currentMaxValue = maxValue !== undefined ? maxValue : attrs.maxValue || max;
2406
+ if (state.rangeMinValue === undefined || state.rangeMaxValue === undefined) {
2407
+ state.rangeMinValue = currentMinValue;
2408
+ state.rangeMaxValue = currentMaxValue;
2409
+ }
2410
+ // Initialize active thumb if not set
2411
+ if (state.activeThumb === null) {
2412
+ state.activeThumb = 'min';
2413
+ }
2414
+ const minPercentage = getPercentage(state.rangeMinValue, min, max);
2415
+ const maxPercentage = getPercentage(state.rangeMaxValue, min, max);
2416
+ // Only keep dynamic styles as inline, use CSS classes for static styles
2417
+ const containerStyle = vertical ? { height } : {};
2418
+ const rangeStyle = vertical
2419
+ ? {
2420
+ bottom: `${minPercentage}%`,
2421
+ height: `${maxPercentage - minPercentage}%`,
2422
+ }
2423
+ : {
2424
+ left: `${minPercentage}%`,
2425
+ width: `${maxPercentage - minPercentage}%`,
2426
+ };
2427
+ // Only keep dynamic positioning and z-index as inline styles
2428
+ const createThumbStyle = (percentage, isActive) => vertical
2429
+ ? {
2430
+ bottom: `${percentage}%`,
2431
+ zIndex: isActive ? 10 : 5,
2432
+ }
2433
+ : {
2434
+ left: `${percentage}%`,
2435
+ marginLeft: '-10px', // Half of thumb size (20px)
2436
+ zIndex: isActive ? 10 : 5,
2437
+ };
2438
+ const handleMouseDown = (thumb) => (e) => {
2439
+ if (disabled)
2440
+ return;
2441
+ e.preventDefault();
2442
+ state.isDragging = true;
2443
+ state.activeThumb = thumb;
2444
+ // Get container reference from the current target's parent
2445
+ const thumbElement = e.currentTarget;
2446
+ const container = thumbElement.parentElement;
2447
+ if (!container)
2448
+ return;
2449
+ const handleMouseMove = (e) => {
2450
+ if (!state.isDragging || !container)
2451
+ return;
2452
+ const rect = container.getBoundingClientRect();
2453
+ let percentage;
2454
+ if (vertical) {
2455
+ percentage = ((rect.bottom - e.clientY) / rect.height) * 100;
2456
+ }
2457
+ else {
2458
+ percentage = ((e.clientX - rect.left) / rect.width) * 100;
2459
+ }
2460
+ percentage = Math.max(0, Math.min(100, percentage));
2461
+ const value = min + (percentage / 100) * (max - min);
2462
+ const steppedValue = Math.round(value / step) * step;
2463
+ if (thumb === 'min') {
2464
+ updateRangeValues(Math.min(steppedValue, state.rangeMaxValue), state.rangeMaxValue, attrs, true);
2465
+ }
2466
+ else {
2467
+ updateRangeValues(state.rangeMinValue, Math.max(steppedValue, state.rangeMinValue), attrs, true);
2468
+ }
2469
+ // Redraw to update the UI
2470
+ m.redraw();
2471
+ };
2472
+ const handleMouseUp = () => {
2473
+ state.isDragging = false;
2474
+ state.activeThumb = null;
2475
+ document.removeEventListener('mousemove', handleMouseMove);
2476
+ document.removeEventListener('mouseup', handleMouseUp);
2477
+ // Fire onchange when dragging ends
2478
+ updateRangeValues(state.rangeMinValue, state.rangeMaxValue, attrs, false);
2479
+ };
2480
+ document.addEventListener('mousemove', handleMouseMove);
2481
+ document.addEventListener('mouseup', handleMouseUp);
2482
+ };
2483
+ const fieldClass = vertical ? 'range-field vertical' : 'range-field';
2484
+ const orientation = vertical ? 'vertical' : 'horizontal';
2485
+ return m('.input-field', { className: cn, style }, [
2486
+ iconName ? m('i.material-icons.prefix', iconName) : undefined,
2487
+ // Hidden inputs for label association and accessibility
2488
+ m('input[type=range]', {
2489
+ id,
2490
+ value: state.rangeMinValue,
2491
+ min,
2492
+ max,
2493
+ step,
2494
+ style: { display: 'none' },
2495
+ disabled,
2496
+ tabindex: -1,
2497
+ }),
2498
+ m('input[type=range]', {
2499
+ id: `${id}_max`,
2500
+ value: state.rangeMaxValue,
2501
+ min,
2502
+ max,
2503
+ step,
2504
+ style: { display: 'none' },
2505
+ disabled,
2506
+ tabindex: -1,
2507
+ }),
2508
+ m(`.${fieldClass}`, [
2509
+ m(`.double-range-slider.${orientation}`, {
2510
+ style: containerStyle,
2511
+ tabindex: disabled ? -1 : 0,
2512
+ role: 'slider',
2513
+ 'aria-valuemin': min,
2514
+ 'aria-valuemax': max,
2515
+ 'aria-valuenow': state.rangeMinValue,
2516
+ 'aria-valuetext': `${state.rangeMinValue} to ${state.rangeMaxValue}`,
2517
+ 'aria-label': label || 'Range slider',
2518
+ onclick: (e) => {
2519
+ // Focus the slider when clicked
2520
+ e.currentTarget.focus();
2521
+ },
2522
+ onkeydown: (e) => {
2523
+ if (disabled)
2524
+ return;
2525
+ let newMinValue = state.rangeMinValue;
2526
+ let newMaxValue = state.rangeMaxValue;
2527
+ const activeThumb = state.activeThumb || 'min';
2528
+ switch (e.key) {
2529
+ case 'ArrowLeft':
2530
+ case 'ArrowDown':
2531
+ e.preventDefault();
2532
+ if (activeThumb === 'min') {
2533
+ newMinValue = Math.max(min, newMinValue - step);
2534
+ updateRangeValues(newMinValue, newMaxValue, attrs, false);
2535
+ }
2536
+ else {
2537
+ newMaxValue = Math.max(newMinValue, newMaxValue - step);
2538
+ updateRangeValues(newMinValue, newMaxValue, attrs, false);
2539
+ }
2540
+ m.redraw();
2541
+ break;
2542
+ case 'ArrowRight':
2543
+ case 'ArrowUp':
2544
+ e.preventDefault();
2545
+ if (activeThumb === 'min') {
2546
+ newMinValue = Math.min(newMaxValue, newMinValue + step);
2547
+ updateRangeValues(newMinValue, newMaxValue, attrs, false);
2548
+ }
2549
+ else {
2550
+ newMaxValue = Math.min(max, newMaxValue + step);
2551
+ updateRangeValues(newMinValue, newMaxValue, attrs, false);
2552
+ }
2553
+ m.redraw();
2554
+ break;
2555
+ // Remove Tab case - let normal tab navigation work
2556
+ // Users can click on specific thumbs to activate them
2557
+ case 'Home':
2558
+ e.preventDefault();
2559
+ if (activeThumb === 'min') {
2560
+ updateRangeValues(min, newMaxValue, attrs, false);
2561
+ }
2562
+ else {
2563
+ updateRangeValues(newMinValue, newMinValue, attrs, false);
2564
+ }
2565
+ m.redraw();
2566
+ break;
2567
+ case 'End':
2568
+ e.preventDefault();
2569
+ if (activeThumb === 'min') {
2570
+ updateRangeValues(newMaxValue, newMaxValue, attrs, false);
2571
+ }
2572
+ else {
2573
+ updateRangeValues(newMinValue, max, attrs, false);
2574
+ }
2575
+ m.redraw();
2576
+ break;
2577
+ }
2578
+ },
2579
+ }, [
2580
+ // Track
2581
+ m(`.track.${orientation}`),
2582
+ // Range
2583
+ m(`.range.${orientation}`, { style: rangeStyle }),
2584
+ // Min thumb
2585
+ m(`.thumb.${orientation}.min-thumb${state.activeThumb === 'min' ? '.active' : ''}`, {
2586
+ style: createThumbStyle(minPercentage, state.activeThumb === 'min'),
2587
+ onmousedown: handleMouseDown('min'),
2588
+ onclick: () => {
2589
+ state.activeThumb = 'min';
2590
+ m.redraw();
2591
+ },
2592
+ }, showValue ? m(`.value.${orientation}`, state.rangeMinValue.toFixed(0)) : null),
2593
+ // Max thumb
2594
+ m(`.thumb.${orientation}.max-thumb${state.activeThumb === 'max' ? '.active' : ''}`, {
2595
+ style: createThumbStyle(maxPercentage, state.activeThumb === 'max'),
2596
+ onmousedown: handleMouseDown('max'),
2597
+ onclick: () => {
2598
+ state.activeThumb = 'max';
2599
+ m.redraw();
2600
+ },
2601
+ }, showValue ? m(`.value.${orientation}`, state.rangeMaxValue.toFixed(0)) : null),
2602
+ ]),
2603
+ ]),
2604
+ label
2605
+ ? m(Label, {
2606
+ label,
2607
+ id,
2608
+ isMandatory,
2609
+ isActive: true, // Range sliders always have active labels
2610
+ })
2611
+ : null,
2612
+ helperText ? m(HelperText, { helperText }) : null,
2613
+ ]);
2614
+ };
2206
2615
  return {
2207
2616
  view: ({ attrs }) => {
2208
2617
  var _a;
2209
2618
  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"]);
2210
2619
  // const attributes = toAttrs(params);
2211
- const cn = [newRow ? 'clear' : '', defaultClass, className].filter(Boolean).join(' ').trim();
2620
+ const cn = [newRow ? 'clear' : '', defaultClass, className].filter(Boolean).join(' ').trim() || undefined;
2212
2621
  const isActive = state.active || ((_a = state.inputElement) === null || _a === void 0 ? void 0 : _a.value) || placeholder || type === 'color' || type === 'range'
2213
2622
  ? true
2214
2623
  : false;
2624
+ // Special rendering for minmax range sliders
2625
+ if (type === 'range' && attrs.minmax) {
2626
+ return renderMinMaxRange(attrs, state, cn, style, iconName, id, label, isMandatory, helperText);
2627
+ }
2628
+ // Special rendering for single range sliders with tooltips
2629
+ if (type === 'range' && attrs.showValue) {
2630
+ return renderSingleRangeWithTooltip(attrs, state, cn, style, iconName, id, label, isMandatory, helperText);
2631
+ }
2215
2632
  return m('.input-field', { className: cn, style }, [
2216
2633
  iconName ? m('i.material-icons.prefix', iconName) : undefined,
2217
2634
  m('input.validate', Object.assign(Object.assign({}, params), { type, tabindex: 0, id,
2218
- placeholder,
2635
+ placeholder, class: type === 'range' && attrs.vertical ? 'range-slider vertical' : undefined, style: type === 'range' && attrs.vertical
2636
+ ? {
2637
+ height: attrs.height || '200px',
2638
+ width: '6px',
2639
+ writingMode: 'vertical-lr',
2640
+ direction: 'rtl',
2641
+ }
2642
+ : undefined,
2219
2643
  // attributes,
2220
2644
  oncreate: ({ dom }) => {
2221
2645
  const input = (state.inputElement = dom);
@@ -2231,7 +2655,7 @@ const InputField = (type, defaultClass = '') => () => {
2231
2655
  state.currentLength = input.value.length; // Initial count
2232
2656
  }
2233
2657
  // Range input functionality
2234
- if (type === 'range') {
2658
+ if (type === 'range' && !attrs.minmax) {
2235
2659
  const updateThumb = () => {
2236
2660
  const value = input.value;
2237
2661
  const min = input.min || '0';
@@ -2484,7 +2908,7 @@ const Options = () => {
2484
2908
  callback(checkedIds);
2485
2909
  }
2486
2910
  : undefined;
2487
- const cn = [newRow ? 'clear' : '', className].filter(Boolean).join(' ').trim();
2911
+ const cn = [newRow ? 'clear' : '', className].filter(Boolean).join(' ').trim() || undefined;
2488
2912
  const optionsContent = layout === 'horizontal'
2489
2913
  ? m('div.grid-container', options.map((option) => m(InputCheckbox, {
2490
2914
  disabled: disabled || option.disabled,
@@ -3355,6 +3779,423 @@ const FloatingActionButton = () => {
3355
3779
  };
3356
3780
  };
3357
3781
 
3782
+ // Utility functions
3783
+ const getPercentage = (value, min, max) => {
3784
+ return ((value - min) / (max - min)) * 100;
3785
+ };
3786
+ const updateRangeValues = (minValue, maxValue, attrs, state, immediate) => {
3787
+ // Ensure min doesn't exceed max and vice versa
3788
+ if (minValue > maxValue)
3789
+ minValue = maxValue;
3790
+ if (maxValue < minValue)
3791
+ maxValue = minValue;
3792
+ state.rangeMinValue = minValue;
3793
+ state.rangeMaxValue = maxValue;
3794
+ // Call oninput for immediate feedback or onchange for final changes
3795
+ if (immediate && attrs.oninput) {
3796
+ attrs.oninput(minValue, maxValue);
3797
+ }
3798
+ else if (!immediate && attrs.onchange) {
3799
+ attrs.onchange(minValue, maxValue);
3800
+ }
3801
+ };
3802
+ // Single Range Slider with Tooltip
3803
+ const renderSingleRangeWithTooltip = (attrs, state, cn, style, iconName, id, label, isMandatory, helperText) => {
3804
+ const { min = 0, max = 100, step = 1, initialValue, vertical = false, showValue = false, height = '200px', disabled = false, tooltipPos = 'top', oninput, onchange, } = attrs;
3805
+ // Initialize single range value
3806
+ const currentValue = initialValue !== undefined ? initialValue : state.singleValue || min;
3807
+ if (state.singleValue === undefined) {
3808
+ state.singleValue = currentValue;
3809
+ }
3810
+ const percentage = getPercentage(state.singleValue, min, max);
3811
+ // Only keep dynamic styles as inline, use CSS classes for static styles
3812
+ const containerStyle = vertical ? { height } : {};
3813
+ const progressStyle = vertical
3814
+ ? {
3815
+ height: `${percentage}%`,
3816
+ }
3817
+ : {
3818
+ width: `${percentage}%`,
3819
+ };
3820
+ const thumbStyle = vertical
3821
+ ? {
3822
+ bottom: `${percentage}%`,
3823
+ }
3824
+ : {
3825
+ left: `${percentage}%`,
3826
+ marginLeft: '-10px', // Half of thumb size (20px)
3827
+ };
3828
+ const updateSingleValue = (newValue, immediate = false) => {
3829
+ state.singleValue = newValue;
3830
+ if (immediate && oninput) {
3831
+ oninput(newValue);
3832
+ }
3833
+ else if (!immediate && onchange) {
3834
+ onchange(newValue);
3835
+ }
3836
+ };
3837
+ const handleMouseDown = (e) => {
3838
+ if (disabled)
3839
+ return;
3840
+ e.preventDefault();
3841
+ state.isDragging = true;
3842
+ // Get container reference from the current target's parent
3843
+ const thumbElement = e.currentTarget;
3844
+ const container = thumbElement.parentElement;
3845
+ if (!container)
3846
+ return;
3847
+ const handleMouseMove = (e) => {
3848
+ if (!state.isDragging || !container)
3849
+ return;
3850
+ const rect = container.getBoundingClientRect();
3851
+ let percentage;
3852
+ if (vertical) {
3853
+ percentage = ((rect.bottom - e.clientY) / rect.height) * 100;
3854
+ }
3855
+ else {
3856
+ percentage = ((e.clientX - rect.left) / rect.width) * 100;
3857
+ }
3858
+ percentage = Math.max(0, Math.min(100, percentage));
3859
+ const value = min + (percentage / 100) * (max - min);
3860
+ const steppedValue = Math.round(value / step) * step;
3861
+ updateSingleValue(steppedValue, true);
3862
+ // Redraw to update the UI during drag
3863
+ m.redraw();
3864
+ };
3865
+ const handleMouseUp = () => {
3866
+ state.isDragging = false;
3867
+ document.removeEventListener('mousemove', handleMouseMove);
3868
+ document.removeEventListener('mouseup', handleMouseUp);
3869
+ // Fire onchange when dragging ends
3870
+ updateSingleValue(state.singleValue, false);
3871
+ };
3872
+ document.addEventListener('mousemove', handleMouseMove);
3873
+ document.addEventListener('mouseup', handleMouseUp);
3874
+ };
3875
+ const fieldClass = vertical ? 'range-field vertical' : 'range-field';
3876
+ const orientation = vertical ? 'vertical' : 'horizontal';
3877
+ // Determine tooltip position for vertical sliders
3878
+ const tooltipPosition = vertical ? (tooltipPos === 'top' || tooltipPos === 'bottom' ? 'right' : tooltipPos) : tooltipPos;
3879
+ return m('.input-field', { className: cn, style }, [
3880
+ iconName ? m('i.material-icons.prefix', iconName) : undefined,
3881
+ // Hidden input for label association and accessibility
3882
+ m('input[type=range]', {
3883
+ id,
3884
+ value: state.singleValue,
3885
+ min,
3886
+ max,
3887
+ step,
3888
+ style: { display: 'none' },
3889
+ disabled,
3890
+ tabindex: -1,
3891
+ }),
3892
+ m('div', { class: fieldClass, style: containerStyle }, [
3893
+ m(`.single-range-slider.${orientation}`, {
3894
+ tabindex: disabled ? -1 : 0,
3895
+ role: 'slider',
3896
+ 'aria-valuemin': min,
3897
+ 'aria-valuemax': max,
3898
+ 'aria-valuenow': state.singleValue,
3899
+ 'aria-label': label || 'Range slider',
3900
+ onclick: (e) => {
3901
+ // Focus the slider when clicked
3902
+ e.currentTarget.focus();
3903
+ },
3904
+ onkeydown: (e) => {
3905
+ if (disabled)
3906
+ return;
3907
+ let newValue = state.singleValue;
3908
+ switch (e.key) {
3909
+ case 'ArrowLeft':
3910
+ case 'ArrowDown':
3911
+ e.preventDefault();
3912
+ newValue = Math.max(min, newValue - step);
3913
+ updateSingleValue(newValue, false);
3914
+ // m.redraw();
3915
+ break;
3916
+ case 'ArrowRight':
3917
+ case 'ArrowUp':
3918
+ e.preventDefault();
3919
+ newValue = Math.min(max, newValue + step);
3920
+ updateSingleValue(newValue, false);
3921
+ // m.redraw();
3922
+ break;
3923
+ case 'Home':
3924
+ e.preventDefault();
3925
+ updateSingleValue(min, false);
3926
+ // m.redraw();
3927
+ break;
3928
+ case 'End':
3929
+ e.preventDefault();
3930
+ updateSingleValue(max, false);
3931
+ // m.redraw();
3932
+ break;
3933
+ }
3934
+ },
3935
+ }, [
3936
+ // Track
3937
+ m(`.track.${orientation}`),
3938
+ // Progress
3939
+ m(`.range-progress.${orientation}`, { style: progressStyle }),
3940
+ // Thumb
3941
+ m(`.thumb.${orientation}`, {
3942
+ style: thumbStyle,
3943
+ onmousedown: handleMouseDown,
3944
+ }, showValue
3945
+ ? m(`.value-tooltip.${tooltipPosition}`, state.singleValue.toFixed(0))
3946
+ : null),
3947
+ ]),
3948
+ ]),
3949
+ label
3950
+ ? m(Label, {
3951
+ label,
3952
+ id,
3953
+ isMandatory,
3954
+ isActive: true, // Range sliders always have active labels
3955
+ })
3956
+ : null,
3957
+ helperText ? m(HelperText, { helperText }) : null,
3958
+ ]);
3959
+ };
3960
+ // Double Range Slider (Min/Max)
3961
+ const renderMinMaxRange = (attrs, state, cn, style, iconName, id, label, isMandatory, helperText) => {
3962
+ const { min = 0, max = 100, step = 1, minValue, maxValue, vertical = false, showValue = false, height = '200px', disabled = false, } = attrs;
3963
+ // Initialize range values
3964
+ const currentMinValue = minValue !== undefined ? minValue : attrs.minValue || min;
3965
+ const currentMaxValue = maxValue !== undefined ? maxValue : attrs.maxValue || max;
3966
+ if (state.rangeMinValue === undefined || state.rangeMaxValue === undefined) {
3967
+ state.rangeMinValue = currentMinValue;
3968
+ state.rangeMaxValue = currentMaxValue;
3969
+ }
3970
+ // Initialize active thumb if not set
3971
+ if (state.activeThumb === null) {
3972
+ state.activeThumb = 'min';
3973
+ }
3974
+ const minPercentage = getPercentage(state.rangeMinValue, min, max);
3975
+ const maxPercentage = getPercentage(state.rangeMaxValue, min, max);
3976
+ // Only keep dynamic styles as inline, use CSS classes for static styles
3977
+ const containerStyle = vertical ? { height } : {};
3978
+ const rangeStyle = vertical
3979
+ ? {
3980
+ bottom: `${minPercentage}%`,
3981
+ height: `${maxPercentage - minPercentage}%`,
3982
+ }
3983
+ : {
3984
+ left: `${minPercentage}%`,
3985
+ width: `${maxPercentage - minPercentage}%`,
3986
+ };
3987
+ // Only keep dynamic positioning and z-index as inline styles
3988
+ const createThumbStyle = (percentage, isActive) => vertical
3989
+ ? {
3990
+ bottom: `${percentage}%`,
3991
+ zIndex: isActive ? 10 : 5,
3992
+ }
3993
+ : {
3994
+ left: `${percentage}%`,
3995
+ marginLeft: '-10px', // Half of thumb size (20px)
3996
+ zIndex: isActive ? 10 : 5,
3997
+ };
3998
+ const handleMouseDown = (thumb) => (e) => {
3999
+ if (disabled)
4000
+ return;
4001
+ e.preventDefault();
4002
+ state.isDragging = true;
4003
+ state.activeThumb = thumb;
4004
+ // Get container reference from the current target's parent
4005
+ const thumbElement = e.currentTarget;
4006
+ const container = thumbElement.parentElement;
4007
+ if (!container)
4008
+ return;
4009
+ const handleMouseMove = (e) => {
4010
+ if (!state.isDragging || !container)
4011
+ return;
4012
+ const rect = container.getBoundingClientRect();
4013
+ let percentage;
4014
+ if (vertical) {
4015
+ percentage = ((rect.bottom - e.clientY) / rect.height) * 100;
4016
+ }
4017
+ else {
4018
+ percentage = ((e.clientX - rect.left) / rect.width) * 100;
4019
+ }
4020
+ percentage = Math.max(0, Math.min(100, percentage));
4021
+ const value = min + (percentage / 100) * (max - min);
4022
+ const steppedValue = Math.round(value / step) * step;
4023
+ if (thumb === 'min') {
4024
+ updateRangeValues(Math.min(steppedValue, state.rangeMaxValue), state.rangeMaxValue, attrs, state, true);
4025
+ }
4026
+ else {
4027
+ updateRangeValues(state.rangeMinValue, Math.max(steppedValue, state.rangeMinValue), attrs, state, true);
4028
+ }
4029
+ // Redraw to update the UI during drag
4030
+ m.redraw();
4031
+ };
4032
+ const handleMouseUp = () => {
4033
+ state.isDragging = false;
4034
+ state.activeThumb = null;
4035
+ document.removeEventListener('mousemove', handleMouseMove);
4036
+ document.removeEventListener('mouseup', handleMouseUp);
4037
+ // Fire onchange when dragging ends
4038
+ updateRangeValues(state.rangeMinValue, state.rangeMaxValue, attrs, state, false);
4039
+ };
4040
+ document.addEventListener('mousemove', handleMouseMove);
4041
+ document.addEventListener('mouseup', handleMouseUp);
4042
+ };
4043
+ const fieldClass = vertical ? 'range-field vertical' : 'range-field';
4044
+ const orientation = vertical ? 'vertical' : 'horizontal';
4045
+ return m('.input-field', { className: cn, style }, [
4046
+ iconName ? m('i.material-icons.prefix', iconName) : undefined,
4047
+ // Hidden inputs for label association and accessibility
4048
+ m('input[type=range]', {
4049
+ id,
4050
+ value: state.rangeMinValue,
4051
+ min,
4052
+ max,
4053
+ step,
4054
+ style: { display: 'none' },
4055
+ disabled,
4056
+ tabindex: -1,
4057
+ }),
4058
+ m('input[type=range]', {
4059
+ id: `${id}_max`,
4060
+ value: state.rangeMaxValue,
4061
+ min,
4062
+ max,
4063
+ step,
4064
+ style: { display: 'none' },
4065
+ disabled,
4066
+ tabindex: -1,
4067
+ }),
4068
+ m(`div`, { className: fieldClass }, [
4069
+ m(`.double-range-slider.${orientation}`, {
4070
+ style: containerStyle,
4071
+ tabindex: disabled ? -1 : 0,
4072
+ role: 'slider',
4073
+ 'aria-valuemin': min,
4074
+ 'aria-valuemax': max,
4075
+ 'aria-valuenow': state.rangeMinValue,
4076
+ 'aria-valuetext': `${state.rangeMinValue} to ${state.rangeMaxValue}`,
4077
+ 'aria-label': label || 'Range slider',
4078
+ onclick: (e) => {
4079
+ // Focus the slider when clicked
4080
+ e.currentTarget.focus();
4081
+ },
4082
+ onkeydown: (e) => {
4083
+ if (disabled)
4084
+ return;
4085
+ let newMinValue = state.rangeMinValue;
4086
+ let newMaxValue = state.rangeMaxValue;
4087
+ const activeThumb = state.activeThumb || 'min';
4088
+ switch (e.key) {
4089
+ case 'ArrowLeft':
4090
+ case 'ArrowDown':
4091
+ e.preventDefault();
4092
+ if (activeThumb === 'min') {
4093
+ newMinValue = Math.max(min, newMinValue - step);
4094
+ updateRangeValues(newMinValue, newMaxValue, attrs, state, false);
4095
+ }
4096
+ else {
4097
+ newMaxValue = Math.max(newMinValue, newMaxValue - step);
4098
+ updateRangeValues(newMinValue, newMaxValue, attrs, state, false);
4099
+ }
4100
+ // m.redraw();
4101
+ break;
4102
+ case 'ArrowRight':
4103
+ case 'ArrowUp':
4104
+ e.preventDefault();
4105
+ if (activeThumb === 'min') {
4106
+ newMinValue = Math.min(newMaxValue, newMinValue + step);
4107
+ updateRangeValues(newMinValue, newMaxValue, attrs, state, false);
4108
+ }
4109
+ else {
4110
+ newMaxValue = Math.min(max, newMaxValue + step);
4111
+ updateRangeValues(newMinValue, newMaxValue, attrs, state, false);
4112
+ }
4113
+ // m.redraw();
4114
+ break;
4115
+ case 'Tab':
4116
+ // Handle Tab navigation properly
4117
+ if (activeThumb === 'min') {
4118
+ if (e.shiftKey) {
4119
+ // Shift+Tab from min thumb: go to previous element (let browser handle)
4120
+ return; // Don't prevent default
4121
+ }
4122
+ else {
4123
+ // Tab from min thumb: go to max thumb
4124
+ e.preventDefault();
4125
+ state.activeThumb = 'max';
4126
+ }
4127
+ }
4128
+ else { // activeThumb === 'max'
4129
+ if (e.shiftKey) {
4130
+ // Shift+Tab from max thumb: go to min thumb
4131
+ e.preventDefault();
4132
+ state.activeThumb = 'min';
4133
+ }
4134
+ else {
4135
+ // Tab from max thumb: go to next element (let browser handle)
4136
+ return; // Don't prevent default
4137
+ }
4138
+ }
4139
+ break;
4140
+ case 'Home':
4141
+ e.preventDefault();
4142
+ if (activeThumb === 'min') {
4143
+ updateRangeValues(min, newMaxValue, attrs, state, false);
4144
+ }
4145
+ else {
4146
+ updateRangeValues(newMinValue, newMinValue, attrs, state, false);
4147
+ }
4148
+ // m.redraw();
4149
+ break;
4150
+ case 'End':
4151
+ e.preventDefault();
4152
+ if (activeThumb === 'min') {
4153
+ updateRangeValues(newMaxValue, newMaxValue, attrs, state, false);
4154
+ }
4155
+ else {
4156
+ updateRangeValues(newMinValue, max, attrs, state, false);
4157
+ }
4158
+ // m.redraw();
4159
+ break;
4160
+ }
4161
+ },
4162
+ }, [
4163
+ // Track
4164
+ m(`.track.${orientation}`),
4165
+ // Range
4166
+ m(`.range.${orientation}`, { style: rangeStyle }),
4167
+ // Min thumb
4168
+ m(`.thumb.${orientation}.min-thumb${state.activeThumb === 'min' ? '.active' : ''}`, {
4169
+ style: createThumbStyle(minPercentage, state.activeThumb === 'min'),
4170
+ onmousedown: handleMouseDown('min'),
4171
+ onclick: () => {
4172
+ state.activeThumb = 'min';
4173
+ // m.redraw();
4174
+ },
4175
+ }, showValue ? m(`.value.${orientation}`, state.rangeMinValue.toFixed(0)) : null),
4176
+ // Max thumb
4177
+ m(`.thumb.${orientation}.max-thumb${state.activeThumb === 'max' ? '.active' : ''}`, {
4178
+ style: createThumbStyle(maxPercentage, state.activeThumb === 'max'),
4179
+ onmousedown: handleMouseDown('max'),
4180
+ onclick: () => {
4181
+ state.activeThumb = 'max';
4182
+ // m.redraw();
4183
+ },
4184
+ }, showValue ? m(`.value.${orientation}`, state.rangeMaxValue.toFixed(0)) : null),
4185
+ ]),
4186
+ ]),
4187
+ label
4188
+ ? m(Label, {
4189
+ label,
4190
+ id,
4191
+ isMandatory,
4192
+ isActive: true, // Range sliders always have active labels
4193
+ })
4194
+ : null,
4195
+ helperText ? m(HelperText, { helperText }) : null,
4196
+ ]);
4197
+ };
4198
+
3358
4199
  /**
3359
4200
  * Pure TypeScript MaterialBox - creates an image lightbox that fills the screen when clicked
3360
4201
  * No MaterializeCSS dependencies
@@ -3529,7 +4370,7 @@ const MaterialBox = () => {
3529
4370
  view: ({ attrs }) => {
3530
4371
  const { src, alt, width, height, caption, className, style } = attrs, otherAttrs = __rest(attrs, ["src", "alt", "width", "height", "caption", "className", "style"]);
3531
4372
  return m('img.materialboxed', Object.assign(Object.assign({}, otherAttrs), { src, alt: alt || '', width,
3532
- height, className: ['materialboxed', className].filter(Boolean).join(' '), style: Object.assign({ cursor: 'zoom-in', transition: 'opacity 200ms ease' }, style), onclick: (e) => {
4373
+ height, className: ['materialboxed', className].filter(Boolean).join(' ') || undefined, style: Object.assign({ cursor: 'zoom-in', transition: 'opacity 200ms ease' }, style), onclick: (e) => {
3533
4374
  e.preventDefault();
3534
4375
  openBox(e.target, attrs);
3535
4376
  } }));
@@ -3611,7 +4452,7 @@ const ModalPanel = () => {
3611
4452
  .filter(Boolean)
3612
4453
  .join(' ')
3613
4454
  .trim();
3614
- const overlayClasses = ['modal-overlay', state.isOpen ? 'active' : ''].filter(Boolean).join(' ').trim();
4455
+ const overlayClasses = ['modal-overlay', state.isOpen ? 'active' : ''].filter(Boolean).join(' ').trim() || undefined;
3615
4456
  return m('div', { className: 'modal-container' }, [
3616
4457
  // Modal overlay
3617
4458
  m('div', {
@@ -3636,23 +4477,25 @@ const ModalPanel = () => {
3636
4477
  role: 'dialog',
3637
4478
  'aria-labelledby': `${id}-title`,
3638
4479
  'aria-describedby': description ? `${id}-desc` : undefined,
3639
- style: Object.assign(Object.assign({ display: state.isOpen ? 'flex' : 'none', position: 'fixed' }, (bottomSheet ? {
3640
- // Bottom sheet positioning
3641
- top: 'auto',
3642
- bottom: '0',
3643
- left: '0',
3644
- right: '0',
3645
- transform: 'none',
3646
- maxWidth: '100%',
3647
- borderRadius: '8px 8px 0 0',
3648
- } : {
3649
- // Regular modal positioning
3650
- top: '50%',
3651
- left: '50%',
3652
- transform: 'translate(-50%, -50%)',
3653
- maxWidth: '75%',
3654
- borderRadius: '4px',
3655
- })), { 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)' }),
4480
+ style: Object.assign(Object.assign({ display: state.isOpen ? 'flex' : 'none', position: 'fixed' }, (bottomSheet
4481
+ ? {
4482
+ // Bottom sheet positioning
4483
+ top: 'auto',
4484
+ bottom: '0',
4485
+ left: '0',
4486
+ right: '0',
4487
+ transform: 'none',
4488
+ maxWidth: '100%',
4489
+ borderRadius: '8px 8px 0 0',
4490
+ }
4491
+ : {
4492
+ // Regular modal positioning
4493
+ top: '50%',
4494
+ left: '50%',
4495
+ transform: 'translate(-50%, -50%)',
4496
+ maxWidth: '75%',
4497
+ borderRadius: '4px',
4498
+ })), { 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)' }),
3656
4499
  onclick: (e) => e.stopPropagation(), // Prevent backdrop click when clicking inside modal
3657
4500
  }, [
3658
4501
  // Close button
@@ -4690,7 +5533,7 @@ const RadioButtons = () => {
4690
5533
  callback(propId);
4691
5534
  }
4692
5535
  };
4693
- const cn = [newRow ? 'clear' : '', className].filter(Boolean).join(' ').trim();
5536
+ const cn = [newRow ? 'clear' : '', className].filter(Boolean).join(' ').trim() || undefined;
4694
5537
  const optionsContent = layout === 'horizontal'
4695
5538
  ? m('div.grid-container', options.map((r) => m(RadioButton, Object.assign(Object.assign({}, r), { onchange,
4696
5539
  groupId, disabled: disabled || r.disabled, className: checkboxClass, checked: r.id === checkedId, inputId: `${componentId}-${r.id}` }))))
@@ -4963,7 +5806,7 @@ const Switch = () => {
4963
5806
  view: ({ attrs }) => {
4964
5807
  const id = attrs.id || state.id;
4965
5808
  const { label, left, right, disabled, newRow, onchange, isMandatory, className = 'col s12' } = attrs, params = __rest(attrs, ["label", "left", "right", "disabled", "newRow", "onchange", "isMandatory", "className"]);
4966
- const cn = ['input-field', newRow ? 'clear' : '', className].filter(Boolean).join(' ').trim();
5809
+ const cn = ['input-field', newRow ? 'clear' : '', className].filter(Boolean).join(' ').trim() || undefined;
4967
5810
  return m('div', {
4968
5811
  className: cn,
4969
5812
  onclick: (e) => {
@@ -5114,7 +5957,7 @@ const Tabs = () => {
5114
5957
  },
5115
5958
  view: ({ attrs }) => {
5116
5959
  const { tabWidth, tabs, className, style, swipeable = false } = attrs;
5117
- const cn = [tabWidth === 'fill' ? 'tabs-fixed-width' : '', className].filter(Boolean).join(' ').trim();
5960
+ const cn = [tabWidth === 'fill' ? 'tabs-fixed-width' : '', className].filter(Boolean).join(' ').trim() || undefined;
5118
5961
  const anchoredTabs = tabs.map(toAnchored());
5119
5962
  const activeTab = setActiveTabId(anchoredTabs, attrs.selectedTabId);
5120
5963
  updateIndicator();
@@ -6100,8 +6943,8 @@ const FileUpload = () => {
6100
6943
  }
6101
6944
  // Check file type
6102
6945
  if (attrs.accept) {
6103
- const acceptedTypes = attrs.accept.split(',').map(type => type.trim());
6104
- const isAccepted = acceptedTypes.some(acceptedType => {
6946
+ const acceptedTypes = attrs.accept.split(',').map((type) => type.trim());
6947
+ const isAccepted = acceptedTypes.some((acceptedType) => {
6105
6948
  if (acceptedType.startsWith('.')) {
6106
6949
  // Extension check
6107
6950
  return file.name.toLowerCase().endsWith(acceptedType.toLowerCase());
@@ -6161,11 +7004,11 @@ const FileUpload = () => {
6161
7004
  }
6162
7005
  // Notify parent component
6163
7006
  if (attrs.onFilesSelected) {
6164
- attrs.onFilesSelected(state.files.filter(f => !f.uploadError));
7007
+ attrs.onFilesSelected(state.files.filter((f) => !f.uploadError));
6165
7008
  }
6166
7009
  };
6167
7010
  const removeFile = (fileToRemove, attrs) => {
6168
- state.files = state.files.filter(file => file !== fileToRemove);
7011
+ state.files = state.files.filter((file) => file !== fileToRemove);
6169
7012
  if (attrs.onFileRemoved) {
6170
7013
  attrs.onFileRemoved(fileToRemove);
6171
7014
  }
@@ -6184,11 +7027,11 @@ const FileUpload = () => {
6184
7027
  id: uniqueId(),
6185
7028
  files: [],
6186
7029
  isDragOver: false,
6187
- isUploading: false
7030
+ isUploading: false,
6188
7031
  };
6189
7032
  },
6190
7033
  view: ({ attrs }) => {
6191
- const { accept, multiple = false, disabled = false, label = 'Choose files or drag them here', helperText, showPreview = true, className = '', error } = attrs;
7034
+ const { accept, multiple = false, disabled = false, label = 'Choose files or drag them here', helperText, showPreview = true, className = '', error, } = attrs;
6192
7035
  return m('.file-upload-container', { class: className }, [
6193
7036
  // Upload area
6194
7037
  m('.file-upload-area', {
@@ -6196,8 +7039,10 @@ const FileUpload = () => {
6196
7039
  state.isDragOver ? 'drag-over' : '',
6197
7040
  disabled ? 'disabled' : '',
6198
7041
  error ? 'error' : '',
6199
- state.files.length > 0 ? 'has-files' : ''
6200
- ].filter(Boolean).join(' '),
7042
+ state.files.length > 0 ? 'has-files' : '',
7043
+ ]
7044
+ .filter(Boolean)
7045
+ .join(' ') || undefined,
6201
7046
  ondragover: (e) => {
6202
7047
  if (disabled)
6203
7048
  return;
@@ -6228,7 +7073,7 @@ const FileUpload = () => {
6228
7073
  return;
6229
7074
  const input = document.getElementById(state.id);
6230
7075
  input === null || input === void 0 ? void 0 : input.click();
6231
- }
7076
+ },
6232
7077
  }, [
6233
7078
  m('input[type="file"]', {
6234
7079
  id: state.id,
@@ -6241,57 +7086,55 @@ const FileUpload = () => {
6241
7086
  if (target.files) {
6242
7087
  handleFiles(target.files, attrs);
6243
7088
  }
6244
- }
7089
+ },
6245
7090
  }),
6246
7091
  m('.file-upload-content', [
6247
7092
  m('i.material-icons.file-upload-icon', 'cloud_upload'),
6248
7093
  m('p.file-upload-label', label),
6249
7094
  helperText && m('p.file-upload-helper', helperText),
6250
- accept && m('p.file-upload-types', `Accepted: ${accept}`)
6251
- ])
7095
+ accept && m('p.file-upload-types', `Accepted: ${accept}`),
7096
+ ]),
6252
7097
  ]),
6253
7098
  // Error message
6254
7099
  error && m('.file-upload-error', error),
6255
7100
  // File list
6256
- state.files.length > 0 && m('.file-upload-list', [
6257
- m('h6', 'Selected Files:'),
6258
- state.files.map(file => m('.file-upload-item', { key: file.name + file.size }, [
6259
- // Preview thumbnail
6260
- showPreview && file.preview && m('.file-preview', [
6261
- m('img', { src: file.preview, alt: file.name })
6262
- ]),
6263
- // File info
6264
- m('.file-info', [
6265
- m('.file-name', file.name),
6266
- m('.file-details', [
6267
- m('span.file-size', formatFileSize(file.size)),
6268
- file.type && m('span.file-type', file.type)
6269
- ]),
6270
- // Progress bar (if uploading)
6271
- file.uploadProgress !== undefined && m('.file-progress', [
6272
- m('.progress', [
6273
- m('.determinate', {
6274
- style: { width: `${file.uploadProgress}%` }
6275
- })
6276
- ])
7101
+ state.files.length > 0 &&
7102
+ m('.file-upload-list', [
7103
+ m('h6', 'Selected Files:'),
7104
+ state.files.map((file) => m('.file-upload-item', { key: file.name + file.size }, [
7105
+ // Preview thumbnail
7106
+ showPreview && file.preview && m('.file-preview', [m('img', { src: file.preview, alt: file.name })]),
7107
+ // File info
7108
+ m('.file-info', [
7109
+ m('.file-name', file.name),
7110
+ m('.file-details', [
7111
+ m('span.file-size', formatFileSize(file.size)),
7112
+ file.type && m('span.file-type', file.type),
7113
+ ]),
7114
+ // Progress bar (if uploading)
7115
+ file.uploadProgress !== undefined &&
7116
+ m('.file-progress', [
7117
+ m('.progress', [
7118
+ m('.determinate', {
7119
+ style: { width: `${file.uploadProgress}%` },
7120
+ }),
7121
+ ]),
7122
+ ]),
7123
+ // Error message
7124
+ file.uploadError && m('.file-error', file.uploadError),
6277
7125
  ]),
6278
- // Error message
6279
- file.uploadError && m('.file-error', file.uploadError)
6280
- ]),
6281
- // Remove button
6282
- m('button.btn-flat.file-remove', {
6283
- onclick: (e) => {
6284
- e.stopPropagation();
6285
- removeFile(file, attrs);
6286
- },
6287
- title: 'Remove file'
6288
- }, [
6289
- m('i.material-icons', 'close')
6290
- ])
6291
- ]))
6292
- ])
7126
+ // Remove button
7127
+ m('button.btn-flat.file-remove', {
7128
+ onclick: (e) => {
7129
+ e.stopPropagation();
7130
+ removeFile(file, attrs);
7131
+ },
7132
+ title: 'Remove file',
7133
+ }, [m('i.material-icons', 'close')]),
7134
+ ])),
7135
+ ]),
6293
7136
  ]);
6294
- }
7137
+ },
6295
7138
  };
6296
7139
  };
6297
7140
 
@@ -6321,7 +7164,7 @@ const Sidenav = () => {
6321
7164
  state = {
6322
7165
  id: attrs.id || uniqueId(),
6323
7166
  isOpen: attrs.isOpen || false,
6324
- isAnimating: false
7167
+ isAnimating: false,
6325
7168
  };
6326
7169
  // Set up keyboard listener
6327
7170
  if (typeof document !== 'undefined' && attrs.closeOnEscape !== false) {
@@ -6350,34 +7193,33 @@ const Sidenav = () => {
6350
7193
  }
6351
7194
  },
6352
7195
  view: ({ attrs, children }) => {
6353
- const { position = 'left', mode = 'overlay', width = 300, className = '', showBackdrop = true, animationDuration = 300, fixed = false } = attrs;
7196
+ const { position = 'left', mode = 'overlay', width = 300, className = '', showBackdrop = true, animationDuration = 300, fixed = false, } = attrs;
6354
7197
  const isOpen = state.isOpen;
6355
7198
  return [
6356
7199
  // Backdrop (using existing materialize class)
6357
- showBackdrop && mode === 'overlay' && m('.sidenav-overlay', {
6358
- style: {
6359
- display: isOpen ? 'block' : 'none',
6360
- opacity: isOpen ? '1' : '0'
6361
- },
6362
- onclick: () => handleBackdropClick(attrs)
6363
- }),
7200
+ showBackdrop &&
7201
+ mode === 'overlay' &&
7202
+ m('.sidenav-overlay', {
7203
+ style: {
7204
+ display: isOpen ? 'block' : 'none',
7205
+ opacity: isOpen ? '1' : '0',
7206
+ },
7207
+ onclick: () => handleBackdropClick(attrs),
7208
+ }),
6364
7209
  // Sidenav (using existing materialize structure)
6365
7210
  m('ul.sidenav', {
6366
7211
  id: state.id,
6367
- class: [
6368
- position === 'right' ? 'right-aligned' : '',
6369
- fixed ? 'sidenav-fixed' : '',
6370
- className
6371
- ].filter(Boolean).join(' '),
7212
+ class: [position === 'right' ? 'right-aligned' : '', fixed ? 'sidenav-fixed' : '', className]
7213
+ .filter(Boolean)
7214
+ .join(' ') || undefined,
6372
7215
  style: {
6373
7216
  width: `${width}px`,
6374
- transform: isOpen ? 'translateX(0)' :
6375
- position === 'left' ? 'translateX(-105%)' : 'translateX(105%)',
6376
- 'transition-duration': `${animationDuration}ms`
6377
- }
6378
- }, children)
7217
+ transform: isOpen ? 'translateX(0)' : position === 'left' ? 'translateX(-105%)' : 'translateX(105%)',
7218
+ 'transition-duration': `${animationDuration}ms`,
7219
+ },
7220
+ }, children),
6379
7221
  ];
6380
- }
7222
+ },
6381
7223
  };
6382
7224
  };
6383
7225
  /**
@@ -6387,37 +7229,30 @@ const Sidenav = () => {
6387
7229
  const SidenavItem = () => {
6388
7230
  return {
6389
7231
  view: ({ attrs, children }) => {
6390
- const { text, icon, active = false, disabled = false, onclick, href, className = '', divider = false, subheader = false } = attrs;
7232
+ const { text, icon, active = false, disabled = false, onclick, href, className = '', divider = false, subheader = false, } = attrs;
6391
7233
  if (divider) {
6392
7234
  return m('li.divider');
6393
7235
  }
6394
7236
  if (subheader) {
6395
7237
  return m('li.subheader', text || children);
6396
7238
  }
6397
- const itemClasses = [
6398
- active ? 'active' : '',
6399
- disabled ? 'disabled' : '',
6400
- className
6401
- ].filter(Boolean).join(' ');
6402
- const content = [
6403
- icon && m('i.material-icons', icon),
6404
- text || children
6405
- ];
7239
+ const itemClasses = [active ? 'active' : '', disabled ? 'disabled' : '', className].filter(Boolean).join(' ') || undefined;
7240
+ const content = [icon && m('i.material-icons', icon), text || children];
6406
7241
  if (href && !disabled) {
6407
7242
  return m('li', { class: itemClasses }, [
6408
7243
  m('a', {
6409
7244
  href,
6410
- onclick: disabled ? undefined : onclick
6411
- }, content)
7245
+ onclick: disabled ? undefined : onclick,
7246
+ }, content),
6412
7247
  ]);
6413
7248
  }
6414
7249
  return m('li', { class: itemClasses }, [
6415
7250
  m('a', {
6416
7251
  onclick: disabled ? undefined : onclick,
6417
- href: '#!'
6418
- }, content)
7252
+ href: '#!',
7253
+ }, content),
6419
7254
  ]);
6420
- }
7255
+ },
6421
7256
  };
6422
7257
  };
6423
7258
  /**
@@ -6468,7 +7303,7 @@ class SidenavManager {
6468
7303
  const Breadcrumb = () => {
6469
7304
  return {
6470
7305
  view: ({ attrs }) => {
6471
- const { items = [], separator = 'chevron_right', className = '', showIcons = false, maxItems, showHome = false } = attrs;
7306
+ const { items = [], separator = 'chevron_right', className = '', showIcons = false, maxItems, showHome = false, } = attrs;
6472
7307
  if (items.length === 0) {
6473
7308
  return null;
6474
7309
  }
@@ -6477,52 +7312,46 @@ const Breadcrumb = () => {
6477
7312
  if (maxItems && items.length > maxItems) {
6478
7313
  const firstItem = items[0];
6479
7314
  const lastItems = items.slice(-(maxItems - 2));
6480
- displayItems = [
6481
- firstItem,
6482
- { text: '...', disabled: true, className: 'breadcrumb-ellipsis' },
6483
- ...lastItems
6484
- ];
7315
+ displayItems = [firstItem, { text: '...', disabled: true, className: 'breadcrumb-ellipsis' }, ...lastItems];
6485
7316
  }
6486
7317
  return m('nav.breadcrumb', { class: className }, [
6487
- m('ol.breadcrumb-list', displayItems.map((item, index) => {
7318
+ m('ol.breadcrumb-list', displayItems
7319
+ .map((item, index) => {
6488
7320
  const isLast = index === displayItems.length - 1;
6489
7321
  const isFirst = index === 0;
6490
7322
  return [
6491
7323
  // Breadcrumb item
6492
7324
  m('li.breadcrumb-item', {
6493
- class: [
6494
- item.active || isLast ? 'active' : '',
6495
- item.disabled ? 'disabled' : '',
6496
- item.className || ''
6497
- ].filter(Boolean).join(' ')
7325
+ class: [item.active || isLast ? 'active' : '', item.disabled ? 'disabled' : '', item.className || '']
7326
+ .filter(Boolean)
7327
+ .join(' ') || undefined,
6498
7328
  }, [
6499
- item.href && !item.disabled && !isLast ?
6500
- // Link item
6501
- m('a.breadcrumb-link', {
6502
- href: item.href,
6503
- onclick: item.onclick
6504
- }, [
6505
- (showIcons && item.icon) && m('i.material-icons.breadcrumb-icon', item.icon),
6506
- (showHome && isFirst && !item.icon) && m('i.material-icons.breadcrumb-icon', 'home'),
6507
- m('span.breadcrumb-text', item.text)
6508
- ]) :
6509
- // Text item (active or disabled)
6510
- m('span.breadcrumb-text', {
6511
- onclick: item.disabled ? undefined : item.onclick
6512
- }, [
6513
- (showIcons && item.icon) && m('i.material-icons.breadcrumb-icon', item.icon),
6514
- (showHome && isFirst && !item.icon) && m('i.material-icons.breadcrumb-icon', 'home'),
6515
- item.text
6516
- ])
7329
+ item.href && !item.disabled && !isLast
7330
+ ? // Link item
7331
+ m('a.breadcrumb-link', {
7332
+ href: item.href,
7333
+ onclick: item.onclick,
7334
+ }, [
7335
+ showIcons && item.icon && m('i.material-icons.breadcrumb-icon', item.icon),
7336
+ showHome && isFirst && !item.icon && m('i.material-icons.breadcrumb-icon', 'home'),
7337
+ m('span.breadcrumb-text', item.text),
7338
+ ])
7339
+ : // Text item (active or disabled)
7340
+ m('span.breadcrumb-text', {
7341
+ onclick: item.disabled ? undefined : item.onclick,
7342
+ }, [
7343
+ showIcons && item.icon && m('i.material-icons.breadcrumb-icon', item.icon),
7344
+ showHome && isFirst && !item.icon && m('i.material-icons.breadcrumb-icon', 'home'),
7345
+ item.text,
7346
+ ]),
6517
7347
  ]),
6518
7348
  // Separator (except for last item)
6519
- !isLast && m('li.breadcrumb-separator', [
6520
- m('i.material-icons', separator)
6521
- ])
7349
+ !isLast && m('li.breadcrumb-separator', [m('i.material-icons', separator)]),
6522
7350
  ];
6523
- }).reduce((acc, val) => acc.concat(val), []))
7351
+ })
7352
+ .reduce((acc, val) => acc.concat(val), [])),
6524
7353
  ]);
6525
- }
7354
+ },
6526
7355
  };
6527
7356
  };
6528
7357
  /**
@@ -6535,7 +7364,7 @@ const createBreadcrumb = (path, basePath = '/') => {
6535
7364
  items.push({
6536
7365
  text: 'Home',
6537
7366
  href: basePath,
6538
- icon: 'home'
7367
+ icon: 'home',
6539
7368
  });
6540
7369
  // Add path segments
6541
7370
  let currentPath = basePath;
@@ -6545,7 +7374,7 @@ const createBreadcrumb = (path, basePath = '/') => {
6545
7374
  items.push({
6546
7375
  text: segment.charAt(0).toUpperCase() + segment.slice(1).replace(/-/g, ' '),
6547
7376
  href: isLast ? undefined : currentPath,
6548
- active: isLast
7377
+ active: isLast,
6549
7378
  });
6550
7379
  });
6551
7380
  return items;
@@ -6564,19 +7393,18 @@ class BreadcrumbManager {
6564
7393
  items.push({
6565
7394
  text: 'Home',
6566
7395
  href: '/',
6567
- icon: 'home'
7396
+ icon: 'home',
6568
7397
  });
6569
7398
  let currentPath = '';
6570
7399
  segments.forEach((segment, index) => {
6571
7400
  currentPath += '/' + segment;
6572
7401
  const isLast = index === segments.length - 1;
6573
7402
  // Use custom text from config or format segment
6574
- const text = routeConfig[currentPath] ||
6575
- segment.charAt(0).toUpperCase() + segment.slice(1).replace(/-/g, ' ');
7403
+ const text = routeConfig[currentPath] || segment.charAt(0).toUpperCase() + segment.slice(1).replace(/-/g, ' ');
6576
7404
  items.push({
6577
7405
  text,
6578
7406
  href: isLast ? undefined : currentPath,
6579
- active: isLast
7407
+ active: isLast,
6580
7408
  });
6581
7409
  });
6582
7410
  return items;
@@ -6588,7 +7416,7 @@ class BreadcrumbManager {
6588
7416
  return hierarchy.map((item, index) => ({
6589
7417
  text: item[textKey],
6590
7418
  href: index === hierarchy.length - 1 ? undefined : item[pathKey],
6591
- active: index === hierarchy.length - 1
7419
+ active: index === hierarchy.length - 1,
6592
7420
  }));
6593
7421
  }
6594
7422
  }
@@ -6722,7 +7550,7 @@ const Wizard = () => {
6722
7550
  hasError ? 'error' : '',
6723
7551
  step.disabled ? 'disabled' : '',
6724
7552
  step.optional ? 'optional' : ''
6725
- ].filter(Boolean).join(' '),
7553
+ ].filter(Boolean).join(' ') || undefined,
6726
7554
  onclick: allowHeaderNavigation && !step.disabled ?
6727
7555
  () => goToStep(index, attrs) : undefined
6728
7556
  }, [
@@ -6796,6 +7624,292 @@ const Stepper = () => {
6796
7624
  };
6797
7625
  };
6798
7626
 
7627
+ // Utility function to check if a node is the last in its branch
7628
+ const isNodeLastInBranch = (nodePath, rootNodes) => {
7629
+ // Navigate to the node's position and check if it's the last child at every level
7630
+ let currentNodes = rootNodes;
7631
+ for (let i = 0; i < nodePath.length; i++) {
7632
+ const index = nodePath[i];
7633
+ const isLastAtThisLevel = index === currentNodes.length - 1;
7634
+ // If this is not the last child at this level, then this node is not last in branch
7635
+ if (!isLastAtThisLevel) {
7636
+ return false;
7637
+ }
7638
+ // Move to the next level if it exists
7639
+ if (i < nodePath.length - 1) {
7640
+ const currentNode = currentNodes[index];
7641
+ if (currentNode.children) {
7642
+ currentNodes = currentNode.children;
7643
+ }
7644
+ }
7645
+ }
7646
+ return true;
7647
+ };
7648
+ const TreeNodeComponent = () => {
7649
+ return {
7650
+ view: ({ attrs }) => {
7651
+ const { node, level, isSelected, isExpanded, isFocused, showConnectors, iconType, selectionMode, onToggleExpand, onToggleSelect, onFocus, } = attrs;
7652
+ const hasChildren = node.children && node.children.length > 0;
7653
+ const indentLevel = level * 24; // 24px per level
7654
+ return m('li.tree-node', {
7655
+ class: [
7656
+ isSelected && 'selected',
7657
+ isFocused && 'focused',
7658
+ node.disabled && 'disabled',
7659
+ hasChildren && 'has-children',
7660
+ attrs.isLastInBranch && 'tree-last-in-branch',
7661
+ ]
7662
+ .filter(Boolean)
7663
+ .join(' ') || undefined,
7664
+ 'data-node-id': node.id,
7665
+ 'data-level': level,
7666
+ }, [
7667
+ // Node content
7668
+ m('.tree-node-content', {
7669
+ style: {
7670
+ paddingLeft: `${indentLevel}px`,
7671
+ },
7672
+ onclick: node.disabled
7673
+ ? undefined
7674
+ : () => {
7675
+ if (selectionMode !== 'none') {
7676
+ onToggleSelect(node.id);
7677
+ }
7678
+ onFocus(node.id);
7679
+ },
7680
+ onkeydown: (e) => {
7681
+ if (e.key === 'Enter' || e.key === ' ') {
7682
+ e.preventDefault();
7683
+ if (!node.disabled && selectionMode !== 'none') {
7684
+ onToggleSelect(node.id);
7685
+ }
7686
+ }
7687
+ },
7688
+ tabindex: node.disabled ? -1 : 0,
7689
+ role: selectionMode === 'multiple' ? 'option' : 'treeitem',
7690
+ 'aria-selected': selectionMode !== 'none' ? isSelected.toString() : undefined,
7691
+ 'aria-expanded': hasChildren ? isExpanded.toString() : undefined,
7692
+ 'aria-disabled': node.disabled ? 'true' : undefined,
7693
+ }, [
7694
+ // Connector lines
7695
+ showConnectors &&
7696
+ level > 0 &&
7697
+ m('.tree-connectors', Array.from({ length: level }, (_, i) => m('.tree-connector', {
7698
+ key: i,
7699
+ style: { left: `${i * 24 + 12}px` },
7700
+ }))),
7701
+ // Expand/collapse icon or spacer
7702
+ hasChildren
7703
+ ? m('.tree-expand-icon', {
7704
+ onclick: (e) => {
7705
+ e.stopPropagation();
7706
+ if (!node.disabled) {
7707
+ onToggleExpand(node.id);
7708
+ }
7709
+ },
7710
+ class: iconType,
7711
+ }, [
7712
+ iconType === 'plus-minus'
7713
+ ? m('span.tree-plus-minus', isExpanded ? '−' : '+')
7714
+ : iconType === 'triangle'
7715
+ ? m('span.tree-triangle', { class: isExpanded ? 'expanded' : undefined }, '▶')
7716
+ : iconType === 'chevron'
7717
+ ? m(MaterialIcon, {
7718
+ name: 'chevron',
7719
+ direction: isExpanded ? 'down' : 'right',
7720
+ class: 'tree-chevron-icon',
7721
+ })
7722
+ : m(MaterialIcon, {
7723
+ name: 'caret',
7724
+ direction: isExpanded ? 'down' : 'right',
7725
+ class: 'tree-caret-icon',
7726
+ }),
7727
+ ])
7728
+ : m('.tree-expand-spacer'), // Spacer for alignment
7729
+ // Selection indicator for multiple selection
7730
+ selectionMode === 'multiple' &&
7731
+ m('.tree-selection-indicator', [
7732
+ m('input[type=checkbox]', {
7733
+ checked: isSelected,
7734
+ disabled: node.disabled,
7735
+ onchange: () => {
7736
+ if (!node.disabled) {
7737
+ onToggleSelect(node.id);
7738
+ }
7739
+ },
7740
+ onclick: (e) => e.stopPropagation(),
7741
+ }),
7742
+ ]),
7743
+ // Node icon (optional)
7744
+ node.icon && m('i.tree-node-icon.material-icons', node.icon),
7745
+ // Node label
7746
+ m('span.tree-node-label', node.label),
7747
+ ]),
7748
+ // Children (recursive)
7749
+ hasChildren &&
7750
+ isExpanded &&
7751
+ m('ul.tree-children', {
7752
+ role: 'group',
7753
+ 'aria-expanded': 'true',
7754
+ }, node.children.map((child, childIndex) => {
7755
+ var _a, _b, _c, _d, _e, _f;
7756
+ // Calculate state for each child using treeState
7757
+ const childIsSelected = (_b = (_a = attrs.treeState) === null || _a === void 0 ? void 0 : _a.selectedIds.has(child.id)) !== null && _b !== void 0 ? _b : false;
7758
+ const childIsExpanded = (_d = (_c = attrs.treeState) === null || _c === void 0 ? void 0 : _c.expandedIds.has(child.id)) !== null && _d !== void 0 ? _d : false;
7759
+ const childIsFocused = ((_e = attrs.treeState) === null || _e === void 0 ? void 0 : _e.focusedNodeId) === child.id;
7760
+ // Calculate if this child is last in branch
7761
+ const childPath = [...(attrs.currentPath || []), childIndex];
7762
+ const childIsLastInBranch = ((_f = attrs.treeAttrs) === null || _f === void 0 ? void 0 : _f.data) ?
7763
+ isNodeLastInBranch(childPath, attrs.treeAttrs.data) : false;
7764
+ return m(TreeNodeComponent, {
7765
+ key: child.id,
7766
+ node: child,
7767
+ level: level + 1,
7768
+ isSelected: childIsSelected,
7769
+ isExpanded: childIsExpanded,
7770
+ isFocused: childIsFocused,
7771
+ showConnectors,
7772
+ iconType,
7773
+ selectionMode,
7774
+ onToggleExpand,
7775
+ onToggleSelect,
7776
+ onFocus,
7777
+ isLastInBranch: childIsLastInBranch,
7778
+ currentPath: childPath,
7779
+ treeState: attrs.treeState,
7780
+ treeAttrs: attrs.treeAttrs,
7781
+ });
7782
+ })),
7783
+ ]);
7784
+ },
7785
+ };
7786
+ };
7787
+ const TreeView = () => {
7788
+ const state = {
7789
+ selectedIds: new Set(),
7790
+ expandedIds: new Set(),
7791
+ focusedNodeId: null,
7792
+ treeMap: new Map(),
7793
+ };
7794
+ const buildTreeMap = (nodes, map) => {
7795
+ nodes.forEach((node) => {
7796
+ map.set(node.id, node);
7797
+ if (node.children) {
7798
+ buildTreeMap(node.children, map);
7799
+ }
7800
+ });
7801
+ };
7802
+ const initializeExpandedNodes = (nodes) => {
7803
+ nodes.forEach((node) => {
7804
+ if (node.expanded) {
7805
+ state.expandedIds.add(node.id);
7806
+ }
7807
+ if (node.children) {
7808
+ initializeExpandedNodes(node.children);
7809
+ }
7810
+ });
7811
+ };
7812
+ const handleToggleExpand = (nodeId, attrs) => {
7813
+ var _a;
7814
+ const isExpanded = state.expandedIds.has(nodeId);
7815
+ if (isExpanded) {
7816
+ state.expandedIds.delete(nodeId);
7817
+ }
7818
+ else {
7819
+ state.expandedIds.add(nodeId);
7820
+ }
7821
+ (_a = attrs.onexpand) === null || _a === void 0 ? void 0 : _a.call(attrs, { nodeId, expanded: !isExpanded });
7822
+ };
7823
+ const handleToggleSelect = (nodeId, attrs) => {
7824
+ var _a;
7825
+ const { selectionMode = 'single' } = attrs;
7826
+ if (selectionMode === 'single') {
7827
+ state.selectedIds.clear();
7828
+ state.selectedIds.add(nodeId);
7829
+ }
7830
+ else if (selectionMode === 'multiple') {
7831
+ if (state.selectedIds.has(nodeId)) {
7832
+ state.selectedIds.delete(nodeId);
7833
+ }
7834
+ else {
7835
+ state.selectedIds.add(nodeId);
7836
+ }
7837
+ }
7838
+ (_a = attrs.onselection) === null || _a === void 0 ? void 0 : _a.call(attrs, Array.from(state.selectedIds));
7839
+ };
7840
+ const handleFocus = (nodeId) => {
7841
+ state.focusedNodeId = nodeId;
7842
+ };
7843
+ const renderNodes = (nodes, attrs, level = 0, parentPath = []) => {
7844
+ return nodes.map((node, index) => {
7845
+ var _a, _b, _c;
7846
+ const isSelected = state.selectedIds.has(node.id);
7847
+ const isExpanded = state.expandedIds.has(node.id);
7848
+ const isFocused = state.focusedNodeId === node.id;
7849
+ const currentPath = [...parentPath, index];
7850
+ const isLastInBranch = isNodeLastInBranch(currentPath, attrs.data);
7851
+ return m(TreeNodeComponent, {
7852
+ key: node.id,
7853
+ node,
7854
+ level,
7855
+ isSelected,
7856
+ isExpanded,
7857
+ isFocused,
7858
+ showConnectors: (_a = attrs.showConnectors) !== null && _a !== void 0 ? _a : true,
7859
+ iconType: (_b = attrs.iconType) !== null && _b !== void 0 ? _b : 'caret',
7860
+ selectionMode: (_c = attrs.selectionMode) !== null && _c !== void 0 ? _c : 'single',
7861
+ onToggleExpand: (nodeId) => handleToggleExpand(nodeId, attrs),
7862
+ onToggleSelect: (nodeId) => handleToggleSelect(nodeId, attrs),
7863
+ onFocus: handleFocus,
7864
+ isLastInBranch,
7865
+ currentPath,
7866
+ // Pass state and attrs for recursive rendering
7867
+ treeState: state,
7868
+ treeAttrs: attrs,
7869
+ });
7870
+ });
7871
+ };
7872
+ return {
7873
+ oninit: ({ attrs }) => {
7874
+ // Build internal tree map for efficient lookups
7875
+ buildTreeMap(attrs.data, state.treeMap);
7876
+ // Initialize expanded nodes from data
7877
+ initializeExpandedNodes(attrs.data);
7878
+ // Initialize selected nodes from props
7879
+ if (attrs.selectedIds) {
7880
+ state.selectedIds = new Set(attrs.selectedIds);
7881
+ }
7882
+ },
7883
+ onupdate: ({ attrs }) => {
7884
+ // Sync selectedIds prop with internal state
7885
+ if (attrs.selectedIds) {
7886
+ const newSelection = new Set(attrs.selectedIds);
7887
+ if (newSelection.size !== state.selectedIds.size ||
7888
+ !Array.from(newSelection).every((id) => state.selectedIds.has(id))) {
7889
+ state.selectedIds = newSelection;
7890
+ }
7891
+ }
7892
+ },
7893
+ view: ({ attrs }) => {
7894
+ const { data, className, style, id, selectionMode = 'single', showConnectors = true } = attrs;
7895
+ return m('div.tree-view', {
7896
+ class: [
7897
+ className,
7898
+ showConnectors && 'show-connectors'
7899
+ ].filter(Boolean).join(' ') || undefined,
7900
+ style,
7901
+ id,
7902
+ role: selectionMode === 'multiple' ? 'listbox' : 'tree',
7903
+ 'aria-multiselectable': selectionMode === 'multiple' ? 'true' : 'false',
7904
+ }, [
7905
+ m('ul.tree-root', {
7906
+ role: 'group',
7907
+ }, renderNodes(data, attrs)),
7908
+ ]);
7909
+ },
7910
+ };
7911
+ };
7912
+
6799
7913
  /**
6800
7914
  * @fileoverview Core TypeScript utility types for mithril-materialized library
6801
7915
  * These types improve type safety and developer experience across all components
@@ -6817,4 +7931,4 @@ const isValidationError = (result) => !isValidationSuccess(result);
6817
7931
  // ============================================================================
6818
7932
  // All types are already exported via individual export declarations above
6819
7933
 
6820
- export { AnchorItem, Autocomplete, Breadcrumb, BreadcrumbManager, Button, ButtonFactory, Carousel, CharacterCounter, Chips, CodeBlock, Collapsible, CollapsibleItem, Collection, CollectionMode, ColorInput, DataTable, DatePicker, Dropdown, EmailInput, FileInput, FileUpload, FlatButton, FloatingActionButton, HelperText, Icon, InputCheckbox, Label, LargeButton, ListItem, Mandatory, MaterialBox, ModalPanel, NumberInput, Options, Pagination, PaginationControls, Parallax, PasswordInput, Pushpin, PushpinComponent, RadioButton, RadioButtons, RangeInput, RoundIconButton, SearchSelect, SecondaryContent, Select, Sidenav, SidenavItem, SidenavManager, SmallButton, Stepper, SubmitButton, Switch, Tabs, TextArea, TextInput, ThemeManager, ThemeSwitcher, ThemeToggle, TimePicker, Toast, ToastComponent, Tooltip, TooltipComponent, UrlInput, Wizard, createBreadcrumb, getDropdownStyles, initPushpins, initTooltips, isNumeric, isValidationError, isValidationSuccess, padLeft, range, toast, uniqueId, uuid4 };
7934
+ export { AnchorItem, Autocomplete, Breadcrumb, BreadcrumbManager, Button, ButtonFactory, Carousel, CharacterCounter, Chips, CodeBlock, Collapsible, CollapsibleItem, Collection, CollectionMode, ColorInput, DataTable, DatePicker, Dropdown, EmailInput, FileInput, FileUpload, FlatButton, FloatingActionButton, HelperText, Icon, InputCheckbox, Label, LargeButton, ListItem, Mandatory, MaterialBox, MaterialIcon, ModalPanel, NumberInput, Options, Pagination, PaginationControls, Parallax, PasswordInput, Pushpin, PushpinComponent, RadioButton, RadioButtons, RangeInput, RoundIconButton, SearchSelect, SecondaryContent, Select, Sidenav, SidenavItem, SidenavManager, SmallButton, Stepper, SubmitButton, Switch, Tabs, TextArea, TextInput, ThemeManager, ThemeSwitcher, ThemeToggle, TimePicker, Toast, ToastComponent, Tooltip, TooltipComponent, TreeView, UrlInput, Wizard, createBreadcrumb, getDropdownStyles, initPushpins, initTooltips, isNumeric, isValidationError, isValidationSuccess, padLeft, range, renderMinMaxRange, renderSingleRangeWithTooltip, toast, uniqueId, uuid4 };