diff --git a/packages/tupaia-web/package.json b/packages/tupaia-web/package.json
index 9c896aa250..0602a5919d 100644
--- a/packages/tupaia-web/package.json
+++ b/packages/tupaia-web/package.json
@@ -15,7 +15,7 @@
"lint": "yarn package:lint",
"lint:fix": "yarn lint --fix",
"preview": "vite preview",
- "start-dev": "REACT_APP_DEPLOYMENT_NAME='dev' && vite",
+ "start-dev": "vite",
"start-fullstack": "npm-run-all -c -l -p start-servers start-frontend",
"start-central-server": "yarn workspace @tupaia/central-server start-dev",
"start-entity-server": "yarn workspace @tupaia/entity-server start-dev",
diff --git a/packages/tupaia-web/src/features/Map/Map.tsx b/packages/tupaia-web/src/features/Map/Map.tsx
index 4a8fac95fb..ee7b9d3baa 100644
--- a/packages/tupaia-web/src/features/Map/Map.tsx
+++ b/packages/tupaia-web/src/features/Map/Map.tsx
@@ -20,7 +20,7 @@ import { MapOverlaySelector } from './MapOverlaySelector';
import { useEntity, useMapOverlays } from '../../api/queries';
import { PolygonLayer } from './PolygonLayer';
import { MarkerLayer } from './MarkerLayer';
-import { useDefaultMapOverlay } from './useDefaultMapOverlay';
+import { useHiddenMapValues, useMapOverlayReport, useDefaultMapOverlay } from './utils';
const MapContainer = styled.div`
height: 100%;
@@ -91,13 +91,18 @@ const MapControlColumn = styled.div`
export const Map = () => {
const { projectCode, entityCode } = useParams();
- const [activeTileSet, setActiveTileSet] = useState(TILE_SETS[0]);
const { data: entity } = useEntity(entityCode);
// set the map default overlay if there isn't one selected
const { mapOverlaysByCode } = useMapOverlays(projectCode, entityCode);
useDefaultMapOverlay(projectCode!, mapOverlaysByCode);
+ // Setup legend hidden values
+ const { data: measureData } = useMapOverlayReport();
+ const { hiddenValues, setValueHidden } = useHiddenMapValues(measureData?.serieses);
+
+ // Setup Tile Picker
+ const [activeTileSet, setActiveTileSet] = useState(TILE_SETS[0]);
const onTileSetChange = (tileSetKey: string) => {
setActiveTileSet(TILE_SETS.find(({ key }) => key === tileSetKey) as typeof TILE_SETS[0]);
};
@@ -107,7 +112,7 @@ export const Map = () => {
-
+
@@ -115,7 +120,7 @@ export const Map = () => {
-
+
{
- return (
-
-
-
- );
-};
diff --git a/packages/tupaia-web/src/features/Map/MapLegend/MapLegend.tsx b/packages/tupaia-web/src/features/Map/MapLegend/MapLegend.tsx
index 9c3d09c650..718869085e 100644
--- a/packages/tupaia-web/src/features/Map/MapLegend/MapLegend.tsx
+++ b/packages/tupaia-web/src/features/Map/MapLegend/MapLegend.tsx
@@ -4,14 +4,62 @@
*/
import React from 'react';
+import { Legend, LegendProps } from '@tupaia/ui-map-components';
import { MobileMapLegend } from './MobileMapLegend';
-import { DesktopMapLegend } from './DesktopMapLegend';
+import { useSearchParams } from 'react-router-dom';
+import { MOBILE_BREAKPOINT, URL_SEARCH_PARAMS } from '../../../constants';
+import { useMapOverlayReport } from '../utils';
+import styled from 'styled-components';
+
+const DesktopWrapper = styled.div`
+ pointer-events: auto;
+
+ @media screen and (max-width: ${MOBILE_BREAKPOINT}) {
+ display: none;
+ }
+`;
+
+const SeriesDivider = styled.div`
+ height: 0;
+ border-width: 1px;
+ border-style: solid;
+ border-color: ${({ theme }) => (theme.palette.type === 'light' ? '#00000022' : '#ffffff22')};
+ width: calc(100% - 2rem);
+ margin: 0.3rem auto 0.2rem;
+
+ @media screen and (min-width: ${MOBILE_BREAKPOINT}) {
+ display: none;
+ }
+`;
+
+export const MapLegend = ({ hiddenValues, setValueHidden }: LegendProps) => {
+ const [urlSearchParams] = useSearchParams();
+ const selectedOverlay = urlSearchParams.get(URL_SEARCH_PARAMS.MAP_OVERLAY);
+ const { data: overlayReportData } = useMapOverlayReport();
+
+ if (!selectedOverlay) {
+ return null;
+ }
+
+ const LegendComponent = () => (
+
+ );
-export const MapLegend = () => {
return (
<>
-
-
+
+
+
+
+
+
>
);
};
diff --git a/packages/tupaia-web/src/features/Map/MapLegend/MobileMapLegend.tsx b/packages/tupaia-web/src/features/Map/MapLegend/MobileMapLegend.tsx
index 8094513b8a..9c1264e350 100644
--- a/packages/tupaia-web/src/features/Map/MapLegend/MobileMapLegend.tsx
+++ b/packages/tupaia-web/src/features/Map/MapLegend/MobileMapLegend.tsx
@@ -2,7 +2,7 @@
* Tupaia
* Copyright (c) 2017 - 2023 Beyond Essential Systems Pty Ltd
*/
-import React, { useState } from 'react';
+import React, { ReactNode, useState } from 'react';
import { Close, ExpandLess } from '@material-ui/icons';
import { Button, IconButton } from '@tupaia/ui-components';
import styled from 'styled-components';
@@ -10,8 +10,10 @@ import { MOBILE_BREAKPOINT } from '../../../constants';
const Wrapper = styled.div`
position: absolute;
- bottom: 1rem;
+ pointer-events: auto;
+ bottom: 0.8rem;
right: 1rem;
+ padding: 0;
@media screen and (min-width: ${MOBILE_BREAKPOINT}) {
display: none;
}
@@ -30,10 +32,11 @@ const ExpandIcon = styled(ExpandLess)`
`;
const ExpandedLegend = styled.div`
+ display: block;
background-color: ${({ theme }) => theme.mobile.background};
- height: 20rem;
- width: 12rem;
border-radius: 0.5rem;
+ padding-top: 0.5rem;
+ padding-bottom: 0.5rem;
`;
const CloseButton = styled(IconButton).attrs({
@@ -44,7 +47,7 @@ const CloseButton = styled(IconButton).attrs({
right: 0;
`;
-export const MobileMapLegend = () => {
+export const MobileMapLegend = ({ children }: { children: ReactNode }) => {
const [expanded, setExpanded] = useState(false);
const toggleExpanded = () => {
setExpanded(!expanded);
@@ -56,6 +59,7 @@ export const MobileMapLegend = () => {
+ {children}
) : (
diff --git a/packages/tupaia-web/src/features/Map/MapOverlaySelector/DesktopMapOverlaySelector.tsx b/packages/tupaia-web/src/features/Map/MapOverlaySelector/DesktopMapOverlaySelector.tsx
index 85ff06f677..0a0ebf507b 100644
--- a/packages/tupaia-web/src/features/Map/MapOverlaySelector/DesktopMapOverlaySelector.tsx
+++ b/packages/tupaia-web/src/features/Map/MapOverlaySelector/DesktopMapOverlaySelector.tsx
@@ -9,12 +9,12 @@ import { useParams } from 'react-router';
import { Accordion, Typography, AccordionSummary, AccordionDetails } from '@material-ui/core';
import { ExpandMore, Layers } from '@material-ui/icons';
import { periodToMoment } from '@tupaia/utils';
-import { MOBILE_BREAKPOINT, URL_SEARCH_PARAMS } from '../../../constants';
+import { MOBILE_BREAKPOINT } from '../../../constants';
import { Entity } from '../../../types';
-import { useMapOverlayReport, useMapOverlays } from '../../../api/queries';
+import { useMapOverlays } from '../../../api/queries';
+import { useMapOverlayReport } from '../utils';
import { MapOverlayList } from './MapOverlayList';
import { MapOverlaySelectorTitle } from './MapOverlaySelectorTitleSection';
-import { useDateRanges } from '../../../utils';
const MaxHeightContainer = styled.div`
max-height: 100%;
@@ -134,15 +134,8 @@ export const DesktopMapOverlaySelector = ({
toggleOverlayLibrary,
}: DesktopMapOverlaySelectorProps) => {
const { projectCode, entityCode } = useParams();
- const { hasMapOverlays, selectedOverlay } = useMapOverlays(projectCode, entityCode);
- const { startDate, endDate } = useDateRanges(
- URL_SEARCH_PARAMS.MAP_OVERLAY_PERIOD,
- selectedOverlay,
- );
- const { data: mapOverlayData } = useMapOverlayReport(projectCode, entityCode, selectedOverlay, {
- startDate,
- endDate,
- });
+ const { hasMapOverlays } = useMapOverlays(projectCode, entityCode);
+ const { data: mapOverlayData } = useMapOverlayReport();
return (
diff --git a/packages/tupaia-web/src/features/Map/MapOverlaySelector/MapOverlayDatePicker.tsx b/packages/tupaia-web/src/features/Map/MapOverlaySelector/MapOverlayDatePicker.tsx
index 3c8e676af6..8fb29e05d0 100644
--- a/packages/tupaia-web/src/features/Map/MapOverlaySelector/MapOverlayDatePicker.tsx
+++ b/packages/tupaia-web/src/features/Map/MapOverlaySelector/MapOverlayDatePicker.tsx
@@ -5,7 +5,8 @@
import React from 'react';
import { useParams } from 'react-router-dom';
import { Skeleton } from '@material-ui/lab';
-import { useMapOverlayReport, useMapOverlays } from '../../../api/queries';
+import { useMapOverlays } from '../../../api/queries';
+import { useMapOverlayReport } from '../utils';
import { DateRangePicker } from '../../../components';
import { useDateRanges } from '../../../utils';
import { URL_SEARCH_PARAMS } from '../../../constants';
@@ -23,15 +24,7 @@ export const MapOverlayDatePicker = () => {
periodGranularity,
} = useDateRanges(URL_SEARCH_PARAMS.MAP_OVERLAY_PERIOD, selectedOverlay);
- const { isLoading: isLoadingMapOverlayData } = useMapOverlayReport(
- projectCode,
- entityCode,
- selectedOverlay,
- {
- startDate,
- endDate,
- },
- );
+ const { isLoading: isLoadingMapOverlayData } = useMapOverlayReport();
if (!showDatePicker) return null;
return (
diff --git a/packages/tupaia-web/src/features/Map/MarkerLayer/MarkerLayer.tsx b/packages/tupaia-web/src/features/Map/MarkerLayer/MarkerLayer.tsx
index d10664a9e0..f4d3c1a2cb 100644
--- a/packages/tupaia-web/src/features/Map/MarkerLayer/MarkerLayer.tsx
+++ b/packages/tupaia-web/src/features/Map/MarkerLayer/MarkerLayer.tsx
@@ -7,14 +7,14 @@ import React from 'react';
import { useParams } from 'react-router';
import camelCase from 'camelcase';
import { useLocation, useNavigate } from 'react-router-dom';
-import { MarkerLayer as UIMarkerLayer, MeasureData } from '@tupaia/ui-map-components';
+import { LegendProps, MarkerLayer as UIMarkerLayer, MeasureData } from '@tupaia/ui-map-components';
import {
useEntitiesWithLocation,
useEntity,
- useMapOverlayReport,
useMapOverlays,
useProject,
} from '../../../api/queries';
+import { useMapOverlayReport } from '../utils';
import { EntityCode } from '../../../types';
import { processMeasureData } from './processMeasureData';
@@ -61,12 +61,12 @@ const useEntitiesByMeasureLevel = (measureLevel?: string) => {
);
};
-export const MarkerLayer = () => {
+export const MarkerLayer = ({ hiddenValues }: { hiddenValues: LegendProps['hiddenValues'] }) => {
const navigateToDashboard = useNavigateToDashboard();
const { projectCode, entityCode } = useParams();
const { selectedOverlay } = useMapOverlays(projectCode, entityCode);
const { data: entitiesData } = useEntitiesByMeasureLevel(selectedOverlay?.measureLevel);
- const { data: mapOverlayData } = useMapOverlayReport(projectCode, entityCode, selectedOverlay);
+ const { data: mapOverlayData } = useMapOverlayReport();
const { data: entity } = useEntity(entityCode);
if (!entitiesData || !mapOverlayData || !entity) {
@@ -84,8 +84,7 @@ export const MarkerLayer = () => {
entitiesData,
measureData: mapOverlayData.measureData,
serieses: mapOverlayData.serieses,
- // Implement this when we add the legend
- hiddenValues: {},
+ hiddenValues,
});
return (
diff --git a/packages/tupaia-web/src/features/Map/utils/index.ts b/packages/tupaia-web/src/features/Map/utils/index.ts
new file mode 100644
index 0000000000..5a0f1f6989
--- /dev/null
+++ b/packages/tupaia-web/src/features/Map/utils/index.ts
@@ -0,0 +1,8 @@
+/*
+ * Tupaia
+ * Copyright (c) 2017 - 2023 Beyond Essential Systems Pty Ltd
+ */
+
+export { useDefaultMapOverlay } from './useDefaultMapOverlay';
+export { useHiddenMapValues } from './useHiddenMapValues';
+export { useMapOverlayReport } from './useMapOverlayReport';
diff --git a/packages/tupaia-web/src/features/Map/useDefaultMapOverlay.ts b/packages/tupaia-web/src/features/Map/utils/useDefaultMapOverlay.ts
similarity index 93%
rename from packages/tupaia-web/src/features/Map/useDefaultMapOverlay.ts
rename to packages/tupaia-web/src/features/Map/utils/useDefaultMapOverlay.ts
index d555d6609d..bee0c3077f 100644
--- a/packages/tupaia-web/src/features/Map/useDefaultMapOverlay.ts
+++ b/packages/tupaia-web/src/features/Map/utils/useDefaultMapOverlay.ts
@@ -5,13 +5,13 @@
import { useEffect } from 'react';
import { useSearchParams } from 'react-router-dom';
-import { useProject } from '../../api/queries';
+import { useProject } from '../../../api/queries';
import {
DEFAULT_MAP_OVERLAY_ID,
DEFAULT_PERIOD_PARAM_STRING,
URL_SEARCH_PARAMS,
-} from '../../constants';
-import { MapOverlayGroup, ProjectCode, EntityCode } from '../../types';
+} from '../../../constants';
+import { MapOverlayGroup, ProjectCode, EntityCode } from '../../../types';
// When the map overlay groups change, update the default map overlay
export const useDefaultMapOverlay = (
diff --git a/packages/tupaia-web/src/features/Map/utils/useHiddenMapValues.ts b/packages/tupaia-web/src/features/Map/utils/useHiddenMapValues.ts
new file mode 100644
index 0000000000..5900eb15d0
--- /dev/null
+++ b/packages/tupaia-web/src/features/Map/utils/useHiddenMapValues.ts
@@ -0,0 +1,29 @@
+/*
+ * Tupaia
+ * Copyright (c) 2017 - 2023 Beyond Essential Systems Pty Ltd
+ */
+import { useState } from 'react';
+import { LegendProps, Series, Value } from '@tupaia/ui-map-components';
+
+export const useHiddenMapValues = (serieses: Series[] = []) => {
+ const [hiddenValues, setHiddenValues] = useState({});
+ const [prevSerieses, setPrevSerieses] = useState(serieses);
+
+ // reset hidden values when changing overlay or entity
+ if (JSON.stringify(serieses) !== JSON.stringify(prevSerieses)) {
+ setPrevSerieses(serieses);
+ const hiddenByDefault = serieses.reduce((values, { hideByDefault, key }) => {
+ return { ...values, [key]: hideByDefault };
+ }, {});
+ setHiddenValues(hiddenByDefault);
+ }
+
+ const setValueHidden = (key: string, value: Value, hidden: boolean) => {
+ setHiddenValues((currentState: LegendProps['hiddenValues']) => ({
+ ...currentState,
+ [key]: { ...currentState[key], [value!]: hidden },
+ }));
+ };
+
+ return { setValueHidden, hiddenValues };
+};
diff --git a/packages/tupaia-web/src/features/Map/utils/useMapOverlayReport.ts b/packages/tupaia-web/src/features/Map/utils/useMapOverlayReport.ts
new file mode 100644
index 0000000000..cf1b7671d3
--- /dev/null
+++ b/packages/tupaia-web/src/features/Map/utils/useMapOverlayReport.ts
@@ -0,0 +1,25 @@
+/*
+ * Tupaia
+ * Copyright (c) 2017 - 2023 Beyond Essential Systems Pty Ltd
+ */
+
+import { useParams } from 'react-router';
+import {
+ useMapOverlays,
+ useMapOverlayReport as useMapOverlayReportQuery,
+} from '../../../api/queries';
+import { useDateRanges } from '../../../utils';
+import { URL_SEARCH_PARAMS } from '../../../constants';
+
+export const useMapOverlayReport = () => {
+ const { projectCode, entityCode } = useParams();
+ const { selectedOverlay } = useMapOverlays(projectCode, entityCode);
+ const { startDate, endDate } = useDateRanges(
+ URL_SEARCH_PARAMS.MAP_OVERLAY_PERIOD,
+ selectedOverlay,
+ );
+ return useMapOverlayReportQuery(projectCode, entityCode, selectedOverlay, {
+ startDate,
+ endDate,
+ });
+};
diff --git a/packages/ui-map-components/src/components/Legend/Legend.tsx b/packages/ui-map-components/src/components/Legend/Legend.tsx
index 53d1acc678..bff18eec76 100644
--- a/packages/ui-map-components/src/components/Legend/Legend.tsx
+++ b/packages/ui-map-components/src/components/Legend/Legend.tsx
@@ -140,11 +140,10 @@ export const Legend = React.memo(
const { type } = series;
const LegendComponent = getLegendComponent(type as MeasureType);
return (
- <>
-
+
+
{legendsHaveSameType && {`${series.name}: `}}
{index < serieses.length - 1 && SeriesDivider && }
- >
+
);
});
})}
diff --git a/packages/ui-map-components/src/components/Legend/LegendEntry.tsx b/packages/ui-map-components/src/components/Legend/LegendEntry.tsx
index 9212c56788..90edc73925 100644
--- a/packages/ui-map-components/src/components/Legend/LegendEntry.tsx
+++ b/packages/ui-map-components/src/components/Legend/LegendEntry.tsx
@@ -60,7 +60,7 @@ export const LegendEntry = React.memo(
const handleClick = () => {
if (!unClickable && onClick) {
- onClick(dataKey, value, !hidden);
+ onClick(dataKey!, value, !hidden);
}
};
diff --git a/packages/ui-map-components/src/types/legend.ts b/packages/ui-map-components/src/types/legend.ts
index f8f8528ac1..7825956c6f 100644
--- a/packages/ui-map-components/src/types/legend.ts
+++ b/packages/ui-map-components/src/types/legend.ts
@@ -6,7 +6,7 @@
import { MarkerSeries, SpectrumSeries, Value } from './series';
export type LegendProps = {
- setValueHidden: (dataKey?: string, value?: Value, hidden?: boolean) => void;
+ setValueHidden: (dataKey: string, value: Value, hidden: boolean) => void;
hiddenValues: Record>;
};