better responsiveness

This commit is contained in:
Kacper Donat 2018-09-16 17:21:12 +02:00
parent 8c36299596
commit b6948ef0f1
12 changed files with 150 additions and 48 deletions

View File

@ -1,5 +1,5 @@
<div class="fold">
<div class="fold__inner" ref="inner">
<div class="fold" :aria-expanded="visible ? 'true' : 'false'">
<div class="fold__inner" ref="inner" :tabindex="visible ? false : -1">
<lazy v-if="lazy" :activate="visible">
<slot></slot>
</lazy>

View File

@ -1,4 +1,4 @@
<span class="line__symbol flex" :class="[`line--${line.type}`]">
<span class="line__symbol flex" :class="{ [`line--${line.type}`]: true, 'line--night': line.night, 'line--fast': line.fast }">
<span class="flex align-items-stretch">
<span class="icon">
<fa :icon="['fac', line.type]" fixed-width></fa>

View File

@ -1,6 +1,15 @@
<ul class="messages list-unstyled">
<li class="messages__message alert" :class="`alert-${type(message)}`" v-for="message in messages">
<li class="message alert" :class="`alert-${type(message)}`" v-for="message in messages">
<fa :icon="icon(message)" fixed-width></fa>
{{ message.message }}
<div class="message__info">
<small class="message__date">
Komunikat ważny od
{{ message.validFrom.format('HH:mm') }}
do
{{ message.validTo.format('HH:mm') }}
</small>
</div>
</li>
</ul>

View File

@ -1,37 +1,35 @@
<div v-if="ready">
<div class="row">
<div class="col-md">
<strong>Linie:</strong>
<ul class="stop__lines list-unstyled list-inline">
<li v-for="line in lines" class="list-inline-item mb-2">
<line-symbol :line="line"></line-symbol>
</li>
</ul>
<div v-if="ready" class="stop__details" v-responsive>
<section>
<strong>Linie:</strong>
<ul class="stop__lines list-unstyled list-inline">
<li v-for="line in lines" class="list-inline-item mb-2">
<line-symbol :line="line"></line-symbol>
</li>
</ul>
<strong>Na mapie:</strong>
<div style="height: 350px">
<l-map :center="stop.location" :zoom=17>
<l-tile-layer url="http://{s}.tile.osm.org/{z}/{x}/{y}.png" attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'></l-tile-layer>
<l-marker :lat-lng="stop.location"></l-marker>
</l-map>
</div>
</div>
<strong>Trasy:</strong>
<ul class="stop__tracks list-underlined">
<li v-for="{ track, order } in tracks" class="track">
<div class="track__line">
<line-symbol :line="track.line"></line-symbol>
</div>
<div class="track__description">
{{ track.description }}
</div>
<span class="badge badge-pill badge-light track__order">#{{ order }}</span>
</li>
</ul>
</section>
<div class="col-md">
<strong>Trasy:</strong>
<ul class="stop__tracks list-underlined">
<li v-for="{ track, order } in tracks" class="track">
<div class="track__line">
<line-symbol :line="track.line"></line-symbol>
</div>
<div class="track__description">
{{ track.description }}
</div>
<span class="badge badge-pill badge-light track__order">#{{ order }}</span>
</li>
</ul>
<section>
<strong>Na mapie:</strong>
<div style="height: 350px" tabindex="-1">
<l-map :center="stop.location" :zoom=17>
<l-tile-layer url="http://{s}.tile.osm.org/{z}/{x}/{y}.png" attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'></l-tile-layer>
<l-marker :lat-lng="stop.location"></l-marker>
</l-map>
</div>
</div>
</section>
</div>
<div v-else class="text-center">
<fa icon="spinner-third" pulse></fa>

View File

@ -16,7 +16,7 @@
</slot>
</div>
<fold :visible="details" class="stop__details" lazy>
<fold :visible="details" class="stop__details-fold" lazy>
<stop-details :stop="stop"></stop-details>
</fold>

View File

@ -3,6 +3,10 @@
@extend .btn-link;
color: black;
&:focus {
outline: 2px solid rgba($blue, .2);
}
}
&.btn-outline-action {

View File

@ -8,9 +8,25 @@
padding: 0 .2rem;
}
&.line--#{$type} .icon {
background-color: $color;
color: color-yiq($color);
&.line--#{$type} {
.icon {
background-color: $color;
color: color-yiq($color);
}
.badge {
background-color: transparent;
color: $color;
border: 1px solid $color;
}
}
&.line--night {
.badge {
background-color: $dark;
color: $white;
border: none;
}
}
}
}

View File

@ -15,7 +15,7 @@
display: flex;
}
.stop__details {
.stop__details-fold {
flex-basis: 100%;
.fold__inner { padding-bottom: .75rem; }
}
@ -29,4 +29,21 @@
.stop__tracks .line__symbol .badge {
flex-grow: 1;
}
.stop__details {
display: flex;
flex-wrap: wrap;
margin: 0 (-$grid-gutter-width/2);
section {
flex: 0 0 100%;
padding: 0 $grid-gutter-width/2;
}
&.size-lg {
section {
flex: 0 0 50%;
}
}
}

View File

@ -15,6 +15,11 @@ $line-types: (
'unknown': $dark
);
$default-spacing: .5rem;
$alert-margin-bottom: $default-spacing;
$headings-margin-bottom: $default-spacing;
@import "~bootstrap/scss/bootstrap";
@import "common";

View File

@ -4,6 +4,8 @@ import { Message } from "../model/message";
import urls from "../urls";
import { faInfoCircle, faExclamationTriangle, faQuestionCircle } from "@fortawesome/pro-light-svg-icons";
import { Jsonified } from "../utils";
import moment = require("moment");
@Component({ template: require("../../components/messages.html") })
export class MessagesComponent extends Vue {
@ -18,7 +20,15 @@ export class MessagesComponent extends Vue {
const response = await fetch(urls.prepare(urls.messages));
if (response.ok) {
this.messages = await response.json();
const messages = (await response.json()) as Jsonified<Message>[];
this.messages = messages.map(m => {
const message = m as Message;
message.validFrom = moment(m.validFrom);
message.validTo = moment(m.validTo);
return message;
});
}
this.$emit('update', this.messages);

View File

@ -1,11 +1,11 @@
export interface Decorator<TArgs extends any[], FArgs extends any[], TRet, FRet> {
decorate(f: (...farg: FArgs) => FRet, ...args: TArgs): (...farg: FArgs) => TRet;
export interface Decorator<TArgs extends any[], FArgs extends any[], TRet extends any, FRet extends any> {
decorate(f: (...farg: FArgs) => any, ...args: TArgs): (...farg: FArgs) => TRet;
(...args: TArgs): (target, name: string | symbol, descriptor: TypedPropertyDescriptor<(...farg: FArgs) => FRet>) => void;
}
export function decorator<TArgs extends any[], FArgs extends any[], TRet, FRet>
(decorate: (f: (...fargs: FArgs) => FRet, ...args: TArgs) => (...fargs: FArgs) => TRet)
export function decorator<TArgs extends any[], FArgs extends any[], TRet extends any, FRet extends any>
(decorate: (f: (...farg: FArgs) => FRet, ...args: TArgs) => (...farg: FArgs) => TRet)
: Decorator<TArgs, FArgs, TRet, FRet> {
const factory = function (this: Decorator<TArgs, FArgs, TRet, FRet>, ...args: TArgs) {
@ -43,3 +43,11 @@ export const debounce = decorator(function (decorated, time: number, max: number
}, time);
}
});
export const condition = decorator(function <Args extends any[], Ret extends any>(decorated: (...args: Args) => Ret, predicate: (...args: Args) => boolean) {
return function (this: any, ...args: Args) {
if (predicate(...args)) {
return decorated(...args);
}
}
});

View File

@ -1,5 +1,6 @@
import { signed } from "./utils";
import Vue from 'vue';
import { condition } from "./decorators";
Vue.filter('signed', signed);
@ -18,6 +19,40 @@ Vue.directive('hover', (el, binding, node) => {
}
};
el.addEventListener('mouseenter', e => update(true, e));
el.addEventListener('mouseleave', e => update(false, e));
});
const activate = event => update(true, event);
const deactivate = event => update(false, event);
el.addEventListener('mouseenter', activate);
el.addEventListener('click', activate);
el.addEventListener('keydown', condition.decorate(deactivate, e => e.keyCode == 27));
el.addEventListener('mouseleave', deactivate);
el.addEventListener('blur', deactivate);
});
Vue.directive('responsive', (el, binding) => {
const breakpoints = typeof binding.value === 'object' ? binding.value : {
'xs': 0,
'sm': 576,
'md': 768,
'lg': 1024,
'xl': 1200,
};
const resize = () => {
const width = el.scrollWidth;
el.classList.remove(...Object.keys(breakpoints).map(breakpoint => `size-${breakpoint}`));
for (let [ breakpoint, size ] of Object.entries(breakpoints)) {
if (width < size) {
break;
}
el.classList.add(`size-${breakpoint}`);
}
};
resize();
if (!binding.modifiers['once']) {
window.addEventListener('resize', resize);
}
});