@ducor/react
Version:
admin template ui interface
102 lines (101 loc) • 6.62 kB
JavaScript
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { forwardRef, useEffect, useRef, useState, useCallback, } from 'react';
const ScrollAreaAuto = forwardRef((_a, ref) => {
var { children, className = '', direction = 'vertical', scrollbarAlwaysVisible = false } = _a, rest = __rest(_a, ["children", "className", "direction", "scrollbarAlwaysVisible"]);
const viewportRef = useRef(null);
const scrollbarThumbRef = useRef(null);
const [thumbSize, setThumbSize] = useState(20);
const [thumbPosition, setThumbPosition] = useState(0);
const [isDragging, setIsDragging] = useState(false);
const isVertical = direction === 'vertical' || direction === 'both';
const isHorizontal = direction === 'horizontal' || direction === 'both';
const calculateThumbSize = useCallback(() => {
if (!viewportRef.current)
return;
if (isVertical) {
const { scrollHeight, clientHeight } = viewportRef.current;
const visibleRatio = clientHeight / scrollHeight;
const thumbHeightRatio = visibleRatio * clientHeight;
setThumbSize(Math.max(thumbHeightRatio, 20));
}
if (isHorizontal) {
const { scrollWidth, clientWidth } = viewportRef.current;
const visibleRatio = clientWidth / scrollWidth;
const thumbWidthRatio = visibleRatio * clientWidth;
setThumbSize(Math.max(thumbWidthRatio, 20));
}
}, [isVertical, isHorizontal]);
const handleScroll = useCallback(() => {
if (!viewportRef.current || !scrollbarThumbRef.current)
return;
if (isVertical) {
const { scrollTop, scrollHeight, clientHeight } = viewportRef.current;
const scrollRatio = scrollTop / (scrollHeight - clientHeight);
const trackHeight = clientHeight - thumbSize;
setThumbPosition(scrollRatio * trackHeight);
}
if (isHorizontal) {
const { scrollLeft, scrollWidth, clientWidth } = viewportRef.current;
const scrollRatio = scrollLeft / (scrollWidth - clientWidth);
const trackWidth = clientWidth - thumbSize;
setThumbPosition(scrollRatio * trackWidth);
}
}, [thumbSize, isVertical, isHorizontal]);
const handleThumbMouseDown = useCallback((e) => {
e.preventDefault();
setIsDragging(true);
document.addEventListener('mousemove', handleMouseMove);
document.addEventListener('mouseup', handleMouseUp);
}, []);
const handleMouseMove = useCallback((e) => {
if (!isDragging || !viewportRef.current || !scrollbarThumbRef.current)
return;
if (isVertical) {
const { clientHeight } = viewportRef.current;
const trackHeight = clientHeight - thumbSize;
const mousePosition = e.clientY - viewportRef.current.getBoundingClientRect().top;
const newPosition = Math.min(Math.max(mousePosition - thumbSize / 2, 0), trackHeight);
const scrollRatio = newPosition / trackHeight;
viewportRef.current.scrollTop =
scrollRatio * (viewportRef.current.scrollHeight - clientHeight);
}
if (isHorizontal) {
const { clientWidth } = viewportRef.current;
const trackWidth = clientWidth - thumbSize;
const mousePosition = e.clientX - viewportRef.current.getBoundingClientRect().left;
const newPosition = Math.min(Math.max(mousePosition - thumbSize / 2, 0), trackWidth);
const scrollRatio = newPosition / trackWidth;
viewportRef.current.scrollLeft =
scrollRatio * (viewportRef.current.scrollWidth - clientWidth);
}
}, [isDragging, thumbSize, isVertical, isHorizontal]);
const handleMouseUp = useCallback(() => {
setIsDragging(false);
document.removeEventListener('mousemove', handleMouseMove);
document.removeEventListener('mouseup', handleMouseUp);
}, [handleMouseMove]);
useEffect(() => {
calculateThumbSize();
window.addEventListener('resize', calculateThumbSize);
return () => window.removeEventListener('resize', calculateThumbSize);
}, [calculateThumbSize]);
return (_jsxs("div", Object.assign({ ref: ref, className: `relative flex ${className} ${scrollbarAlwaysVisible ? 'hover:[&_.scrollbar]:opacity-100' : 'hover:[&_.scrollbar]:opacity-60'}` }, rest, { children: [_jsx("div", { ref: viewportRef, onScroll: handleScroll, className: `flex-1 overflow-auto scrollbar-none ${isVertical ? 'overflow-y-auto' : ''} ${isHorizontal ? 'overflow-x-auto' : ''}`, children: children }), isVertical && (_jsx("div", { className: `scrollbar absolute top-0 right-0 w-2 h-full transition-opacity duration-200 pointer-events-none select-none py-[2px] ${scrollbarAlwaysVisible ? 'opacity-100' : 'opacity-0'}`, children: _jsx("div", { className: "w-full h-full bg-black/5 rounded relative", children: _jsx("div", { ref: scrollbarThumbRef, className: `absolute w-full rounded cursor-pointer pointer-events-auto transition-all duration-200 ${isDragging ? 'bg-black/50' : 'bg-black/30 hover:bg-black/40'}`, style: {
height: thumbSize,
transform: `translateY(${thumbPosition}px)`,
}, onMouseDown: handleThumbMouseDown }) }) })), isHorizontal && (_jsx("div", { className: `scrollbar absolute bottom-0 left-0 h-2 w-full transition-opacity duration-200 pointer-events-none select-none px-[2px] ${scrollbarAlwaysVisible ? 'opacity-100' : 'opacity-0'}`, children: _jsx("div", { className: "h-full w-full bg-black/5 rounded relative", children: _jsx("div", { className: `absolute h-full rounded cursor-pointer pointer-events-auto transition-all duration-200 ${isDragging ? 'bg-black/50' : 'bg-black/30 hover:bg-black/40'}`, style: {
width: thumbSize,
transform: `translateX(${thumbPosition}px)`,
}, onMouseDown: handleThumbMouseDown }) }) }))] })));
});
export default ScrollAreaAuto;