Skip to content

Commit

Permalink
projects: add project tiles for map including a fixed overlay for mob…
Browse files Browse the repository at this point in the history
…ile view
  • Loading branch information
vellip authored and hom3mad3 committed Nov 6, 2024
1 parent 9d8e4bd commit 0147eb5
Show file tree
Hide file tree
Showing 9 changed files with 211 additions and 22 deletions.
20 changes: 17 additions & 3 deletions meinberlin/assets/scss/components_user_facing/_project-tile.scss
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,33 @@ a.project-tile {
height: 100%;
}

&--horizontal {
&--horizontal:not(.project-tile--map) {
border-bottom: 1px solid $gray-lighter;
padding-bottom: 1.5em;
padding-top: 1.5em;
}
}

.project-tile__image-wrapper {
flex: 1
flex: 1;
min-width: 0;

.project-tile--map & {
flex: 0 0 auto;

@media screen and (min-width: $breakpoint-tablet) {
flex: 3;
}
}
}

.project-tile__image {
object-fit: cover;
width: 100%;
max-width: 100%;
height: auto;
max-height: 100%;
width: auto;
aspect-ratio: 1 / 1;
}

.project-tile__body {
Expand Down Expand Up @@ -91,4 +104,5 @@ a.project-tile {

.project-tile__timespan {
font-size: $font-size-sm;
font-weight: 400;
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
flex: 1;
}

.projects-list__map,
.modul-geomap,
.geomap-main,
.geomap-container,
Expand Down
54 changes: 53 additions & 1 deletion meinberlin/assets/scss/components_user_facing/_projects_map.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,55 @@
.projects-map {
height: 100%;
height: 100%;

.leaflet-bottom.leaflet-left {
position: static;
}
}

.project-overlay-control {
background: $white;
position: absolute;
bottom: 1.5em;
left: 1.5em;
right: 1.5em;
z-index: 1000;
padding: 0.5em;

@media screen and (min-width: $breakpoint-tablet) {
display: none;
}

.project-tile__image {
height: 175px;
}

a {
color: $text-base;
}
}

.projects-map__popup {
display: none;

@media screen and (min-width: $breakpoint-tablet) {
display: block;

}

.leaflet-popup-content-wrapper {
border-radius: 0;
}

.leaflet-popup-content {
margin: 1em;
}

.leaflet-popup-content a {
color: $text-base;
}

.leaflet-popup-tip-container {
display: none;
}
}

7 changes: 3 additions & 4 deletions meinberlin/react/plans/PlansListMapBox.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
/* global django */
import StickyBox from 'react-sticky-box'
import React, { Component } from 'react'
import FilterNav from './FilterNav'
import Toggles from './Toggles'
Expand Down Expand Up @@ -409,9 +408,9 @@ class PlansListMapBox extends Component {
{this.getPlansList(true)}
</div>
<div id="map" className="projects-list__map">
<StickyBox offsetTop={0} offsetBottom={0}>
{this.getPlansMap(true, 'topright')}
</StickyBox>
{/* <StickyBox offsetTop={0} offsetBottom={0}> */}
{this.getPlansMap(true, 'topright')}
{/* </StickyBox> */}
</div>
</div>
)
Expand Down
76 changes: 76 additions & 0 deletions meinberlin/react/projects/ProjectMapOverlay.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import React from 'react'
import L from 'leaflet'
import ReactDOM from 'react-dom'
import {
createContainerComponent,
createControlHook,
createElementHook,
createElementObject,
extendContext
} from '@react-leaflet/core'
import ProjectTile from './ProjectTile'

// Create the custom control class
const ProjectMapOverlayControl = L.Control.extend({
options: {
position: 'bottomleft'
},

onAdd: function (map) {
this._map = map
this._container = L.DomUtil.create('div', 'project-overlay-control')

if (this._content) {
this._container.appendChild(this._content)
}

L.DomEvent.disableClickPropagation(this._container)
L.DomEvent.disableScrollPropagation(this._container)

return this._container
},

onRemove: function (map) {
if (this._container) {
ReactDOM.unmountComponentAtNode(this._container)
L.DomUtil.remove(this._container)
this._container = null
}
},

addTo: function (map) {
L.Control.prototype.addTo.call(this, map)
this._container.classList.remove('leaflet-control')
return this
}
})

const useProjectMapOverlayElement = createElementHook((props, context) => {
const { position, children } = props

const control = new ProjectMapOverlayControl({ position })

// Create a div for React content
const contentDiv = L.DomUtil.create('div')
ReactDOM.render(children, contentDiv)
control._content = contentDiv

return createElementObject(control, extendContext(context, {
overlayContainer: control
}))
})

const useProjectMapOverlay = createControlHook(useProjectMapOverlayElement)
const ProjectMapOverlayContainer = createContainerComponent(useProjectMapOverlay)

const ProjectMapOverlay = ({ project, topicChoices, position = 'bottomleft', ...props }) => {
return project
? (
<ProjectMapOverlayContainer position={position} {...props}>
<ProjectTile project={project} isHorizontal isMapTile topicChoices={topicChoices} />
</ProjectMapOverlayContainer>
)
: null
}

export default ProjectMapOverlay
8 changes: 4 additions & 4 deletions meinberlin/react/projects/ProjectTile.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ function truncateText (item) {
}
}

const ProjectTile = ({ project, isHorizontal, topicChoices }) => {
const ProjectTile = ({ project, isHorizontal, topicChoices, isMapTile }) => {
const labelId = useId()
const describedById = useId()
const statusId = useId()
Expand All @@ -36,7 +36,7 @@ const ProjectTile = ({ project, isHorizontal, topicChoices }) => {
aria-labelledby={labelId}
id={describedById}
aria-describedby={describedById}
className={classNames('project-tile', isHorizontal ? 'project-tile--horizontal' : 'project-tile--vertical')}
className={classNames('project-tile', isHorizontal ? 'project-tile--horizontal' : 'project-tile--vertical', isMapTile && 'project-tile--map')}
>
<div className="project-tile__image-wrapper">
<ImageWithPlaceholder
Expand All @@ -52,13 +52,13 @@ const ProjectTile = ({ project, isHorizontal, topicChoices }) => {
</div>

<div className="project-tile__body">
<span className="project-tile__head">{project.district}</span>
{!isMapTile && <span className="project-tile__head">{project.district}</span>}
{project.topics?.length > 0 &&
<div className="project-tile__topics">
<ProjectTopics project={project} topicChoices={topicChoices} />
</div>}
<h3 className="project-tile__title" id={labelId}>{project.title}</h3>
{project.description && (
{project.description && !isMapTile && (
<p className="project-tile__description">
{truncateText(project.description)}
</p>
Expand Down
3 changes: 1 addition & 2 deletions meinberlin/react/projects/ProjectsListMapBox.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,6 @@ const ProjectsListMapBox = ({
</div>
{showMap &&
<div id="map" className="projects-list__map">
{/* <StickyBox offsetTop={0} offsetBottom={0}> */}
<ProjectsMap
attribution={attribution}
items={items}
Expand All @@ -167,8 +166,8 @@ const ProjectsListMapBox = ({
mapboxToken={mapboxToken}
omtToken={omtToken}
useVectorMap={useVectorMap}
topicChoices={topicChoices}
/>
{/* </StickyBox> */}
</div>}
</div>
</div>
Expand Down
61 changes: 55 additions & 6 deletions meinberlin/react/projects/ProjectsMap.jsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,69 @@
import React from 'react'
import 'leaflet.markercluster'
import { MapWithMarkers } from '../contrib/map/Map'
import { Popup } from 'react-leaflet'
import MarkerClusterLayer
from 'adhocracy4/adhocracy4/maps_react/static/a4maps_react/MarkerClusterLayer'
import GeoJsonMarker
from 'adhocracy4/adhocracy4/maps_react/static/a4maps_react/GeoJsonMarker'

const ProjectsMap = ({ items, ...props }) => {
import { Map } from '../contrib/map/Map'
import ProjectMapOverlay from './ProjectMapOverlay'
import ProjectTile from './ProjectTile'

const Markers = React.memo(({ items, topicChoices }) => {
const [activeProject, setActiveProject] = React.useState(null)

return (
<>
<MarkerClusterLayer>
{items
.filter(item => !!item.point)
.map(item => ({ ...item.point, properties: item }))
.map((project) => (
<GeoJsonMarker
key={project.title}
feature={project}
eventHandlers={{
popupopen: () => setActiveProject(project),
popupclose: () => setActiveProject(null)
}}
>
<Popup
className="projects-map__popup"
offset={[0, 270]}
maxWidth={400}
minWidth={400}
>
<ProjectTile
project={project.properties}
isHorizontal
topicChoices={topicChoices}
isMapTile
/>
</Popup>
</GeoJsonMarker>
))}
</MarkerClusterLayer>
<ProjectMapOverlay position="bottomleft" project={activeProject?.properties} topicChoices={topicChoices} />
</>
)
})
Markers.displayName = 'Markers'

const ProjectsMap = ({ items, topicChoices, ...props }) => {
return (
<div className="projects-map">
<MapWithMarkers
<Map
scrollWheelZoom={false}
zoomControl
maxZoom={18}
{...props}
points={items.filter(item => !!item.point).map(item => ({ ...item.point, properties: item }))}
id="project-map"
key="project-map"
style={{ minHeight: '100%', height: '100%' }}
className="projects-map__map"
/>
>
<Markers items={items} topicChoices={topicChoices} />
</Map>
</div>
)
}
Expand Down
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@
"react-markdown": "9.0.1",
"react-router-dom": "6.24.0",
"react-slick": "0.30.2",
"react-sticky-box": "2.0.5",
"sass": "1.77.6",
"sass-loader": "14.2.1",
"select2": "4.0.13",
Expand All @@ -64,8 +63,8 @@
"@babel/plugin-transform-runtime": "7.23.9",
"@babel/preset-env": "7.23.9",
"@babel/preset-react": "7.23.3",
"@testing-library/react": "14.2.1",
"@testing-library/jest-dom": "6.1.4",
"@testing-library/react": "14.2.1",
"babel-loader": "9.1.3",
"eslint": "8.57.0",
"eslint-config-standard": "17.1.0",
Expand Down

0 comments on commit 0147eb5

Please sign in to comment.