diff --git a/app/routes/DragableBox.tsx b/app/routes/DragableBox.tsx new file mode 100644 index 0000000..fa53727 --- /dev/null +++ b/app/routes/DragableBox.tsx @@ -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(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 ( +
+ {children} +
+ ); +}; + +export default DraggableBox; diff --git a/app/routes/_index.tsx b/app/routes/_index.tsx index d5b7f5c..8304bd2 100644 --- a/app/routes/_index.tsx +++ b/app/routes/_index.tsx @@ -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 ( -
Hej Augusta!
) +
+
+
+ + +
+
+ ); } diff --git a/package-lock.json b/package-lock.json index 41706ab..e093b5c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index 6a6afae..a5368fa 100644 --- a/package.json +++ b/package.json @@ -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",