diff --git a/public/static/locales/en/maps.json b/public/static/locales/en/maps.json index e1f6db05e..3743dbaeb 100644 --- a/public/static/locales/en/maps.json +++ b/public/static/locales/en/maps.json @@ -47,6 +47,40 @@ "showProjectDetails": "Show Project Details", "hideProjectList": "Hide Project List", "hideProjectDetails": "Hide Project Details", - "countries": "Countries" + "countries": "Countries", + "layers": { + "dataYears": "Data Years", + "covariates": "Covariates", + "description": "Description", + "resolution": "Resolution", + "underlyingData": "Underlying Data", + "forests": "Forests", + "soil": "Soil", + "biodiversity": "Biodiversity", + "risks": "Risks", + "forestLayers": { + "forestCover": "Forest Cover", + "forestBiomass": "Forest Biomass", + "potentialAdditionalForestBiomass": "Potential Additional Forest Biomass", + "deforestation": "Deforestation", + "canopyHeight": "Canopy Height" + }, + "soilLayers": { + "soilNitrogen": "Soil Nitrogen", + "soilPH": "Soil pH", + "soilOrganicCarbon": "Soil Organic Carbon", + "soilBulkDensity": "Soil Bulk Density" + }, + "biodiversityLayers": { + "treeSpeciesDensity": "Tree Species Density", + "birdsDensity": "Birds Density", + "mammalsDensity": "Mammals Density", + "amphibiansDensity": "Amphibians Density" + }, + "risksLayers": { + "fireRisk": "Fire Risk", + "deforestationRisk": "Deforestation Risk" + } + } } } diff --git a/src/features/projectsV2/ProjectListControls/ProjectListControlForMobile.tsx b/src/features/projectsV2/ProjectListControls/ProjectListControlForMobile.tsx index 5115654d0..e1b0c64b0 100644 --- a/src/features/projectsV2/ProjectListControls/ProjectListControlForMobile.tsx +++ b/src/features/projectsV2/ProjectListControls/ProjectListControlForMobile.tsx @@ -133,6 +133,7 @@ const ProjectListControlForMobile = ({ )} diff --git a/src/features/projectsV2/ProjectsMap/MapFeatureExplorer/MapFeatureExplorer.module.scss b/src/features/projectsV2/ProjectsMap/MapFeatureExplorer/MapFeatureExplorer.module.scss index a86a4d424..7734fc60b 100644 --- a/src/features/projectsV2/ProjectsMap/MapFeatureExplorer/MapFeatureExplorer.module.scss +++ b/src/features/projectsV2/ProjectsMap/MapFeatureExplorer/MapFeatureExplorer.module.scss @@ -9,26 +9,117 @@ .exploreMainContainer { max-width: 182px; - background-color: $backgroundColor; + background-color: #fff; border-radius: 12px; margin-top: 10px; - padding: 14px 16px; + padding: 12px; box-shadow: 0px 6px 10px rgba(0, 0, 0, 0.12); + max-height: 485px; + + &::-webkit-scrollbar { + display: none; + } @include xsPhoneView { position: absolute; + max-height: max-content; + min-width: 293px; + border-radius: unset; + right: 0px; + top: -10px; } } -.exploreContainer { - width: 150px; +.exploreItemsContainer { + max-height: 400px; + overflow-y: auto; + + &::-webkit-scrollbar { + display: none; + } + + @include xsPhoneView { + max-height: max-content; + } +} + +.exploreFeatureMobileHeader { + display: flex; + justify-content: space-between; + padding: 3px 4px 18px; + + .exploreLabel { + display: flex; + gap: 8px; + + p { + font-size: 14px; + font-weight: 700; + gap: 8px; + } + } + + svg { + width: 16px; + } +} + +.exploreItemSection { + background: rgba(0, 122, 73, 0.05); + border-radius: 8px; + padding: 8px 9px; + font-size: 12px; + margin-bottom: 10px; + + h2 { + font-weight: 600; + margin-bottom: 14px; + } +} + +.layerSwitchContainer { + display: flex; + justify-content: space-between; + align-items: center; + + .mapLayer { + cursor: pointer; + } + + hr { + border-top: 1px dashed rgba(189, 189, 189, 1); + margin-top: 0px; + margin-bottom: 0px; + } +} + +.layerInfoPopupContainer { + display: flex; + flex-direction: column; + gap: 8px; + max-width: 189px; + background: #fff; + padding: 12px; + font-size: 10px; + + .label { + font-weight: 600; + color: rgba(130, 130, 130, 1); + text-transform: uppercase; + } + .source { + color: $primaryDarkColor; + } } .toggleMainContainer { + padding: 15px 9px; height: 14px; display: flex; align-items: center; justify-content: space-between; + background: rgba(0, 122, 73, 0.05); + border-radius: 8px; } .toggleContainer { @@ -86,11 +177,8 @@ } .exploreDescription { - padding: 8px; height: fit-content; - margin-top: 14px; - background-color: rgba(var(--explore-description-background-color-new), 1); - font-size: $fontXXSmall; + font-size: var(--font-xx-extra-small); border-radius: 6px; } diff --git a/src/features/projectsV2/ProjectsMap/MapFeatureExplorer/MapSettings.tsx b/src/features/projectsV2/ProjectsMap/MapFeatureExplorer/MapSettings.tsx index f654dd3f7..d546725dc 100644 --- a/src/features/projectsV2/ProjectsMap/MapFeatureExplorer/MapSettings.tsx +++ b/src/features/projectsV2/ProjectsMap/MapFeatureExplorer/MapSettings.tsx @@ -1,40 +1,99 @@ -import type { ChangeEvent, FC } from 'react'; +import type { FC } from 'react'; import type { MapOptions } from '../../ProjectsMapContext'; +import type { SetState } from '../../../common/types/common'; import styles from './MapFeatureExplorer.module.scss'; -import { MapLayerToggle } from '.'; +// // import { MapLayerToggle } from '.'; // import InfoIcon from '../../../../../public/assets/images/icons/projectV2/InfoIcon'; -import { StyledSwitch } from './CustomSwitch'; +// import { StyledSwitch } from './CustomSwitch'; // import { YearRangeSlider } from '.'; import { useTranslations } from 'next-intl'; // import themeProperties from '../../../../theme/themeProperties'; +import MapLayerControlPanel from './microComponents/MapLayerControlPanel'; +import ExploreDropdownHeaderMobile from './microComponents/ExploreDropdownHeaderMobile'; type MapSettingsProps = { mapOptions: MapOptions; updateMapOption: (option: keyof MapOptions, value: boolean) => void; + isMobile?: boolean; + setIsOpen?: SetState; }; -const MapSettings: FC = ({ mapOptions, updateMapOption }) => { +const MapSettings: FC = ({ + mapOptions, + updateMapOption, + isMobile, + setIsOpen, +}) => { const tAllProjects = useTranslations('AllProjects'); const tMaps = useTranslations('Maps'); - /* const { - primaryColorNew, - restorationToggleColorNew, - deforestrationToggleColorNew, - } = themeProperties; */ - + // const { primaryColorNew } = themeProperties; + // const forestConfig = [ + // { + // label: tMaps('layers.forestLayers.canopyHeight'), + // color: undefined, + // showDivider: true, + // shouldRender: true, + // additionalInfo: { + // dataYears: 2018, + // resolution: '1m', + // description: 'Global canopy height between year 2018-2020', + // underlyingData: + // 'Global Canopy Height Maps based on AI model (DinoV2) and remote sensing data (MAXAR and GEDI) by Meta', + // source: + // 'https://sustainability.atmeta.com/blog/2024/04/22/using-artificial-intelligence-to-map-the-earths-forests/', + // covariates: 'Tolan et al. 2024', + // }, + // }, + // { + // label: tMaps('layers.forestLayers.deforestation'), + // color: primaryColorNew, + // showDivider: true, + // shouldRender: true, + // additionalInfo: { + // dataYears: 2023, + // resolution: '30m', + // description: 'Location of deforestation in previous year', + // underlyingData: 'Landsat satellite programs', + // source: 'https://www.science.org/doi/10.1126/science.1244693', + // covariates: 'Hansen et al. 2013', + // }, + // }, + // { + // label: tMaps('layers.forestLayers.forestBiomass'), + // color: primaryColorNew, + // showDivider: true, + // shouldRender: true, + // additionalInfo: { + // dataYears: 2023, + // resolution: '500km', + // description: 'Tree cover as a binary map', + // underlyingData: + // 'AI model built by Google to classify Sentinel II images in 9 different land use and land cover classes', + // source: 'https://dynamicworld.app/', + // covariates: 'Dynamic World', + // }, + // }, + // ]; + const projectConfig = [ + { + label: tAllProjects('projects'), + color: undefined, + showDivider: false, + shouldRender: true, + }, + ]; return (
-
-
+
+
{/* } label={tAllProjects('currentForests')} switchComponent={ } - /> -
+ /> */} + {/*
} label={tAllProjects('restorationPotential')} @@ -49,10 +108,9 @@ const MapSettings: FC = ({ mapOptions, updateMapOption }) => { switchComponent={ } - /> -
*/} - */} + {/*
*/} + {/* = ({ mapOptions, updateMapOption }) => { ) => updateMapOption('showProjects', checked)} /> } + /> */} + {isMobile && setIsOpen && ( + + )} + + {/* */}
{tMaps('3trilliontrees')} diff --git a/src/features/projectsV2/ProjectsMap/MapFeatureExplorer/index.tsx b/src/features/projectsV2/ProjectsMap/MapFeatureExplorer/index.tsx index 4a508f162..6c8e34d81 100644 --- a/src/features/projectsV2/ProjectsMap/MapFeatureExplorer/index.tsx +++ b/src/features/projectsV2/ProjectsMap/MapFeatureExplorer/index.tsx @@ -8,6 +8,7 @@ import styles from './MapFeatureExplorer.module.scss'; // import PlayIcon from '../../../../../public/assets/images/icons/projectV2/PlayIcon'; import CustomButton from './CustomButton'; import MapSettings from './MapSettings'; +import { Modal } from '@mui/material'; /* interface ExploreProjectProps { label: string | string[]; @@ -17,7 +18,6 @@ import MapSettings from './MapSettings'; } */ interface EcosystemOptionProps { - infoIcon: React.ReactNode; label: string; switchComponent: React.ReactNode; } @@ -28,7 +28,6 @@ export interface YearRangeSliderProps { } export const MapLayerToggle = ({ - infoIcon, label, switchComponent, }: EcosystemOptionProps) => { @@ -36,7 +35,6 @@ export const MapLayerToggle = ({ <>
-
{infoIcon}
{label}
{switchComponent}
@@ -96,11 +94,13 @@ export const MapLayerToggle = ({ type MapFeatureExplorerProps = { mapOptions: MapOptions; updateMapOption: (option: keyof MapOptions, value: boolean) => void; + isMobile?: boolean; }; const MapFeatureExplorer = ({ mapOptions, updateMapOption, + isMobile, }: MapFeatureExplorerProps) => { const t = useTranslations('Maps'); const [isOpen, setIsOpen] = useState(false); @@ -114,12 +114,22 @@ const MapFeatureExplorer = ({ {t('explore')} - {isOpen && ( + {isOpen && !isMobile && ( )} + {isMobile && ( + + + + )}
); }; diff --git a/src/features/projectsV2/ProjectsMap/MapFeatureExplorer/microComponents/ExploreDropdownHeaderMobile.tsx b/src/features/projectsV2/ProjectsMap/MapFeatureExplorer/microComponents/ExploreDropdownHeaderMobile.tsx new file mode 100644 index 000000000..812571960 --- /dev/null +++ b/src/features/projectsV2/ProjectsMap/MapFeatureExplorer/microComponents/ExploreDropdownHeaderMobile.tsx @@ -0,0 +1,27 @@ +import type { SetState } from '../../../../common/types/common'; + +import { useTranslations } from 'next-intl'; +import { ExploreIcon } from '../../../../../../public/assets/images/icons/projectV2/ExploreIcon'; +import CrossIcon from '../../../../../../public/assets/images/icons/projectV2/CrossIcon'; +import styles from '../MapFeatureExplorer.module.scss'; + +interface Props { + setIsOpen: SetState; +} + +const ExploreDropdownHeaderMobile = ({ setIsOpen }: Props) => { + const tMaps = useTranslations('Maps'); + return ( +
+
+ +

{tMaps('explore')}

+
+ +
+ ); +}; + +export default ExploreDropdownHeaderMobile; diff --git a/src/features/projectsV2/ProjectsMap/MapFeatureExplorer/microComponents/LayerInfoPopupContent.tsx b/src/features/projectsV2/ProjectsMap/MapFeatureExplorer/microComponents/LayerInfoPopupContent.tsx new file mode 100644 index 000000000..93e94943a --- /dev/null +++ b/src/features/projectsV2/ProjectsMap/MapFeatureExplorer/microComponents/LayerInfoPopupContent.tsx @@ -0,0 +1,68 @@ +import type { AdditionalInfo } from './MapLayerControlPanel'; +import type { SetState } from '../../../../common/types/common'; + +import { useTranslations } from 'next-intl'; +import styles from '../MapFeatureExplorer.module.scss'; + +interface Props { + additionalInfo: AdditionalInfo | undefined; + handleMouseLeave: () => void; + setAnchorEl: SetState; + anchorEl: HTMLDivElement | null; +} + +const LayerInfoPopupContent = ({ + additionalInfo, + handleMouseLeave, + setAnchorEl, + anchorEl, +}: Props) => { + const tMaps = useTranslations('Maps'); + return ( +
setAnchorEl(anchorEl)} + onMouseLeave={handleMouseLeave} + > + {additionalInfo?.dataYears && ( +
+

{tMaps('layers.dataYears')}

+

{additionalInfo?.dataYears}

+
+ )} + {additionalInfo?.resolution && ( +
+

{tMaps('layers.resolution')}

+

~{additionalInfo?.resolution}

+
+ )} + {additionalInfo?.description && ( +
+

{tMaps('layers.description')}

+

{additionalInfo.description}

+
+ )} + {additionalInfo?.underlyingData && ( +
+

{tMaps('layers.underlyingData')}

+

{additionalInfo.underlyingData}

+
+ )} + {additionalInfo?.covariates && ( +
+

{tMaps('layers.covariates')}

+ + {additionalInfo.covariates} + +
+ )} +
+ ); +}; + +export default LayerInfoPopupContent; diff --git a/src/features/projectsV2/ProjectsMap/MapFeatureExplorer/microComponents/LayerSwitchContainer.tsx b/src/features/projectsV2/ProjectsMap/MapFeatureExplorer/microComponents/LayerSwitchContainer.tsx new file mode 100644 index 000000000..9e50a4776 --- /dev/null +++ b/src/features/projectsV2/ProjectsMap/MapFeatureExplorer/microComponents/LayerSwitchContainer.tsx @@ -0,0 +1,80 @@ +import type { AdditionalInfo } from './MapLayerControlPanel'; +import type { ReactNode } from 'react'; + +// import { useState } from 'react'; +import styles from '../MapFeatureExplorer.module.scss'; +// import { Popover } from '@mui/material'; +// import LayerInfoPopupContent from './LayerInfoPopupContent'; + +interface Props { + label: string; + switchComponent: ReactNode; + showDivider: boolean; + additionalInfo?: AdditionalInfo; +} + +const LayerSwitchContainer = ({ + label, + switchComponent, + showDivider, +}: Props) => { + // const [anchorEl, setAnchorEl] = useState(null); + + // const handleMouseEnter = (e: React.MouseEvent) => { + // setAnchorEl(e.currentTarget); + // }; + + // const handleMouseLeave = () => { + // setAnchorEl(null); + // }; + + return ( + <> +
+
+

{label}

+ {showDivider &&
} +
+
{switchComponent}
+
+ {/* {showDivider &&
} + {showDivider && ( + setAnchorEl(null)} + anchorEl={anchorEl} + anchorOrigin={{ + vertical: 'top', + horizontal: 'left', + }} + transformOrigin={{ + vertical: 'top', + horizontal: 'center', + }} + disableRestoreFocus + sx={{ + pointerEvents: 'auto', // Enable pointer events + '& .MuiPaper-root': { + borderRadius: '12px', + marginTop: '-8px', + pointerEvents: 'auto', // Ensure interaction inside Popover works + }, + }} + > + + + )} */} + + ); +}; + +export default LayerSwitchContainer; diff --git a/src/features/projectsV2/ProjectsMap/MapFeatureExplorer/microComponents/MapLayerControlPanel.tsx b/src/features/projectsV2/ProjectsMap/MapFeatureExplorer/microComponents/MapLayerControlPanel.tsx new file mode 100644 index 000000000..a78e41f61 --- /dev/null +++ b/src/features/projectsV2/ProjectsMap/MapFeatureExplorer/microComponents/MapLayerControlPanel.tsx @@ -0,0 +1,69 @@ +import type { ChangeEvent } from 'react'; +import type { MapOptions } from '../../../ProjectsMapContext'; + +import { StyledSwitch } from '../CustomSwitch'; +import styles from '../MapFeatureExplorer.module.scss'; +import LayerSwitchContainer from './LayerSwitchContainer'; + +export type AdditionalInfo = { + dataYears: number; + resolution: string; + description: string; + underlyingData: string; + source: string; + covariates: string; +}; + +interface Props { + category?: string; + exploreConfig: { + label: string; + color: string | undefined; + showDivider: boolean; + additionalInfo?: AdditionalInfo; + shouldRender: boolean; + }[]; + mapOptions?: MapOptions; + updateMapOption?: (option: keyof MapOptions, value: boolean) => void; +} + +const MapLayerControlPanel = ({ + category, + exploreConfig, + mapOptions, + updateMapOption, +}: Props) => { + return ( +
+ {category &&

{category}

} +
+ {exploreConfig.map((item) => { + if (!item.shouldRender) return <>; + return ( + , + checked: boolean + ) => { + if (updateMapOption) + updateMapOption('showProjects', checked); + }} + /> + } + label={item.label} + additionalInfo={item.additionalInfo} + /> + ); + })} +
+
+ ); +}; + +export default MapLayerControlPanel;