-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
ProjectDashboard: Migrate Map and Teaser
- Loading branch information
Showing
20 changed files
with
15,111 additions
and
10,944 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
83 changes: 83 additions & 0 deletions
83
src/projects/components/Map/BackgroundSwitcher/BackgroundSwitcher.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
import { Fragment } from "react" | ||
import { Listbox, Transition } from "@headlessui/react" | ||
import { CheckIcon, ChevronDownIcon } from "@heroicons/react/20/solid" | ||
import clsx from "clsx" | ||
|
||
export type LayerType = "vector" | "satellite" | ||
|
||
const labels: { [index: string]: string } = { | ||
vector: "Kartenlayer einblenden", | ||
satellite: "Satellitenlayer einblenden", | ||
} | ||
|
||
type Props = { | ||
value: LayerType | ||
onChange: (_: LayerType) => void | ||
className: string | ||
} | ||
|
||
export const BackgroundSwitcher: React.FC<Props> = ({ value, onChange, className }) => { | ||
return ( | ||
<div className={className}> | ||
<Listbox value={value} onChange={onChange}> | ||
{({ open }) => ( | ||
<div className="relative mt-1"> | ||
<Listbox.Button className="relative cursor-default rounded-md border border-gray-300 bg-white py-2 pl-3 pr-10 text-left shadow-sm focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500 sm:text-sm"> | ||
<span className="block truncate">{labels[value]}</span> | ||
<span className="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2"> | ||
<ChevronDownIcon className="h-5 w-5 text-gray-400" aria-hidden="true" /> | ||
</span> | ||
</Listbox.Button> | ||
|
||
<Transition | ||
show={open} | ||
as={Fragment} | ||
leave="transition ease-in duration-100" | ||
leaveFrom="opacity-100" | ||
leaveTo="opacity-0" | ||
> | ||
<Listbox.Options className="absolute z-10 mt-1 max-h-60 overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm"> | ||
{Object.keys(labels).map((id) => ( | ||
<Listbox.Option | ||
key={id} | ||
className={({ active }) => | ||
clsx( | ||
active ? "bg-indigo-600 text-white" : "text-gray-900", | ||
"relative cursor-default select-none py-2 pl-3 pr-9" | ||
) | ||
} | ||
value={id} | ||
> | ||
{({ selected, active }) => ( | ||
<> | ||
<span | ||
className={clsx( | ||
selected ? "font-semibold" : "font-normal", | ||
"block truncate" | ||
)} | ||
> | ||
{labels[id]} | ||
</span> | ||
|
||
{selected ? ( | ||
<span | ||
className={clsx( | ||
active ? "text-white" : "text-indigo-600", | ||
"absolute inset-y-0 right-0 flex items-center pr-4" | ||
)} | ||
> | ||
<CheckIcon className="h-5 w-5" aria-hidden="true" /> | ||
</span> | ||
) : null} | ||
</> | ||
)} | ||
</Listbox.Option> | ||
))} | ||
</Listbox.Options> | ||
</Transition> | ||
</div> | ||
)} | ||
</Listbox> | ||
</div> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from "./BackgroundSwitcher" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
import { Routes, useParam } from "@blitzjs/next" | ||
import { Section, Subsection } from "@prisma/client" | ||
import { lineString } from "@turf/helpers" | ||
import clsx from "clsx" | ||
import maplibregl from "maplibre-gl" | ||
import "maplibre-gl/dist/maplibre-gl.css" | ||
import { useRouter } from "next/router" | ||
import React, { useState } from "react" | ||
import Map, { Layer, NavigationControl, ScaleControl, Source } from "react-map-gl" | ||
import { BackgroundSwitcher, LayerType } from "./BackgroundSwitcher/BackgroundSwitcher" | ||
import { sectionBbox, geometryStartEndPoint } from "./utils" | ||
|
||
export type BaseMapSections = (Section & { | ||
subsections: Pick<Subsection, "id" | "geometry">[] | ||
})[] | ||
|
||
type Props = { | ||
children?: React.ReactNode | ||
sections: BaseMapSections | ||
selectedSection?: Section | ||
className?: string | ||
isInteractive?: boolean | ||
} | ||
|
||
export const BaseMap: React.FC<Props> = ({ | ||
children, | ||
sections, | ||
selectedSection, | ||
className, | ||
isInteractive = true, | ||
}) => { | ||
const router = useRouter() | ||
const projectSlug = useParam("projectSlug", "string") | ||
|
||
const [hoveredSectionIds, setHoveredSectionIds] = useState<number[]>([]) | ||
|
||
const [selectedLayer, setSelectedLayer] = useState<LayerType>("vector") | ||
const handleLayerSwitch = (layer: LayerType) => { | ||
setSelectedLayer(layer) | ||
} | ||
|
||
const maptilerApiKey = "ECOoUBmpqklzSCASXxcu" | ||
const vectorStyle = `https://api.maptiler.com/maps/a4824657-3edd-4fbd-925e-1af40ab06e9c/style.json?key=${maptilerApiKey}` | ||
const satelliteStyle = `https://api.maptiler.com/maps/hybrid/style.json?key=${maptilerApiKey}` | ||
|
||
// Layer style for segments depending on selected section and segment (if | ||
// enableHover is true). | ||
type PickLineColor = { | ||
section: Section | ||
} | ||
const pickLineColor = ({ section }: PickLineColor) => { | ||
let lineColor = "#eab308" | ||
|
||
if (hoveredSectionIds.includes(section.id)) { | ||
lineColor = "#e6007d" | ||
} | ||
|
||
if (selectedSection && section.id === selectedSection.id) { | ||
lineColor = "#0a64ae" | ||
} | ||
|
||
return lineColor | ||
} | ||
|
||
const handleClick = async (e: mapboxgl.MapLayerMouseEvent) => { | ||
if (!isInteractive) return | ||
const sectionSlug = e?.features?.[0]?.properties?.sectionSlug | ||
if (sectionSlug) { | ||
await router.push(Routes.SectionDashboardPage({ projectSlug: projectSlug!, sectionSlug })) | ||
} | ||
} | ||
|
||
const interactiveLayerIds = sections | ||
.map((s) => s.subsections.map((ss) => `layer_${ss.id}`)) | ||
.flat() | ||
// TODO re-evaluate previous code: | ||
// selectedSection?.subSegments | ||
// .filter((segment) => segment !== selectedSegment) | ||
// .map((segment) => String(segment.id)) || []; | ||
|
||
const [cursorStyle, setCursorStyle] = useState("grab") | ||
const handleMouseEnter = (e: mapboxgl.MapLayerMouseEvent) => { | ||
if (!isInteractive) return | ||
setCursorStyle("pointer") | ||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment | ||
// @ts-ignore TODO find out why TS is unhappy | ||
setHoveredSectionIds(e.features.map((f) => f?.properties?.sectionId)) | ||
} | ||
const handleMouseLeave = () => { | ||
if (!isInteractive) return | ||
setCursorStyle("grab") | ||
} | ||
|
||
const [minX, minY, maxX, maxY] = sectionBbox(sections) | ||
|
||
if (!minX || !minY || !maxX || !maxY) return null | ||
|
||
return ( | ||
<div className={clsx(className, "relative h-full w-full")}> | ||
<Map | ||
mapLib={maplibregl} | ||
initialViewState={{ | ||
bounds: [minX, minY, maxX, maxY], | ||
fitBoundsOptions: { | ||
padding: 60, | ||
}, | ||
}} | ||
scrollZoom={false} | ||
mapStyle={selectedLayer === "vector" ? vectorStyle : satelliteStyle} | ||
onClick={handleClick} | ||
cursor={cursorStyle} | ||
onMouseEnter={handleMouseEnter} | ||
onMouseLeave={handleMouseLeave} | ||
interactiveLayerIds={interactiveLayerIds} | ||
> | ||
{children} | ||
|
||
{sections.map((section) => { | ||
return section.subsections.map((subsection) => { | ||
return ( | ||
<Source | ||
key={subsection.id} | ||
type="geojson" | ||
data={lineString(JSON.parse(subsection.geometry), { | ||
sectionSlug: section.slug, | ||
sectionId: section.id, | ||
})} | ||
> | ||
<Layer | ||
id={`layer_${subsection.id}`} | ||
type="line" | ||
paint={{ | ||
"line-width": 7, | ||
"line-color": pickLineColor({ | ||
section, | ||
}), | ||
}} | ||
/> | ||
</Source> | ||
) | ||
}) | ||
})} | ||
|
||
{sections | ||
// TODO re-evaluate this old code; I think we don't need this… | ||
// .filter((section) => section === selectedSection) | ||
.map((section) => { | ||
return section.subsections.map((subsection) => ( | ||
<Source | ||
key={`layer_dots_${subsection.id}`} | ||
type="geojson" | ||
data={geometryStartEndPoint(subsection.geometry)} | ||
> | ||
<Layer | ||
id={`layer_dots_${subsection.id}`} | ||
type="circle" | ||
paint={{ | ||
"circle-color": pickLineColor({ | ||
section, | ||
}), | ||
"circle-radius": 6, | ||
}} | ||
/> | ||
</Source> | ||
)) | ||
})} | ||
|
||
<NavigationControl showCompass={false} /> | ||
<ScaleControl /> | ||
</Map> | ||
<BackgroundSwitcher | ||
className="absolute top-4 left-4" | ||
value={selectedLayer} | ||
onChange={handleLayerSwitch} | ||
/> | ||
</div> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
export const SectionMarker: React.FC<{ number: number }> = ({ number }) => { | ||
return ( | ||
<div className="flex h-10 w-10 flex-none items-center justify-center rounded-full border-2 border-rsv-ochre bg-white pt-0.5 font-sans text-xl font-bold leading-none"> | ||
{number} | ||
</div> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from "./SectionMarker" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import { Section } from "@prisma/client" | ||
import clsx from "clsx" | ||
|
||
type Props = { | ||
section: Section | ||
className?: string | ||
} | ||
|
||
export const SectionPanel: React.FC<Props> = ({ section, className }) => { | ||
return ( | ||
<div className={clsx(className, "overflow-hidden rounded-md bg-white drop-shadow-sm")}> | ||
<div className="overflow-auto border-t-[10px] border-[#979797] py-4 px-2"> | ||
<h2 className="my-4 text-center text-[30px] font-semibold leading-[36px]"> | ||
Abschnitt {section.id} | ||
</h2> | ||
<p className="mb-4"> | ||
<strong>{section.name}</strong> | ||
</p> | ||
{/* <p>Status: {section.status}</p> | ||
<p>Segments: {section.subsections.length}</p> */} | ||
</div> | ||
</div> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from "./SectionPanel" |
Oops, something went wrong.