mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-18 21:16:47 +00:00
refactor(client): type both trip map renderers against one MapViewProps contract
MapViewAuto took props: any and the Leaflet MapView destructured any, so a prop one renderer learned and the other never heard of went unnoticed. components/Map/mapViewContract.ts holds MapViewProps, derived from the GL renderer's props and what the callers pass; MapView and the GL bindings in glLazy declare satisfies ComponentType against it, and the GL renderer extends it with its engine. The file imports types only, so check:gl-split is not affected. Types only, no runtime change. The types show two known gaps, recorded in the contract rather than papered over: onMapReady is called by the GL renderer only, and callers pass pins without trip_id (assignment and collection places) while both renderers type their pins as Place; MapViewAuto bridges the latter with one commented cast.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"src": {
|
||||
"@typescript-eslint/no-explicit-any": 155,
|
||||
"@typescript-eslint/no-explicit-any": 154,
|
||||
"@typescript-eslint/no-unused-expressions": 2,
|
||||
"@typescript-eslint/no-unused-vars": 155,
|
||||
"no-empty": 19,
|
||||
@@ -12,14 +12,14 @@
|
||||
"unused-disable-directive": 6
|
||||
},
|
||||
"suppressed": {
|
||||
"@typescript-eslint/no-explicit-any": 44,
|
||||
"@typescript-eslint/no-explicit-any": 40,
|
||||
"react-hooks/exhaustive-deps": 46
|
||||
},
|
||||
"tests": {
|
||||
"@typescript-eslint/no-explicit-any": 492,
|
||||
"@typescript-eslint/no-explicit-any": 491,
|
||||
"@typescript-eslint/no-this-alias": 1,
|
||||
"@typescript-eslint/no-unsafe-function-type": 2,
|
||||
"@typescript-eslint/no-unused-vars": 53,
|
||||
"@typescript-eslint/no-unused-vars": 52,
|
||||
"react-refresh/only-export-components": 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"src/components/Budget/CostsPanel.tsx": 1854,
|
||||
"src/components/Map/MapView.tsx": 1461,
|
||||
"src/components/Map/MapViewGL.test.tsx": 2920,
|
||||
"src/components/Map/MapViewGL.tsx": 2549,
|
||||
"src/components/Map/MapViewGL.tsx": 2425,
|
||||
"src/components/Map/RouteCalculator.ts": 1195,
|
||||
"src/components/PDF/TripPDF.tsx": 1097,
|
||||
"src/components/Planner/DayDetailPanel.test.tsx": 2333,
|
||||
|
||||
@@ -209,6 +209,7 @@ vi.mock('./gcj02Crs', () => ({
|
||||
}))
|
||||
|
||||
import { MapView } from './MapView'
|
||||
import type { RouteVia } from '../../types'
|
||||
|
||||
// Helper: build a place with the extra fields MapView uses (category_name/color/icon)
|
||||
// that exist on joined DB rows but are not in the base Place TypeScript type.
|
||||
@@ -1025,7 +1026,9 @@ describe('MapView explore POIs', () => {
|
||||
})
|
||||
|
||||
describe('MapView plugin route vias', () => {
|
||||
const via = (overrides: Record<string, any> = {}) => ({ lat: 48.5, lng: 2.5, tone: 'default', ...overrides })
|
||||
// Cast, not typed: one case hands in a tone outside RouteVia's union on purpose.
|
||||
const via = (overrides: Partial<Record<keyof RouteVia, unknown>> = {}) =>
|
||||
({ lat: 48.5, lng: 2.5, tone: 'default', ...overrides }) as RouteVia
|
||||
|
||||
it('FE-COMP-MAPVIEW-037: draws a tone dot for each via point', () => {
|
||||
render(<MapView routeVias={[via({ tone: 'success' }), via({ tone: 'danger', lat: 48.7 })]} />)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useRef, useState, useMemo, useCallback, createElement, memo } from 'react'
|
||||
import { useEffect, useRef, useState, useMemo, useCallback, createElement, memo, type ComponentType } from 'react'
|
||||
import DOM from 'react-dom'
|
||||
import { renderIconMarkup } from '../../utils/iconMarkup'
|
||||
import { MapContainer, TileLayer, Marker, Polyline, CircleMarker, Circle, useMap, Tooltip } from 'react-leaflet'
|
||||
@@ -48,7 +48,7 @@ import { visibleRouteReservations } from '../../utils/reservationRoutes'
|
||||
import { safeHexColor } from '../../utils/safeColor'
|
||||
import { placeMarkerLook } from './markerLook'
|
||||
import { escapeHtml } from '@trek/shared'
|
||||
import type { Day, Reservation, RouteVia } from '../../types'
|
||||
import type { Day, Place, Reservation, RouteVia } from '../../types'
|
||||
import type { MapHoverInfo } from './mapHover'
|
||||
import { nightPauseMarker, NIGHT_PAUSE_MIN_ZOOM } from './nightPauseMarker'
|
||||
import NightPauseTooltip from './NightPauseTooltip'
|
||||
@@ -75,7 +75,7 @@ function categoryIconSvg(iconName: string | null | undefined, size: number): str
|
||||
return renderIconMarkup(createElement(IconComponent, { size, color: 'white', strokeWidth: 2.5 }))
|
||||
} catch { return '' }
|
||||
}
|
||||
import type { Place } from '../../types'
|
||||
import type { MapViewProps } from './mapViewContract'
|
||||
|
||||
// Fix default marker icons for vite. `_getIconUrl` is a Leaflet-internal field
|
||||
// not present in the public typings, so narrow to delete it.
|
||||
@@ -839,7 +839,7 @@ export const MapView = memo(function MapView({
|
||||
routeProfileFocus = null,
|
||||
viewBaseLayer,
|
||||
onViewBaseLayerChange,
|
||||
}: any) {
|
||||
}: MapViewProps) {
|
||||
const globalBaseLayer = useSettingsStore(s => s.settings.map_base_layer) || 'default'
|
||||
const updateSetting = useSettingsStore(s => s.updateSetting)
|
||||
const baseLayer = (viewBaseLayer ?? globalBaseLayer) as TourBaseLayer
|
||||
@@ -1415,4 +1415,4 @@ export const MapView = memo(function MapView({
|
||||
)}
|
||||
</>
|
||||
)
|
||||
})
|
||||
}) satisfies ComponentType<MapViewProps>
|
||||
|
||||
@@ -7,6 +7,7 @@ import ErrorBoundary from '../shared/ErrorBoundary'
|
||||
import { MapViewGLMapbox, MapViewGLMaplibre } from './glLazy'
|
||||
import { useRoadtripHazards } from './useRoadtripHazards'
|
||||
import { useTranslation } from '../../i18n/TranslationContext'
|
||||
import type { MapViewAutoProps, MapViewProps } from './mapViewContract'
|
||||
|
||||
// Auto-selects the map renderer based on user settings. Keeps the existing
|
||||
// Leaflet MapView untouched so the Mapbox GL variant can mature iteratively
|
||||
@@ -16,14 +17,15 @@ import { useTranslation } from '../../i18n/TranslationContext'
|
||||
// tiles via sync/tilePrefetcher.ts). GL maps are best-effort offline — their
|
||||
// vector tiles are cached opportunistically by the Service Worker as you view
|
||||
// them online (see the GL tile rules in vite.config.js), not prefetched.
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export function MapViewAuto(props: any) {
|
||||
export function MapViewAuto(props: MapViewAutoProps) {
|
||||
const { t } = useTranslation()
|
||||
const hazards = useRoadtripHazards(props.tripId, !!props.clusterLoosely)
|
||||
// `dawarichTrack` arrives as a prop rather than being fetched here: the pill
|
||||
// that switches it on lives at page level and needs the load status, so the
|
||||
// fetch sits in useTripPlanner and both shells read the same one.
|
||||
const mapProps = { ...props, hazards: hazards.feed?.hazards }
|
||||
// The one cast between what callers pass and what the renderers type: pins
|
||||
// without trip_id (see MapViewAutoProps). Neither renderer reads trip_id.
|
||||
const mapProps = { ...props, hazards: hazards.feed?.hazards } as MapViewProps
|
||||
const provider = useSettingsStore(s => s.settings.map_provider)
|
||||
const token = useSettingsStore(s => s.settings.mapbox_access_token)
|
||||
// Fall back to Leaflet when Mapbox is selected but no token is set,
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import { useEffect, useRef, useMemo, useState, createElement, useCallback } from 'react'
|
||||
import { makeMarkerDraggable, makePoiDraggable, draggedPoiId } from './markerDrag'
|
||||
import type { DawarichTrack, RoadtripVia } from '@trek/shared'
|
||||
import { useStableVias } from './viaMarkerState'
|
||||
import { ALT_CASING, ALT_LABEL_TEXT } from '../Roadtrip/alternativeColors'
|
||||
import type { AlternativeOverlay } from '../Roadtrip/alternativeOverlays'
|
||||
import { serviceMarkerHtml, serviceMarkerOuter } from '../Roadtrip/serviceMarker'
|
||||
import { renderIconMarkup } from '../../utils/iconMarkup'
|
||||
import type mapboxgl from 'mapbox-gl'
|
||||
@@ -28,24 +26,24 @@ import { useIsPhone } from '../../mobile/useIsPhone'
|
||||
import { useGeolocation } from '../../hooks/useGeolocation'
|
||||
import type { Day, Place, Reservation, RouteVia } from '../../types'
|
||||
import type { MapHoverInfo } from './mapHover'
|
||||
import type { MapViewProps, RouteSegment } from './mapViewContract'
|
||||
import { nightPauseMarker, NIGHT_PAUSE_MIN_ZOOM } from './nightPauseMarker'
|
||||
import { clusterPois, poiClusterMarkup, poiClusterList, POI_CLUSTER_DETAIL_ZOOM } from './poiClusters'
|
||||
import { groupCoincidentPlaces } from './coincidentPlaces'
|
||||
import type { RoadtripHazard } from '@trek/shared'
|
||||
import { useHazardLayerGL } from './useHazardLayerGL'
|
||||
import { useDawarichTrailGL } from './useDawarichTrailGL'
|
||||
import { bindDayBoundaryDrag, type DayBoundaryControls } from './dayBoundaryDrag'
|
||||
import { bindDayBoundaryDrag } from './dayBoundaryDrag'
|
||||
import NightPauseTooltip from './NightPauseTooltip'
|
||||
import PlaceHoverCard from './PlaceHoverCard'
|
||||
import { ratingBadgeHtml } from './ratingBadge'
|
||||
import type { Poi } from './poiCategories'
|
||||
import { poiPinParts } from './poiMarker'
|
||||
import { resolveTrackColor, hasManualTrackColor } from './trackColors'
|
||||
import { parseRenderableRouteGeometry, type RouteProfileFocus } from '../../utils/routeGeometry'
|
||||
import { parseRenderableRouteGeometry } from '../../utils/routeGeometry'
|
||||
import { buildPoiPopupHtml } from './placePopup'
|
||||
import { pluginsApi, type PluginMapMarker, type PluginMapLayer } from '../../api/client'
|
||||
import { DEFAULT_MAP_CENTER, DEFAULT_MAP_ZOOM, SATELLITE_TILE_URL, SATELLITE_TILE_ATTRIBUTION, SATELLITE_TILE_MAXZOOM } from '../../constants/mapDefaults'
|
||||
import { computeMapViewport, TILE_SIZE_GL, type ViewportPadding } from '../../utils/mapViewport'
|
||||
import { computeMapViewport, TILE_SIZE_GL } from '../../utils/mapViewport'
|
||||
import { selectedPlaceTarget } from './selectedPlaceTarget'
|
||||
|
||||
function categoryIconSvg(iconName: string | null | undefined, size: number): string {
|
||||
@@ -107,13 +105,6 @@ function buildPlaceClusterData(places: Place[]) {
|
||||
}
|
||||
}
|
||||
|
||||
interface RouteSegment {
|
||||
mid: [number, number]
|
||||
from: [number, number]
|
||||
to: [number, number]
|
||||
walkingText?: string
|
||||
drivingText?: string
|
||||
}
|
||||
|
||||
// Stable identities for the omitted collection props. An inline `= []` / `= {}`
|
||||
// default allocates a fresh object on every render, and these props sit in the
|
||||
@@ -132,120 +123,8 @@ const NO_CONNECTION_IDS: number[] = []
|
||||
const NO_POIS: Poi[] = []
|
||||
const NO_DAYS: Day[] = []
|
||||
|
||||
interface Props {
|
||||
places: Place[]
|
||||
dayPlaces?: Place[]
|
||||
// Enables the plugin map contributions (markers + layers). Absent on surfaces
|
||||
// without a trip (CollectionMap), which naturally excludes them — same rule as
|
||||
// the Leaflet MapPluginMarkers.
|
||||
tripId?: number | string
|
||||
// Charging stops / rest areas a plugin route places on the drawn day route.
|
||||
routeVias?: RouteVia[]
|
||||
dayBoundaryControls?: DayBoundaryControls
|
||||
/** The dashed last bit to a place the road network does not reach. */
|
||||
accessLines?: { line: [[number, number], [number, number]]; meters: number }[]
|
||||
route?: [number, number][][] | null
|
||||
/**
|
||||
* One colour pair per entry of `route`, or absent for the blue the route has always
|
||||
* been. Only the road trip passes these, and only while colouring by day is on.
|
||||
*/
|
||||
routeColors?: ({ line: string; casing: string } | undefined)[] | null
|
||||
/** One flag per entry of `route`: true where the stretch is walked, drawn dashed (#2532). */
|
||||
routeWalking?: boolean[] | null
|
||||
/** False while the map is locked (#2010): picking a place leaves the view alone. */
|
||||
followSelection?: boolean
|
||||
/** Given, the lock that sets followSelection sits above the layer switcher. */
|
||||
onToggleFollow?: () => void
|
||||
routeSegments?: RouteSegment[]
|
||||
selectedPlaceId?: number | null
|
||||
/** The selected place itself, for when no pin on this map stands for it. */
|
||||
selectedPlace?: Place | null
|
||||
onMarkerClick?: (id: number) => void
|
||||
hoverDisabled?: boolean
|
||||
onMapClick?: (info: { latlng: { lat: number; lng: number } }) => void
|
||||
onMapContextMenu?: ((e: { latlng: { lat: number; lng: number }; originalEvent: MouseEvent | TouchEvent }) => void) | null
|
||||
center?: [number, number]
|
||||
zoom?: number
|
||||
fitKey?: number | null
|
||||
dayOrderMap?: Record<number, number[] | null>
|
||||
leftWidth?: number
|
||||
rightWidth?: number
|
||||
hasInspector?: boolean
|
||||
hasDayDetail?: boolean
|
||||
reservations?: Reservation[]
|
||||
visibleConnectionIds?: number[]
|
||||
showTransitRoutes?: boolean
|
||||
days?: Day[]
|
||||
selectedDayId?: number | null
|
||||
/**
|
||||
* Whether a booking switched on by hand also has to run on the selected day to be
|
||||
* drawn. Only the phone's plan map asks for it; see RouteVisibilityOptions.
|
||||
*/
|
||||
scopeConnectionsToDay?: boolean
|
||||
showReservationStats?: boolean
|
||||
onReservationClick?: (reservationId: number) => void
|
||||
pois?: Poi[]
|
||||
onPoiClick?: (poi: Poi) => void
|
||||
/**
|
||||
* A corridor hit dropped somewhere on the map, with the coordinate it landed on.
|
||||
* The caller decides whether that point is near enough to the drive to mean anything.
|
||||
*/
|
||||
onPoiDropOnRoute?: (osmId: string, lat: number, lng: number) => void
|
||||
/** A click on the drawn route, for putting a via point there (#1797). */
|
||||
onRouteClick?: (lat: number, lng: number) => void
|
||||
/** The ways of driving one leg, drawn while the picker is open. */
|
||||
alternativeRoutes?: AlternativeOverlay[]
|
||||
/** Which option is being considered, so it can be lit up in its own colour. */
|
||||
activeAlternative?: number | null
|
||||
onChooseAlternative?: (index: number) => void
|
||||
/** Reports which option the pointer is over, so the list and the map agree. */
|
||||
onHighlightAlternative?: (index: number | null) => void
|
||||
/** Generic numbered control points for list-first route editors. */
|
||||
plannerWaypoints?: Array<{ id: string; lat: number; lng: number }>
|
||||
selectedPlannerWaypointId?: string | null
|
||||
onPlannerWaypointClick?: (id: string) => void
|
||||
routeProfileFocus?: RouteProfileFocus | null
|
||||
viewBaseLayer?: TourBaseLayer
|
||||
onViewBaseLayerChange?: (layer: TourBaseLayer) => void
|
||||
/**
|
||||
* An explicit stretch of map to frame, independent of the day being shown.
|
||||
*
|
||||
* `fitKey` cannot express this: it carries no coordinates, and each renderer decides
|
||||
* for itself that it means "the selected day". Weighing the ways of driving one leg
|
||||
* needs that leg on screen, which is neither the day nor the trip.
|
||||
*/
|
||||
focusPoints?: [number, number][]
|
||||
/** Changes only when the caller intentionally wants a new initial frame. */
|
||||
focusKey?: number
|
||||
/**
|
||||
* What the caller's own chrome covers while `focusPoints` is framed, in pixels per edge.
|
||||
*
|
||||
* The default padding knows this component's panels and nothing else, and on a phone it
|
||||
* is a flat margin. A shell that lays its own bars over the map passes what they cover,
|
||||
* so the frame lands in the part still visible. Only the fit on `focusPoints` reads it.
|
||||
* Compared by value: the same numbers in a new object do not refit, while new numbers
|
||||
* refit the points already handed over, because the chrome they must clear has moved.
|
||||
*/
|
||||
fitPadding?: ViewportPadding
|
||||
/**
|
||||
* Let markers stay apart longer than usual.
|
||||
*
|
||||
* A road trip is read along a line: two stops fifty kilometres apart on the same
|
||||
* motorway are the shape of the day, and merging them into one dot hides it.
|
||||
*/
|
||||
clusterLoosely?: boolean
|
||||
hazards?: RoadtripHazard[]
|
||||
/** The route recorded in Dawarich, already fetched by MapViewAuto (#2279). */
|
||||
dawarichTrack?: DawarichTrack | null
|
||||
/** Draw only this local day of the recording. */
|
||||
dawarichSelectedDate?: string | null
|
||||
/** Local dates whose day is collapsed in the day plan; their recording is not drawn. */
|
||||
dawarichHiddenDates?: ReadonlySet<string> | null
|
||||
/** Via points to draw as draggable handles, keyed by day (#1797). */
|
||||
roadtripVias?: Record<number, RoadtripVia[]>
|
||||
onMoveVia?: (dayId: number, id: number, lat: number, lng: number) => void
|
||||
onRemoveVia?: (dayId: number, id: number) => void
|
||||
onViewportChange?: (bbox: { south: number; west: number; north: number; east: number }) => void
|
||||
/** The GL renderer: the shared map contract plus the engine it is bound to. */
|
||||
interface Props extends MapViewProps {
|
||||
glProvider?: GlMapProvider
|
||||
/**
|
||||
* The GL engine, injected instead of imported. Both SDKs used to be pulled in
|
||||
@@ -254,8 +133,6 @@ interface Props {
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
gl: any
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
onMapReady?: (map: any | null) => void
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
import type { ComponentType } from 'react'
|
||||
import { lazyWithRetry } from '../../utils/lazyWithRetry'
|
||||
import type { GlMapProvider } from './glProviders'
|
||||
import type { MapViewProps } from './mapViewContract'
|
||||
|
||||
/** What a caller hands the GL trip map: the shared map contract and which provider's style to load. */
|
||||
export type MapViewGLProps = MapViewProps & { glProvider?: GlMapProvider }
|
||||
|
||||
/**
|
||||
* The six ways into a GL map, one chunk per engine.
|
||||
@@ -21,15 +27,15 @@ import { lazyWithRetry } from '../../utils/lazyWithRetry'
|
||||
export const MapViewGLMapbox = lazyWithRetry(async () => {
|
||||
const [component, engine] = await Promise.all([import('./MapViewGL'), import('./engines/mapbox')])
|
||||
const MapViewGL = component.MapViewGL
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
return { default: (props: any) => <MapViewGL {...props} gl={engine.default} /> }
|
||||
const bound = (props: MapViewGLProps) => <MapViewGL {...props} gl={engine.default} />
|
||||
return { default: bound satisfies ComponentType<MapViewGLProps> }
|
||||
})
|
||||
|
||||
export const MapViewGLMaplibre = lazyWithRetry(async () => {
|
||||
const [component, engine] = await Promise.all([import('./MapViewGL'), import('./engines/maplibre')])
|
||||
const MapViewGL = component.MapViewGL
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
return { default: (props: any) => <MapViewGL {...props} gl={engine.default} /> }
|
||||
const bound = (props: MapViewGLProps) => <MapViewGL {...props} gl={engine.default} />
|
||||
return { default: bound satisfies ComponentType<MapViewGLProps> }
|
||||
})
|
||||
|
||||
export const JourneyMapGLMapbox = lazyWithRetry(async () => {
|
||||
|
||||
@@ -0,0 +1,178 @@
|
||||
import type { DawarichTrack, RoadtripHazard, RoadtripVia } from '@trek/shared';
|
||||
import type { Day, Place, Reservation, RouteVia } from '../../types';
|
||||
import type { ViewportPadding } from '../../utils/mapViewport';
|
||||
import type { RouteProfileFocus } from '../../utils/routeGeometry';
|
||||
import type { AlternativeOverlay } from '../Roadtrip/alternativeOverlays';
|
||||
import type { DayBoundaryControls } from './dayBoundaryDrag';
|
||||
import type { CompassMap } from './MapCompassPill';
|
||||
import type { TourBaseLayer } from './MapLayerSwitcher';
|
||||
import type { Poi } from './poiCategories';
|
||||
|
||||
/*
|
||||
* The props both trip map renderers take: the Leaflet MapView and the GL
|
||||
* MapViewGL (Mapbox or MapLibre), between which MapViewAuto picks at runtime.
|
||||
*
|
||||
* Callers talk to MapViewAuto, so one prop type has to describe what either
|
||||
* renderer does with it; before this file each side read `any`, and a prop one
|
||||
* renderer learned and the other never heard of went unnoticed. Both renderers
|
||||
* now declare `satisfies ComponentType<...>` against this type.
|
||||
*
|
||||
* Types only, on purpose: this module must stay free of leaflet, mapbox-gl and
|
||||
* maplibre-gl (`check:gl-split`), so every import here is `import type`.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A pin as the callers of MapViewAuto hand it down. Besides trip places they
|
||||
* pass the places inside a day's assignments and collection places, and those
|
||||
* carry no trip_id. Neither renderer reads trip_id, but both type their pins
|
||||
* as Place, which requires it; see MapViewAutoProps.
|
||||
*/
|
||||
export type MapPlace = Omit<Place, 'trip_id'> & { trip_id?: number };
|
||||
|
||||
/** A leg label between two consecutive stops: where it sits and what it says. */
|
||||
export interface RouteSegment {
|
||||
mid: [number, number];
|
||||
from: [number, number];
|
||||
to: [number, number];
|
||||
walkingText?: string;
|
||||
drivingText?: string;
|
||||
}
|
||||
|
||||
export interface MapViewProps {
|
||||
places?: Place[];
|
||||
dayPlaces?: Place[];
|
||||
// Enables the plugin map contributions (markers + layers). Absent on surfaces
|
||||
// without a trip (CollectionMap), which naturally excludes them — same rule as
|
||||
// the Leaflet MapPluginMarkers.
|
||||
tripId?: number;
|
||||
// Charging stops / rest areas a plugin route places on the drawn day route.
|
||||
routeVias?: RouteVia[];
|
||||
dayBoundaryControls?: DayBoundaryControls;
|
||||
/** The dashed last bit to a place the road network does not reach. */
|
||||
accessLines?: { line: [[number, number], [number, number]]; meters: number }[];
|
||||
route?: [number, number][][] | null;
|
||||
/**
|
||||
* One colour pair per entry of `route`, or absent for the blue the route has always
|
||||
* been. Only the road trip passes these, and only while colouring by day is on.
|
||||
*/
|
||||
routeColors?: ({ line: string; casing: string } | undefined)[] | null;
|
||||
/** One flag per entry of `route`: true where the stretch is walked, drawn dashed (#2532). */
|
||||
routeWalking?: boolean[] | null;
|
||||
/** False while the map is locked (#2010): picking a place leaves the view alone. */
|
||||
followSelection?: boolean;
|
||||
/** Given, the lock that sets followSelection sits above the layer switcher. */
|
||||
onToggleFollow?: () => void;
|
||||
routeSegments?: RouteSegment[];
|
||||
selectedPlaceId?: number | null;
|
||||
/** The selected place itself, for when no pin on this map stands for it. */
|
||||
selectedPlace?: Place | null;
|
||||
onMarkerClick?: (id: number) => void;
|
||||
hoverDisabled?: boolean;
|
||||
onMapClick?: (info: { latlng: { lat: number; lng: number } }) => void;
|
||||
onMapContextMenu?:
|
||||
((e: { latlng: { lat: number; lng: number }; originalEvent: MouseEvent | TouchEvent }) => void) | null;
|
||||
center?: [number, number];
|
||||
zoom?: number;
|
||||
fitKey?: number | null;
|
||||
dayOrderMap?: Record<number, number[] | null>;
|
||||
leftWidth?: number;
|
||||
rightWidth?: number;
|
||||
hasInspector?: boolean;
|
||||
hasDayDetail?: boolean;
|
||||
reservations?: Reservation[];
|
||||
visibleConnectionIds?: number[];
|
||||
showTransitRoutes?: boolean;
|
||||
days?: Day[];
|
||||
selectedDayId?: number | null;
|
||||
/**
|
||||
* Whether a booking switched on by hand also has to run on the selected day to be
|
||||
* drawn. Only the phone's plan map asks for it; see RouteVisibilityOptions.
|
||||
*/
|
||||
scopeConnectionsToDay?: boolean;
|
||||
showReservationStats?: boolean;
|
||||
onReservationClick?: (reservationId: number) => void;
|
||||
pois?: Poi[];
|
||||
onPoiClick?: (poi: Poi) => void;
|
||||
/**
|
||||
* A corridor hit dropped somewhere on the map, with the coordinate it landed on.
|
||||
* The caller decides whether that point is near enough to the drive to mean anything.
|
||||
*/
|
||||
onPoiDropOnRoute?: (osmId: string, lat: number, lng: number) => void;
|
||||
/** A click on the drawn route, for putting a via point there (#1797). */
|
||||
onRouteClick?: (lat: number, lng: number) => void;
|
||||
/** The ways of driving one leg, drawn while the picker is open. */
|
||||
alternativeRoutes?: AlternativeOverlay[];
|
||||
/** Which option is being considered, so it can be lit up in its own colour. */
|
||||
activeAlternative?: number | null;
|
||||
onChooseAlternative?: (index: number) => void;
|
||||
/** Reports which option the pointer is over, so the list and the map agree. */
|
||||
onHighlightAlternative?: (index: number | null) => void;
|
||||
/** Generic numbered control points for list-first route editors. */
|
||||
plannerWaypoints?: Array<{ id: string; lat: number; lng: number }>;
|
||||
selectedPlannerWaypointId?: string | null;
|
||||
onPlannerWaypointClick?: (id: string) => void;
|
||||
routeProfileFocus?: RouteProfileFocus | null;
|
||||
viewBaseLayer?: TourBaseLayer;
|
||||
onViewBaseLayerChange?: (layer: TourBaseLayer) => void;
|
||||
/**
|
||||
* An explicit stretch of map to frame, independent of the day being shown.
|
||||
*
|
||||
* `fitKey` cannot express this: it carries no coordinates, and each renderer decides
|
||||
* for itself that it means "the selected day". Weighing the ways of driving one leg
|
||||
* needs that leg on screen, which is neither the day nor the trip.
|
||||
*/
|
||||
focusPoints?: [number, number][];
|
||||
/** Changes only when the caller intentionally wants a new initial frame. */
|
||||
focusKey?: number;
|
||||
/**
|
||||
* What the caller's own chrome covers while `focusPoints` is framed, in pixels per edge.
|
||||
*
|
||||
* The default padding knows this component's panels and nothing else, and on a phone it
|
||||
* is a flat margin. A shell that lays its own bars over the map passes what they cover,
|
||||
* so the frame lands in the part still visible. Only the fit on `focusPoints` reads it.
|
||||
* Compared by value: the same numbers in a new object do not refit, while new numbers
|
||||
* refit the points already handed over, because the chrome they must clear has moved.
|
||||
*/
|
||||
fitPadding?: ViewportPadding;
|
||||
/**
|
||||
* Let markers stay apart longer than usual.
|
||||
*
|
||||
* A road trip is read along a line: two stops fifty kilometres apart on the same
|
||||
* motorway are the shape of the day, and merging them into one dot hides it.
|
||||
*/
|
||||
clusterLoosely?: boolean;
|
||||
hazards?: RoadtripHazard[];
|
||||
/** The route recorded in Dawarich, already fetched by MapViewAuto (#2279). */
|
||||
dawarichTrack?: DawarichTrack | null;
|
||||
/** Draw only this local day of the recording. */
|
||||
dawarichSelectedDate?: string | null;
|
||||
/** Local dates whose day is collapsed in the day plan; their recording is not drawn. */
|
||||
dawarichHiddenDates?: ReadonlySet<string> | null;
|
||||
/** Via points to draw as draggable handles, keyed by day (#1797). */
|
||||
roadtripVias?: Record<number, RoadtripVia[]>;
|
||||
onMoveVia?: (dayId: number, id: number, lat: number, lng: number) => void;
|
||||
onRemoveVia?: (dayId: number, id: number) => void;
|
||||
onViewportChange?: (bbox: { south: number; west: number; north: number; east: number }) => void;
|
||||
/**
|
||||
* The raster tile URL template. Only the Leaflet renderer draws raster tiles;
|
||||
* the GL renderer draws the style of the chosen provider and ignores it.
|
||||
*/
|
||||
tileUrl?: string;
|
||||
/**
|
||||
* Hands the caller the live map once it is ready. Only the GL renderer calls
|
||||
* it; the Leaflet renderer takes the prop and never calls it.
|
||||
*/
|
||||
onMapReady?: (map: CompassMap | null) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* What MapViewAuto accepts: the renderer contract with pins as callers really
|
||||
* pass them (MapPlace, possibly without trip_id). The gap between this and
|
||||
* MapViewProps is known drift between the callers and the renderers' Place
|
||||
* type; it is bridged in one place, in MapViewAuto, until the renderers type
|
||||
* their pins by what they read.
|
||||
*/
|
||||
export type MapViewAutoProps = Omit<MapViewProps, 'places' | 'dayPlaces'> & {
|
||||
places?: MapPlace[];
|
||||
dayPlaces?: MapPlace[];
|
||||
};
|
||||
Reference in New Issue
Block a user