commit 73817e3f8294836fed3eeb84c1ff63ad5e36f235
parent 2743c5b459feb0c0acd59d878d003993fb567723
Author: Katja (ctucx) <git@ctu.cx>
Date: Sun, 20 Apr 2025 12:54:17 +0200
parent 2743c5b459feb0c0acd59d878d003993fb567723
Author: Katja (ctucx) <git@ctu.cx>
Date: Sun, 20 Apr 2025 12:54:17 +0200
improve templates
9 files changed, 413 insertions(+), 321 deletions(-)
M
|
95
++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
M
|
121
++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
M
|
168
++++++++++++++++++++++++++++++++++++++++++-------------------------------------
diff --git a/src/baseView.js b/src/baseView.js @@ -1,4 +1,6 @@ import { LitElement, html, nothing } from 'lit'; +import { choose } from 'lit/directives/choose.js'; + import { t } from './translate.js'; import { baseStyles, flexboxStyles, overlaysStyles, buttonInputStyles, iconStyles } from './styles.js'; @@ -67,40 +69,30 @@ export class BaseView extends LitElement { let overlayContent; if (this.overlayState.visible) { - switch (this.overlayState.type) { - case 'loader': - overlayContent = html`<div class="spinner"></div>`; - break; - case 'dialog': - overlayContent = html` - <div class="modal dialog"> - <div class="header flex-row"> - <h4>${t(this.overlayState.title)}</h4> - <div class="icon-close" title="${t('close')}" @click=${this.hideOverlay}></div> - </div> - <div class="body">${this.overlayState.content}</div> - </div> - `; - break; - case 'select': - overlayContent = html` - <div class="modal select flex-column"> - ${this.overlayState.content.map(item => html`<a class="button color" @click=${item.action}>${t(item.label)}</a>`)} - <a class="button color" @click=${this.hideOverlay}>${t('close')}</a> - </div> - `; - break; - case 'alert': - overlayContent = html` - <div class="modal alert" style="overflow:auto"> - ${this.overlayState.content}<br><button class="color" style="float:right" @click=${this.hideOverlay}>OK</button> + overlayContent = choose(this.overlayState.type, [ + [ 'plain', () => this.overlayState.content ], + [ 'loader', () => html`<div class="spinner"></div>` ], + [ 'dialog', () => html` + <div class="modal dialog"> + <div class="header flex-row"> + <h4>${t(this.overlayState.title)}</h4> + <div class="icon-close" title="${t('close')}" @click=${this.hideOverlay}></div> </div> - `; - break; - default: - overlayContent = this.overlayState.content; - break; - } + <div class="body">${this.overlayState.content}</div> + </div> + ` ], + [ 'select', () => html` + <div class="modal select flex-column"> + ${this.overlayState.content.map(item => html`<a class="button color" @click=${item.action}>${t(item.label)}</a>`)} + <a class="button color" @click=${this.hideOverlay}>${t('close')}</a> + </div> + ` ], + [ 'alert', () => html` + <div class="modal alert" style="overflow:auto"> + ${this.overlayState.content}<br><button class="color" style="float:right" @click=${this.hideOverlay}>OK</button> + </div> + ` ], + ]); } return [
diff --git a/src/departuresView.js b/src/departuresView.js @@ -1,4 +1,6 @@ import { html, nothing } from 'lit'; +import { classMap } from 'lit/directives/class-map.js'; +import { when } from 'lit/directives/when.js'; import { BaseView } from './baseView.js'; import { sleep, queryBackgroundColor, setThemeColor } from './helpers.js'; @@ -84,48 +86,63 @@ class DeparturesView extends BaseView { renderView = () => [ html` - <div class="header-container"> - <header> - <a class="icon-back ${history.length !== 1 ? '': 'invisible'}" title="${t('back')}" @click=${() => history.back()}></a> - <div class="container"> - ${this.viewState !== null ? html` - <a class="icon-bahnexpert" href="https://bahn.expert/${encodeURIComponent(this.viewState.name)}"></a> - ` : nothing} - <h3>Departures from ${this.viewState !== null ? this.viewState.name : '...'}</h3> - </div> - <a class="icon-reload ${!this.isUpdating ? '' : 'spinning'} ${!this.isOffline ? '' : 'invisible'}" title="${t("refresh")}" @click=${this.updateViewState}></a> - </header> - </div> + <div class="header-container"> + <header> + <a class="icon-back ${classMap({ invisible: history.length === 1 })}" title=${t('back')} @click=${() => history.back()}></a> + <div class="container"> + ${when( + this.viewState, + () => html`<a class="icon-bahnexpert" href="https://bahn.expert/${encodeURIComponent(this.viewState.name)}"></a>` + )} + <h3>Departures from ${when(!this.viewState, () => '...', () => this.viewState.name)}</h3> + </div> + <a class="icon-reload ${classMap({ spinning: this.isUpdating, invisible: this.isOffline })}" title=${t("refresh")} @click=${this.updateViewState}></a> + </header> + </div> `, - this.viewState !== null ? html` - <div class="container"> - <div class="table" style="grid-template-columns: 1fr .5fr 3.5fr 1fr"> - <div class="row head"> - <span>Time</span> - <span></span> - <span></span> - <span>${t('platform')}</span> + when(!this.viewState, + () => when( + !this.isOffline, + () => html`<div class="spinner"></div>`, + () => html`<div class="offline"></div>` + ), + () => html` + <div class="container"> + <div class="table" style="grid-template-columns: 1fr .5fr 3.5fr 1fr"> + <div class="row head"> + <span>Time</span> + <span></span> + <span></span> + <span>${t('platform')}</span> + </div> + ${(this.viewState.departures || []).map(departure => html` + <a class="row" href="#/t/${this.profile}/${departure.tripId}"> + <span class=${classMap({ cancelled: departure.cancelled })}> + ${timeTemplate(departure)} + </span> + <span class="direction ${classMap({ cancelled: departure.cancelled })}"> + ${departure.line.name} + </span> + <span class="direction ${classMap({ cancelled: departure.cancelled })}"> + ${when( + departure.direction, + () => html` → ${departure.direction}` + )} + </span> + ${when( + !departure.cancelled, + () => html`<span>${platformTemplate(departure)}</span>`, + () => html`<span class="cancelled-text">${t('cancelled')}</span>` + )} + </a> + `)} + </tbody> + </div> </div> - ${(this.viewState.departures || []).map(departure => html` - <a class="row" href="#/t/${this.profile}/${departure.tripId}"> - <span class="${departure.cancelled ? 'cancelled' : nothing}">${timeTemplate(departure)}</span> - <span class="direction ${departure.cancelled ? 'cancelled' : ''}">${departure.line.name}</span> - <span class="direction ${departure.cancelled ? 'cancelled' : ''}">${departure.direction ? html` → ${departure.direction}` : nothing}</span> - ${departure.cancelled ? html` - <span class="cancelled-text">${t('cancelled')}</span> - ` : html` - <span>${platformTemplate(departure)}</span> - `} - </a> - `)} - </tbody> - </div> - </div> - <footer-component></footer-component> - ` : !this.isOffline ? - html`<div class="spinner"></div>` - : html`<div class="offline"></div>` + <footer-component></footer-component> + ` + ) ]; }
diff --git a/src/journeyView.js b/src/journeyView.js @@ -1,4 +1,6 @@ import { html, css, nothing } from 'lit'; +import { classMap } from 'lit/directives/class-map.js'; +import { when } from 'lit/directives/when.js'; import { BaseView } from './baseView.js'; import { createEvents } from 'ics'; @@ -63,45 +65,49 @@ class JourneyView extends BaseView { html` <div class="header-container"> <header> - <a id="back" class="icon-back ${history.length !== 1 ? '': 'invisible'}" title="${t('back')}" @click=${() => history.back()}></a> + <a class="icon-back ${classMap({ invisible: history.length === 1 })}" title=${t('back')} @click=${() => history.back()}></a> <div class="container"> - <a class="icon-reload ${this.isUpdating ? 'spinning' : ''} ${!this.isOffline ? '' : 'invisible'}" title="${t("refresh")}" @click=${this.refreshJourney}></a> - ${this.viewState !== null ? html` - <h3> - <span>${formatPoint(this.viewState.legs[0].origin)}</span> - <span>${formatPoint(this.viewState.legs[this.viewState.legs.length - 1].destination)}</span> - </h3> - <p> - <span>${t('duration')}: ${formatDuration(this.viewState.duration)}</span> - <span>${t('changes')}: ${this.viewState.changes-1}</span> - <span>${t('date')}: ${this.viewState.legs[0].plannedDeparture.formatDate()}</span> - ${this.settingsState.showPrices && this.viewState.price ? - html`<span> ${t('price')}: ${formatPrice(this.viewState.price)}</span>` - : nothing} - </p> - ` : html` - <h3> - <span>...</span> - <span>...</span> - </h3> - <p> - <span>${t('duration')}: ...</span> - <span>${t('changes')}: ...</span> - <span>${t('date')}: ...</span> - </p> - `} + <a class="icon-reload ${classMap({ spinning: this.isUpdating, invisible: this.isOffline })}" title=${t("refresh")} @click=${this.refreshJourney}></a> + ${when(!this.viewState, + () => html` + <h3> + <span>...</span> + <span>...</span> + </h3> + <p> + <span>${t('duration')}: ...</span> + <span>${t('changes')}: ...</span> + <span>${t('date')}: ...</span> + </p> + `, + () => html` + <h3> + <span>${formatPoint(this.viewState.legs[0].origin)}</span> + <span>${formatPoint(this.viewState.legs[this.viewState.legs.length - 1].destination)}</span> + </h3> + <p> + <span>${t('duration')}: ${formatDuration(this.viewState.duration)}</span> + <span>${t('changes')}: ${this.viewState.changes-1}</span> + <span>${t('date')}: ${this.viewState.legs[0].plannedDeparture.formatDate()}</span> + ${when( + this.settingsState.showPrices && this.viewState.price, + () => html`<span> ${t('price')}: ${formatPrice(this.viewState.price)}</span>` + )} + </p> + ` + )} </div> - <a class="icon-dots" title="${t("more")}" @click=${this.moreModal}></a> + <a class="icon-dots" title=${t("more")} @click=${this.moreModal}></a> </header> </div> - `, this.viewState !== null ? html` - <div class="container"> - ${this.viewState.legs.map(leg => this.legTemplate(leg))} - </div> - <footer-component></footer-component> - ` : html` - <div class="spinner"></div> - `, + `, + when(!this.viewState, + () => html`<div class="spinner"></div>`, + () => html` + <div class="container">${this.viewState.legs.map(leg => this.legTemplate(leg))}</div> + <footer-component></footer-component> + ` + ) ]; legTemplate = leg => { @@ -116,16 +122,29 @@ class JourneyView extends BaseView { <div class="card"> <div class="train-info flex-center nowrap"> <a href="#/t/${this.profile}/${leg.tripId}">${formatLineDisplayName(leg.line)}${!leg.direction ? nothing : html` → ${leg.direction}`}</a> - ${leg.remarks.length === 0 ? nothing : html` - <a class="${leg.remarksIcon}" @click=${() => remarksModal(this, leg.remarks)}></a> - `} + ${when(leg.remarks.length !== 0, () => html`<a class="${leg.remarksIcon}" @click=${() => remarksModal(this, leg.remarks)}></a>`)} </div> <div class="train-info flex-center"> - ${!leg.cancelled ? nothing : html`<span class="cancelled-text">${t('cancelled')}</span>`} - ${formatLineAdditionalName(leg.line) === null ? nothing : html`<span>Trip: ${formatLineAdditionalName(leg.line)}</span>`} - ${!leg.line.trainType ? nothing : html`<span>${t('trainType')}: ${leg.line.trainType}</span>`} - ${(!leg.arrival && !leg.departure) ? nothing : html`<span>${t('duration')}: ${formatDuration(leg.arrival - leg.departure)}</span>`} - ${!leg.loadFactor ? nothing : html`<span>${t(`load-${leg.loadFactor}`)}</span>`} + ${when( + leg.cancelled, + () => html`<span class="cancelled-text">${t('cancelled')}</span>` + )} + ${when( + formatLineAdditionalName(leg.line) !== null, + () => html`<span>Trip: ${formatLineAdditionalName(leg.line)}</span>` + )} + ${when( + leg.line.trainType, + () => html`<span>${t('trainType')}: ${leg.line.trainType}</span>` + )} + ${when( + leg.arrival && leg.departure, + () => html`<span>${t('duration')}: ${formatDuration(leg.arrival - leg.departure)}</span>` + )} + ${when( + leg.loadFactor, + () => html`<span>${t(`load-${leg.loadFactor}`)}</span>` + )} </div> <div class="table"> <div class="row head"> @@ -136,10 +155,18 @@ class JourneyView extends BaseView { </div> ${(leg.stopovers || []).map(stop => html` <div class="row"> - <span class="${!stop.cancelled ? nothing : 'cancelled'}">${timeTemplate(stop, 'arrival')}</span> - <span class="${!stop.cancelled ? nothing : 'cancelled'}">${timeTemplate(stop, 'departure')}</span> - <span class="station ${!stop.cancelled ? '' : 'cancelled'}">${stopTemplate(this.profile, stop.stop)}</span> - <span class="${!stop.cancelled ? nothing : 'cancelled'}">${platformTemplate(stop)}</span> + <span class=${classMap({ cancelled: stop.cancelled })}> + ${timeTemplate(stop, 'arrival')} + </span> + <span class=${classMap({ cancelled: stop.cancelled })}> + ${timeTemplate(stop, 'departure')} + </span> + <span class="station ${classMap({ cancelled: stop.cancelled })}"> + ${stopTemplate(this.profile, stop.stop)} + </span> + <span class=${classMap({ cancelled: stop.cancelled })}> + ${platformTemplate(stop)} + </span> </div> `)} </div>
diff --git a/src/journeysView.js b/src/journeysView.js @@ -1,4 +1,8 @@ import { html, nothing } from 'lit'; +import { classMap } from 'lit/directives/class-map.js'; +import { choose } from 'lit/directives/choose.js'; +import { when } from 'lit/directives/when.js'; +import { join } from 'lit/directives/join.js'; import { sleep, queryBackgroundColor, setThemeColor } from './helpers.js'; import { getJourneys, getMoreJourneys, refreshJourneys, getFromPoint, getToPoint } from './app_functions.js'; @@ -87,60 +91,66 @@ export class JourneysView extends JourneysCanvas { renderView = () => [ html` - <div class="header-container"> - <header> - <a id="back" class="icon-back" title="${t('back')}" href="#/"></a> - <div class="container flex-row"> - <div> - <h3>${t('from')}: ${this.viewState !== null ? formatPoint(getFromPoint(this.viewState.journeys)) : '...'}</h3> - <h3>${t('to')}: ${this.viewState !== null ? formatPoint(getToPoint(this.viewState.journeys)) : '...'}</h3> + <div class="header-container"> + <header> + <a class="icon-back" title=${t('back')} href="#/"></a> + <div class="container flex-row"> + <div> + <h3>${t('from')}: ${!this.viewState ? '...' : formatPoint(getFromPoint(this.viewState.journeys))}</h3> + <h3>${t('to')}: ${!this.viewState ? '...' : formatPoint(getToPoint(this.viewState.journeys))}</h3> + </div> + <div class="mode-changers flex-row"> + <a href="#/${this.slug}/table" class="${this.settingsState.journeysViewMode === 'table' ? 'active' : nothing}"> + <div class="icon-table"></div> + <span>${t('tableView')}</span> + </a> + <a href="#/${this.slug}/canvas" class="${this.settingsState.journeysViewMode === 'canvas' ? 'active' : nothing}"> + <div class="icon-canvas"></div> + <span>${t('canvasView')}</span> + </a> + </div> </div> - <div class="mode-changers flex-row"> - <a href="#/${this.slug}/table" class="${this.settingsState.journeysViewMode === 'table' ? 'active' : ''}"> - <div class="icon-table"></div> - <span>${t('tableView')}</span> - </a> - <a href="#/${this.slug}/canvas" class="${this.settingsState.journeysViewMode === 'canvas' ? 'active' : ''}"> - <div class="icon-canvas"></div> - <span>${t('canvasView')}</span> - </a> - </div> - </div> - <a class="icon-reload ${this.isUpdating ? 'spinning' : ''} ${!this.isOffline ? '' : 'invisible'}" title="${t("reload")}" @click=${this.refreshJourneys}></a> - </header> - </div> + <a class="icon-reload ${classMap({ spinning: this.isUpdating, invisible: this.isOffline })}" title=${t("refresh")} @click=${this.refreshJourneys}></a> + </header> + </div> `, - - this.viewState !== null ? [ - this.settingsState.journeysViewMode === 'canvas' ? this.getCanvas() : nothing, - this.settingsState.journeysViewMode === 'table' ? html` - <div class="container"> - ${!this.viewState.earlierRef ? nothing : html` - <a class="arrowButton icon-arrow flipped flex-center" title="${t('earlier')}" @click=${() => this.moreJourneys('earlier')}></a> - `} - - <div class="table" style="grid-template-columns: repeat(${this.settingsState.showPrices && this.viewState.profile === 'db' ? '6' : '5'}, 1fr) .3fr"> - <div class="row head"> - <span>${t('departure')}</span> - <span>${t('arrival')}</span> - <span>${t('duration')}</span> - <span>${t('changes')}</span> - <span>${t('products')}</span> - ${this.settingsState.showPrices && this.viewState.profile === 'db' ? html` - <span>${t('price')}</spanv> - ` : nothing} - <span></span> + when( + !this.viewState, + () => html`<div class="spinner"></div>`, + () => choose(this.settingsState.journeysViewMode, [ + [ 'canvas', () => this.getCanvas() ], + [ 'table', () => html` + <div class="container"> + ${when( + this.viewState.earlierRef, + () => html`<a class="arrowButton icon-arrow flipped flex-center" title="${t('earlier')}" @click=${() => this.moreJourneys('earlier')}></a>` + )} + + <div class="table" style="grid-template-columns: repeat(${this.settingsState.showPrices && this.viewState.profile === 'db' ? '6' : '5'}, 1fr) .3fr"> + <div class="row head"> + <span>${t('departure')}</span> + <span>${t('arrival')}</span> + <span>${t('duration')}</span> + <span>${t('changes')}</span> + <span>${t('products')}</span> + ${when( + this.settingsState.showPrices && this.viewState.profile === 'db', + () => html`<span>${t('price')}</span>` + )} + <span></span> + </div> + ${this.viewState.journeys.map(journey => this.journeyTemplate(journey))} + </div> + + ${when( + this.viewState.laterRef, + () => html`<a class="arrowButton icon-arrow flex-center" title="${t('later')}" @click=${() => this.moreJourneys('later')}></a>` + )} </div> - ${this.viewState.journeys.map(journey => this.journeyTemplate(journey))} - </div> - - ${!this.viewState.laterRef ? nothing : html` - <a class="arrowButton icon-arrow flex-center" title="${t('later')}" @click=${() => this.moreJourneys('later')}></a> - `} - </div> - <footer-component></footer-component> - ` : nothing - ] : html`<div class="spinner"></div>` + <footer-component></footer-component> + ` ], + ]) + ) ]; journeyTemplate = journey => { @@ -149,18 +159,22 @@ export class JourneysView extends JourneysCanvas { return html` <a class="row" href="#/j/${this.viewState.profile}/${journey.refreshToken}"> - <span class="${!journey.cancelled ? nothing : 'cancelled'}">${timeTemplate(firstLeg, 'departure')}</span> - ${journey.cancelled ? html` - <span class="cancelled-text">${t('cancelled')}</span> - ` : html` - <span>${timeTemplate(lastLeg, 'arrival')}</span> - `} - <span class="${!journey.cancelled ? nothing : 'cancelled'}" title="${journey.changesDuration > 0 ? 'including '+formatDuration(journey.changesDuration)+' transfer durations' : ''}">${formatDuration(journey.duration)}</span> + <span class=${!journey.cancelled ? nothing : 'cancelled'}>${timeTemplate(firstLeg, 'departure')}</span> + ${when( + !journey.cancelled, + () => html`<span>${timeTemplate(lastLeg, 'arrival')}</span>`, + () => html`<span class="cancelled-text">${t('cancelled')}</span>` + )} + <span class=${classMap({ cancelled: journey.cancelled })} title="${when( + journey.changesDuration, + () => 'including '+formatDuration(journey.changesDuration)+' transfer durations' + )}">${formatDuration(journey.duration)}</span> <span>${journey.changes-1}</span> - <span>${journey.products}</span> - ${this.settingsState.showPrices && this.viewState.profile === 'db' ? html` - <span>${formatPrice(journey.price)}</span> - ` : nothing} + <span>${join(journey.products, ', ')}</span> + ${when( + this.settingsState.showPrices && this.viewState.profile === 'db', + () => html`<span>${formatPrice(journey.price)}</span>` + )} <span><i class="icon-arrow" style="transform:rotate(-90deg)"></i></span> </a> `; @@ -179,34 +193,31 @@ export class JourneysView extends JourneysCanvas { viewState.journeys.forEach((journey, index, journeys) => { const firstLeg = journey.legs[0]; const lastLeg = journey.legs[journey.legs.length - 1]; - const products = {}; - journeys[index].duration = Number(lastLeg.arrival || lastLeg.plannedArrival) - Number(firstLeg.departure || firstLeg.plannedDeparture); journeys[index].changesDuration = 0; + journeys[index].duration = Number(lastLeg.arrival || lastLeg.plannedArrival) - Number(firstLeg.departure || firstLeg.plannedDeparture); journeys[index].cancelled = false; journeys[index].changes = 0; + journeys[index].products = []; + let previousLegArrival = null; journey.legs.forEach(leg => { if (leg.cancelled) journeys[index].cancelled = true; if (leg.walking || leg.transfer) return; + if (previousLegArrival) journeys[index].changesDuration += Number(leg.departure || leg.plannedDeparture) - previousLegArrival; + + previousLegArrival = Number(leg.arrival || leg.plannedArrival) journeys[index].changes++; if (leg.line) { const productName = leg.line.name.split(' ')[0]; - if (!products[productName]) products[productName] = []; - } - //if (leg.line && leg.line.trainTypeShort) products[leg.line.productName].push(leg.line.trainTypeShort); - }) - - journeys[index].products = Object.entries(products).map(([prod, types]) => { - if (types.length >= 2) { - prod += ' (' + types.join(', ') + ')'; - } else if (types.length) { - prod += ' ' + types[0]; + + if (!journeys[index].products.includes(productName)) { + journeys[index].products.push(productName); + } } - return prod; - }).join(', '); + }); }); this.viewState = viewState; @@ -218,8 +229,7 @@ export class JourneysView extends JourneysCanvas { } refreshJourneys = async () => { - if (this.isOffline !== false) return; - if (this.isUpdating !== false) return false; + if (this.isOffline || this.isUpdating) return; try { this.isUpdating = true; @@ -235,7 +245,7 @@ export class JourneysView extends JourneysCanvas { } moreJourneys = async mode => { - if (this.isOffline !== false) { + if (this.isOffline) { this.showAlertOverlay(t('offline')); return; }
diff --git a/src/languages.js b/src/languages.js @@ -58,7 +58,7 @@ export const languages = { 'tableView': 'Table', 'canvasView': 'Graphical', 'showDS100': 'Show DS100 (if available)', - 'showPrices ': 'Show prices', + 'showPrices': 'Show prices', 'price': 'Price', 'back': 'Back', 'refresh': 'Refresh data',
diff --git a/src/main.js b/src/main.js @@ -69,7 +69,7 @@ class Oeffisearch extends LitElement { customElements.define('oeffi-search', Oeffisearch); -(async () => { +window.addEventListener('load', async () => { await initSettingsState(); await initDataStorage(); await initHafasClient(settingsState.profile); @@ -80,14 +80,12 @@ customElements.define('oeffi-search', Oeffisearch); document.head.appendChild(style); document.body.innerHTML = '<oeffi-search></oeffi-search>'; -})(); -if (!isDevServer) { - window.addEventListener('load', () => { + if (!isDevServer) { navigator.serviceWorker.register('/sw.js').then(registration => { console.log('SW registered: ', registration); }).catch(registrationError => { console.log('SW registration failed: ', registrationError); }); - }); -} + } +});
diff --git a/src/searchView.js b/src/searchView.js @@ -1,4 +1,6 @@ import { LitElement, html, nothing } from 'lit'; +import { classMap } from 'lit/directives/class-map.js'; +import { when } from 'lit/directives/when.js'; import { BaseView } from './baseView.js'; import { db } from './dataStorage.js'; @@ -121,11 +123,11 @@ class SearchView extends BaseView { autocomplete="off" ?required=${name !== 'via'}> ${name !== 'from' ? nothing : html` - <div class="button icon-arrow ${this.settingsState.showVia ? 'flipped' : ''}" tabindex="0" title="${t('via')}" + <div class="button icon-arrow ${classMap({ flipped: this.settingsState.showVia })}" tabindex="0" title="${t('via')}" @click=${this.settingsState.toggleShowVia}></div> `} - ${name !== 'via' ? nothing : html`<div class="button invisible"></div>`} - ${name !== 'to' ? nothing : html`<div class="button icon-swap" tabindex="0" title="${t('swap')}" @click=${this.swapFromTo}></div>`} + ${name !== 'via' ? nothing : html`<div class="button icon-arrow invisible"></div>`} + ${name !== 'to' ? nothing : html`<div class="button icon-swap" tabindex="0" title=${t('swap')} @click=${this.swapFromTo}></div>`} </div> <div class="suggestions ${this.location[name].suggestionsVisible ? '' : 'hidden'}"> @@ -182,28 +184,31 @@ class SearchView extends BaseView { </div> ${this.history.length !== 0 ? html` - <div id="historyButton" class="arrowButton icon-arrow ${!this.showHistory ? '' : 'flipped'}" title="History" @click=${this.toggleHistory}></div> + <div id="historyButton" class="arrowButton icon-arrow ${classMap({ flipped: this.showHistory })}" title=${t('history')} @click=${this.toggleHistory}></div> ` : nothing} </form> - ${!this.showHistory ? nothing : html` - <div id="history" class="history center"> - ${this.history.map((element, index) => html` - <div class="flex-row" @click="${() => this.journeysHistoryAction(index)}"> - <div class="from"> - <small>${t('from')}:</small> - ${formatPoint(element.fromPoint)} - ${element.viaPoint ? html`<div class="via">${t('via')} ${formatPoint(element.viaPoint)}</div>` : nothing} - </div> - <div class="icon-arrow1"></div> - <div class="to"> - <small>${t('to')}:</small> - ${formatPoint(element.toPoint)} + ${when( + this.showHistory, + () => html` + <div id="history" class="history center"> + ${this.history.map((element, index) => html` + <div class="flex-row" @click="${() => this.journeysHistoryAction(index)}"> + <div class="from"> + <small>${t('from')}:</small> + ${formatPoint(element.fromPoint)} + ${element.viaPoint ? html`<div class="via">${t('via')} ${formatPoint(element.viaPoint)}</div>` : nothing} + </div> + <div class="icon-arrow1"></div> + <div class="to"> + <small>${t('to')}:</small> + ${formatPoint(element.toPoint)} + </div> + </div> + `)} </div> - </div> - `)} - </div> - `} + ` + )} <footer-component></footer-component> </div> `; @@ -345,18 +350,17 @@ class SearchView extends BaseView { from: null, to: null, via: null, - bike: this.settingsState.bikeFriendly, - transferTime: this.settingsState.transferTime, - results: 6, + results: 6, products: {}, + bike: this.settingsState.bikeFriendly, + transferTime: this.settingsState.transferTime, }; - await Promise.all([ 'from', 'via','to' ].map(async mode => { + await Promise.all([ 'from', 'via', 'to' ].map(async mode => { if (this.location[mode].value !== '') { - if (mode === 'via' && this.settingsState.showVia === false) return false; - if (this.location[mode].suggestionSelected !== null) { - params[mode] = this.location[mode].suggestionSelected; - } else { + if (mode === 'via' && !this.settingsState.showVia) return false; + + if (!this.location[mode].suggestionSelected) { if (this.location[mode].suggestions.length !== 0) { params[mode] = this.location[mode].suggestions[0] } else { @@ -364,11 +368,13 @@ class SearchView extends BaseView { if (!data[0]) return false; params[mode] = data[0]; } + } else { + params[mode] = this.location[mode].suggestionSelected; } } })); - if (params.from === null || params.to === null) return false; + if (!params.from || !params.to) return false; if (formatPoint(params.from) === formatPoint(params.to) && params.via === null) { this.showAlertOverlay('From and To are the same place.'); @@ -437,7 +443,7 @@ class SearchView extends BaseView { keydownHandler = event => { const name = event.target.name; - if (this.location[name].suggestions.length == 0) return true; + if (this.location[name].suggestions.length === 0) return true; if (event.key === 'Enter') { event.preventDefault(); @@ -476,6 +482,7 @@ class SearchView extends BaseView { } } } + this.requestUpdate(); } @@ -498,6 +505,7 @@ class SearchView extends BaseView { this.location[name].suggestionSelected = null; this.location[name].suggestion = 0; this.location[name].suggestions = suggestions; + this.requestUpdate(); }; }
diff --git a/src/settingsView.js b/src/settingsView.js @@ -1,4 +1,6 @@ import { html, nothing } from 'lit'; +import { when } from 'lit/directives/when.js'; +import { classMap } from 'lit/directives/class-map.js'; import { BaseView } from './baseView.js'; import { sleep, queryBackgroundColor, setThemeColor } from './helpers.js'; @@ -43,23 +45,25 @@ class SettingsView extends BaseView { } - render () { - return html` + render = () => [ + html` <div class="flex-row"> <label for="language">${t('language')}:</label> <select id="language" @change=${this.changeHandler}> ${getLanguages().map(lang => html`<option value="${lang}" ?selected=${this.viewState.language === lang}>${t(lang)}</option>`)} </select> </div> - + `, + html` <div class="flex-row"> <label for="profile">${t('datasource')}:</label> <select id="profile" @change=${this.changeHandler}> ${Object.keys(profiles).map(profile => html`<option value="${profile}" ?selected=${this.viewState.profile === profile}>${profiles[profile].name}</option>`)} </select> </div> - - <div class="flex-row ${this.viewState.profile !== 'db' ? '' : 'hidden'}"> + `, + when(this.viewState.profile !== 'db', () => html` + <div class="flex-row"> <label for="accessibility">${t('accessibility')}:</label> <select id="accessibility" @change=${this.changeHandler}> <option value="none" ?selected=${this.viewState.accessibility === 'none'}>${t('accessibilityNone')}</option> @@ -67,8 +71,9 @@ class SettingsView extends BaseView { <option value="complete" ?selected=${this.viewState.accessibility === 'complete'}>${t('accessibilityComplete')}</option> </select> </div> - - <div class="flex-row ${this.viewState.profile !== 'db' ? '' : 'hidden'}"> + `), + when(this.viewState.profile !== 'db', () => html` + <div class="flex-row"> <label for="walkingSpeed">${t('walkingSpeed')}:</label> <select id="walkingSpeed" @change=${this.changeHandler}> <option value="slow" ?selected=${this.viewState.walkingSpeed === 'slow'}>${t('walkingSpeedSlow')}</option> @@ -76,8 +81,9 @@ class SettingsView extends BaseView { <option value="fast" ?selected=${this.viewState.walkingSpeed === 'fast'}>${t('walkingSpeedFast')}</option> </select> </div> - - <div class="flex-row ${this.viewState.profile !== 'db' ? 'hidden' : ''}"> + `), + when(this.viewState.profile === 'db', () => html` + <div class="flex-row"> <label for="ageGroup">${t('ageGroup')}:</label> <select id="ageGroup" @change=${this.changeHandler}> <option value="K" ?selected=${this.viewState.ageGroup === 'K'}>${t('ageGroupChild')} (7-14)</option> @@ -86,8 +92,9 @@ class SettingsView extends BaseView { <option value="S" ?selected=${this.viewState.ageGroup === 'S'}>${t('ageGroupSenior')} (65+)</option> </select> </div> - - <div class="flex-row ${this.viewState.profile !== 'db' ? 'hidden' : ''}"> + `), + when(this.viewState.profile === 'db', () => html` + <div class="flex-row"> <label for="loyaltyCard">${t('loyaltyCard')}:</label> <select id="loyaltyCard" @change=${this.changeHandler}> <option value="NONE" ?selected=${this.viewState.loyaltyCard === 'NONE'}>${t('loyaltyCardNone')}</option> @@ -99,26 +106,29 @@ class SettingsView extends BaseView { <option value="BAHNCARD-100-1" ?selected=${this.viewState.loyaltyCard === 'BAHNCARD-100-1'}>BahnCard 100, 1. ${t("class")}</option> </select> </div> - + `), + html` <div class="flex-row"> <label for="transferTime">${t('minTransferTime')}:</label> <input type="number" id="transferTime" min="0" max="99" @change=${this.changeHandler} .value="${this.viewState.transferTime}"> </div> - + `, + html` <div class="flex-column"> <span>${t('options')}:</span><br> <label><input type="checkbox" id="showDS100" @change=${this.changeHandler} ?checked=${this.viewState.showDS100}> ${t('showDS100')}<br></label> <label><input type="checkbox" id="combineDateTime" @change=${this.changeHandler} ?checked=${this.viewState.combineDateTime}> ${t('combineDateTime')}<br></label> - <label class="${this.viewState.profile !== 'db' ? 'hidden' : nothing}"> + <label class="${classMap({ hidden: this.viewState.profile !== 'db' })}"> <input type="checkbox" id="showPrices" @change=${this.changeHandler} ?checked=${this.viewState.showPrices}> ${t('showPrices')} </label> </div> - + `, + html` <div class="flex-row"> <div class="button color icon-trashcan" title="${t('clearstorage')}" @click=${this.clearStorage}></div> </div> - `; - } + `, + ]; changeHandler = async event => { const id = event.target.id;
diff --git a/src/tripView.js b/src/tripView.js @@ -1,5 +1,7 @@ import { html, nothing } from 'lit'; import { ifDefined } from 'lit/directives/if-defined.js'; +import { classMap } from 'lit/directives/class-map.js'; +import { when } from 'lit/directives/when.js'; import { BaseView } from './baseView.js'; import { sleep, queryBackgroundColor, setThemeColor } from './helpers.js'; @@ -61,9 +63,8 @@ class TripView extends BaseView { this.isUpdating = true; try { - const client = await getHafasClient(this.profile); - - let viewState = await client.trip(this.refreshToken, {stopovers: true}); + const client = await getHafasClient(this.profile); + let viewState = await client.trip(this.refreshToken, {stopovers: true}); processLeg(viewState.trip); @@ -104,79 +105,108 @@ class TripView extends BaseView { html` <div class="header-container"> <header> - <a id="back" class="icon-back ${history.length !== 1 ? '': 'invisible'}" title="${t('back')}" @click=${() => history.back()}></a> + <a class="icon-back ${classMap({ invisible: history.length === 1 })}" title=${t('back')} @click=${() => history.back()}></a> <div class="container"> - ${!this.viewState ? html` - <h3>Trip of ...</h3> - ` : html` - <h3>Trip of ${formatLineDisplayName(this.viewState.trip.line)} to ${this.viewState.trip.direction}</h3> - `} + ${when( + !this.viewState, + () => html`<h3>Trip of ...</h3>`, + () => html`<h3>Trip of ${formatLineDisplayName(this.viewState.trip.line)} to ${this.viewState.trip.direction}</h3>` + )} </div> - <a class="icon-reload ${!this.isUpdating ? '' : 'spinning'} ${!this.isOffline ? '' : 'invisible'}" title="${t("refresh")}" @click=${this.updateViewState}></a> + <a class="icon-reload ${classMap({ spinning: this.isUpdating, invisible: this.isOffline })}" title=${t("refresh")} @click=${this.updateViewState}></a> </header> </div> `, - this.viewState !== null ? html` - <div class="container"> - <div class="card"> - <div class="train-info flex-center nowrap"> - <a href="${ifDefined(this.viewState.trip.bahnExpertUrl)}"> - ${formatLineDisplayName(this.viewState.trip.line)} ${!this.viewState.trip.direction ? '' : html` → ${this.viewState.trip.direction}`} - </a> - ${this.viewState.trip.remarks.length === 0 ? nothing : html` - <a class="${this.viewState.trip.remarksIcon}" @click=${() => remarksModal(this, this.viewState.trip.remarks)}></a> - `} - </div> - - <div class="train-info flex-center"> - ${!this.viewState.trip.cancelled ? nothing : html` - <span class="cancelled-text">${t('cancelled')}</span> - `} - - ${formatLineAdditionalName(this.viewState.trip.line) !== "" ? nothing : html` - <span>Trip: ${formatLineAdditionalName(this.viewState.trip.line)}</span> - `} - - ${!this.viewState.trip.line.trainType ? nothing : html` - <span>Train type: ${this.viewState.trip.line.trainType}</span> - `} - - ${this.viewState.trip.cancelled ? nothing : html` - <span> - ${t('duration')}: - ${formatDuration(this.viewState.trip.arrival - (this.viewState.trip.departure ? this.viewState.trip.departure : this.viewState.trip.plannedDeparture))} - ${this.viewState.trip.departure ? '' : ('(' + t('planned') + ')')} - </span> - `} - - ${!this.viewState.trip.loadFactor ? nothing : html` - <span>${t("load-"+this.viewState.trip.loadFactor)}</span> - `} - </div> - - <div class="table"> - <div class="row head"> - <span>${t('arrival')}</span> - <span>${t('departure')}</span> - <span class="station">${t('station')}</span> - <span>${t('platform')}</span> - </div> - ${(this.viewState.trip.stopovers || []).map(stop => html` - <div class="row"> - <span class="${!stop.cancelled ? nothing : 'cancelled'}">${timeTemplate(stop, 'arrival')}</span> - <span class="${!stop.cancelled ? nothing : 'cancelled'}">${timeTemplate(stop, 'departure')}</span> - <span class="station ${!stop.cancelled ? '' : 'cancelled'}">${stopTemplate(this.profile, stop.stop)}</span> - <span class="${!stop.cancelled ? nothing : 'cancelled'}">${platformTemplate(stop)}</span> + when( + !this.viewState, + () => when( + !this.isOffline, + () => html`<div class="spinner"></div>`, + () => html`<div class="offline"></div>` + ), + () => html` + <div class="container"> + <div class="card"> + <div class="train-info flex-center nowrap"> + <a href=${ifDefined(this.viewState.trip.bahnExpertUrl)}> + ${formatLineDisplayName(this.viewState.trip.line)} + ${when( + this.viewState.trip.direction, + () => html` → ${this.viewState.trip.direction}` + )} + </a> + + ${when( + this.viewState.trip.remarks.length !== 0, + () => html`<a class="${this.viewState.trip.remarksIcon}" @click=${() => remarksModal(this, this.viewState.trip.remarks)}></a>` + )} + </div> + + <div class="train-info flex-center"> + ${when( + this.viewState.trip.cancelled, + () => html`<span class="cancelled-text">${t('cancelled')}</span>` + )} + + ${when( + formatLineAdditionalName(this.viewState.trip.line), + () => html`<span>Trip: ${formatLineAdditionalName(this.viewState.trip.line)}</span>` + )} + + ${when( + this.viewState.trip.line.trainType, + () => html`<span>Train type: ${this.viewState.trip.line.trainType}</span>` + )} + + ${when( + !this.viewState.trip.cancelled, + () => html` + <span> + ${t('duration')}: + ${formatDuration( + (this.viewState.trip.arrival ? this.viewState.trip.arrival : this.viewState.trip.plannedArrival) - + (this.viewState.trip.departure ? this.viewState.trip.departure : this.viewState.trip.plannedDeparture) + )} + </span> + ` + )} + + ${when( + this.viewState.trip.loadFactor, + () => html`<span>${t("load-"+this.viewState.trip.loadFactor)}</span>` + )} + </div> + + <div class="table"> + <div class="row head"> + <span>${t('arrival')}</span> + <span>${t('departure')}</span> + <span class="station">${t('station')}</span> + <span>${t('platform')}</span> + </div> + ${(this.viewState.trip.stopovers || []).map(stop => html` + <div class="row"> + <span class=${classMap({ cancelled: stop.cancelled })}> + ${timeTemplate(stop, 'arrival')} + </span> + <span class=${classMap({ cancelled: stop.cancelled })}> + ${timeTemplate(stop, 'departure')} + </span> + <span class="station ${classMap({ cancelled: stop.cancelled })}"> + ${stopTemplate(this.profile, stop.stop)} + </span> + <span class=${classMap({ cancelled: stop.cancelled })}> + ${platformTemplate(stop)} + </span> + </div> + `)} + </div> </div> - `)} </div> - </div> - </div> - <footer-component></footer-component> - ` : !this.isOffline ? - html`<div class="spinner"></div>` - : html`<div class="offline"></div>` + <footer-component></footer-component> + ` + ) ]; }