Skip to content

Commit 4836fc9

Browse files
feat: Add GeoJSON export functionality
This commit introduces a new feature that allows users to export all their notes as a GeoJSON file. A new menu component has been added to the left of the note input field. This menu contains an "Export as GeoJSON" option, which, when clicked, generates a GeoJSON file containing all notes with their location, timestamp, and content. The new component is styled to match the existing application aesthetic.
1 parent ee281e6 commit 4836fc9

4 files changed

Lines changed: 110 additions & 2 deletions

File tree

.gitignore

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
node_modules
2-
dist
1+
node_modules/
2+
npm_output.log

index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111

1212
<form is="cycloops-form">
13+
<div is="cycloops-menu"></div>
1314
<textarea name="message" placeholder="What's happening here" rows="1"></textarea>
1415

1516
<button>

src/components/menu.ts

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import { db } from "../db";
2+
import { featureCollection, point } from "@turf/turf";
3+
4+
export class CycloopsMenu extends HTMLDivElement {
5+
constructor() {
6+
super();
7+
8+
this.attachShadow({ mode: "open" });
9+
this.shadowRoot!.innerHTML = `
10+
<style>
11+
:host {
12+
position: relative;
13+
display: inline-block;
14+
}
15+
16+
button {
17+
border-radius: 100%;
18+
border: none;
19+
height: 42px;
20+
width: 42px;
21+
display: flex;
22+
padding: 0;
23+
justify-content: center;
24+
align-items: center;
25+
background-color: #fff;
26+
color: #000;
27+
cursor: pointer;
28+
}
29+
30+
.dropdown {
31+
display: none;
32+
position: absolute;
33+
top: 100%;
34+
left: 0;
35+
background-color: #fff;
36+
min-width: 160px;
37+
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
38+
z-index: 1;
39+
border-radius: 11px;
40+
overflow: hidden;
41+
}
42+
43+
.dropdown a {
44+
color: #000;
45+
padding: 12px 16px;
46+
text-decoration: none;
47+
display: block;
48+
}
49+
50+
.dropdown a:hover {
51+
background-color: #0001;
52+
}
53+
54+
.show {
55+
display: block;
56+
}
57+
</style>
58+
<button>
59+
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="currentColor">
60+
<path d="M120-240v-80h720v80H120Zm0-200v-80h720v80H120Zm0-200v-80h720v80H120Z"/>
61+
</svg>
62+
</button>
63+
<div class="dropdown">
64+
<a href="#" id="export-geojson">Export as GeoJSON</a>
65+
</div>
66+
`;
67+
}
68+
69+
connectedCallback() {
70+
const button = this.shadowRoot!.querySelector("button");
71+
const dropdown = this.shadowRoot!.querySelector(".dropdown");
72+
const exportLink = this.shadowRoot!.querySelector("#export-geojson");
73+
74+
button?.addEventListener("click", () => {
75+
dropdown?.classList.toggle("show");
76+
});
77+
78+
exportLink?.addEventListener("click", (e) => {
79+
e.preventDefault();
80+
this.exportAsGeoJSON();
81+
dropdown?.classList.remove("show");
82+
});
83+
}
84+
85+
async exportAsGeoJSON() {
86+
const notes = await db.notes.toArray();
87+
const features = notes.map((note) =>
88+
point([note.lon, note.lat], {
89+
timestamp: note.time,
90+
text: note.text,
91+
})
92+
);
93+
const featureCollectionObject = featureCollection(features);
94+
95+
const dataStr =
96+
"data:text/json;charset=utf-8," +
97+
encodeURIComponent(JSON.stringify(featureCollectionObject));
98+
const downloadAnchorNode = document.createElement("a");
99+
downloadAnchorNode.setAttribute("href", dataStr);
100+
downloadAnchorNode.setAttribute("download", "cycloops-notes.geojson");
101+
document.body.appendChild(downloadAnchorNode); // required for firefox
102+
downloadAnchorNode.click();
103+
downloadAnchorNode.remove();
104+
}
105+
}

src/main.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ import "./style.css";
33
import { CycloopsForm } from "./components/form";
44
import { CycloopsList } from "./components/list";
55
import { CycloopsMap } from "./components/map";
6+
import { CycloopsMenu } from "./components/menu";
67

78
customElements.define("cycloops-form", CycloopsForm, { extends: "form" });
89
customElements.define("cycloops-list", CycloopsList, { extends: "ol" });
910
customElements.define("cycloops-map", CycloopsMap, { extends: "div" });
11+
customElements.define("cycloops-menu", CycloopsMenu, { extends: "div" });

0 commit comments

Comments
 (0)