diff --git a/resources/components/favourites.html b/resources/components/favourites.html new file mode 100644 index 0000000..1c5c33e --- /dev/null +++ b/resources/components/favourites.html @@ -0,0 +1,19 @@ +
+ +
+ + Brak zapisanych zespołów przystanków +
+
diff --git a/resources/components/favourites/save.html b/resources/components/favourites/save.html new file mode 100644 index 0000000..0d17625 --- /dev/null +++ b/resources/components/favourites/save.html @@ -0,0 +1,6 @@ +
+ + +
diff --git a/resources/components/finder.html b/resources/components/finder.html index 93597df..bb17c91 100644 --- a/resources/components/finder.html +++ b/resources/components/finder.html @@ -1,7 +1,7 @@
-
+
diff --git a/resources/components/popper.html b/resources/components/popper.html index 82cfe63..ed83d7a 100644 --- a/resources/components/popper.html +++ b/resources/components/popper.html @@ -1,4 +1,4 @@ -
+
diff --git a/resources/styles/_animations.scss b/resources/styles/_animations.scss new file mode 100644 index 0000000..fc4a7d0 --- /dev/null +++ b/resources/styles/_animations.scss @@ -0,0 +1,38 @@ +@mixin vue-animation($name, $animation: .5s ease-in-out) { + .#{$name}-enter-active { + animation: #{$name}-in $animation; + animation-fill-mode: backwards; + } + + .#{$name}-leave-active { + animation: #{$name}-in $animation reverse; + animation-fill-mode: forwards; + } + + @keyframes #{$name}-in { + @content + } +} + +@include vue-animation(fade, 250ms ease-in-out) { + 0% { + opacity: 0 + } + 100% { + opacity: 1 + } +} + +.transition-box { + @include clearfix; + + > * { + width: 100%; + float: left; + min-height: 2px; + + &:not(:first-child) { + margin-left: -100%; + } + } +} \ No newline at end of file diff --git a/resources/styles/_common.scss b/resources/styles/_common.scss index ba1d4b6..29b9570 100644 --- a/resources/styles/_common.scss +++ b/resources/styles/_common.scss @@ -87,7 +87,7 @@ background: none; } - .btn { + > .btn { margin-top: -.5rem; margin-bottom: -.5rem; } @@ -97,3 +97,15 @@ svg.svg-inline--fa { transform: rotate(360deg) } + +.btn-unstyled { + padding: 0; + margin: 0; + background: none; + border: none; + display: block; +} + +.icon { + padding: .5rem 0.75rem; +} \ No newline at end of file diff --git a/resources/styles/_controls.scss b/resources/styles/_controls.scss index 3e8a722..818cfae 100644 --- a/resources/styles/_controls.scss +++ b/resources/styles/_controls.scss @@ -4,11 +4,17 @@ color: black; + &:hover, &:active, &:focus { + text-decoration: none; + } + &:focus { outline: 2px solid rgba($blue, .2); } } + display: inline-block; + &.btn-outline-action { @extend .btn-outline-dark; } diff --git a/resources/styles/_popper.scss b/resources/styles/_popper.scss index db193e8..1168ad0 100644 --- a/resources/styles/_popper.scss +++ b/resources/styles/_popper.scss @@ -113,6 +113,4 @@ @include placement("top"); @include placement("bottom"); } - - animation: ease-in fade-in 150ms } diff --git a/resources/styles/main.scss b/resources/styles/main.scss index 94929f9..54184b5 100644 --- a/resources/styles/main.scss +++ b/resources/styles/main.scss @@ -43,6 +43,7 @@ $container-max-widths: map-merge($container-max-widths, ( xl: 1320px )); @import "line"; @import "controls"; @import "popper"; +@import "animations"; body { min-height: 100vh; diff --git a/resources/ts/app.ts b/resources/ts/app.ts index 5db619f..0f157a4 100644 --- a/resources/ts/app.ts +++ b/resources/ts/app.ts @@ -25,11 +25,14 @@ Vue.use(Vuex); import('bootstrap'), ]); + // here goes "public" API + window['czydojade'] = Object.assign({ + state: {} + }, window['czydojade'], { + components, + application: new components.Application({ el: '#app' }) + }); + store.dispatch('messages/update'); store.dispatch('load', window['czydojade'].state); - - // here goes "public" API - window['czydojade'] = Object.assign({}, window['czydojade'], { - components, application: new components.Application({ el: '#app' }) - }); })(); diff --git a/resources/ts/components/app.ts b/resources/ts/components/app.ts index e664444..0a215dc 100644 --- a/resources/ts/components/app.ts +++ b/resources/ts/components/app.ts @@ -4,6 +4,7 @@ import { Component, Watch } from "vue-property-decorator"; import { Mutation, Action } from 'vuex-class' import { ObtainPayload } from "../store/departures"; import { Stop } from "../model"; +import { PopperComponent } from "./utils"; @Component({ store }) export class Application extends Vue { @@ -11,9 +12,11 @@ export class Application extends Vue { messages: true }; - private settings = { + private visibility = { messages: false, - departures: false + departures: false, + save: false, + picker: 'search' }; private autorefresh = { @@ -55,23 +58,19 @@ export class Application extends Vue { this.$el.classList.remove('not-ready'); } - @Action('messages/update') updateMessages: () => void; + @Action('messages/update') updateMessages: () => void; @Action('departures/update') updateDepartures: (payload: ObtainPayload) => void; @Mutation add: (stops: Stop[]) => void; @Mutation remove: (stop: Stop) => void; @Mutation clear: () => void; - save() { - this.$store.dispatch('save').then(x => console.log(x)); - } - @Watch('stops') onStopUpdate(this: any, stops) { this.updateDepartures({ stops }); } - @Watch('settings', { immediate: true, deep: true }) + @Watch('autorefresh', { immediate: true, deep: true }) onAutorefreshUpdate(settings) { if (this.intervals.messages) { clearInterval(this.intervals.messages); diff --git a/resources/ts/components/favourites.ts b/resources/ts/components/favourites.ts new file mode 100644 index 0000000..b904681 --- /dev/null +++ b/resources/ts/components/favourites.ts @@ -0,0 +1,39 @@ +import Vue from 'vue' +import { Component } from 'vue-property-decorator' +import { namespace } from "vuex-class"; +import { Favourite } from "../store/favourites"; +import { SavedState } from "../store/root"; + +const { State, Mutation } = namespace('favourites'); + +@Component({ template: require('../../components/favourites.html' )}) +export class FavouritesComponent extends Vue { + @State favourites: Favourite[]; + @Mutation remove: (fav: Favourite) => void; + + choose(favourite: Favourite) { + this.$store.dispatch('load', favourite.state); + } +} + +@Component({ template: require('../../components/favourites/save.html' )}) +export class FavouritesAdderComponent extends Vue { + private name = ""; + + @Mutation add: (fav: Favourite) => void; + + async save() { + const state = await this.$store.dispatch('save') as SavedState; + const name = this.name; + + const favourite: Favourite = { name, state }; + + this.add(favourite); + this.name = ''; + + this.$emit('saved', favourite); + } +} + +Vue.component('Favourites', FavouritesComponent); +Vue.component('FavouritesAdder', FavouritesAdderComponent); diff --git a/resources/ts/components/index.ts b/resources/ts/components/index.ts index ce46728..c7258d3 100644 --- a/resources/ts/components/index.ts +++ b/resources/ts/components/index.ts @@ -5,4 +5,5 @@ export * from './departures' export * from './stop' export * from './messages' export * from './map' -export * from './app' \ No newline at end of file +export * from './app' +export * from './favourites' diff --git a/resources/ts/components/utils.ts b/resources/ts/components/utils.ts index 3fed9f1..a074847 100644 --- a/resources/ts/components/utils.ts +++ b/resources/ts/components/utils.ts @@ -20,11 +20,12 @@ export class PopperComponent extends Vue { public lazy: boolean; public hovered: boolean = false; + public focused: boolean = false; private _popper; get show() { - return this.visible || this.hovered; + return this.visible || this.hovered || this.focused; } mounted() { diff --git a/resources/ts/filters.ts b/resources/ts/filters.ts index 8ca0110..44c5ae1 100644 --- a/resources/ts/filters.ts +++ b/resources/ts/filters.ts @@ -30,7 +30,7 @@ Vue.directive('hover', { el.addEventListener('click', activate); el.addEventListener('keydown', keyboard); el.addEventListener('mouseleave', deactivate); - el.addEventListener('focusout', deactivate); + // el.addEventListener('focusout', deactivate); }, unbind(el, binding) { if (typeof binding['events'] !== 'undefined') { @@ -40,7 +40,7 @@ Vue.directive('hover', { el.removeEventListener('click', activate); el.removeEventListener('keydown', keyboard); el.removeEventListener('mouseleave', deactivate); - el.removeEventListener('focusout', deactivate); + // el.removeEventListener('focusout', deactivate); } } }); diff --git a/resources/ts/store/favourites.ts b/resources/ts/store/favourites.ts new file mode 100644 index 0000000..51d711c --- /dev/null +++ b/resources/ts/store/favourites.ts @@ -0,0 +1,37 @@ +import { RootState, SavedState } from "./root"; +import { Module, Plugin, Store } from "vuex"; +import * as utils from "../utils"; + +export interface Favourite { + name: string; + state: SavedState; +} + +export interface FavouritesState { + favourites: Favourite[]; +} + +const favourites: Module = { + namespaced: true, + state: { + favourites: [] + }, + mutations: { + add(state, favourite: Favourite) { + state.favourites.push(favourite); + }, + remove(state, favourite: Favourite) { + state.favourites = state.favourites.filter(f => f != favourite); + } + } +}; + +export const localStorageSaver = (path: string, key: string): Plugin => (store: Store) => { + utils.set(store.state, path, JSON.parse(window.localStorage.getItem(key) || '[]')); + + store.subscribe((mutation, state) => { + window.localStorage.setItem(key, JSON.stringify(utils.get(state, path))); + }) +}; + +export default favourites; \ No newline at end of file diff --git a/resources/ts/store/index.ts b/resources/ts/store/index.ts index f629d16..07ffcc8 100644 --- a/resources/ts/store/index.ts +++ b/resources/ts/store/index.ts @@ -1,10 +1,15 @@ import Vuex from 'vuex'; import messages from './messages'; -import departures from './departures'; +import departures from './departures' +import favourites, { localStorageSaver } from './favourites' + import { state, mutations, actions } from "./root"; export default new Vuex.Store({ state, mutations, actions, - modules: { messages, departures } + modules: { messages, departures, favourites }, + plugins: [ + localStorageSaver('favourites.favourites', 'favourites'), + ] }) \ No newline at end of file diff --git a/resources/ts/store/root.ts b/resources/ts/store/root.ts index a549eeb..2a108ad 100644 --- a/resources/ts/store/root.ts +++ b/resources/ts/store/root.ts @@ -17,9 +17,10 @@ export const state: RootState = { }; export const mutations: MutationTree = { - add: (state, stops) => state.stops = [...state.stops, ...ensureArray(stops)], - remove: (state, stop) => state.stops = state.stops.filter(s => s != stop), - clear: (state) => state.stops = [], + add: (state, stops) => state.stops = [...state.stops, ...ensureArray(stops)], + replace: (state, stops) => state.stops = stops, + remove: (state, stop) => state.stops = state.stops.filter(s => s != stop), + clear: (state) => state.stops = [], }; export const actions: ActionTree = { @@ -28,7 +29,7 @@ export const actions: ActionTree = { const response = await fetch(urls.prepare(urls.stops.all, { id: stops })); if (response.ok) { - commit('updateStops', await response.json()); + commit('replace', await response.json()); } } }, diff --git a/src/Controller/MainController.php b/src/Controller/MainController.php index fa34056..583322f 100644 --- a/src/Controller/MainController.php +++ b/src/Controller/MainController.php @@ -5,6 +5,7 @@ namespace App\Controller; use App\Provider\Provider; use App\Service\ProviderResolver; +use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Annotation\Route; class MainController extends Controller @@ -20,9 +21,15 @@ class MainController extends Controller /** * @Route("/{provider}", name="app") */ - public function app(Provider $provider) + public function app(Provider $provider, Request $request) { - return $this->render('app.html.twig', ['provider' => $provider]); + $state = json_decode($request->query->get('state', '{}'), true) ?: []; + $state = array_merge([ + 'version' => 1, + 'stops' => [] + ], $state); + + return $this->render('app.html.twig', compact('state', 'provider')); } /** diff --git a/src/Service/VersionExtension.php b/src/Service/VersionExtension.php new file mode 100644 index 0000000..d60c84c --- /dev/null +++ b/src/Service/VersionExtension.php @@ -0,0 +1,18 @@ + Komunikaty {{ '{{ messages.count }}' }} - - - +

@@ -47,14 +47,14 @@ Odjazdy

- - +

@@ -87,6 +87,17 @@ + + + +

+ + Dodaj do ulubionych +

+ +
    @@ -99,11 +110,34 @@
-

- - Wybierz przystanki -

- +
+ + +
+
+ + + + + + +

@@ -113,6 +147,9 @@ {% endblock %} diff --git a/templates/base.html.twig b/templates/base.html.twig index 2247e5a..9b6be8b 100644 --- a/templates/base.html.twig +++ b/templates/base.html.twig @@ -26,6 +26,10 @@
{% block footer %} + + czydojade logo + v. {{ version() }} + brought to you by kadet.net logo @@ -35,14 +39,6 @@
{% block javascripts %}{% endblock %} -