<div class="atm-range-slider" data-component="range-slider">
    <div class="slider-wrapper">
        <label for="age-slider">Leeftijd</label>
        <div class="slider-track">
            <input type="range" id="age-slider" name="age" min="15" max="40" value="24" step="1">
            <div class="slider-value"><span>24</span></div>
        </div>
    </div>
</div>
<div class="atm-range-slider" data-component="range-slider">
    <div class="slider-wrapper">
        <label for="{{id}}">{{label}}</label>
        <div class="slider-track">
            <input type="range" id="{{id}}" name="{{name}}" min="{{min}}" max="{{max}}" value="{{value}}" step="{{step}}">
            <div class="slider-value"><span>{{value}}</span></div>
        </div>
    </div>
</div>
{
  "id": "age-slider",
  "name": "age",
  "label": "Leeftijd",
  "min": 15,
  "max": 40,
  "value": 24,
  "step": 1
}
  • Content:
    .atm-range-slider {
        @apply relative mb-8 text-white font-display;
    
        .slider-wrapper {
            @apply flex items-center gap-8;
        }
    
        label {
            @apply text-lg whitespace-nowrap min-w-[100px];
        }
    
        .slider-value {
            @apply absolute bg-links text-black rounded-md flex items-center justify-center h-7 w-8 cursor-pointer text-md;
            transform: translate(-50%, -50%);
            top: 50%;
            z-index: 3;
            user-select: none;
        }
    
        .slider-track {
            @apply relative h-2 flex-1;
        }
    
        input[type='range'] {
            @apply absolute w-full h-4 appearance-none cursor-pointer;
            background: transparent;
            z-index: 2;
        }
    
        /* White background track */
        .slider-track::before {
            content: '';
            @apply absolute top-1/2 -translate-y-1/2 w-full h-1 bg-white rounded-full;
            z-index: 1;
        }
    
        /* Orange filled track */
        .slider-track::after {
            content: '';
            @apply absolute top-1/2 -translate-y-1/2 h-1 bg-links rounded-full;
            left: 0;
            width: var(--fill-width, 0%);
            z-index: 2;
        }
    
        /* Hide default thumb */
        input[type='range']::-webkit-slider-thumb {
            @apply appearance-none;
            width: 0;
            height: 0;
            background: transparent;
        }
    
        input[type='range']::-moz-range-thumb {
            width: 0;
            height: 0;
            border: 0;
            background: transparent;
        }
    
        input[type='range']::-ms-thumb {
            width: 0;
            height: 0;
            background: transparent;
        }
    }
  • URL: /components/raw/range-slider/range-slider.css
  • Filesystem Path: src\components\02-atoms\range-slider\range-slider.css
  • Size: 1.6 KB
  • Content:
    (function () {
    
        'use strict';
    
        function initRangeSliders(context) {
            const sliders = (context || document).querySelectorAll('[data-component="range-slider"]');
    
            sliders.forEach(slider => {
                const input = slider.querySelector('input[type="range"]');
                const valueDisplay = slider.querySelector('.slider-value span');
                const valueBox = slider.querySelector('.slider-value');
                const track = slider.querySelector('.slider-track');
    
                function updateValue() {
                    const { min, max } = getSliderValues();
                    const value = parseFloat(input.value) || 0;
                    const percentage = ((value - min) / (max - min)) * 100;
                    
                    // Update value display
                    if (valueDisplay) {
                        valueDisplay.textContent = value;
                    }
                    
                    // Update visual position and fill
                    if (valueBox && track) {
                        valueBox.style.left = `${percentage}%`;
                        track.style.setProperty('--fill-width', `${percentage}%`);
                    }
                }
    
                function updateValueFromPosition(clientX) {
                    const rect = track.getBoundingClientRect();
                    const { min, max, step } = getSliderValues();
                    
                    // Calculate position ratio (0-1) from mouse position
                    const positionRatio = Math.max(0, Math.min(1, (clientX - rect.left) / rect.width));
                    
                    // Convert position to value and apply step constraints
                    const rawValue = min + positionRatio * (max - min);
                    const steppedValue = Math.round(rawValue / step) * step;
                    const clampedValue = Math.max(min, Math.min(max, steppedValue));
                    
                    // Update input and trigger events
                    input.value = clampedValue;
                    updateValue();
                    input.dispatchEvent(new Event('input', { bubbles: true }));
                }
    
                function getSliderValues() {
                    return {
                        min: parseFloat(input.min) || 0,
                        max: parseFloat(input.max) || 100,
                        step: parseFloat(input.step) || 1
                    };
                }
    
                let isDragging = false;
    
                // Make value box draggable
                valueBox.addEventListener('mousedown', (e) => {
                    isDragging = true;
                    e.preventDefault();
                });
    
                // Click on track to set value
                track.addEventListener('click', (e) => {
                    if (e.target === track || e.target === track.querySelector('::before')) {
                        updateValueFromPosition(e.clientX);
                    }
                });
    
                document.addEventListener('mousemove', (e) => {
                    if (isDragging) {
                        updateValueFromPosition(e.clientX);
                    }
                });
    
                document.addEventListener('mouseup', () => {
                    isDragging = false;
                });
    
                // Touch support
                valueBox.addEventListener('touchstart', (e) => {
                    isDragging = true;
                    e.preventDefault();
                });
    
                document.addEventListener('touchmove', (e) => {
                    if (isDragging && e.touches.length > 0) {
                        updateValueFromPosition(e.touches[0].clientX);
                    }
                });
    
                document.addEventListener('touchend', () => {
                    isDragging = false;
                });
    
                input.addEventListener('input', updateValue);
                updateValue(); // Initial value
            });
        }
    
        // Initial load
        initRangeSliders();
    
        // Expose to global scope for re-initialization if needed
        window.initRangeSliders = initRangeSliders;
    })();
  • URL: /components/raw/range-slider/range-slider.js
  • Filesystem Path: src\components\02-atoms\range-slider\range-slider.js
  • Size: 4 KB

No notes defined.