mirror of
https://github.com/lana-k/sqliteviz.git
synced 2025-12-06 18:18:53 +08:00
not greedy splitter
This commit is contained in:
1202
package-lock.json
generated
1202
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -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",
|
||||||
|
|||||||
@@ -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 () {
|
||||||
container: null,
|
return {
|
||||||
paneBefore: null,
|
container: null,
|
||||||
paneAfter: null,
|
paneBefore: this.before,
|
||||||
beforeMinimising: 20,
|
paneAfter: this.after,
|
||||||
touch: {
|
beforeMinimising: this.before.size,
|
||||||
mouseDown: false,
|
touch: {
|
||||||
dragging: false
|
mouseDown: 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>
|
||||||
|
|||||||
@@ -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>
|
|
||||||
Reference in New Issue
Block a user