Replace Bootstrap with Material UI
This commit is contained in:
52
sncf-station/src/App.js
Normal file
52
sncf-station/src/App.js
Normal file
@ -0,0 +1,52 @@
|
||||
import {createBrowserRouter, RouterProvider} from "react-router-dom"
|
||||
import Station from "./Station"
|
||||
import {createTheme, CssBaseline, ThemeProvider, useMediaQuery} from "@mui/material"
|
||||
import React, {useMemo} from "react"
|
||||
import {frFR, LocalizationProvider} from "@mui/x-date-pickers"
|
||||
import {AdapterDayjs} from "@mui/x-date-pickers/AdapterDayjs"
|
||||
import 'dayjs/locale/fr'
|
||||
|
||||
function App() {
|
||||
const router = createBrowserRouter([
|
||||
{
|
||||
path: "/",
|
||||
element: <div>Hello World!</div>
|
||||
},
|
||||
{
|
||||
path: "/station/:stopId",
|
||||
element: <Station />
|
||||
}
|
||||
])
|
||||
|
||||
const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)');
|
||||
const theme = useMemo(
|
||||
() =>
|
||||
createTheme({
|
||||
palette: {
|
||||
mode: prefersDarkMode ? 'dark' : 'light',
|
||||
sncf: {
|
||||
departures: {
|
||||
dark: "#003A79",
|
||||
light: "#0064AB",
|
||||
},
|
||||
arrivals: {
|
||||
dark: "#1F5628",
|
||||
light: "#187936",
|
||||
},
|
||||
}
|
||||
},
|
||||
}),
|
||||
[prefersDarkMode],
|
||||
);
|
||||
|
||||
return <>
|
||||
<ThemeProvider theme={theme}>
|
||||
<CssBaseline />
|
||||
<LocalizationProvider dateAdapter={AdapterDayjs} localeText={frFR.components.MuiLocalizationProvider.defaultProps.localeText} adapterLocale="fr">
|
||||
<RouterProvider router={router} />
|
||||
</LocalizationProvider>
|
||||
</ThemeProvider>
|
||||
</>
|
||||
}
|
||||
|
||||
export default App;
|
@ -1,35 +1,27 @@
|
||||
import './sncf.scss'
|
||||
import {useParams, useSearchParams} from "react-router-dom"
|
||||
import TrainsTable from "./TrainsTable"
|
||||
import {useEffect, useState} from "react";
|
||||
import {Box, Button, FormControl, FormControlLabel, FormGroup, FormLabel} from "@mui/material";
|
||||
import {DatePicker, DateTimePicker, TimePicker} from "@mui/x-date-pickers";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
function DateTimeSelector({date, time}) {
|
||||
return <>
|
||||
<form>
|
||||
<div className="form-group row">
|
||||
<div className="col-sm-2">
|
||||
Modifier la date et l'heure de recherche
|
||||
</div>
|
||||
<label htmlFor="date" className="col-sm-1 col-form-label">Date</label>
|
||||
<div className="col-sm-1">
|
||||
<input type="date" name="date" className="form-control" id="date" defaultValue={date} />
|
||||
</div>
|
||||
<label htmlFor="time" className="col-sm-1 col-form-label">Heure</label>
|
||||
<div className="col-sm-1">
|
||||
<input type="time" name="time" className="form-control" id="time" defaultValue={time} />
|
||||
</div>
|
||||
<div className="col-sm-1">
|
||||
<button type="submit" className="btn btn-primary">Rechercher</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<Box component="form" display="flex" alignItems="center" sx={{'& .MuiTextField-root': { m: 1, width: '25ch' },}}>
|
||||
<FormLabel>
|
||||
Modifier la date et l'heure de recherche :
|
||||
</FormLabel>
|
||||
<DatePicker name="date" label="Date" format="YYYY-MM-DD" defaultValue={dayjs(`${date}`)} />
|
||||
<TimePicker name="time" label="Heure" format="HH:mm" defaultValue={dayjs(`${date} ${time}`)} />
|
||||
<Button type="submit">Rechercher</Button>
|
||||
</Box>
|
||||
</>
|
||||
}
|
||||
|
||||
function Station() {
|
||||
let {stopId} = useParams()
|
||||
let [stop, setStop] = useState({'name': "Chargement…"})
|
||||
let [searchParams, setSearchParams] = useSearchParams()
|
||||
let [searchParams, _setSearchParams] = useSearchParams()
|
||||
const now = new Date()
|
||||
let dateNow = now.toISOString().split('T')[0]
|
||||
let timeNow = now.toTimeString().split(' ')[0].substring(0, 5)
|
||||
@ -44,6 +36,16 @@ function Station() {
|
||||
})
|
||||
}, [stopId])
|
||||
|
||||
if (time === timeNow) {
|
||||
setInterval(() => {
|
||||
const now = new Date()
|
||||
let dateNow = now.toISOString().split('T')[0]
|
||||
let timeNow = now.toTimeString().split(' ')[0].substring(0, 5)
|
||||
setDate(dateNow)
|
||||
setTime(timeNow)
|
||||
}, 60000)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="Station">
|
||||
<header className="App-header">
|
||||
|
@ -1,24 +1,51 @@
|
||||
import {useEffect, useState} from "react";
|
||||
import {
|
||||
Box,
|
||||
Paper,
|
||||
styled,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableContainer,
|
||||
TableHead,
|
||||
TableRow,
|
||||
Typography
|
||||
} from "@mui/material";
|
||||
|
||||
const StyledTableRow = styled(TableRow)(({ theme, tableType }) => ({
|
||||
'tbody &:nth-of-type(odd)': {
|
||||
backgroundColor: theme.palette.sncf[tableType].light,
|
||||
},
|
||||
'th, &:nth-of-type(even)': {
|
||||
backgroundColor: theme.palette.sncf[tableType].dark,
|
||||
},
|
||||
// hide last border
|
||||
'&:last-child td, &:last-child th': {
|
||||
border: 0,
|
||||
},
|
||||
}));
|
||||
|
||||
function TrainsTable({stop, date, time, tableType}) {
|
||||
let tableClass = tableType === "departures" ? "table-departures" : "table-arrivals"
|
||||
tableClass = `table table-striped ${tableClass}`
|
||||
return <>
|
||||
<table className={tableClass}>
|
||||
<TrainsTableHeader />
|
||||
<TrainsTableBody stop={stop} date={date} time={time} tableType={tableType} />
|
||||
</table>
|
||||
<TableContainer>
|
||||
<Table>
|
||||
<TrainsTableHeader tableType={tableType} />
|
||||
<TrainsTableBody stop={stop} date={date} time={time} tableType={tableType} />
|
||||
</Table>
|
||||
</TableContainer>
|
||||
</>
|
||||
}
|
||||
|
||||
function TrainsTableHeader() {
|
||||
return <thead>
|
||||
<tr>
|
||||
<th className="px-3 py-1" scope="col" colSpan="2">Train</th>
|
||||
<th className="px-3 py-1" scope="col">Heure</th>
|
||||
<th className="px-3 py-1" scope="col">Destination</th>
|
||||
</tr>
|
||||
</thead>
|
||||
function TrainsTableHeader({tableType}) {
|
||||
return <>
|
||||
<TableHead>
|
||||
<StyledTableRow tableType={tableType}>
|
||||
<TableCell colSpan="2" fontSize={16} fontWeight="bold">Train</TableCell>
|
||||
<TableCell fontSize={16} fontWeight="bold">Heure</TableCell>
|
||||
<TableCell fontSize={16} fontWeight="bold">Destination</TableCell>
|
||||
</StyledTableRow>
|
||||
</TableHead>
|
||||
</>
|
||||
}
|
||||
|
||||
function TrainsTableBody({stop, date, time, tableType}) {
|
||||
@ -37,9 +64,9 @@ function TrainsTableBody({stop, date, time, tableType}) {
|
||||
|
||||
let table_rows = trains.map((train) => <TrainRow train={train} tableType={tableType} />)
|
||||
|
||||
return <tbody>
|
||||
return <TableBody>
|
||||
{table_rows}
|
||||
</tbody>
|
||||
</TableBody>
|
||||
}
|
||||
|
||||
function TrainRow({train, tableType}) {
|
||||
@ -94,47 +121,50 @@ function TrainRow({train, tableType}) {
|
||||
}, [trip.route])
|
||||
|
||||
let headline = stopTimes[tableType === "departures" ? stopTimes.length - 1 : 0]?.stop ?? {name: "Chargement…"}
|
||||
let stops_names = stopTimes.filter(stop_time => tableType === "departures" ? stop_time.stop_sequence > train.stop_sequence : stop_time.stop_sequence < train.stop_sequence)
|
||||
.map(stop_time => stop_time?.stop.name ?? "").join(", ")
|
||||
|
||||
let style = {
|
||||
width: "4em",
|
||||
height: "4em",
|
||||
borderRadius: "15%",
|
||||
backgroundColor: `#${getBackgroundColor(train, route)}`,
|
||||
color: `#${getTextColor(train, route)}`,
|
||||
fontWeight: "bold",
|
||||
}
|
||||
let stops_filter
|
||||
if (tableType === "departures")
|
||||
stops_filter = (stop_time) => stop_time.stop_sequence > train.stop_sequence && stop_time.drop_off_type === 0
|
||||
else
|
||||
stops_filter = (stop_time) => stop_time.stop_sequence < train.stop_sequence && stop_time.pickup_type === 0
|
||||
let stops_names = stopTimes.filter(stops_filter).map(stop_time => stop_time?.stop.name ?? "").join(", ")
|
||||
|
||||
return <>
|
||||
<tr className="h-100">
|
||||
<td className="h-100">
|
||||
<div className="train-type d-flex h-100 align-items-center justify-content-center">
|
||||
<div
|
||||
className="transilien-route d-flex align-items-center justify-content-center font-weight-bold small p-1 m-2 text-center text-wrap"
|
||||
style={style}>
|
||||
{trainType}
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td className="h-100">
|
||||
<div className="train-number d-flex align-items-center justify-content-center h-100">
|
||||
<StyledTableRow tableType={tableType}>
|
||||
<TableCell>
|
||||
<div>
|
||||
<div>{trip.short_name}</div>
|
||||
<div>{trip.headsign}</div>
|
||||
<Box display="flex"
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
textAlign="center"
|
||||
width="4em"
|
||||
height="4em"
|
||||
borderRadius="15%"
|
||||
fontWeight="bold"
|
||||
backgroundColor={`#${getBackgroundColor(train, route)}`}
|
||||
color={`#${getTextColor(train, route)}`}>
|
||||
{trainType}
|
||||
</Box>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div className="table-hour d-flex align-items-center justify-content-center text-time fw-bold h-100">
|
||||
{getDisplayTime(train, tableType)}
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<h3 className="headline">{headline.name}</h3>
|
||||
<span className="stops">{stops_names}</span>
|
||||
</td>
|
||||
</tr>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Box display="flex" alignItems="center" justifyContent="center" textAlign="center">
|
||||
<div>
|
||||
<div>{trip.short_name}</div>
|
||||
<div>{trip.headsign}</div>
|
||||
</div>
|
||||
</Box>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Box display="flex" alignItems="center" justifyContent="center" fontWeight="bold" color="#FFED02" fontSize={24}>
|
||||
{getDisplayTime(train, tableType)}
|
||||
</Box>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Typography fontSize={24} fontWeight="bold">{headline.name}</Typography>
|
||||
<span className="stops">{stops_names}</span>
|
||||
</TableCell>
|
||||
</StyledTableRow>
|
||||
</>
|
||||
}
|
||||
|
||||
|
@ -1,25 +1,13 @@
|
||||
import React from 'react';
|
||||
import React, {useMemo} from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import './index.css';
|
||||
import Station from './Station';
|
||||
import reportWebVitals from './reportWebVitals';
|
||||
import {createBrowserRouter, RouterProvider} from "react-router-dom";
|
||||
|
||||
const router = createBrowserRouter([
|
||||
{
|
||||
path: "/",
|
||||
element: <div>Hello World!</div>
|
||||
},
|
||||
{
|
||||
path: "/station/:stopId",
|
||||
element: <Station />
|
||||
}
|
||||
])
|
||||
import App from "./App";
|
||||
|
||||
const root = ReactDOM.createRoot(document.getElementById('root'));
|
||||
root.render(
|
||||
<React.StrictMode>
|
||||
<RouterProvider router={router} />
|
||||
<App />
|
||||
</React.StrictMode>
|
||||
);
|
||||
|
||||
|
@ -1,31 +0,0 @@
|
||||
@import "~bootstrap/scss/_functions.scss";
|
||||
@import "~bootstrap/scss/_variables.scss";
|
||||
|
||||
$departures: #003A79;
|
||||
$arrivals: #1F5628;
|
||||
$time: #FF9500;
|
||||
|
||||
$custom-colors: (
|
||||
"departures": $departures,
|
||||
"arrivals": $arrivals,
|
||||
"time": $time,
|
||||
);
|
||||
|
||||
// Merge the maps
|
||||
$theme-colors: map-merge($theme-colors, $custom-colors);
|
||||
|
||||
$sncf-table-variants: (
|
||||
"departures": $departures,
|
||||
"arrivals": $arrivals,
|
||||
);
|
||||
$sncf-table-variants-stripped: (
|
||||
"departures": $departures,
|
||||
"arrivals": $arrivals,
|
||||
);
|
||||
|
||||
$table-variants: map-merge($table-variants, $sncf-table-variants);
|
||||
$table-striped-bg-factor: 0.25;
|
||||
|
||||
|
||||
|
||||
@import "~bootstrap/scss/bootstrap.scss";
|
Reference in New Issue
Block a user