| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152 |
- import { ref } from 'vue';
- interface DragOptions {
- max: number;
- min: number;
- }
- interface DragElements {
- dragBar: HTMLElement | null;
- target: HTMLElement | null;
- }
- type DragCallback = (newWidth: number) => void;
- export function useSidebarDrag() {
- const isDragging = ref(false);
- let cleanup: (() => void) | null = null;
- let dragOverlay: HTMLElement | null = null;
- const startDrag = (
- e: MouseEvent,
- options: DragOptions,
- elements: DragElements,
- onDrag: DragCallback,
- ) => {
- const { min, max } = options;
- const { dragBar, target } = elements;
- if (isDragging.value || !dragBar || !target) return;
- e.preventDefault();
- e.stopPropagation();
- isDragging.value = true;
- const startX = e.clientX;
- const startWidth = target.getBoundingClientRect().width;
- const startLeft = dragBar.offsetLeft;
- dragBar.classList.add('bg-primary');
- dragBar.classList.remove('bg-primary/30');
- const dragBarTransition = dragBar.style.transition;
- const targetTransition = target.style.transition;
- dragBar.style.transition = 'none';
- target.style.transition = 'none';
- dragOverlay = document.createElement('div');
- dragOverlay.style.position = 'fixed';
- dragOverlay.style.inset = '0';
- dragOverlay.style.zIndex = '9999';
- dragOverlay.style.cursor = 'col-resize';
- dragOverlay.style.userSelect = 'none';
- dragOverlay.style.outline = 'none';
- dragOverlay.tabIndex = -1;
- dragOverlay.style.background = 'rgba(0,0,0,0)';
- document.body.append(dragOverlay);
- const onMouseMove = (moveEvent: MouseEvent) => {
- if (!isDragging.value || !dragBar || !target) {
- endDrag();
- return;
- }
- const deltaX = moveEvent.clientX - startX;
- let currentWidth = startWidth + deltaX;
- const isOutOfMin = currentWidth < min;
- const isOutOfMax = currentWidth > max;
- const isOutOfBounds = isOutOfMin || isOutOfMax;
- if (isOutOfMin) currentWidth = min;
- if (isOutOfMax) currentWidth = max;
- const newLeft = startLeft + (currentWidth - startWidth);
- if (dragOverlay)
- dragOverlay.style.cursor = isOutOfBounds ? 'not-allowed' : 'col-resize';
- dragBar.style.left = `${newLeft}px`;
- if (isOutOfBounds) {
- dragBar.classList.add('bg-primary/30');
- dragBar.classList.remove('bg-primary');
- } else {
- dragBar.classList.add('bg-primary');
- dragBar.classList.remove('bg-primary/30');
- }
- };
- const onMouseUp = (upEvent: MouseEvent) => {
- if (!isDragging.value || !dragBar || !target) {
- endDrag();
- return;
- }
- const deltaX = upEvent.clientX - startX;
- let newWidth = startWidth + deltaX;
- newWidth = Math.min(max, Math.max(min, newWidth));
- dragBar.classList.remove('bg-primary', 'bg-primary/30');
- try {
- onDrag?.(Math.round(newWidth));
- } finally {
- endDrag();
- }
- };
- document.addEventListener('mousemove', onMouseMove);
- document.addEventListener('mouseup', onMouseUp);
- cleanup = () => {
- if (!cleanup) return;
- document.removeEventListener('mousemove', onMouseMove);
- document.removeEventListener('mouseup', onMouseUp);
- if (dragBar) {
- dragBar.style.transition = dragBarTransition;
- dragBar.style.left = '';
- dragBar.classList.remove('bg-primary', 'bg-primary/30');
- }
- if (target) {
- target.style.transition = targetTransition;
- }
- if (dragOverlay) {
- dragOverlay.remove();
- dragOverlay = null;
- }
- isDragging.value = false;
- cleanup = null;
- };
- };
- const endDrag = () => {
- cleanup?.();
- };
- return {
- startDrag,
- endDrag,
- get isDragging() {
- return isDragging.value;
- },
- };
- }
|