Playground

Dragging Slider

September 2025

Select a ☕

  • Decaf with caffeine
  • Mild and smooth
  • Balanced daily brew
  • Strong bold morning boost
  • Extra intense flavor
  • Rocket fuel power shot
"use client";

"use client";

import {Slider} from "@/components/Headless/Slider";
import {cn} from "@/lib/cn";
import {calculatePercentage} from "@/utilities/calculate-percentage";
import React, {useMemo, useRef, useState} from "react";

const array = [
    "Decaf with caffeine",
    "Mild and smooth",
    "Balanced daily brew",
    "Strong bold morning boost",
    "Extra intense flavor",
    "Rocket fuel power shot",
];

export const FrequencySlider = () => {
    const [selectedValue, setSelectedValue] = useState<number | null>(null);
    const [value, setValue] = useState([(array.length - 1) / 2]);
    const toolTipRef = useRef<HTMLDivElement>(null);
    const maxValue = array.length - 1;

    const isSelectedValueNull = selectedValue === null;

    const tooltipPositions = useMemo(() => {
        const listItems = toolTipRef.current?.querySelectorAll("li");
        if (!listItems) return [];

        return array.map((_, index) => {
            const itemWidth = listItems[index].clientWidth;
            const offsetLeft = -listItems[index].offsetLeft;
            const x =
                index === 0 ? "0%" : index === maxValue ? "-100%" : "-50%";
            const left =
                index === 0 || index === maxValue
                    ? `${calculatePercentage(index, maxValue)}%`
                    : `calc(${calculatePercentage(index, maxValue)}% + ${19 - (index / maxValue) * 38}px)`;

            return {
                left: left,
                x,
                width: itemWidth,
                offsetLeft,
                id: index,
            };
        });
    }, [array, maxValue, toolTipRef.current]);

    const handleSliderChange = (sliderValue: number[]) => {
        const selectedIndex = sliderValue[0];
        setValue(sliderValue);
        setSelectedValue(selectedIndex);
    };

    return (
        <div className="relative mx-auto w-8/9 sm:w-3/4">
            <Slider
                min={0}
                max={maxValue}
                value={value}
                onValueChange={handleSliderChange}
            />

            <p
                className={cn(
                    "text-text-medium-accent pointer-events-none absolute bottom-[calc(1.2rem+100%)] left-1/2 -translate-x-1/2 text-sm transition-opacity duration-300",
                    selectedValue !== null && "opacity-0"
                )}>
                Select a ☕
            </p>

            <div
                className="pointer-events-none absolute bottom-[calc(1.2rem+100%)] overflow-hidden rounded-lg bg-amber-700 shadow-[2px_2px_4px_rgba(0,0,0,0.8)] transition-all duration-300"
                ref={toolTipRef}
                style={{
                    left: !isSelectedValueNull
                        ? tooltipPositions[selectedValue]?.left
                        : "50%",
                    transform: `translateX(${!isSelectedValueNull ? tooltipPositions[selectedValue]?.x : "-50%"})`,
                    width: `${!isSelectedValueNull ? tooltipPositions[selectedValue]?.width : 0}px`,
                    opacity: !isSelectedValueNull ? 1 : 0,
                }}>
                <ul
                    className="flex whitespace-nowrap transition-all duration-300"
                    style={{
                        transform: `translateX(${!isSelectedValueNull ? tooltipPositions[selectedValue]?.offsetLeft : 0}px)`,
                    }}>
                    {array.map((item, index) => {
                        return (
                            <li
                                className={cn(
                                    "text-text-accent px-2 py-1.5 text-sm font-medium transition-all duration-300",
                                    index === selectedValue
                                        ? "delay-75"
                                        : "opacity-0 blur-xs"
                                )}
                                key={index}>
                                {item}
                            </li>
                        );
                    })}
                </ul>
            </div>
        </div>
    );
};

const DraggingSlider = () => {
    return (
        <article className="grid min-h-96 place-items-center bg-white/5 sm:aspect-video sm:min-h-0">
            <FrequencySlider />
        </article>
    );
};

export default DraggingSlider;
Next

Spotlight