// src/components/user-action/ScrollPicker.tsx
import { useCallback, useEffect, useState } from "react";
import clsx from "clsx";

// src/utils/array.ts
var defaultRangeOptions = {
  allowEmptyRange: false,
  stepSize: 1,
  exclusiveStart: false,
  exclusiveEnd: true
};
var range = (endOrRange, options) => {
  const { allowEmptyRange, stepSize, exclusiveStart, exclusiveEnd } = { ...defaultRangeOptions, ...options };
  let start = 0;
  let end;
  if (typeof endOrRange === "number") {
    end = endOrRange;
  } else {
    start = endOrRange[0];
    end = endOrRange[1];
  }
  if (!exclusiveEnd) {
    end -= 1;
  }
  if (exclusiveStart) {
    start += 1;
  }
  if (end - 1 < start) {
    if (!allowEmptyRange) {
      console.warn(`range: end (${end}) < start (${start}) should be allowed explicitly, set options.allowEmptyRange to true`);
    }
    return [];
  }
  return Array.from({ length: end - start }, (_, index) => index * stepSize + start);
};
var getNeighbours = (list, item, neighbourDistance = 2) => {
  const index = list.indexOf(item);
  const totalItems = neighbourDistance * 2 + 1;
  if (list.length < totalItems) {
    console.warn("List is to short");
    return list;
  }
  if (index === -1) {
    console.error("item not found in list");
    return list.splice(0, totalItems);
  }
  let start = index - neighbourDistance;
  if (start < 0) {
    start += list.length;
  }
  const end = (index + neighbourDistance + 1) % list.length;
  const result = [];
  let ignoreOnce = list.length === totalItems;
  for (let i = start; i !== end || ignoreOnce; i = (i + 1) % list.length) {
    result.push(list[i]);
    if (end === i && ignoreOnce) {
      ignoreOnce = false;
    }
  }
  return result;
};

// src/utils/math.ts
var clamp = (value, range2 = [0, 1]) => {
  const [min, max] = range2;
  return Math.min(Math.max(value, min), max);
};

// src/components/user-action/ScrollPicker.tsx
import { jsx, jsxs } from "react/jsx-runtime";
var up = 1;
var down = -1;
var ScrollPicker = ({
  options,
  mapping,
  selected,
  onChange,
  disabled = false
}) => {
  let selectedIndex = 0;
  if (selected && options.indexOf(selected) !== -1) {
    selectedIndex = options.indexOf(selected);
  }
  const [{
    currentIndex,
    transition,
    items,
    lastTimeStamp
  }, setAnimation] = useState({
    targetIndex: selectedIndex,
    currentIndex: disabled ? selectedIndex : 0,
    velocity: 0,
    animationVelocity: Math.floor(options.length / 2),
    transition: 0,
    items: options
  });
  const itemsShownCount = 5;
  const shownItems = getNeighbours(range(items.length), currentIndex).map((index) => ({
    name: mapping(items[index]),
    index
  }));
  const itemHeight = 40;
  const distance = 8;
  const containerHeight = itemHeight * (itemsShownCount - 2) + distance * (itemsShownCount - 2 + 1);
  const getDirection = useCallback((targetIndex, currentIndex2, transition2, length) => {
    if (targetIndex === currentIndex2) {
      return transition2 > 0 ? up : down;
    }
    let distanceForward = targetIndex - currentIndex2;
    if (distanceForward < 0) {
      distanceForward += length;
    }
    return distanceForward >= length / 2 ? down : up;
  }, []);
  const animate = useCallback((timestamp, startTime) => {
    setAnimation((prevState) => {
      const {
        targetIndex,
        currentIndex: currentIndex2,
        transition: transition2,
        animationVelocity,
        velocity,
        items: items2,
        lastScrollTimeStamp
      } = prevState;
      if (disabled) {
        return { ...prevState, currentIndex: targetIndex, velocity: 0, lastTimeStamp: timestamp };
      }
      if (targetIndex === currentIndex2 && velocity === 0 && transition2 === 0 || !startTime) {
        return { ...prevState, lastTimeStamp: timestamp };
      }
      const progress = (timestamp - startTime) / 1e3;
      const direction = getDirection(targetIndex, currentIndex2, transition2, items2.length);
      let newVelocity = velocity;
      let usedVelocity;
      let newCurrentIndex = currentIndex2;
      const isAutoScrolling = velocity <= 10 && (!lastScrollTimeStamp || timestamp - lastScrollTimeStamp > 50);
      const newLastScrollTimeStamp = velocity !== 0 ? timestamp : lastScrollTimeStamp;
      if (isAutoScrolling) {
        usedVelocity = direction * animationVelocity;
      } else {
        usedVelocity = velocity;
        newVelocity = velocity * 0.5;
        if (Math.abs(newVelocity) <= 0.05) {
          newVelocity = 0;
        }
      }
      let newTransition = transition2 + usedVelocity * progress;
      const changeThreshold = 0.5;
      while (newTransition >= changeThreshold) {
        if (newCurrentIndex === targetIndex && newTransition >= changeThreshold && isAutoScrolling) {
          newTransition = 0;
          break;
        }
        newCurrentIndex = (currentIndex2 + 1) % items2.length;
        newTransition -= 1;
      }
      if (newTransition >= changeThreshold) {
        newTransition = 0;
      }
      while (newTransition <= -changeThreshold) {
        if (newCurrentIndex === targetIndex && newTransition <= -changeThreshold && isAutoScrolling) {
          newTransition = 0;
          break;
        }
        newCurrentIndex = currentIndex2 === 0 ? items2.length - 1 : currentIndex2 - 1;
        newTransition += 1;
      }
      let newTargetIndex = targetIndex;
      if (!isAutoScrolling) {
        newTargetIndex = newCurrentIndex;
      }
      if ((currentIndex2 !== newTargetIndex || newTargetIndex !== targetIndex) && newTargetIndex === newCurrentIndex) {
        onChange?.(items2[newCurrentIndex]);
      }
      return {
        targetIndex: newTargetIndex,
        currentIndex: newCurrentIndex,
        animationVelocity,
        transition: newTransition,
        velocity: newVelocity,
        items: items2,
        lastTimeStamp: timestamp,
        lastScrollTimeStamp: newLastScrollTimeStamp
      };
    });
  }, [disabled, getDirection, onChange]);
  useEffect(() => {
    requestAnimationFrame((timestamp) => animate(timestamp, lastTimeStamp));
  });
  const opacity = (transition2, index, itemsCount) => {
    const max = 100;
    const min = 0;
    const distance2 = max - min;
    let opacityValue = min;
    const unitTransition = clamp(transition2 / 0.5);
    if (index === 1 || index === itemsCount - 2) {
      if (index === 1 && transition2 > 0) {
        opacityValue += Math.floor(unitTransition * distance2);
      }
      if (index === itemsCount - 2 && transition2 < 0) {
        opacityValue += Math.floor(unitTransition * distance2);
      }
    } else {
      opacityValue = max;
    }
    return clamp(1 - opacityValue / max);
  };
  return /* @__PURE__ */ jsx(
    "div",
    {
      className: "relative overflow-hidden",
      style: { height: containerHeight },
      onWheel: (event) => {
        if (event.deltaY !== 0) {
          const deltaY = clamp(event.deltaY, [-itemHeight * 2 / 3, itemHeight * 2 / 3]);
          setAnimation(({ velocity, ...animationData }) => ({ ...animationData, velocity: velocity + deltaY }));
        }
      },
      children: /* @__PURE__ */ jsxs("div", { className: "absolute top-1/2 -translate-y-1/2 -translate-x-1/2 left-1/2", children: [
        /* @__PURE__ */ jsx(
          "div",
          {
            className: "absolute z-[1] top-1/2 -translate-y-1/2 -translate-x-1/2 left-1/2 w-full min-w-[40px] border border-divider/50 border-y-2 border-x-0 ",
            style: { height: `${itemHeight}px` }
          }
        ),
        /* @__PURE__ */ jsx(
          "div",
          {
            className: "flex-col-2 select-none",
            style: {
              transform: `translateY(${-transition * (distance + itemHeight)}px)`,
              columnGap: `${distance}px`
            },
            children: shownItems.map(({ name, index }, arrayIndex) => /* @__PURE__ */ jsx(
              "div",
              {
                className: clsx(
                  `flex-col-2 items-center justify-center rounded-md`,
                  {
                    "text-primary font-bold": currentIndex === index,
                    "text-on-background": currentIndex === index,
                    "cursor-pointer": !disabled,
                    "cursor-not-allowed": disabled
                  }
                ),
                style: {
                  opacity: currentIndex !== index ? opacity(transition, arrayIndex, shownItems.length) : void 0,
                  height: `${itemHeight}px`,
                  maxHeight: `${itemHeight}px`
                },
                onClick: () => !disabled && setAnimation((prevState) => ({ ...prevState, targetIndex: index })),
                children: name
              },
              index
            ))
          }
        )
      ] })
    }
  );
};
export {
  ScrollPicker
};
//# sourceMappingURL=ScrollPicker.js.map