Skip to content

Commit

Permalink
Merge pull request #588 from VEuPathDB/multiple-marker-selection-simp…
Browse files Browse the repository at this point in the history
…lified

Multiple marker selection via BoundsDriftMarker and SemanticMarkers
  • Loading branch information
bobular authored Dec 2, 2023
2 parents a3bcf88 + e23b37e commit 4456176
Showing 19 changed files with 404 additions and 57 deletions.
59 changes: 30 additions & 29 deletions packages/libs/components/public/css/vb-popbio-maps.css
Original file line number Diff line number Diff line change
@@ -95,16 +95,10 @@ body {
text-align: left;
line-height: 15px;
color: #555555;
/* DKDK changed margin-bottom from 5 to 0 */
margin-bottom: 0px !important;
border-radius: 0 !important;
/* DKDK change these for mapveu v2
width : 280px;
min-height : 300px; */
width: 250px;
min-height: 210px;
/* DKDK combine with other CSS defined */
/* padding: 6px 8px 6px 8px; */
padding: 0px 0px 0px 0px;
background: white;
/*max-width : 230px;
@@ -128,17 +122,14 @@ body {
min-width: 50px;
text-align: right;
font-weight: 600;
/*DKDK VB-8650*/
width: 27%;
}

.summ-by-value {
float: left;
/*DKDK VB-8650 */
/*width: 79%;*/
}

/*DKDK VB-8650 add below class to handle active-legend and external link separately*/
/* VB-8650 add below class to handle active-legend and external link separately*/
.active-legend-area {
float: left;
width: 73%;
@@ -150,18 +141,11 @@ body {

.legend {
position: absolute;
/* DKDK set bottom to be smaller
bottom: 40px; */
bottom: 10px;
right: 0px;
z-index: 10000;
}

/* .legend > div {
/* DKDK change padding from 0 8px to 0 4px
padding: 0 2px;
} */

.legend p {
font-size: smaller;
word-wrap: break-word;
@@ -212,7 +196,7 @@ body {

.mapboxbottom .overlaybottom {
position: absolute;
/*DKDK VB-8707 change bottom from 3 to 25 px*/
/* VB-8707 change bottom from 3 to 25 px*/
bottom: 25px;
left: 40%;
width: 30%;
@@ -278,12 +262,29 @@ path,
text-shadow: -1px -1px 0 #ffffff, 1px -1px 0 #ffffff, -1px 1px 0 #ffffff,
1px 1px 0 #ffffff;
}
.highlight-marker {
background-clip: padding-box;
border-radius: 30px;
box-shadow: 0px 0px 0px 4px rgba(255, 255, 0, 1);

/* donut and bubble markers highlight */
.highlight-marker.donut-marker,
.highlight-marker.bubble-marker {
/* background-clip: padding-box; */
border-radius: 50%;
/* add inset to reduce a gap between the element and the box-shadow */
box-shadow: 2.5px 2.5px 0px 6px rgba(255, 255, 0, 1),
2.5px 2.5px 0px 2px rgba(255, 255, 0, 1) inset;
z-index: 9999 !important;
}

/* chart marker highlight */
.highlight-marker.chart-marker {
/* background-clip: padding-box; */
/* the rounded corner of the chart marker has 10px in radius */
border-radius: 10px;
/* add inset to reduce a gap between the element and the box-shadow */
box-shadow: -1px -0.5px 0px 6px rgba(255, 255, 0, 1),
-1px -0.5px 0px 2px rgba(255, 255, 0, 1) inset;
z-index: 9999 !important;
}

.top-marker {
z-index: 3100 !important;
}
@@ -449,7 +450,7 @@ path,
fill: none;
stroke: #000000;
stroke-width: 1px;
/* DKDK temporarily block below */
/* temporarily block below */
/* color-rendering : optimizeQuality !important; */
shape-rendering: crispEdges !important;
text-rendering: geometricPrecision !important;
@@ -541,7 +542,7 @@ html {
border-radius: 0px;
box-shadow: none;
}
/* x-jsrender active terms DKDK VB-8650 */
/* x-jsrender active terms VB-8650 */
.active-term,
.active-legend,
.active-others,
@@ -741,13 +742,13 @@ div.hint div {
text-overflow: ellipsis;
}

/* DKDK VB-8650 */
/* VB-8650 */
.active-legend,
.insertExternalLinkLegend {
line-height: 17px;
}

/* DKDK VB-8650 */
/* VB-8650 */
/* .active-legend i, #Other-Terms-List i{ */
.active-legend .summ-by-value .summ-by-value-item,
.insertExternalLinkLegend .summ-by-value .summ-by-value-item,
@@ -1079,7 +1080,7 @@ div.hint div {
text-align: center;
}

/*DKDK add vectorbase-icon*/
/* add vectorbase-icon*/
#vectorbase-icon {
height: 40px;
font-size: 12pt;
@@ -1274,7 +1275,7 @@ div.hint div {
text-align: center;
}

/* DKDK VB-8112 disabling cursor change on pie chart legend */
/* VB-8112 disabling cursor change on pie chart legend */
.nvd3 .nv-legend .nv-series {
cursor: default !important;
}
@@ -1291,7 +1292,7 @@ div.hint div {
cursor: ew-resize;
}

/* DKDK VB-8650 related CSS */
/* VB-8650 related CSS */
/*.insertExternalLinkLegend .insertExternalLink {
float: initial;
}
73 changes: 59 additions & 14 deletions packages/libs/components/src/map/BoundsDriftMarker.tsx
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
import { useMap, Popup } from 'react-leaflet';
import { useRef, useEffect } from 'react';
import { useRef, useEffect, useState } from 'react';
// use new ReactLeafletDriftMarker instead of DriftMarker
import ReactLeafletDriftMarker from 'react-leaflet-drift-marker';
import { MarkerProps, Bounds } from './Types';
import L, { LeafletMouseEvent, LatLngBounds } from 'leaflet';

import { debounce } from 'lodash';

export interface BoundsDriftMarkerProps extends MarkerProps {
bounds: Bounds;
duration: number;
// A class to add to the popup element
popupClass?: string;
// selectedMarkers state
selectedMarkers?: string[];
// selectedMarkers setState
setSelectedMarkers?: React.Dispatch<React.SetStateAction<string[]>>;
}

/**
@@ -39,8 +45,12 @@ export default function BoundsDriftMarker({
popupContent,
popupClass,
zIndexOffset,
selectedMarkers,
setSelectedMarkers,
...props
}: BoundsDriftMarkerProps) {
const map = useMap();

const boundingBox = new LatLngBounds([
[bounds.southWest.lat, bounds.southWest.lng],
[bounds.northEast.lat, bounds.northEast.lng],
@@ -171,20 +181,20 @@ export default function BoundsDriftMarker({
markerRef.current._icon.firstChild.getBoundingClientRect();
const anchorRect = popupRef.current._tipContainer.getBoundingClientRect();
const { height: anchorHeight, width: anchorWidth } = anchorRect;
const popupRect = popupRef.current._container.getBoundingClientRect();

/**
* Within each conditional block, we will:
* 1. check the position of the gray box vs the marker to determine which element the popup should
* be anchored to
* 2. set the popupRef's offset accordingly (with some fuzzy calculations)
*/
// with the marker clicl event for selectedMarkers, popupRef is not used as it changes by click event
if (orientation === 'down') {
const yOffset =
(markerIconRect.bottom > grayBoundsRect.bottom
? markerIconRect.bottom
: grayBoundsRect.bottom) -
popupRect.top +
markerRect.bottom +
anchorHeight -
FINE_ADJUSTMENT / 2;
popupRef.current.options.offset = [FINE_ADJUSTMENT / 2, yOffset];
@@ -194,14 +204,14 @@ export default function BoundsDriftMarker({
? markerIconRect.right
: grayBoundsRect.right) +
anchorWidth / 2 -
popupRect.left;
markerRect.right;
popupRef.current.options.offset = [xOffset, DEFAULT_OFFSET[1]];
} else if (orientation === 'left') {
const xOffset =
(markerRect.left < grayBoundsRect.left
? markerRect.left
: grayBoundsRect.left) -
popupRect.right -
markerRect.left -
anchorWidth / 2;
popupRef.current.options.offset = [xOffset, DEFAULT_OFFSET[1]];
} else {
@@ -210,7 +220,7 @@ export default function BoundsDriftMarker({
? markerRect.top
: grayBoundsRect.top) -
FINE_ADJUSTMENT / 2 -
popupRect.bottom;
markerRect.y;
popupRef.current.options.offset = [FINE_ADJUSTMENT / 2, yOffset];
}
};
@@ -270,12 +280,30 @@ export default function BoundsDriftMarker({
e.target.closePopup();
};

// add click events for highlighting markers
const handleClick = (e: LeafletMouseEvent) => {
// Sometimes clicking throws off the popup's orientation, so reorient it
orientPopup(popupOrientationRef.current);
// Default popup behavior is to open on marker click
// Prevent by immediately closing it
e.target.closePopup();
// check the number of mouse click and enable function for single click only
if (e.originalEvent.detail === 1) {
if (setSelectedMarkers) {
if (selectedMarkers?.find((id) => id === props.id)) {
setSelectedMarkers((prevSelectedMarkers: string[]) =>
prevSelectedMarkers.filter((id: string) => id !== props.id)
);
} else {
// select
setSelectedMarkers((prevSelectedMarkers: string[]) => [
...prevSelectedMarkers,
props.id,
]);
}
}

// Sometimes clicking throws off the popup's orientation, so reorient it
orientPopup(popupOrientationRef.current);
// Default popup behavior is to open on marker click
// Prevent by immediately closing it
e.target.closePopup();
}
};

const handleDoubleClick = () => {
@@ -284,6 +312,22 @@ export default function BoundsDriftMarker({
}
};

// debounce single click to be prevented when double clicking marker
const debounceSingleClick = debounce(handleClick, 300);

// set this marker as highlighted
if (icon && selectedMarkers?.find((id) => id === props.id))
icon.options.className += ' highlight-marker';

// set this marker's popup as highlighted
if (popupContent && popupRef.current && popupRef.current._container) {
if (selectedMarkers?.find((id) => id === props.id)) {
popupRef.current._container.classList.add('marker-popup-highlight');
} else {
popupRef.current._container.classList.remove('marker-popup-highlight');
}
}

// DriftMarker misbehaves if icon=undefined is provided
// is this the most elegant way?
const optionalIconProp = icon ? { icon } : {};
@@ -296,9 +340,10 @@ export default function BoundsDriftMarker({
position={position}
// new way to handle mouse events
eventHandlers={{
click: (e: LeafletMouseEvent) => handleClick(e),
mouseover: (e: LeafletMouseEvent) => handleMouseOver(e),
mouseout: (e: LeafletMouseEvent) => handleMouseOut(e),
// debounce single click to be prevented when double clicking marker
click: debounceSingleClick,
mouseover: handleMouseOver,
mouseout: handleMouseOut,
dblclick: handleDoubleClick,
}}
zIndexOffset={zIndexOffset}
10 changes: 8 additions & 2 deletions packages/libs/components/src/map/BubbleMarker.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// import React from 'react';
import L from 'leaflet';
import BoundsDriftMarker, { BoundsDriftMarkerProps } from './BoundsDriftMarker';

@@ -26,11 +25,15 @@ export interface BubbleMarkerProps extends BoundsDriftMarkerProps {
* this is a SVG bubble marker icon
*/
export default function BubbleMarker(props: BubbleMarkerProps) {
const selectedMarkers = props.selectedMarkers;
const setSelectedMarkers = props.setSelectedMarkers;

const { html: svgHTML, diameter: size } = bubbleMarkerSVGIcon(props);

// set icon as divIcon
const SVGBubbleIcon = L.divIcon({
className: 'leaflet-canvas-icon', // may need to change this className but just leave it as it for now
className:
'leaflet-canvas-icon ' + 'marker-id-' + props.id + ' bubble-marker',
iconSize: new L.Point(size, size), // this will make icon to cover up SVG area!
iconAnchor: new L.Point(size / 2, size / 2), // location of topleft corner: this is used for centering of the icon like transform/translate in CSS
html: svgHTML, // divIcon HTML svg code generated above
@@ -74,6 +77,9 @@ export default function BubbleMarker(props: BubbleMarkerProps) {
},
}}
showPopup={props.showPopup}
// pass selectedMarkers state and setState
selectedMarkers={selectedMarkers}
setSelectedMarkers={setSelectedMarkers}
/>
);
}
11 changes: 9 additions & 2 deletions packages/libs/components/src/map/ChartMarker.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React from 'react';
import L from 'leaflet';

import BoundsDriftMarker, { BoundsDriftMarkerProps } from './BoundsDriftMarker';
@@ -46,11 +45,16 @@ export interface ChartMarkerProps
* - accordingly icon size could be reduced
*/
export default function ChartMarker(props: ChartMarkerProps) {
const selectedMarkers = props.selectedMarkers;
const setSelectedMarkers = props.setSelectedMarkers;

const { html: svgHTML, size, sumValuesString } = chartMarkerSVGIcon(props);

// set icon
let HistogramIcon: any = L.divIcon({
className: 'leaflet-canvas-icon',
// add class, highlight-chartmarker, for panning
className:
'leaflet-canvas-icon ' + 'marker-id-' + props.id + ' chart-marker',
iconSize: new L.Point(size, size),
iconAnchor: new L.Point(size / 2, size / 2), // location of topleft corner: this is used for centering of the icon like transform/translate in CSS
html: svgHTML, // divIcon HTML svg code generated above
@@ -116,6 +120,9 @@ export default function ChartMarker(props: ChartMarkerProps) {
}}
showPopup={props.showPopup}
popupClass="histogram-popup"
// pass // selectedMarkers state and setState
selectedMarkers={selectedMarkers}
setSelectedMarkers={setSelectedMarkers}
/>
);
}
Loading

0 comments on commit 4456176

Please sign in to comment.