selector almost done
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
		
			
				
	
				Deploy to Cloudflare Pages / deploy (push) Successful in 33s
				
			
		
		
	
	
				
					
				
			
		
			All checks were successful
		
		
	
	Deploy to Cloudflare Pages / deploy (push) Successful in 33s
				
			This commit is contained in:
		
							parent
							
								
									40012a771e
								
							
						
					
					
						commit
						154962ff21
					
				
							
								
								
									
										99
									
								
								app/routes/DragableBox.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								app/routes/DragableBox.tsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,99 @@
 | 
			
		||||
import { useSearchParams } from '@remix-run/react';
 | 
			
		||||
import { useState, useEffect, MouseEvent, useRef } from 'react';
 | 
			
		||||
 | 
			
		||||
interface DraggableBoxProps {
 | 
			
		||||
  tag: string;
 | 
			
		||||
  initialX: number;
 | 
			
		||||
  initialY: number;
 | 
			
		||||
  className?: string;
 | 
			
		||||
  children?: React.ReactNode;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const DraggableBox = ({
 | 
			
		||||
  tag,
 | 
			
		||||
  initialX,
 | 
			
		||||
  initialY,
 | 
			
		||||
  className = "",
 | 
			
		||||
  children = ""
 | 
			
		||||
}: DraggableBoxProps) => {
 | 
			
		||||
  const [isDragging, setIsDragging] = useState(false);
 | 
			
		||||
  const [dragOffset, setDragOffset] = useState({ x: 0, y: 0 });
 | 
			
		||||
  const [searchParams, setSearchParams] = useSearchParams();
 | 
			
		||||
  const dragRef = useRef<HTMLDivElement>(null);
 | 
			
		||||
 | 
			
		||||
  const xParam = `x${tag}`;
 | 
			
		||||
  const yParam = `y${tag}`;
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    if (!searchParams.has(xParam) || !searchParams.has(yParam)) {
 | 
			
		||||
      const newParams = new URLSearchParams(searchParams);
 | 
			
		||||
      newParams.set(xParam, initialX.toString());
 | 
			
		||||
      newParams.set(yParam, initialY.toString());
 | 
			
		||||
      setSearchParams(newParams, { replace: true });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!isDragging) return;
 | 
			
		||||
 | 
			
		||||
    const handleMouseMove = (e: globalThis.MouseEvent) => {
 | 
			
		||||
      if (!dragRef.current?.parentElement) return;
 | 
			
		||||
 | 
			
		||||
      const parent = dragRef.current.parentElement;
 | 
			
		||||
      const parentRect = parent.getBoundingClientRect();
 | 
			
		||||
      const boxRect = dragRef.current.getBoundingClientRect();
 | 
			
		||||
 | 
			
		||||
      const relativeX = e.clientX - parentRect.left - dragOffset.x;
 | 
			
		||||
      const relativeY = e.clientY - parentRect.top - dragOffset.y;
 | 
			
		||||
 | 
			
		||||
      const maxX = parentRect.width - boxRect.width;
 | 
			
		||||
      const maxY = parentRect.height - boxRect.height;
 | 
			
		||||
 | 
			
		||||
      const newX = Math.max(0, Math.min(relativeX, maxX));
 | 
			
		||||
      const newY = Math.max(0, Math.min(relativeY, maxY));
 | 
			
		||||
 | 
			
		||||
      const newParams = new URLSearchParams(searchParams);
 | 
			
		||||
      newParams.set(xParam, newX.toString());
 | 
			
		||||
      newParams.set(yParam, newY.toString());
 | 
			
		||||
      setSearchParams(newParams, { replace: true });
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const handleMouseUp = () => setIsDragging(false);
 | 
			
		||||
 | 
			
		||||
    window.addEventListener('mousemove', handleMouseMove);
 | 
			
		||||
    window.addEventListener('mouseup', handleMouseUp);
 | 
			
		||||
 | 
			
		||||
    return () => {
 | 
			
		||||
      window.removeEventListener('mousemove', handleMouseMove);
 | 
			
		||||
      window.removeEventListener('mouseup', handleMouseUp);
 | 
			
		||||
    };
 | 
			
		||||
  }, [isDragging, dragOffset, tag, searchParams, initialX, initialY]);
 | 
			
		||||
 | 
			
		||||
  const handleMouseDown = (e: MouseEvent) => {
 | 
			
		||||
    if (!dragRef.current?.parentElement) return;
 | 
			
		||||
 | 
			
		||||
    const parentRect = dragRef.current.parentElement.getBoundingClientRect();
 | 
			
		||||
    const currentX = parseInt(searchParams.get(xParam) || '0');
 | 
			
		||||
    const currentY = parseInt(searchParams.get(yParam) || '0');
 | 
			
		||||
 | 
			
		||||
    setDragOffset({
 | 
			
		||||
      x: e.clientX - parentRect.left - currentX,
 | 
			
		||||
      y: e.clientY - parentRect.top - currentY
 | 
			
		||||
    });
 | 
			
		||||
    setIsDragging(true);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <div
 | 
			
		||||
      ref={dragRef}
 | 
			
		||||
      className={`absolute cursor-move bg-red-500 rounded-full w-4 h-4 text-white shadow-lg ${className}`}
 | 
			
		||||
      style={{
 | 
			
		||||
        transform: `translate(${searchParams.get(xParam) || 0}px, ${searchParams.get(yParam) || 0}px)`,
 | 
			
		||||
        userSelect: 'none',
 | 
			
		||||
      }}
 | 
			
		||||
      onMouseDown={handleMouseDown}
 | 
			
		||||
    >
 | 
			
		||||
      {children}
 | 
			
		||||
    </div>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default DraggableBox;
 | 
			
		||||
@ -1,13 +1,60 @@
 | 
			
		||||
import type { MetaFunction } from "@remix-run/cloudflare";
 | 
			
		||||
import type { LoaderFunctionArgs, MetaFunction } from "@remix-run/cloudflare";
 | 
			
		||||
import DragableBox from "./DragableBox";
 | 
			
		||||
import { useEffect, useState } from "react";
 | 
			
		||||
import { useLoaderData, useSearchParams } from "@remix-run/react";
 | 
			
		||||
 | 
			
		||||
export const meta: MetaFunction = () => {
 | 
			
		||||
  return [
 | 
			
		||||
    { title: "New Remix App" },
 | 
			
		||||
    { name: "description", content: "Welcome to Remix!" },
 | 
			
		||||
    { title: "Convert images for the web!" },
 | 
			
		||||
    { name: "image converter", content: "Image conversion service" },
 | 
			
		||||
  ];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default function Index() {
 | 
			
		||||
  const [searchParams] = useSearchParams();
 | 
			
		||||
  const [selectorHeight, setSelectorHeight] = useState(0);
 | 
			
		||||
  const [selectorWidth, setSelectorWidth] = useState(0);
 | 
			
		||||
  const [selectorTop, setSelectorTop] = useState(0);
 | 
			
		||||
  const [selectorLeft, setSelectorLeft] = useState(0);
 | 
			
		||||
 | 
			
		||||
  // Parse the values and convert to numbers, using 0 as fallback
 | 
			
		||||
  let x1 = Number(searchParams.get("x1") || 0);
 | 
			
		||||
  let y1 = Number(searchParams.get("y1") || 0);
 | 
			
		||||
  let x2 = Number(searchParams.get("x2") || 0);
 | 
			
		||||
  let y2 = Number(searchParams.get("y2") || 0);
 | 
			
		||||
 | 
			
		||||
  // Use useEffect to calculate dimensions and position
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    // Calculate width
 | 
			
		||||
    const width = Math.abs(x2 - x1);
 | 
			
		||||
    setSelectorWidth(width);
 | 
			
		||||
    
 | 
			
		||||
    // Calculate height
 | 
			
		||||
    const height = Math.abs(y2 - y1);
 | 
			
		||||
    setSelectorHeight(height);
 | 
			
		||||
    
 | 
			
		||||
    // Calculate top position (minimum of y coordinates)
 | 
			
		||||
    setSelectorTop(Math.min(y1, y2));
 | 
			
		||||
    
 | 
			
		||||
    // Calculate left position (minimum of x coordinates)
 | 
			
		||||
    setSelectorLeft(Math.min(x1, x2));
 | 
			
		||||
  }, [x1, x2, y1, y2]);
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <div>Hej Augusta!</div>)
 | 
			
		||||
    <div className="flex h-screen w-screen justify-center items-center">
 | 
			
		||||
      <div className="relative w-1/3 h-1/3 border-black border">
 | 
			
		||||
        <div
 | 
			
		||||
          className="border border-black absolute"
 | 
			
		||||
          style={{
 | 
			
		||||
            top: `${selectorTop}px`,
 | 
			
		||||
            left: `${selectorLeft}px`,
 | 
			
		||||
            height: `${selectorHeight}px`,
 | 
			
		||||
            width: `${selectorWidth}px`,
 | 
			
		||||
          }}
 | 
			
		||||
        />
 | 
			
		||||
        <DragableBox tag="1" initialX={0} initialY={0} />
 | 
			
		||||
        <DragableBox tag="2" initialX={100} initialY={100} />
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										6
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										6
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							@ -21,15 +21,15 @@
 | 
			
		||||
        "@types/react-dom": "^18.2.7",
 | 
			
		||||
        "@typescript-eslint/eslint-plugin": "^6.7.4",
 | 
			
		||||
        "@typescript-eslint/parser": "^6.7.4",
 | 
			
		||||
        "autoprefixer": "^10.4.19",
 | 
			
		||||
        "autoprefixer": "^10.4.20",
 | 
			
		||||
        "eslint": "^8.38.0",
 | 
			
		||||
        "eslint-import-resolver-typescript": "^3.6.1",
 | 
			
		||||
        "eslint-plugin-import": "^2.28.1",
 | 
			
		||||
        "eslint-plugin-jsx-a11y": "^6.7.1",
 | 
			
		||||
        "eslint-plugin-react": "^7.33.2",
 | 
			
		||||
        "eslint-plugin-react-hooks": "^4.6.0",
 | 
			
		||||
        "postcss": "^8.4.38",
 | 
			
		||||
        "tailwindcss": "^3.4.4",
 | 
			
		||||
        "postcss": "^8.4.47",
 | 
			
		||||
        "tailwindcss": "^3.4.14",
 | 
			
		||||
        "typescript": "^5.1.6",
 | 
			
		||||
        "vite": "^5.1.0",
 | 
			
		||||
        "vite-tsconfig-paths": "^4.2.1",
 | 
			
		||||
 | 
			
		||||
@ -28,15 +28,15 @@
 | 
			
		||||
    "@types/react-dom": "^18.2.7",
 | 
			
		||||
    "@typescript-eslint/eslint-plugin": "^6.7.4",
 | 
			
		||||
    "@typescript-eslint/parser": "^6.7.4",
 | 
			
		||||
    "autoprefixer": "^10.4.19",
 | 
			
		||||
    "autoprefixer": "^10.4.20",
 | 
			
		||||
    "eslint": "^8.38.0",
 | 
			
		||||
    "eslint-import-resolver-typescript": "^3.6.1",
 | 
			
		||||
    "eslint-plugin-import": "^2.28.1",
 | 
			
		||||
    "eslint-plugin-jsx-a11y": "^6.7.1",
 | 
			
		||||
    "eslint-plugin-react": "^7.33.2",
 | 
			
		||||
    "eslint-plugin-react-hooks": "^4.6.0",
 | 
			
		||||
    "postcss": "^8.4.38",
 | 
			
		||||
    "tailwindcss": "^3.4.4",
 | 
			
		||||
    "postcss": "^8.4.47",
 | 
			
		||||
    "tailwindcss": "^3.4.14",
 | 
			
		||||
    "typescript": "^5.1.6",
 | 
			
		||||
    "vite": "^5.1.0",
 | 
			
		||||
    "vite-tsconfig-paths": "^4.2.1",
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user