Skip to content

Commit

Permalink
feat: rework the pins design and options
Browse files Browse the repository at this point in the history
  • Loading branch information
frederic-maury committed Dec 21, 2024
1 parent 8ee2b38 commit abc0709
Show file tree
Hide file tree
Showing 23 changed files with 965 additions and 212 deletions.
435 changes: 433 additions & 2 deletions frontend/package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@
"postcss-simple-vars": "^7.0.1",
"prettier": "^3.3.3",
"redux-mock-store": "^1.5.5",
"sass-embedded": "^1.83.0",
"semantic-release": "^24.2.0",
"tailwindcss": "^3.4.15",
"typescript": "^5.7.2",
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/Map/MapEvents.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { useLeafletContext } from '@react-leaflet/core';
import { useAppSelector } from '../../hooks/useInitStore';
import { selectedLatLng } from '../../store/marker/markerSelector';
import { selectDifferentiateOwned, selectMarkerOpacity } from '../../store/mapOptions/mapOptionsSelector';
import { CSSCLASSES, OWNED_SELECTOR } from './MapMarkers';
import { CSSCLASSES, OWNED_SELECTOR } from './map.utils';

let previousLatLng: Pick<LatLng, 'lat' | 'lng'> | undefined;
export function MapEvents() {
Expand Down
113 changes: 11 additions & 102 deletions frontend/src/components/Map/MapMarkers.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useCallback, useEffect, useMemo } from 'react';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { TFunction } from 'i18next';
import { DivIcon, MarkerClusterGroup, marker, Marker, markerClusterGroup, LeafletMouseEvent, LeafletEvent } from 'leaflet';
import { MarkerClusterGroup, marker, Marker, LeafletMouseEvent, LeafletEvent } from 'leaflet';
import { useLeafletContext } from '@react-leaflet/core';
import 'leaflet.markercluster';
import { Property } from '../../types/property';
Expand All @@ -14,72 +14,15 @@ import { Maybe } from '../../types/global';
import { analyticsEvent } from '../../services/analytics';
import { filterProperties } from '../../utils/properties';
import { debounce } from '../../utils/debounce';
import { generateIcon, getCleanMarkerCluster, zoomMapOffsets } from './map.utils';

export const OWNED_SELECTOR = '[data-marker-owned]';
export const CSSCLASSES = {
owned: 'stroke-white owned drop-shadow-lg',
notOwned: 'opacity-80',
}

function pinSvg(cssClasses: string) {
return `<svg width="50" height="50" viewBox="0 0 50 78" class="marker-svg ${cssClasses
}"><path class="at-176__pin" d="M24,0A24,24,0,0,0,0,24C0,37.25,20,72,24,72S48,37.25,48,24A24,24,0,0,0,24,0Zm0,33a9,9,0,1,1,9-9A9,9,0,0,1,24,33Z"/></svg>`;
}

export function generateIcon(
property: Property,
differentiateOwned: boolean,
markerOpacity: number,
selected: boolean,
) {
const owned = property.ownedAmount > 0;
const ownedClass = differentiateOwned && owned ? CSSCLASSES.owned : CSSCLASSES.notOwned;
return new DivIcon({
html:
`<div class="relative marker-icon"
style="opacity: ${markerOpacity};"
data-marker="${property.address}"
${owned ? 'data-marker-owned' : ''}
${property.source ? `data-marker-${property.source}` : ''}
${property.ownerWallets.length ? `data-marker-wallet="${property.ownerWallets.join(' ')}"` : ''}>
${pinSvg(`${property.iconColorClass}-icon ${ownedClass + (selected ? ' selected' : '')}`)}
<i class="text-3xl drop-shadow-sm mf-icon material-icons absolute top-0 left-[20%]">${property.icon}</i>
</div>`,
iconSize: [50, 50],
iconAnchor: [25, 50],
});
}

const zoomMapOffsets = {
0: 500,
1: 156,
2: 78,
3: 38.4,
4: 19.2,
5: 9.28,
6: 4.64,
7: 2.56,
8: 1.28,
9: 0.64,
10: 0.32,
11: 0.16,
12: 0.08,
13: 0.04,
14: 0.02,
15: 0.01,
16: 0.005,
17: 0.0025,
18: 0.00125,
19: 0.000625,
};

let markerCluster: MarkerClusterGroup;
let markers: Map<string, Marker> = new Map();
const markers: Map<string, Marker> = new Map();
export function MapMarkers({
properties,
}: {
properties: Property[];
}) {

const { t } = useTranslation('common');
const dispatch = useAppDispatch();
const { map } = useLeafletContext();
Expand All @@ -93,28 +36,10 @@ export function MapMarkers({
} = useAppSelector((state) => state.mapOptions);
const selectedUrlParam = useAppSelector(selectedProperty);
const property = useAppSelector(selectedMarker);
const [markerCluster, setMarkerCluster] = useState<MarkerClusterGroup>(getCleanMarkerCluster(markerClustering));

const memoizedGenerateIcon = useMemo(() => generateIcon, []);

const memoizedGenerateIcon = useMemo(() => {
return (property: Property, differentiateOwned: boolean, markerOpacity: number, selected: boolean) => {
const owned = property.ownedAmount > 0;
const ownedClass = differentiateOwned && owned ? CSSCLASSES.owned : CSSCLASSES.notOwned;
return new DivIcon({
html:
`<div class="relative marker-icon"
style="opacity: ${markerOpacity};"
data-marker="${property.address}"
${owned ? 'data-marker-owned' : ''}
${property.source ? `data-marker-${property.source}` : ''}
${property.ownerWallets.length ? `data-marker-wallet="${property.ownerWallets.join(' ')}"` : ''}>
${pinSvg(`${property.iconColorClass}-icon ${ownedClass + (selected ? ' selected' : '')}`)}
<i class="text-3xl drop-shadow-sm mf-icon material-icons absolute top-0 left-[20%]">${property.icon}</i>
</div>`,
iconSize: [50, 50],
iconAnchor: [25, 50],
});
};
}, []);

const memoizedCreateMarker = useCallback((
property: Property,
markerOpacity: number,
Expand Down Expand Up @@ -196,22 +121,6 @@ export function MapMarkers({
map.removeEventListener('moveend');
}

function getCleanMarkerCluster(clustering: number = 14) {
return markerClusterGroup({
disableClusteringAtZoom: clustering,
showCoverageOnHover: false,
chunkedLoading: true,
maxClusterRadius: 100,
zoomToBoundsOnClick: true,
spiderfyOnMaxZoom: false,
removeOutsideVisibleBounds: true,
animate: false,
chunkInterval: 100,
chunkDelay: 50,
singleMarkerMode: false,
});
}

const debouncedZoomHandler = debounce((event: LeafletEvent) => {
dispatch(setZoom(event.target.getZoom()));
}, 300);
Expand All @@ -222,7 +131,8 @@ export function MapMarkers({

useEffect(() => {
clearMap();
markerCluster = getCleanMarkerCluster(markerClustering);
markerCluster.clearLayers();
setMarkerCluster(getCleanMarkerCluster(markerClustering));

const filteredProperties = filterProperties(properties, displayAll, displayGnosis, displayRmm, selectedUrlParam);
const chunkSize = 100;
Expand Down Expand Up @@ -270,8 +180,7 @@ export function MapMarkers({
debouncedZoomHandler.cancel();
debouncedMoveHandler.cancel();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [properties, displayAll, displayGnosis, displayRmm, differentiateOwned, markerOpacity, markerClustering]);

return null;
}
}
81 changes: 0 additions & 81 deletions frontend/src/components/Map/MapWrapper.css

This file was deleted.

Loading

0 comments on commit abc0709

Please sign in to comment.