chore: cleanup unused files and scripts
- Removed legacy TypeScript templates (replaced by JSON) - Removed unused PowerShell and CJS maintenance scripts - Removed debug logs and unused components - Files moved to local _TRASH/ directory (excluded from git)
This commit is contained in:
@@ -1,272 +0,0 @@
|
||||
import React, { useRef, useEffect, useState, useCallback } from 'react';
|
||||
|
||||
interface CustomScrollbarProps {
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
horizontal?: boolean;
|
||||
}
|
||||
|
||||
export const CustomScrollbar: React.FC<CustomScrollbarProps> = ({
|
||||
children,
|
||||
className = '',
|
||||
horizontal = false
|
||||
}) => {
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const thumbRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const [isHoveringContainer, setIsHoveringContainer] = useState(false);
|
||||
const [isHoveringTrack, setIsHoveringTrack] = useState(false);
|
||||
const [isDragging, setIsDragging] = useState(false);
|
||||
const [thumbSize, setThumbSize] = useState(40);
|
||||
|
||||
// Drag state
|
||||
const dragState = useRef({
|
||||
isActive: false,
|
||||
startMouse: 0,
|
||||
startScroll: 0,
|
||||
trackSize: 0,
|
||||
maxScroll: 0
|
||||
});
|
||||
|
||||
// Update thumb size
|
||||
const updateMetrics = useCallback(() => {
|
||||
const container = containerRef.current;
|
||||
if (!container) return;
|
||||
|
||||
if (horizontal) {
|
||||
const ratio = container.clientWidth / Math.max(container.scrollWidth, 1);
|
||||
setThumbSize(Math.max(40, ratio * container.clientWidth));
|
||||
} else {
|
||||
const ratio = container.clientHeight / Math.max(container.scrollHeight, 1);
|
||||
setThumbSize(Math.max(40, ratio * container.clientHeight));
|
||||
}
|
||||
|
||||
updateThumbVisual();
|
||||
}, [horizontal]);
|
||||
|
||||
// Update thumb position from scroll
|
||||
const updateThumbVisual = useCallback(() => {
|
||||
const container = containerRef.current;
|
||||
const thumb = thumbRef.current;
|
||||
if (!container || !thumb) return;
|
||||
|
||||
if (dragState.current.isActive) return;
|
||||
|
||||
if (horizontal) {
|
||||
const maxScroll = container.scrollWidth - container.clientWidth;
|
||||
const trackSize = container.clientWidth - thumbSize;
|
||||
if (maxScroll <= 0 || trackSize <= 0) {
|
||||
thumb.style.transform = 'translateX(0px)';
|
||||
return;
|
||||
}
|
||||
const ratio = container.scrollLeft / maxScroll;
|
||||
thumb.style.transform = `translateX(${ratio * trackSize}px)`;
|
||||
} else {
|
||||
const maxScroll = container.scrollHeight - container.clientHeight;
|
||||
const trackSize = container.clientHeight - thumbSize;
|
||||
if (maxScroll <= 0 || trackSize <= 0) {
|
||||
thumb.style.transform = 'translateY(0px)';
|
||||
return;
|
||||
}
|
||||
const ratio = container.scrollTop / maxScroll;
|
||||
thumb.style.transform = `translateY(${ratio * trackSize}px)`;
|
||||
}
|
||||
}, [horizontal, thumbSize]);
|
||||
|
||||
// Setup
|
||||
useEffect(() => {
|
||||
updateMetrics();
|
||||
|
||||
const handleResize = () => updateMetrics();
|
||||
window.addEventListener('resize', handleResize);
|
||||
|
||||
const observer = new MutationObserver(() => updateMetrics());
|
||||
if (containerRef.current) {
|
||||
observer.observe(containerRef.current, { childList: true, subtree: true });
|
||||
}
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('resize', handleResize);
|
||||
observer.disconnect();
|
||||
};
|
||||
}, [updateMetrics]);
|
||||
|
||||
// Scroll listener
|
||||
useEffect(() => {
|
||||
const container = containerRef.current;
|
||||
if (!container) return;
|
||||
|
||||
const onScroll = () => {
|
||||
if (!dragState.current.isActive) {
|
||||
updateThumbVisual();
|
||||
}
|
||||
};
|
||||
|
||||
container.addEventListener('scroll', onScroll, { passive: true });
|
||||
return () => container.removeEventListener('scroll', onScroll);
|
||||
}, [updateThumbVisual]);
|
||||
|
||||
// Drag handling - DIRECT synchronous updates
|
||||
useEffect(() => {
|
||||
if (!isDragging) {
|
||||
// Restore transition when not dragging
|
||||
if (thumbRef.current) {
|
||||
thumbRef.current.style.transition = 'opacity 0.15s, width 0.15s, height 0.15s';
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const container = containerRef.current;
|
||||
const thumb = thumbRef.current;
|
||||
if (!container || !thumb) return;
|
||||
|
||||
const state = dragState.current;
|
||||
state.isActive = true;
|
||||
|
||||
// REMOVE transition during drag for instant response
|
||||
thumb.style.transition = 'none';
|
||||
|
||||
const handleMouseMove = (e: MouseEvent) => {
|
||||
const mousePos = horizontal ? e.clientX : e.clientY;
|
||||
const deltaMouse = mousePos - state.startMouse;
|
||||
const scrollRatio = deltaMouse / state.trackSize;
|
||||
const newScroll = state.startScroll + (scrollRatio * state.maxScroll);
|
||||
const clampedScroll = Math.max(0, Math.min(newScroll, state.maxScroll));
|
||||
|
||||
if (horizontal) {
|
||||
container.scrollLeft = clampedScroll;
|
||||
const visualRatio = state.maxScroll > 0 ? clampedScroll / state.maxScroll : 0;
|
||||
thumb.style.transform = `translateX(${visualRatio * state.trackSize}px)`;
|
||||
} else {
|
||||
container.scrollTop = clampedScroll;
|
||||
const visualRatio = state.maxScroll > 0 ? clampedScroll / state.maxScroll : 0;
|
||||
thumb.style.transform = `translateY(${visualRatio * state.trackSize}px)`;
|
||||
}
|
||||
};
|
||||
|
||||
const handleMouseUp = () => {
|
||||
state.isActive = false;
|
||||
setIsDragging(false);
|
||||
// Transition will be restored by effect cleanup
|
||||
};
|
||||
|
||||
document.addEventListener('mousemove', handleMouseMove);
|
||||
document.addEventListener('mouseup', handleMouseUp);
|
||||
|
||||
return () => {
|
||||
document.removeEventListener('mousemove', handleMouseMove);
|
||||
document.removeEventListener('mouseup', handleMouseUp);
|
||||
state.isActive = false;
|
||||
};
|
||||
}, [isDragging, horizontal]);
|
||||
|
||||
const handleThumbMouseDown = (e: React.MouseEvent) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
const container = containerRef.current;
|
||||
const thumb = thumbRef.current;
|
||||
if (!container || !thumb) return;
|
||||
|
||||
const state = dragState.current;
|
||||
|
||||
if (horizontal) {
|
||||
state.startMouse = e.clientX;
|
||||
state.startScroll = container.scrollLeft;
|
||||
state.trackSize = container.clientWidth - thumbSize;
|
||||
state.maxScroll = container.scrollWidth - container.clientWidth;
|
||||
} else {
|
||||
state.startMouse = e.clientY;
|
||||
state.startScroll = container.scrollTop;
|
||||
state.trackSize = container.clientHeight - thumbSize;
|
||||
state.maxScroll = container.scrollHeight - container.clientHeight;
|
||||
}
|
||||
|
||||
// Remove transition immediately on mouse down
|
||||
thumb.style.transition = 'none';
|
||||
setIsDragging(true);
|
||||
};
|
||||
|
||||
const handleTrackClick = (e: React.MouseEvent) => {
|
||||
if (e.target === thumbRef.current) return;
|
||||
|
||||
const container = containerRef.current;
|
||||
if (!container) return;
|
||||
|
||||
const rect = container.getBoundingClientRect();
|
||||
|
||||
if (horizontal) {
|
||||
const clickX = e.clientX - rect.left;
|
||||
const percentage = clickX / rect.width;
|
||||
container.scrollTo({
|
||||
left: percentage * (container.scrollWidth - container.clientWidth),
|
||||
behavior: 'smooth'
|
||||
});
|
||||
} else {
|
||||
const clickY = e.clientY - rect.top;
|
||||
const percentage = clickY / rect.height;
|
||||
container.scrollTo({
|
||||
top: percentage * (container.scrollHeight - container.clientHeight),
|
||||
behavior: 'smooth'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const isActive = isHoveringTrack || isDragging;
|
||||
const opacity = isActive ? 0.6 : (isHoveringContainer ? 0.2 : 0);
|
||||
const size = isActive ? '6px' : '2px';
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`relative ${className}`}
|
||||
onMouseEnter={() => setIsHoveringContainer(true)}
|
||||
onMouseLeave={() => {
|
||||
setIsHoveringContainer(false);
|
||||
if (!isDragging) setIsHoveringTrack(false);
|
||||
}}
|
||||
>
|
||||
<div
|
||||
ref={containerRef}
|
||||
className="h-full w-full overflow-auto scrollbar-hide"
|
||||
style={{
|
||||
scrollbarWidth: 'none',
|
||||
msOverflowStyle: 'none',
|
||||
overflowX: horizontal ? 'auto' : 'hidden',
|
||||
overflowY: horizontal ? 'hidden' : 'auto'
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={`absolute cursor-pointer z-10 ${horizontal ? 'bottom-0 left-0 right-0 h-4' : 'top-0 right-0 bottom-0 w-4'}`}
|
||||
style={{ opacity }}
|
||||
onMouseEnter={() => setIsHoveringTrack(true)}
|
||||
onMouseLeave={() => { if (!isDragging) setIsHoveringTrack(false); }}
|
||||
onClick={handleTrackClick}
|
||||
>
|
||||
<div
|
||||
ref={thumbRef}
|
||||
className="absolute rounded-full bg-zinc-400 cursor-grab active:cursor-grabbing"
|
||||
style={{
|
||||
[horizontal ? 'left' : 'top']: 0,
|
||||
[horizontal ? 'bottom' : 'right']: '4px',
|
||||
[horizontal ? 'width' : 'height']: thumbSize,
|
||||
[horizontal ? 'height' : 'width']: size,
|
||||
opacity: isActive ? 0.9 : 0.5,
|
||||
transition: 'opacity 0.15s, width 0.15s, height 0.15s',
|
||||
transform: horizontal ? 'translateX(0px)' : 'translateY(0px)',
|
||||
willChange: 'transform'
|
||||
}}
|
||||
onMouseDown={handleThumbMouseDown}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<style>{`
|
||||
.scrollbar-hide::-webkit-scrollbar {
|
||||
display: none !important;
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user