autoupdate feature
This commit is contained in:
parent
82adf1139a
commit
1fe04718f9
@ -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>
|
||||||
|
@ -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='© <a href="//osm.org/copyright">OpenStreetMap</a> contributors'></l-tile-layer>
|
<l-tile-layer url="//{s}.tile.osm.org/{z}/{x}/{y}.png" attribution='© <a href="//osm.org/copyright">OpenStreetMap</a> contributors'></l-tile-layer>
|
||||||
|
@ -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;
|
||||||
|
@ -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");
|
||||||
|
@ -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";
|
||||||
|
@ -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({
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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()];
|
||||||
|
}
|
||||||
|
@ -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">
|
||||||
|
Loading…
Reference in New Issue
Block a user