From a367a146bd804c59adf9394582ac7df0e8e96fa8 Mon Sep 17 00:00:00 2001 From: e-halinen <54105602+e-halinen@users.noreply.github.com> Date: Tue, 15 Oct 2024 15:14:17 +0300 Subject: [PATCH] AB#32085: Change LineTimetable print layout to A5 (#420) * AB#32085: Change print layout to A5, tweak styling * Variant logic for tram routes * Add empty array checks to timetable rendering --- scripts/generator.js | 7 ++ src/components/lineTimetable/allStopsList.css | 10 ++- .../lineTimetable/lineTableColumns.css | 33 ++++--- .../lineTimetable/lineTableColumns.js | 33 ++++--- .../lineTimetable/lineTableHeader.css | 23 +++-- .../lineTimetable/lineTableHeader.js | 12 ++- .../lineTimetable/lineTimetable.css | 18 +++- src/components/lineTimetable/lineTimetable.js | 89 +++++++++++++------ .../lineTimetable/lineTimetableContainer.js | 63 +++++++++++-- .../lineTimetable/lineTimetableHeader.css | 8 +- src/components/timetable/timetable.js | 2 +- .../timetable/timetableContainer.js | 2 +- 12 files changed, 219 insertions(+), 81 deletions(-) diff --git a/scripts/generator.js b/scripts/generator.js index 3734d03f..620ee83b 100644 --- a/scripts/generator.js +++ b/scripts/generator.js @@ -94,6 +94,13 @@ async function renderComponent(options) { margin: 0, timeout: PDF_TIMEOUT, }; + } else if (props.printAsA5) { + printOptions = { + printBackground: true, + format: 'A5', + margin: 0, + timeout: PDF_TIMEOUT, + }; } else { printOptions = { printBackground: true, diff --git a/src/components/lineTimetable/allStopsList.css b/src/components/lineTimetable/allStopsList.css index f5de0443..727cc90f 100644 --- a/src/components/lineTimetable/allStopsList.css +++ b/src/components/lineTimetable/allStopsList.css @@ -1,19 +1,21 @@ .stopListsContainer { - margin-top: 2rem; - max-width: 1171px; + margin-top: 1rem; + max-width: 95%; page-break-inside: avoid; + page-break-after: always; } .stopList { - margin-bottom: 2rem; + margin-bottom: 1rem; padding-left: 1.2rem; } .lineInfoText { font-family: GothamRounded-Book; - font-size: 2rem; + font-size: 1rem; } .stopListText { font-family: GothamRounded-Medium; + font-size: 0.5rem; } diff --git a/src/components/lineTimetable/lineTableColumns.css b/src/components/lineTimetable/lineTableColumns.css index 3c535058..3b829c0a 100644 --- a/src/components/lineTimetable/lineTableColumns.css +++ b/src/components/lineTimetable/lineTableColumns.css @@ -1,20 +1,23 @@ .departureRowContainer { - max-width: 1171px; + max-width: 420px; min-width: 120px; font-family: GothamRounded-Book; margin: 0.5rem 0; } .departureRow { - font-size: 1.2rem; - margin-left: 2rem; + font-size: 1rem; + margin-left: 1.6rem; padding-top: 5px; padding-bottom: 5px; + height: 35px; } .departureColumnContainer { flex-grow: 1; align-items: normal; + min-width: 100px; + max-width: 350px; } .tableContainer { @@ -23,13 +26,14 @@ } .wider { - min-width: 400px !important; + min-width: 270px; } .hour { font-family: GothamXNarrow-Medium; - min-width: 3rem; - align-self: baseline; + font-size: 15px; + width: 1.5rem; + margin-right: 0.5em; } .minutesContainer { @@ -40,13 +44,20 @@ .minutes { display: flex; - min-width: 2em; - padding: 0.25em 0 0 0.6em; + min-width: 0.9em; font-family: GothamXNarrow-Book; - font-size: 0.75em; - letter-spacing: -0.025em; + font-size: 12px; + letter-spacing: -0.02em; line-height: 1; - margin-right: 0.25em; + margin-right: 0.2em; +} + +.divider { + border-right: 2px solid #3333338a; +} + +.departureRowContainer > *:nth-child(even) { + background-color: #eaeaea; } @media print { diff --git a/src/components/lineTimetable/lineTableColumns.js b/src/components/lineTimetable/lineTableColumns.js index 6c14462a..7e11b075 100644 --- a/src/components/lineTimetable/lineTableColumns.js +++ b/src/components/lineTimetable/lineTableColumns.js @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import { Column, Row, WrappingRow } from '../util'; import LineTableHeader from './lineTableHeader'; import styles from './lineTableColumns.css'; -import classNames from 'classnames'; +import classnames from 'classnames'; import { isArray, filter, isEmpty, groupBy } from 'lodash'; import { filterDuplicateDepartureHours, getDuplicateCutOff } from '../timetable/tableRows'; @@ -19,8 +19,8 @@ const LineTimetableRow = props => {
{sortedMinuteDepartures.map((departure, index) => (
- {departure.note === 'p' - ? `${departure.minutes.toString().padStart(2, '0')} pe` + {departure.note !== null + ? `${departure.minutes.toString().padStart(2, '0')}${departure.note}` : departure.minutes.toString().padStart(2, '0')}
))} @@ -87,8 +87,15 @@ const DeparturesColumn = props => { }); return ( -
- +
+
{departureRows}
); @@ -104,6 +111,8 @@ const DeparturesColumn = props => { DeparturesColumn.propTypes = { departures: PropTypes.array.isRequired, stop: PropTypes.object.isRequired, + showBothDirections: PropTypes.bool.isRequired, + isLastStop: PropTypes.bool.isRequired, }; const LineTableColumns = props => { @@ -117,13 +126,12 @@ const LineTableColumns = props => { }); return (
- +
@@ -131,13 +139,12 @@ const LineTableColumns = props => { } return (
- +
diff --git a/src/components/lineTimetable/lineTableHeader.css b/src/components/lineTimetable/lineTableHeader.css index db5490be..8c9154e7 100644 --- a/src/components/lineTimetable/lineTableHeader.css +++ b/src/components/lineTimetable/lineTableHeader.css @@ -1,20 +1,27 @@ +.headerContainer { + display: flex; +} + .stop { flex-grow: 1; - height: 60px; + height: 50px; padding-right: 16px; - margin-bottom: 2rem; + margin-bottom: 1.5rem; } .stopNamePrimary { - font-size: 1.2em; - margin: 0 0 0 2rem; + font-size: 1rem; + margin: 0 0 0 1.2rem; font-family: GothamRounded-Medium; - word-break: normal; } .stopNameSecondary { - font-size: 1.2em; - margin: 0 0 0 2rem; + font-size: 1rem; + margin: 0 0 0 1.2rem; font-family: GothamXNarrow-Book; - word-break: normal; +} + +.directionBracket { + font-family: GothamRounded-Medium; + font-size: 1rem; } diff --git a/src/components/lineTimetable/lineTableHeader.js b/src/components/lineTimetable/lineTableHeader.js index 2ca8c544..ffa2005e 100644 --- a/src/components/lineTimetable/lineTableHeader.js +++ b/src/components/lineTimetable/lineTableHeader.js @@ -4,17 +4,21 @@ import PropTypes from 'prop-types'; import styles from './lineTableHeader.css'; const LineTableHeader = props => { - const { stop } = props; + const { stop, isLastStop } = props; return ( -
-

{stop.nameFi}

-

{stop.nameSe}

+
+
+

{stop.nameFi}

+

{stop.nameSe}

+
+ {!isLastStop &&
>
}
); }; LineTableHeader.propTypes = { stop: PropTypes.object.isRequired, + isLastStop: PropTypes.bool.isRequired, }; export default LineTableHeader; diff --git a/src/components/lineTimetable/lineTimetable.css b/src/components/lineTimetable/lineTimetable.css index c6699eb2..742cd477 100644 --- a/src/components/lineTimetable/lineTimetable.css +++ b/src/components/lineTimetable/lineTimetable.css @@ -143,14 +143,15 @@ } .timetableDays { - margin: 0 1rem 1rem 2rem; - font-size: 1.5em; + margin: 0 1rem 1rem 1.2rem; + font-size: 0.8em; font-family: GothamRounded-Book; + word-wrap: break-word; } .timetableDates { margin: 0 1rem 1rem 2rem; - font-size: 1.5em; + font-size: 0.8em; font-family: GothamRounded-Book; } @@ -168,6 +169,17 @@ page-break-after: always; } +.fridayNote { + margin-left: 1.5rem; + font-family: GothamRounded-Book; + font-size: 12px; +} + +.notesContainer { + margin: 0.5rem 0 1rem 0.5rem; + page-break-after: always; +} + @media print { .noPrint, .noPrint * { diff --git a/src/components/lineTimetable/lineTimetable.js b/src/components/lineTimetable/lineTimetable.js index 7d38558f..6b173f68 100644 --- a/src/components/lineTimetable/lineTimetable.js +++ b/src/components/lineTimetable/lineTimetable.js @@ -21,7 +21,7 @@ import { scheduleSegments } from '../../util/domain'; import { addMissingFridayNote, combineConsecutiveDays } from '../timetable/timetableContainer'; import { shortenTrainParsedLineId } from '../../util/routes'; -const MAX_STOPS = 6; // Maximum amount of timed stops rendered on the timetable +const MAX_STOPS = 4; // Maximum amount of timed stops rendered on the timetable const getScheduleWeekdaysText = dayType => { switch (dayType) { @@ -183,6 +183,7 @@ const RouteDepartures = props => { }); const combinedDepartureTables = Object.keys(mergedWeekdaysDepartures[0].combinedDays).map(key => { + const showDivider = departuresByStop.length === 1 ? false : !showTimedStops; return (
{ {formatDate(new Date(dateBegin))}-{formatDate(new Date(dateEnd))} @@ -232,16 +233,6 @@ RouteDepartures.propTypes = { showTimedStops: PropTypes.bool, }; -const dateRangeHasDepartures = routeDepartures => { - const hasDepartures = find( - Object.values(routeDepartures.departuresByStop[0].departures), - weekday => { - return !isEmpty(weekday); - }, - ); - return hasDepartures; -}; - const checkForTrainRoutes = routes => { return routes.map(route => { if (route.mode === 'RAIL') { @@ -251,12 +242,27 @@ const checkForTrainRoutes = routes => { }); }; +// Add note for friday departures because of merged timetables +const addFridayNote = notes => { + return notes.splice(0, 0, { noteText: 'p) Vain perjantaisin' }); +}; + function LineTimetable(props) { const { routes } = props; + const notes = props.line.notes.nodes; + addFridayNote(notes); const showTimedStops = hasTimedStopRoutes(routes); const checkedRoutes = checkForTrainRoutes(routes); + const mappedNotes = notes.map(note => { + return ( +
+ {note.noteText} +
+ ); + }); + if (showTimedStops) { return (
@@ -311,6 +317,12 @@ function LineTimetable(props) { return routeDeparturesForDateRanges; })} + {checkedRoutes.length >= 1 &&
{mappedNotes}
} + {checkedRoutes.length === 0 && ( +
+ Linjaa ei löytynyt, tarkista tulosteen asetukset +
+ )}
); } @@ -362,27 +374,50 @@ function LineTimetable(props) { return routeWithDepartures.departuresByDateRanges.map(departuresFordateRange => { const { nameFi, nameSe, routeIdParsed } = routeWithDepartures; const { dateBegin, dateEnd, departuresByStop } = departuresFordateRange; + + const hasDepartures = some(departuresByStop, stop => + some(stop.departures, departureDay => departureDay.length > 0), + ); + + if ( + hasDepartures && + departuresByStop[0].stop.stopId === departuresByStop[1].stop.stopId + ) { + departuresByStop.pop(1); + } + return (
- - + {hasDepartures && ( + + )} + {hasDepartures && ( + + )} + {hasDepartures &&
}
); }); })} + {checkedRoutes.length >= 1 &&
{mappedNotes}
} + {checkedRoutes.length === 0 && ( +
+ Linjaa ei löytynyt, tarkista tulosteen asetukset +
+ )}
); } diff --git a/src/components/lineTimetable/lineTimetableContainer.js b/src/components/lineTimetable/lineTimetableContainer.js index 3abadf85..b66d77b0 100644 --- a/src/components/lineTimetable/lineTimetableContainer.js +++ b/src/components/lineTimetable/lineTimetableContainer.js @@ -4,7 +4,7 @@ import { graphql } from 'react-apollo'; import gql from 'graphql-tag'; import mapProps from 'recompose/mapProps'; import compose from 'recompose/compose'; -import { filter, isEmpty, uniqBy } from 'lodash'; +import { filter, forEach, isEmpty, uniqBy, some } from 'lodash'; import apolloWrapper from 'util/apolloWrapper'; @@ -74,6 +74,7 @@ const lineQuery = gql` dateEnd timingStopType stopIndex + note } } } @@ -90,6 +91,8 @@ const lineQuery = gql` } `; +const VARIKKOLINJA_REGEX = /\d{4}[\w]\d+/; + const regularDayTypes = ['Ma', 'Ti', 'Ke', 'To', 'Pe', 'La', 'Su']; const groupByValidityDateRange = departures => { @@ -142,11 +145,59 @@ const groupDeparturesByWeekday = departuresByStop => { }); }; -// Filters 'varikkolinja' routes from the timetable -const removeExtraRoutes = routes => { - return filter(routes, route => { +const hasSameTimedStops = (variantRoute, regularRoute) => { + const regularTimedStops = regularRoute.timedStops.nodes; + const variantTimedStops = variantRoute.timedStops.nodes; + + if (regularTimedStops.length !== variantTimedStops.length) { + return false; + } + + let hasSameStops = true; + for (let i = 0; i < regularTimedStops.length; i++) { + const regularRouteStop = regularTimedStops[i]; + const variantRouteStop = variantTimedStops[i]; + + if (regularRouteStop.stop.stopId !== variantRouteStop.stop.stopId) { + hasSameStops = false; + } + } + return hasSameStops; +}; + +// Merge variant routes from the timetable if they have the same timed stops. +const mergeExtraRoutes = routes => { + // Filter 'varikkolinja' routes from the list + const filteredRoutes = filter(routes, route => { + if (route.mode === 'TRAM') { + return route.routeId.match(VARIKKOLINJA_REGEX) === null; + } + return true; + }); + + const regularRoutes = filter(filteredRoutes, route => { return !route.routeId.includes(' '); }); + + const variantRoutes = filter(filteredRoutes, route => { + return route.routeId.includes(' '); + }); + + forEach(variantRoutes, variantRoute => { + forEach(regularRoutes, regularRoute => { + if ( + regularRoute.routeIdParsed === variantRoute.routeIdParsed && + regularRoute.mode === variantRoute.mode && + regularRoute.direction === variantRoute.direction + ) { + if (hasSameTimedStops(variantRoute, regularRoute)) { + // Found a matching "regular" route where we can merge variant departures + regularRoute.timedStopsDepartures.nodes.push(...variantRoute.timedStopsDepartures.nodes); + } + } + }); + }); + return regularRoutes; }; // Filters empty routes from the timetable @@ -160,9 +211,9 @@ const lineQueryMapper = mapProps(props => { const line = props.data.lines.nodes[0]; const { showPrintBtn, lang } = props; - const filteredRoutes = removeExtraRoutes(line.routes.nodes); + const mergedRoutes = mergeExtraRoutes(line.routes.nodes); - const routesWithGroupedDepartures = filteredRoutes.map(route => { + const routesWithGroupedDepartures = mergedRoutes.map(route => { const byValidityDateRange = groupByValidityDateRange(route.timedStopsDepartures.nodes); const departuresByStopsAndDateRanges = groupDepartureDateRangesForStops( route, diff --git a/src/components/lineTimetable/lineTimetableHeader.css b/src/components/lineTimetable/lineTimetableHeader.css index bca7631c..3a5502e4 100644 --- a/src/components/lineTimetable/lineTimetableHeader.css +++ b/src/components/lineTimetable/lineTimetableHeader.css @@ -2,10 +2,12 @@ display: flex; margin-bottom: 1rem; word-wrap: break-word; + page-break-before: always; + page-break-inside: avoid; } .lineId { - font-size: 4rem; + font-size: 2rem; margin: 2rem 2rem 0 2rem; font-family: GothamRounded-Medium; } @@ -16,12 +18,12 @@ } .lineName { - font-size: 30px; + font-size: 20px; font-family: GothamRounded-Medium; } .lineNameSecondary { - font-size: 25px; + font-size: 15px; font-family: GothamRounded-Book; } diff --git a/src/components/timetable/timetable.js b/src/components/timetable/timetable.js index 330245a1..256880a5 100644 --- a/src/components/timetable/timetable.js +++ b/src/components/timetable/timetable.js @@ -46,7 +46,7 @@ const getZoneLetterStyle = zone => ({ : 'translate(-50%, -50%)', // No px adjustments for zone A and the "else" case. }); -const getNotes = (notes, symbols) => { +export const getNotes = (notes, symbols) => { const parsedNotes = []; symbols.forEach(symbol => { notes.forEach(note => { diff --git a/src/components/timetable/timetableContainer.js b/src/components/timetable/timetableContainer.js index b4f1666a..af3e1ea4 100644 --- a/src/components/timetable/timetableContainer.js +++ b/src/components/timetable/timetableContainer.js @@ -258,7 +258,7 @@ export function addMissingFridayNote(departure) { departure.dayType.includes('Pe') && (!departure.note || !departure.note.includes('p')) ? 'p' - : null; + : departure.note; } function addMissingNonAccessibleNote(departure) {