nguhmap/src/App.tsx
2025-09-17 12:15:13 +02:00

109 lines
4.2 KiB
TypeScript

import { MapContainer, Marker, Rectangle, TileLayer, Tooltip } from "react-leaflet";
import L from "leaflet";
import { useCallback, useState } from "react";
import Modal from 'react-modal';
import "./index.css";
type SetState<T> = React.Dispatch<React.SetStateAction<T>>;
type StateInit<T> = [T, SetState<T>];
function Login(params: { modal: StateInit<boolean>, login: StateInit<string | undefined>; }) {
const {modal, login} = params;
const customStyles = {
content: {
top: '50%',
left: '50%',
right: 'auto',
bottom: 'auto',
marginRight: '-50%',
transform: 'translate(-50%, -50%)',
},
};
const openModal = () => modal[1](true)
const afterOpenModal = () => { }
const closeModal = () => modal[1](false)
return (<>
<button type="button" onClick={openModal}>Log In</button>
<Modal
isOpen={modal[0]}
onAfterOpen={afterOpenModal}
onRequestClose={closeModal}
style={customStyles}
contentLabel="Log In"
ariaHideApp={false}
>
<h2>Authentification</h2>
<form action={(e : FormData) => {
// TODO Check Auth
const l = e.get("login")
if (l == null) return;
closeModal()
login[1](l.toString())
}}>
<input name="login" type="text" placeholder="Login" required={true} /><br />
<input name="password" type="password" placeholder="Password" required={true} /><br />
<button id="auth!submit" type="submit">Log In</button>
</form>
</Modal>
</>)
}
function Toolbar(params: { logged_in: [boolean,] }) {
}
export function App() {
const [modalIsOpen, setModalIsOpen]: StateInit<boolean> = useState(false);
const [coords, setCoords]: StateInit<{ x: number; y: number }> = useState({ x: 0, y: 0 })
const [nextLabel, setNextLabel]: StateInit<string> = useState("")
const [currentZoom, setCurrentZoom]: StateInit<number> = useState(0)
const [markers, setMarkers]: StateInit<{ label: string; x: number; y: number }[]> = useState([
{ label: "Spawn", x: 0, y: 0 }
])
const [login, setLogin]: StateInit<string | undefined> = useState()
let map = useCallback((it: L.Map | null) => {
if (it == null) return;
it.on('mousemove', (e) => { setCoords({ x: Math.round(e.latlng.lng), y: Math.round(e.latlng.lat) }) })
it.on('zoomend', (_) => setCurrentZoom(it.getZoom()))
it.on('click', (e) => setMarkers([...markers, { label: nextLabel, x: Math.round(e.latlng.lng), y: Math.round(e.latlng.lat) }]))
}, [coords, markers, nextLabel])
return (<>{ (!modalIsOpen) ?
<MapContainer
className={(login) ? "edit" : "fullscreen"}
id="map"
center={[0, 0]}
maxBounds={[[-8192, -8192], [8192, 8192]]}
crs={L.CRS.Simple}
zoom={0}
ref={map}>
<TileLayer
url="/static/tiles/{z}/{x}_{y}.png"
tileSize={512}
minZoom={-4}
maxNativeZoom={0} />
{(currentZoom > -2) ? markers.map((it, i) => (
<Marker key={i} position={[it.y, it.x]} icon={new L.Icon({ iconUrl: "/static/legend/city.svg", iconAnchor: [6, 6] })}>
<Tooltip>{it.label}</Tooltip>
</Marker>
)) : null
}
<Rectangle bounds={[[-8000, 8000], [8000, 8000]]} pathOptions={{ color: "#000", stroke: true, fill: false, weight: 1 }} />
</MapContainer> : <></> } {
(login) ?
<>
<p>Mouse is at {coords.x}, {-coords.y}</p>
<label htmlFor="nextmarker">Next Marker Label</label>
<input id="nextmarker" type="text" onChange={(e) => setNextLabel(e.target.value)} value={nextLabel} />
<p>Hello {login}!</p>
<button type="button" onClick={() => setLogin(undefined)}>Log out</button>
</>
: <Login login={[login,setLogin]} modal={[modalIsOpen, setModalIsOpen]} />}
</>)
}
export default App;