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.umd.js CHANGED
@@ -2194,6 +2194,12 @@
2194
2194
  isValid: true,
2195
2195
  active: false,
2196
2196
  inputElement: null,
2197
+ // Range-specific state
2198
+ rangeMinValue: undefined,
2199
+ rangeMaxValue: undefined,
2200
+ singleValue: undefined,
2201
+ isDragging: false,
2202
+ activeThumb: null,
2197
2203
  };
2198
2204
  // let labelManager: { updateLabelState: () => void; cleanup: () => void } | null = null;
2199
2205
  // let lengthUpdateHandler: (() => void) | null = null;
@@ -2219,6 +2225,397 @@
2219
2225
  state.hasInteracted = length > 0;
2220
2226
  }
2221
2227
  };
2228
+ // Range slider helper functions
2229
+ const getPercentage = (value, min, max) => {
2230
+ return ((value - min) / (max - min)) * 100;
2231
+ };
2232
+ const updateRangeValues = (minValue, maxValue, attrs, immediate = false) => {
2233
+ // Ensure min doesn't exceed max and vice versa
2234
+ if (minValue > maxValue)
2235
+ minValue = maxValue;
2236
+ if (maxValue < minValue)
2237
+ maxValue = minValue;
2238
+ state.rangeMinValue = minValue;
2239
+ state.rangeMaxValue = maxValue;
2240
+ // Call oninput for immediate feedback or onchange for final changes
2241
+ if (immediate && attrs.oninput) {
2242
+ attrs.oninput(minValue, maxValue);
2243
+ }
2244
+ else if (!immediate && attrs.onchange) {
2245
+ attrs.onchange(minValue, maxValue);
2246
+ }
2247
+ };
2248
+ // Render function for single range slider with tooltip
2249
+ const renderSingleRangeWithTooltip = (attrs, state, cn, style, iconName, id, label, isMandatory, helperText) => {
2250
+ const { min = 0, max = 100, step = 1, initialValue, vertical = false, showValue = false, height = '200px', disabled = false, oninput, onchange, } = attrs;
2251
+ // Initialize single range value
2252
+ const currentValue = initialValue !== undefined ? initialValue : state.singleValue || min;
2253
+ if (state.singleValue === undefined) {
2254
+ state.singleValue = currentValue;
2255
+ }
2256
+ const percentage = getPercentage(state.singleValue, min, max);
2257
+ // Only keep dynamic styles as inline, use CSS classes for static styles
2258
+ const containerStyle = vertical ? { height } : {};
2259
+ const progressStyle = vertical
2260
+ ? {
2261
+ height: `${percentage}%`,
2262
+ }
2263
+ : {
2264
+ width: `${percentage}%`,
2265
+ };
2266
+ const thumbStyle = vertical
2267
+ ? {
2268
+ bottom: `${percentage}%`,
2269
+ }
2270
+ : {
2271
+ left: `${percentage}%`,
2272
+ marginLeft: '-10px', // Half of thumb size (20px)
2273
+ };
2274
+ const updateSingleValue = (newValue, immediate = false) => {
2275
+ state.singleValue = newValue;
2276
+ if (immediate && oninput) {
2277
+ oninput(newValue);
2278
+ }
2279
+ else if (!immediate && onchange) {
2280
+ onchange(newValue);
2281
+ }
2282
+ };
2283
+ const handleMouseDown = (e) => {
2284
+ if (disabled)
2285
+ return;
2286
+ e.preventDefault();
2287
+ state.isDragging = true;
2288
+ // Get container reference from the current target's parent
2289
+ const thumbElement = e.currentTarget;
2290
+ const container = thumbElement.parentElement;
2291
+ if (!container)
2292
+ return;
2293
+ const handleMouseMove = (e) => {
2294
+ if (!state.isDragging || !container)
2295
+ return;
2296
+ const rect = container.getBoundingClientRect();
2297
+ let percentage;
2298
+ if (vertical) {
2299
+ percentage = ((rect.bottom - e.clientY) / rect.height) * 100;
2300
+ }
2301
+ else {
2302
+ percentage = ((e.clientX - rect.left) / rect.width) * 100;
2303
+ }
2304
+ percentage = Math.max(0, Math.min(100, percentage));
2305
+ const value = min + (percentage / 100) * (max - min);
2306
+ const steppedValue = Math.round(value / step) * step;
2307
+ updateSingleValue(steppedValue, true);
2308
+ // Redraw to update the UI
2309
+ m.redraw();
2310
+ };
2311
+ const handleMouseUp = () => {
2312
+ state.isDragging = false;
2313
+ document.removeEventListener('mousemove', handleMouseMove);
2314
+ document.removeEventListener('mouseup', handleMouseUp);
2315
+ // Fire onchange when dragging ends
2316
+ updateSingleValue(state.singleValue, false);
2317
+ };
2318
+ document.addEventListener('mousemove', handleMouseMove);
2319
+ document.addEventListener('mouseup', handleMouseUp);
2320
+ };
2321
+ const fieldClass = vertical ? 'range-field vertical' : 'range-field';
2322
+ const orientation = vertical ? 'vertical' : 'horizontal';
2323
+ return m('.input-field', { className: cn, style }, [
2324
+ iconName ? m('i.material-icons.prefix', iconName) : undefined,
2325
+ // Hidden input for label association and accessibility
2326
+ m('input[type=range]', {
2327
+ id,
2328
+ value: state.singleValue,
2329
+ min,
2330
+ max,
2331
+ step,
2332
+ style: { display: 'none' },
2333
+ disabled,
2334
+ tabindex: -1,
2335
+ }),
2336
+ m('div', { class: fieldClass, style: containerStyle }, [
2337
+ m(`.single-range-slider.${orientation}`, {
2338
+ tabindex: disabled ? -1 : 0,
2339
+ role: 'slider',
2340
+ 'aria-valuemin': min,
2341
+ 'aria-valuemax': max,
2342
+ 'aria-valuenow': state.singleValue,
2343
+ 'aria-label': label || 'Range slider',
2344
+ onclick: (e) => {
2345
+ // Focus the slider when clicked
2346
+ e.currentTarget.focus();
2347
+ },
2348
+ onkeydown: (e) => {
2349
+ if (disabled)
2350
+ return;
2351
+ let newValue = state.singleValue;
2352
+ switch (e.key) {
2353
+ case 'ArrowLeft':
2354
+ case 'ArrowDown':
2355
+ e.preventDefault();
2356
+ newValue = Math.max(min, newValue - step);
2357
+ updateSingleValue(newValue, false);
2358
+ m.redraw();
2359
+ break;
2360
+ case 'ArrowRight':
2361
+ case 'ArrowUp':
2362
+ e.preventDefault();
2363
+ newValue = Math.min(max, newValue + step);
2364
+ updateSingleValue(newValue, false);
2365
+ m.redraw();
2366
+ break;
2367
+ case 'Home':
2368
+ e.preventDefault();
2369
+ updateSingleValue(min, false);
2370
+ m.redraw();
2371
+ break;
2372
+ case 'End':
2373
+ e.preventDefault();
2374
+ updateSingleValue(max, false);
2375
+ m.redraw();
2376
+ break;
2377
+ }
2378
+ },
2379
+ }, [
2380
+ // Track
2381
+ m(`.track.${orientation}`),
2382
+ // Progress
2383
+ m(`.range-progress.${orientation}`, { style: progressStyle }),
2384
+ // Thumb
2385
+ m(`.thumb.${orientation}`, {
2386
+ style: thumbStyle,
2387
+ onmousedown: handleMouseDown,
2388
+ }, showValue
2389
+ ? m(`.value-tooltip.${vertical ? 'right' : 'top'}`, state.singleValue.toFixed(0))
2390
+ : null),
2391
+ ]),
2392
+ ]),
2393
+ label
2394
+ ? m(Label, {
2395
+ label,
2396
+ id,
2397
+ isMandatory,
2398
+ isActive: true, // Range sliders always have active labels
2399
+ })
2400
+ : null,
2401
+ helperText ? m(HelperText, { helperText }) : null,
2402
+ ]);
2403
+ };
2404
+ // Render function for minmax range slider
2405
+ const renderMinMaxRange = (attrs, state, cn, style, iconName, id, label, isMandatory, helperText) => {
2406
+ const { min = 0, max = 100, step = 1, minValue, maxValue, vertical = false, showValue = false, height = '200px', disabled = false, } = attrs;
2407
+ // Initialize range values
2408
+ const currentMinValue = minValue !== undefined ? minValue : attrs.minValue || min;
2409
+ const currentMaxValue = maxValue !== undefined ? maxValue : attrs.maxValue || max;
2410
+ if (state.rangeMinValue === undefined || state.rangeMaxValue === undefined) {
2411
+ state.rangeMinValue = currentMinValue;
2412
+ state.rangeMaxValue = currentMaxValue;
2413
+ }
2414
+ // Initialize active thumb if not set
2415
+ if (state.activeThumb === null) {
2416
+ state.activeThumb = 'min';
2417
+ }
2418
+ const minPercentage = getPercentage(state.rangeMinValue, min, max);
2419
+ const maxPercentage = getPercentage(state.rangeMaxValue, min, max);
2420
+ // Only keep dynamic styles as inline, use CSS classes for static styles
2421
+ const containerStyle = vertical ? { height } : {};
2422
+ const rangeStyle = vertical
2423
+ ? {
2424
+ bottom: `${minPercentage}%`,
2425
+ height: `${maxPercentage - minPercentage}%`,
2426
+ }
2427
+ : {
2428
+ left: `${minPercentage}%`,
2429
+ width: `${maxPercentage - minPercentage}%`,
2430
+ };
2431
+ // Only keep dynamic positioning and z-index as inline styles
2432
+ const createThumbStyle = (percentage, isActive) => vertical
2433
+ ? {
2434
+ bottom: `${percentage}%`,
2435
+ zIndex: isActive ? 10 : 5,
2436
+ }
2437
+ : {
2438
+ left: `${percentage}%`,
2439
+ marginLeft: '-10px', // Half of thumb size (20px)
2440
+ zIndex: isActive ? 10 : 5,
2441
+ };
2442
+ const handleMouseDown = (thumb) => (e) => {
2443
+ if (disabled)
2444
+ return;
2445
+ e.preventDefault();
2446
+ state.isDragging = true;
2447
+ state.activeThumb = thumb;
2448
+ // Get container reference from the current target's parent
2449
+ const thumbElement = e.currentTarget;
2450
+ const container = thumbElement.parentElement;
2451
+ if (!container)
2452
+ return;
2453
+ const handleMouseMove = (e) => {
2454
+ if (!state.isDragging || !container)
2455
+ return;
2456
+ const rect = container.getBoundingClientRect();
2457
+ let percentage;
2458
+ if (vertical) {
2459
+ percentage = ((rect.bottom - e.clientY) / rect.height) * 100;
2460
+ }
2461
+ else {
2462
+ percentage = ((e.clientX - rect.left) / rect.width) * 100;
2463
+ }
2464
+ percentage = Math.max(0, Math.min(100, percentage));
2465
+ const value = min + (percentage / 100) * (max - min);
2466
+ const steppedValue = Math.round(value / step) * step;
2467
+ if (thumb === 'min') {
2468
+ updateRangeValues(Math.min(steppedValue, state.rangeMaxValue), state.rangeMaxValue, attrs, true);
2469
+ }
2470
+ else {
2471
+ updateRangeValues(state.rangeMinValue, Math.max(steppedValue, state.rangeMinValue), attrs, true);
2472
+ }
2473
+ // Redraw to update the UI
2474
+ m.redraw();
2475
+ };
2476
+ const handleMouseUp = () => {
2477
+ state.isDragging = false;
2478
+ state.activeThumb = null;
2479
+ document.removeEventListener('mousemove', handleMouseMove);
2480
+ document.removeEventListener('mouseup', handleMouseUp);
2481
+ // Fire onchange when dragging ends
2482
+ updateRangeValues(state.rangeMinValue, state.rangeMaxValue, attrs, false);
2483
+ };
2484
+ document.addEventListener('mousemove', handleMouseMove);
2485
+ document.addEventListener('mouseup', handleMouseUp);
2486
+ };
2487
+ const fieldClass = vertical ? 'range-field vertical' : 'range-field';
2488
+ const orientation = vertical ? 'vertical' : 'horizontal';
2489
+ return m('.input-field', { className: cn, style }, [
2490
+ iconName ? m('i.material-icons.prefix', iconName) : undefined,
2491
+ // Hidden inputs for label association and accessibility
2492
+ m('input[type=range]', {
2493
+ id,
2494
+ value: state.rangeMinValue,
2495
+ min,
2496
+ max,
2497
+ step,
2498
+ style: { display: 'none' },
2499
+ disabled,
2500
+ tabindex: -1,
2501
+ }),
2502
+ m('input[type=range]', {
2503
+ id: `${id}_max`,
2504
+ value: state.rangeMaxValue,
2505
+ min,
2506
+ max,
2507
+ step,
2508
+ style: { display: 'none' },
2509
+ disabled,
2510
+ tabindex: -1,
2511
+ }),
2512
+ m(`.${fieldClass}`, [
2513
+ m(`.double-range-slider.${orientation}`, {
2514
+ style: containerStyle,
2515
+ tabindex: disabled ? -1 : 0,
2516
+ role: 'slider',
2517
+ 'aria-valuemin': min,
2518
+ 'aria-valuemax': max,
2519
+ 'aria-valuenow': state.rangeMinValue,
2520
+ 'aria-valuetext': `${state.rangeMinValue} to ${state.rangeMaxValue}`,
2521
+ 'aria-label': label || 'Range slider',
2522
+ onclick: (e) => {
2523
+ // Focus the slider when clicked
2524
+ e.currentTarget.focus();
2525
+ },
2526
+ onkeydown: (e) => {
2527
+ if (disabled)
2528
+ return;
2529
+ let newMinValue = state.rangeMinValue;
2530
+ let newMaxValue = state.rangeMaxValue;
2531
+ const activeThumb = state.activeThumb || 'min';
2532
+ switch (e.key) {
2533
+ case 'ArrowLeft':
2534
+ case 'ArrowDown':
2535
+ e.preventDefault();
2536
+ if (activeThumb === 'min') {
2537
+ newMinValue = Math.max(min, newMinValue - step);
2538
+ updateRangeValues(newMinValue, newMaxValue, attrs, false);
2539
+ }
2540
+ else {
2541
+ newMaxValue = Math.max(newMinValue, newMaxValue - step);
2542
+ updateRangeValues(newMinValue, newMaxValue, attrs, false);
2543
+ }
2544
+ m.redraw();
2545
+ break;
2546
+ case 'ArrowRight':
2547
+ case 'ArrowUp':
2548
+ e.preventDefault();
2549
+ if (activeThumb === 'min') {
2550
+ newMinValue = Math.min(newMaxValue, newMinValue + step);
2551
+ updateRangeValues(newMinValue, newMaxValue, attrs, false);
2552
+ }
2553
+ else {
2554
+ newMaxValue = Math.min(max, newMaxValue + step);
2555
+ updateRangeValues(newMinValue, newMaxValue, attrs, false);
2556
+ }
2557
+ m.redraw();
2558
+ break;
2559
+ // Remove Tab case - let normal tab navigation work
2560
+ // Users can click on specific thumbs to activate them
2561
+ case 'Home':
2562
+ e.preventDefault();
2563
+ if (activeThumb === 'min') {
2564
+ updateRangeValues(min, newMaxValue, attrs, false);
2565
+ }
2566
+ else {
2567
+ updateRangeValues(newMinValue, newMinValue, attrs, false);
2568
+ }
2569
+ m.redraw();
2570
+ break;
2571
+ case 'End':
2572
+ e.preventDefault();
2573
+ if (activeThumb === 'min') {
2574
+ updateRangeValues(newMaxValue, newMaxValue, attrs, false);
2575
+ }
2576
+ else {
2577
+ updateRangeValues(newMinValue, max, attrs, false);
2578
+ }
2579
+ m.redraw();
2580
+ break;
2581
+ }
2582
+ },
2583
+ }, [
2584
+ // Track
2585
+ m(`.track.${orientation}`),
2586
+ // Range
2587
+ m(`.range.${orientation}`, { style: rangeStyle }),
2588
+ // Min thumb
2589
+ m(`.thumb.${orientation}.min-thumb${state.activeThumb === 'min' ? '.active' : ''}`, {
2590
+ style: createThumbStyle(minPercentage, state.activeThumb === 'min'),
2591
+ onmousedown: handleMouseDown('min'),
2592
+ onclick: () => {
2593
+ state.activeThumb = 'min';
2594
+ m.redraw();
2595
+ },
2596
+ }, showValue ? m(`.value.${orientation}`, state.rangeMinValue.toFixed(0)) : null),
2597
+ // Max thumb
2598
+ m(`.thumb.${orientation}.max-thumb${state.activeThumb === 'max' ? '.active' : ''}`, {
2599
+ style: createThumbStyle(maxPercentage, state.activeThumb === 'max'),
2600
+ onmousedown: handleMouseDown('max'),
2601
+ onclick: () => {
2602
+ state.activeThumb = 'max';
2603
+ m.redraw();
2604
+ },
2605
+ }, showValue ? m(`.value.${orientation}`, state.rangeMaxValue.toFixed(0)) : null),
2606
+ ]),
2607
+ ]),
2608
+ label
2609
+ ? m(Label, {
2610
+ label,
2611
+ id,
2612
+ isMandatory,
2613
+ isActive: true, // Range sliders always have active labels
2614
+ })
2615
+ : null,
2616
+ helperText ? m(HelperText, { helperText }) : null,
2617
+ ]);
2618
+ };
2222
2619
  return {
2223
2620
  view: ({ attrs }) => {
2224
2621
  var _a;
@@ -2228,10 +2625,25 @@
2228
2625
  const isActive = state.active || ((_a = state.inputElement) === null || _a === void 0 ? void 0 : _a.value) || placeholder || type === 'color' || type === 'range'
2229
2626
  ? true
2230
2627
  : false;
2628
+ // Special rendering for minmax range sliders
2629
+ if (type === 'range' && attrs.minmax) {
2630
+ return renderMinMaxRange(attrs, state, cn, style, iconName, id, label, isMandatory, helperText);
2631
+ }
2632
+ // Special rendering for single range sliders with tooltips
2633
+ if (type === 'range' && attrs.showValue) {
2634
+ return renderSingleRangeWithTooltip(attrs, state, cn, style, iconName, id, label, isMandatory, helperText);
2635
+ }
2231
2636
  return m('.input-field', { className: cn, style }, [
2232
2637
  iconName ? m('i.material-icons.prefix', iconName) : undefined,
2233
2638
  m('input.validate', Object.assign(Object.assign({}, params), { type, tabindex: 0, id,
2234
- placeholder,
2639
+ placeholder, class: type === 'range' && attrs.vertical ? 'range-slider vertical' : undefined, style: type === 'range' && attrs.vertical
2640
+ ? {
2641
+ height: attrs.height || '200px',
2642
+ width: '6px',
2643
+ writingMode: 'vertical-lr',
2644
+ direction: 'rtl',
2645
+ }
2646
+ : undefined,
2235
2647
  // attributes,
2236
2648
  oncreate: ({ dom }) => {
2237
2649
  const input = (state.inputElement = dom);
@@ -2247,7 +2659,7 @@
2247
2659
  state.currentLength = input.value.length; // Initial count
2248
2660
  }
2249
2661
  // Range input functionality
2250
- if (type === 'range') {
2662
+ if (type === 'range' && !attrs.minmax) {
2251
2663
  const updateThumb = () => {
2252
2664
  const value = input.value;
2253
2665
  const min = input.min || '0';
@@ -3371,6 +3783,423 @@
3371
3783
  };
3372
3784
  };
3373
3785
 
3786
+ // Utility functions
3787
+ const getPercentage = (value, min, max) => {
3788
+ return ((value - min) / (max - min)) * 100;
3789
+ };
3790
+ const updateRangeValues = (minValue, maxValue, attrs, state, immediate) => {
3791
+ // Ensure min doesn't exceed max and vice versa
3792
+ if (minValue > maxValue)
3793
+ minValue = maxValue;
3794
+ if (maxValue < minValue)
3795
+ maxValue = minValue;
3796
+ state.rangeMinValue = minValue;
3797
+ state.rangeMaxValue = maxValue;
3798
+ // Call oninput for immediate feedback or onchange for final changes
3799
+ if (immediate && attrs.oninput) {
3800
+ attrs.oninput(minValue, maxValue);
3801
+ }
3802
+ else if (!immediate && attrs.onchange) {
3803
+ attrs.onchange(minValue, maxValue);
3804
+ }
3805
+ };
3806
+ // Single Range Slider with Tooltip
3807
+ const renderSingleRangeWithTooltip = (attrs, state, cn, style, iconName, id, label, isMandatory, helperText) => {
3808
+ const { min = 0, max = 100, step = 1, initialValue, vertical = false, showValue = false, height = '200px', disabled = false, tooltipPos = 'top', oninput, onchange, } = attrs;
3809
+ // Initialize single range value
3810
+ const currentValue = initialValue !== undefined ? initialValue : state.singleValue || min;
3811
+ if (state.singleValue === undefined) {
3812
+ state.singleValue = currentValue;
3813
+ }
3814
+ const percentage = getPercentage(state.singleValue, min, max);
3815
+ // Only keep dynamic styles as inline, use CSS classes for static styles
3816
+ const containerStyle = vertical ? { height } : {};
3817
+ const progressStyle = vertical
3818
+ ? {
3819
+ height: `${percentage}%`,
3820
+ }
3821
+ : {
3822
+ width: `${percentage}%`,
3823
+ };
3824
+ const thumbStyle = vertical
3825
+ ? {
3826
+ bottom: `${percentage}%`,
3827
+ }
3828
+ : {
3829
+ left: `${percentage}%`,
3830
+ marginLeft: '-10px', // Half of thumb size (20px)
3831
+ };
3832
+ const updateSingleValue = (newValue, immediate = false) => {
3833
+ state.singleValue = newValue;
3834
+ if (immediate && oninput) {
3835
+ oninput(newValue);
3836
+ }
3837
+ else if (!immediate && onchange) {
3838
+ onchange(newValue);
3839
+ }
3840
+ };
3841
+ const handleMouseDown = (e) => {
3842
+ if (disabled)
3843
+ return;
3844
+ e.preventDefault();
3845
+ state.isDragging = true;
3846
+ // Get container reference from the current target's parent
3847
+ const thumbElement = e.currentTarget;
3848
+ const container = thumbElement.parentElement;
3849
+ if (!container)
3850
+ return;
3851
+ const handleMouseMove = (e) => {
3852
+ if (!state.isDragging || !container)
3853
+ return;
3854
+ const rect = container.getBoundingClientRect();
3855
+ let percentage;
3856
+ if (vertical) {
3857
+ percentage = ((rect.bottom - e.clientY) / rect.height) * 100;
3858
+ }
3859
+ else {
3860
+ percentage = ((e.clientX - rect.left) / rect.width) * 100;
3861
+ }
3862
+ percentage = Math.max(0, Math.min(100, percentage));
3863
+ const value = min + (percentage / 100) * (max - min);
3864
+ const steppedValue = Math.round(value / step) * step;
3865
+ updateSingleValue(steppedValue, true);
3866
+ // Redraw to update the UI during drag
3867
+ m.redraw();
3868
+ };
3869
+ const handleMouseUp = () => {
3870
+ state.isDragging = false;
3871
+ document.removeEventListener('mousemove', handleMouseMove);
3872
+ document.removeEventListener('mouseup', handleMouseUp);
3873
+ // Fire onchange when dragging ends
3874
+ updateSingleValue(state.singleValue, false);
3875
+ };
3876
+ document.addEventListener('mousemove', handleMouseMove);
3877
+ document.addEventListener('mouseup', handleMouseUp);
3878
+ };
3879
+ const fieldClass = vertical ? 'range-field vertical' : 'range-field';
3880
+ const orientation = vertical ? 'vertical' : 'horizontal';
3881
+ // Determine tooltip position for vertical sliders
3882
+ const tooltipPosition = vertical ? (tooltipPos === 'top' || tooltipPos === 'bottom' ? 'right' : tooltipPos) : tooltipPos;
3883
+ return m('.input-field', { className: cn, style }, [
3884
+ iconName ? m('i.material-icons.prefix', iconName) : undefined,
3885
+ // Hidden input for label association and accessibility
3886
+ m('input[type=range]', {
3887
+ id,
3888
+ value: state.singleValue,
3889
+ min,
3890
+ max,
3891
+ step,
3892
+ style: { display: 'none' },
3893
+ disabled,
3894
+ tabindex: -1,
3895
+ }),
3896
+ m('div', { class: fieldClass, style: containerStyle }, [
3897
+ m(`.single-range-slider.${orientation}`, {
3898
+ tabindex: disabled ? -1 : 0,
3899
+ role: 'slider',
3900
+ 'aria-valuemin': min,
3901
+ 'aria-valuemax': max,
3902
+ 'aria-valuenow': state.singleValue,
3903
+ 'aria-label': label || 'Range slider',
3904
+ onclick: (e) => {
3905
+ // Focus the slider when clicked
3906
+ e.currentTarget.focus();
3907
+ },
3908
+ onkeydown: (e) => {
3909
+ if (disabled)
3910
+ return;
3911
+ let newValue = state.singleValue;
3912
+ switch (e.key) {
3913
+ case 'ArrowLeft':
3914
+ case 'ArrowDown':
3915
+ e.preventDefault();
3916
+ newValue = Math.max(min, newValue - step);
3917
+ updateSingleValue(newValue, false);
3918
+ // m.redraw();
3919
+ break;
3920
+ case 'ArrowRight':
3921
+ case 'ArrowUp':
3922
+ e.preventDefault();
3923
+ newValue = Math.min(max, newValue + step);
3924
+ updateSingleValue(newValue, false);
3925
+ // m.redraw();
3926
+ break;
3927
+ case 'Home':
3928
+ e.preventDefault();
3929
+ updateSingleValue(min, false);
3930
+ // m.redraw();
3931
+ break;
3932
+ case 'End':
3933
+ e.preventDefault();
3934
+ updateSingleValue(max, false);
3935
+ // m.redraw();
3936
+ break;
3937
+ }
3938
+ },
3939
+ }, [
3940
+ // Track
3941
+ m(`.track.${orientation}`),
3942
+ // Progress
3943
+ m(`.range-progress.${orientation}`, { style: progressStyle }),
3944
+ // Thumb
3945
+ m(`.thumb.${orientation}`, {
3946
+ style: thumbStyle,
3947
+ onmousedown: handleMouseDown,
3948
+ }, showValue
3949
+ ? m(`.value-tooltip.${tooltipPosition}`, state.singleValue.toFixed(0))
3950
+ : null),
3951
+ ]),
3952
+ ]),
3953
+ label
3954
+ ? m(Label, {
3955
+ label,
3956
+ id,
3957
+ isMandatory,
3958
+ isActive: true, // Range sliders always have active labels
3959
+ })
3960
+ : null,
3961
+ helperText ? m(HelperText, { helperText }) : null,
3962
+ ]);
3963
+ };
3964
+ // Double Range Slider (Min/Max)
3965
+ const renderMinMaxRange = (attrs, state, cn, style, iconName, id, label, isMandatory, helperText) => {
3966
+ const { min = 0, max = 100, step = 1, minValue, maxValue, vertical = false, showValue = false, height = '200px', disabled = false, } = attrs;
3967
+ // Initialize range values
3968
+ const currentMinValue = minValue !== undefined ? minValue : attrs.minValue || min;
3969
+ const currentMaxValue = maxValue !== undefined ? maxValue : attrs.maxValue || max;
3970
+ if (state.rangeMinValue === undefined || state.rangeMaxValue === undefined) {
3971
+ state.rangeMinValue = currentMinValue;
3972
+ state.rangeMaxValue = currentMaxValue;
3973
+ }
3974
+ // Initialize active thumb if not set
3975
+ if (state.activeThumb === null) {
3976
+ state.activeThumb = 'min';
3977
+ }
3978
+ const minPercentage = getPercentage(state.rangeMinValue, min, max);
3979
+ const maxPercentage = getPercentage(state.rangeMaxValue, min, max);
3980
+ // Only keep dynamic styles as inline, use CSS classes for static styles
3981
+ const containerStyle = vertical ? { height } : {};
3982
+ const rangeStyle = vertical
3983
+ ? {
3984
+ bottom: `${minPercentage}%`,
3985
+ height: `${maxPercentage - minPercentage}%`,
3986
+ }
3987
+ : {
3988
+ left: `${minPercentage}%`,
3989
+ width: `${maxPercentage - minPercentage}%`,
3990
+ };
3991
+ // Only keep dynamic positioning and z-index as inline styles
3992
+ const createThumbStyle = (percentage, isActive) => vertical
3993
+ ? {
3994
+ bottom: `${percentage}%`,
3995
+ zIndex: isActive ? 10 : 5,
3996
+ }
3997
+ : {
3998
+ left: `${percentage}%`,
3999
+ marginLeft: '-10px', // Half of thumb size (20px)
4000
+ zIndex: isActive ? 10 : 5,
4001
+ };
4002
+ const handleMouseDown = (thumb) => (e) => {
4003
+ if (disabled)
4004
+ return;
4005
+ e.preventDefault();
4006
+ state.isDragging = true;
4007
+ state.activeThumb = thumb;
4008
+ // Get container reference from the current target's parent
4009
+ const thumbElement = e.currentTarget;
4010
+ const container = thumbElement.parentElement;
4011
+ if (!container)
4012
+ return;
4013
+ const handleMouseMove = (e) => {
4014
+ if (!state.isDragging || !container)
4015
+ return;
4016
+ const rect = container.getBoundingClientRect();
4017
+ let percentage;
4018
+ if (vertical) {
4019
+ percentage = ((rect.bottom - e.clientY) / rect.height) * 100;
4020
+ }
4021
+ else {
4022
+ percentage = ((e.clientX - rect.left) / rect.width) * 100;
4023
+ }
4024
+ percentage = Math.max(0, Math.min(100, percentage));
4025
+ const value = min + (percentage / 100) * (max - min);
4026
+ const steppedValue = Math.round(value / step) * step;
4027
+ if (thumb === 'min') {
4028
+ updateRangeValues(Math.min(steppedValue, state.rangeMaxValue), state.rangeMaxValue, attrs, state, true);
4029
+ }
4030
+ else {
4031
+ updateRangeValues(state.rangeMinValue, Math.max(steppedValue, state.rangeMinValue), attrs, state, true);
4032
+ }
4033
+ // Redraw to update the UI during drag
4034
+ m.redraw();
4035
+ };
4036
+ const handleMouseUp = () => {
4037
+ state.isDragging = false;
4038
+ state.activeThumb = null;
4039
+ document.removeEventListener('mousemove', handleMouseMove);
4040
+ document.removeEventListener('mouseup', handleMouseUp);
4041
+ // Fire onchange when dragging ends
4042
+ updateRangeValues(state.rangeMinValue, state.rangeMaxValue, attrs, state, false);
4043
+ };
4044
+ document.addEventListener('mousemove', handleMouseMove);
4045
+ document.addEventListener('mouseup', handleMouseUp);
4046
+ };
4047
+ const fieldClass = vertical ? 'range-field vertical' : 'range-field';
4048
+ const orientation = vertical ? 'vertical' : 'horizontal';
4049
+ return m('.input-field', { className: cn, style }, [
4050
+ iconName ? m('i.material-icons.prefix', iconName) : undefined,
4051
+ // Hidden inputs for label association and accessibility
4052
+ m('input[type=range]', {
4053
+ id,
4054
+ value: state.rangeMinValue,
4055
+ min,
4056
+ max,
4057
+ step,
4058
+ style: { display: 'none' },
4059
+ disabled,
4060
+ tabindex: -1,
4061
+ }),
4062
+ m('input[type=range]', {
4063
+ id: `${id}_max`,
4064
+ value: state.rangeMaxValue,
4065
+ min,
4066
+ max,
4067
+ step,
4068
+ style: { display: 'none' },
4069
+ disabled,
4070
+ tabindex: -1,
4071
+ }),
4072
+ m(`div`, { className: fieldClass }, [
4073
+ m(`.double-range-slider.${orientation}`, {
4074
+ style: containerStyle,
4075
+ tabindex: disabled ? -1 : 0,
4076
+ role: 'slider',
4077
+ 'aria-valuemin': min,
4078
+ 'aria-valuemax': max,
4079
+ 'aria-valuenow': state.rangeMinValue,
4080
+ 'aria-valuetext': `${state.rangeMinValue} to ${state.rangeMaxValue}`,
4081
+ 'aria-label': label || 'Range slider',
4082
+ onclick: (e) => {
4083
+ // Focus the slider when clicked
4084
+ e.currentTarget.focus();
4085
+ },
4086
+ onkeydown: (e) => {
4087
+ if (disabled)
4088
+ return;
4089
+ let newMinValue = state.rangeMinValue;
4090
+ let newMaxValue = state.rangeMaxValue;
4091
+ const activeThumb = state.activeThumb || 'min';
4092
+ switch (e.key) {
4093
+ case 'ArrowLeft':
4094
+ case 'ArrowDown':
4095
+ e.preventDefault();
4096
+ if (activeThumb === 'min') {
4097
+ newMinValue = Math.max(min, newMinValue - step);
4098
+ updateRangeValues(newMinValue, newMaxValue, attrs, state, false);
4099
+ }
4100
+ else {
4101
+ newMaxValue = Math.max(newMinValue, newMaxValue - step);
4102
+ updateRangeValues(newMinValue, newMaxValue, attrs, state, false);
4103
+ }
4104
+ // m.redraw();
4105
+ break;
4106
+ case 'ArrowRight':
4107
+ case 'ArrowUp':
4108
+ e.preventDefault();
4109
+ if (activeThumb === 'min') {
4110
+ newMinValue = Math.min(newMaxValue, newMinValue + step);
4111
+ updateRangeValues(newMinValue, newMaxValue, attrs, state, false);
4112
+ }
4113
+ else {
4114
+ newMaxValue = Math.min(max, newMaxValue + step);
4115
+ updateRangeValues(newMinValue, newMaxValue, attrs, state, false);
4116
+ }
4117
+ // m.redraw();
4118
+ break;
4119
+ case 'Tab':
4120
+ // Handle Tab navigation properly
4121
+ if (activeThumb === 'min') {
4122
+ if (e.shiftKey) {
4123
+ // Shift+Tab from min thumb: go to previous element (let browser handle)
4124
+ return; // Don't prevent default
4125
+ }
4126
+ else {
4127
+ // Tab from min thumb: go to max thumb
4128
+ e.preventDefault();
4129
+ state.activeThumb = 'max';
4130
+ }
4131
+ }
4132
+ else { // activeThumb === 'max'
4133
+ if (e.shiftKey) {
4134
+ // Shift+Tab from max thumb: go to min thumb
4135
+ e.preventDefault();
4136
+ state.activeThumb = 'min';
4137
+ }
4138
+ else {
4139
+ // Tab from max thumb: go to next element (let browser handle)
4140
+ return; // Don't prevent default
4141
+ }
4142
+ }
4143
+ break;
4144
+ case 'Home':
4145
+ e.preventDefault();
4146
+ if (activeThumb === 'min') {
4147
+ updateRangeValues(min, newMaxValue, attrs, state, false);
4148
+ }
4149
+ else {
4150
+ updateRangeValues(newMinValue, newMinValue, attrs, state, false);
4151
+ }
4152
+ // m.redraw();
4153
+ break;
4154
+ case 'End':
4155
+ e.preventDefault();
4156
+ if (activeThumb === 'min') {
4157
+ updateRangeValues(newMaxValue, newMaxValue, attrs, state, false);
4158
+ }
4159
+ else {
4160
+ updateRangeValues(newMinValue, max, attrs, state, false);
4161
+ }
4162
+ // m.redraw();
4163
+ break;
4164
+ }
4165
+ },
4166
+ }, [
4167
+ // Track
4168
+ m(`.track.${orientation}`),
4169
+ // Range
4170
+ m(`.range.${orientation}`, { style: rangeStyle }),
4171
+ // Min thumb
4172
+ m(`.thumb.${orientation}.min-thumb${state.activeThumb === 'min' ? '.active' : ''}`, {
4173
+ style: createThumbStyle(minPercentage, state.activeThumb === 'min'),
4174
+ onmousedown: handleMouseDown('min'),
4175
+ onclick: () => {
4176
+ state.activeThumb = 'min';
4177
+ // m.redraw();
4178
+ },
4179
+ }, showValue ? m(`.value.${orientation}`, state.rangeMinValue.toFixed(0)) : null),
4180
+ // Max thumb
4181
+ m(`.thumb.${orientation}.max-thumb${state.activeThumb === 'max' ? '.active' : ''}`, {
4182
+ style: createThumbStyle(maxPercentage, state.activeThumb === 'max'),
4183
+ onmousedown: handleMouseDown('max'),
4184
+ onclick: () => {
4185
+ state.activeThumb = 'max';
4186
+ // m.redraw();
4187
+ },
4188
+ }, showValue ? m(`.value.${orientation}`, state.rangeMaxValue.toFixed(0)) : null),
4189
+ ]),
4190
+ ]),
4191
+ label
4192
+ ? m(Label, {
4193
+ label,
4194
+ id,
4195
+ isMandatory,
4196
+ isActive: true, // Range sliders always have active labels
4197
+ })
4198
+ : null,
4199
+ helperText ? m(HelperText, { helperText }) : null,
4200
+ ]);
4201
+ };
4202
+
3374
4203
  /**
3375
4204
  * Pure TypeScript MaterialBox - creates an image lightbox that fills the screen when clicked
3376
4205
  * No MaterializeCSS dependencies
@@ -7183,6 +8012,8 @@
7183
8012
  exports.isValidationSuccess = isValidationSuccess;
7184
8013
  exports.padLeft = padLeft;
7185
8014
  exports.range = range;
8015
+ exports.renderMinMaxRange = renderMinMaxRange;
8016
+ exports.renderSingleRangeWithTooltip = renderSingleRangeWithTooltip;
7186
8017
  exports.toast = toast;
7187
8018
  exports.uniqueId = uniqueId;
7188
8019
  exports.uuid4 = uuid4;