Exctract trip to separate component
This commit is contained in:
parent
c00e038fba
commit
e49a70c71a
@ -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",
|
||||
|
@ -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>
|
||||
|
13
resources/components/trip.html
Normal file
13
resources/components/trip.html
Normal 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>
|
6
resources/styles/_dragscroll.scss
Normal file
6
resources/styles/_dragscroll.scss
Normal file
@ -0,0 +1,6 @@
|
||||
.drag-scroll {
|
||||
cursor : grab;
|
||||
&:active {
|
||||
cursor : grabbing;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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 () {
|
||||
|
@ -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);
|
||||
|
@ -7,3 +7,4 @@ export * from './messages'
|
||||
export * from './map'
|
||||
export * from './app'
|
||||
export * from './favourites'
|
||||
export * from './trip'
|
||||
|
11
resources/ts/components/trip.ts
Normal file
11
resources/ts/components/trip.ts
Normal 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);
|
@ -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;
|
||||
}
|
||||
|
@ -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),
|
||||
})));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -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] }
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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"
|
||||
|
Loading…
Reference in New Issue
Block a user