mirror of
https://github.com/zadam/trilium.git
synced 2026-05-07 05:06:37 +02:00
fix(collections/map): tooltips not always available on first reload
This commit is contained in:
42
apps/client/src/widgets/collections/geomap/Tooltips.tsx
Normal file
42
apps/client/src/widgets/collections/geomap/Tooltips.tsx
Normal file
@@ -0,0 +1,42 @@
|
||||
import { MapMouseEvent, Popup } from "maplibre-gl";
|
||||
import { useContext, useEffect } from "preact/hooks";
|
||||
|
||||
import { ParentMap } from "./map";
|
||||
import { MARKER_LAYER } from "./marker_data";
|
||||
|
||||
export default function Tooltips() {
|
||||
const map = useContext(ParentMap);
|
||||
|
||||
useEffect(() => {
|
||||
if (!map) return;
|
||||
|
||||
const tooltip = new Popup({
|
||||
closeButton: false,
|
||||
closeOnClick: false,
|
||||
offset: 12,
|
||||
className: "marker-tooltip"
|
||||
});
|
||||
|
||||
function onMouseEnter(e: MapMouseEvent) {
|
||||
const feature = e.features[0];
|
||||
tooltip
|
||||
.setLngLat(feature.geometry.coordinates)
|
||||
.setHTML(`<strong>${feature.properties.name}</strong>`)
|
||||
.addTo(map);
|
||||
}
|
||||
|
||||
function onMouseLeave() {
|
||||
tooltip.remove();
|
||||
}
|
||||
|
||||
map.on("mouseenter", MARKER_LAYER, onMouseEnter);
|
||||
map.on("mouseleave", MARKER_LAYER, onMouseLeave);
|
||||
|
||||
return () => {
|
||||
map.off("mouseenter", MARKER_LAYER, onMouseEnter);
|
||||
map.off("mouseleave", MARKER_LAYER, onMouseLeave);
|
||||
};
|
||||
}, [ map ]);
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -25,6 +25,7 @@ import Map, { GeoMouseEvent } from "./map";
|
||||
import { DEFAULT_MAP_LAYER_NAME, MAP_LAYERS, MapLayer } from "./map_layer";
|
||||
import Marker, { GpxTrack } from "./marker";
|
||||
import { MARKER_LAYER, MARKER_SVG, useMarkerData } from "./marker_data";
|
||||
import Tooltips from "./Tooltips";
|
||||
|
||||
const DEFAULT_COORDINATES: [number, number] = [3.878638227135724, 446.6630455551659];
|
||||
const DEFAULT_ZOOM = 2;
|
||||
@@ -119,7 +120,6 @@ export default function GeoView({ note, noteIds, viewConfig, saveConfig }: ViewM
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const apiRef = useRef<maplibregl.Map | null>(null);
|
||||
useMarkerData(note, apiRef);
|
||||
useHoverTooltip(containerRef, apiRef);
|
||||
useNoteTreeDrag(containerRef, {
|
||||
dragEnabled: !isReadOnly,
|
||||
dragNotEnabledMessage: {
|
||||
@@ -178,7 +178,7 @@ export default function GeoView({ note, noteIds, viewConfig, saveConfig }: ViewM
|
||||
onContextMenu={onContextMenu}
|
||||
scale={hasScale}
|
||||
>
|
||||
{/* {notes.map(note => <NoteWrapper note={note} isReadOnly={isReadOnly} hideLabels={hideLabels} />)} */}
|
||||
<Tooltips />
|
||||
</Map>}
|
||||
<GeoMapTouchBar state={state} map={apiRef.current} />
|
||||
</div>
|
||||
@@ -207,40 +207,6 @@ function useLayerData(note: FNote) {
|
||||
return layerData;
|
||||
}
|
||||
|
||||
function useHoverTooltip(containerRef: RefObject<HTMLDivElement>, mapRef: RefObject<maplibregl.Map | null>) {
|
||||
useEffect(() => {
|
||||
const map = mapRef.current;
|
||||
if (!map) return;
|
||||
|
||||
const tooltip = new Popup({
|
||||
closeButton: false,
|
||||
closeOnClick: false,
|
||||
offset: 12,
|
||||
className: "marker-tooltip"
|
||||
});
|
||||
|
||||
function onMouseEnter(e: MapMouseEvent) {
|
||||
const feature = e.features[0];
|
||||
tooltip
|
||||
.setLngLat(feature.geometry.coordinates)
|
||||
.setHTML(`<strong>${feature.properties.name}</strong>`)
|
||||
.addTo(map);
|
||||
}
|
||||
|
||||
function onMouseLeave() {
|
||||
tooltip.remove();
|
||||
}
|
||||
|
||||
map.on("mouseenter", MARKER_LAYER, onMouseEnter);
|
||||
map.on("mouseleave", MARKER_LAYER, onMouseLeave);
|
||||
|
||||
return () => {
|
||||
map.off("mouseenter", MARKER_LAYER, onMouseEnter);
|
||||
map.off("mouseleave", MARKER_LAYER, onMouseLeave);
|
||||
};
|
||||
}, [ mapRef ]);
|
||||
}
|
||||
|
||||
function ToggleReadOnlyButton({ note }: { note: FNote }) {
|
||||
const [ isReadOnly, setReadOnly ] = useNoteLabelBoolean(note, "readOnly");
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import "maplibre-gl/dist/maplibre-gl.css";
|
||||
|
||||
import maplibregl, { NavigationControl, type Point } from "maplibre-gl";
|
||||
import { ComponentChildren, createContext, RefObject } from "preact";
|
||||
import { useEffect, useImperativeHandle, useRef } from "preact/hooks";
|
||||
import { useEffect, useImperativeHandle, useRef, useState } from "preact/hooks";
|
||||
|
||||
import { useElementSize, useSyncedRef } from "../../react/hooks";
|
||||
import { MapLayer } from "./map_layer";
|
||||
@@ -38,10 +38,10 @@ function toMapLibreEvent(e: maplibregl.MapMouseEvent): GeoMouseEvent {
|
||||
}
|
||||
|
||||
export default function Map({ coordinates, zoom, layerData, viewportChanged, children, onClick, onContextMenu, scale, apiRef, containerRef: _containerRef, onZoom }: MapProps) {
|
||||
const mapRef = useRef<maplibregl.Map>(null);
|
||||
const [ map, setMap ] = useState<maplibregl.Map | null>(null);
|
||||
const containerRef = useSyncedRef<HTMLDivElement>(_containerRef);
|
||||
|
||||
useImperativeHandle(apiRef ?? null, () => mapRef.current);
|
||||
useImperativeHandle(apiRef ?? null, () => map);
|
||||
|
||||
// Initialize the map.
|
||||
useEffect(() => {
|
||||
@@ -93,7 +93,7 @@ export default function Map({ coordinates, zoom, layerData, viewportChanged, chi
|
||||
showZoom: true
|
||||
}), "top-left");
|
||||
|
||||
mapRef.current = mapInstance;
|
||||
setMap(mapInstance);
|
||||
|
||||
// Load async vector style if needed.
|
||||
if (layerData.type === "vector" && typeof layerData.style !== "string") {
|
||||
@@ -104,13 +104,12 @@ export default function Map({ coordinates, zoom, layerData, viewportChanged, chi
|
||||
|
||||
return () => {
|
||||
mapInstance.remove();
|
||||
mapRef.current = null;
|
||||
setMap(null);
|
||||
};
|
||||
}, []);
|
||||
|
||||
// React to layer changes.
|
||||
useEffect(() => {
|
||||
const map = mapRef.current;
|
||||
if (!map) return;
|
||||
|
||||
if (layerData.type === "vector") {
|
||||
@@ -141,23 +140,21 @@ export default function Map({ coordinates, zoom, layerData, viewportChanged, chi
|
||||
]
|
||||
});
|
||||
}
|
||||
}, [ layerData ]);
|
||||
}, [ map, layerData ]);
|
||||
|
||||
// React to coordinate changes.
|
||||
useEffect(() => {
|
||||
if (!mapRef.current) return;
|
||||
|
||||
if (!map) return;
|
||||
const center = Array.isArray(coordinates)
|
||||
? [coordinates[1], coordinates[0]] as [number, number]
|
||||
: [coordinates.lng, coordinates.lat] as [number, number];
|
||||
|
||||
mapRef.current.setCenter(center);
|
||||
mapRef.current.setZoom(zoom);
|
||||
}, [ coordinates, zoom ]);
|
||||
map.setCenter(center);
|
||||
map.setZoom(zoom);
|
||||
}, [ map, coordinates, zoom ]);
|
||||
|
||||
// Viewport callback.
|
||||
useEffect(() => {
|
||||
const map = mapRef.current;
|
||||
if (!map) return;
|
||||
|
||||
const updateFn = () => {
|
||||
@@ -169,19 +166,17 @@ export default function Map({ coordinates, zoom, layerData, viewportChanged, chi
|
||||
return () => {
|
||||
map.off("moveend", updateFn);
|
||||
};
|
||||
}, [ viewportChanged ]);
|
||||
}, [ map, viewportChanged ]);
|
||||
|
||||
useEffect(() => {
|
||||
const map = mapRef.current;
|
||||
if (!onClick || !map) return;
|
||||
|
||||
const handler = (e: maplibregl.MapMouseEvent) => onClick(toMapLibreEvent(e));
|
||||
map.on("click", handler);
|
||||
return () => { map.off("click", handler); };
|
||||
}, [ onClick ]);
|
||||
}, [ map, onClick ]);
|
||||
|
||||
useEffect(() => {
|
||||
const map = mapRef.current;
|
||||
if (!onContextMenu || !map) return;
|
||||
|
||||
const handler = (e: maplibregl.MapMouseEvent) => {
|
||||
@@ -190,37 +185,35 @@ export default function Map({ coordinates, zoom, layerData, viewportChanged, chi
|
||||
};
|
||||
map.on("contextmenu", handler);
|
||||
return () => { map.off("contextmenu", handler); };
|
||||
}, [ onContextMenu ]);
|
||||
}, [ map, onContextMenu ]);
|
||||
|
||||
useEffect(() => {
|
||||
const map = mapRef.current;
|
||||
if (!onZoom || !map) return;
|
||||
|
||||
map.on("zoom", onZoom);
|
||||
return () => { map.off("zoom", onZoom); };
|
||||
}, [ onZoom ]);
|
||||
}, [ map, onZoom ]);
|
||||
|
||||
// Scale
|
||||
useEffect(() => {
|
||||
const map = mapRef.current;
|
||||
if (!scale || !map) return;
|
||||
const scaleControl = new maplibregl.ScaleControl();
|
||||
map.addControl(scaleControl);
|
||||
return () => { map.removeControl(scaleControl); };
|
||||
}, [ scale ]);
|
||||
}, [ map, scale ]);
|
||||
|
||||
// Adapt to container size changes.
|
||||
const size = useElementSize(containerRef);
|
||||
useEffect(() => {
|
||||
mapRef.current?.resize();
|
||||
}, [ size?.width, size?.height ]);
|
||||
map?.resize();
|
||||
}, [ map, size?.width, size?.height ]);
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={containerRef}
|
||||
className={`geo-map-container ${layerData.isDarkTheme ? "dark" : ""}`}
|
||||
>
|
||||
<ParentMap.Provider value={mapRef.current}>
|
||||
<ParentMap.Provider value={map}>
|
||||
{children}
|
||||
</ParentMap.Provider>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user