add skeleton for most UI elements, implement cirlces

This commit is contained in:
Iris Lightshard 2022-08-13 15:40:38 -06:00
parent cd2f0ab2ec
commit d60cf73184
Signed by: Iris Lightshard
GPG key ID: 3B7FBC22144E6398
10 changed files with 302 additions and 53 deletions

View file

@ -2,7 +2,7 @@
## about
`onyx/scry` is a lightweight map annotation and location data management and sharing tool built using [leaflet](https://leafletjs.org) and [typescript](https://typescriptlang.org). It is intended as a standalone tool to generate, manage, and share simple location data in the form of points, circles, and polygons. All of these have associated titles and descriptions and can be easily imported or exported to a `json` format for easy sharing. All data is saved locally via the `localStorage` API, and the only network calls are those to retrieve either the streetmap or satellite tile data.
`onyx/scry` is a lightweight map annotation and location data management and sharing tool built using [leaflet](https://leafletjs.com) and [typescript](https://typescriptlang.org). It is intended as a standalone tool to generate, manage, and share simple location data in the form of points, circles, and polygons. All of these have associated titles and descriptions and can be easily imported or exported to a `json` format for easy sharing. All data is saved locally via the `localStorage` API, and the only network calls are those to retrieve either the streetmap or satellite tile data.
## usage

View file

@ -11,6 +11,7 @@
<body>
<div id="map"></div>
<div id="mapControls">
<button id="addPoint-btn">&middot;</button>
<button id="addCircle-btn">&compfn;</button>
@ -29,6 +30,32 @@
<div id="modal-content">
</div>
</div>
<div id="cancel-container">
<button class="cancel-btn">Cancel</button>
</div>
<div id="confirm-container">
<span id="confirm-msg"></span>
<button id="yes-btn">Yes</button>
<button id="no-btn">No</button>
</div>
<div id="import-export-container">
<h2></h2>
<textarea id="import-export-textarea"></textarea>
<button id="import-export-ok-btn">OK</button>
<button id="import-export-cancel-btn">Cancel</button>
</div>
<div id="overlays-menu-container">
<details id="markers-wrapper"><summary>Markers</summary><ul id="markers-list"></ul></details>
<details id="circles-wrapper"><summary>Circles</summary><ul id="circles-list"></ul></details>
<details id="polygons-wrapper"><summary>Polygons</summary><ul id="polygons-list"></ul></details>
<button id="import-btn">Import</button>
<button id="export-all-btn">Export All</button>
</div>
</body>
<link rel='stylesheet' type="text/css" href="/static/leaflet.css">
<script src="/static/leaflet.js"></script>

View file

@ -1,5 +1,5 @@
10-overlay.ts 146
10-overlay.ts 147
11-tilelayer.ts 37
20-modal.ts 78
20-modal.ts 112
30-handlers.ts 2
99-onyx-scry.ts 79
99-onyx-scry.ts 103

View file

@ -70,6 +70,7 @@ class Circle extends OverlayBase {
constructor(name: string, desc: string, point: Point, options: any) {
super(name, desc, [ point ], options);
this.self = L.circle(point, options);
this.self.bindPopup(`<h3>${name}</h3><p>${desc}</p>`);
}
}

View file

@ -24,6 +24,10 @@ class Modal {
return document.getElementById("modal-submitBtn");
}
radiusContainer(): HTMLElement | null {
return document.getElementById("radius-container");
}
nameField(): string {
return (document.getElementById("modal-name") as HTMLInputElement)?.value ?? "";
}
@ -32,6 +36,10 @@ class Modal {
return (document.getElementById("modal-desc") as HTMLInputElement)?.value ?? "";
}
radiusField(): string {
return (document.getElementById("modal-radius") as HTMLInputElement)?.value ?? "";
}
visible(): boolean {
return this.self()?.style.display != "none";
}
@ -45,9 +53,9 @@ class Modal {
setState(state: OverlayType, args: any): void {
const _this = this;
const title = this.title()
switch (state) {
case OverlayType.POINT:
const title = this.title()
if (title) {
title.innerHTML = "Add Marker";
}
@ -71,6 +79,32 @@ class Modal {
break;
case OverlayType.CIRCLE:
if (title) {
title.innerHTML = "Add Circle";
}
fetch("/static/pointModal.html")
.then(r=>r.text())
.then(t=> {
const content = _this.content();
if (content) { content.innerHTML = t; }
const radiusContainer = _this.radiusContainer();
if (radiusContainer) {
radiusContainer.style.display = "block";
}
const submitBtn = _this.submitBtn();
if (submitBtn) {
submitBtn.onclick = () => {
const name = _this.nameField();
const desc = _this.descField();
const radius = _this.radiusField();
const circle = new Circle(name, desc, args.latlng, {radius: Number(radius)});
circle.add(args.map);
args.overlays.circles.push(circle);
_this.setVisible(false);
}
}
});
break;
case OverlayType.POLYGON:
break;

View file

@ -29,7 +29,16 @@ function init(): void {
const modal = new Modal();
const addMarkerHandler = (e: any) => {
const resetMapClick = (): void => {
try {
map.off("click", addMarkerHandler);
} catch {}
try {
map.off("click", addCircleHandler);
} catch {}
}
const addMarkerHandler = (e: any): void => {
modal.setVisible(true);
modal.setState(OverlayType.POINT, {
latlng: e.latlng,
@ -39,34 +48,49 @@ function init(): void {
map.off("click", addMarkerHandler);
}
const addCircleHandler = (e: any): void => {
modal.setVisible(true);
modal.setState(OverlayType.CIRCLE, {
latlng: e.latlng,
map: map,
overlays: overlays,
});
map.off("click", addCircleHandler);
}
const addMarkerBtn = document.getElementById("addPoint-btn");
if (addMarkerBtn) {
addMarkerBtn.onclick = (e: any)=>{
try{
map.off("click", addMarkerHandler);
} finally {
map.on("click", addMarkerHandler);
}
addMarkerBtn.onclick = (e: any): void => {
resetMapClick()
map.on("click", addMarkerHandler);
};
}
const addCircleBtn = document.getElementById("addCircle-btn");
if (addCircleBtn) {
addCircleBtn.onclick = (e: any): void => {
resetMapClick();
map.on("click", addCircleHandler);
}
}
const saveBtn = document.getElementById("save-btn");
if (saveBtn) {
saveBtn.onclick = (e: any) => {
saveBtn.onclick = (e: any): void => {
OverlayState.save(overlays);
};
}
const clearBtn = document.getElementById("clear-btn");
if (clearBtn) {
clearBtn.onclick = (e: any) => {
clearBtn.onclick = (e: any): void => {
overlays = OverlayState.clear(overlays, map);
}
}
const tilesBtn = document.getElementById("tiles-btn");
if (tilesBtn) {
tilesBtn.onclick = (e: any) => {
tilesBtn.onclick = (e: any): void => {
if (TileLayerWrapper.getActiveLayer() == satelliteLayer) {
TileLayerWrapper.enableOnly(streetLayer, map);
} else {

View file

@ -70,6 +70,7 @@ class Circle extends OverlayBase {
constructor(name: string, desc: string, point: Point, options: any) {
super(name, desc, [ point ], options);
this.self = L.circle(point, options);
this.self.bindPopup(`<h3>${name}</h3><p>${desc}</p>`);
}
}
@ -207,6 +208,10 @@ class OverlayState {
return document.getElementById("modal-submitBtn");
}
radiusContainer(): HTMLElement | null {
return document.getElementById("radius-container");
}
nameField(): string {
return (document.getElementById("modal-name") as HTMLInputElement)?.value ?? "";
}
@ -215,6 +220,10 @@ class OverlayState {
return (document.getElementById("modal-desc") as HTMLInputElement)?.value ?? "";
}
radiusField(): string {
return (document.getElementById("modal-radius") as HTMLInputElement)?.value ?? "";
}
visible(): boolean {
return this.self()?.style.display != "none";
}
@ -228,9 +237,9 @@ class OverlayState {
setState(state: OverlayType, args: any): void {
const _this = this;
const title = this.title()
switch (state) {
case OverlayType.POINT:
const title = this.title()
if (title) {
title.innerHTML = "Add Marker";
}
@ -254,6 +263,32 @@ class OverlayState {
break;
case OverlayType.CIRCLE:
if (title) {
title.innerHTML = "Add Circle";
}
fetch("/static/pointModal.html")
.then(r=>r.text())
.then(t=> {
const content = _this.content();
if (content) { content.innerHTML = t; }
const radiusContainer = _this.radiusContainer();
if (radiusContainer) {
radiusContainer.style.display = "block";
}
const submitBtn = _this.submitBtn();
if (submitBtn) {
submitBtn.onclick = () => {
const name = _this.nameField();
const desc = _this.descField();
const radius = _this.radiusField();
const circle = new Circle(name, desc, args.latlng, {radius: Number(radius)});
circle.add(args.map);
args.overlays.circles.push(circle);
_this.setVisible(false);
}
}
});
break;
case OverlayType.POLYGON:
break;
@ -292,7 +327,16 @@ function init(): void {
const modal = new Modal();
const addMarkerHandler = (e: any) => {
const resetMapClick = (): void => {
try {
map.off("click", addMarkerHandler);
} catch {}
try {
map.off("click", addCircleHandler);
} catch {}
}
const addMarkerHandler = (e: any): void => {
modal.setVisible(true);
modal.setState(OverlayType.POINT, {
latlng: e.latlng,
@ -302,34 +346,49 @@ function init(): void {
map.off("click", addMarkerHandler);
}
const addCircleHandler = (e: any): void => {
modal.setVisible(true);
modal.setState(OverlayType.CIRCLE, {
latlng: e.latlng,
map: map,
overlays: overlays,
});
map.off("click", addCircleHandler);
}
const addMarkerBtn = document.getElementById("addPoint-btn");
if (addMarkerBtn) {
addMarkerBtn.onclick = (e: any)=>{
try{
map.off("click", addMarkerHandler);
} finally {
map.on("click", addMarkerHandler);
}
addMarkerBtn.onclick = (e: any): void => {
resetMapClick()
map.on("click", addMarkerHandler);
};
}
const addCircleBtn = document.getElementById("addCircle-btn");
if (addCircleBtn) {
addCircleBtn.onclick = (e: any): void => {
resetMapClick();
map.on("click", addCircleHandler);
}
}
const saveBtn = document.getElementById("save-btn");
if (saveBtn) {
saveBtn.onclick = (e: any) => {
saveBtn.onclick = (e: any): void => {
OverlayState.save(overlays);
};
}
const clearBtn = document.getElementById("clear-btn");
if (clearBtn) {
clearBtn.onclick = (e: any) => {
clearBtn.onclick = (e: any): void => {
overlays = OverlayState.clear(overlays, map);
}
}
const tilesBtn = document.getElementById("tiles-btn");
if (tilesBtn) {
tilesBtn.onclick = (e: any) => {
tilesBtn.onclick = (e: any): void => {
if (TileLayerWrapper.getActiveLayer() == satelliteLayer) {
TileLayerWrapper.enableOnly(streetLayer, map);
} else {

View file

@ -65,6 +65,7 @@ var Circle = /** @class */ (function (_super) {
function Circle(name, desc, point, options) {
var _this_1 = _super.call(this, name, desc, [point], options) || this;
_this_1.self = L.circle(point, options);
_this_1.self.bindPopup("<h3>" + name + "</h3><p>" + desc + "</p>");
return _this_1;
}
return Circle;
@ -189,6 +190,9 @@ var Modal = /** @class */ (function () {
Modal.prototype.submitBtn = function () {
return document.getElementById("modal-submitBtn");
};
Modal.prototype.radiusContainer = function () {
return document.getElementById("radius-container");
};
Modal.prototype.nameField = function () {
var _a, _b;
return (_b = (_a = document.getElementById("modal-name")) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : "";
@ -197,6 +201,10 @@ var Modal = /** @class */ (function () {
var _a, _b;
return (_b = (_a = document.getElementById("modal-desc")) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : "";
};
Modal.prototype.radiusField = function () {
var _a, _b;
return (_b = (_a = document.getElementById("modal-radius")) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : "";
};
Modal.prototype.visible = function () {
var _a;
return ((_a = this.self()) === null || _a === void 0 ? void 0 : _a.style.display) != "none";
@ -209,9 +217,9 @@ var Modal = /** @class */ (function () {
};
Modal.prototype.setState = function (state, args) {
var _this = this;
var title = this.title();
switch (state) {
case OverlayType.POINT:
var title = this.title();
if (title) {
title.innerHTML = "Add Marker";
}
@ -236,6 +244,33 @@ var Modal = /** @class */ (function () {
});
break;
case OverlayType.CIRCLE:
if (title) {
title.innerHTML = "Add Circle";
}
fetch("/static/pointModal.html")
.then(function (r) { return r.text(); })
.then(function (t) {
var content = _this.content();
if (content) {
content.innerHTML = t;
}
var radiusContainer = _this.radiusContainer();
if (radiusContainer) {
radiusContainer.style.display = "block";
}
var submitBtn = _this.submitBtn();
if (submitBtn) {
submitBtn.onclick = function () {
var name = _this.nameField();
var desc = _this.descField();
var radius = _this.radiusField();
var circle = new Circle(name, desc, args.latlng, { radius: Number(radius) });
circle.add(args.map);
args.overlays.circles.push(circle);
_this.setVisible(false);
};
}
});
break;
case OverlayType.POLYGON:
break;
@ -265,6 +300,16 @@ function init() {
overlays.circles.forEach(function (m) { return m.add(map); });
overlays.polygons.forEach(function (m) { return m.add(map); });
var modal = new Modal();
var resetMapClick = function () {
try {
map.off("click", addMarkerHandler);
}
catch (_a) { }
try {
map.off("click", addCircleHandler);
}
catch (_b) { }
};
var addMarkerHandler = function (e) {
modal.setVisible(true);
modal.setState(OverlayType.POINT, {
@ -274,15 +319,27 @@ function init() {
});
map.off("click", addMarkerHandler);
};
var addCircleHandler = function (e) {
modal.setVisible(true);
modal.setState(OverlayType.CIRCLE, {
latlng: e.latlng,
map: map,
overlays: overlays
});
map.off("click", addCircleHandler);
};
var addMarkerBtn = document.getElementById("addPoint-btn");
if (addMarkerBtn) {
addMarkerBtn.onclick = function (e) {
try {
map.off("click", addMarkerHandler);
}
finally {
map.on("click", addMarkerHandler);
}
resetMapClick();
map.on("click", addMarkerHandler);
};
}
var addCircleBtn = document.getElementById("addCircle-btn");
if (addCircleBtn) {
addCircleBtn.onclick = function (e) {
resetMapClick();
map.on("click", addCircleHandler);
};
}
var saveBtn = document.getElementById("save-btn");

View file

@ -1,5 +1,9 @@
<label for="modal-name">Name</label>
<input type="text" id="modal-name">
<label for="modal-desc">Description</label>
<textarea id="modal-desc"></textarea>
<label for="modal-name">Name</label><br/>
<input type="text" id="modal-name"><br/>
<label for="modal-desc">Description</label><br/>
<textarea id="modal-desc"></textarea><br/>
<div id="radius-container">
<label for="modal-radius">Radius (meters)</label><br/>
<input type="number" step="1" id="modal-radius" value="500"><br/>
</div>
<button id="modal-submitBtn">Add</button>

View file

@ -47,15 +47,16 @@ body {
}
#modal-container {
background: #222222;
background: #000000;
color: #c9c9c9;
position: fixed;
width: 1200px;
max-width: 80vw;
width: 100%;
max-width: 800px;
top: 50%;
left: 50%;
transform: translate3d(-50%, -50%, 0);
max-height: 80%;
height: auto;
max-height: calc(100vh - 2.5em);
z-index: 4;
text-align: left;
display: none;
@ -78,23 +79,24 @@ body {
display: block;
float: left;
text-transform: uppercase;
background: dimgray;
color: black;
color: white;
}
#modal-content input[type="text"], #modal-content textarea {
#modal-content #radius-container {
display: none;
}
#modal-content input[type="text"], #modal-content textarea, #modal-content input[type="number"] {
display: block;
width: 100%;
font-size: 150%;
color: #c9c9c9;
background: transparent;
color: white;
border: none;
border: solid 2px dimgray;
margin-bottom: 1em;
font-family: sans-serif;
box-sizing: border-box;
padding: 0.5em;
background: rgba(0, 0, 0, 0.5);
background: #222222;
}
@ -103,6 +105,12 @@ body {
height: 8em;
}
#modal-content input[type="number"] {
width: 12ch;
text-align: right;
}
button.closeBtn {
float: right;
background: transparent;
@ -120,13 +128,48 @@ button.closeBtn:hover {
button#modal-submitBtn {
font-size: 150%;
background: black;
color: #c9c9c9;
border: none;
border-left: solid 2px dimgray;
border-right: solid 2px dimgray;
background: transparent;;
color: white;
border: solid 2px dimgray;
position: relative;
margin-left: auto;
padding: 0.25em;
text-transform: uppercase;
}
button#modal-submitBtn:hover {
background: white;
color: black;
border: solid 2px white;
}
#cancel-container, #confirm-container {
position: fixed;
font-size: 200%;
bottom: 1.5em;
display: none;
left: 50%;
transform: translateY(-50%);
background: black;
}
#import-export-container {
position: fixed;
width: 100%;
max-width: 800px;
top: 50%;
left: 50%;
transform: translate3d(-50%, -50%, 0);
height: calc(100vh - 2.5em);
max-height: 600px;
background: black;
}
#overlays-menu-container {
position: fixed;
height: calc(100vh - 2.5em);
right: 0;
width: 100%;
max-width: 800px;
background: black;
}