chore(react/collections/geomap): bring back dragging

This commit is contained in:
Elian Doran 2025-09-04 16:44:35 +03:00
parent ec40d20e6a
commit 5854adb806
No known key found for this signature in database
6 changed files with 47 additions and 37 deletions

View File

@ -0,0 +1,8 @@
import { LatLng } from "leaflet";
import { LOCATION_ATTRIBUTE } from ".";
import attributes from "../../../services/attributes";
export async function moveMarker(noteId: string, latLng: LatLng | null) {
const value = latLng ? [latLng.lat, latLng.lng].join(",") : "";
await attributes.setLabel(noteId, LOCATION_ATTRIBUTE, value);
}

View File

@ -11,6 +11,7 @@ import FNote from "../../../entities/fnote";
import markerIcon from "leaflet/dist/images/marker-icon.png"; import markerIcon from "leaflet/dist/images/marker-icon.png";
import markerIconShadow from "leaflet/dist/images/marker-shadow.png"; import markerIconShadow from "leaflet/dist/images/marker-shadow.png";
import appContext from "../../../components/app_context"; import appContext from "../../../components/app_context";
import { moveMarker } from "./api";
const DEFAULT_COORDINATES: [number, number] = [3.878638227135724, 446.6630455551659]; const DEFAULT_COORDINATES: [number, number] = [3.878638227135724, 446.6630455551659];
const DEFAULT_ZOOM = 2; const DEFAULT_ZOOM = 2;
@ -25,6 +26,7 @@ interface MapData {
export default function GeoView({ note, noteIds, viewConfig, saveConfig }: ViewModeProps<MapData>) { export default function GeoView({ note, noteIds, viewConfig, saveConfig }: ViewModeProps<MapData>) {
const [ layerName ] = useNoteLabel(note, "map:style"); const [ layerName ] = useNoteLabel(note, "map:style");
const [ isReadOnly ] = useNoteLabel(note, "readOnly");
const [ notes, setNotes ] = useState<FNote[]>([]); const [ notes, setNotes ] = useState<FNote[]>([]);
const spacedUpdate = useSpacedUpdate(() => { const spacedUpdate = useSpacedUpdate(() => {
if (viewConfig) { if (viewConfig) {
@ -46,13 +48,13 @@ export default function GeoView({ note, noteIds, viewConfig, saveConfig }: ViewM
spacedUpdate.scheduleUpdate(); spacedUpdate.scheduleUpdate();
}} }}
> >
{notes.map(note => <NoteMarker note={note} />)} {notes.map(note => <NoteMarker note={note} editable={!isReadOnly} />)}
</Map> </Map>
</div> </div>
); );
} }
function NoteMarker({ note }: { note: FNote }) { function NoteMarker({ note, editable }: { note: FNote, editable: boolean }) {
const [ location ] = useNoteLabel(note, LOCATION_ATTRIBUTE); const [ location ] = useNoteLabel(note, LOCATION_ATTRIBUTE);
// React to changes // React to changes
@ -65,17 +67,25 @@ function NoteMarker({ note }: { note: FNote }) {
const latLng = location?.split(",", 2).map((el) => parseFloat(el)) as [ number, number ] | undefined; const latLng = location?.split(",", 2).map((el) => parseFloat(el)) as [ number, number ] | undefined;
const icon = useMemo(() => buildIcon(iconClass, colorClass ?? undefined, title, note.noteId), [ iconClass, colorClass, title, note.noteId]); const icon = useMemo(() => buildIcon(iconClass, colorClass ?? undefined, title, note.noteId), [ iconClass, colorClass, title, note.noteId]);
// Middle click to open in new tab
const onMouseDown = useCallback((e: MouseEvent) => {
if (e.button === 1) {
const hoistedNoteId = appContext.tabManager.getActiveContext()?.hoistedNoteId;
appContext.tabManager.openInNewTab(note.noteId, hoistedNoteId);
return true;
}
}, [ note.noteId ]);
const onDragged = useCallback((newCoordinates: LatLng) => {
moveMarker(note.noteId, newCoordinates);
}, [ note.noteId ]);
return latLng && <Marker return latLng && <Marker
coordinates={latLng} coordinates={latLng}
icon={icon} icon={icon}
mouseDown={useCallback((e: MouseEvent) => { mouseDown={onMouseDown}
// Middle click to open in new tab draggable={editable}
if (e.button === 1) { dragged={onDragged}
const hoistedNoteId = appContext.tabManager.getActiveContext()?.hoistedNoteId;
appContext.tabManager.openInNewTab(note.noteId, hoistedNoteId);
return true;
}
}, [ note.noteId ])}
/> />
} }

View File

@ -1,27 +1,41 @@
import { useContext, useEffect } from "preact/hooks"; import { useContext, useEffect } from "preact/hooks";
import { ParentMap } from "./map"; import { ParentMap } from "./map";
import { DivIcon, Icon, marker } from "leaflet"; import { DivIcon, Icon, LatLng, Marker as LeafletMarker, marker, MarkerOptions } from "leaflet";
export interface MarkerProps { export interface MarkerProps {
coordinates: [ number, number ]; coordinates: [ number, number ];
icon?: Icon | DivIcon; icon?: Icon | DivIcon;
mouseDown?: (e: MouseEvent) => void; mouseDown?: (e: MouseEvent) => void;
dragged: ((newCoordinates: LatLng) => void)
draggable?: boolean;
} }
export default function Marker({ coordinates, icon, mouseDown }: MarkerProps) { export default function Marker({ coordinates, icon, draggable, dragged, mouseDown }: MarkerProps) {
const parentMap = useContext(ParentMap); const parentMap = useContext(ParentMap);
useEffect(() => { useEffect(() => {
if (!parentMap) return; if (!parentMap) return;
const newMarker = marker(coordinates, { const options: MarkerOptions = { icon };
icon if (draggable) {
}); options.draggable = true;
options.autoPan = true;
options.autoPanSpeed = 5;
}
const newMarker = marker(coordinates, options);
if (mouseDown) { if (mouseDown) {
newMarker.on("mousedown", e => mouseDown(e.originalEvent)); newMarker.on("mousedown", e => mouseDown(e.originalEvent));
} }
if (dragged) {
newMarker.on("moveend", e => {
const coordinates = (e.target as LeafletMarker).getLatLng();
dragged(coordinates);
});
}
newMarker.addTo(parentMap); newMarker.addTo(parentMap);
return () => newMarker.removeFrom(parentMap); return () => newMarker.removeFrom(parentMap);

View File

@ -18,11 +18,6 @@ interface CreateChildResponse {
}; };
} }
export async function moveMarker(noteId: string, latLng: LatLng | null) {
const value = latLng ? [latLng.lat, latLng.lng].join(",") : "";
await attributes.setLabel(noteId, LOCATION_ATTRIBUTE, value);
}
export async function createNewNote(noteId: string, e: LeafletMouseEvent) { export async function createNewNote(noteId: string, e: LeafletMouseEvent) {
const title = await dialog.prompt({ message: t("relation_map.enter_title_of_new_note"), defaultValue: t("relation_map.default_new_note_title") }); const title = await dialog.prompt({ message: t("relation_map.enter_title_of_new_note"), defaultValue: t("relation_map.default_new_note_title") });

View File

@ -9,19 +9,6 @@ import L from "leaflet";
let gpxLoaded = false; let gpxLoaded = false;
export default function processNoteWithMarker(map: Map, note: FNote, location: string, isEditable: boolean) { export default function processNoteWithMarker(map: Map, note: FNote, location: string, isEditable: boolean) {
const newMarker = marker(latLng(lat, lng), {
icon,
draggable: isEditable,
autoPan: true,
autoPanSpeed: 5
}).addTo(map);
if (isEditable) {
newMarker.on("moveend", (e) => {
moveMarker(note.noteId, (e.target as Marker).getLatLng());
});
}
newMarker.on("contextmenu", (e) => { newMarker.on("contextmenu", (e) => {
openContextMenu(note.noteId, e, isEditable); openContextMenu(note.noteId, e, isEditable);
}); });

View File

@ -53,8 +53,4 @@ export default abstract class ViewMode<T extends object> extends Component {
} }
} }
get isReadOnly() {
return this.parentNote.hasLabel("readOnly");
}
} }