@servicetitan/assist-ui
Version:
ServiceTitan Atlas UI Components
121 lines (120 loc) • 3.89 kB
JavaScript
import { useCallback, useEffect, useState } from 'react';
export const useDraggable = ({ initialX = window.innerWidth - 390, initialY = 64, minVisibleHorizontal = 390, minVisibleVertical = 56, width = 390 } = {})=>{
const [position, setPosition] = useState({
x: initialX,
y: initialY
});
const [isDragging, setIsDragging] = useState(false);
const [dragOffset, setDragOffset] = useState({
x: 0,
y: 0
});
// Function to ensure position is within viewport boundaries
const ensurePositionInViewport = useCallback((pos)=>{
const maxX = window.innerWidth - minVisibleHorizontal;
const minX = -width + minVisibleHorizontal;
const maxY = window.innerHeight - minVisibleVertical;
return {
x: Math.min(Math.max(pos.x, minX), maxX),
y: Math.min(Math.max(pos.y, minVisibleVertical), maxY)
};
}, [
minVisibleHorizontal,
minVisibleVertical,
width
]);
// Function to recalculate initial position based on current viewport
const recalculateInitialPosition = useCallback(()=>{
const newInitialX = window.innerWidth - width;
return {
x: newInitialX,
y: initialY
};
}, [
width,
initialY
]);
const resetPosition = useCallback(()=>{
const newInitialPos = recalculateInitialPosition();
setPosition(newInitialPos);
}, [
recalculateInitialPosition
]);
const handleMouseDown = useCallback((e)=>{
e.preventDefault();
setIsDragging(true);
setDragOffset({
x: e.clientX - position.x,
y: e.clientY - position.y
});
}, [
position
]);
const handleMouseMove = useCallback((e)=>{
if (!isDragging) {
return;
}
const newX = e.clientX - dragOffset.x;
const newY = e.clientY - dragOffset.y;
const constrainedPosition = ensurePositionInViewport({
x: newX,
y: newY
});
setPosition(constrainedPosition);
}, [
isDragging,
dragOffset,
ensurePositionInViewport
]);
const handleMouseUp = useCallback(()=>{
setIsDragging(false);
}, []);
// Handle viewport resize
const handleResize = useCallback(()=>{
const constrainedPosition = ensurePositionInViewport(position);
setPosition(constrainedPosition);
}, [
position,
ensurePositionInViewport
]);
useEffect(()=>{
if (isDragging) {
// Prevent text selection while dragging
document.body.style.userSelect = 'none';
document.body.style.webkitUserSelect = 'none';
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
document.addEventListener('mousemove', handleMouseMove);
document.addEventListener('mouseup', handleMouseUp);
}
return ()=>{
// Restore text selection
document.body.style.userSelect = '';
document.body.style.webkitUserSelect = '';
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
document.removeEventListener('mousemove', handleMouseMove);
document.removeEventListener('mouseup', handleMouseUp);
};
}, [
isDragging,
handleMouseMove,
handleMouseUp
]);
// Add resize listener
useEffect(()=>{
window.addEventListener('resize', handleResize);
return ()=>{
window.removeEventListener('resize', handleResize);
};
}, [
handleResize
]);
return {
position,
isDragging,
handleMouseDown,
resetPosition
};
};
//# sourceMappingURL=use-draggable.js.map