Skip to content

Commit 79f435d

Browse files
Merge pull request #1101 from CodeForPhilly/staging
Weekly PR from Staging to Main
2 parents 3bae6d7 + 035a758 commit 79f435d

File tree

8 files changed

+178
-8
lines changed

8 files changed

+178
-8
lines changed

.github/auto_assign.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ useReviewGroups: true
1414
# A list of reviewers, split into different groups, to be added to pull requests (GitHub user name)
1515
reviewGroups:
1616
backend:
17-
- zigouras
17+
- null
1818
frontend:
1919
- CodeWritingCow
2020

next.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ module.exports = {
66
PROJECT_ROOT: __dirname,
77
},
88
images: {
9-
domains: ['storage.googleapis.com'],
9+
domains: ['storage.googleapis.com', 'cloud.maptiler.com'],
1010
},
1111
async redirects() {
1212
return [

package-lock.json

Lines changed: 20 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
},
1414
"dependencies": {
1515
"@heroicons/react": "^2.1.5",
16+
"@maptiler/sdk": "^2.5.1",
1617
"@maptiler/geocoding-control": "^1.4.1",
1718
"@nextui-org/react": "^2.4.6",
1819
"@phosphor-icons/react": "^2.1.7",

src/components/MapStyleSwitcher.tsx

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
'use client';
2+
import React, { useEffect, useState } from 'react';
3+
import Image from 'next/image';
4+
5+
interface MapStyleSwitcherProps {
6+
handleStyleChange: (style: string) => void;
7+
}
8+
9+
const MapStyleSwitcher: React.FC<MapStyleSwitcherProps> = ({
10+
handleStyleChange,
11+
}) => {
12+
const [activeStyle, setActiveStyle] = useState('DATAVIZ');
13+
const [isHovered, setIsHovered] = useState(false);
14+
type BaseMap = {
15+
name: string;
16+
img: string;
17+
};
18+
19+
type BaseMaps = Record<string, BaseMap>;
20+
21+
const baseMaps: BaseMaps = {
22+
DATAVIZ: {
23+
name: 'DataVisualization',
24+
img: 'https://cloud.maptiler.com/static/img/maps/dataviz.png',
25+
},
26+
HYBRID: {
27+
name: 'Hybrid',
28+
img: 'https://cloud.maptiler.com/static/img/maps/hybrid.png',
29+
},
30+
STREETS: {
31+
name: 'Street',
32+
img: 'https://cloud.maptiler.com/static/img/maps/streets.png',
33+
},
34+
};
35+
36+
useEffect(() => {
37+
handleStyleChange(baseMaps[activeStyle].name);
38+
}, [activeStyle]);
39+
40+
const onClick = (key: string) => {
41+
setActiveStyle(key);
42+
handleStyleChange(baseMaps[key].name);
43+
};
44+
45+
return (
46+
<div
47+
className="relative maplibregl-ctrl maplibregl-ctrl-basemaps p-2 w-1/5"
48+
onMouseEnter={() => setIsHovered(true)}
49+
onMouseLeave={() => setIsHovered(false)}
50+
>
51+
<Image
52+
src={baseMaps[activeStyle].img}
53+
alt={activeStyle}
54+
width={65}
55+
height={65}
56+
title={activeStyle.toLowerCase()}
57+
className="cursor-pointer w-16 h-16 rounded-md border-2 border-gray-400 z-10"
58+
/>
59+
60+
<div
61+
className={`absolute flex flex-row items-center pl-2 transition-transform duration-300 ${
62+
isHovered
63+
? 'translate-x-20 opacity-100'
64+
: 'translate-x-0 opacity-0 pointer-events-none'
65+
}`}
66+
style={{
67+
left: '4rem',
68+
top: '50%',
69+
transform: 'translateY(-50%)',
70+
}}
71+
>
72+
{Object.keys(baseMaps)
73+
.filter((key) => key !== activeStyle)
74+
.map((key) => (
75+
<Image
76+
key={key}
77+
src={baseMaps[key].img}
78+
alt={key}
79+
width={65}
80+
height={65}
81+
title={key.toLowerCase()}
82+
onClick={() => onClick(key)}
83+
className={`cursor-pointer rounded-md border-2 border-transparent hover:border-gray-400`}
84+
/>
85+
))}
86+
</div>
87+
</div>
88+
);
89+
};
90+
91+
export default MapStyleSwitcher;

src/components/PropertyMap.tsx

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
'use client';
2-
2+
import '../components/components-css/PropertyMap.css';
33
import {
44
FC,
55
useEffect,
@@ -26,6 +26,7 @@ import Map, {
2626
} from 'react-map-gl/maplibre';
2727
import maplibregl, {
2828
Map as MaplibreMap,
29+
IControl,
2930
PointLike,
3031
MapGeoJSONFeature,
3132
ColorSpecification,
@@ -50,6 +51,13 @@ import { centroid } from '@turf/centroid';
5051
import { Position } from 'geojson';
5152
import { toTitleCase } from '../utilities/toTitleCase';
5253
import { ThemeButton } from '../components/ThemeButton';
54+
import MapStyleSwitcher from './MapStyleSwitcher';
55+
56+
type MapStyle = {
57+
url: string;
58+
};
59+
60+
type MapStyles = Record<string, MapStyle>;
5361

5462
type SearchedProperty = {
5563
coordinates: [number, number];
@@ -105,15 +113,30 @@ const layerStylePoints: CircleLayerSpecification = {
105113
},
106114
};
107115

116+
const mapStyles: MapStyles = {
117+
DataVisualization: {
118+
url: `https://api.maptiler.com/maps/dataviz/style.json?key=${maptilerApiKey}`,
119+
},
120+
Hybrid: {
121+
url: `https://api.maptiler.com/maps/hybrid/style.json?key=${maptilerApiKey}`,
122+
},
123+
Street: {
124+
url: `https://api.maptiler.com/maps/streets/style.json?key=${maptilerApiKey}`,
125+
},
126+
};
127+
108128
// info icon in legend summary
109129
let summaryInfo: ReactElement | null = null;
110130

111-
const MapControls = () => {
131+
const MapControls: React.FC<{
132+
handleStyleChange: (styleName: string) => void;
133+
}> = ({ handleStyleChange }) => {
112134
const [smallScreenToggle, setSmallScreenToggle] = useState<boolean>(false);
113135
return (
114136
<>
115137
<NavigationControl showCompass={false} position="bottom-right" />
116138
<GeolocateControl position="bottom-right" />
139+
<MapStyleSwitcher handleStyleChange={handleStyleChange} />
117140
{smallScreenToggle || window.innerWidth > 640 ? (
118141
<MapLegendControl
119142
position="bottom-left"
@@ -162,7 +185,10 @@ const PropertyMap: FC<PropertyMapProps> = ({
162185
const { appFilter } = useFilter();
163186
const [popupInfo, setPopupInfo] = useState<any | null>(null);
164187
const [map, setMap] = useState<MaplibreMap | null>(null);
165-
const [mapController, setMapController] = useState();
188+
const [mapController, setMapController] = useState<IControl>();
189+
const [currentStyle, setCurrentStyle] = useState<string>(
190+
'Data Visualization View'
191+
);
166192
const [searchedProperty, setSearchedProperty] = useState<SearchedProperty>({
167193
coordinates: [-75.1628565788269, 39.97008211622267],
168194
address: '',
@@ -181,6 +207,10 @@ const PropertyMap: FC<PropertyMapProps> = ({
181207
handleMapClick(e.lngLat);
182208
};
183209

210+
const handleStyleChange = (styleName: string) => {
211+
setCurrentStyle(styleName);
212+
};
213+
184214
const moveMap = (targetPoint: LngLatLike) => {
185215
if (map) {
186216
map.easeTo({
@@ -387,7 +417,7 @@ const PropertyMap: FC<PropertyMapProps> = ({
387417
if (map) {
388418
updateFilter();
389419
}
390-
}, [map, appFilter]);
420+
}, [map, appFilter, currentStyle]);
391421

392422
const changeCursor = (e: any, cursorType: 'pointer' | 'default') => {
393423
e.target.getCanvas().style.cursor = cursorType;
@@ -399,7 +429,7 @@ const PropertyMap: FC<PropertyMapProps> = ({
399429
<Map
400430
mapLib={maplibregl as any}
401431
initialViewState={initialViewState}
402-
mapStyle={`https://api.maptiler.com/maps/dataviz/style.json?key=${maptilerApiKey}`}
432+
mapStyle={mapStyles[currentStyle]?.url}
403433
onMouseEnter={(e) => changeCursor(e, 'pointer')}
404434
onMouseLeave={(e) => changeCursor(e, 'default')}
405435
onClick={onMapClick}
@@ -423,8 +453,20 @@ const PropertyMap: FC<PropertyMapProps> = ({
423453
onSourceData={(e) => {
424454
handleSetFeatures(e);
425455
}}
456+
onStyleData={(e) => {
457+
const layerIds = e.target
458+
.getStyle()
459+
.layers.map((layer: any) => layer.id);
460+
const layersApplied = layers.every((layer) =>
461+
layerIds.includes(layer)
462+
);
463+
if (layersApplied) {
464+
setHasLoadingError(false);
465+
}
466+
}}
426467
onMoveEnd={handleSetFeatures}
427468
>
469+
<MapControls handleStyleChange={handleStyleChange} />
428470
<div
429471
className="geocoding"
430472
style={{
@@ -467,7 +509,6 @@ const PropertyMap: FC<PropertyMapProps> = ({
467509
}}
468510
/>
469511
</div>
470-
<MapControls />
471512
{popupInfo && (
472513
<Popup
473514
className="customized-map-popup"
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
.map-style-button {
2+
position: absolute;
3+
top: 10px;
4+
left: 10px;
5+
z-index: 1;
6+
padding: 10px 15px;
7+
background: white;
8+
border: 1px solid #ccc;
9+
border-radius: 5px;
10+
cursor: pointer;
11+
transition: all 0.3s ease;
12+
}
13+
14+
.map-style-button:hover {
15+
background: #e0e0e0;
16+
}

src/components/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ export { default as Hotjar } from './Hotjar';
33
export { default as Footer } from './Footer';
44
export { default as PropertyMap } from './PropertyMap';
55
export { default as PropertyDetailSection } from './PropertyDetailSection';
6+
export { default as MapStyleSwitcher } from './MapStyleSwitcher';
67
export { default as SidePanel } from './SidePanel';
78
export { default as SidePanelControlBar } from './SidePanelControlBar';
89
export { default as FilterView } from './FilterView';

0 commit comments

Comments
 (0)