6 Commits

28 changed files with 1946 additions and 2016 deletions

View File

@@ -1,10 +1,7 @@
from asyncio import sleep
from logging import getLogger
from typing import Annotated, AsyncIterator
from fastapi import Depends
from opentelemetry.instrumentation.sqlalchemy import SQLAlchemyInstrumentor
from sqlalchemy import text
from sqlalchemy.exc import OperationalError, SQLAlchemyError
from sqlalchemy.ext.asyncio import (
async_sessionmaker,
@@ -16,6 +13,7 @@ from sqlalchemy.ext.asyncio import (
from .base_class import Base
from ..settings import DatabaseSettings
logger = getLogger(__name__)
@@ -30,8 +28,7 @@ class Database:
except (SQLAlchemyError, AttributeError) as e:
logger.exception(e)
return None
raise
# TODO: Preserve UserLastStopSearchResults table from drop.
async def connect(

View File

@@ -60,7 +60,7 @@ types-aiofiles = "^22.1.0.2"
wrapt = "^1.14.1"
pydocstyle = "^6.2.2"
dill = "^0.3.6"
python-lsp-ruff = "^1.0.5"
python-lsp-ruff = "^2.1.0"
python-lsp-server = "^1.7.1"
autopep8 = "^2.0.1"
pyflakes = "^3.0.1"

View File

@@ -32,6 +32,7 @@
"matrix-widget-api": "^1.1.1",
"ol": "^7.3.0",
"solid-js": "^1.6.6",
"solid-transition-group": "^0.0.10"
"solid-transition-group": "^0.0.10",
"solidjs-lazily": "^0.1.2"
}
}

View File

@@ -1,15 +1,12 @@
import { Component, createSignal } from 'solid-js';
import { IVisibilityActionRequest, MatrixCapabilities, WidgetApi, WidgetApiToWidgetAction } from 'matrix-widget-api';
import { HopeProvider } from "@hope-ui/solid";
import { Component, createSignal, onCleanup, onMount } from 'solid-js';
// import { IVisibilityActionRequest, MatrixCapabilities, WidgetApi, WidgetApiToWidgetAction } from 'matrix-widget-api';
import { BusinessDataProvider } from './businessData';
import { AppContextProvider } from './appContext';
import { PassagesDisplay } from './passagesDisplay';
import { StopsSearchMenu } from './stopsSearchMenu/stopsSearchMenu';
import "./App.scss";
import { onCleanup, onMount } from 'solid-js';
function parseFragment() {
@@ -28,21 +25,21 @@ const App: Component = () => {
console.log("App: widgetId:" + widgetId);
console.log("App: userId:" + userId);
const api = new WidgetApi(widgetId != null ? widgetId : undefined);
api.requestCapability(MatrixCapabilities.AlwaysOnScreen);
api.start();
api.on("ready", function() {
console.log("App: widget API is READY !!!!");
});
// const api = new WidgetApi(widgetId != null ? widgetId : undefined);
// api.requestCapability(MatrixCapabilities.AlwaysOnScreen);
// api.start();
// api.on("ready", function() {
// console.log("App: widget API is READY !!!!");
// });
// Seems to don´t be used...
api.on(`action:${WidgetApiToWidgetAction.UpdateVisibility}`, (ev: CustomEvent<IVisibilityActionRequest>) => {
console.log("App: Visibility change");
ev.preventDefault(); // we're handling it, so stop the widget API from doing something.
console.log("App: ", ev.detail); // custom handling here
/* api.transport.reply(ev.detail, <IWidgetApiRequestEmptyData>{}); */
api.transport.reply(ev.detail, {});
});
// api.on(`action:${WidgetApiToWidgetAction.UpdateVisibility}`, (ev: CustomEvent<IVisibilityActionRequest>) => {
// console.log("App: Visibility change");
// ev.preventDefault(); // we're handling it, so stop the widget API from doing something.
// console.log("App: ", ev.detail); // custom handling here
// /* api.transport.reply(ev.detail, <IWidgetApiRequestEmptyData>{}); */
// api.transport.reply(ev.detail, {});
// });
createSignal({
height: window.innerHeight,
@@ -71,10 +68,8 @@ const App: Component = () => {
window.removeEventListener('resize', onResize);
})
return (
<BusinessDataProvider>
return <BusinessDataProvider>
<AppContextProvider>
<HopeProvider>
<div class="App">
<div class="panel">
<StopsSearchMenu />
@@ -83,10 +78,8 @@ const App: Component = () => {
<PassagesDisplay />
</div>
</div>
</HopeProvider>
</AppContextProvider>
</BusinessDataProvider>
);
</BusinessDataProvider>;
};
export default App;

View File

@@ -6,7 +6,7 @@ import { Stop } from './types';
export interface AppContextStore {
getDisplayedStops: () => Stop[];
setDisplayedStops: (stops: Stop[]) => void;
};
}
export const AppContextContext = createContext<AppContextStore>();
@@ -26,10 +26,7 @@ export function AppContextProvider(props: { children: JSX.Element }) {
const setDisplayedStops = (stops: Stop[]): void => {
console.log("setDisplayedStops=", stops);
// setStore((s: Store) => {
setStore('displayedStops', stops);
// return s;
// });
}
return (
@@ -39,5 +36,4 @@ export function AppContextProvider(props: { children: JSX.Element }) {
{props.children}
</AppContextContext.Provider>
);
};
}

View File

@@ -6,6 +6,7 @@ import { Line, Lines, Passage, Passages, Stop, StopShape, StopShapes, Stops } fr
export type StopDestinations = Record<string, string[]>;
export interface BusinessDataStore {
getLine: (lineId: string) => Promise<Line>;
getLinePassages: (lineId: string) => Record<string, Passage[]>;
@@ -23,7 +24,7 @@ export interface BusinessDataStore {
getStopDestinations: (stopId: number) => Promise<StopDestinations | undefined>;
getStopShape: (stopId: number) => Promise<StopShape | undefined>;
};
}
export const BusinessDataContext = createContext<BusinessDataStore>();
@@ -120,7 +121,7 @@ export function BusinessDataProvider(props: { children: JSX.Element }) {
for (const lineId of Object.keys(passages)) {
const newLinePassages = passages[lineId];
const linePassages = storePassages[lineId];
if (linePassages === undefined) {
if (linePassages === undefined || Object.keys(linePassages).length == 0) {
setStore('passages', lineId, newLinePassages);
}
else {
@@ -159,7 +160,7 @@ ${linePassagesDestination.length} here... refresh all them.`);
const clearPassages = (): void => {
setStore((s: Store): Store => {
for (const lineId of Object.keys(s.passages)) {
setStore('passages', lineId, undefined);
setStore('passages', lineId, {});
}
return s;
});
@@ -231,21 +232,9 @@ ${linePassagesDestination.length} here... refresh all them.`);
getLine, getLinePassages, getLineDestinations, getDestinationPassages, passages, getPassagesLineIds,
refreshPassages, addPassages, clearPassages,
getStop, getStopDestinations, getStopShape, searchStopByName
}}>
{props.children}
</BusinessDataContext.Provider>
);
}
export interface BusinessDataStore {
getLine: (lineId: string) => Promise<Line>;
getLinePassages: (lineId: string) => Record<string, Passage[]>;
passages: () => Passages;
refreshPassages: (stopId: number) => Promise<void>;
addPassages: (passages: Passages) => void;
clearPassages: () => void;
getStop: (stopId: number) => Stop | undefined;
searchStopByName: (name: string) => Promise<Stops>;
};

View File

@@ -4,7 +4,8 @@ import { VoidComponent } from "solid-js";
export const IconHamburgerMenu: VoidComponent<{}> = () => {
return (
<svg class="iconHamburgerMenu" viewBox="0 0 15 15">
<path d="M1.5 3C1.22386 3 1 3.22386 1 3.5C1 3.77614 1.22386 4 1.5 4H13.5C13.7761 4 14 3.77614 14 3.5C14 3.22386
<path
d="M1.5 3C1.22386 3 1 3.22386 1 3.5C1 3.77614 1.22386 4 1.5 4H13.5C13.7761 4 14 3.77614 14 3.5C14 3.22386
13.7761 3 13.5 3H1.5ZM1 7.5C1 7.22386 1.22386 7 1.5 7H13.5C13.7761 7 14 7.22386 14 7.5C14 7.77614 13.7761 8 13.5
8H1.5C1.22386 8 1 7.77614 1 7.5ZM1 11.5C1 11.2239 1.22386 11 1.5 11H13.5C13.7761 11 14 11.2239 14 11.5C14 11.7761
13.7761 12 13.5 12H1.5C1.22386 12 1 11.7761 1 11.5Z"

View File

@@ -14,6 +14,7 @@
}
html, body {
height: 100vh;
aspect-ratio: 16/9;
margin: 0;

View File

@@ -1,7 +1,6 @@
import { createContext, createEffect, createResource, createSignal, For, JSX, ParentComponent, Show, useContext, VoidComponent } from "solid-js";
import { createStore } from "solid-js/store";
import { createDateNow } from "@solid-primitives/date";
import { IconButton, Menu, MenuTrigger, MenuContent, MenuItem } from "@hope-ui/solid";
import { format } from "date-fns";
import { BusinessDataContext, BusinessDataStore } from "./businessData";
@@ -9,7 +8,6 @@ import { AppContextContext, AppContextStore } from "./appContext";
import { getTransportModeSrc, PositionedPanel } from "./utils";
import { PassagesPanel } from "./passagesPanel";
import { IconHamburgerMenu } from './extra/iconHamburgerMenu';
import "./passagesDisplay.scss";
@@ -25,7 +23,7 @@ interface PassagesDisplayStore {
getDisplayedPanelId: () => number;
setDisplayedPanelId: (panelId: number) => void;
};
}
const PassagesDisplayContext = createContext<PassagesDisplayStore>();
@@ -91,7 +89,6 @@ const Header: VoidComponent<{ title: string }> = (props) => {
return <div />;
const { getLine, passages } = businessDataStore;
const { isPassagesRefreshEnabled, togglePassagesRefresh } = passagesDisplayStore;
const [dateNow] = createDateNow(1000);
@@ -113,16 +110,14 @@ const Header: VoidComponent<{ title: string }> = (props) => {
setLinesIds(Object.keys(passages()));
});
return (
<div class="header">
return <div class="header">
<Show when={transportModeUrls() !== undefined} >
<For each={transportModeUrls()}>
{(url) =>
<div class="transportMode">
<img src={url} />
</div>
}
</For>
}</For>
</Show>
<div class="title">
<svg viewBox="0 0 1260 50">
@@ -132,15 +127,6 @@ const Header: VoidComponent<{ title: string }> = (props) => {
</svg>
</div>
<div class="menu">
<Menu>
<MenuTrigger
as={IconButton}
icon=<IconHamburgerMenu />
/>
<MenuContent>
<MenuItem onSelect={() => togglePassagesRefresh()}>{isPassagesRefreshEnabled() ? "Disable" : "Enable"}</MenuItem>
</MenuContent>
</Menu>
</div>
<div class="clock">
<svg viewBox="0 0 115 43">
@@ -149,8 +135,7 @@ const Header: VoidComponent<{ title: string }> = (props) => {
</text>
</svg>
</div>
</div >
);
</div>;
};
const Footer: VoidComponent<{}> = () => {
@@ -176,8 +161,7 @@ const Footer: VoidComponent<{}> = () => {
</svg>
</div>
);
}}
</For>
}}</For>
</div>
);
}
@@ -286,13 +270,11 @@ export const PassagesDisplay: ParentComponent = () => {
const syncPeriodMsec = 20 * 1000;
const panelSwitchPeriodMsec = 4 * 1000;
return (
<div class="passagesDisplay">
return <div class="passagesDisplay">
<PassagesDisplayProvider>
<Header title="Prochains passages" />
<Body maxPassagesPerPanel={MAX_PASSAGES_PER_PANEL} syncPeriodMsec={syncPeriodMsec} panelSwitchPeriodMsec={panelSwitchPeriodMsec} />
<Footer />
</PassagesDisplayProvider>
</div>
);
</div>;
};

View File

@@ -14,15 +14,13 @@ import "./passagesPanel.scss";
const UnavailablePassage: VoidComponent<{ style: string }> = (props) => {
const textStyle = { fill: "#000000" };
return (
<div class={props.style}>
return <div class={props.style}>
<svg viewBox="0 0 230 110">
<text x="100%" y="26" font-size="25" text-anchor="end" style={textStyle}>Information</text>
<text x="100%" y="63" font-size="25" text-anchor="end" style={textStyle}>non</text>
<text x="100%" y="100" font-size="25" text-anchor="end" style={textStyle}>disponible</text>
</svg>
</div>
);
</div>;
}
const Platform: VoidComponent<{ name: string }> = (props) => {
@@ -43,14 +41,12 @@ const Platform: VoidComponent<{ name: string }> = (props) => {
}
});
return (
<svg class="platform" viewBox={`0 0 ${viewBoxWidthPx} 40`}>
return <svg class="platform" viewBox={`0 0 ${viewBoxWidthPx} 40`}>
<rect ref={rectRef} x="0" y="0" height="100%" rx="9" ry="9" />
<text ref={textRef} x="100%" y="55%" dominant-baseline="middle" text-anchor="end" font-size="25" style={{ fill: "#ffffff" }}>
QUAI {props.name}
</text>
</svg>
);
</svg>;
}
const TtwPassage: VoidComponent<{
@@ -88,8 +84,7 @@ const TtwPassage: VoidComponent<{
</Motion.text>
</svg>;
return (
<Show when={passage !== undefined} fallback=<UnavailablePassage style={props.fallbackStyle} />>
return <Show when={passage !== undefined} fallback={<UnavailablePassage style={props.fallbackStyle} />}>
<Show
when={passage.arrivalPlatformName !== null}
fallback={
@@ -101,8 +96,7 @@ const TtwPassage: VoidComponent<{
<Platform name={passage.arrivalPlatformName} />
</div>
</Show>
</Show >
);
</Show>;
});
}
@@ -122,8 +116,7 @@ const DestinationPassages: VoidComponent<{ line: Line, destination: string }> =
// const trafficStatusStyle = { fill: trafficStatusColor.get(props.line.trafficStatus) };
const trafficStatusStyle = { fill: trafficStatusColor.get(TrafficStatus.UNKNOWN) };
return (
<div class="line">
return <div class="line">
<div class="transportMode">
{renderLineTransportMode(props.line)}
</div>
@@ -142,8 +135,7 @@ const DestinationPassages: VoidComponent<{ line: Line, destination: string }> =
<TtwPassage line={props.line} destination={props.destination} index={1}
style="secondPassage" withPlatformStyle="withPlatformSecondPassage"
fontSize={45} fallbackStyle="unavailableSecondPassage" />
</div >
);
</div>;
}
export type PassagesPanelComponentProps = ParentProps & { stopId: number, lineIds: string[], show: boolean };
@@ -162,19 +154,16 @@ export const PassagesPanel: PassagesPanelComponent = (props) => {
}
const [lines] = createResource<Line[], string[]>(props.lineIds, getLines);
return (
<div classList={{ ["passagesContainer"]: true, ["displayed"]: props.show }} >
return <div classList={{ "passagesContainer": true, "displayed": props.show }} >
<Show when={lines() !== undefined} >
<For each={lines()}>
{(line) =>
<Show when={getLineDestinations(line.id) !== undefined}>
<For each={getLineDestinations(line.id)}>
{(destination) => <DestinationPassages line={line} destination={destination} />}
</For>
<For each={getLineDestinations(line.id)}>{
(destination) => <DestinationPassages line={line} destination={destination} />
}</For>
</Show>
}
</For>
}</For>
</Show>
</div >
);
</div>;
}

View File

@@ -1,8 +1,8 @@
import { batch, createContext, JSX } from 'solid-js';
import { createContext, JSX } from 'solid-js';
import { createStore } from 'solid-js/store';
import { Marker as LeafletMarker } from 'leaflet';
import { Stop, Stops } from './types';
import { Stop } from './types';
export type ByStopIdMarkers = Record<number, LeafletMarker[] | undefined>;
@@ -52,9 +52,7 @@ export function SearchProvider(props: { children: JSX.Element }) {
setStore('markers', stopId, markers);
}
return (
<SearchContext.Provider value={{ getFoundStops, setFoundStops, getDisplayedStops, setDisplayedStops, addMarkers }}>
return <SearchContext.Provider value={{ getFoundStops, setFoundStops, getDisplayedStops, setDisplayedStops, addMarkers }}>
{props.children}
</SearchContext.Provider>
);
</SearchContext.Provider>;
}

View File

@@ -41,7 +41,7 @@ export const Map: ParentComponent<{}> = () => {
// TODO: Set padding according to the marker design.
const fitPointsPadding = [50, 50, 50, 50];
let mapDiv: HTMLDivElement | undefined = undefined;
let mapDiv: HTMLDivElement | undefined;
let popup: StopPopup | undefined = undefined;
const stopVectorSource = new OlVectorSource({ features: [] });
@@ -76,6 +76,7 @@ export const Map: ParentComponent<{}> = () => {
],
overlays: [overlay],
});
console.log("map=", map);
map.on('singleclick', onClickedMap);
}
@@ -108,7 +109,10 @@ export const Map: ParentComponent<{}> = () => {
}
}
onMount(() => buildMap(mapDiv));
onMount(() => {
buildMap(mapDiv);
})
;
// Filling the map with stops shape
createEffect(() => {
@@ -207,8 +211,8 @@ export const Map: ParentComponent<{}> = () => {
}
return <>
<div ref={mapDiv} class="map">
<StopPopup ref={popup} stop={selectedMapStop()} show={isPopupDisplayed()} />
<div ref={mapDiv!} class="map">
<StopPopup ref={popup!} stop={selectedMapStop()} show={isPopupDisplayed()} />
</div>
<For each={getFoundStops()}>{(stop) => <MapStop stop={stop} selected={selectedMapStop()} />}</For>
</>;

View File

@@ -149,8 +149,7 @@ export function SearchProvider(props: { children: JSX.Element }) {
setStore('mapFeatures', stopId, feature);
};
return (
<SearchContext.Provider value={{
return <SearchContext.Provider value={{
getSearchText, setSearchText,
getFoundStops, setFoundStops,
getDisplayedPanelId, setDisplayedPanelId,
@@ -160,6 +159,5 @@ export function SearchProvider(props: { children: JSX.Element }) {
getMapFeature, getAllMapFeatures, setMapFeature,
}}>
{props.children}
</SearchContext.Provider>
);
</SearchContext.Provider>;
}

View File

@@ -33,8 +33,7 @@ const StopRepr: VoidComponent<{ stop: Stop }> = (props) => {
const [lineReprs] = createResource<JSX.Element[], string[]>(props.stop.lines, fetchLinesRepr);
return (
<div class="stop">
return <div class="stop">
<svg class="name" viewBox={`0 0 215 ${fontSize}`}>
<text
x="100%" y="55%"
@@ -44,8 +43,7 @@ const StopRepr: VoidComponent<{ stop: Stop }> = (props) => {
</text>
</svg>
<For each={lineReprs()}>{(line: JSX.Element) => line}</For>
</div>
);
</div>;
}
@@ -91,12 +89,11 @@ const StopAreaRepr: VoidComponent<{ stop: Stop }> = (props) => {
}
}
const sortedTransportModes = Object.keys(byModeReprs).sort((x, y) => TransportModeWeights[x] <
TransportModeWeights[y] ? 1 : -1);
const sortedTransportModes = Object.keys(byModeReprs).sort((x, y) => TransportModeWeights[x] < TransportModeWeights[y] ? 1 : -1);
return (
<div class="lineRepr">
<For each={sortedTransportModes}>{(transportMode) => {
return <div class="lineRepr">
<For each={sortedTransportModes}>
{(transportMode) => {
const reprs = byModeReprs[transportMode];
const lineNames = Object.keys(reprs.lines).sort((x, y) => x.localeCompare(y));
return <>
@@ -104,16 +101,14 @@ const StopAreaRepr: VoidComponent<{ stop: Stop }> = (props) => {
<div class="linesRepresentationMatrix">
<For each={lineNames}>{(lineName) => reprs.lines[lineName]}</For>
</div>
</>
}}
</For>
</div >
);
</>;
}}</For>
</div>;
}
const [lineReprs] = createResource(props.stop, fetchLinesRepr);
return (
<div
return <div
class="stop"
onClick={() => setDisplayedStops([props.stop])}
onMouseEnter={() => setHighlightedStop(props.stop)}
@@ -123,21 +118,17 @@ const StopAreaRepr: VoidComponent<{ stop: Stop }> = (props) => {
<ScrollingText height={fontSize} width={100} content={props.stop.name} />
</div>
{lineReprs()}
</div>
);
</div>;
}
export const StopsPanel: ParentComponent<{ stops: Stop[], show: boolean }> = (props) => {
return (
<div classList={{ "stopPanel": true, "displayed": props.show }}>
return <div classList={{ "stopPanel": true, "displayed": props.show }}>
<For each={props.stops.sort((x, y) => x.name.localeCompare(y.name))}>
{(stop) => {
return <Show when={stop.stops !== undefined} fallback={<StopRepr stop={stop} />}>
<StopAreaRepr stop={stop} />
</Show>;
}}
</For>
</div>
);
}}</For>
</div>;
}

View File

@@ -29,8 +29,8 @@ export const StopPopup: ParentComponent<{ stop: Stop, show: boolean }> = (props)
}
const [destinations] = createResource(() => props.stop, getDestinations);
return (
<div ref={popupDiv} classList={{ "popup": true, "displayed": props.show }}>
return <div ref={popupDiv} classList={{ "popup": true, "displayed": props.show }}>
<div class="header">{props.stop?.name}</div>
<div class="body" >
<For each={destinations()}>
@@ -41,9 +41,7 @@ export const StopPopup: ParentComponent<{ stop: Stop, show: boolean }> = (props)
<ScrollingText height={10} width={130} content={dst.destinations.join('/')} />
</div>
</div>;
}}
</For>
}}</For>
</div>
</div >
);
</div>;
}

View File

@@ -33,6 +33,7 @@
background-color: transparent;
.leftAddon {
width: 17%;

View File

@@ -1,4 +1,4 @@
import { createEffect, For, JSX, lazy, ParentComponent, useContext, Show, VoidComponent } from 'solid-js';
import { createEffect, For, JSX, ParentComponent, useContext, Show, VoidComponent } from 'solid-js';
import { lazily } from 'solidjs-lazily';
import { createScrollPosition } from "@solid-primitives/scroll";
@@ -16,11 +16,10 @@ import "./stopsSearchMenu.scss";
const StopNameInput: VoidComponent<{ onInput: JSX.EventHandler<HTMLInputElement, InputEvent>, leftAddon: string, placeholder: string }> = (props) => {
return (
<div class="stopNameInput">
return <div class="stopNameInput">
<div class="leftAddon">{props.leftAddon}</div>
<input type="text" oninput={props.onInput} placeholder={props.placeholder} />
</div>);
</div>;
};
@@ -40,8 +39,7 @@ const Header: VoidComponent<{ title: string, minCharsNb: number }> = (props) =>
}
}
return (
<div class="header">
return <div class="header">
<div class="title">
<svg viewBox="0 0 1260 50">
<text x="0" y="50%" dominant-baseline="middle" font-size="50" style="fill: #ffffff">
@@ -50,8 +48,7 @@ const Header: VoidComponent<{ title: string, minCharsNb: number }> = (props) =>
</svg>
</div>
<StopNameInput onInput={onStopNameInput} leftAddon="🚉 🚏" placeholder="Stop name..." />
</div >
);
</div>;
};
@@ -71,13 +68,15 @@ const StopsPanels: ParentComponent<{ maxStopsPerPanel: number }> = (props) => {
yStopsPanelsScroll();
for (const panel of getPanels()) {
const panelDiv = panel.panel();
const panelDiv = panel.panel;
if (panelDiv != null) {
const panelDivClientRect = panelDiv.getBoundingClientRect();
if (panelDivClientRect.y > 0) {
setDisplayedPanelId(panel.position);
break;
}
}
}
});
return (
@@ -133,7 +132,8 @@ const MapPlaceholder: VoidComponent<{}> = () => {
}
return <div
class="mapPlaceholder" ondblclick={() => onDoubleClick()}>
class="mapPlaceholder"
ondblclick={() => onDoubleClick()}>
Double-clic pour activer la carte
</div>;
};
@@ -166,35 +166,31 @@ const Footer: VoidComponent<{}> = () => {
const { getDisplayedPanelId, getPanels } = searchStore;
return (
<div class="footer">
return <div class="footer">
<For each={getPanels()}>
{(panel) => {
const position = panel.position;
return (
<div>
<svg viewBox="0 0 29 29">
<circle cx="50%" cy="50%" r="13" stroke="#ffffff" stroke-width="3"
<circle
cx="50%" cy="50%" r="13" stroke="#ffffff" stroke-width="3"
style={{ fill: `var(--idfm-${position == getDisplayedPanelId() ? "white" : "black"})` }}
/>
</svg>
</div>
);
}}
</For>
</div>
);
}}</For>
</div>;
};
export const StopsSearchMenu: VoidComponent = () => {
return (
<div class="stopSearchMenu">
return <div class="stopSearchMenu">
<SearchProvider>
<Header title="Recherche de l'arrêt..." minCharsNb={4} />
<Body />
<Footer />
</SearchProvider>
</div>
);
</div>;
};

View File

@@ -34,7 +34,7 @@ export class Passage {
this.arrivalStatus = arrivalStatus;
this.departStatus = departStatus;
}
};
}
export type Passages = Record<string, Record<string, Passage[]>>;
@@ -59,7 +59,7 @@ export class Stop {
this.lines.push(...stop.lines);
}
}
};
}
export type Stops = Record<number, Stop>;
@@ -77,7 +77,7 @@ export class StopShape {
this.epsg3857_bbox = epsg3857_bbox;
this.epsg3857_points = epsg3857_points;
}
};
}
export type StopShapes = Record<number, StopShape>;
@@ -111,6 +111,6 @@ export class Line {
this.audibleSignsAvailable = audibleSignsAvailable;
this.stopIds = stopIds;
}
};
}
export type Lines = Record<string, Line>;

View File

@@ -36,11 +36,11 @@ export function renderLineTransportMode(line: Line): JSX.Element {
}
function renderBusLinePicto(line: Line): JSX.Element {
return (
<div class="busLinePicto">
return <div class="busLinePicto">
<svg viewBox="0 0 31.5 14">
<rect x="0" y="0" width="31.5" height="14" rx="1.5" ry="1.5" style={{ fill: `#${line.backColorHexa}` }} />
<text x="50%"
<text
x="50%"
y="55%"
dominant-baseline="middle"
text-anchor="middle"
@@ -49,18 +49,17 @@ function renderBusLinePicto(line: Line): JSX.Element {
{line.shortName}
</text>
</svg>
</div>
);
</div>;
}
function renderTramLinePicto(line: Line): JSX.Element {
const lineStyle = { fill: `#${line.backColorHexa}` };
return (
<div class="tramLinePicto">
return <div class="tramLinePicto">
<svg viewBox="0 0 20 20">
<rect x="0" y="0" width="20" height="3" rx="1" ry="1" style={lineStyle} />
<rect x="0" y="17" width="20" height="3" rx="1" ry="1" style={lineStyle} />
<text x="50%"
<text
x="50%"
y="55%"
dominant-baseline="middle"
text-anchor="middle"
@@ -69,16 +68,15 @@ function renderTramLinePicto(line: Line): JSX.Element {
{line.shortName}
</text>
</svg>
</div>
);
</div>;
}
function renderMetroLinePicto(line: Line): JSX.Element {
return (
<div class="metroLinePicto">
return <div class="metroLinePicto">
<svg viewBox="0 0 20 20">
<circle cx="10" cy="10" r="10" style={{ fill: `#${line.backColorHexa}` }} />
<text x="50%"
<text
x="50%"
y="55%"
dominant-baseline="middle"
text-anchor="middle"
@@ -86,16 +84,15 @@ function renderMetroLinePicto(line: Line): JSX.Element {
{line.shortName}
</text>
</svg>
</div>
);
</div>;
}
function renderTrainLinePicto(line: Line): JSX.Element {
return (
<div class="trainLinePicto">
return <div class="trainLinePicto">
<svg viewBox="0 0 20 20">
<rect x="0" y="0" width="20" height="20" rx="4.5" ry="4.5" style={{ fill: `#${line.backColorHexa}` }} />
<text x="50%"
<text
x="50%"
y="55%"
dominant-baseline="middle"
text-anchor="middle"
@@ -104,8 +101,7 @@ function renderTrainLinePicto(line: Line): JSX.Element {
{line.shortName}
</text>
</svg>
</div>
);
</div>;
}
export function renderLinePicto(line: Line): JSX.Element {
@@ -152,8 +148,7 @@ export const ScrollingText: VoidComponent<{ height: number, width: number, conte
}
});
return (
<svg ref={viewBoxRef} viewBox={`0 0 ${props.width} ${props.height}`}>
return <svg ref={viewBoxRef} viewBox={`0 0 ${props.width} ${props.height}`}>
<text
ref={textRef}
x="0%" y="55%"
@@ -161,6 +156,5 @@ export const ScrollingText: VoidComponent<{ height: number, width: number, conte
font-size={`${props.height}px`}>
{props.content}
</text>
</svg >
);
</svg>;
}

View File

@@ -4,6 +4,7 @@
"jsxImportSource": "solid-js",
"noImplicitAny": true,
"target": "ES6",
"module": "esnext",
"moduleResolution": "node",
"allowJs": true,
"outDir": "build",