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 = () => {
 | 
					export const meta: MetaFunction = () => {
 | 
				
			||||||
  return [
 | 
					  return [
 | 
				
			||||||
    { title: "New Remix App" },
 | 
					    { title: "Convert images for the web!" },
 | 
				
			||||||
    { name: "description", content: "Welcome to Remix!" },
 | 
					    { name: "image converter", content: "Image conversion service" },
 | 
				
			||||||
  ];
 | 
					  ];
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function Index() {
 | 
					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 (
 | 
					  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",
 | 
					        "@types/react-dom": "^18.2.7",
 | 
				
			||||||
        "@typescript-eslint/eslint-plugin": "^6.7.4",
 | 
					        "@typescript-eslint/eslint-plugin": "^6.7.4",
 | 
				
			||||||
        "@typescript-eslint/parser": "^6.7.4",
 | 
					        "@typescript-eslint/parser": "^6.7.4",
 | 
				
			||||||
        "autoprefixer": "^10.4.19",
 | 
					        "autoprefixer": "^10.4.20",
 | 
				
			||||||
        "eslint": "^8.38.0",
 | 
					        "eslint": "^8.38.0",
 | 
				
			||||||
        "eslint-import-resolver-typescript": "^3.6.1",
 | 
					        "eslint-import-resolver-typescript": "^3.6.1",
 | 
				
			||||||
        "eslint-plugin-import": "^2.28.1",
 | 
					        "eslint-plugin-import": "^2.28.1",
 | 
				
			||||||
        "eslint-plugin-jsx-a11y": "^6.7.1",
 | 
					        "eslint-plugin-jsx-a11y": "^6.7.1",
 | 
				
			||||||
        "eslint-plugin-react": "^7.33.2",
 | 
					        "eslint-plugin-react": "^7.33.2",
 | 
				
			||||||
        "eslint-plugin-react-hooks": "^4.6.0",
 | 
					        "eslint-plugin-react-hooks": "^4.6.0",
 | 
				
			||||||
        "postcss": "^8.4.38",
 | 
					        "postcss": "^8.4.47",
 | 
				
			||||||
        "tailwindcss": "^3.4.4",
 | 
					        "tailwindcss": "^3.4.14",
 | 
				
			||||||
        "typescript": "^5.1.6",
 | 
					        "typescript": "^5.1.6",
 | 
				
			||||||
        "vite": "^5.1.0",
 | 
					        "vite": "^5.1.0",
 | 
				
			||||||
        "vite-tsconfig-paths": "^4.2.1",
 | 
					        "vite-tsconfig-paths": "^4.2.1",
 | 
				
			||||||
 | 
				
			|||||||
@ -28,15 +28,15 @@
 | 
				
			|||||||
    "@types/react-dom": "^18.2.7",
 | 
					    "@types/react-dom": "^18.2.7",
 | 
				
			||||||
    "@typescript-eslint/eslint-plugin": "^6.7.4",
 | 
					    "@typescript-eslint/eslint-plugin": "^6.7.4",
 | 
				
			||||||
    "@typescript-eslint/parser": "^6.7.4",
 | 
					    "@typescript-eslint/parser": "^6.7.4",
 | 
				
			||||||
    "autoprefixer": "^10.4.19",
 | 
					    "autoprefixer": "^10.4.20",
 | 
				
			||||||
    "eslint": "^8.38.0",
 | 
					    "eslint": "^8.38.0",
 | 
				
			||||||
    "eslint-import-resolver-typescript": "^3.6.1",
 | 
					    "eslint-import-resolver-typescript": "^3.6.1",
 | 
				
			||||||
    "eslint-plugin-import": "^2.28.1",
 | 
					    "eslint-plugin-import": "^2.28.1",
 | 
				
			||||||
    "eslint-plugin-jsx-a11y": "^6.7.1",
 | 
					    "eslint-plugin-jsx-a11y": "^6.7.1",
 | 
				
			||||||
    "eslint-plugin-react": "^7.33.2",
 | 
					    "eslint-plugin-react": "^7.33.2",
 | 
				
			||||||
    "eslint-plugin-react-hooks": "^4.6.0",
 | 
					    "eslint-plugin-react-hooks": "^4.6.0",
 | 
				
			||||||
    "postcss": "^8.4.38",
 | 
					    "postcss": "^8.4.47",
 | 
				
			||||||
    "tailwindcss": "^3.4.4",
 | 
					    "tailwindcss": "^3.4.14",
 | 
				
			||||||
    "typescript": "^5.1.6",
 | 
					    "typescript": "^5.1.6",
 | 
				
			||||||
    "vite": "^5.1.0",
 | 
					    "vite": "^5.1.0",
 | 
				
			||||||
    "vite-tsconfig-paths": "^4.2.1",
 | 
					    "vite-tsconfig-paths": "^4.2.1",
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user