1
0
mirror of https://github.com/lana-k/sqliteviz.git synced 2025-12-07 02:28:54 +08:00

not greedy splitter

This commit is contained in:
lana-k
2020-10-22 22:47:15 +02:00
parent b39a6bdb86
commit fdd50b2f86
4 changed files with 1057 additions and 421 deletions

1202
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -12,9 +12,9 @@
"codemirror": "^5.57.0", "codemirror": "^5.57.0",
"core-js": "^3.6.5", "core-js": "^3.6.5",
"nanoid": "^3.1.12", "nanoid": "^3.1.12",
"plotly.js": "^1.54.6", "plotly.js": "^1.57.1",
"react": "^16.13.1", "react": "^16.13.1",
"react-chart-editor": "^0.41.7", "react-chart-editor": "^0.42.0",
"react-dom": "^16.13.1", "react-dom": "^16.13.1",
"sql.js": "^1.3.0", "sql.js": "^1.3.0",
"sqlite-parser": "^1.0.1", "sqlite-parser": "^1.0.1",

View File

@@ -1,8 +1,13 @@
<template> <template>
<div <div
ref="container" ref="container"
:class="['splitpanes', `splitpanes--${horizontal ? 'horizontal' : 'vertical'}`, { 'splitpanes--dragging': touch.dragging }]" :class="[
'splitpanes',
`splitpanes--${horizontal ? 'horizontal' : 'vertical'}`,
{ 'splitpanes--dragging': touch.dragging }
]"
> >
<div class="movable-splitter" ref="movableSplitter" :style="movableSplitterStyle" />
<div <div
class="splitpanes__pane" class="splitpanes__pane"
ref="left" ref="left"
@@ -12,13 +17,21 @@
> >
<slot name="left-pane" /> <slot name="left-pane" />
</div> </div>
<!-- Splitter start-->
<splitter <div
class="splitpanes__splitter"
@mousedown="onMouseDown" @mousedown="onMouseDown"
@toggle="toggleFirstPane" @touchstart="onMouseDown"
:expanded="paneBefore.size !== 0" >
/> <div class="toggle-btn" @click="toggleFirstPane">
<img
class="direction-icon"
:src="require('@/assets/images/chevron.svg')"
:style="directionIconStyle"
>
</div>
</div>
<!-- splitter end -->
<div <div
class="splitpanes__pane" class="splitpanes__pane"
ref="right" ref="right"
@@ -30,33 +43,58 @@
</template> </template>
<script> <script>
import Splitter from '@/components/splitter'
export default { export default {
name: 'splitpanes', name: 'splitpanes',
components: { Splitter },
props: { props: {
horizontal: { type: Boolean, default: false }, horizontal: { type: Boolean, default: false },
before: { type: Object }, before: { type: Object },
after: { type: Object } after: { type: Object }
}, },
data: () => ({ data () {
return {
container: null, container: null,
paneBefore: null, paneBefore: this.before,
paneAfter: null, paneAfter: this.after,
beforeMinimising: 20, beforeMinimising: this.before.size,
touch: { touch: {
mouseDown: false, mouseDown: false,
dragging: false dragging: false
},
movableSplitter: {
top: 0,
left: 0,
visibility: 'hidden'
} }
}), }
},
computed: { computed: {
styles () { styles () {
return [ return [
{ [this.horizontal ? 'height' : 'width']: `${this.paneBefore.size}%` }, { [this.horizontal ? 'height' : 'width']: `${this.paneBefore.size}%` },
{ [this.horizontal ? 'height' : 'width']: `${this.paneAfter.size}%` } { [this.horizontal ? 'height' : 'width']: `${this.paneAfter.size}%` }
] ]
},
movableSplitterStyle () {
const style = { ...this.movableSplitter }
style.top += '%'
style.left += '%'
return style
},
expanded () {
return this.paneBefore.size !== 0
},
directionIconStyle () {
const translation = 'translate(-50%, -50%)'
if (this.horizontal) {
return {
transform: `${translation} ${this.expanded ? 'rotate(-90deg)' : 'rotate(90deg)'}`
}
} else {
return {
transform: `${translation} ${this.expanded ? 'rotate(180deg)' : ''}`
}
}
} }
}, },
@@ -92,12 +130,28 @@ export default {
// Prevent scrolling while touch dragging (only works with an active event, eg. passive: false). // Prevent scrolling while touch dragging (only works with an active event, eg. passive: false).
event.preventDefault() event.preventDefault()
this.touch.dragging = true this.touch.dragging = true
this.calculatePanesSize(this.getCurrentMouseDrag(event)) this.$set(this.movableSplitter, 'visibility', 'visible')
this.moveSplitter(event)
} }
}, },
onMouseUp () { onMouseUp () {
this.touch.mouseDown = false this.touch.mouseDown = false
if (this.touch.dragging) {
const dragPercentage = this.horizontal
? this.movableSplitter.top
: this.movableSplitter.left
this.paneBefore.size = dragPercentage
this.paneAfter.size = 100 - dragPercentage
this.movableSplitter = {
top: 0,
left: 0,
visibility: 'hidden'
}
}
// Keep dragging flag until click event is finished (click happens immediately after mouseup) // Keep dragging flag until click event is finished (click happens immediately after mouseup)
// in order to prevent emitting `splitter-click` event if splitter was dragged. // in order to prevent emitting `splitter-click` event if splitter was dragged.
setTimeout(() => { setTimeout(() => {
@@ -109,8 +163,9 @@ export default {
// Get the cursor position relative to the splitpane container. // Get the cursor position relative to the splitpane container.
getCurrentMouseDrag (event) { getCurrentMouseDrag (event) {
const rect = this.container.getBoundingClientRect() const rect = this.container.getBoundingClientRect()
const { clientX, clientY } = ('ontouchstart' in window && event.touches) ? event.touches[0] : event const { clientX, clientY } = ('ontouchstart' in window && event.touches)
? event.touches[0]
: event
return { return {
x: clientX - rect.left, x: clientX - rect.left,
y: clientY - rect.top y: clientY - rect.top
@@ -126,27 +181,26 @@ export default {
return drag * 100 / containerSize return drag * 100 / containerSize
}, },
calculatePanesSize (drag) { moveSplitter (event) {
const dragPercentage = this.getCurrentDragPercentage(drag) const dragPercentage = this.getCurrentDragPercentage(this.getCurrentMouseDrag(event))
// If not pushing other panes, panes to resize are right before and right after splitter.
const paneBefore = this.paneBefore const paneBefore = this.paneBefore
const paneAfter = this.paneAfter const paneAfter = this.paneAfter
const paneBeforeMaxReached = paneBefore.max < 100 && (dragPercentage >= paneBefore.max) const paneBeforeMaxReached = paneBefore.max < 100 && (dragPercentage >= paneBefore.max)
const paneAfterMaxReached = paneAfter.max < 100 && (dragPercentage <= 100 - paneAfter.max) const paneAfterMaxReached = paneAfter.max < 100 && (dragPercentage <= 100 - paneAfter.max)
const dir = this.horizontal ? 'top' : 'left'
// Prevent dragging beyond pane max. // Prevent dragging beyond pane max.
if (paneBeforeMaxReached || paneAfterMaxReached) { if (paneBeforeMaxReached || paneAfterMaxReached) {
if (paneBeforeMaxReached) { if (paneBeforeMaxReached) {
paneBefore.size = paneBefore.max this.$set(this.movableSplitter, dir, paneBefore.max)
paneAfter.size = Math.max(100 - paneBefore.max, 0)
} else { } else {
paneBefore.size = Math.max(100 - paneAfter.max, 0) this.$set(this.movableSplitter, dir, Math.max(100 - paneAfter.max, 0))
paneAfter.size = paneAfter.max
} }
return } else {
this.$set(this.movableSplitter, dir, Math.min(Math.max(dragPercentage, 0), paneBefore.max))
} }
paneBefore.size = Math.min(Math.max(dragPercentage, 0), paneBefore.max)
paneAfter.size = Math.min(Math.max(100 - dragPercentage, 0), paneAfter.max)
}, },
toggleFirstPane () { toggleFirstPane () {
if (this.paneBefore.size > 0) { if (this.paneBefore.size > 0) {
@@ -160,10 +214,6 @@ export default {
}, },
mounted () { mounted () {
this.container = this.$refs.container this.container = this.$refs.container
},
created () {
this.paneBefore = this.before
this.paneAfter = this.after
} }
} }
</script> </script>
@@ -172,6 +222,7 @@ export default {
.splitpanes { .splitpanes {
display: flex; display: flex;
height: 100%; height: 100%;
position: relative;
} }
.splitpanes--vertical {flex-direction: row;} .splitpanes--vertical {flex-direction: row;}
@@ -183,4 +234,68 @@ export default {
height: 100%; height: 100%;
overflow: auto; overflow: auto;
} }
/* Splitter */
.splitpanes--vertical > .splitpanes__splitter,
.splitpanes--vertical.splitpanes--dragging {
cursor: col-resize;
}
.splitpanes--horizontal > .splitpanes__splitter,
.splitpanes--horizontal.splitpanes--dragging {
cursor: row-resize;
}
.splitpanes__splitter {
touch-action: none;
background-color: var(--color-bg-light-2);
box-sizing: border-box;
position: relative;
flex-shrink: 0;
z-index: 1;
}
.movable-splitter {
position: absolute;
background-color:rgba(162, 177, 198, 0.5);
}
.splitpanes--vertical > .splitpanes__splitter,
.splitpanes--vertical .movable-splitter {
width: 3px;
z-index: 5;
height: 100%
}
.splitpanes--horizontal > .splitpanes__splitter,
.splitpanes--horizontal .movable-splitter {
height: 3px;
width: 100%;
}
.splitpanes__splitter .toggle-btn {
background-color: var(--color-bg-light-2);
border-radius: var(--border-radius-small);
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.splitpanes__splitter .toggle-btn:hover {
cursor: pointer;
}
.splitpanes--vertical .toggle-btn {
height: 68px;
width: 15px;
}
.splitpanes--horizontal .toggle-btn {
width: 68px;
height: 15px;
}
.splitpanes__splitter .toggle-btn .direction-icon {
position: absolute;
top: 50%;
left: 50%;
}
</style> </style>

View File

@@ -1,85 +0,0 @@
<template>
<div
class="splitpanes__splitter"
@mousedown="$emit('mousedown')"
@touchstart="$emit('mousedown')"
>
<div class="toggle-btn" @click="$emit('toggle')">
<img
class="direction-icon"
:src="require('@/assets/images/chevron.svg')"
:style="directionIconStyle"
>
</div>
</div>
</template>
<script>
export default {
name: 'splitter',
props: ['expanded'],
computed: {
directionIconStyle () {
const translation = 'translate(-50%, -50%)'
if (this.$parent.horizontal) {
return {
transform: `${translation} ${this.expanded ? 'rotate(-90deg)' : 'rotate(90deg)'}`
}
} else {
return {
transform: `${translation} ${this.expanded ? 'rotate(180deg)' : ''}`
}
}
}
}
}
</script>
<style>
.splitpanes--vertical > .splitpanes__splitter {min-width: 1px;cursor: col-resize;}
.splitpanes--horizontal > .splitpanes__splitter {min-height: 1px; cursor: row-resize;}
.splitpanes__splitter {
touch-action: none;
background-color: var(--color-bg-light-2);
box-sizing: border-box;
position: relative;
flex-shrink: 0;
z-index: 1;
}
.splitpanes--vertical > .splitpanes__splitter {
width: 3px;
z-index: 3;
}
.splitpanes--horizontal > .splitpanes__splitter {
height: 3px;
width: 100%;
}
.splitpanes__splitter .toggle-btn {
background-color: var(--color-bg-light-2);
border-radius: var(--border-radius-small);
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.splitpanes__splitter .toggle-btn:hover {
cursor: pointer;
}
.splitpanes--vertical .toggle-btn {
height: 68px;
width: 15px;
}
.splitpanes--horizontal .toggle-btn {
width: 68px;
height: 15px;
}
.splitpanes__splitter .toggle-btn .direction-icon {
position: absolute;
top: 50%;
left: 50%;
}
</style>