autoupdate feature

This commit is contained in:
Kacper Donat 2018-09-30 21:30:12 +02:00
parent 82adf1139a
commit 1fe04718f9
10 changed files with 177 additions and 25 deletions

View File

@ -1,6 +1,6 @@
<div class="popper" :class="{ 'has-arrow': arrow, 'd-none': !visible }"> <div class="popper" :class="{ 'popper--arrow': arrow, 'd-none': !show }" v-hover="hovered">
<div class="popper-arrow" ref="arrow" v-if="arrow"></div> <div class="popper__arrow" ref="arrow" v-if="arrow"></div>
<lazy v-if="lazy" :activate="visible"> <lazy v-if="lazy" :activate="show">
<slot></slot> <slot></slot>
</lazy> </lazy>
<slot v-else></slot> <slot v-else></slot>

View File

@ -20,7 +20,7 @@
<stop-details :stop="stop"></stop-details> <stop-details :stop="stop"></stop-details>
</fold> </fold>
<popper reference="action-map" :visible="map" arrow> <popper reference="action-map" :visible="map" arrow class="popper--no-padding">
<div style="height: 300px; width: 500px"> <div style="height: 300px; width: 500px">
<l-map :center="stop.location" :zoom=17 :options="{ zoomControl: false, dragging: false }"> <l-map :center="stop.location" :zoom=17 :options="{ zoomControl: false, dragging: false }">
<l-tile-layer url="//{s}.tile.osm.org/{z}/{x}/{y}.png" attribution='&copy; <a href="//osm.org/copyright">OpenStreetMap</a> contributors'></l-tile-layer> <l-tile-layer url="//{s}.tile.osm.org/{z}/{x}/{y}.png" attribution='&copy; <a href="//osm.org/copyright">OpenStreetMap</a> contributors'></l-tile-layer>

View File

@ -44,6 +44,21 @@
.flex { .flex {
display: flex; display: flex;
align-items: center; align-items: center;
> .text {
margin-left: .2rem;
margin-right: .2rem;
display: inline-block;
margin-bottom: 0;
&:last-child, & + .text {
margin-right: 0;
}
&:first-child {
margin-left: 0;
}
}
} }
.section { .section {
@ -53,6 +68,15 @@
@extend .alert; @extend .alert;
@extend .alert-dark; @extend .alert-dark;
> * {
font-size: medium;
}
@include direct-headings {
@extend .flex;
margin-bottom: 0;
}
font-size: medium; font-size: medium;
background: transparent; background: transparent;
padding-top: .5rem; padding-top: .5rem;

View File

@ -55,11 +55,11 @@
@mixin triangle-right($size, $color, $border: none) { @include triangle(right, $size, $color, $border); } @mixin triangle-right($size, $color, $border: none) { @include triangle(right, $size, $color, $border); }
.popper { .popper {
$arrow-base: 10px; $arrow-base: 8px;
$arrow-color: white; $arrow-color: white;
$arrow-border: black; $arrow-border: black;
$popper-padding: 2px; $popper-padding: .5rem;
padding: $popper-padding; padding: $popper-padding;
background: white; background: white;
@ -73,12 +73,22 @@
border-radius: 2px; border-radius: 2px;
.popper-arrow { .popper__arrow {
position: absolute; position: absolute;
width: 0; width: 0;
height: 0; height: 0;
} }
&.popper--no-padding {
padding: 0;
}
.popper__heading {
font-size: $font-size-sm;
font-weight: bold;
margin-bottom: .5rem;
}
@mixin placement($placement) { @mixin placement($placement) {
$opposite: ( $opposite: (
left: right, left: right,
@ -90,14 +100,14 @@
&[x-placement*="#{$placement}"] { &[x-placement*="#{$placement}"] {
margin-#{map-get($opposite, $placement)}: $arrow-base; margin-#{map-get($opposite, $placement)}: $arrow-base;
.popper-arrow { .popper__arrow {
#{map-get($opposite, $placement)}: 0; #{map-get($opposite, $placement)}: 0;
@include triangle(map-get($opposite, $placement), $arrow-base, $arrow-color, $arrow-border); @include triangle(map-get($opposite, $placement), $arrow-base, $arrow-color, $arrow-border);
} }
} }
} }
&.has-arrow { &.popper--arrow {
@include placement("left"); @include placement("left");
@include placement("right"); @include placement("right");
@include placement("top"); @include placement("top");

View File

@ -1,6 +1,7 @@
$border-radius: 0; $border-radius: 0;
$border-radius-lg: $border-radius; $border-radius-lg: $border-radius;
$border-radius-sm: $border-radius; $border-radius-sm: $border-radius;
@import "~bootstrap/scss/functions"; @import "~bootstrap/scss/functions";
@import "~bootstrap/scss/variables"; @import "~bootstrap/scss/variables";
@ -24,6 +25,18 @@ $container-max-widths: map-merge($container-max-widths, ( xl: 1320px ));
@import "~bootstrap/scss/bootstrap"; @import "~bootstrap/scss/bootstrap";
@mixin headings {
h1, h2, h3, h4, h5, h6 {
@content
}
}
@mixin direct-headings {
> h1, > h2, > h3, > h4, > h5, > h6 {
@content
}
}
@import "common"; @import "common";
@import "stop"; @import "stop";
@import "departure"; @import "departure";

View File

@ -32,6 +32,8 @@ Vue.use(Vuex);
components components
}; };
let intervals = { messages: null, departures: null };
window['app'] = new Vue({ window['app'] = new Vue({
el: '#app', el: '#app',
store: store, store: store,
@ -39,7 +41,22 @@ Vue.use(Vuex);
stops: [], stops: [],
sections: { sections: {
messages: true messages: true
} },
settings: {
messages: false,
departures: false
},
autorefresh: {
messages: {
active: true,
interval: 60
},
departures: {
active: true,
interval: 10
}
},
}, },
computed: { computed: {
messages(this: any) { messages(this: any) {
@ -58,7 +75,26 @@ Vue.use(Vuex);
watch: { watch: {
stops(this: any, stops) { stops(this: any, stops) {
this.updateDepartures({ stops }); this.updateDepartures({ stops });
} },
autorefresh: { immediate: true, handler(this: any, settings) {
if (intervals.messages) {
clearInterval(intervals.messages);
intervals.messages = null;
}
if (intervals.departures) {
clearInterval(intervals.departures);
intervals.messages = null;
}
if (settings.messages.active) {
intervals.messages = setInterval(() => this.updateMessages(), Math.max(5, settings.messages.interval) * 1000);
}
if (settings.departures.active) {
intervals.departures = setInterval(() => this.updateDepartures({ stops: this.stops }), Math.max(5, settings.departures.interval) * 1000);
}
} }
}, },
methods: { methods: {
...mapActions({ ...mapActions({

View File

@ -19,8 +19,14 @@ export class PopperComponent extends Vue {
@Prop(Boolean) @Prop(Boolean)
public lazy: boolean; public lazy: boolean;
public hovered: boolean = false;
private _popper; private _popper;
get show() {
return this.visible || this.hovered;
}
mounted() { mounted() {
const reference = this.$parent.$refs[this.reference] as HTMLElement; const reference = this.$parent.$refs[this.reference] as HTMLElement;
@ -30,6 +36,12 @@ export class PopperComponent extends Vue {
arrow: { enabled: this.arrow, element: this.$refs['arrow'] as Element } arrow: { enabled: this.arrow, element: this.$refs['arrow'] as Element }
} }
}); });
this.$nextTick(() => this._popper.update())
}
updated() {
this._popper.update();
} }
@Watch('visible') @Watch('visible')
@ -68,6 +80,7 @@ export class FoldComponent extends Vue {
this.observer.disconnect(); this.observer.disconnect();
} }
@Watch('visible') @Watch('visible')
private resize() { private resize() {
const inner = this.$refs['inner'] as HTMLDivElement; const inner = this.$refs['inner'] as HTMLDivElement;

View File

@ -1,4 +1,4 @@
import { signed } from "./utils"; import { set, signed } from "./utils";
import Vue from 'vue'; import Vue from 'vue';
import { condition } from "./decorators"; import { condition } from "./decorators";
@ -11,11 +11,11 @@ Vue.directive('hover', (el, binding, node) => {
} }
if (typeof binding.value === 'boolean') { if (typeof binding.value === 'boolean') {
node.context[binding.expression] = hovered; set(node.context, binding.expression, hovered);
} }
if (typeof binding.arg !== 'undefined') { if (typeof binding.arg !== 'undefined') {
node.context[binding.arg] = hovered; set(node.context, binding.arg, hovered);
} }
}; };

View File

@ -40,4 +40,22 @@ export function signed(number: number): string {
export function ensureArray<T>(x: T[]|T): T[] { export function ensureArray<T>(x: T[]|T): T[] {
return x instanceof Array ? x : [ x ]; return x instanceof Array ? x : [ x ];
} }
export function set(object: any, path: string, value: any) {
const segments = path.split('.');
while (segments.length > 1) {
object = object[segments.shift()];
}
object[segments.shift()] = value;
}
export function get(object: any, path: string): any {
const segments = path.split('.');
while (segments.length > 1) {
object = object[segments.shift()];
}
return object[segments.shift()];
}

View File

@ -6,29 +6,67 @@
<div class="row"> <div class="row">
<div class="col-md-8 order-md-last"> <div class="col-md-8 order-md-last">
<section class="section messages" v-show="messages.count > 0"> <section class="section messages" v-show="messages.count > 0">
<h2 class="section__title flex"> <header class="section__title flex">
<fa :icon="['fal', 'bullhorn']" fixed-width class="mr-2"></fa> <h2>
Komunikaty <span class="ml-2 badge badge-pill badge-dark">{{ '{{ messages.count }}' }}</span> <fa :icon="['fal', 'bullhorn']" fixed-width class="mr-2"></fa>
<button class="btn btn-action flex-space-left" @click="updateMessages"> Komunikaty <span class="ml-2 badge badge-pill badge-dark">{{ '{{ messages.count }}' }}</span>
</h2>
<button class="btn btn-action flex-space-left" ref="settings-messages" v-hover="settings.messages">
<fa :icon="['fal', 'cog']"></fa>
</button>
<button class="btn btn-action" @click="updateMessages" ref="btn-messages-refresh">
<fa :icon="['fal', 'sync']" :spin="messages.state === 'fetching'"></fa> <fa :icon="['fal', 'sync']" :spin="messages.state === 'fetching'"></fa>
</button> </button>
<button class="btn btn-action" @click="sections.messages = !sections.messages"> <button class="btn btn-action" @click="sections.messages = !sections.messages">
<fa :icon="['fal', sections.messages ? 'chevron-up' : 'chevron-down']" fixed-width/> <fa :icon="['fal', sections.messages ? 'chevron-up' : 'chevron-down']" fixed-width/>
</button> </button>
</h2>
<popper reference="settings-messages" :visible="settings.messages" arrow placement="left-start">
<h3 class="popper__heading flex">
<fa :icon="['far', 'cog']"></fa>
<label class="text" for="messages-auto-refresh">autoodświeżanie</label>
<input type="checkbox" class="flex-space-left" id="messages-auto-refresh" v-model="autorefresh.messages.active"/>
</h3>
<div class="flex" v-show="autorefresh.messages.active">
<span class="text">co</span>
<label class="sr-only" for="messages-auto-refresh-interval">częstotliwość odświeżania</label>
<input type="text" class="form-control form-control-sm" id="messages-auto-refresh-interval" v-model="autorefresh.messages.interval"/>
<span class="text">s</span>
</div>
</popper>
</header>
<fold :visible="sections.messages"> <fold :visible="sections.messages">
<messages></messages> <messages></messages>
</fold> </fold>
</section> </section>
<section class="section"> <section class="section">
<h2 class="section__title flex"> <header class="section__title flex">
<fa :icon="['fal', 'clock']" fixed-width class="mr-1"></fa> <h2>
Odjazdy <fa :icon="['fal', 'clock']" fixed-width></fa>
<button class="btn btn-action flex-space-left" @click="updateDepartures({ stops })"> <span class="text">Odjazdy</span>
</h2>
<button class="btn btn-action flex-space-left" ref="settings-departures" v-hover="settings.departures">
<fa :icon="['fal', 'cog']"></fa>
</button>
<button class="btn btn-action" @click="updateDepartures({ stops })">
<fa :icon="['fal', 'sync']" :spin="departures.state === 'fetching'"></fa> <fa :icon="['fal', 'sync']" :spin="departures.state === 'fetching'"></fa>
</button> </button>
</h2>
<popper reference="settings-departures" :visible="settings.departures" arrow placement="left-start">
<h3 class="popper__heading flex">
<fa :icon="['far', 'cog']"></fa>
<label class="text" for="messages-auto-refresh">autoodświeżanie</label>
<input type="checkbox" class="flex-space-left" id="messages-auto-refresh" v-model="autorefresh.departures.active"/>
</h3>
<div class="flex" v-show="autorefresh.messages.active">
<span class="text">co</span>
<label class="sr-only" for="messages-auto-refresh-interval">częstotliwość odświeżania</label>
<input type="text" class="form-control form-control-sm" id="messages-auto-refresh-interval" v-model="autorefresh.departures.interval"/>
<span class="text">s</span>
</div>
</popper>
</header>
<departures :stops="stops"></departures> <departures :stops="stops"></departures>
{% if provider.attribution %} {% if provider.attribution %}
<div class="attribution"> <div class="attribution">