Exctract trip to separate component
This commit is contained in:
parent
c00e038fba
commit
e49a70c71a
@ -41,6 +41,7 @@
|
|||||||
"imagemin-webpack-plugin": "^2.3.0",
|
"imagemin-webpack-plugin": "^2.3.0",
|
||||||
"mini-css-extract-plugin": "^0.4.2",
|
"mini-css-extract-plugin": "^0.4.2",
|
||||||
"portal-vue": "^2.1.7",
|
"portal-vue": "^2.1.7",
|
||||||
|
"vue-dragscroll": "^1.10.2",
|
||||||
"vue2-leaflet": "^1.0.2",
|
"vue2-leaflet": "^1.0.2",
|
||||||
"vuex": "^3.0.1",
|
"vuex": "^3.0.1",
|
||||||
"vuex-class": "^0.3.1",
|
"vuex-class": "^0.3.1",
|
||||||
|
@ -34,16 +34,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<fold :visible="showTrip">
|
<fold :visible="showTrip">
|
||||||
<div v-if="trip != null" class="trip" :class="[ `trip--${departure.line.type}` ]">
|
<trip :schedule="trip.schedule" v-if="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>
|
|
||||||
<div v-else class="text-center">
|
<div v-else class="text-center">
|
||||||
<fa icon="spinner-third" pulse></fa>
|
<fa icon="spinner-third" pulse></fa>
|
||||||
</div>
|
</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;
|
$description-width: 250px;
|
||||||
|
|
||||||
$trip-stop-marker-size: .9rem;
|
$trip-stop-marker-size: .9rem;
|
||||||
$trip-stop-line-width: .2rem;
|
$trip-stop-marker-spacing: .75rem;
|
||||||
|
$trip-line-width: .2rem;
|
||||||
|
|
||||||
.trip {
|
.trip {
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -18,45 +19,49 @@ $trip-stop-line-width: .2rem;
|
|||||||
display: flex;
|
display: flex;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
|
|
||||||
|
@include no-scrollbars;
|
||||||
|
@extend .drag-scroll;
|
||||||
}
|
}
|
||||||
|
|
||||||
.trip-stop {
|
.trip__stop {
|
||||||
width: 2.5rem;
|
width: 2.5rem;
|
||||||
position: relative;
|
position: relative;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.trip-stop:first-child {
|
.trip__stop:first-child {
|
||||||
.trip-stop__marker::before {
|
.trip__marker::before {
|
||||||
content: none;
|
content: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.trip-stop:last-child {
|
.trip__stop:last-child {
|
||||||
.trip-stop__marker::after {
|
.trip__marker::after {
|
||||||
content: none;
|
content: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.trip-stop__marker {
|
.trip__marker {
|
||||||
width: $trip-stop-marker-size;
|
width: $trip-stop-marker-size;
|
||||||
height: $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%;
|
border-radius: 100%;
|
||||||
background: white;
|
background: white;
|
||||||
margin: .75rem 0;
|
margin: $trip-stop-marker-spacing 0;
|
||||||
|
|
||||||
&::before, &::after {
|
&::before, &::after {
|
||||||
content: "";
|
content: "";
|
||||||
display: block;
|
display: block;
|
||||||
height: $trip-stop-line-width;
|
height: $trip-line-width;
|
||||||
background: $dark;
|
background: $dark;
|
||||||
width: 50%;
|
width: 50%;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 50%;
|
top: $trip-stop-marker-spacing + ($trip-stop-marker-size) / 2;
|
||||||
transform: translateY(-50%);
|
transform: translateY(-50%);
|
||||||
z-index: -1;
|
z-index: -1;
|
||||||
}
|
}
|
||||||
@ -72,7 +77,7 @@ $trip-stop-line-width: .2rem;
|
|||||||
|
|
||||||
@each $type, $color in $line-types {
|
@each $type, $color in $line-types {
|
||||||
.trip--#{$type} {
|
.trip--#{$type} {
|
||||||
.trip-stop__marker {
|
.trip__marker {
|
||||||
border-color: $color;
|
border-color: $color;
|
||||||
|
|
||||||
&::before, &::after {
|
&::before, &::after {
|
||||||
@ -82,7 +87,7 @@ $trip-stop-line-width: .2rem;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.trip-stop__description {
|
.trip__description {
|
||||||
display: flex;
|
display: flex;
|
||||||
transform: rotate(-$description-rotation) translateX(.75rem);
|
transform: rotate(-$description-rotation) translateX(.75rem);
|
||||||
transform-origin: 0 50%;
|
transform-origin: 0 50%;
|
||||||
@ -95,3 +100,10 @@ $trip-stop-line-width: .2rem;
|
|||||||
width: max-content;
|
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 "common";
|
||||||
@import "stop";
|
@import "stop";
|
||||||
@import "departure";
|
@import "departure";
|
||||||
@ -52,6 +62,7 @@ $grid-gutter-width: $spacer * 2;
|
|||||||
@import "form";
|
@import "form";
|
||||||
@import "fabourites";
|
@import "fabourites";
|
||||||
@import "trip";
|
@import "trip";
|
||||||
|
@import "dragscroll";
|
||||||
|
|
||||||
body {
|
body {
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
|
@ -14,10 +14,12 @@ window['Popper'] = Popper;
|
|||||||
import Vue from "vue";
|
import Vue from "vue";
|
||||||
import Vuex from 'vuex';
|
import Vuex from 'vuex';
|
||||||
import PortalVue from 'portal-vue';
|
import PortalVue from 'portal-vue';
|
||||||
|
import VueDragscroll from 'vue-dragscroll';
|
||||||
import { Workbox } from "workbox-window";
|
import { Workbox } from "workbox-window";
|
||||||
|
|
||||||
Vue.use(Vuex);
|
Vue.use(Vuex);
|
||||||
Vue.use(PortalVue);
|
Vue.use(PortalVue);
|
||||||
|
Vue.use(VueDragscroll);
|
||||||
|
|
||||||
// async dependencies
|
// async dependencies
|
||||||
(async function () {
|
(async function () {
|
||||||
|
@ -5,6 +5,8 @@ import { namespace } from 'vuex-class';
|
|||||||
import store from '../store'
|
import store from '../store'
|
||||||
import { Trip } from "../model/trip";
|
import { Trip } from "../model/trip";
|
||||||
import urls from "../urls";
|
import urls from "../urls";
|
||||||
|
import { Jsonified } from "../utils";
|
||||||
|
import * as moment from "moment";
|
||||||
|
|
||||||
const { State } = namespace('departures');
|
const { State } = namespace('departures');
|
||||||
|
|
||||||
@ -19,9 +21,20 @@ export class DeparturesComponent extends Vue {
|
|||||||
@Component({ template: require("../../components/departures/departure.html") })
|
@Component({ template: require("../../components/departures/departure.html") })
|
||||||
export class DepartureComponent extends Vue {
|
export class DepartureComponent extends Vue {
|
||||||
@Prop(Object) departure: Departure;
|
@Prop(Object) departure: Departure;
|
||||||
|
scheduledTrip: Trip = null;
|
||||||
|
|
||||||
showTrip: boolean = false;
|
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() {
|
get timeDiffers() {
|
||||||
const departure = this.departure;
|
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 }));
|
const response = await fetch(urls.prepare(urls.trip, { id: this.departure.trip.id }));
|
||||||
|
|
||||||
if (response.ok) {
|
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);
|
Vue.component('Departures', DeparturesComponent);
|
||||||
|
@ -7,3 +7,4 @@ export * from './messages'
|
|||||||
export * from './map'
|
export * from './map'
|
||||||
export * from './app'
|
export * from './app'
|
||||||
export * from './favourites'
|
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;
|
id: any;
|
||||||
name: string;
|
name: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
location?: [ number, number ];
|
location?: {
|
||||||
|
lat: number,
|
||||||
|
lng: number,
|
||||||
|
};
|
||||||
onDemand?: boolean;
|
onDemand?: boolean;
|
||||||
variant?: string;
|
variant?: string;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Module } from "vuex";
|
import { Module } from "vuex";
|
||||||
import { RootState } from "./root";
|
import { RootState } from "./root";
|
||||||
import { Departure, Stop } from "../model";
|
import { Departure, Line, Stop } from "../model";
|
||||||
import * as moment from 'moment'
|
import * as moment from 'moment'
|
||||||
import common, { CommonState } from './common'
|
import common, { CommonState } from './common'
|
||||||
import urls from "../urls";
|
import urls from "../urls";
|
||||||
@ -43,12 +43,12 @@ export const departures: Module<DeparturesState, RootState> = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const departures = await response.json() as Jsonified<Departure>[];
|
const departures = await response.json() as Jsonified<Departure>[];
|
||||||
commit('update', departures.map(departure => {
|
commit('update', departures.map((departure): Departure => ({
|
||||||
departure.scheduled = moment.parseZone(departure.scheduled);
|
...departure,
|
||||||
departure.estimated = departure.estimated && moment.parseZone(departure.estimated);
|
line: departure.line as Line,
|
||||||
|
scheduled: moment.parseZone(departure.scheduled),
|
||||||
return departure as Departure;
|
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 string ? string :
|
||||||
T extends number ? number :
|
T extends number ? number :
|
||||||
T extends boolean ? boolean :
|
T extends boolean ? boolean :
|
||||||
T extends Array<K> ? Array<K> :
|
T extends Moment ? string :
|
||||||
T extends Object ? Object : any;
|
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 Jsonified<T> = { [K in keyof T]: Simplify<T[K]> }
|
||||||
export type Optionalify<T> = { [K in keyof T]?: T[K] }
|
export type Optionalify<T> = { [K in keyof T]?: T[K] }
|
||||||
|
@ -8,7 +8,7 @@ use Tightenco\Collect\Support\Collection;
|
|||||||
|
|
||||||
interface ScheduleRepository
|
interface ScheduleRepository
|
||||||
{
|
{
|
||||||
const DEFAULT_DEPARTURES_COUNT = 8;
|
const DEFAULT_DEPARTURES_COUNT = 16;
|
||||||
|
|
||||||
public function getDeparturesForStop(
|
public function getDeparturesForStop(
|
||||||
Stop $stop,
|
Stop $stop,
|
||||||
|
@ -46,7 +46,9 @@ class ZtmGdanskDepartureRepository implements DepartureRepository
|
|||||||
$first = $real->map(t\getter('scheduled'))->min() ?? $now;
|
$first = $real->map(t\getter('scheduled'))->min() ?? $now;
|
||||||
$scheduled = $this->getScheduledDepartures($stop, $first);
|
$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)
|
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"
|
resolved "https://registry.yarnpkg.com/vue-class-component/-/vue-class-component-6.3.2.tgz#e6037e84d1df2af3bde4f455e50ca1b9eec02be6"
|
||||||
integrity sha512-cH208IoM+jgZyEf/g7mnFyofwPDJTM/QvBNhYMjqGB8fCsRyTf68rH2ISw/G20tJv+5mIThQ3upKwoL4jLTr1A==
|
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:
|
vue-property-decorator@^7.0.0:
|
||||||
version "7.3.0"
|
version "7.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/vue-property-decorator/-/vue-property-decorator-7.3.0.tgz#d50d67f0b0f1c814f9f2fba36d6eeccbcc62dbb6"
|
resolved "https://registry.yarnpkg.com/vue-property-decorator/-/vue-property-decorator-7.3.0.tgz#d50d67f0b0f1c814f9f2fba36d6eeccbcc62dbb6"
|
||||||
|
Loading…
Reference in New Issue
Block a user