mithril-materialized 2.0.0-beta.12 → 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
@@ -2190,6 +2190,12 @@ const InputField = (type, defaultClass = '') => () => {
2190
2190
  isValid: true,
2191
2191
  active: false,
2192
2192
  inputElement: null,
2193
+ // Range-specific state
2194
+ rangeMinValue: undefined,
2195
+ rangeMaxValue: undefined,
2196
+ singleValue: undefined,
2197
+ isDragging: false,
2198
+ activeThumb: null,
2193
2199
  };
2194
2200
  // let labelManager: { updateLabelState: () => void; cleanup: () => void } | null = null;
2195
2201
  // let lengthUpdateHandler: (() => void) | null = null;
@@ -2215,6 +2221,397 @@ const InputField = (type, defaultClass = '') => () => {
2215
2221
  state.hasInteracted = length > 0;
2216
2222
  }
2217
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
+ };
2218
2615
  return {
2219
2616
  view: ({ attrs }) => {
2220
2617
  var _a;
@@ -2224,10 +2621,25 @@ const InputField = (type, defaultClass = '') => () => {
2224
2621
  const isActive = state.active || ((_a = state.inputElement) === null || _a === void 0 ? void 0 : _a.value) || placeholder || type === 'color' || type === 'range'
2225
2622
  ? true
2226
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
+ }
2227
2632
  return m('.input-field', { className: cn, style }, [
2228
2633
  iconName ? m('i.material-icons.prefix', iconName) : undefined,
2229
2634
  m('input.validate', Object.assign(Object.assign({}, params), { type, tabindex: 0, id,
2230
- 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,
2231
2643
  // attributes,
2232
2644
  oncreate: ({ dom }) => {
2233
2645
  const input = (state.inputElement = dom);
@@ -2243,7 +2655,7 @@ const InputField = (type, defaultClass = '') => () => {
2243
2655
  state.currentLength = input.value.length; // Initial count
2244
2656
  }
2245
2657
  // Range input functionality
2246
- if (type === 'range') {
2658
+ if (type === 'range' && !attrs.minmax) {
2247
2659
  const updateThumb = () => {
2248
2660
  const value = input.value;
2249
2661
  const min = input.min || '0';
@@ -3367,6 +3779,423 @@ const FloatingActionButton = () => {
3367
3779
  };
3368
3780
  };
3369
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
+
3370
4199
  /**
3371
4200
  * Pure TypeScript MaterialBox - creates an image lightbox that fills the screen when clicked
3372
4201
  * No MaterializeCSS dependencies
@@ -7102,4 +7931,4 @@ const isValidationError = (result) => !isValidationSuccess(result);
7102
7931
  // ============================================================================
7103
7932
  // All types are already exported via individual export declarations above
7104
7933
 
7105
- 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, 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 };