mirror of
https://github.com/lana-k/sqliteviz.git
synced 2025-12-06 10:08:52 +08:00
395 lines
8.9 KiB
Vue
395 lines
8.9 KiB
Vue
<template>
|
|
<div
|
|
ref="container"
|
|
:class="[
|
|
'splitpanes',
|
|
`splitpanes-${horizontal ? 'horizontal' : 'vertical'}`,
|
|
{ 'splitpanes-dragging': dragging }
|
|
]"
|
|
>
|
|
<div
|
|
ref="movableSplitter"
|
|
class="movable-splitter"
|
|
:style="movableSplitterStyle"
|
|
/>
|
|
<div
|
|
ref="left"
|
|
class="splitpanes-pane"
|
|
:size="paneBefore.size"
|
|
max-size="30"
|
|
:style="styles.before"
|
|
>
|
|
<slot name="left-pane" />
|
|
</div>
|
|
<!-- Splitter start-->
|
|
<div
|
|
class="splitpanes-splitter"
|
|
@mousedown="bindEvents"
|
|
@touchstart="bindEvents"
|
|
>
|
|
<div
|
|
:class="[
|
|
'toggle-btns',
|
|
{
|
|
both:
|
|
after.max === 100 &&
|
|
before.max === 100 &&
|
|
paneAfter.size > 0 &&
|
|
paneBefore.size > 0
|
|
}
|
|
]"
|
|
>
|
|
<div
|
|
v-if="after.max === 100 && paneAfter.size > 0"
|
|
class="toggle-btn"
|
|
@click="togglePane(paneBefore)"
|
|
>
|
|
<img
|
|
class="direction-icon"
|
|
src="~@/assets/images/chevron.svg"
|
|
:style="directionBeforeIconStyle"
|
|
/>
|
|
</div>
|
|
<div
|
|
v-if="before.max === 100 && paneBefore.size > 0"
|
|
class="toggle-btn"
|
|
@click="togglePane(paneAfter)"
|
|
>
|
|
<img
|
|
class="direction-icon"
|
|
src="~@/assets/images/chevron.svg"
|
|
:style="directionAfterIconStyle"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- splitter end -->
|
|
<div ref="right" class="splitpanes-pane" :style="styles.after">
|
|
<slot name="right-pane" />
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import splitter from './splitter'
|
|
|
|
export default {
|
|
name: 'Splitpanes',
|
|
props: {
|
|
horizontal: { type: Boolean, default: false },
|
|
before: { type: Object },
|
|
after: { type: Object },
|
|
default: {
|
|
type: Object,
|
|
default: () => {
|
|
return {
|
|
before: 50,
|
|
after: 50
|
|
}
|
|
}
|
|
}
|
|
},
|
|
emits: [],
|
|
data() {
|
|
return {
|
|
container: null,
|
|
paneBefore: this.before,
|
|
paneAfter: this.after,
|
|
beforeMinimising:
|
|
!this.after.size || !this.before.size
|
|
? this.default
|
|
: {
|
|
before: this.before.size,
|
|
after: this.after.size
|
|
},
|
|
dragging: false,
|
|
movableSplitter: {
|
|
top: 0,
|
|
left: 0,
|
|
visibility: 'hidden'
|
|
}
|
|
}
|
|
},
|
|
computed: {
|
|
styles() {
|
|
return {
|
|
before: {
|
|
[this.horizontal ? 'height' : 'width']: `${this.paneBefore.size}%`
|
|
},
|
|
after: {
|
|
[this.horizontal ? 'height' : 'width']: `${this.paneAfter.size}%`
|
|
}
|
|
}
|
|
},
|
|
movableSplitterStyle() {
|
|
const style = { ...this.movableSplitter }
|
|
style.top += '%'
|
|
style.left += '%'
|
|
return style
|
|
},
|
|
directionBeforeIconStyle() {
|
|
const expanded = this.paneBefore.size !== 0
|
|
const translation = 'translate(-50%, -50%) '
|
|
let rotation = ''
|
|
|
|
if (this.horizontal) {
|
|
rotation = expanded ? 'rotate(90deg)' : 'rotate(-90deg)'
|
|
} else {
|
|
rotation = expanded ? 'rotate(0deg)' : 'rotate(180deg)'
|
|
}
|
|
|
|
return {
|
|
transform: translation + rotation
|
|
}
|
|
},
|
|
directionAfterIconStyle() {
|
|
const expanded = this.paneAfter.size !== 0
|
|
const translation = 'translate(-50%, -50%)'
|
|
let rotation = ''
|
|
|
|
if (this.horizontal) {
|
|
rotation = expanded ? 'rotate(-90deg)' : 'rotate(90deg)'
|
|
} else {
|
|
rotation = expanded ? 'rotate(180deg)' : 'rotate(0deg)'
|
|
}
|
|
|
|
return {
|
|
transform: translation + rotation
|
|
}
|
|
}
|
|
},
|
|
mounted() {
|
|
this.container = this.$refs.container
|
|
},
|
|
|
|
methods: {
|
|
bindEvents() {
|
|
// Passive: false to prevent scrolling while touch dragging.
|
|
document.addEventListener('mousemove', this.onMouseMove, {
|
|
passive: false
|
|
})
|
|
document.addEventListener('mouseup', this.onMouseUp)
|
|
|
|
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)
|
|
}
|
|
},
|
|
|
|
onMouseMove(event) {
|
|
event.preventDefault()
|
|
this.dragging = true
|
|
this.movableSplitter.visibility = 'visible'
|
|
this.moveSplitter(event)
|
|
},
|
|
|
|
onMouseUp() {
|
|
if (this.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'
|
|
}
|
|
|
|
this.dragging = false
|
|
}
|
|
|
|
this.unbindEvents()
|
|
},
|
|
|
|
moveSplitter(event) {
|
|
const splitterInfo = {
|
|
container: this.container,
|
|
paneBeforeMax: this.paneBefore.max,
|
|
paneAfterMax: this.paneAfter.max,
|
|
isHorisontal: this.horizontal
|
|
}
|
|
const offset = splitter.calculateOffset(event, splitterInfo)
|
|
const dir = this.horizontal ? 'top' : 'left'
|
|
this.movableSplitter[dir] = offset
|
|
},
|
|
|
|
togglePane(pane) {
|
|
if (pane.size > 0) {
|
|
this.beforeMinimising.before = this.paneBefore.size
|
|
this.beforeMinimising.after = this.paneAfter.size
|
|
pane.size = 0
|
|
const otherPane =
|
|
pane === this.paneBefore ? this.paneAfter : this.paneBefore
|
|
otherPane.size = 100 - pane.size
|
|
} else {
|
|
this.paneBefore.size = this.beforeMinimising.before
|
|
this.paneAfter.size = this.beforeMinimising.after
|
|
}
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style>
|
|
.splitpanes {
|
|
display: flex;
|
|
height: 100%;
|
|
position: relative;
|
|
}
|
|
|
|
.splitpanes-vertical {
|
|
flex-direction: row;
|
|
}
|
|
.splitpanes-horizontal {
|
|
flex-direction: column;
|
|
}
|
|
.splitpanes-dragging * {
|
|
user-select: none;
|
|
}
|
|
|
|
.splitpanes-pane {
|
|
width: 100%;
|
|
height: 100%;
|
|
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);
|
|
box-sizing: border-box;
|
|
position: relative;
|
|
flex-shrink: 0;
|
|
z-index: 1;
|
|
}
|
|
|
|
.splitpanes-horizontal > .splitpanes-splitter {
|
|
border-top: 1px solid var(--color-border-light);
|
|
border-bottom: 1px solid var(--color-border-light);
|
|
}
|
|
|
|
.splitpanes-vertical > .splitpanes-splitter {
|
|
border-left: 1px solid var(--color-border-light);
|
|
border-right: 1px solid var(--color-border-light);
|
|
}
|
|
|
|
.movable-splitter {
|
|
position: absolute;
|
|
background-color: rgba(162, 177, 198, 0.5);
|
|
}
|
|
|
|
.splitpanes-vertical > .splitpanes-splitter,
|
|
.splitpanes-vertical > .movable-splitter {
|
|
width: 8px;
|
|
z-index: 5;
|
|
height: 100%;
|
|
}
|
|
|
|
.splitpanes-horizontal > .splitpanes-splitter,
|
|
.splitpanes-horizontal > .movable-splitter {
|
|
height: 8px;
|
|
width: 100%;
|
|
z-index: 5;
|
|
}
|
|
/* Toggle buttons */
|
|
.toggle-btns {
|
|
position: absolute;
|
|
top: 50%;
|
|
left: 50%;
|
|
transform: translate(-50%, -50%);
|
|
display: flex;
|
|
}
|
|
|
|
.splitpanes-vertical > .splitpanes-splitter .toggle-btns {
|
|
flex-direction: column;
|
|
}
|
|
|
|
.splitpanes-horizontal > .splitpanes-splitter .toggle-btns {
|
|
flex-direction: row;
|
|
}
|
|
|
|
.toggle-btn {
|
|
background-color: var(--color-border-light);
|
|
border-radius: var(--border-radius-small);
|
|
border: 1px solid var(--color-border);
|
|
box-sizing: border-box;
|
|
position: relative;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.splitpanes-vertical > .splitpanes-splitter .toggle-btn {
|
|
height: 49px;
|
|
width: 8px;
|
|
}
|
|
|
|
.splitpanes-horizontal > .splitpanes-splitter .toggle-btn {
|
|
width: 49px;
|
|
height: 8px;
|
|
}
|
|
|
|
.toggle-btn .direction-icon {
|
|
position: absolute;
|
|
top: 50%;
|
|
left: 50%;
|
|
}
|
|
|
|
.splitpanes-horizontal
|
|
> .splitpanes-splitter
|
|
.toggle-btns.both
|
|
.toggle-btn:first-child {
|
|
border-radius: var(--border-radius-small) 0 0 var(--border-radius-small);
|
|
}
|
|
|
|
.splitpanes-horizontal
|
|
> .splitpanes-splitter
|
|
.toggle-btns.both
|
|
.toggle-btn:last-child {
|
|
border-radius: 0 var(--border-radius-small) var(--border-radius-small) 0;
|
|
margin-left: -1px;
|
|
}
|
|
|
|
.splitpanes-vertical
|
|
> .splitpanes-splitter
|
|
.toggle-btns.both
|
|
.toggle-btn:first-child {
|
|
border-radius: var(--border-radius-small) var(--border-radius-small) 0 0;
|
|
}
|
|
|
|
.splitpanes-vertical
|
|
> .splitpanes-splitter
|
|
.toggle-btns.both
|
|
.toggle-btn:last-child {
|
|
border-radius: 0 0 var(--border-radius-small) var(--border-radius-small);
|
|
margin-top: -1px;
|
|
}
|
|
</style>
|