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.js CHANGED
@@ -2192,6 +2192,12 @@ const InputField = (type, defaultClass = '') => () => {
2192
2192
  isValid: true,
2193
2193
  active: false,
2194
2194
  inputElement: null,
2195
+ // Range-specific state
2196
+ rangeMinValue: undefined,
2197
+ rangeMaxValue: undefined,
2198
+ singleValue: undefined,
2199
+ isDragging: false,
2200
+ activeThumb: null,
2195
2201
  };
2196
2202
  // let labelManager: { updateLabelState: () => void; cleanup: () => void } | null = null;
2197
2203
  // let lengthUpdateHandler: (() => void) | null = null;
@@ -2217,6 +2223,397 @@ const InputField = (type, defaultClass = '') => () => {
2217
2223
  state.hasInteracted = length > 0;
2218
2224
  }
2219
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
+ };
2220
2617
  return {
2221
2618
  view: ({ attrs }) => {
2222
2619
  var _a;
@@ -2226,10 +2623,25 @@ const InputField = (type, defaultClass = '') => () => {
2226
2623
  const isActive = state.active || ((_a = state.inputElement) === null || _a === void 0 ? void 0 : _a.value) || placeholder || type === 'color' || type === 'range'
2227
2624
  ? true
2228
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
+ }
2229
2634
  return m('.input-field', { className: cn, style }, [
2230
2635
  iconName ? m('i.material-icons.prefix', iconName) : undefined,
2231
2636
  m('input.validate', Object.assign(Object.assign({}, params), { type, tabindex: 0, id,
2232
- 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,
2233
2645
  // attributes,
2234
2646
  oncreate: ({ dom }) => {
2235
2647
  const input = (state.inputElement = dom);
@@ -2245,7 +2657,7 @@ const InputField = (type, defaultClass = '') => () => {
2245
2657
  state.currentLength = input.value.length; // Initial count
2246
2658
  }
2247
2659
  // Range input functionality
2248
- if (type === 'range') {
2660
+ if (type === 'range' && !attrs.minmax) {
2249
2661
  const updateThumb = () => {
2250
2662
  const value = input.value;
2251
2663
  const min = input.min || '0';
@@ -3369,6 +3781,423 @@ const FloatingActionButton = () => {
3369
3781
  };
3370
3782
  };
3371
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
+
3372
4201
  /**
3373
4202
  * Pure TypeScript MaterialBox - creates an image lightbox that fills the screen when clicked
3374
4203
  * No MaterializeCSS dependencies
@@ -7181,6 +8010,8 @@ exports.isValidationError = isValidationError;
7181
8010
  exports.isValidationSuccess = isValidationSuccess;
7182
8011
  exports.padLeft = padLeft;
7183
8012
  exports.range = range;
8013
+ exports.renderMinMaxRange = renderMinMaxRange;
8014
+ exports.renderSingleRangeWithTooltip = renderSingleRangeWithTooltip;
7184
8015
  exports.toast = toast;
7185
8016
  exports.uniqueId = uniqueId;
7186
8017
  exports.uuid4 = uuid4;