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

add splitters

This commit is contained in:
lana-k
2020-09-25 14:57:49 +02:00
parent 708714b3a5
commit 22b8b86138
6 changed files with 345 additions and 47 deletions

View File

@@ -0,0 +1,3 @@
<svg width="8" height="12" viewBox="0 0 8 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0.721924 9.93097L4.85292 5.79997L0.721924 1.66897L1.99992 0.399973L7.39992 5.79997L1.99992 11.2L0.721924 9.93097Z" fill="#506784"/>
</svg>

After

Width:  |  Height:  |  Size: 243 B

View File

@@ -1,19 +1,16 @@
/* width */
::-webkit-scrollbar {
width: 10px;
width: 5px;
}
/* Track */
::-webkit-scrollbar-track {
background: #f1f1f1;
background: #ebf0f8;
}
/* Handle */
::-webkit-scrollbar-thumb {
background: #888;
background: var(--color-accent);
border-radius: 10px;
}
/* Handle on hover */
::-webkit-scrollbar-thumb:hover {
background: #555;
}

View File

@@ -0,0 +1,186 @@
<template>
<div
ref="container"
:class="['splitpanes', `splitpanes--${horizontal ? 'horizontal' : 'vertical'}`, { 'splitpanes--dragging': touch.dragging }]"
>
<div
class="splitpanes__pane"
ref="left"
:size="paneBefore.size"
max-size="30"
:style="styles[0]"
>
<slot name="left-pane" />
</div>
<splitter
@mousedown="onMouseDown"
@toggle="toggleFirstPane"
:expanded="paneBefore.size !== 0"
/>
<div
class="splitpanes__pane"
ref="right"
:style="styles[1]"
>
<slot name="right-pane" />
</div>
</div>
</template>
<script>
import Splitter from '@/components/splitter'
export default {
name: 'splitpanes',
components: { Splitter },
props: {
horizontal: { type: Boolean, default: false },
before: { type: Object },
after: { type: Object }
},
data: () => ({
container: null,
paneBefore: null,
paneAfter: null,
beforeMinimising: 20,
touch: {
mouseDown: false,
dragging: false
}
}),
computed: {
styles () {
return [
{ [this.horizontal ? 'height' : 'width']: `${this.paneBefore.size}%` },
{ [this.horizontal ? 'height' : 'width']: `${this.paneAfter.size}%` }
]
}
},
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 () {
this.bindEvents()
this.touch.mouseDown = true
},
onMouseMove (event) {
if (this.touch.mouseDown) {
// Prevent scrolling while touch dragging (only works with an active event, eg. passive: false).
event.preventDefault()
this.touch.dragging = true
this.calculatePanesSize(this.getCurrentMouseDrag(event))
}
},
onMouseUp () {
this.touch.mouseDown = false
// 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.
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
},
calculatePanesSize (drag) {
const dragPercentage = this.getCurrentDragPercentage(drag)
// If not pushing other panes, panes to resize are right before and right after splitter.
const paneBefore = this.paneBefore
const paneAfter = this.paneAfter
const paneBeforeMaxReached = paneBefore.max < 100 && (dragPercentage >= paneBefore.max)
const paneAfterMaxReached = paneAfter.max < 100 && (dragPercentage <= 100 - paneAfter.max)
// Prevent dragging beyond pane max.
if (paneBeforeMaxReached || paneAfterMaxReached) {
if (paneBeforeMaxReached) {
paneBefore.size = paneBefore.max
paneAfter.size = Math.max(100 - paneBefore.max, 0)
} else {
paneBefore.size = Math.max(100 - paneAfter.max, 0)
paneAfter.size = paneAfter.max
}
return
}
paneBefore.size = Math.min(Math.max(dragPercentage, 0), paneBefore.max)
paneAfter.size = Math.min(Math.max(100 - dragPercentage, 0), paneAfter.max)
},
toggleFirstPane () {
if (this.paneBefore.size > 0) {
this.beforeMinimising = this.paneBefore.size
this.paneBefore.size = 0
} else {
this.paneBefore.size = this.beforeMinimising
}
this.paneAfter.size = 100 - this.paneBefore.size
}
},
mounted () {
this.container = this.$refs.container
},
created () {
this.paneBefore = this.before
this.paneAfter = this.after
}
}
</script>
<style>
.splitpanes {
display: flex;
height: 100%;
}
.splitpanes--vertical {flex-direction: row;}
.splitpanes--horizontal {flex-direction: column;}
.splitpanes--dragging * {user-select: none;}
.splitpanes__pane {
width: 100%;
height: 100%;
overflow: auto;
}
</style>

View File

@@ -0,0 +1,85 @@
<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: 2px;
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>

View File

@@ -1,9 +1,21 @@
<template>
<div>
<div style="display:flex">
<div>
<splitpanes
class="schema-tabs-splitter"
:before="{ size: 20, max: 30 }"
:after="{ size: 80, max: 100 }"
>
<div slot="left-pane">
<schema :schema="schema"/>
</div>
<div slot="right-pane">
<splitpanes
class="query-results-splitter"
horizontal
:before="{ size: 50, max: 50 }"
:after="{ size: 50, max: 100 }"
>
<div slot="left-pane">
<div>
<codemirror v-model="code" :options="cmOptions" @changes="onCmChange" />
<button id="execute" class="button" @click="execEditorContents">Execute</button>
@@ -12,12 +24,11 @@
</label>
<div id="error" class="error"></div>
<pre ref="output" id="output">Results will be displayed here</pre>
<sql-table :data="result" />
</div>
</div>
<div slot="right-pane">
<PlotlyEditor
:data="state.data"
:layout="state.layout"
@@ -32,11 +43,16 @@
:advancedTraceTypeSelector="true"
/>
</div>
</splitpanes>
</div>
</splitpanes>
</div>
</template>
<script>
import SqlTable from '@/components/SqlTable'
import Schema from '@/components/Schema'
import Splitpanes from '@/components/splitpanes'
import plotly from 'plotly.js/dist/plotly'
import 'react-chart-editor/lib/react-chart-editor.min.css'
@@ -55,7 +71,8 @@ export default {
components: {
codemirror,
SqlTable,
Schema
Schema,
Splitpanes
},
data () {
return {
@@ -179,3 +196,13 @@ export default {
}
}
</script>
<style>
.schema-tabs-splitter {
height: 100%;
margin-left: 6px;
}
.query-results-splitter {
height: calc(100vh - 74px);
margin-top: 6px;
}
</style>

View File

@@ -1,4 +1,4 @@
const CopyPlugin = require('copy-webpack-plugin');
const CopyPlugin = require('copy-webpack-plugin')
module.exports = {
configureWebpack: {
@@ -7,7 +7,7 @@ module.exports = {
// This wasm file will be fetched dynamically when we initialize sql.js
// It is important that we do not change its name, and that it is in the same folder as the js
{ from: 'node_modules/sql.js/dist/sql-wasm.wasm', to: 'js/' },
{ from: 'node_modules/sql.js/dist/worker.sql-wasm.js', to: 'js/' },
{ from: 'node_modules/sql.js/dist/worker.sql-wasm.js', to: 'js/' }
])
]
}