109 lines
4.2 KiB
TypeScript
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;
|