Added UI for log
This commit is contained in:
@ -22,5 +22,3 @@ bun devscripts/optimise_assets.ts threadcount
|
||||
bun build
|
||||
bun start
|
||||
```
|
||||
|
||||
This project was created using `bun init` in bun v1.2.21. [Bun](https://bun.com) is a fast all-in-one JavaScript runtime.
|
||||
|
22
bun.lock
22
bun.lock
@ -6,10 +6,12 @@
|
||||
"dependencies": {
|
||||
"@types/leaflet": "^1.9.20",
|
||||
"@types/react-leaflet": "^3.0.0",
|
||||
"@types/react-modal": "^3.16.3",
|
||||
"leaflet": "^1.9.4",
|
||||
"react": "^19",
|
||||
"react-dom": "^19",
|
||||
"react-leaflet": "^5.0.0",
|
||||
"react-modal": "^3.16.3",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bun": "latest",
|
||||
@ -35,20 +37,40 @@
|
||||
|
||||
"@types/react-leaflet": ["@types/react-leaflet@3.0.0", "", { "dependencies": { "react-leaflet": "*" } }, "sha512-p8R9mVKbCDDqOdW+M6GyJJuFn6q+IgDFYavFiOIvaWHuOe5kIHZEtCy1pfM43JIA6JiB3D/aDoby7C51eO+XSg=="],
|
||||
|
||||
"@types/react-modal": ["@types/react-modal@3.16.3", "", { "dependencies": { "@types/react": "*" } }, "sha512-xXuGavyEGaFQDgBv4UVm8/ZsG+qxeQ7f77yNrW3n+1J6XAstUy5rYHeIHPh1KzsGc6IkCIdu6lQ2xWzu1jBTLg=="],
|
||||
|
||||
"bun-types": ["bun-types@1.2.21", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-sa2Tj77Ijc/NTLS0/Odjq/qngmEPZfbfnOERi0KRUYhT9R8M4VBioWVmMWE5GrYbKMc+5lVybXygLdibHaqVqw=="],
|
||||
|
||||
"csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
|
||||
|
||||
"exenv": ["exenv@1.2.2", "", {}, "sha512-Z+ktTxTwv9ILfgKCk32OX3n/doe+OcLTRtqK9pcL+JsP3J1/VW8Uvl4ZjLlKqeW4rzK4oesDOGMEMRIZqtP4Iw=="],
|
||||
|
||||
"js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
|
||||
|
||||
"leaflet": ["leaflet@1.9.4", "", {}, "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA=="],
|
||||
|
||||
"loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="],
|
||||
|
||||
"object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="],
|
||||
|
||||
"prop-types": ["prop-types@15.8.1", "", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="],
|
||||
|
||||
"react": ["react@19.1.1", "", {}, "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ=="],
|
||||
|
||||
"react-dom": ["react-dom@19.1.1", "", { "dependencies": { "scheduler": "^0.26.0" }, "peerDependencies": { "react": "^19.1.1" } }, "sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw=="],
|
||||
|
||||
"react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],
|
||||
|
||||
"react-leaflet": ["react-leaflet@5.0.0", "", { "dependencies": { "@react-leaflet/core": "^3.0.0" }, "peerDependencies": { "leaflet": "^1.9.0", "react": "^19.0.0", "react-dom": "^19.0.0" } }, "sha512-CWbTpr5vcHw5bt9i4zSlPEVQdTVcML390TjeDG0cK59z1ylexpqC6M1PJFjV8jD7CF+ACBFsLIDs6DRMoLEofw=="],
|
||||
|
||||
"react-lifecycles-compat": ["react-lifecycles-compat@3.0.4", "", {}, "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="],
|
||||
|
||||
"react-modal": ["react-modal@3.16.3", "", { "dependencies": { "exenv": "^1.2.0", "prop-types": "^15.7.2", "react-lifecycles-compat": "^3.0.0", "warning": "^4.0.3" }, "peerDependencies": { "react": "^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18 || ^19", "react-dom": "^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18 || ^19" } }, "sha512-yCYRJB5YkeQDQlTt17WGAgFJ7jr2QYcWa1SHqZ3PluDmnKJ/7+tVU+E6uKyZ0nODaeEj+xCpK4LcSnKXLMC0Nw=="],
|
||||
|
||||
"scheduler": ["scheduler@0.26.0", "", {}, "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA=="],
|
||||
|
||||
"undici-types": ["undici-types@7.11.0", "", {}, "sha512-kt1ZriHTi7MU+Z/r9DOdAI3ONdaR3M3csEaRc6ewa4f4dTvX4cQCbJ4NkEn0ohE4hHtq85+PhPSTY+pO/1PwgA=="],
|
||||
|
||||
"warning": ["warning@4.0.3", "", { "dependencies": { "loose-envify": "^1.0.0" } }, "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w=="],
|
||||
}
|
||||
}
|
||||
|
@ -11,10 +11,12 @@
|
||||
"dependencies": {
|
||||
"@types/leaflet": "^1.9.20",
|
||||
"@types/react-leaflet": "^3.0.0",
|
||||
"@types/react-modal": "^3.16.3",
|
||||
"leaflet": "^1.9.4",
|
||||
"react": "^19",
|
||||
"react-dom": "^19",
|
||||
"react-leaflet": "^5.0.0"
|
||||
"react-leaflet": "^5.0.0",
|
||||
"react-modal": "^3.16.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^19",
|
||||
|
134
src/App.tsx
134
src/App.tsx
@ -1,46 +1,108 @@
|
||||
import { MapContainer, Marker, TileLayer, Tooltip } from "react-leaflet";
|
||||
import L, { marker } from "leaflet";
|
||||
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>];
|
||||
|
||||
export function App() {
|
||||
const [coords, set_coords] = useState({x:0, y:0})
|
||||
const [next_label, set_next_label] = useState("")
|
||||
const [markers, set_markers] = useState([
|
||||
{label: "Spawn", x: 0, y: 0}
|
||||
])
|
||||
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%)',
|
||||
},
|
||||
};
|
||||
|
||||
let map = useCallback((it: L.Map|null) => {
|
||||
if (it == null) return;
|
||||
it.on('mousemove', (e) => {set_coords({x: Math.round(e.latlng.lng), y: Math.round(e.latlng.lat)})})
|
||||
it.on('click', (e) => set_markers([...markers, {label: next_label, x: Math.round(e.latlng.lng), y: Math.round(e.latlng.lat)}]))
|
||||
}, [coords, markers, next_label])
|
||||
|
||||
const openModal = () => modal[1](true)
|
||||
const afterOpenModal = () => { }
|
||||
const closeModal = () => modal[1](false)
|
||||
return (<>
|
||||
<MapContainer id="map"
|
||||
center={[0,0]}
|
||||
maxBounds={[[-8192,-8192], [8192,8192]]}
|
||||
crs={L.CRS.Simple}
|
||||
zoom={0}
|
||||
ref={map}>
|
||||
<TileLayer
|
||||
url="/tiles/{z}/{x}/{y}"
|
||||
tileSize={512}
|
||||
minZoom={-4}
|
||||
maxNativeZoom={0}/>
|
||||
{
|
||||
markers.map((it, i) => (
|
||||
<Marker key={i} position={[it.y, it.x]} icon={new L.Icon({iconUrl: "/legend/city.svg", iconAnchor: [8,8]})}>
|
||||
<Tooltip>{it.label}</Tooltip>
|
||||
</Marker>
|
||||
))
|
||||
}
|
||||
</MapContainer>
|
||||
<p>Mouse is at {coords.x}, {-coords.y}</p>
|
||||
<label htmlFor="nextmarker">Next Marker Label</label>
|
||||
<input id="nextmarker" type="text" onChange={(e) => set_next_label(e.target.value)} value={next_label} />
|
||||
<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;
|
||||
|
@ -1,21 +1,18 @@
|
||||
import { serve, file, type BunRequest } from "bun";
|
||||
import index from "./index.html";
|
||||
import { statSync, } from "node:fs";
|
||||
|
||||
const server = serve({
|
||||
routes: {
|
||||
// Serve index.html for all unmatched routes.
|
||||
"/": index,
|
||||
"/tiles/:z/:x/:y": (req : BunRequest<"/tiles/:z/:x/:y">) => {
|
||||
const { z, x, y} = req.params;
|
||||
return new Response(file(`./static/tiles/${z}/${x}_${y}.png`), {
|
||||
headers: {
|
||||
"Content-Type": "image/png"
|
||||
}
|
||||
})
|
||||
},
|
||||
"/legend/:item": (req: BunRequest<"/legend/:item">) => {
|
||||
const {item} = req.params
|
||||
return new Response(file(`./static/legend/${item}`))
|
||||
"/static/*": (req : BunRequest<"/static/*">) => {
|
||||
const path = "." + new URL(req.url).pathname
|
||||
if (statSync(path).isFile()) {
|
||||
return new Response(file(path))
|
||||
} else {
|
||||
return new Response(`${path} not found`, {status: 404})
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -1,11 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="16px"
|
||||
height="16px"
|
||||
viewBox="0 0 16 16"
|
||||
width="12px"
|
||||
height="12x"
|
||||
viewBox="0 0 12 12"
|
||||
version="1.1"
|
||||
>
|
||||
<circle style="fill: #000" r="8" transform="translate(8,8)" />
|
||||
<circle style="fill: #fff" r="6" transform="translate(8,8)" />
|
||||
<rect style="stroke-width:2px;stroke:#000;fill:#fff" x="1" y="1" width="10" height="10"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 311 B After Width: | Height: | Size: 274 B |
Reference in New Issue
Block a user