Skip to content

Commit

Permalink
feat(ts/components/markers): move vehicle marker into own file (#2859)
Browse files Browse the repository at this point in the history
* feat(ts/components/markers): move vehicle marker into own file

* chore(ts/components/vehicleMarker): add imports

* chore(ts/components/vehicleMarker): convert `Leaflet` reference to named import

* feat(ts/components/vehicleMarker): move `vehicleMarker` css into own file
  • Loading branch information
firestack authored Oct 21, 2024
1 parent 493e088 commit 0d224a6
Show file tree
Hide file tree
Showing 9 changed files with 273 additions and 246 deletions.
65 changes: 0 additions & 65 deletions assets/css/_vehicle_map.scss
Original file line number Diff line number Diff line change
Expand Up @@ -15,71 +15,6 @@ $map-component-z-index: (
}
}

.c-vehicle-map__label {
svg {
overflow: visible;
}

&.primary {
font-size: var(--font-size-s);
font-weight: 500;
}

&.secondary {
@include font-tiny;
}

&.selected .c-vehicle-icon__label-background {
fill: $color-eggplant-300;
}
}

.c-vehicle-map__icon {
path {
fill: $color-primary-legacy;
stroke: $color-bg-base;
stroke-width: 2;

&.on-time {
fill: $color-vehicle-ontime;
}

&.early.early-red {
fill: $color-vehicle-red;
}
&.early.early-blue {
fill: $color-vehicle-blue;
}

&.late.early-red {
fill: $color-vehicle-blue;
}
&.late.early-blue {
fill: $color-vehicle-red;
}

&.off-course {
fill: $color-vehicle-off-course;
}

&.logged-out {
fill: $color-gray-300;
stroke: $color-gray-600;
stroke-width: 1px;
}

&.selected {
stroke: $color-eggplant-600;
stroke-width: 1.5;
filter: drop-shadow(1px 1px 4px $color-eggplant-400);
}
}

svg {
overflow: visible;
}
}

.c-vehicle-properties-panel__location .c-vehicle-map .leaflet-right {
padding-right: calc(env(safe-area-inset-right) - #{$vpp-location-padding});
}
Expand Down
1 change: 1 addition & 0 deletions assets/css/app.scss
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ $vpp-location-padding: 1rem;
@import "map/markers/missed_stop_icon";
@import "map/markers/stop_icon";
@import "map/markers/user_location_marker";
@import "map/markers/vehicle_marker";
@import "minimal_ladder_page";
@import "minischedule";
@import "modal";
Expand Down
64 changes: 64 additions & 0 deletions assets/css/map/markers/_vehicle_marker.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
.c-vehicle-map__icon {
path {
fill: $color-primary-legacy;
stroke: $color-bg-base;
stroke-width: 2;

&.on-time {
fill: $color-vehicle-ontime;
}

&.early.early-red {
fill: $color-vehicle-red;
}
&.early.early-blue {
fill: $color-vehicle-blue;
}

&.late.early-red {
fill: $color-vehicle-blue;
}
&.late.early-blue {
fill: $color-vehicle-red;
}

&.off-course {
fill: $color-vehicle-off-course;
}

&.logged-out {
fill: $color-gray-300;
stroke: $color-gray-600;
stroke-width: 1px;
}

&.selected {
stroke: $color-eggplant-600;
stroke-width: 1.5;
filter: drop-shadow(1px 1px 4px $color-eggplant-400);
}
}

svg {
overflow: visible;
}
}

.c-vehicle-map__label {
svg {
overflow: visible;
}

&.primary {
font-size: var(--font-size-s);
font-weight: 500;
}

&.secondary {
@include font-tiny;
}

&.selected .c-vehicle-icon__label-background {
fill: $color-eggplant-300;
}
}
2 changes: 1 addition & 1 deletion assets/src/components/map.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ import {
RouteStopMarkers,
StationMarker,
TrainVehicleMarker,
VehicleMarker,
} from "./mapMarkers"
import { VehicleMarker } from "./map/markers/vehicleMarker"
import ZoomLevelWrapper from "./ZoomLevelWrapper"
import { StreetViewControl } from "./map/controls/StreetViewSwitch"
import StreetViewModeEnabledContext from "../contexts/streetViewModeEnabledContext"
Expand Down
162 changes: 162 additions & 0 deletions assets/src/components/map/markers/vehicleMarker.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
import { LatLngExpression, Marker } from "leaflet"
import React, {
PropsWithChildren,
useContext,
useRef,
useState,
useEffect,
} from "react"

import { StateDispatchContext } from "../../../contexts/stateDispatchContext"
import { joinClasses } from "../../../helpers/dom"
import { vehicleLabel } from "../../../helpers/vehicleLabel"
import { statusClasses, drawnStatus } from "../../../models/vehicleStatus"

import { Vehicle } from "../../../realtime"
import { ReactMarker } from "../utilities/reactMarker"

interface VehicleMarkerProps extends PropsWithChildren {
vehicle: Vehicle
isPrimary: boolean
isSelected?: boolean
onSelect?: (vehicle: Vehicle) => void
shouldShowPopup?: boolean
onShouldShowPopupChange?: (newValue: boolean) => void
}

export const VehicleMarker = ({
children,
vehicle,
isPrimary,
onSelect,
isSelected = false,
shouldShowPopup = false,
onShouldShowPopupChange = () => {},
}: VehicleMarkerProps) => {
const [{ userSettings }] = useContext(StateDispatchContext)
const markerRef = useRef<Marker<any>>(null)

const [isPopupVisible, setIsPopupVisible] = useState<boolean>(false)

useEffect(() => {
if (shouldShowPopup && !isPopupVisible) {
markerRef.current?.openPopup()
}

if (!shouldShowPopup && isPopupVisible) {
markerRef.current?.closePopup()
}
}, [shouldShowPopup, isPopupVisible])

const eventHandlers = {
click: () => {
onSelect && onSelect(vehicle)
onShouldShowPopupChange(false)
},
contextmenu: () => {
onShouldShowPopupChange(true)
},
popupopen: () => {
setIsPopupVisible(true)
},
popupclose: () => {
setIsPopupVisible(false)
onShouldShowPopupChange(false)
},
}
const position: LatLngExpression = [vehicle.latitude, vehicle.longitude]
const labelBackgroundHeight = isPrimary ? 16 : 12
const labelBackgroundWidth =
vehicleLabel(vehicle, userSettings).length <= 4
? isPrimary
? 40
: 30
: isPrimary
? 62
: 40

// https://leafletjs.com/reference.html#marker-zindexoffset
// > By default, marker images zIndex is set automatically based on its latitude
// > [...] if you want to put the marker on top of all others,
// > [specify] a high value like 1000 [...]
const zIndexOffset = isSelected ? 1000 : 0

return (
<>
<ReactMarker
position={position}
eventHandlers={eventHandlers}
zIndexOffset={zIndexOffset}
ref={markerRef}
divIconSettings={{
iconAnchor: [0, 0],
className: "c-vehicle-map__icon",
}}
icon={
<svg
height="24"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg"
>
<path
className={joinClasses([
...statusClasses(
drawnStatus(vehicle),
userSettings.vehicleAdherenceColors
),
isSelected ? "selected" : null,
])}
d="m10 2.7-6.21 16.94a2.33 2.33 0 0 0 1.38 3 2.36 2.36 0 0 0 1.93-.14l4.9-2.67 4.89 2.71a2.34 2.34 0 0 0 3.34-2.8l-5.81-17a2.34 2.34 0 0 0 -4.4 0z"
transform={
`scale(${isPrimary ? 1.0 : 0.8}) ` +
`rotate(${vehicle.bearing || 0}) ` +
`translate(-12, -12)`
}
/>
</svg>
}
>
{children}
</ReactMarker>

<ReactMarker
position={position}
divIconSettings={{
iconAnchor: [labelBackgroundWidth / 2, isPrimary ? -16 : -10],
className: joinClasses([
"c-vehicle-map__label",
isPrimary ? "primary" : "secondary",
isSelected && "selected",
]),
}}
icon={
<svg
viewBox={`0 0 ${labelBackgroundWidth} ${labelBackgroundHeight}`}
width={labelBackgroundWidth}
height={labelBackgroundHeight}
>
<rect
className="c-vehicle-icon__label-background"
width="100%"
height="100%"
rx="5.5px"
ry="5.5px"
/>
<text
className="c-vehicle-icon__label"
x="50%"
y="50%"
textAnchor="middle"
dominantBaseline="central"
>
{vehicleLabel(vehicle, userSettings)}
</text>
</svg>
}
eventHandlers={eventHandlers}
zIndexOffset={zIndexOffset}
/>
</>
)
}
Loading

0 comments on commit 0d224a6

Please sign in to comment.