@gfazioli/mantine-picker
Version:
Picker component is a wrapper for any component that can be flipped. It is used to create cards, flip boxes and more.
897 lines (894 loc) • 32 kB
JavaScript
'use client';
import React, { useRef, useState, useEffect, useCallback } from 'react';
import { createVarsResolver, polymorphicFactory, useProps, useStyles, Box, Text } from '@mantine/core';
import classes from './Picker.module.css.mjs';
const defaultProps = {
animate: true,
animationDuration: 300,
loop: true,
itemHeight: 40,
visibleItems: 3,
preventPageScroll: true,
disabled: false,
readOnly: false,
minItemOpacity: 0.3,
minItemScale: 0.85,
maxBlurAmount: 0,
easingFunction: "linear",
focusable: true,
wheelSensitivity: 1,
withHighlight: true,
withDividers: true,
withMask: false,
enable3D: true,
perspective: 300,
maxRotation: 60,
cylinderRadius: 4,
momentum: 0.95,
decelerationRate: 0.95,
maskHeight: 55,
maskIntensity: 10,
leftSectionWidth: "auto",
rightSectionWidth: "auto",
rotateY: 0
};
const varsResolver = createVarsResolver(
(_, {
itemHeight,
animationDuration,
visibleItems,
easingFunction,
perspective,
maskHeight,
maskIntensity,
rotateY,
enable3D
}) => {
const pickerHeight = (itemHeight || 40) * (visibleItems || 5);
return {
root: {
"--picker-height": `${pickerHeight}px`,
"--picker-item-height": `${itemHeight || 40}px`,
"--picker-animation-duration": `${animationDuration || 300}ms`,
"--picker-animation-easing": easingFunction || "ease-out",
"--picker-perspective": `${perspective || 300}px`,
"--picker-mask-height": `${maskHeight || 45}%`,
"--picker-mask-intensity": `${maskIntensity || 60}%`,
"--picker-rotate-y": enable3D ? `${rotateY || 0}deg` : "0deg"
}
};
}
);
const Picker = polymorphicFactory((_props, ref) => {
const props = useProps("Picker", defaultProps, _props);
const {
data,
value,
onChange,
animate,
animationDuration,
easingFunction,
loop,
itemHeight,
visibleItems,
renderItem,
preventPageScroll,
disabled,
readOnly,
minItemOpacity,
minItemScale,
maxBlurAmount,
id,
label,
description,
keyboardHint,
focusable,
wheelSensitivity,
withHighlight,
withDividers,
withMask,
enable3D,
perspective,
maxRotation,
cylinderRadius,
momentum,
decelerationRate,
maskHeight,
maskIntensity,
leftSection,
rightSection,
leftSectionWidth,
rightSectionWidth,
rotateY,
size,
lineClamp,
truncate,
inline,
inherit,
gradient,
fw,
fs,
td,
c,
tt,
ta,
variant,
classNames,
style,
styles,
unstyled,
vars,
className,
...others
} = props;
const textProps = {
size,
lineClamp,
truncate,
inline,
inherit,
gradient,
fw,
fs,
td,
c,
tt,
ta,
variant
};
const getStyles = useStyles({
name: "Picker",
props,
classes,
className,
style,
classNames,
styles,
unstyled,
vars,
varsResolver
});
const selectedIndex = value !== void 0 ? data.indexOf(value) : 0;
const prevValueRef = useRef(value);
const containerRef = useRef(null);
const rootRef = useRef(null);
const [isDragging, setIsDragging] = useState(false);
const [lastY, setLastY] = useState(0);
const [dragOffset, setDragOffset] = useState(0);
const [velocity, setVelocity] = useState(0);
const [isMomentumScrolling, setIsMomentumScrolling] = useState(false);
const lastMoveTime = useRef(0);
const lastMovePosition = useRef(0);
const momentumAnimationRef = useRef(null);
const [isWheeling, setIsWheeling] = useState(false);
const wheelTimeoutRef = useRef(null);
const lastWheelEventTime = useRef(0);
const [currentPosition, setCurrentPosition] = useState(selectedIndex);
const [isAnimating, setIsAnimating] = useState(false);
const animationRef = useRef(null);
const [isFocused, setIsFocused] = useState(false);
const prevLoopRef = useRef(loop);
useEffect(() => {
if (value !== prevValueRef.current && selectedIndex !== -1) {
if (animate) {
const targetPosition = findBestPathToValue(selectedIndex);
animateToPosition(targetPosition);
} else {
setCurrentPosition(selectedIndex);
}
prevValueRef.current = value;
}
}, [value, selectedIndex, animate]);
useEffect(() => {
if (prevLoopRef.current !== loop) {
if (!loop) {
const clampedPosition = Math.max(0, Math.min(data.length - 1, currentPosition));
if (clampedPosition !== currentPosition) {
setCurrentPosition(clampedPosition);
}
if (animationRef.current !== null) {
cancelAnimationFrame(animationRef.current);
animationRef.current = null;
setIsAnimating(false);
}
if (momentumAnimationRef.current !== null) {
cancelAnimationFrame(momentumAnimationRef.current);
momentumAnimationRef.current = null;
setIsMomentumScrolling(false);
}
const realIndex = Math.round(clampedPosition);
if (realIndex >= 0 && realIndex < data.length && value !== data[realIndex]) {
onChange?.(data[realIndex]);
}
}
setIsDragging(false);
prevLoopRef.current = loop;
}
}, [loop, currentPosition, onChange, value, data]);
const findBestPathToValue = (targetIndex) => {
if (!loop || data.length <= 1) {
return targetIndex;
}
if (data.length <= (visibleItems || 5)) {
const currentRoundedPos2 = Math.round(currentPosition);
const currentDataIndex2 = (currentRoundedPos2 % data.length + data.length) % data.length;
let directPath2 = targetIndex - currentDataIndex2;
if (directPath2 > data.length / 2) {
directPath2 -= data.length;
} else if (directPath2 < -data.length / 2) {
directPath2 += data.length;
}
return currentRoundedPos2 + directPath2;
}
const currentRoundedPos = Math.round(currentPosition);
const currentDataIndex = (currentRoundedPos % data.length + data.length) % data.length;
const directPath = Math.abs(targetIndex - currentDataIndex);
const wrapPath = data.length - directPath;
if (wrapPath < directPath) {
if (targetIndex > currentDataIndex) {
return currentRoundedPos - wrapPath;
}
return currentRoundedPos + wrapPath;
}
if (targetIndex > currentDataIndex) {
return currentRoundedPos + directPath;
}
return currentRoundedPos - directPath;
};
const animateToPosition = (targetPosition) => {
if (targetPosition === currentPosition || isAnimating) {
return;
}
let clampedTargetPosition = targetPosition;
if (!loop) {
clampedTargetPosition = Math.max(0, Math.min(data.length - 1, targetPosition));
}
if (animationRef.current !== null) {
cancelAnimationFrame(animationRef.current);
}
if (momentumAnimationRef.current !== null) {
cancelAnimationFrame(momentumAnimationRef.current);
setIsMomentumScrolling(false);
}
setIsAnimating(true);
const startTime = performance.now();
const startPosition = currentPosition;
const distance = Math.abs(clampedTargetPosition - startPosition);
const duration = Math.min(
animationDuration || 300,
Math.max(100, (animationDuration || 300) * Math.min(distance / 3, 1))
);
const animate2 = (time) => {
const elapsed = time - startTime;
const progress = Math.min(elapsed / duration, 1);
const easeProgress = 1 - (1 - progress) * (1 - progress);
const position = startPosition + (clampedTargetPosition - startPosition) * easeProgress;
setCurrentPosition(position);
if (progress < 1) {
animationRef.current = requestAnimationFrame(animate2);
} else {
setIsAnimating(false);
animationRef.current = null;
setCurrentPosition(clampedTargetPosition);
if (!disabled) {
const realIndex2 = loop ? (Math.round(clampedTargetPosition) % data.length + data.length) % data.length : Math.round(clampedTargetPosition);
onChange?.(data[realIndex2]);
}
const realIndex = loop ? (Math.round(clampedTargetPosition) % data.length + data.length) % data.length : Math.round(clampedTargetPosition);
prevValueRef.current = data[realIndex];
}
};
animationRef.current = requestAnimationFrame(animate2);
};
const applyMomentum = () => {
if (velocity === 0 || disabled) {
return;
}
setIsMomentumScrolling(true);
let currentVelocity = velocity * (momentum || 0.95);
let currentPos = currentPosition;
let lastDirection = Math.sign(currentVelocity);
const momentumScroll = () => {
currentVelocity *= decelerationRate || 0.95;
let newPos = currentPos + currentVelocity;
if (!loop) {
const minPos = 0;
const maxPos = data.length - 1;
if (newPos < minPos) {
newPos = minPos + (minPos - newPos) * 0.2;
currentVelocity *= -0.5;
} else if (newPos > maxPos) {
newPos = maxPos - (newPos - maxPos) * 0.2;
currentVelocity *= -0.5;
}
if (Math.abs(newPos - minPos) < 0.1) {
newPos = minPos;
}
if (Math.abs(newPos - maxPos) < 0.1) {
newPos = maxPos;
}
if (newPos === minPos || newPos === maxPos) {
currentVelocity *= 0.7;
}
}
currentPos = newPos;
const currentDirection = Math.sign(currentVelocity);
const directionChanged = lastDirection !== 0 && currentDirection !== 0 && lastDirection !== currentDirection;
const isSlowEnough = Math.abs(currentVelocity) < 0.02;
setCurrentPosition(currentPos);
if (!isSlowEnough && !directionChanged) {
momentumAnimationRef.current = requestAnimationFrame(momentumScroll);
} else {
setIsMomentumScrolling(false);
let roundedPosition = Math.round(currentPos);
if (!loop) {
roundedPosition = Math.max(0, Math.min(data.length - 1, roundedPosition));
}
setCurrentPosition(roundedPosition);
requestAnimationFrame(() => {
if (!disabled) {
const realIndex2 = loop ? (roundedPosition % data.length + data.length) % data.length : roundedPosition;
onChange?.(data[realIndex2]);
}
const realIndex = loop ? (roundedPosition % data.length + data.length) % data.length : roundedPosition;
prevValueRef.current = data[realIndex];
});
}
lastDirection = currentDirection;
};
momentumAnimationRef.current = requestAnimationFrame(momentumScroll);
};
useEffect(() => {
return () => {
if (animationRef.current !== null) {
cancelAnimationFrame(animationRef.current);
}
if (momentumAnimationRef.current !== null) {
cancelAnimationFrame(momentumAnimationRef.current);
}
if (wheelTimeoutRef.current) {
clearTimeout(wheelTimeoutRef.current);
}
};
}, []);
const handleMouseEnter = () => {
const isInteractionDisabled = disabled || readOnly;
if (preventPageScroll && !isInteractionDisabled && typeof document !== "undefined") {
document.body.style.overflow = "hidden";
}
};
const handleMouseLeave = () => {
if (preventPageScroll && typeof document !== "undefined") {
document.body.style.overflow = "auto";
}
};
useEffect(() => {
return () => {
if (preventPageScroll && typeof document !== "undefined") {
document.body.style.overflow = "auto";
}
};
}, [preventPageScroll]);
const halfVisible = Math.floor((visibleItems || 5) / 2);
const handleMouseDown = (e) => {
const isInteractionDisabled = disabled || readOnly;
if (isAnimating || isInteractionDisabled || isMomentumScrolling) {
return;
}
if (momentumAnimationRef.current !== null) {
cancelAnimationFrame(momentumAnimationRef.current);
setIsMomentumScrolling(false);
}
e.preventDefault();
setIsDragging(true);
setLastY(e.clientY);
setDragOffset(0);
setVelocity(0);
lastMoveTime.current = performance.now();
lastMovePosition.current = currentPosition;
};
const handleTouchStart = (e) => {
const isInteractionDisabled = disabled || readOnly;
if (isAnimating || isInteractionDisabled || isMomentumScrolling) {
return;
}
if (momentumAnimationRef.current !== null) {
cancelAnimationFrame(momentumAnimationRef.current);
setIsMomentumScrolling(false);
}
setIsDragging(true);
setLastY(e.touches[0].clientY);
setDragOffset(0);
setVelocity(0);
lastMoveTime.current = performance.now();
lastMovePosition.current = currentPosition;
};
const clampPosition = (position) => {
if (loop) {
return position;
}
if (position < 0) {
return position * 0.3;
} else if (position > data.length - 1) {
return data.length - 1 + (position - (data.length - 1)) * 0.3;
}
return position;
};
const handleMouseMove = (e) => {
const isInteractionDisabled = disabled || readOnly;
if (!isDragging || isInteractionDisabled) {
return;
}
const currentTime = performance.now();
const newY = e.clientY;
const deltaY = newY - lastY;
const timeDelta = currentTime - lastMoveTime.current;
const itemOffsetRatio = deltaY / (itemHeight || 40);
setCurrentPosition((prev) => {
let newPosition = prev - itemOffsetRatio;
newPosition = clampPosition(newPosition);
if (timeDelta > 0) {
const positionDelta = newPosition - lastMovePosition.current;
setVelocity(positionDelta / timeDelta * 16);
}
lastMovePosition.current = newPosition;
lastMoveTime.current = currentTime;
return newPosition;
});
setLastY(newY);
};
const handleTouchMove = (e) => {
const isInteractionDisabled = disabled || readOnly;
if (!isDragging || isInteractionDisabled) {
return;
}
const currentTime = performance.now();
const newY = e.touches[0].clientY;
const deltaY = newY - lastY;
const timeDelta = currentTime - lastMoveTime.current;
const itemOffsetRatio = deltaY / (itemHeight || 40);
setCurrentPosition((prev) => {
let newPosition = prev - itemOffsetRatio;
newPosition = clampPosition(newPosition);
if (timeDelta > 0) {
const positionDelta = newPosition - lastMovePosition.current;
setVelocity(positionDelta / timeDelta * 16);
}
lastMovePosition.current = newPosition;
lastMoveTime.current = currentTime;
return newPosition;
});
setLastY(newY);
};
const handleMouseUp = () => {
const isInteractionDisabled = disabled || readOnly;
if (!isDragging || isInteractionDisabled) {
return;
}
setIsDragging(false);
setDragOffset(0);
if (Math.abs(velocity) > 0.05) {
applyMomentum();
} else {
let roundedPosition = Math.round(currentPosition);
if (!loop) {
roundedPosition = Math.max(0, Math.min(data.length - 1, roundedPosition));
}
if (roundedPosition !== currentPosition) {
animateToPosition(roundedPosition);
} else {
if (!disabled) {
const realIndex2 = loop ? (roundedPosition % data.length + data.length) % data.length : roundedPosition;
onChange?.(data[realIndex2]);
}
const realIndex = loop ? (roundedPosition % data.length + data.length) % data.length : roundedPosition;
prevValueRef.current = data[realIndex];
}
}
};
const handleTouchEnd = () => {
const isInteractionDisabled = disabled || readOnly;
if (!isDragging || isInteractionDisabled) {
return;
}
setIsDragging(false);
setDragOffset(0);
if (Math.abs(velocity) > 0.05) {
applyMomentum();
} else {
let roundedPosition = Math.round(currentPosition);
if (!loop) {
roundedPosition = Math.max(0, Math.min(data.length - 1, roundedPosition));
}
if (roundedPosition !== currentPosition) {
animateToPosition(roundedPosition);
} else {
if (!disabled) {
const realIndex2 = loop ? (roundedPosition % data.length + data.length) % data.length : roundedPosition;
onChange?.(data[realIndex2]);
}
const realIndex = loop ? (roundedPosition % data.length + data.length) % data.length : roundedPosition;
prevValueRef.current = data[realIndex];
}
}
};
useEffect(() => {
const handleGlobalMouseMove = (e) => handleMouseMove(e);
const handleGlobalMouseUp = () => handleMouseUp();
const handleGlobalTouchMove = (e) => handleTouchMove(e);
const handleGlobalTouchEnd = () => handleTouchEnd();
if (isDragging) {
window.addEventListener("mousemove", handleGlobalMouseMove, { passive: false });
window.addEventListener("mouseup", handleGlobalMouseUp);
window.addEventListener("touchmove", handleGlobalTouchMove, { passive: false });
window.addEventListener("touchend", handleGlobalTouchEnd);
}
return () => {
window.removeEventListener("mousemove", handleGlobalMouseMove);
window.removeEventListener("mouseup", handleGlobalMouseUp);
window.removeEventListener("touchmove", handleGlobalTouchMove);
window.removeEventListener("touchend", handleGlobalTouchEnd);
};
}, [isDragging, lastY, currentPosition, disabled, readOnly, loop, data.length]);
const isWindows = useCallback(() => {
if (typeof navigator !== "undefined") {
return navigator.userAgent.indexOf("Windows") !== -1;
}
return false;
}, []);
const handleWheel = useCallback(
(e) => {
const isInteractionDisabled = disabled || readOnly;
if (isAnimating || isInteractionDisabled || isMomentumScrolling) {
return;
}
if (momentumAnimationRef.current !== null) {
cancelAnimationFrame(momentumAnimationRef.current);
setIsMomentumScrolling(false);
}
e.preventDefault();
e.stopPropagation();
setIsWheeling(true);
let sensitivity = wheelSensitivity || 1;
let delta = e.deltaY;
if (e.deltaMode === 1) {
delta *= 16;
} else if (e.deltaMode === 2) {
delta *= 100;
}
if (isWindows()) {
sensitivity *= 0.2;
}
delta *= sensitivity;
setCurrentPosition((prev) => {
let newPosition = prev + delta / (itemHeight || 40) * 0.08;
if (!loop) {
if (newPosition < 0) {
newPosition *= 0.3;
} else if (newPosition > data.length - 1) {
newPosition = data.length - 1 + (newPosition - (data.length - 1)) * 0.3;
}
}
return newPosition;
});
if (wheelTimeoutRef.current) {
clearTimeout(wheelTimeoutRef.current);
}
lastWheelEventTime.current = Date.now();
wheelTimeoutRef.current = setTimeout(() => {
if (Date.now() - lastWheelEventTime.current >= 150) {
setIsWheeling(false);
let roundedPosition = Math.round(currentPosition);
if (!loop) {
roundedPosition = Math.max(0, Math.min(data.length - 1, roundedPosition));
}
if (roundedPosition !== currentPosition) {
animateToPosition(roundedPosition);
} else {
if (!disabled) {
const realIndex2 = loop ? (roundedPosition % data.length + data.length) % data.length : roundedPosition;
onChange?.(data[realIndex2]);
}
const realIndex = loop ? (roundedPosition % data.length + data.length) % data.length : roundedPosition;
prevValueRef.current = data[realIndex];
}
wheelTimeoutRef.current = null;
} else {
if (wheelTimeoutRef.current) {
clearTimeout(wheelTimeoutRef.current);
}
wheelTimeoutRef.current = setTimeout(() => {
setIsWheeling(false);
let roundedPosition = Math.round(currentPosition);
if (!loop) {
roundedPosition = Math.max(0, Math.min(data.length - 1, roundedPosition));
}
if (roundedPosition !== currentPosition) {
animateToPosition(roundedPosition);
} else {
if (!disabled) {
const realIndex2 = loop ? (roundedPosition % data.length + data.length) % data.length : roundedPosition;
onChange?.(data[realIndex2]);
}
const realIndex = loop ? (roundedPosition % data.length + data.length) % data.length : roundedPosition;
prevValueRef.current = data[realIndex];
}
wheelTimeoutRef.current = null;
}, 150);
}
}, 150);
},
[
currentPosition,
data,
loop,
isAnimating,
disabled,
readOnly,
itemHeight,
onChange,
wheelSensitivity,
isMomentumScrolling,
isWindows
]
);
const handleKeyDown = useCallback(
(e) => {
const isInteractionDisabled = disabled || readOnly;
if (isInteractionDisabled) {
return;
}
if (momentumAnimationRef.current !== null) {
cancelAnimationFrame(momentumAnimationRef.current);
setIsMomentumScrolling(false);
}
switch (e.key) {
case "ArrowUp": {
e.preventDefault();
let prevPosition = Math.round(currentPosition) - 1;
if (!loop) {
prevPosition = Math.max(0, prevPosition);
}
animateToPosition(prevPosition);
break;
}
case "ArrowDown": {
e.preventDefault();
let nextPosition = Math.round(currentPosition) + 1;
if (!loop) {
nextPosition = Math.min(data.length - 1, nextPosition);
}
animateToPosition(nextPosition);
break;
}
case "Home":
e.preventDefault();
animateToPosition(0);
break;
case "End":
e.preventDefault();
animateToPosition(data.length - 1);
break;
case "PageUp": {
e.preventDefault();
let pageUpPosition = Math.round(currentPosition) - 5;
if (loop) {
pageUpPosition = (pageUpPosition % data.length + data.length) % data.length;
} else {
pageUpPosition = Math.max(0, pageUpPosition);
}
animateToPosition(pageUpPosition);
break;
}
case "PageDown": {
e.preventDefault();
let pageDownPosition = Math.round(currentPosition) + 5;
if (loop) {
pageDownPosition = (pageDownPosition % data.length + data.length) % data.length;
} else {
pageDownPosition = Math.min(data.length - 1, pageDownPosition);
}
animateToPosition(pageDownPosition);
break;
}
}
},
[currentPosition, data.length, loop, disabled, readOnly, isMomentumScrolling]
);
const handleFocus = () => {
if (!disabled) {
setIsFocused(true);
}
};
const handleBlur = () => {
setIsFocused(false);
};
const handleItemClick = (clickedIndex, virtualIndex) => {
const isInteractionDisabled = disabled || readOnly;
if (isDragging || isAnimating || isInteractionDisabled || isMomentumScrolling) {
return;
}
if (momentumAnimationRef.current !== null) {
cancelAnimationFrame(momentumAnimationRef.current);
setIsMomentumScrolling(false);
}
const currentRoundedPos = Math.round(currentPosition);
const directVisualPath = virtualIndex - currentRoundedPos;
animateToPosition(currentRoundedPos + directVisualPath);
if (!disabled) {
onChange?.(data[clickedIndex]);
}
prevValueRef.current = data[clickedIndex];
};
const createContinuousIndices = () => {
if (!loop || data.length <= 1) {
return Array.from({ length: data.length }, (_, i) => ({ dataIndex: i, virtualIndex: i }));
}
const duplicatesPerSide = Math.max(
// At least visibleItems * 2 to ensure we have enough items on both sides
(visibleItems || 5) * 2,
// Or data.length * 2 to ensure we have enough duplicates for large datasets
data.length * 2
);
const continuousIndices = [];
const startIndex = Math.floor(currentPosition) - duplicatesPerSide;
const endIndex = Math.floor(currentPosition) + duplicatesPerSide;
for (let i = startIndex; i <= endIndex; i++) {
const dataIndex = (i % data.length + data.length) % data.length;
continuousIndices.push({ dataIndex, virtualIndex: i });
}
return continuousIndices;
};
const renderItems = () => {
const items = [];
const continuousIndices = createContinuousIndices();
const visibleRange = Math.max(
halfVisible + 1,
// Add just 1 extra item on each side for smoother scrolling
Math.ceil(data.length / 2)
// Keep this for small datasets
);
for (const { dataIndex, virtualIndex } of continuousIndices) {
const relativePos = virtualIndex - currentPosition;
if (Math.abs(relativePos) <= visibleRange) {
const itemOffset = relativePos * (itemHeight || 40) + dragOffset;
const distanceFromCenter = Math.abs(relativePos);
const maxDistance = halfVisible;
const normalizedDistance = Math.min(distanceFromCenter / maxDistance, 1);
const isSelected = Math.round(currentPosition) === virtualIndex;
const minOpacity = typeof minItemOpacity === "number" ? minItemOpacity : 0.3;
let opacity;
if (isSelected) {
opacity = 1;
} else {
opacity = minOpacity + (1 - minOpacity) * (1 - normalizedDistance);
}
const minScale = typeof minItemScale === "number" ? minItemScale : 0.85;
const scale = isSelected ? 1 : minScale + (1 - minScale) * (1 - normalizedDistance);
const blurAmount = maxBlurAmount && maxBlurAmount > 0 && !isSelected ? `${normalizedDistance * maxBlurAmount}px` : "0px";
const key = `item-${dataIndex}-${virtualIndex}`;
let transform;
if (enable3D) {
const zTranslation = enable3D ? Math.abs(relativePos) * -20 : 0;
const rotationAngle = enable3D ? -(relativePos * (maxRotation || 45)) / (cylinderRadius || 4) : 0;
transform = `
translateY(${itemOffset}px)
translateZ(${zTranslation}px)
rotateX(${rotationAngle}deg)
scale(${scale})
`;
} else {
transform = `translateY(${itemOffset}px) scale(${scale})`;
}
items.push(
/* @__PURE__ */ React.createElement(
Text,
{
...textProps,
span: true,
key,
id: id ? `${id}-item-${dataIndex}` : void 0,
className: classes.item,
role: "option",
"aria-selected": isSelected || void 0,
style: {
transform,
opacity,
height: `${itemHeight || 40}px`,
transition: isDragging || isWheeling || isMomentumScrolling ? "none" : void 0,
filter: `blur(${blurAmount})`,
// Add pointer-events: none when opacity is very low to prevent clicking on nearly invisible items
// or when interaction is disabled
pointerEvents: opacity < 0.1 || disabled || readOnly ? "none" : "auto",
// For 3D effect
backfaceVisibility: enable3D ? "hidden" : void 0,
transformStyle: enable3D ? "preserve-3d" : void 0
},
"data-selected": isSelected || void 0,
"data-dragging": isDragging || void 0,
"data-wheeling": isWheeling || void 0,
"data-animating": isAnimating || void 0,
"data-momentum": isMomentumScrolling || void 0,
"data-disabled": disabled || void 0,
"data-readonly": readOnly || void 0,
"data-3d": enable3D || void 0,
onClick: () => handleItemClick(dataIndex, virtualIndex)
},
renderItem ? renderItem(data[dataIndex]) : data[dataIndex]
)
);
}
}
return items;
};
const renderLeftSection = leftSection && /* @__PURE__ */ React.createElement("div", { className: classes.section, "data-position": "left", style: { width: leftSectionWidth } }, leftSection);
const renderRightSection = rightSection && /* @__PURE__ */ React.createElement("div", { className: classes.section, "data-position": "right", style: { width: rightSectionWidth } }, rightSection);
return /* @__PURE__ */ React.createElement(
Box,
{
ref: (node) => {
if (typeof ref === "function") {
ref(node);
} else if (ref) {
ref.current = node;
}
rootRef.current = node;
},
...getStyles("root", {
style: {
perspective: enable3D ? `${perspective}px` : void 0
}
}),
...others,
onMouseEnter: handleMouseEnter,
onMouseLeave: handleMouseLeave,
"data-disabled": disabled || void 0,
"data-readonly": readOnly || void 0,
"data-focused": isFocused || void 0,
"data-3d": enable3D || void 0,
"data-with-left-section": !!leftSection || void 0,
"data-with-right-section": !!rightSection || void 0,
role: "group",
"aria-labelledby": label && id ? `${id}-label` : void 0,
"aria-describedby": (description && id ? `${id}-description` : void 0) || (keyboardHint && id ? `${id}-hint` : void 0) || void 0
},
label && id && /* @__PURE__ */ React.createElement("div", { id: `${id}-label`, className: "sr-only" }, label),
description && id && /* @__PURE__ */ React.createElement("div", { id: `${id}-description`, className: "sr-only" }, description),
keyboardHint && id && /* @__PURE__ */ React.createElement("div", { id: `${id}-hint`, className: "sr-only" }, keyboardHint),
/* @__PURE__ */ React.createElement("div", { className: classes.pickerWrapper }, withHighlight && /* @__PURE__ */ React.createElement("div", { className: classes.highlight }), withMask && /* @__PURE__ */ React.createElement("div", { className: classes.mask }), withDividers && /* @__PURE__ */ React.createElement("div", { className: classes.dividers }), renderLeftSection, renderRightSection, /* @__PURE__ */ React.createElement(
"div",
{
ref: containerRef,
className: classes.container,
onMouseDown: disabled || readOnly ? void 0 : handleMouseDown,
onTouchStart: disabled || readOnly ? void 0 : handleTouchStart,
onWheel: disabled || readOnly ? void 0 : handleWheel,
onKeyDown: disabled || readOnly ? void 0 : handleKeyDown,
onFocus: handleFocus,
onBlur: handleBlur,
tabIndex: disabled || readOnly ? -1 : 0,
role: "listbox",
"aria-activedescendant": id ? `${id}-item-${Math.round(currentPosition)}` : void 0,
"aria-disabled": disabled || void 0,
"aria-readonly": readOnly || void 0,
style: {
cursor: disabled ? "not-allowed" : readOnly ? "default" : isDragging ? "grabbing" : "grab",
perspective: enable3D ? `${perspective}px` : void 0,
transformStyle: enable3D ? "preserve-3d" : void 0
},
"data-animating": isAnimating || void 0,
"data-wheeling": isWheeling || void 0,
"data-momentum": isMomentumScrolling || void 0,
"data-disabled": disabled || void 0,
"data-readonly": readOnly || void 0,
"data-3d": enable3D || void 0,
"data-dragging": isDragging || void 0
},
renderItems()
))
);
});
Picker.classes = classes;
Picker.displayName = "Picker";
export { Picker };
//# sourceMappingURL=Picker.mjs.map