1
0
mirror of https://github.com/lana-k/sqliteviz.git synced 2025-12-06 18:18:53 +08:00

Splitpanes.vue refactoring

This commit is contained in:
lana-k
2021-02-17 17:57:45 +01:00
parent 797430e9f8
commit 720f23745f
2 changed files with 122 additions and 125 deletions

View File

@@ -3,13 +3,13 @@
ref="container" ref="container"
:class="[ :class="[
'splitpanes', 'splitpanes',
`splitpanes--${horizontal ? 'horizontal' : 'vertical'}`, `splitpanes-${horizontal ? 'horizontal' : 'vertical'}`,
{ 'splitpanes--dragging': touch.dragging } { 'splitpanes-dragging': dragging }
]" ]"
> >
<div class="movable-splitter" ref="movableSplitter" :style="movableSplitterStyle" /> <div class="movable-splitter" ref="movableSplitter" :style="movableSplitterStyle" />
<div <div
class="splitpanes__pane" class="splitpanes-pane"
ref="left" ref="left"
:size="paneBefore.size" :size="paneBefore.size"
max-size="30" max-size="30"
@@ -19,7 +19,7 @@
</div> </div>
<!-- Splitter start--> <!-- Splitter start-->
<div <div
class="splitpanes__splitter" class="splitpanes-splitter"
@mousedown="onMouseDown" @mousedown="onMouseDown"
@touchstart="onMouseDown" @touchstart="onMouseDown"
> >
@@ -32,7 +32,7 @@
<div <div
v-if="after.max === 100 && after.size > 0" v-if="after.max === 100 && after.size > 0"
class="toggle-btn" class="toggle-btn"
@click="togglePane('before')" @click="togglePane(paneBefore)"
> >
<img <img
class="direction-icon" class="direction-icon"
@@ -43,7 +43,7 @@
<div <div
v-if="before.max === 100 && before.size > 0" v-if="before.max === 100 && before.size > 0"
class="toggle-btn" class="toggle-btn"
@click="togglePane('after')" @click="togglePane(paneAfter)"
> >
<img <img
class="direction-icon" class="direction-icon"
@@ -55,7 +55,7 @@
</div> </div>
<!-- splitter end --> <!-- splitter end -->
<div <div
class="splitpanes__pane" class="splitpanes-pane"
ref="right" ref="right"
:style="styles.after" :style="styles.after"
> >
@@ -65,6 +65,7 @@
</template> </template>
<script> <script>
import splitter from '@/splitter'
export default { export default {
name: 'Splitpanes', name: 'Splitpanes',
@@ -82,10 +83,7 @@ export default {
before: this.before.size, before: this.before.size,
after: this.after.size after: this.after.size
}, },
touch: { dragging: false,
mouseDown: false,
dragging: false
},
movableSplitter: { movableSplitter: {
top: 0, top: 0,
left: 0, left: 0,
@@ -108,72 +106,50 @@ export default {
}, },
directionBeforeIconStyle () { directionBeforeIconStyle () {
const expanded = this.paneBefore.size !== 0 const expanded = this.paneBefore.size !== 0
const translation = 'translate(-50%, -50%)' const translation = 'translate(-50%, -50%) '
let rotation = ''
if (this.horizontal) { if (this.horizontal) {
return { rotation = expanded ? 'rotate(90deg)' : 'rotate(-90deg)'
transform: `${translation} ${expanded ? 'rotate(90deg)' : 'rotate(-90deg)'}`
}
} else { } else {
return { rotation = expanded ? 'rotate(0deg)' : 'rotate(180deg)'
transform: `${translation} ${expanded ? 'rotate(0deg)' : 'rotate(180deg)'}` }
}
return {
transform: translation + rotation
} }
}, },
directionAfterIconStyle () { directionAfterIconStyle () {
const expanded = this.paneAfter.size !== 0 const expanded = this.paneAfter.size !== 0
const translation = 'translate(-50%, -50%)' const translation = 'translate(-50%, -50%)'
let rotation = ''
if (this.horizontal) { if (this.horizontal) {
return { rotation = expanded ? 'rotate(-90deg)' : 'rotate(90deg)'
transform: `${translation} ${expanded ? 'rotate(-90deg)' : 'rotate(90deg)'}`
}
} else { } else {
return { rotation = expanded ? 'rotate(180deg)' : 'rotate(0deg)'
transform: `${translation} ${expanded ? 'rotate(180deg)' : 'rotate(0deg)'}` }
}
return {
transform: translation + rotation
} }
} }
}, },
methods: { methods: {
bindEvents () {
document.addEventListener('mousemove', this.onMouseMove, { passive: false })
document.addEventListener('mouseup', this.onMouseUp)
// Passive: false to prevent scrolling while touch dragging.
if ('ontouchstart' in window) {
document.addEventListener('touchmove', this.onMouseMove, { passive: false })
document.addEventListener('touchend', this.onMouseUp)
}
},
unbindEvents () {
document.removeEventListener('mousemove', this.onMouseMove, { passive: false })
document.removeEventListener('mouseup', this.onMouseUp)
if ('ontouchstart' in window) {
document.removeEventListener('touchmove', this.onMouseMove, { passive: false })
document.removeEventListener('touchend', this.onMouseUp)
}
},
onMouseDown () { onMouseDown () {
this.bindEvents() splitter.bindEvents(this.onMouseMove, this.onMouseUp)
this.touch.mouseDown = true
}, },
onMouseMove (event) { onMouseMove (event) {
if (this.touch.mouseDown) { event.preventDefault()
// Prevent scrolling while touch dragging (only works with an active event, eg. passive: false). this.dragging = true
event.preventDefault() this.$set(this.movableSplitter, 'visibility', 'visible')
this.touch.dragging = true this.moveSplitter(event)
this.$set(this.movableSplitter, 'visibility', 'visible')
this.moveSplitter(event)
}
}, },
onMouseUp () { onMouseUp () {
this.touch.mouseDown = false if (this.dragging) {
if (this.touch.dragging) {
const dragPercentage = this.horizontal const dragPercentage = this.horizontal
? this.movableSplitter.top ? this.movableSplitter.top
: this.movableSplitter.left : this.movableSplitter.left
@@ -186,65 +162,32 @@ export default {
left: 0, left: 0,
visibility: 'hidden' visibility: 'hidden'
} }
this.dragging = false
} }
// Keep dragging flag until click event is finished (click happens immediately after mouseup) splitter.unbindEvents(this.onMouseMove, this.onMouseUp)
// in order to prevent emitting `splitter-click` event if splitter was dragged.
setTimeout(() => {
this.touch.dragging = false
this.unbindEvents()
}, 100)
},
// Get the cursor position relative to the splitpane container.
getCurrentMouseDrag (event) {
const rect = this.container.getBoundingClientRect()
const { clientX, clientY } = ('ontouchstart' in window && event.touches)
? event.touches[0]
: event
return {
x: clientX - rect.left,
y: clientY - rect.top
}
},
// Returns the drag percentage of the splitter relative to the 2 panes it's inbetween.
// if the sum of size of the 2 cells is 60%, the dragPercentage range will be 0 to 100% of this 60%.
getCurrentDragPercentage (drag) {
drag = drag[this.horizontal ? 'y' : 'x']
// In the code bellow 'size' refers to 'width' for vertical and 'height' for horizontal layout.
const containerSize = this.container[this.horizontal ? 'clientHeight' : 'clientWidth']
return drag * 100 / containerSize
}, },
moveSplitter (event) { moveSplitter (event) {
const dragPercentage = this.getCurrentDragPercentage(this.getCurrentMouseDrag(event)) const dragPercentage = splitter
const paneBefore = this.paneBefore .getCurrentDragPercentage(event, this.container, this.horizontal)
const paneAfter = this.paneAfter
const paneBeforeMaxReached = paneBefore.max < 100 && (dragPercentage >= paneBefore.max) const paneBeforeMax = this.paneBefore.max
const paneAfterMaxReached = paneAfter.max < 100 && (dragPercentage <= 100 - paneAfter.max) const paneAfterMax = this.paneAfter.max
const dir = this.horizontal ? 'top' : 'left'
// Prevent dragging beyond pane max. // Prevent dragging beyond pane max.
if (paneBeforeMaxReached || paneAfterMaxReached) { const offset = splitter.calculateOffset(paneBeforeMax, paneAfterMax, dragPercentage)
if (paneBeforeMaxReached) { const dir = this.horizontal ? 'top' : 'left'
this.$set(this.movableSplitter, dir, paneBefore.max) this.$set(this.movableSplitter, dir, offset)
} else {
this.$set(this.movableSplitter, dir, Math.max(100 - paneAfter.max, 0))
}
} else {
this.$set(this.movableSplitter, dir, Math.min(Math.max(dragPercentage, 0), paneBefore.max))
}
}, },
togglePane (toggledPane) {
const pane = toggledPane === 'before' ? this.paneBefore : this.paneAfter togglePane (pane) {
if (pane.size > 0) { if (pane.size > 0) {
this.beforeMinimising.before = this.paneBefore.size this.beforeMinimising.before = this.paneBefore.size
this.beforeMinimising.after = this.paneAfter.size this.beforeMinimising.after = this.paneAfter.size
pane.size = 0 pane.size = 0
const otherPane = toggledPane === 'before' ? this.paneAfter : this.paneBefore const otherPane = pane === this.paneBefore ? this.paneAfter : this.paneBefore
otherPane.size = 100 - pane.size otherPane.size = 100 - pane.size
} else { } else {
this.paneBefore.size = this.beforeMinimising.before this.paneBefore.size = this.beforeMinimising.before
@@ -265,11 +208,11 @@ export default {
position: relative; position: relative;
} }
.splitpanes--vertical {flex-direction: row;} .splitpanes-vertical {flex-direction: row;}
.splitpanes--horizontal {flex-direction: column;} .splitpanes-horizontal {flex-direction: column;}
.splitpanes--dragging * {user-select: none;} .splitpanes-dragging * {user-select: none;}
.splitpanes__pane { .splitpanes-pane {
width: 100%; width: 100%;
height: 100%; height: 100%;
overflow: auto; overflow: auto;
@@ -277,16 +220,16 @@ export default {
/* Splitter */ /* Splitter */
.splitpanes--vertical > .splitpanes__splitter, .splitpanes-vertical > .splitpanes-splitter,
.splitpanes--vertical.splitpanes--dragging { .splitpanes-vertical.splitpanes-dragging {
cursor: col-resize; cursor: col-resize;
} }
.splitpanes--horizontal > .splitpanes__splitter, .splitpanes-horizontal > .splitpanes-splitter,
.splitpanes--horizontal.splitpanes--dragging { .splitpanes-horizontal.splitpanes-dragging {
cursor: row-resize; cursor: row-resize;
} }
.splitpanes__splitter { .splitpanes-splitter {
touch-action: none; touch-action: none;
background-color: var(--color-bg-light); background-color: var(--color-bg-light);
box-sizing: border-box; box-sizing: border-box;
@@ -295,12 +238,12 @@ export default {
z-index: 1; z-index: 1;
} }
.splitpanes--horizontal > .splitpanes__splitter { .splitpanes-horizontal > .splitpanes-splitter {
border-top: 1px solid var(--color-border-light); border-top: 1px solid var(--color-border-light);
border-bottom: 1px solid var(--color-border-light); border-bottom: 1px solid var(--color-border-light);
} }
.splitpanes--vertical > .splitpanes__splitter { .splitpanes-vertical > .splitpanes-splitter {
border-left: 1px solid var(--color-border-light); border-left: 1px solid var(--color-border-light);
border-right: 1px solid var(--color-border-light); border-right: 1px solid var(--color-border-light);
} }
@@ -310,15 +253,15 @@ export default {
background-color:rgba(162, 177, 198, 0.5); background-color:rgba(162, 177, 198, 0.5);
} }
.splitpanes--vertical > .splitpanes__splitter, .splitpanes-vertical > .splitpanes-splitter,
.splitpanes--vertical > .movable-splitter { .splitpanes-vertical > .movable-splitter {
width: 8px; width: 8px;
z-index: 5; z-index: 5;
height: 100% height: 100%
} }
.splitpanes--horizontal > .splitpanes__splitter, .splitpanes-horizontal > .splitpanes-splitter,
.splitpanes--horizontal > .movable-splitter { .splitpanes-horizontal > .movable-splitter {
height: 8px; height: 8px;
width: 100%; width: 100%;
z-index: 5; z-index: 5;
@@ -332,11 +275,11 @@ export default {
display: flex; display: flex;
} }
.splitpanes--vertical > .splitpanes__splitter .toggle-btns { .splitpanes-vertical > .splitpanes-splitter .toggle-btns {
flex-direction: column; flex-direction: column;
} }
.splitpanes--horizontal > .splitpanes__splitter .toggle-btns { .splitpanes-horizontal > .splitpanes-splitter .toggle-btns {
flex-direction: row; flex-direction: row;
} }
@@ -349,12 +292,12 @@ export default {
cursor: pointer; cursor: pointer;
} }
.splitpanes--vertical > .splitpanes__splitter .toggle-btn { .splitpanes-vertical > .splitpanes-splitter .toggle-btn {
height: 49px; height: 49px;
width: 8px; width: 8px;
} }
.splitpanes--horizontal > .splitpanes__splitter .toggle-btn { .splitpanes-horizontal > .splitpanes-splitter .toggle-btn {
width: 49px; width: 49px;
height: 8px; height: 8px;
} }
@@ -365,20 +308,20 @@ export default {
left: 50%; left: 50%;
} }
.splitpanes--horizontal > .splitpanes__splitter .toggle-btns.both .toggle-btn:first-child { .splitpanes-horizontal > .splitpanes-splitter .toggle-btns.both .toggle-btn:first-child {
border-radius: var(--border-radius-small) 0 0 var(--border-radius-small); border-radius: var(--border-radius-small) 0 0 var(--border-radius-small);
} }
.splitpanes--horizontal > .splitpanes__splitter .toggle-btns.both .toggle-btn:last-child { .splitpanes-horizontal > .splitpanes-splitter .toggle-btns.both .toggle-btn:last-child {
border-radius: 0 var(--border-radius-small) var(--border-radius-small) 0; border-radius: 0 var(--border-radius-small) var(--border-radius-small) 0;
margin-left: -1px; margin-left: -1px;
} }
.splitpanes--vertical > .splitpanes__splitter .toggle-btns.both .toggle-btn:first-child { .splitpanes-vertical > .splitpanes-splitter .toggle-btns.both .toggle-btn:first-child {
border-radius: var(--border-radius-small) var(--border-radius-small) 0 0; border-radius: var(--border-radius-small) var(--border-radius-small) 0 0;
} }
.splitpanes--vertical > .splitpanes__splitter .toggle-btns.both .toggle-btn:last-child { .splitpanes-vertical > .splitpanes-splitter .toggle-btns.both .toggle-btn:last-child {
border-radius: 0 0 var(--border-radius-small) var(--border-radius-small); border-radius: 0 0 var(--border-radius-small) var(--border-radius-small);
margin-top: -1px; margin-top: -1px;
} }

54
src/splitter.js Normal file
View File

@@ -0,0 +1,54 @@
export default {
bindEvents (onMouseMove, onMouseUp) {
// Passive: false to prevent scrolling while touch dragging.
document.addEventListener('mousemove', onMouseMove, { passive: false })
document.addEventListener('mouseup', onMouseUp)
if ('ontouchstart' in window) {
document.addEventListener('touchmove', onMouseMove, { passive: false })
document.addEventListener('touchend', onMouseUp)
}
},
unbindEvents (onMouseMove, onMouseUp) {
document.removeEventListener('mousemove', onMouseMove, { passive: false })
document.removeEventListener('mouseup', onMouseUp)
if ('ontouchstart' in window) {
document.removeEventListener('touchmove', onMouseMove, { passive: false })
document.removeEventListener('touchend', onMouseUp)
}
},
// Get the cursor position relative to the splitpane container.
getCurrentMouseDrag (event, container) {
const rect = container.getBoundingClientRect()
const { clientX, clientY } = ('ontouchstart' in window && event.touches)
? event.touches[0]
: event
return {
x: clientX - rect.left,
y: clientY - rect.top
}
},
// Returns the drag percentage of the splitter relative to the 2 panes it's inbetween.
getCurrentDragPercentage (event, container, isHorisontal) {
let drag = this.getCurrentMouseDrag(event, container)
drag = drag[isHorisontal ? 'y' : 'x']
const containerSize = container[isHorisontal ? 'clientHeight' : 'clientWidth']
return drag * 100 / containerSize
},
calculateOffset (paneBeforeMax, paneAfterMax, dragPercentage) {
const paneBeforeMaxReached = paneBeforeMax < 100 && (dragPercentage >= paneBeforeMax)
const paneAfterMaxReached = paneAfterMax < 100 && (dragPercentage <= 100 - paneAfterMax)
// Prevent dragging beyond pane max.
if (paneBeforeMaxReached || paneAfterMaxReached) {
return paneBeforeMaxReached ? paneBeforeMax : Math.max(100 - paneAfterMax, 0)
} else {
return Math.min(Math.max(dragPercentage, 0), paneBeforeMax)
}
}
}