diff --git a/src/lib/components/ui/station-board/StaffServicesTable.svelte b/src/lib/components/ui/station-board/StaffServicesTable.svelte index 20c733f..b76736d 100644 --- a/src/lib/components/ui/station-board/StaffServicesTable.svelte +++ b/src/lib/components/ui/station-board/StaffServicesTable.svelte @@ -1,6 +1,6 @@ - - - - - - - - - - - - - - - - +
ArrDep
IDOrigDestPltSchActSchAct
+ + + + + + + + + + + + + + + + + + + {#each services as service (getRowKey(service))} + + + + + + + + + {#if service.wtp} + + + + + {:else} + + + + + + + + + + {/if} - - {#each services as service (getRowKey(service))} - - - - - - - - - {#if service.wtp} - - - - {:else} - - - - - {/if} - - - {#if service.c && service.cr?.r} - - - - - {/if} - {#if service.dr?.r} - - - - - {/if} - - {/each} - -
ArrDep
IDOrigDestPltSchActSchAct
{service.h}{service.og.t}{service.dt.t}{service.p || '-'}Pass{formatUkTime(service.wtp)} + + {service.c + ? '-' + : delayClassFromTimePair(service.wtp, service.atp || service.etp) === 'delay-rt' + ? 'RT' + : formatUkTime(service.atp || service.etp)} + + {formatUkTime(service.sta)} + + {service.c + ? '-' + : delayClassFromTimePair(service.sta, service.ata || service.eta) === 'delay-rt' + ? 'RT' + : formatUkTime(service.ata || service.eta)} + + {formatUkTime(service.std)} + + {service.c + ? '-' + : delayClassFromTimePair(service.std, service.atd || service.etd) === 'delay-rt' + ? 'RT' + : formatUkTime(service.atd || service.etd)} + +
{service.h}{service.og.t}{service.dt.t}{service.p}Pass{formatUkTime(service.wtp)}{formatUkTime(service.atp || service.etp)}{formatUkTime(service.sta)}{formatUkTime(service.ata || service.eta)}{formatUkTime(service.std)}{formatUkTime(service.atd || service.etd)}
- {service.cr.r} - {#if service.cr.l} - {service.cr.n ? "near" : "at"} - {service.cr.l} - {/if} -
- {service.dr.r} - {#if service.dr.l} - {service.dr.n ? "near" : "at"} - {service.dr.l} - {/if} -
+ {#if service.c && service.cr?.r} + + + {service.cr.r} + {#if service.cr.l} + {service.cr.n ? 'near' : 'at'} + {service.cr.l} + {/if} + + + {/if} + {#if service.dr?.r} + + + {service.dr.r} + {#if service.dr.l} + {service.dr.n ? 'near' : 'at'} + {service.dr.l} + {/if} + + + {/if} + + {/each} + \ No newline at end of file + + /* Special Row Styles */ + .cancel-row td, + .delay-row td { + font-size: 0.88rem; + padding-bottom: 8px; + } + + .cancel-row td[colspan], + .delay-row td[colspan] { + padding-left: 0.75ch; + } + + .cancel-row td { + color: rgb(255, 131, 131); + font-weight: 400; + } + + .delay-row td { + color: var(--delay-orange); + font-style: italic; + } + + /* Column Specifics */ + .id-cell { + text-align: center; + font-weight: 400; + font-stretch: 80%; + filter: brightness(0.75); + } + .orig-cell, + .dest-cell { + font-stretch: 90%; + font-weight: 400; + } + .orig-cell { + text-align: left; + color: var(--location-yellow); + } + .dest-cell { + text-align: right; + color: var(--location-yellow); + } + .plt-cell { + text-align: center; + font-weight: 405; + font-stretch: 70%; + } + .plt-cell.platSup { + font-weight: 200; + opacity: 0.3; + } + .plt-cell.platChange { + animation: fast-pulse 2s ease-out infinite; + } + .service-row.serviceCancelled .plt-cell { + text-decoration: line-through; + } + .pass-cell { + text-align: center; + } + /* Colour orig and dest values when cancelled */ + .service-row.serviceCancelled .orig-cell, + .service-row.serviceCancelled .dest-cell { + color: rgb(255, 131, 131); + } + + .time-cell { + text-align: center; + font-stretch: 70%; + } + + /* RT Logic */ + .time-cell.delay-rt span { + display: none; + } + .time-cell.delay-rt::after { + content: 'RT'; + } + + .time-cell.delay-early { + color: var(--early-blue); + } + + .time-cell.delay-late { + color: var(--delay-orange); + } + + /* Time Types */ + .est { + font-style: italic; + opacity: 0.75; + font-weight: 200; + } + .act { + font-weight: 600; + } + diff --git a/src/lib/components/ui/station-board/StationAlertCard.svelte b/src/lib/components/ui/station-board/StationAlertCard.svelte index 3bbe14a..837891c 100644 --- a/src/lib/components/ui/station-board/StationAlertCard.svelte +++ b/src/lib/components/ui/station-board/StationAlertCard.svelte @@ -49,6 +49,11 @@ margin-bottom: 0; width: 95%; max-width: 750px; + -webkit-tap-highlight-color: transparent; + } + + .alert-card:active { + filter: brightness(1.2); } .trigger { @@ -56,7 +61,7 @@ display: flex; border-radius: 8px; align-items: center; - padding: 0.4rem 1rem; + padding: 0.1rem 1.05rem; background: var(--alert-orange); color: white; border: none; @@ -64,13 +69,13 @@ text-align: left; position: relative; z-index: 2; - height: 46px; + height: 40px; transition: all 0.65s 0.2s; } .trigger.active { border-radius: 8px 8px 0 0; - transition: all 0.1s 0s; + transition: all 0.01s 0s; } .warning-icon { @@ -105,7 +110,7 @@ .content { padding: 1rem; position: absolute; - top: 46px; + top: 40px; border-radius: 0 0 8px 8px; left: 0; right: 0; diff --git a/src/lib/global.css b/src/lib/global.css index 5f8f8fa..1b10f6b 100644 --- a/src/lib/global.css +++ b/src/lib/global.css @@ -129,6 +129,36 @@ font-style: normal; font-display: swap; } + +/* Fira Code - Variable with fallback */ +@font-face { + font-family: 'Fira Code'; + src: url('/type/fira-code/FiraCode-VF.woff2') format('woff2-variations'); + font-weight: 300 700; + font-style: normal; + font-display: swap; +} +@font-face { + font-family: 'Fira Code'; + src: url('/type/fira-code/FiraCode-Regular.woff2') format('woff2'); + font-weight: 400; + font-style: normal; + font-display: swap; +} + +/* Inconsolata Variable */ +@font-face { + font-family: 'Inconsolata Variable'; + font-style: normal; + font-display: swap; + font-weight: 200 900; + font-stretch: 50% 200%; + src: url(type/Inconsolata/latin-wdth-normal.woff2) format('woff2-variations'); + unicode-range: + U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, + U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} + :root { /* Fixed Heights */ --header-height: 80px; @@ -163,7 +193,7 @@ opacity: 1; } 50% { - opacity: 0.3; + opacity: 0.5; } } diff --git a/src/lib/utils/time.ts b/src/lib/utils/time.ts index 5805713..dd48a3e 100644 --- a/src/lib/utils/time.ts +++ b/src/lib/utils/time.ts @@ -1,6 +1,6 @@ import type { ApiTrainsTrainDetails } from '@owlboard/owlboard-ts'; -export const estClass = (act: any, est: any) => (!act && est ? 'est' : 'act'); +export const estClass = (act: any, est: any) => (act ? 'act' : 'est'); /** * Converts ISO/JSON time to UK-formatted HH:MM string, with optional (default off) seconds @@ -10,10 +10,10 @@ export function formatUkTime( dateStr: string | Date | undefined, includeSeconds: boolean = false ): string { - if (!dateStr) return '--:--'; + if (!dateStr) return '-'; const date = typeof dateStr === 'string' ? new Date(dateStr) : dateStr; - if (isNaN(date.getTime())) return '--:--'; + if (isNaN(date.getTime())) return '-'; return date.toLocaleTimeString('en-GB', { hour: '2-digit', @@ -56,15 +56,21 @@ export function formatUkDateTime( * Specific type that can handle the TrainDetails.ServiceLocation and the ApiStationsBoard.BoardService types for delay calculation */ interface DelayInput { - // Board types - sta?: string | null; std?: string | null; - eta?: string | null; etd?: string | null; - ata?: string | null; atd?: string | null; - - // Journey types - pta?: string | null; ptd?: string | null; - wta?: string | null; wtd?: string | null; - atp?: string | null; wtp?: string | null; + // Board types + sta?: string | null; + std?: string | null; + eta?: string | null; + etd?: string | null; + ata?: string | null; + atd?: string | null; + + // Journey types + pta?: string | null; + ptd?: string | null; + wta?: string | null; + wtd?: string | null; + atp?: string | null; + wtp?: string | null; etp?: string | null; } @@ -79,13 +85,13 @@ export function calculateDelay(loc: DelayInput): { type: string; } { const pairs = [ - // Departure check (Board: atd/etd vs std | Journey: atd vs ptd/wtd) - { actual: loc.atd || loc.etd, sched: loc.std || loc.ptd || loc.wtd }, - // Arrival check (Board: ata/eta vs sta | Journey: ata vs pta/wta) - { actual: loc.ata || loc.eta, sched: loc.sta || loc.pta || loc.wta }, - // Passing check - { actual: loc.atp || loc.etp, sched: loc.wtp } - ]; + // Departure check (Board: atd/etd vs std | Journey: atd vs ptd/wtd) + { actual: loc.atd || loc.etd, sched: loc.std || loc.ptd || loc.wtd }, + // Arrival check (Board: ata/eta vs sta | Journey: ata vs pta/wta) + { actual: loc.ata || loc.eta, sched: loc.sta || loc.pta || loc.wta }, + // Passing check + { actual: loc.atp || loc.etp, sched: loc.wtp } + ]; const match = pairs.find((p) => p.actual && p.sched); @@ -109,20 +115,20 @@ export function calculateDelay(loc: DelayInput): { * @param act Actual Time (string, Date) */ export function delayClassFromTimePair(sched: any, act: any): string { - if (!sched || !act) return ''; + if (!sched || !act) return ''; - const s = new Date(sched).getTime(); - const a = new Date(act).getTime(); + const s = new Date(sched).getTime(); + const a = new Date(act).getTime(); - if (isNaN(s) || isNaN(a)) return ''; + if (isNaN(s) || isNaN(a)) return ''; - const diff = a - s; - const oneMinute = 60000; + const diff = a - s; + const oneMinute = 60000; - // on-time if within one minute - if (Math.abs(diff) < oneMinute) { - return 'delay-rt'; - } + // on-time if within one minute + if (Math.abs(diff) < oneMinute) { + return 'delay-rt'; + } - return diff > 0 ? 'delay-late' : 'delay-early'; + return diff > 0 ? 'delay-late' : 'delay-early'; } diff --git a/src/routes/board/+page.svelte b/src/routes/board/+page.svelte index bac520c..5d28f73 100644 --- a/src/routes/board/+page.svelte +++ b/src/routes/board/+page.svelte @@ -44,9 +44,9 @@ {/if} {#if data.boardData.data.s?.length} -
- -
+
+ +
{/if} diff --git a/static/type/inconsolata/latin-wdth-normal.woff2 b/static/type/inconsolata/latin-wdth-normal.woff2 new file mode 100644 index 0000000..70e429c Binary files /dev/null and b/static/type/inconsolata/latin-wdth-normal.woff2 differ