Basic Modal system
This commit is contained in:
parent
452a5c4993
commit
e0574615a7
@ -1,4 +1,4 @@
|
|||||||
<form class="favourite-add-form" @submit="save">
|
<form class="favourite-add-form" @submit.prevent="save">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="favourite_add_name">Nazwa</label>
|
<label for="favourite_add_name">Nazwa</label>
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
|
@ -35,8 +35,8 @@
|
|||||||
</fold>
|
</fold>
|
||||||
|
|
||||||
<keep-alive>
|
<keep-alive>
|
||||||
<popper reference="action-map" v-if="showMap" arrow class="popper--no-padding" style="width: 500px;" placement="right-start" v-hover:inMap>
|
<ui-dialog reference="action-map" v-if="showMap" arrow class="ui-popup--no-padding" style="width: 500px;" placement="right-start" v-hover:inMap>
|
||||||
<stop-map :stop="stop" style="height: 300px"/>
|
<stop-map :stop="stop" style="height: 300px"/>
|
||||||
</popper>
|
</ui-dialog>
|
||||||
</keep-alive>
|
</keep-alive>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,4 +0,0 @@
|
|||||||
<div :class="[ 'popper', arrow && 'popper--arrow' ]" v-on="$listeners">
|
|
||||||
<div class="popper__arrow" ref="arrow" v-if="arrow"></div>
|
|
||||||
<slot />
|
|
||||||
</div>
|
|
@ -1,9 +1,9 @@
|
|||||||
<fragment>
|
<fragment>
|
||||||
<portal to="popups">
|
<portal to="popups">
|
||||||
<transition name="tooltip">
|
<transition name="tooltip">
|
||||||
<popper class="popper--tooltip" aria-hidden="true" arrow :reference="root" :placement="placement" v-if="show" :responsive="false">
|
<ui-dialog class="ui-popup--tooltip" aria-hidden="true" arrow :reference="root" :placement="placement" v-if="show" :responsive="false">
|
||||||
<slot />
|
<slot />
|
||||||
</popper>
|
</ui-dialog>
|
||||||
</transition>
|
</transition>
|
||||||
</portal>
|
</portal>
|
||||||
<span ref="root" class="sr-only"><slot /></span>
|
<span ref="root" class="sr-only"><slot /></span>
|
||||||
|
29
resources/components/ui/dialog.html
Normal file
29
resources/components/ui/dialog.html
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<div class="ui-backdrop" @click="handleBackdropClick" v-if="currentBehaviour === 'modal'">
|
||||||
|
<div class="ui-modal" v-bind="$attrs">
|
||||||
|
<div class="ui-modal__top-bar">
|
||||||
|
<div class="ui-modal__header">
|
||||||
|
<slot name="header">
|
||||||
|
<div class="ui-modal__title">{{ title }}</div>
|
||||||
|
</slot>
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-action ui-modal__close" @click.prevent="handleCloseClick">
|
||||||
|
<ui-icon icon="close"/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<slot />
|
||||||
|
<div class="ui-modal__footer" v-if="hasFooter">
|
||||||
|
<slot name="footer" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div :class="[ 'ui-popup', arrow && 'ui-popup--arrow' ]" v-bind="$attrs" v-on="$listeners" v-else>
|
||||||
|
<div class="ui-popup__arrow" ref="arrow" v-if="arrow"></div>
|
||||||
|
<div class="ui-popup__header" v-if="hasHeader">
|
||||||
|
<slot name="header" />
|
||||||
|
</div>
|
||||||
|
<slot />
|
||||||
|
<div class="ui-popup__footer" v-if="hasFooter">
|
||||||
|
<slot name="footer" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
@ -1,5 +1,5 @@
|
|||||||
.map__label-box {
|
.map__label-box {
|
||||||
@extend .popper;
|
@extend .ui-popup;
|
||||||
|
|
||||||
padding: .5rem;
|
padding: .5rem;
|
||||||
background: white;
|
background: white;
|
||||||
|
@ -75,12 +75,24 @@ $grid-gutter-width: $spacer * 2;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@mixin position($position, $top: inherit, $right: inherit, $bottom: inherit, $left: inherit) {
|
||||||
|
$right: if($right == inherit, $top, $right);
|
||||||
|
$bottom: if($bottom == inherit, $top, $bottom);
|
||||||
|
$left: if($left == inherit, $right, $left);
|
||||||
|
|
||||||
|
position: $position;
|
||||||
|
|
||||||
|
top: $top;
|
||||||
|
right: $right;
|
||||||
|
left: $left;
|
||||||
|
bottom: $bottom;
|
||||||
|
}
|
||||||
|
|
||||||
@import "common";
|
@import "common";
|
||||||
@import "stop";
|
@import "stop";
|
||||||
@import "departure";
|
@import "departure";
|
||||||
@import "line";
|
@import "line";
|
||||||
@import "controls";
|
@import "controls";
|
||||||
@import "popper";
|
|
||||||
@import "animations";
|
@import "animations";
|
||||||
@import "form";
|
@import "form";
|
||||||
@import "favourites";
|
@import "favourites";
|
||||||
@ -89,6 +101,8 @@ $grid-gutter-width: $spacer * 2;
|
|||||||
@import "map";
|
@import "map";
|
||||||
|
|
||||||
@import "ui/switch";
|
@import "ui/switch";
|
||||||
|
@import "ui/popup";
|
||||||
|
@import "ui/modal";
|
||||||
|
|
||||||
@import "page/provider-picker";
|
@import "page/provider-picker";
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.provider-picker {
|
.provider-picker {
|
||||||
@extend .popper;
|
@extend .ui-popup;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
margin: 3rem;
|
margin: 3rem;
|
||||||
}
|
}
|
||||||
|
58
resources/styles/ui/_modal.scss
Normal file
58
resources/styles/ui/_modal.scss
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
.ui-backdrop {
|
||||||
|
@include position(fixed, 0);
|
||||||
|
background: rgba(black, .75);
|
||||||
|
padding: $spacer;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
overflow-y: auto;
|
||||||
|
overscroll-behavior-y: contain;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
height: 1rem;
|
||||||
|
display: block;
|
||||||
|
content: "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$dialog-margin: 1rem;
|
||||||
|
$dialog-sizes: (
|
||||||
|
medium: 480px,
|
||||||
|
small: 320px,
|
||||||
|
large: 640px,
|
||||||
|
)
|
||||||
|
;
|
||||||
|
|
||||||
|
.ui-modal {
|
||||||
|
padding: $dialog-margin;
|
||||||
|
background: white;
|
||||||
|
margin: auto;
|
||||||
|
box-shadow: rgba(black, .7) 0 1px 3px;
|
||||||
|
border-radius: 1px;
|
||||||
|
|
||||||
|
@each $size, $width in $dialog-sizes {
|
||||||
|
.ui-modal--#{$size} {
|
||||||
|
width: $width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-modal__close {
|
||||||
|
margin-right: -$dialog-margin;
|
||||||
|
padding: $dialog-margin $dialog-margin 0;
|
||||||
|
margin-top: -$dialog-margin;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-modal__header {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-modal__title {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-modal__top-bar {
|
||||||
|
display: flex;
|
||||||
|
margin-bottom: $dialog-margin * 0.75;
|
||||||
|
}
|
@ -54,7 +54,7 @@
|
|||||||
@mixin triangle-left($size, $color, $border: none) { @include triangle(left, $size, $color, $border); }
|
@mixin triangle-left($size, $color, $border: none) { @include triangle(left, $size, $color, $border); }
|
||||||
@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 {
|
.ui-popup {
|
||||||
$arrow-base: 8px;
|
$arrow-base: 8px;
|
||||||
$arrow-color: white;
|
$arrow-color: white;
|
||||||
$arrow-border: rgba(black, 0.2);
|
$arrow-border: rgba(black, 0.2);
|
||||||
@ -74,17 +74,17 @@
|
|||||||
|
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
|
|
||||||
.popper__arrow {
|
.ui-popup__arrow {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 0;
|
width: 0;
|
||||||
height: 0;
|
height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.popper--no-padding {
|
&.ui-popup--no-padding {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.popper__heading {
|
.ui-popup__heading {
|
||||||
font-size: $font-size-sm;
|
font-size: $font-size-sm;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
margin-bottom: .5rem;
|
margin-bottom: .5rem;
|
||||||
@ -105,7 +105,7 @@
|
|||||||
&[x-placement*="#{$placement}"] {
|
&[x-placement*="#{$placement}"] {
|
||||||
margin-#{map-get($opposite, $placement)}: $arrow-base;
|
margin-#{map-get($opposite, $placement)}: $arrow-base;
|
||||||
|
|
||||||
.popper__arrow {
|
.ui-popup__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);
|
||||||
}
|
}
|
||||||
@ -119,11 +119,11 @@
|
|||||||
@include placement("bottom");
|
@include placement("bottom");
|
||||||
}
|
}
|
||||||
|
|
||||||
&.popper--arrow {
|
&.ui-popup--arrow {
|
||||||
@include arrows;
|
@include arrows;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.popper--tooltip {
|
&.ui-popup--tooltip {
|
||||||
background: $dark;
|
background: $dark;
|
||||||
color: white;
|
color: white;
|
||||||
padding: .5rem .75rem;
|
padding: .5rem .75rem;
|
||||||
@ -132,14 +132,14 @@
|
|||||||
min-width: 0;
|
min-width: 0;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
|
|
||||||
&.popper--arrow {
|
&.ui-popup--arrow {
|
||||||
$arrow-color: $dark;
|
$arrow-color: $dark;
|
||||||
$arrow-border: none;
|
$arrow-border: none;
|
||||||
$arrow-base: 6px;
|
$arrow-base: 6px;
|
||||||
|
|
||||||
@include arrows;
|
@include arrows;
|
||||||
|
|
||||||
.popper__arrow::before {
|
.ui-popup__arrow::before {
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -147,7 +147,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@include media-breakpoint-down('sm') {
|
@include media-breakpoint-down('sm') {
|
||||||
.popper {
|
.ui-popup {
|
||||||
margin-left: $spacer;
|
margin-left: $spacer;
|
||||||
margin-right: $spacer;
|
margin-right: $spacer;
|
||||||
}
|
}
|
@ -31,10 +31,14 @@ Vue.use(VueMoment, { moment });
|
|||||||
declare module 'vue/types/vue' {
|
declare module 'vue/types/vue' {
|
||||||
interface Vue {
|
interface Vue {
|
||||||
$isTouch: boolean;
|
$isTouch: boolean;
|
||||||
|
$hasSlot: (slot: string) => string;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Vue.prototype.$isTouch = 'ontouchstart' in window || navigator.msMaxTouchPoints > 0;
|
Vue.prototype.$isTouch = 'ontouchstart' in window || navigator.msMaxTouchPoints > 0;
|
||||||
|
Vue.prototype.$hasSlot = function (this: Vue, slot: string): boolean {
|
||||||
|
return !!this.$slots[slot] || !!this.$scopedSlots[slot];
|
||||||
|
}
|
||||||
|
|
||||||
Component.registerHooks(['removed']);
|
Component.registerHooks(['removed']);
|
||||||
|
|
||||||
|
187
resources/ts/components/ui/dialog.ts
Normal file
187
resources/ts/components/ui/dialog.ts
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
import Vue from "vue";
|
||||||
|
import { Component, Prop, Watch } from "vue-property-decorator";
|
||||||
|
import Popper, { Placement } from "popper.js";
|
||||||
|
import { defaultBreakpoints } from "../../filters";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How popup will be presented to user:
|
||||||
|
* - "modal" - modal window
|
||||||
|
* - "popup" - simple popup
|
||||||
|
*/
|
||||||
|
export type DialogBehaviour = "modal" | "popup";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
template: require('../../../components/ui/dialog.html'),
|
||||||
|
inheritAttrs: false,
|
||||||
|
})
|
||||||
|
export default class UiDialog extends Vue {
|
||||||
|
@Prop({ type: String, default: "popup" })
|
||||||
|
private behaviour: DialogBehaviour;
|
||||||
|
|
||||||
|
@Prop({ type: String })
|
||||||
|
private mobileBehaviour: DialogBehaviour;
|
||||||
|
|
||||||
|
@Prop([String, HTMLElement])
|
||||||
|
public reference: string | HTMLElement;
|
||||||
|
|
||||||
|
@Prop(Object)
|
||||||
|
public refs: string;
|
||||||
|
|
||||||
|
@Prop({ type: String, default: "auto" })
|
||||||
|
public placement: Placement;
|
||||||
|
|
||||||
|
@Prop(Boolean)
|
||||||
|
public arrow: boolean;
|
||||||
|
|
||||||
|
@Prop({ type: Boolean, default: true })
|
||||||
|
public responsive: boolean;
|
||||||
|
|
||||||
|
@Prop(String)
|
||||||
|
public title: string;
|
||||||
|
|
||||||
|
private isMobile: boolean = false;
|
||||||
|
|
||||||
|
private _focusOutEvent;
|
||||||
|
private _resizeEvent;
|
||||||
|
|
||||||
|
private _popper;
|
||||||
|
|
||||||
|
get currentBehaviour(): DialogBehaviour {
|
||||||
|
if (!this.mobileBehaviour) {
|
||||||
|
return this.behaviour;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.isMobile ? this.mobileBehaviour : this.behaviour;
|
||||||
|
}
|
||||||
|
|
||||||
|
get hasFooter() {
|
||||||
|
return this.$hasSlot('footer')
|
||||||
|
}
|
||||||
|
|
||||||
|
get hasHeader() {
|
||||||
|
return this.$hasSlot('header')
|
||||||
|
}
|
||||||
|
|
||||||
|
private getReferenceElement() {
|
||||||
|
const isInWrapper = this.$parent.$options.name == 'portalTarget';
|
||||||
|
|
||||||
|
if (typeof this.reference === 'string') {
|
||||||
|
if (this.refs) {
|
||||||
|
return this.refs[this.reference];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isInWrapper) {
|
||||||
|
return this.$parent.$parent.$refs[this.reference];
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.$parent.$refs[this.reference];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.reference instanceof HTMLElement) {
|
||||||
|
return this.reference;
|
||||||
|
}
|
||||||
|
|
||||||
|
return isInWrapper ? this.$parent.$el : this.$el.parentElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
focusOut(event: MouseEvent) {
|
||||||
|
if (this.$el.contains(event.target as Node)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$emit('leave', event);
|
||||||
|
}
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
this.handleWindowResize();
|
||||||
|
|
||||||
|
if (this.behaviour === 'popup') {
|
||||||
|
this.initPopper();
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('resize', this._resizeEvent = this.handleWindowResize.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
private initPopper() {
|
||||||
|
const reference = this.getReferenceElement();
|
||||||
|
|
||||||
|
this._popper = new Popper(reference, this.$el, {
|
||||||
|
placement: this.placement,
|
||||||
|
modifiers: {
|
||||||
|
arrow: { enabled: this.arrow, element: this.$refs['arrow'] as Element },
|
||||||
|
responsive: {
|
||||||
|
enabled: this.responsive,
|
||||||
|
order: 890,
|
||||||
|
fn(data) {
|
||||||
|
if (window.innerWidth < 560) {
|
||||||
|
data.instance.options.placement = 'top';
|
||||||
|
data.styles.transform = `translate3d(0, ${ data.offsets.popper.top }px, 0)`;
|
||||||
|
data.styles.right = '0';
|
||||||
|
data.styles.left = '0';
|
||||||
|
data.styles.width = 'auto';
|
||||||
|
data.arrowStyles.left = `${ data.offsets.popper.left + data.offsets.arrow.left }px`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this._popper && this._popper.update();
|
||||||
|
document.addEventListener('click', this._focusOutEvent = this.focusOut.bind(this), { capture: true });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private removePopper() {
|
||||||
|
this._popper.destroy()
|
||||||
|
this._popper = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
updated() {
|
||||||
|
if (this._popper) {
|
||||||
|
this._popper.update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeDestroy() {
|
||||||
|
this._focusOutEvent && document.removeEventListener('click', this._focusOutEvent, { capture: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
removed() {
|
||||||
|
if (this._popper) {
|
||||||
|
this.removePopper();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleBackdropClick(ev: Event) {
|
||||||
|
const target = ev.target as HTMLElement;
|
||||||
|
|
||||||
|
if (target.classList.contains("ui-backdrop")) {
|
||||||
|
this.$emit('leave');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleCloseClick() {
|
||||||
|
this.$emit('leave');
|
||||||
|
this.$emit('close');
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleWindowResize() {
|
||||||
|
this.isMobile = screen.width < defaultBreakpoints.md;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Watch('currentBehaviour')
|
||||||
|
private handleBehaviourChange(newBehaviour: DialogBehaviour, oldBehaviour: DialogBehaviour) {
|
||||||
|
if (oldBehaviour === 'popup') {
|
||||||
|
this.removePopper();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newBehaviour === 'popup') {
|
||||||
|
this.$nextTick(() => this.initPopper());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Vue.component("ui-dialog", UiDialog);
|
@ -7,7 +7,8 @@ import {
|
|||||||
faCheck,
|
faCheck,
|
||||||
faCheckDouble,
|
faCheckDouble,
|
||||||
faChevronCircleUp,
|
faChevronCircleUp,
|
||||||
faChevronDown, faChevronUp,
|
faChevronDown,
|
||||||
|
faChevronUp,
|
||||||
faClock,
|
faClock,
|
||||||
faCog,
|
faCog,
|
||||||
faExclamationTriangle,
|
faExclamationTriangle,
|
||||||
@ -15,7 +16,8 @@ import {
|
|||||||
faInfoCircle,
|
faInfoCircle,
|
||||||
faMapMarkerAlt,
|
faMapMarkerAlt,
|
||||||
faMoon,
|
faMoon,
|
||||||
faQuestionCircle, faQuestionSquare,
|
faQuestionCircle,
|
||||||
|
faQuestionSquare,
|
||||||
faSearch,
|
faSearch,
|
||||||
faSign,
|
faSign,
|
||||||
faStar,
|
faStar,
|
||||||
@ -23,7 +25,13 @@ import {
|
|||||||
faTimes,
|
faTimes,
|
||||||
faTrashAlt
|
faTrashAlt
|
||||||
} from "@fortawesome/pro-light-svg-icons";
|
} from "@fortawesome/pro-light-svg-icons";
|
||||||
import { faClock as faClockBold, faCodeCommit, faMinus, faPlus, faSpinnerThird } from "@fortawesome/pro-regular-svg-icons";
|
import {
|
||||||
|
faClock as faClockBold,
|
||||||
|
faCodeCommit,
|
||||||
|
faMinus,
|
||||||
|
faPlus,
|
||||||
|
faSpinnerThird
|
||||||
|
} from "@fortawesome/pro-regular-svg-icons";
|
||||||
import { faExclamationTriangle as faSolidExclamationTriangle, faWalking } from "@fortawesome/pro-solid-svg-icons";
|
import { faExclamationTriangle as faSolidExclamationTriangle, faWalking } from "@fortawesome/pro-solid-svg-icons";
|
||||||
import { fac } from "../../icons";
|
import { fac } from "../../icons";
|
||||||
import { FontAwesomeIcon, FontAwesomeLayers, FontAwesomeLayersText } from "@fortawesome/vue-fontawesome";
|
import { FontAwesomeIcon, FontAwesomeLayers, FontAwesomeLayersText } from "@fortawesome/vue-fontawesome";
|
||||||
@ -88,6 +96,7 @@ const definitions: Dictionary<Icon> = {
|
|||||||
{icon: faClockBold},
|
{icon: faClockBold},
|
||||||
{icon: faSolidExclamationTriangle, transform: "shrink-5 down-4 right-6"}
|
{icon: faSolidExclamationTriangle, transform: "shrink-5 down-4 right-6"}
|
||||||
]),
|
]),
|
||||||
|
'close': simple(faTimes),
|
||||||
...lineTypeIcons,
|
...lineTypeIcons,
|
||||||
...messageTypeIcons,
|
...messageTypeIcons,
|
||||||
};
|
};
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
export * from './switch';
|
export * from './switch';
|
||||||
export * from './icon';
|
export * from './icon';
|
||||||
export * from './numeric-input'
|
export * from './numeric-input'
|
||||||
|
export * from './dialog'
|
||||||
|
@ -1,111 +1,6 @@
|
|||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import { Component, Prop, Watch } from "vue-property-decorator";
|
import { Component, Prop, Watch } from "vue-property-decorator";
|
||||||
import Popper, { Placement } from "popper.js";
|
|
||||||
import vueRemovedHookMixin from "vue-removed-hook-mixin";
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
template: require("../../components/popper.html"),
|
|
||||||
mixins: [ vueRemovedHookMixin ]
|
|
||||||
})
|
|
||||||
export class PopperComponent extends Vue {
|
|
||||||
@Prop([ String, HTMLElement ])
|
|
||||||
public reference: string | HTMLElement;
|
|
||||||
|
|
||||||
@Prop(Object)
|
|
||||||
public refs: string;
|
|
||||||
|
|
||||||
@Prop({ type: String, default: "auto" })
|
|
||||||
public placement: Placement;
|
|
||||||
|
|
||||||
@Prop(Boolean)
|
|
||||||
public arrow: boolean;
|
|
||||||
|
|
||||||
@Prop({ type: Boolean, default: true })
|
|
||||||
public responsive: boolean;
|
|
||||||
|
|
||||||
private _event;
|
|
||||||
private _popper;
|
|
||||||
|
|
||||||
private getReferenceElement() {
|
|
||||||
const isInPortal = this.$parent.$options.name == 'portalTarget';
|
|
||||||
|
|
||||||
if (typeof this.reference === 'string') {
|
|
||||||
if (this.refs) {
|
|
||||||
return this.refs[this.reference];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isInPortal) {
|
|
||||||
return this.$parent.$parent.$refs[this.reference];
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.$parent.$refs[this.reference];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.reference instanceof HTMLElement) {
|
|
||||||
return this.reference;
|
|
||||||
}
|
|
||||||
|
|
||||||
return isInPortal ? this.$parent.$el : this.$el.parentElement;
|
|
||||||
}
|
|
||||||
|
|
||||||
focusOut(event: MouseEvent) {
|
|
||||||
if (this.$el.contains(event.target as Node)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.$emit('leave', event);
|
|
||||||
}
|
|
||||||
|
|
||||||
mounted() {
|
|
||||||
const reference = this.getReferenceElement();
|
|
||||||
|
|
||||||
this._popper = new Popper(reference, this.$el, {
|
|
||||||
placement: this.placement,
|
|
||||||
modifiers: {
|
|
||||||
arrow: { enabled: this.arrow, element: this.$refs['arrow'] as Element },
|
|
||||||
responsive: {
|
|
||||||
enabled: this.responsive,
|
|
||||||
order: 890,
|
|
||||||
fn(data) {
|
|
||||||
if (window.innerWidth < 560) {
|
|
||||||
data.instance.options.placement = 'top';
|
|
||||||
data.styles.transform = `translate3d(0, ${data.offsets.popper.top}px, 0)`;
|
|
||||||
data.styles.right = '0';
|
|
||||||
data.styles.left = '0';
|
|
||||||
data.styles.width = 'auto';
|
|
||||||
data.arrowStyles.left = `${data.offsets.popper.left + data.offsets.arrow.left}px`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this._popper.update();
|
|
||||||
document.addEventListener('click', this._event = this.focusOut.bind(this), { capture: true });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
updated() {
|
|
||||||
this._popper.update();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Watch('visible')
|
|
||||||
private onVisibilityUpdate() {
|
|
||||||
this._popper.update();
|
|
||||||
window.dispatchEvent(new Event('resize'));
|
|
||||||
}
|
|
||||||
|
|
||||||
beforeDestroy() {
|
|
||||||
this._event && document.removeEventListener('click', this._event, { capture: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
removed() {
|
|
||||||
this._popper.destroy()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({ template: require('../../components/fold.html') })
|
@Component({ template: require('../../components/fold.html') })
|
||||||
export class FoldComponent extends Vue {
|
export class FoldComponent extends Vue {
|
||||||
@ -151,7 +46,6 @@ export class LazyComponent extends Vue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Vue.component('Popper', PopperComponent);
|
|
||||||
Vue.component('Fold', FoldComponent);
|
Vue.component('Fold', FoldComponent);
|
||||||
Vue.component('Lazy', LazyComponent);
|
Vue.component('Lazy', LazyComponent);
|
||||||
|
|
||||||
|
@ -2,6 +2,14 @@ import { set, signed } from "./utils";
|
|||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import { condition } from "./decorators";
|
import { condition } from "./decorators";
|
||||||
|
|
||||||
|
export const defaultBreakpoints = {
|
||||||
|
'xs': 0,
|
||||||
|
'sm': 576,
|
||||||
|
'md': 768,
|
||||||
|
'lg': 1024,
|
||||||
|
'xl': 1200,
|
||||||
|
}
|
||||||
|
|
||||||
Vue.filter('signed', signed);
|
Vue.filter('signed', signed);
|
||||||
|
|
||||||
Vue.directive('hover', {
|
Vue.directive('hover', {
|
||||||
@ -61,13 +69,7 @@ Vue.directive('autofocus', {
|
|||||||
|
|
||||||
Vue.directive('responsive', {
|
Vue.directive('responsive', {
|
||||||
inserted(el, binding) {
|
inserted(el, binding) {
|
||||||
const breakpoints = typeof binding.value === 'object' ? binding.value : {
|
const breakpoints = typeof binding.value === 'object' ? binding.value : defaultBreakpoints;
|
||||||
'xs': 0,
|
|
||||||
'sm': 576,
|
|
||||||
'md': 768,
|
|
||||||
'lg': 1024,
|
|
||||||
'xl': 1200,
|
|
||||||
};
|
|
||||||
|
|
||||||
const resize = binding['resize'] = () => {
|
const resize = binding['resize'] = () => {
|
||||||
const width = el.scrollWidth;
|
const width = el.scrollWidth;
|
||||||
|
@ -29,9 +29,9 @@
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
<portal to="popups">
|
<portal to="popups">
|
||||||
<popper reference="settings-messages" v-if="visibility.messages" arrow placement="left-start" @leave="visibility.messages = false">
|
<ui-dialog reference="settings-messages" v-if="visibility.messages" arrow placement="left-start" @leave="visibility.messages = false">
|
||||||
<settings-messages></settings-messages>
|
<settings-messages></settings-messages>
|
||||||
</popper>
|
</ui-dialog>
|
||||||
</portal>
|
</portal>
|
||||||
</header>
|
</header>
|
||||||
<fold :visible="sections.messages">
|
<fold :visible="sections.messages">
|
||||||
@ -54,9 +54,9 @@
|
|||||||
<ui-icon icon="refresh" :spin="departures.state === 'fetching'" fixed-width></ui-icon>
|
<ui-icon icon="refresh" :spin="departures.state === 'fetching'" fixed-width></ui-icon>
|
||||||
</button>
|
</button>
|
||||||
<portal to="popups">
|
<portal to="popups">
|
||||||
<popper reference="settings-departures" v-if="visibility.departures" arrow placement="left-start" @leave="visibility.departures = false">
|
<ui-dialog reference="settings-departures" v-if="visibility.departures" @leave="visibility.departures = false" arrow placement="left-start">
|
||||||
<settings-departures></settings-departures>
|
<settings-departures></settings-departures>
|
||||||
</popper>
|
</ui-dialog>
|
||||||
</portal>
|
</portal>
|
||||||
</header>
|
</header>
|
||||||
<departures :stops="stops" v-if="stops.length > 0"></departures>
|
<departures :stops="stops" v-if="stops.length > 0"></departures>
|
||||||
@ -105,9 +105,11 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<popper reference="save" v-if="visibility.save" arrow tabindex="-1" @leave="visibility.save = false" placement="bottom-end">
|
<portal to="popups">
|
||||||
<favourites-adder @saved="visibility.save = false"/>
|
<ui-dialog reference="settings-messages" v-if="visibility.messages" arrow placement="left-start" @leave="visibility.messages = false">
|
||||||
</popper>
|
<favourites-adder @saved="visibility.save = false"/>
|
||||||
|
</ui-dialog>
|
||||||
|
</portal>
|
||||||
</section>
|
</section>
|
||||||
<section class="section picker">
|
<section class="section picker">
|
||||||
<header class="section__title flex">
|
<header class="section__title flex">
|
||||||
|
Loading…
Reference in New Issue
Block a user