245 lines
4.9 KiB
Vue
245 lines
4.9 KiB
Vue
<template>
|
|
<div
|
|
class="bottom-sheet"
|
|
ref="sheet"
|
|
:style="state == 'closed' ? { 'pointer-events': 'none' } : {}"
|
|
>
|
|
<v-fade-transition>
|
|
<div
|
|
class="bottom-sheet-bg"
|
|
v-show="state != 'closed'"
|
|
@click.stop="onBackgroundClick"
|
|
/>
|
|
</v-fade-transition>
|
|
<div
|
|
class="bottom-sheet-content"
|
|
:data-state="isMove ? 'move' : state"
|
|
ref="pan"
|
|
:style="{ top: `${isMove ? y : calcY()}px` }"
|
|
>
|
|
<v-btn
|
|
fab
|
|
x-small
|
|
elevation="0"
|
|
color="black"
|
|
@click.stop="onBackgroundClick"
|
|
class="bottom-sheet-close"
|
|
v-if="showCloseButton"
|
|
>
|
|
<v-icon color="white" >cancel</v-icon>
|
|
</v-btn>
|
|
<div class="bottom-sheet-handle"><div class="bottom-sheet-handle-decoration" /></div>
|
|
<div ref="sheetContent" class="sheetContent">
|
|
<slot></slot>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import Hammer from "hammerjs";
|
|
|
|
export default {
|
|
props: {
|
|
showCloseButton: {
|
|
type: Boolean,
|
|
default: true,
|
|
},
|
|
openY: {
|
|
type: Number,
|
|
default: 0.1,
|
|
},
|
|
halfY: {
|
|
type: Number,
|
|
default: 0.5,
|
|
},
|
|
defaultState: {
|
|
type: String,
|
|
default: "closed",
|
|
},
|
|
},
|
|
data() {
|
|
return {
|
|
mc: null,
|
|
y: 0,
|
|
startY: 0,
|
|
startYCoord: 0,
|
|
isMove: false,
|
|
state: this.defaultState,
|
|
rect: {},
|
|
};
|
|
},
|
|
mounted() {
|
|
window.onresize = () => {
|
|
this.rect = this.$refs.sheet.getBoundingClientRect();
|
|
};
|
|
this.rect = this.$refs.sheet.getBoundingClientRect();
|
|
|
|
this.mc = new Hammer(this.$refs.pan);
|
|
this.mc.get("pan").set({ direction: Hammer.DIRECTION_ALL });
|
|
|
|
const self = this;
|
|
|
|
this.mc.on("panup pandown", (evt) => {
|
|
self.y = self.startYCoord - (self.startY - evt.center.y);
|
|
});
|
|
|
|
this.mc.on("panstart", (evt) => {
|
|
self.startY = evt.center.y;
|
|
self.startYCoord = this.calcY();
|
|
self.isMove = true;
|
|
});
|
|
|
|
this.mc.on("pancancel", (ignoredEvt) => {
|
|
self.y = self.startYCoord;
|
|
self.isMove = false;
|
|
});
|
|
|
|
this.mc.on("panend", (evt) => {
|
|
self.isMove = false;
|
|
|
|
switch (self.state) {
|
|
case "small":
|
|
if (self.startY - evt.center.y > 120) {
|
|
self.state = "open";
|
|
}
|
|
|
|
if (self.startY - evt.center.y < -50) {
|
|
self.state = "closed";
|
|
}
|
|
break;
|
|
case "open":
|
|
if (self.startY - evt.center.y < -120) {
|
|
self.state = "small";
|
|
}
|
|
break;
|
|
}
|
|
});
|
|
},
|
|
beforeDestroy() {
|
|
this.mc.destroy();
|
|
window.onresize = null;
|
|
},
|
|
methods: {
|
|
calcY() {
|
|
switch (this.state) {
|
|
case "closed":
|
|
return this.rect.height;
|
|
case "open":
|
|
return this.rect.height * this.openY;
|
|
case "small":
|
|
return this.rect.height * this.halfY;
|
|
default:
|
|
return this.y;
|
|
}
|
|
},
|
|
open() {
|
|
this.setState("small");
|
|
},
|
|
close() {
|
|
this.setState("closed");
|
|
},
|
|
setState(state) {
|
|
this.state = state;
|
|
if (state == "closed") {
|
|
this.scrollToTop();
|
|
}
|
|
},
|
|
onBackgroundClick() {
|
|
if (this.state == "open") {
|
|
this.setState("small");
|
|
} else {
|
|
this.setState("closed");
|
|
}
|
|
},
|
|
scrollToTop() {
|
|
const container = this.$refs.sheetContent;
|
|
if (container) {
|
|
container.scrollTo(0, 0);
|
|
}
|
|
},
|
|
},
|
|
};
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
@import '~vuetify/src/styles/settings/_variables.scss';
|
|
@import '@/assets/css/variables';
|
|
.sheetContent {
|
|
position:absolute;
|
|
top:20px;left:0;
|
|
right:0;
|
|
bottom:0;
|
|
overflow-y:auto;
|
|
padding:20px;
|
|
}
|
|
|
|
.bottom-sheet {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
overflow-x: hidden;
|
|
overflow-y: hidden;
|
|
z-index: 20;
|
|
}
|
|
|
|
.bottom-sheet-bg {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
background-color: rgba(black, 0.4);
|
|
}
|
|
|
|
.bottom-sheet-handle {
|
|
height: 20px;
|
|
background-color: white;
|
|
position: relative;
|
|
.bottom-sheet-handle-decoration {
|
|
background-color: #cccccc;
|
|
height: 2px;
|
|
position: absolute;
|
|
top: 50%;
|
|
left: 30%;
|
|
right: 30%;
|
|
}
|
|
}
|
|
|
|
.bottom-sheet-content {
|
|
position: absolute;
|
|
top: 0px;
|
|
bottom: 0px;
|
|
left: 0;
|
|
right: 0;
|
|
border-radius: 10px 10px 0px 0px;
|
|
background-color: white;
|
|
overflow: hidden;
|
|
|
|
.bottom-sheet-close {
|
|
position: absolute;
|
|
right: 0;
|
|
top: 0;
|
|
z-index: 1;
|
|
}
|
|
|
|
@media #{map-get($display-breakpoints, 'lg-and-up')} {
|
|
margin: 0 auto;
|
|
width: $dialog-desktop-width;
|
|
}
|
|
}
|
|
|
|
.bottom-sheet-content[data-state="small"],
|
|
.bottom-sheet-content[data-state="open"],
|
|
.bottom-sheet-content[data-state="closed"] {
|
|
transition: top 0.3s ease-out;
|
|
}
|
|
|
|
.bottom-sheet-content[data-state="small"] {
|
|
@media #{map-get($display-breakpoints, 'lg-and-up')} {
|
|
top: 100px !important;
|
|
}
|
|
}
|
|
</style>
|