refactor: wrapping element with drag and resize behavior as a standalone component
This commit is contained in:
parent
b6eebda177
commit
124627d724
|
@ -16,6 +16,7 @@ import ToolbarControlWidget from '@/components/common/ToolbarControlWidget.vue'
|
|||
import { EventsOn, WindowIsFullscreen, WindowIsMaximised, WindowToggleMaximise } from 'wailsjs/runtime/runtime.js'
|
||||
import { isMacOS } from '@/utils/platform.js'
|
||||
import iconUrl from '@/assets/images/icon.png'
|
||||
import ResizeableWrapper from '@/components/common/ResizeableWrapper.vue'
|
||||
|
||||
const themeVars = useThemeVars()
|
||||
|
||||
|
@ -25,8 +26,6 @@ const props = defineProps({
|
|||
|
||||
const data = reactive({
|
||||
navMenuWidth: 60,
|
||||
hoverResize: false,
|
||||
resizing: false,
|
||||
toolbarHeight: 45,
|
||||
})
|
||||
|
||||
|
@ -38,32 +37,9 @@ const logPaneRef = ref(null)
|
|||
// provide('preferences', preferences)
|
||||
|
||||
const saveSidebarWidth = debounce(prefStore.savePreferences, 1000, { trailing: true })
|
||||
const handleResize = (evt) => {
|
||||
if (data.resizing) {
|
||||
prefStore.setAsideWidth(Math.max(evt.clientX - data.navMenuWidth, 300))
|
||||
const handleResize = () => {
|
||||
saveSidebarWidth()
|
||||
}
|
||||
}
|
||||
|
||||
const stopResize = () => {
|
||||
data.resizing = false
|
||||
document.removeEventListener('mousemove', handleResize)
|
||||
document.removeEventListener('mouseup', stopResize)
|
||||
}
|
||||
|
||||
const startResize = () => {
|
||||
data.resizing = true
|
||||
document.addEventListener('mousemove', handleResize)
|
||||
document.addEventListener('mouseup', stopResize)
|
||||
}
|
||||
|
||||
const asideWidthVal = computed(() => {
|
||||
return prefStore.behavior.asideWidth + 'px'
|
||||
})
|
||||
|
||||
const dragging = computed(() => {
|
||||
return data.hoverResize || data.resizing
|
||||
})
|
||||
|
||||
watch(
|
||||
() => tabStore.nav,
|
||||
|
@ -166,15 +142,6 @@ onMounted(async () => {
|
|||
</transition>
|
||||
</n-space>
|
||||
</div>
|
||||
<div
|
||||
:class="{
|
||||
'resize-divider-hover': data.hoverResize,
|
||||
'resize-divider-drag': data.resizing,
|
||||
}"
|
||||
class="resize-divider resize-divider-hide"
|
||||
@mousedown="startResize"
|
||||
@mouseout="data.hoverResize = false"
|
||||
@mouseover="data.hoverResize = true" />
|
||||
<!-- browser tabs -->
|
||||
<div v-show="tabStore.nav === 'browser'" class="app-toolbar-tab flex-item-expand">
|
||||
<content-value-tab />
|
||||
|
@ -196,23 +163,19 @@ onMounted(async () => {
|
|||
style="--wails-draggable: none">
|
||||
<nav-menu v-model:value="tabStore.nav" :width="data.navMenuWidth" />
|
||||
<!-- browser page -->
|
||||
<div v-show="tabStore.nav === 'browser'" :class="{ dragging }" class="flex-box-h flex-item-expand">
|
||||
<div :style="{ width: asideWidthVal }" class="app-side flex-box-h flex-item">
|
||||
<div v-show="tabStore.nav === 'browser'" class="flex-box-h flex-item-expand">
|
||||
<resizeable-wrapper
|
||||
v-model:size="prefStore.behavior.asideWidth"
|
||||
:min-size="300"
|
||||
:offset="data.navMenuWidth"
|
||||
class="flex-item"
|
||||
@update:size="handleResize">
|
||||
<browser-pane
|
||||
v-for="t in tabStore.tabs"
|
||||
v-show="get(tabStore.currentTab, 'name') === t.name"
|
||||
:key="t.name"
|
||||
class="flex-item-expand" />
|
||||
<div
|
||||
:class="{
|
||||
'resize-divider-hover': data.hoverResize,
|
||||
'resize-divider-drag': data.resizing,
|
||||
}"
|
||||
class="resize-divider"
|
||||
@mousedown="startResize"
|
||||
@mouseout="data.hoverResize = false"
|
||||
@mouseover="data.hoverResize = true" />
|
||||
</div>
|
||||
class="app-side flex-item-expand" />
|
||||
</resizeable-wrapper>
|
||||
<content-pane
|
||||
v-for="t in tabStore.tabs"
|
||||
v-show="get(tabStore.currentTab, 'name') === t.name"
|
||||
|
@ -222,19 +185,15 @@ onMounted(async () => {
|
|||
</div>
|
||||
|
||||
<!-- server list page -->
|
||||
<div v-show="tabStore.nav === 'server'" :class="{ dragging }" class="flex-box-h flex-item-expand">
|
||||
<div :style="{ width: asideWidthVal }" class="app-side flex-box-h flex-item">
|
||||
<connection-pane class="flex-item-expand" />
|
||||
<div
|
||||
:class="{
|
||||
'resize-divider-hover': data.hoverResize,
|
||||
'resize-divider-drag': data.resizing,
|
||||
}"
|
||||
class="resize-divider"
|
||||
@mousedown="startResize"
|
||||
@mouseout="data.hoverResize = false"
|
||||
@mouseover="data.hoverResize = true" />
|
||||
</div>
|
||||
<div v-show="tabStore.nav === 'server'" class="flex-box-h flex-item-expand">
|
||||
<resizeable-wrapper
|
||||
v-model:size="prefStore.behavior.asideWidth"
|
||||
:min-size="300"
|
||||
:offset="data.navMenuWidth"
|
||||
class="flex-item"
|
||||
@update:size="handleResize">
|
||||
<connection-pane class="app-side flex-item-expand" />
|
||||
</resizeable-wrapper>
|
||||
<content-server-pane class="flex-item-expand" />
|
||||
</div>
|
||||
|
||||
|
@ -282,6 +241,7 @@ onMounted(async () => {
|
|||
//overflow: hidden;
|
||||
height: 100%;
|
||||
background-color: v-bind('themeVars.tabColor');
|
||||
border-right: 1px solid v-bind('themeVars.borderColor');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -305,10 +265,6 @@ onMounted(async () => {
|
|||
border-right-color: v-bind('themeVars.primaryColor');
|
||||
}
|
||||
|
||||
.dragging {
|
||||
cursor: col-resize !important;
|
||||
}
|
||||
|
||||
.fade-enter-from,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
<script setup>
|
||||
import { useThemeVars } from 'naive-ui'
|
||||
import { ref } from 'vue'
|
||||
|
||||
/**
|
||||
* Resizeable component wrapper
|
||||
*/
|
||||
const themeVars = useThemeVars()
|
||||
|
||||
const props = defineProps({
|
||||
size: {
|
||||
type: Number,
|
||||
default: 100,
|
||||
},
|
||||
minSize: {
|
||||
type: Number,
|
||||
default: 300,
|
||||
},
|
||||
maxSize: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
offset: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
borderWidth: {
|
||||
type: Number,
|
||||
default: 4,
|
||||
},
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:size'])
|
||||
|
||||
const resizing = ref(false)
|
||||
const hover = ref(false)
|
||||
|
||||
const handleResize = (evt) => {
|
||||
if (resizing.value) {
|
||||
let size = evt.clientX - props.offset
|
||||
if (size < props.minSize) {
|
||||
size = props.minSize
|
||||
}
|
||||
if (props.maxSize > 0 && size > props.maxSize) {
|
||||
size = props.maxSize
|
||||
}
|
||||
emit('update:size', size)
|
||||
}
|
||||
}
|
||||
|
||||
const stopResize = () => {
|
||||
resizing.value = false
|
||||
document.removeEventListener('mousemove', handleResize)
|
||||
document.removeEventListener('mouseup', stopResize)
|
||||
}
|
||||
|
||||
const startResize = () => {
|
||||
if (props.disabled) {
|
||||
return
|
||||
}
|
||||
resizing.value = true
|
||||
document.addEventListener('mousemove', handleResize)
|
||||
document.addEventListener('mouseup', stopResize)
|
||||
}
|
||||
|
||||
const handleMouseOver = () => {
|
||||
if (props.disabled) {
|
||||
return
|
||||
}
|
||||
hover.value = true
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :style="{ width: props.size + 'px' }" class="resize-wrapper flex-box-h">
|
||||
<slot></slot>
|
||||
<div
|
||||
:class="{
|
||||
'resize-divider-hover': hover,
|
||||
'resize-divider-drag': resizing,
|
||||
dragging: hover || resizing,
|
||||
}"
|
||||
:style="{ width: props.borderWidth + 'px', right: Math.floor(-props.borderWidth / 2) + 'px' }"
|
||||
class="resize-divider"
|
||||
@mousedown="startResize"
|
||||
@mouseout="hover = false"
|
||||
@mouseover="handleMouseOver" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.resize-wrapper {
|
||||
position: relative;
|
||||
|
||||
.resize-divider {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
transition: background-color 0.3s ease-in;
|
||||
}
|
||||
|
||||
.resize-divider-hide {
|
||||
background-color: #0000;
|
||||
}
|
||||
|
||||
.resize-divider-hover {
|
||||
background-color: v-bind('themeVars.borderColor');
|
||||
}
|
||||
|
||||
.resize-divider-drag {
|
||||
background-color: v-bind('themeVars.primaryColor');
|
||||
}
|
||||
|
||||
.dragging {
|
||||
cursor: col-resize !important;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -17,13 +17,13 @@ const tabStore = useTabStore()
|
|||
const connectionStore = useConnectionStore()
|
||||
|
||||
const onCloseTab = (tabIndex) => {
|
||||
$dialog.warning(i18n.t('dialogue.close_confirm'), () => {
|
||||
const tab = get(tabStore.tabs, tabIndex)
|
||||
if (tab != null) {
|
||||
$dialog.warning(i18n.t('dialogue.close_confirm', { name: tab.name }), () => {
|
||||
connectionStore.closeConnection(tab.name)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const tabMarkColor = computed(() => {
|
||||
const { name } = tabStore?.currentTab || {}
|
||||
|
@ -53,7 +53,7 @@ const tab = computed(() =>
|
|||
<n-tabs
|
||||
v-model:value="tabStore.activatedIndex"
|
||||
:closable="true"
|
||||
:tabs-padding="0"
|
||||
:tabs-padding="3"
|
||||
:theme-overrides="{
|
||||
tabFontWeightActive: 800,
|
||||
tabGapSmallCard: 0,
|
||||
|
|
|
@ -108,7 +108,7 @@
|
|||
"log": "Log"
|
||||
},
|
||||
"dialogue": {
|
||||
"close_confirm": "Confirm close this tab and connection",
|
||||
"close_confirm": "Confirm close this tab and connection ({name})",
|
||||
"edit_close_confirm": "Please close the relevant connections before editing. Do you want to continue?",
|
||||
"opening_connection": "Opening Connection...",
|
||||
"interrupt_connection": "Cancel",
|
||||
|
|
|
@ -108,7 +108,7 @@
|
|||
"log": "日志"
|
||||
},
|
||||
"dialogue": {
|
||||
"close_confirm": "是否关闭当前连接",
|
||||
"close_confirm": "是否关闭此连接({name})",
|
||||
"edit_close_confirm": "编辑前需要关闭相关连接,是否继续",
|
||||
"opening_connection": "正在打开连接...",
|
||||
"interrupt_connection": "中断连接",
|
||||
|
|
Loading…
Reference in New Issue