Exctract trip to separate component

This commit is contained in:
Kacper Donat 2020-01-24 21:52:13 +01:00
parent c00e038fba
commit e49a70c71a
16 changed files with 125 additions and 38 deletions

View File

@ -41,6 +41,7 @@
"imagemin-webpack-plugin": "^2.3.0",
"mini-css-extract-plugin": "^0.4.2",
"portal-vue": "^2.1.7",
"vue-dragscroll": "^1.10.2",
"vue2-leaflet": "^1.0.2",
"vuex": "^3.0.1",
"vuex-class": "^0.3.1",

View File

@ -34,16 +34,7 @@
</div>
</div>
<fold :visible="showTrip">
<div v-if="trip != null" class="trip" :class="[ `trip--${departure.line.type}` ]">
<ol class="trip__stops">
<li v-for="stop in trip.schedule" class="trip-stop">
<div class="trip-stop__marker"/>
<div class="trip-stop__description">
<stop :stop="stop.stop"/>
</div>
</li>
</ol>
</div>
<trip :schedule="trip.schedule" v-if="trip" :class="[ `trip--${departure.line.type}` ]"/>
<div v-else class="text-center">
<fa icon="spinner-third" pulse></fa>
</div>

View File

@ -0,0 +1,13 @@
<div class="trip">
<ol class="trip__stops" v-dragscroll.x>
<li v-for="stop in schedule" class="trip__stop">
<div class="trip__marker"/>
<div class="trip__description">
<stop :stop="stop.stop"/>
</div>
<div class="trip__departure">
{{ stop.departure.format('HH:mm') }}
</div>
</li>
</ol>
</div>

View File

@ -0,0 +1,6 @@
.drag-scroll {
cursor : grab;
&:active {
cursor : grabbing;
}
}

View File

@ -4,7 +4,8 @@ $description-rotation: 60deg;
$description-width: 250px;
$trip-stop-marker-size: .9rem;
$trip-stop-line-width: .2rem;
$trip-stop-marker-spacing: .75rem;
$trip-line-width: .2rem;
.trip {
display: flex;
@ -18,45 +19,49 @@ $trip-stop-line-width: .2rem;
display: flex;
list-style: none;
overflow-x: auto;
@include no-scrollbars;
@extend .drag-scroll;
}
.trip-stop {
.trip__stop {
width: 2.5rem;
position: relative;
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
}
.trip-stop:first-child {
.trip-stop__marker::before {
.trip__stop:first-child {
.trip__marker::before {
content: none;
}
}
.trip-stop:last-child {
.trip-stop__marker::after {
.trip__stop:last-child {
.trip__marker::after {
content: none;
}
}
.trip-stop__marker {
.trip__marker {
width: $trip-stop-marker-size;
height: $trip-stop-marker-size;
border: $dark $trip-stop-line-width solid;
border: $dark $trip-line-width solid;
border-radius: 100%;
background: white;
margin: .75rem 0;
margin: $trip-stop-marker-spacing 0;
&::before, &::after {
content: "";
display: block;
height: $trip-stop-line-width;
height: $trip-line-width;
background: $dark;
width: 50%;
position: absolute;
top: 50%;
top: $trip-stop-marker-spacing + ($trip-stop-marker-size) / 2;
transform: translateY(-50%);
z-index: -1;
}
@ -72,7 +77,7 @@ $trip-stop-line-width: .2rem;
@each $type, $color in $line-types {
.trip--#{$type} {
.trip-stop__marker {
.trip__marker {
border-color: $color;
&::before, &::after {
@ -82,7 +87,7 @@ $trip-stop-line-width: .2rem;
}
}
.trip-stop__description {
.trip__description {
display: flex;
transform: rotate(-$description-rotation) translateX(.75rem);
transform-origin: 0 50%;
@ -95,3 +100,10 @@ $trip-stop-line-width: .2rem;
width: max-content;
}
}
.trip__departure {
font-size: $small-font-size;
font-weight: bold;
margin-bottom: .5rem;
}

View File

@ -42,6 +42,16 @@ $grid-gutter-width: $spacer * 2;
}
}
@mixin no-scrollbars {
scrollbar-width: none; /* Firefox */
-ms-overflow-style: none; /* Internet Explorer 10+ */
&::-webkit-scrollbar { /* WebKit */
width: 0;
height: 0;
}
}
@import "common";
@import "stop";
@import "departure";
@ -52,6 +62,7 @@ $grid-gutter-width: $spacer * 2;
@import "form";
@import "fabourites";
@import "trip";
@import "dragscroll";
body {
min-height: 100vh;

View File

@ -14,10 +14,12 @@ window['Popper'] = Popper;
import Vue from "vue";
import Vuex from 'vuex';
import PortalVue from 'portal-vue';
import VueDragscroll from 'vue-dragscroll';
import { Workbox } from "workbox-window";
Vue.use(Vuex);
Vue.use(PortalVue);
Vue.use(VueDragscroll);
// async dependencies
(async function () {

View File

@ -5,6 +5,8 @@ import { namespace } from 'vuex-class';
import store from '../store'
import { Trip } from "../model/trip";
import urls from "../urls";
import { Jsonified } from "../utils";
import * as moment from "moment";
const { State } = namespace('departures');
@ -19,9 +21,20 @@ export class DeparturesComponent extends Vue {
@Component({ template: require("../../components/departures/departure.html") })
export class DepartureComponent extends Vue {
@Prop(Object) departure: Departure;
scheduledTrip: Trip = null;
showTrip: boolean = false;
trip: Trip = null;
processTrip(trip: Jsonified<Trip>): Trip {
return {
...trip,
schedule: trip.schedule.map(s => ({
...s,
arrival: moment.parseZone(s.arrival),
departure: moment.parseZone(s.departure),
}))
};
};
get timeDiffers() {
const departure = this.departure;
@ -42,9 +55,21 @@ export class DepartureComponent extends Vue {
const response = await fetch(urls.prepare(urls.trip, { id: this.departure.trip.id }));
if (response.ok) {
this.trip = await response.json();
this.scheduledTrip = this.processTrip(await response.json());
}
}
get trip() {
const trip = this.scheduledTrip;
return trip && {
...trip,
schedule: trip.schedule.map(stop => ({
...stop,
arrival: stop.arrival.clone().add(this.departure.delay, 'seconds'),
departure: stop.departure.clone().add(this.departure.delay, 'seconds'),
}))
};
}
}
Vue.component('Departures', DeparturesComponent);

View File

@ -7,3 +7,4 @@ export * from './messages'
export * from './map'
export * from './app'
export * from './favourites'
export * from './trip'

View File

@ -0,0 +1,11 @@
import Vue from "vue";
import Component from "vue-class-component";
import { Prop } from "vue-property-decorator";
import { ScheduledStop } from "../model/trip";
@Component({ template: require("../../components/trip.html") })
export class TripComponent extends Vue {
@Prop(Array) public schedule: ScheduledStop[];
}
Vue.component('Trip', TripComponent);

View File

@ -2,7 +2,10 @@ export interface Stop {
id: any;
name: string;
description?: string;
location?: [ number, number ];
location?: {
lat: number,
lng: number,
};
onDemand?: boolean;
variant?: string;
}

View File

@ -1,6 +1,6 @@
import { Module } from "vuex";
import { RootState } from "./root";
import { Departure, Stop } from "../model";
import { Departure, Line, Stop } from "../model";
import * as moment from 'moment'
import common, { CommonState } from './common'
import urls from "../urls";
@ -43,12 +43,12 @@ export const departures: Module<DeparturesState, RootState> = {
}
const departures = await response.json() as Jsonified<Departure>[];
commit('update', departures.map(departure => {
departure.scheduled = moment.parseZone(departure.scheduled);
departure.estimated = departure.estimated && moment.parseZone(departure.estimated);
return departure as Departure;
}));
commit('update', departures.map((departure): Departure => ({
...departure,
line: departure.line as Line,
scheduled: moment.parseZone(departure.scheduled),
estimated: departure.estimated && moment.parseZone(departure.estimated),
})));
}
}
};

View File

@ -1,9 +1,13 @@
type Simplify<T, K = any> = string |
import { Moment } from "moment";
type Simplify<T> = string |
T extends string ? string :
T extends number ? number :
T extends boolean ? boolean :
T extends Array<K> ? Array<K> :
T extends Object ? Object : any;
T extends Moment ? string :
T extends Array<infer K> ? Array<Simplify<K>> :
T extends (infer K)[] ? Simplify<K>[] :
T extends Object ? Jsonified<T> : any;
export type Jsonified<T> = { [K in keyof T]: Simplify<T[K]> }
export type Optionalify<T> = { [K in keyof T]?: T[K] }

View File

@ -8,7 +8,7 @@ use Tightenco\Collect\Support\Collection;
interface ScheduleRepository
{
const DEFAULT_DEPARTURES_COUNT = 8;
const DEFAULT_DEPARTURES_COUNT = 16;
public function getDeparturesForStop(
Stop $stop,

View File

@ -46,7 +46,9 @@ class ZtmGdanskDepartureRepository implements DepartureRepository
$first = $real->map(t\getter('scheduled'))->min() ?? $now;
$scheduled = $this->getScheduledDepartures($stop, $first);
return $this->pair($scheduled, $real);
return $this->pair($scheduled, $real)->filter(function (Departure $departure) use ($now) {
return $departure->getDeparture() > $now;
});
}
private function getRealDepartures(Stop $stop)

View File

@ -6197,6 +6197,11 @@ vue-class-component@^6.2.0:
resolved "https://registry.yarnpkg.com/vue-class-component/-/vue-class-component-6.3.2.tgz#e6037e84d1df2af3bde4f455e50ca1b9eec02be6"
integrity sha512-cH208IoM+jgZyEf/g7mnFyofwPDJTM/QvBNhYMjqGB8fCsRyTf68rH2ISw/G20tJv+5mIThQ3upKwoL4jLTr1A==
vue-dragscroll@^1.10.2:
version "1.10.2"
resolved "https://registry.yarnpkg.com/vue-dragscroll/-/vue-dragscroll-1.10.2.tgz#34ace3c6aa7a39edc157cac5e9ea1d5b31b1119c"
integrity sha512-fGVw8KP3ggbp49csa1Tbj2my0YuNmZ1zxYYge4QWIypGNHVwd9hResy/v6QF5HxY0a+qd2EBteeBpxtJxFMp5A==
vue-property-decorator@^7.0.0:
version "7.3.0"
resolved "https://registry.yarnpkg.com/vue-property-decorator/-/vue-property-decorator-7.3.0.tgz#d50d67f0b0f1c814f9f2fba36d6eeccbcc62dbb6"