Refine modal system
This commit is contained in:
parent
e0574615a7
commit
3bc176936c
@ -21,7 +21,7 @@
|
|||||||
<slot name="actions">
|
<slot name="actions">
|
||||||
<button class="btn btn-action" ref="action-info" @click="details = !details">
|
<button class="btn btn-action" ref="action-info" @click="details = !details">
|
||||||
<tooltip>dodatkowe informacje</tooltip>
|
<tooltip>dodatkowe informacje</tooltip>
|
||||||
<ui-icon :icon="details ? 'info-hide' : 'info'"/>
|
<ui-icon icon="info"/>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button class="btn btn-action" ref="action-map" v-hover:map>
|
<button class="btn btn-action" ref="action-map" v-hover:map>
|
||||||
@ -30,11 +30,16 @@
|
|||||||
</slot>
|
</slot>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<fold :visible="details" class="stop__details-fold" lazy>
|
|
||||||
<stop-details :stop="stop"/>
|
|
||||||
</fold>
|
|
||||||
|
|
||||||
<keep-alive>
|
<keep-alive>
|
||||||
|
<portal to="popups">
|
||||||
|
<ui-dialog v-if="details" @leave="details = false" behaviour="modal" class="ui-modal--medium" title="Szczegóły przystanku">
|
||||||
|
<stop-details :stop="stop"/>
|
||||||
|
</ui-dialog>
|
||||||
|
</portal>
|
||||||
|
</keep-alive>
|
||||||
|
<keep-alive>
|
||||||
|
<!-- FIXME: This should be in portal but it's not possible due to information loss, maybe in vue3 it will be better?-->
|
||||||
<ui-dialog reference="action-map" v-if="showMap" arrow class="ui-popup--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"/>
|
||||||
</ui-dialog>
|
</ui-dialog>
|
||||||
|
@ -16,7 +16,9 @@
|
|||||||
<div class="track__description">
|
<div class="track__description">
|
||||||
{{ track.description }}
|
{{ track.description }}
|
||||||
</div>
|
</div>
|
||||||
<span class="badge badge-pill badge-light track__order">#{{ order }}</span>
|
<span class="badge badge-pill badge-light track__order">
|
||||||
|
#{{ order }}
|
||||||
|
</span>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</section>
|
</section>
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
<div class="ui-backdrop" @click="handleBackdropClick" v-if="currentBehaviour === 'modal'">
|
<div class="ui-backdrop" @click="handleBackdropClick" v-if="currentBehaviour === 'modal'">
|
||||||
<div class="ui-modal" v-bind="$attrs">
|
<div class="ui-modal" v-bind="attrs" v-on="$listeners">
|
||||||
<div class="ui-modal__top-bar">
|
<div class="ui-modal__top-bar">
|
||||||
<div class="ui-modal__header">
|
<div class="ui-modal__header">
|
||||||
<slot name="header">
|
<slot name="header">
|
||||||
<div class="ui-modal__title">{{ title }}</div>
|
<div class="ui-modal__title"><slot name="title">{{ title }}</slot></div>
|
||||||
</slot>
|
</slot>
|
||||||
</div>
|
</div>
|
||||||
<button class="btn btn-action ui-modal__close" @click.prevent="handleCloseClick">
|
<button class="btn btn-action ui-modal__close" @click.prevent="handleCloseClick">
|
||||||
@ -16,7 +16,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div :class="[ 'ui-popup', arrow && 'ui-popup--arrow' ]" v-bind="$attrs" v-on="$listeners" v-else>
|
<div :class="[ 'ui-popup', arrow && 'ui-popup--arrow' ]" v-bind="attrs" :style="{ zIndex: zIndex }" v-on="$listeners" v-else>
|
||||||
<div class="ui-popup__arrow" ref="arrow" v-if="arrow"></div>
|
<div class="ui-popup__arrow" ref="arrow" v-if="arrow"></div>
|
||||||
<div class="ui-popup__header" v-if="hasHeader">
|
<div class="ui-popup__header" v-if="hasHeader">
|
||||||
<slot name="header" />
|
<slot name="header" />
|
||||||
|
@ -116,6 +116,10 @@ body {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
background: url("../images/background.png") repeat-x center bottom 63px;
|
background: url("../images/background.png") repeat-x center bottom 63px;
|
||||||
|
|
||||||
|
&.contains-modal {
|
||||||
|
overflow-y: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
main {
|
main {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
@ -7,11 +7,14 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
overscroll-behavior-y: contain;
|
overscroll-behavior-y: contain;
|
||||||
|
z-index: 10000;
|
||||||
|
|
||||||
&::after {
|
&::after {
|
||||||
height: 1rem;
|
height: $spacer;
|
||||||
display: block;
|
display: block;
|
||||||
content: "";
|
content: "";
|
||||||
|
width: 1px;
|
||||||
|
flex: 0 0 auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,7 +34,7 @@ $dialog-sizes: (
|
|||||||
border-radius: 1px;
|
border-radius: 1px;
|
||||||
|
|
||||||
@each $size, $width in $dialog-sizes {
|
@each $size, $width in $dialog-sizes {
|
||||||
.ui-modal--#{$size} {
|
&.ui-modal--#{$size} {
|
||||||
width: $width;
|
width: $width;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -56,3 +59,11 @@ $dialog-sizes: (
|
|||||||
display: flex;
|
display: flex;
|
||||||
margin-bottom: $dialog-margin * 0.75;
|
margin-bottom: $dialog-margin * 0.75;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@include media-breakpoint-down('sm') {
|
||||||
|
@each $size, $width in $dialog-sizes {
|
||||||
|
.ui-modal.ui-modal--#{$size} {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -10,9 +10,31 @@ import { defaultBreakpoints } from "../../filters";
|
|||||||
*/
|
*/
|
||||||
export type DialogBehaviour = "modal" | "popup";
|
export type DialogBehaviour = "modal" | "popup";
|
||||||
|
|
||||||
|
let openModalCounter: number = 0;
|
||||||
|
|
||||||
|
function computeZIndexOfElement(element: HTMLElement): number {
|
||||||
|
let current = element;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const zIndex = window.getComputedStyle(current).zIndex;
|
||||||
|
|
||||||
|
if (zIndex !== "auto") {
|
||||||
|
return parseInt(zIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!current.parentElement) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
current = current.parentElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
template: require('../../../components/ui/dialog.html'),
|
|
||||||
inheritAttrs: false,
|
inheritAttrs: false,
|
||||||
|
template: require('../../../components/ui/dialog.html'),
|
||||||
})
|
})
|
||||||
export default class UiDialog extends Vue {
|
export default class UiDialog extends Vue {
|
||||||
@Prop({ type: String, default: "popup" })
|
@Prop({ type: String, default: "popup" })
|
||||||
@ -41,11 +63,23 @@ export default class UiDialog extends Vue {
|
|||||||
|
|
||||||
private isMobile: boolean = false;
|
private isMobile: boolean = false;
|
||||||
|
|
||||||
|
/** Inherited class hack */
|
||||||
|
private staticClass: string[] = [];
|
||||||
|
|
||||||
|
private zIndex: number = 1000;
|
||||||
|
|
||||||
private _focusOutEvent;
|
private _focusOutEvent;
|
||||||
private _resizeEvent;
|
private _resizeEvent;
|
||||||
|
|
||||||
private _popper;
|
private _popper;
|
||||||
|
|
||||||
|
get attrs() {
|
||||||
|
return {
|
||||||
|
...this.$attrs,
|
||||||
|
"class": this.staticClass
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
get currentBehaviour(): DialogBehaviour {
|
get currentBehaviour(): DialogBehaviour {
|
||||||
if (!this.mobileBehaviour) {
|
if (!this.mobileBehaviour) {
|
||||||
return this.behaviour;
|
return this.behaviour;
|
||||||
@ -93,16 +127,60 @@ export default class UiDialog extends Vue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
|
this.zIndex = computeZIndexOfElement(this.getReferenceElement()) + 100;
|
||||||
|
|
||||||
this.handleWindowResize();
|
this.handleWindowResize();
|
||||||
|
|
||||||
if (this.behaviour === 'popup') {
|
if (this.behaviour === 'popup') {
|
||||||
this.initPopper();
|
this.mountPopper();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.staticClass = Array.from(this.$el.classList).filter(cls => ["ui-backdrop", "ui-popup", "ui-popup--arrow"].indexOf(cls) === -1);
|
||||||
|
|
||||||
window.addEventListener('resize', this._resizeEvent = this.handleWindowResize.bind(this));
|
window.addEventListener('resize', this._resizeEvent = this.handleWindowResize.bind(this));
|
||||||
|
|
||||||
|
this._activated();
|
||||||
}
|
}
|
||||||
|
|
||||||
private initPopper() {
|
private _activated() {
|
||||||
|
if (this.behaviour === 'modal') {
|
||||||
|
this.mountModal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _deactivated() {
|
||||||
|
if (this.behaviour === 'modal') {
|
||||||
|
this.dismountModal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private mountModal() {
|
||||||
|
if (openModalCounter === 0) {
|
||||||
|
document.body.style.paddingRight = `${window.screen.width - document.body.clientWidth}px`
|
||||||
|
document.body.classList.add('contains-modal');
|
||||||
|
}
|
||||||
|
|
||||||
|
openModalCounter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
private dismountModal() {
|
||||||
|
openModalCounter--;
|
||||||
|
|
||||||
|
if (openModalCounter === 0) {
|
||||||
|
document.body.style.paddingRight = "";
|
||||||
|
document.body.classList.remove('contains-modal');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
activated() {
|
||||||
|
this._activated();
|
||||||
|
}
|
||||||
|
|
||||||
|
deactivated() {
|
||||||
|
this._deactivated();
|
||||||
|
}
|
||||||
|
|
||||||
|
private mountPopper() {
|
||||||
const reference = this.getReferenceElement();
|
const reference = this.getReferenceElement();
|
||||||
|
|
||||||
this._popper = new Popper(reference, this.$el, {
|
this._popper = new Popper(reference, this.$el, {
|
||||||
@ -147,6 +225,8 @@ export default class UiDialog extends Vue {
|
|||||||
|
|
||||||
beforeDestroy() {
|
beforeDestroy() {
|
||||||
this._focusOutEvent && document.removeEventListener('click', this._focusOutEvent, { capture: true });
|
this._focusOutEvent && document.removeEventListener('click', this._focusOutEvent, { capture: true });
|
||||||
|
|
||||||
|
this._deactivated()
|
||||||
}
|
}
|
||||||
|
|
||||||
removed() {
|
removed() {
|
||||||
@ -179,7 +259,15 @@ export default class UiDialog extends Vue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (newBehaviour === 'popup') {
|
if (newBehaviour === 'popup') {
|
||||||
this.$nextTick(() => this.initPopper());
|
this.$nextTick(() => this.mountPopper());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newBehaviour === 'modal') {
|
||||||
|
this.mountModal();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oldBehaviour === 'modal') {
|
||||||
|
this.dismountModal();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -105,11 +105,9 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<portal to="popups">
|
<ui-dialog reference="save" v-if="visibility.save" arrow placement="bottom-end" @leave="visibility.save = false">
|
||||||
<ui-dialog reference="settings-messages" v-if="visibility.messages" arrow placement="left-start" @leave="visibility.messages = false">
|
<favourites-adder @saved="visibility.save = false"/>
|
||||||
<favourites-adder @saved="visibility.save = false"/>
|
</ui-dialog>
|
||||||
</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