diff --git a/app/javascript/pages/components/App.jsx b/app/javascript/pages/components/App.jsx index b952f675..dd4545b4 100644 --- a/app/javascript/pages/components/App.jsx +++ b/app/javascript/pages/components/App.jsx @@ -1,6 +1,6 @@ import React from 'react'; import { Route, Routes, Navigate, useParams } from 'react-router-dom'; - +import Community from './Community'; import Faq from './Faq'; import Gallery from './gallery/Gallery'; import Login from './Login'; @@ -42,6 +42,7 @@ const App = ({ muniOptions, tabOptions }) => ( path="/profile/:muni/:tab?" element={} /> + } /> } /> } /> } /> diff --git a/app/javascript/pages/components/Community.jsx b/app/javascript/pages/components/Community.jsx new file mode 100644 index 00000000..6bb3c988 --- /dev/null +++ b/app/javascript/pages/components/Community.jsx @@ -0,0 +1,15 @@ +import React from "react"; +import CommunitySelector from "../containers/CommunitySelector"; + +class Community extends React.Component { + render() { + return ( + + ); + } +} + +export default Community; diff --git a/app/javascript/pages/components/CommunityProfiles.jsx b/app/javascript/pages/components/CommunityProfiles.jsx index 2661c022..d54a9850 100644 --- a/app/javascript/pages/components/CommunityProfiles.jsx +++ b/app/javascript/pages/components/CommunityProfiles.jsx @@ -34,7 +34,7 @@ const CommunityProfiles = (props) => {
- {'< Back'} + {'< Back'}
diff --git a/app/javascript/pages/components/CommunitySelector.jsx b/app/javascript/pages/components/CommunitySelector.jsx index 027732e2..124525b5 100644 --- a/app/javascript/pages/components/CommunitySelector.jsx +++ b/app/javascript/pages/components/CommunitySelector.jsx @@ -25,6 +25,8 @@ class CommunitySelector extends React.Component { ); @@ -44,4 +46,4 @@ CommunitySelector.propTypes = { muniFill: PropTypes.shape(layerShape).isRequired, }; -export default CommunitySelector; +export default CommunitySelector; \ No newline at end of file diff --git a/app/javascript/pages/components/Home.jsx b/app/javascript/pages/components/Home.jsx index 6c31bacb..f6ae3736 100644 --- a/app/javascript/pages/components/Home.jsx +++ b/app/javascript/pages/components/Home.jsx @@ -1,13 +1,13 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { Link } from 'react-router-dom'; +import React from "react"; +import PropTypes from "prop-types"; +import { Link } from "react-router-dom"; -import Particles from './partials/Particles'; -import SearchBar from '../containers/SearchBar'; -import CategoryGrid from '../containers/CategoryGrid'; -import CommunitySelector from '../containers/CommunitySelector'; -import CallToAction from './partials/CallToAction'; -import CalendarImage from '../assets/images/calendar-home.svg'; +import Particles from "./partials/Particles"; +import SearchBar from "../containers/SearchBar"; +import CategoryGrid from "../containers/CategoryGrid"; +import CommunitySelector from "../containers/CommunitySelector"; +import CallToAction from "./partials/CallToAction"; +import CalendarImage from "../assets/images/calendar-home.svg"; class Home extends React.Component { constructor() { @@ -63,11 +63,19 @@ class Home extends React.Component {
- -
+
+
+

Community Profiles

+

+ a paragrah to explain what is community profile +

+ +
+
); } diff --git a/app/javascript/pages/components/MapBox.jsx b/app/javascript/pages/components/MapBox.jsx index 60ec8282..ff4aedc7 100644 --- a/app/javascript/pages/components/MapBox.jsx +++ b/app/javascript/pages/components/MapBox.jsx @@ -1,105 +1,150 @@ -import React from 'react'; -import PropTypes from 'prop-types'; - -import colors from '../constants/colors'; - -mapboxgl.accessToken = 'pk.eyJ1IjoiaWhpbGwiLCJhIjoiY2plZzUwMTRzMW45NjJxb2R2Z2thOWF1YiJ9.szIAeMS4c9YTgNsJeG36gg'; +import React from "react"; +import PropTypes from "prop-types"; +import mapboxgl from 'mapbox-gl'; +import { MAP_CONFIG } from '../constants/mapConfig'; +import { setupMouseEvents } from '../utils/mapEventHandlers'; +import { addMapLayer, updateMapLayers } from '../utils/layerManager'; +import mapcRegions from "../data/mapc-regions.json"; +import colors from "../constants/colors"; +mapboxgl.accessToken = MAP_CONFIG.accessToken; class MapBox extends React.Component { + state = { + finishedLoading: false, + showMapcRegions: false, + }; - constructor() { - super(...arguments) ; - - this.addLayer = this.addLayer.bind(this); - - this.state = { - finishedLoading: false, - } + componentDidMount() { + this.initializeMap(); } - - addLayer(layer = null) { - if (layer && !this.map.getSource(`ma-${layer.type}`)) { - this.map.addLayer({ - id: `ma-${layer.type}`, - type: layer.type, - source: { - type: 'geojson', - data: layer.geojson, - }, - paint: { - [`${layer.type}-color`]: colors.BRAND.PRIMARY, - }, - }); - } + componentDidUpdate() { + this.handleLayerUpdates(); } + componentWillUnmount() { + this.map?.remove(); + } - componentDidMount() { + initializeMap() { this.map = new mapboxgl.Map({ container: this.mapContainer, - style: 'mapbox://styles/ihill/ckeucj9gy9vt319qm4dxcn73l', - scrollZoom: false, + style: MAP_CONFIG.style, dragPan: false, dragRotate: false, - doubleClickZoom: false, - boxZoom: false, - interactive: false, ...this.props, }); - this.map.fitBounds([[-73.5081481933594, 41.1863288879395], [-69.8615341186523, 42.8867149353027]], { - padding: { top: 30, left: 300, right: 30, bottom: 30 }, + this.map.fitBounds(MAP_CONFIG.bounds, { + padding: MAP_CONFIG.padding, animate: false, - }) + }); - this.map.on('load', () => { - this.map.resize(); + this.map.addControl( + new mapboxgl.NavigationControl(MAP_CONFIG.navigationControl), + MAP_CONFIG.navigationControl.position + ); - if (this.props.layers) { - this.props.layers.forEach(this.addLayer); - } + this.map.on("load", () => this.onMapLoad()); + } - this.setState({ finishedLoading: true }); - }); + onMapLoad() { + this.map.resize(); + window.map = this.map; + + this.initializeHoverLayer(); + this.initializeMAPCRegions(); + + if (this.props.layers) { + this.props.layers.forEach(layer => addMapLayer(this.map, layer)); + } + + setupMouseEvents(this.map, this.props.muniPoly, this.props.toProfile); + this.setState({ finishedLoading: true }); } + initializeHoverLayer() { + if (!this.props.muniPoly) return; - componentDidUpdate() { - if ( - this.state.finishedLoading - && this.props.layers - ) { - this.props.layers.forEach(layer => { - if (layer) { - var source = this.map.getSource(`ma-${layer.type}`); - - if (source) { - source.setData(layer.geojson); - } - else { - this.addLayer(layer); - } - } - }); - } + this.map.addSource("hover-fill", { + type: "geojson", + data: this.props.muniPoly, + }); + + this.map.addLayer({ + id: "hover-fill", + type: "fill", + source: "hover-fill", + paint: { + "fill-color": colors.BRAND.PRIMARY, + "fill-opacity": 0, + }, + }); } + initializeMAPCRegions() { + this.map.addSource("mapc-region", { + type: "geojson", + data: mapcRegions, + }); - componentWillUnmount() { - this.map.remove(); + this.map.addLayer({ + id: "mapc-region-line", + type: "fill", + source: "mapc-region", + layout: { visibility: "none" }, + paint: { + "fill-color": "#006400", + "fill-opacity": 0.7, + }, + }); } + handleLayerUpdates() { + if (this.state.finishedLoading && this.props.layers) { + updateMapLayers(this.map, this.props.layers); + } + } + + toggleLayer = () => { + this.setState( + prevState => ({ + showMAPCRegions: !prevState.showMAPCRegions, + }), + () => { + this.map?.setLayoutProperty( + "mapc-region-line", + "visibility", + this.state.showMAPCRegions ? "visible" : "none" + ); + } + ); + }; render() { return (
-
this.mapContainer = el} /> +
+
+ +
Show MAPC regions
+
+
+
(this.mapContainer = el)} + />
); } - } MapBox.propTypes = { @@ -109,11 +154,14 @@ MapBox.propTypes = { zoom: PropTypes.number, minZoom: PropTypes.number, maxZoom: PropTypes.number, - layers: PropTypes.arrayOf(PropTypes.shape({ - type: PropTypes.string.required, - geojson: PropTypes.object.required, - })), + layers: PropTypes.arrayOf( + PropTypes.shape({ + type: PropTypes.string.isRequired, + geojson: PropTypes.object.isRequired, + }) + ), + muniPoly: PropTypes.object, + toProfile: PropTypes.func, }; export default MapBox; - diff --git a/app/javascript/pages/components/partials/Header.jsx b/app/javascript/pages/components/partials/Header.jsx index f27ef91c..b4afae7a 100644 --- a/app/javascript/pages/components/partials/Header.jsx +++ b/app/javascript/pages/components/partials/Header.jsx @@ -1,19 +1,19 @@ -import React from 'react'; -import { useLocation } from 'react-router-dom'; // 添加這行 -import logoImg from '../../assets/images/logo.svg'; +import React from "react"; +import { useLocation } from "react-router-dom"; +import logoImg from "../../assets/images/logo.svg"; -function handleActivePage(subdirectory, link = '/home') { +function handleActivePage(subdirectory, link = "/home") { if (subdirectory.startsWith(link)) { - return 'active'; - } if (subdirectory.startsWith('/calendar') && link === '/gallery') { - return 'active'; + return "active"; + } + if (subdirectory.startsWith("/calendar") && link === "/gallery") { + return "active"; } return null; } -// 移除 location prop,改用 useLocation hook const Header = () => { - const location = useLocation(); // 添加這行 + const location = useLocation(); return (
@@ -28,24 +28,24 @@ const Header = () => {