Skip to content

Commit

Permalink
Merge pull request #67 from natcap/task/32-legend-sortable
Browse files Browse the repository at this point in the history
Sortable legends
  • Loading branch information
dcdenu4 authored Sep 27, 2021
2 parents eef998b + 6ee010d commit 7d31ff2
Show file tree
Hide file tree
Showing 5 changed files with 164 additions and 31 deletions.
4 changes: 2 additions & 2 deletions map-viewer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,24 @@
"main": "src/index.js",
"private": true,
"dependencies": {
"@dnd-kit/core": "^3.1.1",
"@mapbox/mapbox-gl-draw": "^1.3.0",
"@mapbox/mapbox-gl-geocoder": "^4.7.0",
"@testing-library/jest-dom": "^5.11.9",
"@testing-library/react": "^11.2.5",
"@testing-library/user-event": "^12.8.3",
"array-move": "^4.0.0",
"bootstrap": "^4.6.0",
"d3": "^6.7.0",
"leaflet": "^1.7.1",
"mapbox-gl": "^2.2.0",
"postcss": "^8.3.5",
"prop-types": "^15.7.2",
"react": "^17.0.2",
"react-beautiful-dnd": "^13.1.0",
"react-bootstrap": "^1.6.1",
"react-dom": "^17.0.2",
"react-icons": "^4.2.0",
"react-scripts": "4.0.3",
"react-sortable-hoc": "^2.0.0",
"styled-components": "^5.2.3",
"turf": "^3.0.14",
"web-vitals": "^1.1.1",
Expand Down
28 changes: 27 additions & 1 deletion map-viewer/src/Map.css
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@
background-color: #f5f6f7;
border-radius: 4px;
line-height: 18px;
max-height: 200px;
max-height: 300px;
width: 340px;
overflow-y: auto;
}
Expand All @@ -193,6 +193,7 @@
margin-right: 5px;
}
.legend-desc {
padding-left: 2px;
padding-bottom: 5px;
font-weight: bold;
font-size: 13px;
Expand All @@ -219,9 +220,34 @@
height: 20px;
overflow-y: auto;
}
.d3-x-axis {
font-size: 12px;
}
.d3-x-axis:not(:last-child) {
padding-right: 4.75rem;
}
.legend-item {
font-size: 16px;
}
.sortable-container-helper {
z-index: 1;
font-family: 'Source Sans Pro', sans-serif;
line-height: 18px;
font-weight: 400;
padding-left: 20px;
}
.drag-handle {
font-size: 12px;
padding-left: 0;
padding-right: 0;
margin-right: 0;
cursor: grab;
}
.sortable-container {
}
.sortable-container:active {
}


.labelIcons {
font-size: 30px;
Expand Down
62 changes: 61 additions & 1 deletion map-viewer/src/Map.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ import mapLayers from './LayerDefinitions';
import { coastalHabitats } from './ScaleDefinitions';
import { protectedLayers } from './ScaleDefinitions';

import {arrayMoveImmutable} from 'array-move';


// import MyComponent from './components/MyComponent';

//mapboxgl.workerClass = MapboxWorker;
Expand Down Expand Up @@ -549,7 +552,8 @@ const Map = () => {
setLayers({...visibleLayersUpdate});
}
else {
selectedServicesUpdate.push(serviceResult);
//Using concat because we want newly added things in front of array
selectedServicesUpdate = [serviceResult].concat(selectedServicesUpdate);
//You're calling setNumbers and passing it the array it already has.
//You've changed one of its values but it's still the same array, and
//I suspect React doesn't see any reason to re-render because state
Expand All @@ -561,6 +565,7 @@ const Map = () => {
// Check 'all' for coastal protection case where we want this one
// layer visible across all scales
if((layer.scaleID === scale.current || layer.scaleID === 'all') && layer.serviceType === serviceResult) {
map.moveLayer(layer.layerID);
map.setLayoutProperty(layer.layerID, 'visibility', 'visible');
visibleLayersUpdate[serviceResult] = {...layer};

Expand Down Expand Up @@ -600,6 +605,60 @@ const Map = () => {
setMap(map);
}

const changeLayerOrder = (servicesSorted, oldIndex, newIndex) => {
console.log("change order");
console.log(selectedServices);
console.log(oldIndex + " : " + newIndex);
console.log(visibleLayers);
// Reverse the sorted services to start with the layers in the back
const reversedServices = servicesSorted.slice().reverse();
reversedServices.forEach((serviceType, i) => {
let zbackId = [];
// Add all layers from a service type if there are multiple of them
if(serviceType in multiFileLayers) {
multiFileLayers[serviceType].forEach((childLayer) => {
zbackId.push(childLayer.id);
});
}
else {
zbackId.push(visibleLayers[serviceType].layerID);
}

if(reversedServices.length < i+1) {
const nextService = reversedServices[i+1];
let zfrontId = [];
// Add all layers from a service type if there are multiple of them
if(nextService in multiFileLayers) {
multiFileLayers[nextService].forEach((childLayer) => {
zfrontId.push(childLayer.id);
});
}
else {
zfrontId.push(visibleLayers[nextService].layerID);
}
// Move each layer behind each next layer
zbackId.forEach((backLayerId) => {
zfrontId.forEach((frontLayerId) => {
map.moveLayer(backLayerId, frontLayerId);
});
});
}
else {
// We are at the most visible set of layers, just move them to the top.
zbackId.forEach((backLayerId) => {
map.moveLayer(backLayerId);
});
}
});

setServices(() => {
return arrayMoveImmutable(selectedServices, oldIndex, newIndex);
});
console.log("servicesSorted ", servicesSorted);
console.log("selectedServices: after order change ", [...selectedServices]);
setMap(map);
};

return (
<Col className="map-container" ref={mapContainer} >
<VerticalMenu
Expand All @@ -611,6 +670,7 @@ const Map = () => {
<Legend
layers={visibleLayers}
services={selectedServices}
changeLayerOrder={changeLayerOrder}
/>
<BasemapControl className="basemap-control"
basemaps={basemaps}
Expand Down
24 changes: 12 additions & 12 deletions map-viewer/src/ScaleDefinitions.js
Original file line number Diff line number Diff line change
Expand Up @@ -511,8 +511,8 @@ export const supportMenuDetails = [

export const coastalHabitats = [
{
id: "wetland",
label: "Wetlands",
id: "forest-scrub",
label: "Forest / Scrub",
helpText: {
text: ``,
metric: "N/A",
Expand All @@ -536,8 +536,8 @@ export const coastalHabitats = [
legend: false,
},
{
id: "seagrass",
label: "Seagrass",
id: "wetland",
label: "Wetlands",
helpText: {
text: ``,
metric: "N/A",
Expand All @@ -561,8 +561,8 @@ export const coastalHabitats = [
legend: false,
},
{
id: "mangroves",
label: "Mangroves",
id: "seagrass",
label: "Seagrass",
helpText: {
text: ``,
metric: "N/A",
Expand All @@ -586,8 +586,8 @@ export const coastalHabitats = [
legend: false,
},
{
id: "forest-scrub",
label: "Forest / Scrub",
id: "saltmarsh",
label: "Saltmarsh",
helpText: {
text: ``,
metric: "N/A",
Expand All @@ -611,8 +611,8 @@ export const coastalHabitats = [
legend: false,
},
{
id: "coral-reef",
label: "Coral Reefs",
id: "mangroves",
label: "Mangroves",
helpText: {
text: ``,
metric: "N/A",
Expand All @@ -636,8 +636,8 @@ export const coastalHabitats = [
legend: false,
},
{
id: "saltmarsh",
label: "Saltmarsh",
id: "coral-reef",
label: "Coral Reefs",
helpText: {
text: ``,
metric: "N/A",
Expand Down
77 changes: 62 additions & 15 deletions map-viewer/src/components/Legend.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,15 @@ import Row from 'react-bootstrap/Row';
import D3Legend from './D3Legend';
import InfoPopover from './InfoPopover';

import {
sortableContainer,
sortableElement,
sortableHandle,
} from 'react-sortable-hoc';
import {arrayMoveImmutable} from 'array-move';

import { GrDrag } from 'react-icons/gr';

const legendStyle = {
'sediment': {
id: 'sediment',
Expand Down Expand Up @@ -102,32 +111,69 @@ const legendStyle = {
},
}

const DragHandle = sortableHandle(() =>
<span>{<GrDrag/>}</span>);

const SortableItem = sortableElement(({value, index}) => (
<ListGroup.Item key={`legendStyle-${index}`} className="legend-item">
<Row>
<Col className="drag-handle" xs="auto">
<DragHandle />
</Col>
<Col className="legend-desc" xs="auto">
{legendStyle[value].desc}
</Col>
</Row>
<Row>
<Col>
<D3Legend serviceType={value} legendStyle={legendStyle}/>
</Col>
<Col xs="auto">
<InfoPopover
key={`legend-popover-${value}`}
content={legendStyle[value].info}
/>
</Col>
</Row>
</ListGroup.Item>
));

const SortableContainer = sortableContainer(({children}) => {
return <ul className="sortable-container">{children}</ul>;
});


const Legend = (props) => {

function handleDragEnd({oldIndex, newIndex}) {
const sortedServices = arrayMoveImmutable(props.services, oldIndex, newIndex);
props.changeLayerOrder(sortedServices, oldIndex, newIndex);
}

const renderLegend = (serviceType, i) => {
return (
<ListGroup.Item key={`legendStyle-${i}`} className="legend-container">
<div className="legend-desc">{legendStyle[serviceType].desc}</div>
<Row>
<Col>
<D3Legend serviceType={serviceType} legendStyle={legendStyle}/>
</Col>
<Col xs="auto">
<InfoPopover
key={`legend-popover-${serviceType}`}
content={legendStyle[serviceType].info}
/>
</Col>
</Row>
</ListGroup.Item>
<SortableItem
className="sortable-item"
key={`item-${serviceType}-${i}`}
index={i}
value={serviceType} />
);
};

if (props.services.length > 0) {
return (
<ListGroup variant="flush" className="legend-group">
{props.services.map(renderLegend)}
<SortableContainer
onSortEnd={handleDragEnd}
helperClass="sortable-container-helper"
axis='y'
//lockAxis='y'
//lockToContainerEdges={true}
lockOffset='0%'
transitionDuration='600'
useDragHandle>
{props.services.map(renderLegend)}
</SortableContainer>
</ListGroup>
);
}
Expand All @@ -139,6 +185,7 @@ const Legend = (props) => {
Legend.propTypes = {
layers: PropTypes.object.isRequired,
services: PropTypes.array.isRequired,
changeLayerOrder: PropTypes.func.isRequired,
}

export default Legend;

0 comments on commit 7d31ff2

Please sign in to comment.