This commit is contained in:
@@ -229,14 +229,6 @@ export default {
|
|||||||
v-model:base-map="baseMap"
|
v-model:base-map="baseMap"
|
||||||
:terrain-enabled="terrainEnabled"
|
:terrain-enabled="terrainEnabled"
|
||||||
/>
|
/>
|
||||||
<Lightbox
|
|
||||||
ref="lightbox"
|
|
||||||
:always-show-nav-on-touch-devices="true"
|
|
||||||
:position-from-top="0"
|
|
||||||
:has-video="true"
|
|
||||||
@media-change="onLightboxMediaChange"
|
|
||||||
@closing="onLightboxClosing"
|
|
||||||
/>
|
|
||||||
<ProjectSettings
|
<ProjectSettings
|
||||||
:ref="setSettings"
|
:ref="setSettings"
|
||||||
:projects="projectOptions"
|
:projects="projectOptions"
|
||||||
@@ -256,5 +248,13 @@ export default {
|
|||||||
@new-markers="addNewMarkers"
|
@new-markers="addNewMarkers"
|
||||||
@toggle="(bIsOpen, iAnimDuration) => onPanelToggle('right', bIsOpen, iAnimDuration)"
|
@toggle="(bIsOpen, iAnimDuration) => onPanelToggle('right', bIsOpen, iAnimDuration)"
|
||||||
/>
|
/>
|
||||||
|
<Lightbox
|
||||||
|
ref="lightbox"
|
||||||
|
:always-show-nav-on-touch-devices="true"
|
||||||
|
:position-from-top="0"
|
||||||
|
:has-video="true"
|
||||||
|
@media-change="onLightboxMediaChange"
|
||||||
|
@closing="onLightboxClosing"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import Simplebar from 'simplebar-vue';
|
|||||||
|
|
||||||
import AppIcon from '@components/AppIcon';
|
import AppIcon from '@components/AppIcon';
|
||||||
import ProjectPost from '@components/ProjectPost';
|
import ProjectPost from '@components/ProjectPost';
|
||||||
|
import { createSwipeToClose, initialSwipeState } from '@scripts/swipeToClose';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
@@ -26,7 +27,7 @@ export default {
|
|||||||
isOpen: false,
|
isOpen: false,
|
||||||
posts: [],
|
posts: [],
|
||||||
refreshRate: 60,
|
refreshRate: 60,
|
||||||
swipe: {x: null, y: null}
|
swipe: initialSwipeState()
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
emits: ['request-last-update', 'new-markers', 'toggle'],
|
emits: ['request-last-update', 'new-markers', 'toggle'],
|
||||||
@@ -76,7 +77,6 @@ export default {
|
|||||||
this.refIdLast = 0;
|
this.refIdLast = 0;
|
||||||
this.firstChunk = true;
|
this.firstChunk = true;
|
||||||
this.posts = [];
|
this.posts = [];
|
||||||
this.swipe = {x: null, y: null};
|
|
||||||
|
|
||||||
this.toggle(!this.isMobile(), 0);
|
this.toggle(!this.isMobile(), 0);
|
||||||
this.getScrollElement().scrollTop = 0;
|
this.getScrollElement().scrollTop = 0;
|
||||||
@@ -162,23 +162,7 @@ export default {
|
|||||||
|
|
||||||
if((box.scrollTop + box.clientHeight) / (content?.offsetHeight || 1) >= 0.8) this.getNextFeed();
|
if((box.scrollTop + box.clientHeight) / (content?.offsetHeight || 1) >= 0.8) this.getNextFeed();
|
||||||
},
|
},
|
||||||
onTouchStart(oEvent) {
|
...createSwipeToClose(1),
|
||||||
if(!this.isMobile() || !this.isOpen || oEvent.touches.length != 1) return;
|
|
||||||
|
|
||||||
const oTouch = oEvent.touches[0];
|
|
||||||
this.swipe = {x: oTouch.clientX, y: oTouch.clientY};
|
|
||||||
},
|
|
||||||
onTouchEnd(oEvent) {
|
|
||||||
const oTouch = oEvent.changedTouches[0];
|
|
||||||
if(!oTouch || this.swipe.x === null) return;
|
|
||||||
|
|
||||||
const iDeltaX = oTouch.clientX - this.swipe.x;
|
|
||||||
const iDeltaY = oTouch.clientY - this.swipe.y;
|
|
||||||
|
|
||||||
if(iDeltaX > 80 && Math.abs(iDeltaX) > Math.abs(iDeltaY) * 1.5) this.toggle();
|
|
||||||
|
|
||||||
this.swipe = {x: null, y: null};
|
|
||||||
},
|
|
||||||
setUpdateTimer(iSeconds) {
|
setUpdateTimer(iSeconds) {
|
||||||
if(typeof this.feedTimer != 'undefined') clearTimeout(this.feedTimer);
|
if(typeof this.feedTimer != 'undefined') clearTimeout(this.feedTimer);
|
||||||
if(iSeconds >= 0) this.feedTimer = setTimeout(this.onUpdateTimer, iSeconds * 1000);
|
if(iSeconds >= 0) this.feedTimer = setTimeout(this.onUpdateTimer, iSeconds * 1000);
|
||||||
@@ -224,7 +208,7 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div id="feed" class="panel panel-right" @touchstart.passive="onTouchStart" @touchend.passive="onTouchEnd">
|
<div id="feed" class="panel panel-right" @touchstart.passive="onTouchStart" @touchmove.passive="onTouchMove" @touchend.passive="onTouchEnd">
|
||||||
<Simplebar id="feed-panel" class="panel-content" ref="feedSimpleBar" :aria-busy="loading">
|
<Simplebar id="feed-panel" class="panel-content" ref="feedSimpleBar" :aria-busy="loading">
|
||||||
<div id="feed-header">
|
<div id="feed-header">
|
||||||
<ProjectPost v-if="modeHisto" :options="{type: 'archived', headerless: true}" />
|
<ProjectPost v-if="modeHisto" :options="{type: 'archived', headerless: true}" />
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import AppIcon from '@components/AppIcon';
|
|||||||
import ProjectAccount from '@components/ProjectAccount';
|
import ProjectAccount from '@components/ProjectAccount';
|
||||||
import logoIconUrl from '@images/icons/favicon.svg';
|
import logoIconUrl from '@images/icons/favicon.svg';
|
||||||
import logoTitleUrl from '@images/logo_title.svg';
|
import logoTitleUrl from '@images/logo_title.svg';
|
||||||
|
import { createSwipeToClose, initialSwipeState } from '@scripts/swipeToClose';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
@@ -26,7 +27,8 @@ export default {
|
|||||||
isOpen: false,
|
isOpen: false,
|
||||||
lastUpdate: {unix_time: 0, relative_time: '', formatted_time: ''},
|
lastUpdate: {unix_time: 0, relative_time: '', formatted_time: ''},
|
||||||
logoIconUrl,
|
logoIconUrl,
|
||||||
logoTitleUrl
|
logoTitleUrl,
|
||||||
|
swipe: initialSwipeState()
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
emits: ['update:baseMap', 'update:projectCodeName', 'update:terrainEnabled', 'toggle'],
|
emits: ['update:baseMap', 'update:projectCodeName', 'update:terrainEnabled', 'toggle'],
|
||||||
@@ -81,13 +83,14 @@ export default {
|
|||||||
},
|
},
|
||||||
getWidth() {
|
getWidth() {
|
||||||
return this.$el.getBoundingClientRect().width;
|
return this.$el.getBoundingClientRect().width;
|
||||||
}
|
},
|
||||||
|
...createSwipeToClose(-1)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div id="settings" class="panel panel-left">
|
<div id="settings" class="panel panel-left" @touchstart.passive="onTouchStart" @touchmove.passive="onTouchMove" @touchend.passive="onTouchEnd">
|
||||||
<div id="settings-panel" class="panel-content">
|
<div id="settings-panel" class="panel-content">
|
||||||
<div class="settings-header settings-box">
|
<div class="settings-header settings-box">
|
||||||
<img :src="logoIconUrl" class="logo-icon clickable" :title="lang.get('project.overview')" @click="hash.items = []" />
|
<img :src="logoIconUrl" class="logo-icon clickable" :title="lang.get('project.overview')" @click="hash.items = []" />
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
/* Swipe-to-close gesture for a slide-in side panel */
|
||||||
|
|
||||||
|
// Initial value for the swipe data field createSwipeToClose()'s handlers expect on the component
|
||||||
|
export function initialSwipeState() {
|
||||||
|
return {x: null, y: null, dragging: false};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Builds touchstart/touchmove/touchend handlers for a panel that closes by
|
||||||
|
// dragging it towards its own off-screen side. iDirection is:
|
||||||
|
// +1 for a panel that opens from the right
|
||||||
|
// -1 for one that opens from the left
|
||||||
|
export function createSwipeToClose(iDirection) {
|
||||||
|
return {
|
||||||
|
onTouchStart(oEvent) {
|
||||||
|
if(!this.isMobile() || !this.isOpen || oEvent.touches.length != 1) return;
|
||||||
|
|
||||||
|
const oTouch = oEvent.touches[0];
|
||||||
|
this.swipe = {...initialSwipeState(), x: oTouch.clientX, y: oTouch.clientY};
|
||||||
|
},
|
||||||
|
onTouchMove(oEvent) {
|
||||||
|
if(this.swipe.x === null || oEvent.touches.length != 1) return;
|
||||||
|
|
||||||
|
const oTouch = oEvent.touches[0];
|
||||||
|
const iDeltaX = oTouch.clientX - this.swipe.x;
|
||||||
|
const iDeltaY = oTouch.clientY - this.swipe.y;
|
||||||
|
|
||||||
|
if(!this.swipe.dragging) {
|
||||||
|
//Only commit to a horizontal drag once the gesture is clearly not a vertical scroll
|
||||||
|
if(iDeltaX * iDirection <= 10 || Math.abs(iDeltaX) <= Math.abs(iDeltaY) * 1.5) return;
|
||||||
|
this.swipe.dragging = true;
|
||||||
|
this.$el.classList.add('moving');
|
||||||
|
}
|
||||||
|
|
||||||
|
const iDragX = iDirection * Math.min(Math.max(iDeltaX * iDirection, 0), this.getWidth());
|
||||||
|
this.$el.style.setProperty('--drag-x', `${iDragX}px`);
|
||||||
|
},
|
||||||
|
onTouchEnd(oEvent) {
|
||||||
|
const oTouch = oEvent.changedTouches[0];
|
||||||
|
if(!oTouch || this.swipe.x === null) return;
|
||||||
|
|
||||||
|
const iDeltaX = oTouch.clientX - this.swipe.x;
|
||||||
|
const iDeltaY = oTouch.clientY - this.swipe.y;
|
||||||
|
|
||||||
|
if(this.swipe.dragging) {
|
||||||
|
this.$el.classList.remove('moving');
|
||||||
|
this.$el.style.removeProperty('--drag-x');
|
||||||
|
}
|
||||||
|
|
||||||
|
if(iDeltaX * iDirection > 80 && Math.abs(iDeltaX) > Math.abs(iDeltaY) * 1.5) this.toggle(false);
|
||||||
|
|
||||||
|
this.swipe = initialSwipeState();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -52,13 +52,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.panel.panel-right {
|
.panel.panel-right {
|
||||||
transform: translateX(calc(var(--button-width) + var.$block-spacing * 2));
|
transform: translateX(calc(var(--button-width) + var.$block-spacing * 2 + var(--drag-x, 0px)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.with-left-panel {
|
&.with-left-panel {
|
||||||
.panel.panel-left {
|
.panel.panel-left {
|
||||||
transform: translateX(0);
|
transform: translateX(var(--drag-x, 0px));
|
||||||
}
|
}
|
||||||
|
|
||||||
.panel.panel-right {
|
.panel.panel-right {
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ $panel-actual-width: min($panel-width, #{$panel-width-max});
|
|||||||
}
|
}
|
||||||
|
|
||||||
.panel.panel-left {
|
.panel.panel-left {
|
||||||
transform: translateX(0);
|
transform: translateX(var(--drag-x, 0px));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,17 +53,18 @@ $panel-actual-width: min($panel-width, #{$panel-width-max});
|
|||||||
bottom: 0;
|
bottom: 0;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
touch-action: pan-y;
|
||||||
width: #{$panel-width};
|
width: #{$panel-width};
|
||||||
max-width: calc(#{$panel-width-max});
|
max-width: calc(#{$panel-width-max});
|
||||||
transition: transform var.$trans-slow;
|
transition: transform var.$trans-slow;
|
||||||
|
|
||||||
&.moving {
|
&.moving {
|
||||||
cursor: grabbing;
|
cursor: grabbing;
|
||||||
transition: none;
|
transition: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.panel-left {
|
&.panel-left {
|
||||||
transform: translateX(-100%);
|
transform: translateX(calc(-100% + var(--drag-x, 0px)));
|
||||||
|
|
||||||
.panel-content {
|
.panel-content {
|
||||||
width: calc(100% - var.$block-spacing);
|
width: calc(100% - var.$block-spacing);
|
||||||
@@ -81,8 +82,8 @@ $panel-actual-width: min($panel-width, #{$panel-width-max});
|
|||||||
}
|
}
|
||||||
|
|
||||||
&.panel-right {
|
&.panel-right {
|
||||||
transform: translateX(100vw);
|
transform: translateX(calc(100vw + var(--drag-x, 0px)));
|
||||||
|
|
||||||
.panel-content {
|
.panel-content {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding-top: var.$block-spacing;
|
padding-top: var.$block-spacing;
|
||||||
|
|||||||
Reference in New Issue
Block a user