From 9802473d7cc3fa5cde54d901e4a95f4ac3340101 Mon Sep 17 00:00:00 2001 From: Kacper Donat Date: Sun, 19 Jan 2020 18:03:38 +0100 Subject: [PATCH] Refactor popups --- .gitignore | 4 +- package.json | 1 + resources/components/favourites/save.html | 20 ++++-- resources/components/finder.html | 4 +- resources/components/picker/stop.html | 10 ++- resources/components/popper.html | 9 +-- resources/components/stop/map.html | 12 ++-- resources/styles/_animations.scss | 2 +- resources/styles/_common.scss | 4 ++ resources/styles/_fabourites.scss | 5 ++ resources/styles/_form.scss | 7 +++ resources/styles/_popper.scss | 7 +++ resources/styles/main.scss | 4 +- resources/ts/app.ts | 2 + resources/ts/components/favourites.ts | 25 +++++++- resources/ts/components/picker.ts | 5 ++ resources/ts/components/utils.ts | 52 +++++++++++----- resources/ts/filters.ts | 14 +++++ .../ZtmGdanskDepartureRepository.php | 8 ++- templates/app.html.twig | 61 ++++++++++--------- yarn.lock | 5 ++ 21 files changed, 184 insertions(+), 77 deletions(-) create mode 100644 resources/styles/_fabourites.scss create mode 100644 resources/styles/_form.scss diff --git a/.gitignore b/.gitignore index 7e5ef76..429897d 100644 --- a/.gitignore +++ b/.gitignore @@ -14,4 +14,6 @@ /.idea/ /public/* !/public/index.php -!/public/manifest.json \ No newline at end of file +!/public/manifest.jso + +/yarn-error.log diff --git a/package.json b/package.json index 64448c9..4ecf3e5 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "copy-webpack-plugin": "^4.5.2", "imagemin-webpack-plugin": "^2.3.0", "mini-css-extract-plugin": "^0.4.2", + "portal-vue": "^2.1.7", "vue2-leaflet": "^1.0.2", "vuex": "^3.0.1", "vuex-class": "^0.3.1", diff --git a/resources/components/favourites/save.html b/resources/components/favourites/save.html index 0d17625..cf6d8f6 100644 --- a/resources/components/favourites/save.html +++ b/resources/components/favourites/save.html @@ -1,6 +1,14 @@ -
- - -
+
+ +
+ + +
+

{{ error }}

+
+
+
diff --git a/resources/components/finder.html b/resources/components/finder.html index cf7a027..e56e801 100644 --- a/resources/components/finder.html +++ b/resources/components/finder.html @@ -1,8 +1,8 @@
- +
- +
diff --git a/resources/components/picker/stop.html b/resources/components/picker/stop.html index d18b9d1..1245508 100644 --- a/resources/components/picker/stop.html +++ b/resources/components/picker/stop.html @@ -1,8 +1,6 @@
- -
diff --git a/resources/components/popper.html b/resources/components/popper.html index ed83d7a..abb20fa 100644 --- a/resources/components/popper.html +++ b/resources/components/popper.html @@ -1,7 +1,4 @@ -
+
- - - - -
\ No newline at end of file + +
diff --git a/resources/components/stop/map.html b/resources/components/stop/map.html index 7c80252..28051dc 100644 --- a/resources/components/stop/map.html +++ b/resources/components/stop/map.html @@ -1,5 +1,7 @@ - - - - +
+ + + + +
diff --git a/resources/styles/_animations.scss b/resources/styles/_animations.scss index fc4a7d0..8fa563a 100644 --- a/resources/styles/_animations.scss +++ b/resources/styles/_animations.scss @@ -35,4 +35,4 @@ margin-left: -100%; } } -} \ No newline at end of file +} diff --git a/resources/styles/_common.scss b/resources/styles/_common.scss index 9e8c0ce..e58e558 100644 --- a/resources/styles/_common.scss +++ b/resources/styles/_common.scss @@ -109,3 +109,7 @@ svg.svg-inline--fa { .icon { padding: .5rem 0.75rem; } + +.invalid-feedback p { + margin-bottom: 0; +} diff --git a/resources/styles/_fabourites.scss b/resources/styles/_fabourites.scss new file mode 100644 index 0000000..0e7609a --- /dev/null +++ b/resources/styles/_fabourites.scss @@ -0,0 +1,5 @@ +@include media-breakpoint-up('sm') { + .favourite-add-form { + width: 250px; + } +} diff --git a/resources/styles/_form.scss b/resources/styles/_form.scss new file mode 100644 index 0000000..37df9bb --- /dev/null +++ b/resources/styles/_form.scss @@ -0,0 +1,7 @@ +label { + font-weight: bold; + font-size: .8rem; + margin-bottom: 0; + margin-top: -0.2rem; + display: block; +} diff --git a/resources/styles/_popper.scss b/resources/styles/_popper.scss index 1168ad0..8a1110a 100644 --- a/resources/styles/_popper.scss +++ b/resources/styles/_popper.scss @@ -114,3 +114,10 @@ @include placement("bottom"); } } + +@include media-breakpoint-down('sm') { + .popper { + margin-left: $spacer; + margin-right: $spacer; + } +} diff --git a/resources/styles/main.scss b/resources/styles/main.scss index 16ac380..653bc87 100644 --- a/resources/styles/main.scss +++ b/resources/styles/main.scss @@ -10,7 +10,6 @@ $primary: #005ea8; $custom-control-indicator-checked-bg: $dark; $custom-control-indicator-active-bg: $dark; - $line-types: ( 'trolleybus': #419517, 'tram': #cd2e12, @@ -27,6 +26,7 @@ $headings-margin-bottom: $default-spacing; $container-max-widths: map-merge($container-max-widths, ( xl: 1320px )); $link-color: #005ea8; +$grid-gutter-width: $spacer * 2; @import "~bootstrap/scss/bootstrap"; @@ -49,6 +49,8 @@ $link-color: #005ea8; @import "controls"; @import "popper"; @import "animations"; +@import "form"; +@import "fabourites"; body { min-height: 100vh; diff --git a/resources/ts/app.ts b/resources/ts/app.ts index 2adc0ca..bb4087c 100644 --- a/resources/ts/app.ts +++ b/resources/ts/app.ts @@ -13,9 +13,11 @@ window['Popper'] = Popper; // dependencies import Vue from "vue"; import Vuex from 'vuex'; +import PortalVue from 'portal-vue'; import { Workbox } from "workbox-window"; Vue.use(Vuex); +Vue.use(PortalVue); // async dependencies (async function () { diff --git a/resources/ts/components/favourites.ts b/resources/ts/components/favourites.ts index b904681..68f9aaa 100644 --- a/resources/ts/components/favourites.ts +++ b/resources/ts/components/favourites.ts @@ -19,6 +19,7 @@ export class FavouritesComponent extends Vue { @Component({ template: require('../../components/favourites/save.html' )}) export class FavouritesAdderComponent extends Vue { private name = ""; + private errors = { name: [] }; @Mutation add: (fav: Favourite) => void; @@ -28,10 +29,28 @@ export class FavouritesAdderComponent extends Vue { const favourite: Favourite = { name, state }; - this.add(favourite); - this.name = ''; + if (this.validate(favourite)) { + this.add(favourite); + this.name = ''; - this.$emit('saved', favourite); + this.$emit('saved', favourite); + } + } + + private validate(favourite: Favourite) { + let errors = { name: [] }; + + if (favourite.name.length == 0) { + errors.name.push("Musisz podać nazwę."); + } + + if (this.$store.state.favourites.favourites.filter(other => other.name == favourite.name).length > 0) { + errors.name.push("Istnieje już zapisana grupa przystanków o takiej nazwie."); + } + + this.errors = errors; + + return Object.entries(errors).map(a => a[1]).reduce((acc, cur) => [ ...acc, ...cur ]).length == 0; } } diff --git a/resources/ts/components/picker.ts b/resources/ts/components/picker.ts index 394541d..f11b5a1 100644 --- a/resources/ts/components/picker.ts +++ b/resources/ts/components/picker.ts @@ -13,6 +13,11 @@ export class PickerStopComponent extends Vue { details: boolean = false; map: boolean = false; + inMap: boolean = false; + + get showMap() { + return this.inMap || this.map; + } } @Component({ diff --git a/resources/ts/components/utils.ts b/resources/ts/components/utils.ts index a3f216e..a2e12b6 100644 --- a/resources/ts/components/utils.ts +++ b/resources/ts/components/utils.ts @@ -1,35 +1,37 @@ import Vue from 'vue'; import { Component, Prop, Watch } from "vue-property-decorator"; import Popper, { Placement } from "popper.js"; +import { Portal } from "portal-vue"; -@Component({ template: require("../../components/popper.html") }) +@Component({ + template: require("../../components/popper.html") +}) export class PopperComponent extends Vue { @Prop(String) public reference: string; + @Prop(Object) + public refs: string; + @Prop({ type: String, default: "auto" }) public placement: Placement; @Prop(Boolean) public arrow: boolean; - @Prop({ type: Boolean, default: false }) - public visible: boolean; - - @Prop(Boolean) - public lazy: boolean; - - public hovered: boolean = false; - public focused: boolean = false; - + private _event; private _popper; - get show() { - return this.visible || this.hovered || this.focused; + focusOut(event: MouseEvent) { + if (this.$el.contains(event.target as Node)) { + return; + } + + this.$emit('leave', event); } mounted() { - const reference = this.$parent.$refs[this.reference] as HTMLElement; + const reference = this.refsSource[this.reference] as HTMLElement; this._popper = new Popper(reference, this.$el, { placement: this.placement, @@ -42,8 +44,6 @@ export class PopperComponent extends Vue { if (window.innerWidth < 560) { data.instance.options.placement = 'bottom'; data.styles.transform = `translate3d(0, ${data.offsets.popper.top}px, 0)`; - data.styles.width = '100%'; - data.styles.margin = '0'; data.styles.right = '0'; data.styles.left = '0'; data.styles.width = 'auto'; @@ -56,13 +56,20 @@ export class PopperComponent extends Vue { } }); - this.$nextTick(() => this._popper.update()) + this.$nextTick(() => { + this._popper.update(); + document.addEventListener('click', this._event = this.focusOut.bind(this), { capture: true }); + }); } updated() { this._popper.update(); } + get listeners() { + return { ...this.$listeners, focusout: this.focusOut } + } + @Watch('visible') private onVisibilityUpdate() { this._popper.update(); @@ -71,6 +78,19 @@ export class PopperComponent extends Vue { beforeDestroy() { this._popper.destroy(); + this._event && document.removeEventListener('click', this._event, { capture: true }); + } + + get refsSource() { + if (this.refs) { + return this.refs; + } + + if (this.$parent.$options.name == 'portalTarget') { + return this.$parent.$parent.$refs; + } + + return this.$parent.$refs } } diff --git a/resources/ts/filters.ts b/resources/ts/filters.ts index 44c5ae1..3a0e040 100644 --- a/resources/ts/filters.ts +++ b/resources/ts/filters.ts @@ -45,6 +45,20 @@ Vue.directive('hover', { } }); +Vue.directive('autofocus', { + inserted(el, binding) { + if (binding.value !== undefined) { + const value = binding.value; + + if ((typeof value === "boolean" && !value) || (typeof value === "function" && !value(el))) { + return; + } + } + + el.focus(); + } +}); + Vue.directive('responsive', { inserted(el, binding) { const breakpoints = typeof binding.value === 'object' ? binding.value : { diff --git a/src/Provider/ZtmGdansk/ZtmGdanskDepartureRepository.php b/src/Provider/ZtmGdansk/ZtmGdanskDepartureRepository.php index 4908aad..e6faedf 100644 --- a/src/Provider/ZtmGdansk/ZtmGdanskDepartureRepository.php +++ b/src/Provider/ZtmGdansk/ZtmGdanskDepartureRepository.php @@ -51,7 +51,13 @@ class ZtmGdanskDepartureRepository implements DepartureRepository private function getRealDepartures(Stop $stop) { - $estimates = json_decode(file_get_contents(static::ESTIMATES_URL . "?stopId=" . $stop->getId()), true)['delay']; + try { + $estimates = file_get_contents(static::ESTIMATES_URL . "?stopId=" . $stop->getId()); + $estimates = json_decode($estimates, true)['delay']; + } catch (\Error $e) { + return collect(); + } + $estimates = collect($estimates); $lines = $estimates->map(function ($delay) { diff --git a/templates/app.html.twig b/templates/app.html.twig index 6d6061c..32753f6 100644 --- a/templates/app.html.twig +++ b/templates/app.html.twig @@ -11,7 +11,7 @@ Komunikaty {{ '{{ messages.count }}' }} - - +

@@ -47,26 +47,27 @@ Odjazdy

- - - -

- - - -

-
- co - - - s -
-
+ + +

+ + + +

+
+ co + + + s +
+
+
{% if provider.attribution %} @@ -87,17 +88,6 @@ - - - -

- - Dodaj do ulubionych -

- -
    @@ -108,6 +98,17 @@
+ +
+ +
+ + + +
@@ -130,7 +131,7 @@
-
+
@@ -141,6 +142,8 @@
+ + {% endblock %} {% block javascripts %} diff --git a/yarn.lock b/yarn.lock index 61c6539..5d6839c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4782,6 +4782,11 @@ popper.js@*, popper.js@^1.14.1, popper.js@^1.14.4: resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.16.0.tgz#2e1816bcbbaa518ea6c2e15a466f4cb9c6e2fbb3" integrity sha512-+G+EkOPoE5S/zChTpmBSSDYmhXJ5PsW8eMhH8cP/CQHMFPBG/kC9Y5IIw6qNYgdJ+/COf0ddY2li28iHaZRSjw== +portal-vue@^2.1.7: + version "2.1.7" + resolved "https://registry.yarnpkg.com/portal-vue/-/portal-vue-2.1.7.tgz#ea08069b25b640ca08a5b86f67c612f15f4e4ad4" + integrity sha512-+yCno2oB3xA7irTt0EU5Ezw22L2J51uKAacE/6hMPMoO/mx3h4rXFkkBkT4GFsMDv/vEe8TNKC3ujJJ0PTwb6g== + posix-character-classes@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab"