From 4836fc9c8f51f29020b99d8a25ddb05fb81fd18f Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Mon, 6 Oct 2025 12:04:57 +0000 Subject: [PATCH 1/3] 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. --- .gitignore | 4 +- index.html | 1 + src/components/menu.ts | 105 +++++++++++++++++++++++++++++++++++++++++ src/main.ts | 2 + 4 files changed, 110 insertions(+), 2 deletions(-) create mode 100644 src/components/menu.ts diff --git a/.gitignore b/.gitignore index f06235c..dda5af8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ -node_modules -dist +node_modules/ +npm_output.log \ No newline at end of file diff --git a/index.html b/index.html index cc95fbf..bc94d0d 100644 --- a/index.html +++ b/index.html @@ -10,6 +10,7 @@
+
+ + `; + } + + connectedCallback() { + const button = this.shadowRoot!.querySelector("button"); + const dropdown = this.shadowRoot!.querySelector(".dropdown"); + const exportLink = this.shadowRoot!.querySelector("#export-geojson"); + + button?.addEventListener("click", () => { + dropdown?.classList.toggle("show"); + }); + + exportLink?.addEventListener("click", (e) => { + e.preventDefault(); + this.exportAsGeoJSON(); + dropdown?.classList.remove("show"); + }); + } + + async exportAsGeoJSON() { + const notes = await db.notes.toArray(); + const features = notes.map((note) => + point([note.lon, note.lat], { + timestamp: note.time, + text: note.text, + }) + ); + const featureCollectionObject = featureCollection(features); + + const dataStr = + "data:text/json;charset=utf-8," + + encodeURIComponent(JSON.stringify(featureCollectionObject)); + const downloadAnchorNode = document.createElement("a"); + downloadAnchorNode.setAttribute("href", dataStr); + downloadAnchorNode.setAttribute("download", "cycloops-notes.geojson"); + document.body.appendChild(downloadAnchorNode); // required for firefox + downloadAnchorNode.click(); + downloadAnchorNode.remove(); + } +} \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index e8eb541..739b5ee 100644 --- a/src/main.ts +++ b/src/main.ts @@ -3,7 +3,9 @@ import "./style.css"; import { CycloopsForm } from "./components/form"; import { CycloopsList } from "./components/list"; import { CycloopsMap } from "./components/map"; +import { CycloopsMenu } from "./components/menu"; customElements.define("cycloops-form", CycloopsForm, { extends: "form" }); customElements.define("cycloops-list", CycloopsList, { extends: "ol" }); customElements.define("cycloops-map", CycloopsMap, { extends: "div" }); +customElements.define("cycloops-menu", CycloopsMenu, { extends: "div" }); From 7ce6b78085fb57456c8bb4994db252c3b24ee4f2 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Mon, 6 Oct 2025 12:27:54 +0000 Subject: [PATCH 2/3] fix: Restore dist directory to .gitignore This commit restores the `dist/` directory to the `.gitignore` file, addressing the feedback from the pull request review. --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index dda5af8..1a2baae 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ node_modules/ +dist/ npm_output.log \ No newline at end of file From 7d6cb7d3ad68cf5e6cc05c2ac3ade1d99de8ffe4 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Mon, 6 Oct 2025 12:31:26 +0000 Subject: [PATCH 3/3] refactor: Use dialog element for menu This commit refactors the `CycloopsMenu` component to use the `` element instead of a `div` for the dropdown menu. This change improves accessibility and leverages modern browser features for a more robust implementation. --- src/components/menu.ts | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/src/components/menu.ts b/src/components/menu.ts index 92953e8..5697c60 100644 --- a/src/components/menu.ts +++ b/src/components/menu.ts @@ -27,58 +27,63 @@ export class CycloopsMenu extends HTMLDivElement { cursor: pointer; } - .dropdown { - display: none; + dialog { position: absolute; top: 100%; left: 0; + margin: 0; + border: none; + padding: 0; background-color: #fff; min-width: 160px; box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2); - z-index: 1; border-radius: 11px; overflow: hidden; } - .dropdown a { + dialog::backdrop { + background: transparent; + } + + dialog a { color: #000; padding: 12px 16px; text-decoration: none; display: block; } - .dropdown a:hover { + dialog a:hover { background-color: #0001; } - - .show { - display: block; - } - + `; } connectedCallback() { const button = this.shadowRoot!.querySelector("button"); - const dropdown = this.shadowRoot!.querySelector(".dropdown"); + const dialog = this.shadowRoot!.querySelector("dialog"); const exportLink = this.shadowRoot!.querySelector("#export-geojson"); button?.addEventListener("click", () => { - dropdown?.classList.toggle("show"); + if (dialog?.open) { + dialog.close(); + } else { + dialog?.showModal(); + } }); exportLink?.addEventListener("click", (e) => { e.preventDefault(); this.exportAsGeoJSON(); - dropdown?.classList.remove("show"); + dialog?.close(); }); }