diff --git a/apps/client/src/widgets/collections/NoteList.tsx b/apps/client/src/widgets/collections/NoteList.tsx index 3b10d831e..501944158 100644 --- a/apps/client/src/widgets/collections/NoteList.tsx +++ b/apps/client/src/widgets/collections/NoteList.tsx @@ -4,6 +4,7 @@ import FNote from "../../entities/fnote"; import "./NoteList.css"; import { ListView, GridView } from "./legacy/ListOrGridView"; import { useEffect, useRef, useState } from "preact/hooks"; +import GeoView from "./geomap"; interface NoteListProps { note?: FNote | null; @@ -67,6 +68,8 @@ function getComponentByViewType(note: FNote, noteIds: string[], viewType: ViewTy return ; case "grid": return ; + case "geoMap": + return ; } } diff --git a/apps/client/src/widgets/collections/geomap/index.css b/apps/client/src/widgets/collections/geomap/index.css new file mode 100644 index 000000000..668962ff1 --- /dev/null +++ b/apps/client/src/widgets/collections/geomap/index.css @@ -0,0 +1,74 @@ +.geo-view { + overflow: hidden; + position: relative; + height: 100%; +} + +.geo-map-container { + height: 100%; + overflow: hidden; +} + +.leaflet-pane { + z-index: 1; +} + +.leaflet-top, +.leaflet-bottom { + z-index: 997; +} + +.geo-map-container.placing-note { + cursor: crosshair; +} + +.geo-map-container .marker-pin { + position: relative; +} + +.geo-map-container .leaflet-div-icon { + position: relative; + background: transparent; + border: 0; + overflow: visible; +} + +.geo-map-container .leaflet-div-icon .icon-shadow { + position: absolute; + top: 0; + left: 0; + z-index: -1; +} + +.geo-map-container .leaflet-div-icon .bx { + position: absolute; + top: 3px; + left: 2px; + background-color: white; + color: black; + padding: 2px; + border-radius: 50%; + font-size: 17px; +} + +.geo-map-container .leaflet-div-icon .title-label { + display: block; + position: absolute; + top: 100%; + left: 50%; + transform: translateX(-50%); + font-size: 0.75rem; + height: 1rem; + color: black; + width: 100px; + text-align: center; + text-overflow: ellipsis; + text-shadow: -1px -1px 0 white, 1px -1px 0 white, -1px 1px 0 white, 1px 1px 0 white; + white-space: no-wrap; + overflow: hidden; +} + +.geo-map-container.dark .leaflet-div-icon .title-label { + color: white; + text-shadow: -1px -1px 0 black, 1px -1px 0 black, -1px 1px 0 black, 1px 1px 0 black; +} \ No newline at end of file diff --git a/apps/client/src/widgets/collections/geomap/index.tsx b/apps/client/src/widgets/collections/geomap/index.tsx new file mode 100644 index 000000000..c116ebd7a --- /dev/null +++ b/apps/client/src/widgets/collections/geomap/index.tsx @@ -0,0 +1,16 @@ +import Map from "./map"; +import "./index.css"; + +const DEFAULT_COORDINATES: [number, number] = [3.878638227135724, 446.6630455551659]; +const DEFAULT_ZOOM = 2; + +export default function GeoView() { + return ( +
+ +
+ ); +} diff --git a/apps/client/src/widgets/collections/geomap/map.tsx b/apps/client/src/widgets/collections/geomap/map.tsx new file mode 100644 index 000000000..8a3dd97b1 --- /dev/null +++ b/apps/client/src/widgets/collections/geomap/map.tsx @@ -0,0 +1,70 @@ +import { useEffect, useRef, useState } from "preact/hooks"; +import L, { LatLng, Layer } from "leaflet"; +import "leaflet/dist/leaflet.css"; +import { useNoteContext, useNoteLabel } from "../../react/hooks"; +import { DEFAULT_MAP_LAYER_NAME, MAP_LAYERS } from "./map_layer"; + +interface MapProps { + coordinates: LatLng | [number, number]; + zoom: number; +} + +export default function Map({ coordinates, zoom }: MapProps) { + const mapRef = useRef(null); + const containerRef = useRef(null); + const { note } = useNoteContext(); + const [ layerName ] = useNoteLabel(note, "map:style"); + + useEffect(() => { + if (!containerRef.current) return; + mapRef.current = L.map(containerRef.current, { + worldCopyJump: true + }); + }, []); + + // Load the layer asynchronously. + const [ layer, setLayer ] = useState(); + useEffect(() => { + async function load() { + const layerData = MAP_LAYERS[layerName ?? DEFAULT_MAP_LAYER_NAME]; + + if (layerData.type === "vector") { + const style = (typeof layerData.style === "string" ? layerData.style : await layerData.style()); + await import("@maplibre/maplibre-gl-leaflet"); + + setLayer(L.maplibreGL({ + style: style as any + })); + } else { + setLayer(L.tileLayer(layerData.url, { + attribution: layerData.attribution, + detectRetina: true + })); + } + } + + load(); + }, [ layerName ]); + + // Attach layer to the map. + useEffect(() => { + const map = mapRef.current; + const layerToAdd = layer; + console.log("Add layer ", map, layerToAdd); + if (!map || !layerToAdd) return; + layerToAdd.addTo(map); + return () => layerToAdd.removeFrom(map); + }, [ mapRef, layer ]); + + // React to coordinate changes. + useEffect(() => { + if (!mapRef.current) return; + mapRef.current.setView(coordinates, zoom); + }, [ mapRef, coordinates, zoom ]); + + return ( +
+ +
+ ); +} diff --git a/apps/client/src/widgets/view_widgets/geo_view/map_layer.ts b/apps/client/src/widgets/collections/geomap/map_layer.ts similarity index 100% rename from apps/client/src/widgets/view_widgets/geo_view/map_layer.ts rename to apps/client/src/widgets/collections/geomap/map_layer.ts diff --git a/apps/client/src/widgets/view_widgets/geo_view/styles/colorful/de.json b/apps/client/src/widgets/collections/geomap/styles/colorful/de.json similarity index 100% rename from apps/client/src/widgets/view_widgets/geo_view/styles/colorful/de.json rename to apps/client/src/widgets/collections/geomap/styles/colorful/de.json diff --git a/apps/client/src/widgets/view_widgets/geo_view/styles/colorful/en.json b/apps/client/src/widgets/collections/geomap/styles/colorful/en.json similarity index 100% rename from apps/client/src/widgets/view_widgets/geo_view/styles/colorful/en.json rename to apps/client/src/widgets/collections/geomap/styles/colorful/en.json diff --git a/apps/client/src/widgets/view_widgets/geo_view/styles/colorful/nolabel.json b/apps/client/src/widgets/collections/geomap/styles/colorful/nolabel.json similarity index 100% rename from apps/client/src/widgets/view_widgets/geo_view/styles/colorful/nolabel.json rename to apps/client/src/widgets/collections/geomap/styles/colorful/nolabel.json diff --git a/apps/client/src/widgets/view_widgets/geo_view/styles/colorful/style.json b/apps/client/src/widgets/collections/geomap/styles/colorful/style.json similarity index 100% rename from apps/client/src/widgets/view_widgets/geo_view/styles/colorful/style.json rename to apps/client/src/widgets/collections/geomap/styles/colorful/style.json diff --git a/apps/client/src/widgets/view_widgets/geo_view/styles/eclipse/de.json b/apps/client/src/widgets/collections/geomap/styles/eclipse/de.json similarity index 100% rename from apps/client/src/widgets/view_widgets/geo_view/styles/eclipse/de.json rename to apps/client/src/widgets/collections/geomap/styles/eclipse/de.json diff --git a/apps/client/src/widgets/view_widgets/geo_view/styles/eclipse/en.json b/apps/client/src/widgets/collections/geomap/styles/eclipse/en.json similarity index 100% rename from apps/client/src/widgets/view_widgets/geo_view/styles/eclipse/en.json rename to apps/client/src/widgets/collections/geomap/styles/eclipse/en.json diff --git a/apps/client/src/widgets/view_widgets/geo_view/styles/eclipse/nolabel.json b/apps/client/src/widgets/collections/geomap/styles/eclipse/nolabel.json similarity index 100% rename from apps/client/src/widgets/view_widgets/geo_view/styles/eclipse/nolabel.json rename to apps/client/src/widgets/collections/geomap/styles/eclipse/nolabel.json diff --git a/apps/client/src/widgets/view_widgets/geo_view/styles/eclipse/style.json b/apps/client/src/widgets/collections/geomap/styles/eclipse/style.json similarity index 100% rename from apps/client/src/widgets/view_widgets/geo_view/styles/eclipse/style.json rename to apps/client/src/widgets/collections/geomap/styles/eclipse/style.json diff --git a/apps/client/src/widgets/view_widgets/geo_view/styles/graybeard/de.json b/apps/client/src/widgets/collections/geomap/styles/graybeard/de.json similarity index 100% rename from apps/client/src/widgets/view_widgets/geo_view/styles/graybeard/de.json rename to apps/client/src/widgets/collections/geomap/styles/graybeard/de.json diff --git a/apps/client/src/widgets/view_widgets/geo_view/styles/graybeard/en.json b/apps/client/src/widgets/collections/geomap/styles/graybeard/en.json similarity index 100% rename from apps/client/src/widgets/view_widgets/geo_view/styles/graybeard/en.json rename to apps/client/src/widgets/collections/geomap/styles/graybeard/en.json diff --git a/apps/client/src/widgets/view_widgets/geo_view/styles/graybeard/nolabel.json b/apps/client/src/widgets/collections/geomap/styles/graybeard/nolabel.json similarity index 100% rename from apps/client/src/widgets/view_widgets/geo_view/styles/graybeard/nolabel.json rename to apps/client/src/widgets/collections/geomap/styles/graybeard/nolabel.json diff --git a/apps/client/src/widgets/view_widgets/geo_view/styles/graybeard/style.json b/apps/client/src/widgets/collections/geomap/styles/graybeard/style.json similarity index 100% rename from apps/client/src/widgets/view_widgets/geo_view/styles/graybeard/style.json rename to apps/client/src/widgets/collections/geomap/styles/graybeard/style.json diff --git a/apps/client/src/widgets/view_widgets/geo_view/styles/neutrino/de.json b/apps/client/src/widgets/collections/geomap/styles/neutrino/de.json similarity index 100% rename from apps/client/src/widgets/view_widgets/geo_view/styles/neutrino/de.json rename to apps/client/src/widgets/collections/geomap/styles/neutrino/de.json diff --git a/apps/client/src/widgets/view_widgets/geo_view/styles/neutrino/en.json b/apps/client/src/widgets/collections/geomap/styles/neutrino/en.json similarity index 100% rename from apps/client/src/widgets/view_widgets/geo_view/styles/neutrino/en.json rename to apps/client/src/widgets/collections/geomap/styles/neutrino/en.json diff --git a/apps/client/src/widgets/view_widgets/geo_view/styles/neutrino/nolabel.json b/apps/client/src/widgets/collections/geomap/styles/neutrino/nolabel.json similarity index 100% rename from apps/client/src/widgets/view_widgets/geo_view/styles/neutrino/nolabel.json rename to apps/client/src/widgets/collections/geomap/styles/neutrino/nolabel.json diff --git a/apps/client/src/widgets/view_widgets/geo_view/styles/neutrino/style.json b/apps/client/src/widgets/collections/geomap/styles/neutrino/style.json similarity index 100% rename from apps/client/src/widgets/view_widgets/geo_view/styles/neutrino/style.json rename to apps/client/src/widgets/collections/geomap/styles/neutrino/style.json diff --git a/apps/client/src/widgets/view_widgets/geo_view/styles/shadow/de.json b/apps/client/src/widgets/collections/geomap/styles/shadow/de.json similarity index 100% rename from apps/client/src/widgets/view_widgets/geo_view/styles/shadow/de.json rename to apps/client/src/widgets/collections/geomap/styles/shadow/de.json diff --git a/apps/client/src/widgets/view_widgets/geo_view/styles/shadow/en.json b/apps/client/src/widgets/collections/geomap/styles/shadow/en.json similarity index 100% rename from apps/client/src/widgets/view_widgets/geo_view/styles/shadow/en.json rename to apps/client/src/widgets/collections/geomap/styles/shadow/en.json diff --git a/apps/client/src/widgets/view_widgets/geo_view/styles/shadow/nolabel.json b/apps/client/src/widgets/collections/geomap/styles/shadow/nolabel.json similarity index 100% rename from apps/client/src/widgets/view_widgets/geo_view/styles/shadow/nolabel.json rename to apps/client/src/widgets/collections/geomap/styles/shadow/nolabel.json diff --git a/apps/client/src/widgets/view_widgets/geo_view/styles/shadow/style.json b/apps/client/src/widgets/collections/geomap/styles/shadow/style.json similarity index 100% rename from apps/client/src/widgets/view_widgets/geo_view/styles/shadow/style.json rename to apps/client/src/widgets/collections/geomap/styles/shadow/style.json diff --git a/apps/client/src/widgets/ribbon/collection-properties-config.ts b/apps/client/src/widgets/ribbon/collection-properties-config.ts index 6a0c74d04..d53513a43 100644 --- a/apps/client/src/widgets/ribbon/collection-properties-config.ts +++ b/apps/client/src/widgets/ribbon/collection-properties-config.ts @@ -2,7 +2,7 @@ import { t } from "i18next"; import FNote from "../../entities/fnote"; import attributes from "../../services/attributes"; import NoteContextAwareWidget from "../note_context_aware_widget"; -import { DEFAULT_MAP_LAYER_NAME, MAP_LAYERS, type MapLayer } from "../view_widgets/geo_view/map_layer"; +import { DEFAULT_MAP_LAYER_NAME, MAP_LAYERS, type MapLayer } from "../collections/geomap/map_layer"; import { ViewTypeOptions } from "../collections/interface"; interface BookConfig { diff --git a/apps/client/src/widgets/view_widgets/geo_view/index.ts b/apps/client/src/widgets/view_widgets/geo_view/index.ts index 9b194f9df..845df3813 100644 --- a/apps/client/src/widgets/view_widgets/geo_view/index.ts +++ b/apps/client/src/widgets/view_widgets/geo_view/index.ts @@ -1,7 +1,6 @@ import ViewMode, { ViewModeArgs } from "../view_mode.js"; import L from "leaflet"; import type { GPX, LatLng, Layer, LeafletMouseEvent, Map, Marker } from "leaflet"; -import "leaflet/dist/leaflet.css"; import SpacedUpdate from "../../../services/spaced_update.js"; import { t } from "../../../services/i18n.js"; import processNoteWithMarker, { processNoteWithGpxTrack } from "./markers.js"; @@ -13,88 +12,6 @@ import { openMapContextMenu } from "./context_menu.js"; import attributes from "../../../services/attributes.js"; import { DEFAULT_MAP_LAYER_NAME, MAP_LAYERS } from "./map_layer.js"; -const TPL = /*html*/` -
- - -
-
`; - interface MapData { view?: { center?: LatLng | [number, number]; @@ -102,8 +19,6 @@ interface MapData { }; } -const DEFAULT_COORDINATES: [number, number] = [3.878638227135724, 446.6630455551659]; -const DEFAULT_ZOOM = 2; export const LOCATION_ATTRIBUTE = "geolocation"; enum State { @@ -142,27 +57,8 @@ export default class GeoView extends ViewMode { } async renderMap() { - const map = L.map(this.$container[0], { - worldCopyJump: true - }); + const layerName = this.parentNote.getLabelValue("map:style") ?? ; - const layerName = this.parentNote.getLabelValue("map:style") ?? DEFAULT_MAP_LAYER_NAME; - let layer: Layer; - const layerData = MAP_LAYERS[layerName]; - - if (layerData.type === "vector") { - const style = (typeof layerData.style === "string" ? layerData.style : await layerData.style()); - await import("@maplibre/maplibre-gl-leaflet"); - - layer = L.maplibreGL({ - style: style as any - }); - } else { - layer = L.tileLayer(layerData.url, { - attribution: layerData.attribution, - detectRetina: true - }); - } if (this.parentNote.hasLabel("map:scale")) { L.control.scale().addTo(map); @@ -220,7 +116,6 @@ export default class GeoView extends ViewMode { // Restore viewport position & zoom const center = parsedContent?.view?.center ?? DEFAULT_COORDINATES; const zoom = parsedContent?.view?.zoom ?? DEFAULT_ZOOM; - map.setView(center, zoom); } private onSave() {