split-timeline: basic resizing
Signed-off-by: Varun Patil <varunpatil@ucla.edu>pull/563/head
parent
dd00857ea0
commit
61b3c7de1c
|
@ -1,8 +1,18 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="primary">
|
<div class="primary" ref="primary">
|
||||||
<component :is="primary" />
|
<component :is="primary" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="separator"
|
||||||
|
ref="separator"
|
||||||
|
@pointerdown="sepDown"
|
||||||
|
@touchmove.passive="sepTouchMove"
|
||||||
|
@touchend.passive="pointerUp"
|
||||||
|
@touchcancel.passive="pointerUp"
|
||||||
|
></div>
|
||||||
|
|
||||||
<div class="timeline">
|
<div class="timeline">
|
||||||
<Timeline />
|
<Timeline />
|
||||||
</div>
|
</div>
|
||||||
|
@ -13,6 +23,7 @@
|
||||||
import { defineComponent } from "vue";
|
import { defineComponent } from "vue";
|
||||||
import Timeline from "./Timeline.vue";
|
import Timeline from "./Timeline.vue";
|
||||||
const MapSplitMatter = () => import("./top-matter/MapSplitMatter.vue");
|
const MapSplitMatter = () => import("./top-matter/MapSplitMatter.vue");
|
||||||
|
import { emit } from "@nextcloud/event-bus";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: "SplitTimeline",
|
name: "SplitTimeline",
|
||||||
|
@ -21,6 +32,11 @@ export default defineComponent({
|
||||||
Timeline,
|
Timeline,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
data: () => ({
|
||||||
|
pointerDown: false,
|
||||||
|
primaryPos: 0,
|
||||||
|
}),
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
primary() {
|
primary() {
|
||||||
switch (this.$route.name) {
|
switch (this.$route.name) {
|
||||||
|
@ -31,6 +47,60 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
beforeDestroy() {
|
||||||
|
this.pointerUp();
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
isVertical() {
|
||||||
|
return globalThis.windowInnerWidth <= 768;
|
||||||
|
},
|
||||||
|
|
||||||
|
sepDown(event: PointerEvent) {
|
||||||
|
this.pointerDown = true;
|
||||||
|
|
||||||
|
// Get position of primary element
|
||||||
|
const primary = <HTMLDivElement>this.$refs.primary;
|
||||||
|
const rect = primary.getBoundingClientRect();
|
||||||
|
this.primaryPos = this.isVertical() ? rect.top : rect.left;
|
||||||
|
|
||||||
|
// Let touch handle itself
|
||||||
|
if (event.pointerType === "touch") return;
|
||||||
|
|
||||||
|
// Otherwise, handle pointer events on document
|
||||||
|
document.addEventListener("pointermove", this.documentPointerMove);
|
||||||
|
document.addEventListener("pointerup", this.pointerUp);
|
||||||
|
|
||||||
|
// Prevent text selection
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
},
|
||||||
|
|
||||||
|
sepTouchMove(event: TouchEvent) {
|
||||||
|
if (!this.pointerDown) return;
|
||||||
|
this.setFlexBasis(event.touches[0]);
|
||||||
|
},
|
||||||
|
|
||||||
|
documentPointerMove(event: PointerEvent) {
|
||||||
|
if (!this.pointerDown || !event.buttons) return this.pointerUp();
|
||||||
|
this.setFlexBasis(event);
|
||||||
|
},
|
||||||
|
|
||||||
|
pointerUp() {
|
||||||
|
// Get rid of listeners on document quickly
|
||||||
|
this.pointerDown = false;
|
||||||
|
document.removeEventListener("pointermove", this.documentPointerMove);
|
||||||
|
document.removeEventListener("pointerup", this.pointerUp);
|
||||||
|
emit("memories:window:resize", null);
|
||||||
|
},
|
||||||
|
|
||||||
|
setFlexBasis(pos: { clientX: number; clientY: number }) {
|
||||||
|
const ref = this.isVertical() ? pos.clientY : pos.clientX;
|
||||||
|
const newWidth = Math.max(ref - this.primaryPos, 50);
|
||||||
|
(<HTMLDivElement>this.$refs.primary).style.flexBasis = `${newWidth}px`;
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -40,29 +110,62 @@ export default defineComponent({
|
||||||
height: 100%;
|
height: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
|
||||||
.primary {
|
> div {
|
||||||
width: 60%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
.timeline {
|
|
||||||
flex: 1;
|
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
max-height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .primary {
|
||||||
|
flex-basis: 60%;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .timeline {
|
||||||
|
flex-basis: auto;
|
||||||
|
flex-grow: 1;
|
||||||
padding-left: 8px;
|
padding-left: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .separator {
|
||||||
|
flex-grow: 0;
|
||||||
|
flex-shrink: 0;
|
||||||
|
width: 5px;
|
||||||
|
background-color: gray;
|
||||||
|
opacity: 0.1;
|
||||||
|
cursor: col-resize;
|
||||||
|
margin: 0 0 0 auto;
|
||||||
|
transition: opacity 0.4s ease-out, background-color 0.4s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .separator:hover {
|
||||||
|
opacity: 0.4;
|
||||||
|
background-color: var(--color-primary);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.container {
|
.container {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
|
||||||
.primary {
|
> div {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 40%;
|
height: unset;
|
||||||
}
|
}
|
||||||
.timeline {
|
|
||||||
width: 100%;
|
> .primary {
|
||||||
height: 60%;
|
flex-basis: 40%;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .timeline {
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
> .separator {
|
||||||
|
height: 5px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
Loading…
Reference in New Issue