Verwobenheit: Farb-Konfigurator

Hier erscheint dein präziser Prompt...
import React, { useMemo, useState } from "react"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"; import { Undo2, MousePointer2, Route, Home, Type, Trash2 } from "lucide-react"; const svgWidth = 1200; const svgHeight = 900; const roadX = 500; const roadTop = 80; const roadBottom = 820; const houseWidth = 34; const houseHeight = 90; const sideRoadLength = 70; const labelGap = 18; const houseGap = 18; const clickSnap = 10; function snap(v) { return Math.round(v / clickSnap) * clickSnap; } function uid() { return Math.random().toString(36).slice(2, 10); } export default function OstertorsteinwegZeichenprogramm() { const [tool, setTool] = useState("side-road"); const [roadName, setRoadName] = useState("Ostertorsteinweg"); const [sideRoads, setSideRoads] = useState([]); const [houses, setHouses] = useState([]); const [history, setHistory] = useState([]); const pushHistory = () => { setHistory((h) => [ ...h, { sideRoads: JSON.parse(JSON.stringify(sideRoads)), houses: JSON.parse(JSON.stringify(houses)), }, ]); }; const undo = () => { setHistory((h) => { if (!h.length) return h; const prev = h[h.length - 1]; setSideRoads(prev.sideRoads); setHouses(prev.houses); return h.slice(0, -1); }); }; const clearAll = () => { pushHistory(); setSideRoads([]); setHouses([]); }; const occupiedBands = useMemo(() => { return { sideRoads: sideRoads.map((s) => s.y), houses: houses.map((h) => h.y), }; }, [sideRoads, houses]); const handleSvgClick = (e) => { const rect = e.currentTarget.getBoundingClientRect(); const scaleX = svgWidth / rect.width; const scaleY = svgHeight / rect.height; const x = (e.clientX - rect.left) * scaleX; const y = snap((e.clientY - rect.top) * scaleY); if (y < roadTop + 10 || y > roadBottom - 10) return; if (tool === "side-road") { const side = x >= roadX ? "right" : "left"; pushHistory(); setSideRoads((prev) => [ ...prev, { id: uid(), y, side, name: "", editing: true }, ]); return; } if (tool === "house") { const side = x >= roadX ? "right" : "left"; pushHistory(); setHouses((prev) => [ ...prev, { id: uid(), y, side, number: "", editing: true }, ]); } }; const updateSideRoad = (id, patch) => { setSideRoads((prev) => prev.map((s) => (s.id === id ? { ...s, ...patch } : s))); }; const updateHouse = (id, patch) => { setHouses((prev) => prev.map((h) => (h.id === id ? { ...h, ...patch } : h))); }; const deleteSideRoad = (id) => { pushHistory(); setSideRoads((prev) => prev.filter((s) => s.id !== id)); }; const deleteHouse = (id) => { pushHistory(); setHouses((prev) => prev.filter((h) => h.id !== id)); }; const sortedSideRoads = [...sideRoads].sort((a, b) => a.y - b.y); const sortedHouses = [...houses].sort((a, b) => a.y - b.y); return (
Zeichenprogramm
setRoadName(e.target.value)} />
Werkzeug
v && setTool(v)} className="grid grid-cols-2 gap-2" > Auswahl Nebenstraße Haus
Klicke in die Zeichenfläche.
Nebenstraße: Es erscheint automatisch ein Strich und ein Eingabefeld für den Namen.
Haus: Es erscheint automatisch ein Rechteck und ein Eingabefeld für die Hausnummer.
Mit Enter verschwindet das jeweilige Eingabefeld.
Nebenstraßen
{sortedSideRoads.length === 0 ? (
Noch keine Nebenstraßen.
) : ( sortedSideRoads.map((s) => (
{s.name || "Ohne Namen"}
{s.side === "left" ? "links" : "rechts"} · y {s.y}
)) )}
Häuser
{sortedHouses.length === 0 ? (
Noch keine Häuser.
) : ( sortedHouses.map((h) => (
{h.number || "Ohne Nummer"}
{h.side === "left" ? "links" : "rechts"} · y {h.y}
)) )}
Zeichenfläche
{roadName} {sortedSideRoads.map((s) => { const x2 = s.side === "left" ? roadX - sideRoadLength : roadX + sideRoadLength; const labelX = s.side === "left" ? x2 - labelGap : x2 + labelGap; const textAnchor = s.side === "left" ? "end" : "start"; const inputX = s.side === "left" ? labelX - 140 : labelX; return ( {s.name} {s.editing && ( updateSideRoad(s.id, { name: e.target.value })} onKeyDown={(e) => { if (e.key === "Enter") updateSideRoad(s.id, { editing: false }); }} style={{ width: "136px", height: "30px", border: "1px solid #999", borderRadius: "6px", padding: "4px 8px", fontSize: "14px", background: "white", }} /> )} ); })} {sortedHouses.map((h) => { const x = h.side === "left" ? roadX - houseGap - houseWidth : roadX + houseGap; const inputX = x; return ( {h.number} {h.editing && ( updateHouse(h.id, { number: e.target.value })} onKeyDown={(e) => { if (e.key === "Enter") updateHouse(h.id, { editing: false }); }} style={{ width: `${houseWidth - 8}px`, height: "24px", border: "1px solid #999", borderRadius: "4px", padding: "2px 6px", fontSize: "13px", textAlign: "center", background: "white", }} /> )} ); })}
); }