import { createEffect, createResource, createSignal, For, JSX, onMount, Show, useContext, VoidComponent } from 'solid-js'; import { Box, Button, Input, InputLeftAddon, InputGroup, HStack, List, ListItem, Progress, ProgressIndicator, VStack } from "@hope-ui/solid"; import 'leaflet/dist/leaflet.css'; import { featureGroup as leafletFeatureGroup, LatLngLiteral as LeafletLatLngLiteral, Map as LeafletMap, Marker as LeafletMarker, tileLayer as leafletTileLayer } from 'leaflet'; import { BusinessDataContext, BusinessDataStore } from "./businessData"; import { SearchContext, SearchStore } from './search'; import { Stop } from './types'; import { renderLineTransportMode, renderLinePicto, TransportModeWeights } from './utils'; import styles from './stopManager.module.css'; const StopRepr: VoidComponent<{ stop: Stop }> = (props) => { const businessDataStore: BusinessDataStore | undefined = useContext(BusinessDataContext); if (businessDataStore === undefined) return
; const { getLine } = businessDataStore; const fetchLinesRepr = async (lineIds: string[]): Promise => { const reprs = []; for (const lineId of lineIds) { const line = await getLine(lineId); if (line !== undefined) { reprs.push(
{renderLineTransportMode(line)}
); reprs.push(renderLinePicto(line, styles)); } } return reprs; } const [lineReprs] = createResource(props.stop.lines, fetchLinesRepr); return ( {props.stop.name} {(line: JSX.Element) => line} ); } const StopAreaRepr: VoidComponent<{ stop: Stop }> = (props) => { const businessDataStore: BusinessDataStore | undefined = useContext(BusinessDataContext); if (businessDataStore === undefined) return
; const { getLine } = businessDataStore; type ByTransportModeReprs = { mode: JSX.Element | undefined; [key: string]: JSX.Element | JSX.Element[] | undefined; }; const fetchLinesRepr = async (stop: Stop): Promise => { const lineIds = new Set(stop.lines); const stops = stop.stops; for (const stop of stops) { stop.lines.forEach(lineIds.add, lineIds); } const byModeReprs: Record = {}; for (const lineId of lineIds) { const line = await getLine(lineId); if (line !== undefined) { if (!(line.transportMode in byModeReprs)) { byModeReprs[line.transportMode] = { mode:
{renderLineTransportMode(line)}
}; } byModeReprs[line.transportMode][line.shortName] = renderLinePicto(line, styles); } } const reprs = []; const sortedTransportModes = Object.keys(byModeReprs).sort((x, y) => TransportModeWeights[x] < TransportModeWeights[y] ? 1 : -1); for (const transportMode of sortedTransportModes) { const lines = byModeReprs[transportMode]; const repr = [lines.mode]; delete lines.mode; for (const lineId of Object.keys(lines).sort((x, y) => x.localeCompare(y))) { repr.push(lines[lineId]); } reprs.push(repr); } return reprs; } const [lineReprs] = createResource(props.stop, fetchLinesRepr); return ( {props.stop.name} {(line) => line} ); } const Map: VoidComponent<{ stops: Stop[] }> = (props) => { const mapCenter: LeafletLatLngLiteral = { lat: 48.853, lng: 2.35 }; const searchStore: SearchStore | undefined = useContext(SearchContext); if (searchStore === undefined) return
; const { addMarkers } = searchStore; let mapDiv: any; let map: LeafletMap | undefined = undefined; const stopsLayerGroup = leafletFeatureGroup(); const buildMap = (div: HTMLDivElement) => { map = new LeafletMap(div).setView(mapCenter, 11); leafletTileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: '© OpenStreetMap contributors' }).addTo(map); stopsLayerGroup.addTo(map); } const setMarker = (stop: Stop): L.Marker[] => { const markers = []; if (stop.lat !== undefined && stop.lon !== undefined) { /* TODO: Add stop lines representation to popup. */ markers.push(new LeafletMarker([stop.lat, stop.lon]).bindPopup(`${stop.name}`).openPopup()); } else { for (const _stop of stop.stops) { markers.push(...setMarker(_stop)); } } return markers; } onMount(() => buildMap(mapDiv)); createEffect(() => { /* TODO: Avoid to clear all layers... */ stopsLayerGroup.clearLayers(); for (const stop of props.stops) { const markers = setMarker(stop); addMarkers(stop.id, markers); for (const marker of markers) { stopsLayerGroup.addLayer(marker); } } const stopsBound = stopsLayerGroup.getBounds(); if (map !== undefined && Object.keys(stopsBound).length) { map.fitBounds(stopsBound); } }); return
; } export const StopsManager: VoidComponent = () => { const businessDataStore: BusinessDataStore | undefined = useContext(BusinessDataContext); const searchStore: SearchStore | undefined = useContext(SearchContext); if (businessDataStore === undefined || searchStore === undefined) return
; const { searchStopByName } = businessDataStore; const { setDisplayedStops } = searchStore; // TODO: Use props instead const [minCharactersNb] = createSignal(4); const [inProgress, setInProgress] = createSignal(false); const [foundStops, setFoundStops] = createSignal([]); const onStopNameInput: JSX.EventHandler = async (event): Promise => { /* TODO: Add a tempo before fetching stop for giving time to user to finish his request */ const stopName = event.currentTarget.value; if (stopName.length >= minCharactersNb()) { console.log(`Fetching data for ${stopName}`); setInProgress(true); const stopsById = await searchStopByName(stopName); setFoundStops(Object.values(stopsById)); setInProgress(false); } } return ( 🚉 🚏 x.name.localeCompare(y.name))}> {(stop) => } ); };