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