From 2163e7a4ab930745ee0ee7d52995d38e50befe04 Mon Sep 17 00:00:00 2001 From: Marcel Date: Fri, 22 Nov 2024 13:18:48 +0000 Subject: [PATCH 1/4] [front-end] Add ApplyFilters component and integrate filter application in Panel --- apps/front-end/src/components/panel/Panel.tsx | 23 ++++++++++ .../panel/applyFilters/ApplyFilters.tsx | 38 +++++++++++++++++ .../panel/searchPanel/SearchPanel.tsx | 42 ++++--------------- .../panel/searchPanel/searchBox/SearchBox.tsx | 11 +++-- 4 files changed, 75 insertions(+), 39 deletions(-) create mode 100644 apps/front-end/src/components/panel/applyFilters/ApplyFilters.tsx diff --git a/apps/front-end/src/components/panel/Panel.tsx b/apps/front-end/src/components/panel/Panel.tsx index 79134f93..aa89a057 100644 --- a/apps/front-end/src/components/panel/Panel.tsx +++ b/apps/front-end/src/components/panel/Panel.tsx @@ -8,6 +8,7 @@ import CloseButton from "../common/closeButton/CloseButton"; import AboutPanel from "./aboutPanel/AboutPanel"; import DirectoryPanel from "./directoryPanel/DirectoryPanel"; import SearchPanel from "./searchPanel/SearchPanel"; +import ApplyFilters from "./applyFilters/ApplyFilters"; import ResultsPanel from "../panel/resultsPanel/ResultsPanel"; import { useAppDispatch, useAppSelector } from "../../app/hooks"; import { @@ -18,7 +19,13 @@ import { selectPanelOpen, selectResultsPanelOpen, openPanel, + openResultsPanel, } from "./panelSlice"; +import { + performSearch, + selectIsFilterActive, +} from "../panel/searchPanel/searchSlice"; +import { useTranslation } from "react-i18next"; const StyledPanel = styled(Drawer)(() => ({ display: "flex", @@ -36,6 +43,7 @@ const StyledPanel = styled(Drawer)(() => ({ visibility: "visible !important", overflow: "visible", boxShadow: "0 0 20px rgba(0, 0, 0, 0.16)", + borderRight: "none", }, })); @@ -52,6 +60,8 @@ const Panel = () => { const isOpen = useAppSelector(selectPanelOpen); const selectedTab = useAppSelector(selectSelectedTab); const resultsOpen = useAppSelector(selectResultsPanelOpen); + const isFilterActive = useAppSelector(selectIsFilterActive); + const { t } = useTranslation(); const isMedium = useMediaQuery("(min-width: 897px)"); @@ -81,6 +91,12 @@ const Panel = () => { dispatch(setSelectedTab(0)); }; + const onApplyFilters = async () => { + console.log(`Applying filters`); + await dispatch(performSearch()); + dispatch(openResultsPanel()); + }; + return ( <> {/* Desktop view */} @@ -137,6 +153,13 @@ const Panel = () => { boxShadow: "0 -2px 10px rgba(0, 0, 0, 0.2)", }} > + {selectedTab === 2 && ( + + )} ({ + width: "100%", + display: "flex", + justifyContent: "center", + position: "sticky", + padding: "var(--spacing-medium)", + backgroundColor: "#fff", + boxShadow: "0 0 20px rgba(0, 0, 0, 0.16)", + margin: 0, + "@media (min-width: 897px)": { + display: "none", + }, +})); + +interface ApplyFiltersProps { + buttonText: string; + disabled: boolean; + buttonAction: () => void; +} + +const ApplyFilters = ({buttonText, buttonAction, disabled}: ApplyFiltersProps) => { + return ( + + + {buttonText} + + + ); +}; + +export default ApplyFilters; diff --git a/apps/front-end/src/components/panel/searchPanel/SearchPanel.tsx b/apps/front-end/src/components/panel/searchPanel/SearchPanel.tsx index 36e825d0..98e52b78 100644 --- a/apps/front-end/src/components/panel/searchPanel/SearchPanel.tsx +++ b/apps/front-end/src/components/panel/searchPanel/SearchPanel.tsx @@ -5,10 +5,7 @@ import Heading from "../heading/Heading"; import ContentPanel from "../contentPanel/ContentPanel"; import SelectBox from "../../common/selectBox/SelectBox"; import SearchBox from "./searchBox/SearchBox"; -import StandardButton from "../../common/standardButton/StandardButton"; import Typography from "@mui/material/Typography"; -import Box from "@mui/material/Box"; -import { styled } from "@mui/material/styles"; import useMediaQuery from "@mui/material/useMediaQuery"; import { useAppDispatch, useAppSelector } from "../../../app/hooks"; import { @@ -23,20 +20,6 @@ import { import { selectTotalItemsCount } from "../../map/mapSlice"; import { openResultsPanel } from "../panelSlice"; -const StyledButtonContainer = styled(Box)(() => ({ - width: "100%", - display: "flex", - justifyContent: "center", - position: "sticky", - padding: "var(--spacing-medium)", - backgroundColor: "rgba(255, 255, 255, 0.25) !important", - boxShadow: "0 0 20px rgba(0, 0, 0, 0.16)", - zIndex: 1, - "@media (min-width: 897px)": { - display: "none", - }, -})); - const SearchPanel = () => { const dispatch = useAppDispatch(); const { t } = useTranslation(); @@ -65,15 +48,9 @@ const SearchPanel = () => { if (isMedium) dispatch(openResultsPanel()); }; - const onApplyFilters = async () => { - console.log(`Applying filters`); - await dispatch(performSearch()); - dispatch(openResultsPanel()); - }; - const onSubmitSearch = async () => { - console.log(`Searching for '${submittedText}'`); dispatch(setText(currentText)); + console.log(`Searching for '${submittedText}'`); await dispatch(performSearch()); if (isMedium) dispatch(openResultsPanel()); }; @@ -87,7 +64,12 @@ const SearchPanel = () => { return (
{ /> ))} - - {!isMedium && ( - - {t("apply_filters")} - - )} - ); }; diff --git a/apps/front-end/src/components/panel/searchPanel/searchBox/SearchBox.tsx b/apps/front-end/src/components/panel/searchPanel/searchBox/SearchBox.tsx index 6cc8dda4..c2ea7aba 100644 --- a/apps/front-end/src/components/panel/searchPanel/searchBox/SearchBox.tsx +++ b/apps/front-end/src/components/panel/searchPanel/searchBox/SearchBox.tsx @@ -42,9 +42,7 @@ const StyledIconButton = styled(IconButton)(() => ({ interface SearchBoxProps { value: string; onChange: (e: React.ChangeEvent) => void; - onSubmit: ( - e: React.KeyboardEvent, - ) => void; + onSubmit: () => void; clearSearch?: () => void; } @@ -63,8 +61,13 @@ const SearchBox = ({ value={value} onChange={onChange} onKeyUp={(event) => { - if (event.key === "Enter") onSubmit(event); + if (event.key === "Enter") { + event.currentTarget.blur(); + onSubmit(); + } }} + onBlur={onSubmit} + onSubmit={onSubmit} autoComplete="off" placeholder={t("search")} startAdornment={ From 572da285c5b41ba8ec4090158bad37803d602d49 Mon Sep 17 00:00:00 2001 From: rogup Date: Fri, 22 Nov 2024 17:00:07 +0000 Subject: [PATCH 2/4] [front-end] Fix clunky spiderfy cluster popup behaviour --- apps/front-end/src/components/map/mapLibre.ts | 41 ++++++++++++------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/apps/front-end/src/components/map/mapLibre.ts b/apps/front-end/src/components/map/mapLibre.ts index c8718153..aa813d58 100644 --- a/apps/front-end/src/components/map/mapLibre.ts +++ b/apps/front-end/src/components/map/mapLibre.ts @@ -172,16 +172,25 @@ export const createMap = ( e: MapLayerMouseEvent, leafOffset: [number, number], ) => { - const coordinates = feature.geometry.coordinates.slice() as LngLatLike; + const coordinates = feature.geometry.coordinates.slice(); const itemIx = feature.properties?.ix; - openPopup( - map, - itemIx, - coordinates, - popupCreatedCallback, - popupClosedCallback, - leafOffset, - ); + map + .easeTo({ + center: [ + coordinates[0], + getMapCentreLatOffsetted(coordinates[1], map.getZoom()), + ], + }) + .once("moveend", () => { + openPopup( + map, + itemIx, + coordinates as LngLatLike, + popupCreatedCallback, + popupClosedCallback, + leafOffset, + ); + }); }, onLeafHover: ( feature: GeoJSON.Feature, @@ -218,13 +227,17 @@ export const createMap = ( 1, 0, )) as GeoJSON.Feature[]; - const zoom = await source.getClusterExpansionZoom( + const clusterExpansionZoom = await source.getClusterExpansionZoom( clusterFeature.properties?.cluster_id, ); + if (map.getZoom() >= 18 && clusterExpansionZoom > 18) { + // This is a cluster that needs to be spiderfied, so don't need to fly anymore + return; + } map.flyTo({ center: features[0].geometry.coordinates as LngLatLike, - zoom: zoom ?? undefined, + zoom: clusterExpansionZoom ?? undefined, speed: 1.5, }); }); @@ -237,9 +250,9 @@ export const createMap = ( const coordinates = feature.geometry.coordinates.slice(); const itemIx = feature.properties?.ix; - // fly to a position so that the popup is fully visible + // ease to a position so that the popup is fully visible map - .flyTo({ + .easeTo({ center: [ coordinates[0], getMapCentreLatOffsetted(coordinates[1], map.getZoom()), @@ -337,7 +350,7 @@ export const createMap = ( popup = undefined; }); - map.on("movestart", () => { + map.on("dragstart", () => { // Close popup when the user moves the map popup?.remove(); popupIx = undefined; From 83e072c1675b6e45242b3bc0344bb099ec2833fb Mon Sep 17 00:00:00 2001 From: rogup Date: Fri, 22 Nov 2024 17:15:03 +0000 Subject: [PATCH 3/4] [front-end] Fix minZoom so continents aren't seen twice --- apps/front-end/src/components/map/mapLibre.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/apps/front-end/src/components/map/mapLibre.ts b/apps/front-end/src/components/map/mapLibre.ts index aa813d58..71de548e 100644 --- a/apps/front-end/src/components/map/mapLibre.ts +++ b/apps/front-end/src/components/map/mapLibre.ts @@ -97,11 +97,11 @@ export const createMap = ( const map = new MapLibreGL.Map({ container: "map-container", style: `https://api.maptiler.com/maps/streets-v2/style.json?key=${import.meta.env.VITE_MAPTILER_API_KEY}`, - minZoom: 1.1, + minZoom: 1.45, maxZoom: 18, bounds: [ - [-180, -59.9], - [180, 83], + [-169, -49.3], + [189, 75.6], ], attributionControl: false, }); @@ -377,6 +377,10 @@ export const createMap = ( } }); + map.on("moveend", () => { + console.log("aaaaa", map.getZoom(), map.getBounds()); + }); + map.on("zoomstart", () => { spiderfy.unspiderfyAll(); }); From 1cc465b2cd2629bf239fc5cc0f8fa5a40286b800 Mon Sep 17 00:00:00 2001 From: rogup Date: Fri, 22 Nov 2024 17:15:15 +0000 Subject: [PATCH 4/4] [front-end] Toggle popup when marker clicked --- apps/front-end/src/components/map/mapLibre.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/apps/front-end/src/components/map/mapLibre.ts b/apps/front-end/src/components/map/mapLibre.ts index 71de548e..517ab33f 100644 --- a/apps/front-end/src/components/map/mapLibre.ts +++ b/apps/front-end/src/components/map/mapLibre.ts @@ -41,6 +41,11 @@ const openPopup = async ( popupClosedCallback: () => void, offset?: [number, number], ) => { + if (popup?.isOpen() && popupIx === itemIx) { + console.log(`Popup for item @${itemIx} already open`); + return; + } + console.log(`Open popup for item @${itemIx} ${coordinates}`); // Shift the popup up a bit so it doesn't cover the marker @@ -250,6 +255,16 @@ export const createMap = ( const coordinates = feature.geometry.coordinates.slice(); const itemIx = feature.properties?.ix; + if (popup?.isOpen() && popupIx === itemIx) { + console.log( + `Popup for item @${itemIx} already open so toggle closed`, + ); + popup?.remove(); + popupIx = undefined; + popup = undefined; + return; + } + // ease to a position so that the popup is fully visible map .easeTo({ @@ -339,6 +354,8 @@ export const createMap = ( // Remove previous popup - remove listener to prevent looping back and confusing React code popup?.off("close", popupClosedCallback); popup?.remove(); + popupIx = undefined; + popup = undefined; flyToThenOpenPopupRecursive(); });