mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-29 19:13:59 +00:00
vertical split tabs #394
This commit is contained in:
@@ -28,6 +28,7 @@
|
|||||||
import FontIcon from './icons/FontIcon.svelte';
|
import FontIcon from './icons/FontIcon.svelte';
|
||||||
import getElectron from './utility/getElectron';
|
import getElectron from './utility/getElectron';
|
||||||
import TabsContainer from './tabpanel/TabsContainer.svelte';
|
import TabsContainer from './tabpanel/TabsContainer.svelte';
|
||||||
|
import MultiTabsContainer from './tabpanel/MultiTabsContainer.svelte';
|
||||||
|
|
||||||
$: currentThemeType = $currentThemeDefinition?.themeType == 'dark' ? 'theme-type-dark' : 'theme-type-light';
|
$: currentThemeType = $currentThemeDefinition?.themeType == 'dark' ? 'theme-type-dark' : 'theme-type-light';
|
||||||
|
|
||||||
@@ -71,7 +72,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
<div class="tabs-container">
|
<div class="tabs-container">
|
||||||
<TabsContainer />
|
<MultiTabsContainer />
|
||||||
</div>
|
</div>
|
||||||
{#if $selectedWidget && $visibleWidgetSideBar}
|
{#if $selectedWidget && $visibleWidgetSideBar}
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -127,6 +127,7 @@
|
|||||||
'icon compare': 'mdi mdi-compare',
|
'icon compare': 'mdi mdi-compare',
|
||||||
'icon no-color': 'mdi mdi-format-color-marker-cancel',
|
'icon no-color': 'mdi mdi-format-color-marker-cancel',
|
||||||
'icon palette': 'mdi mdi-palette',
|
'icon palette': 'mdi mdi-palette',
|
||||||
|
'icon split': 'mdi mdi-view-split-vertical',
|
||||||
|
|
||||||
'icon num-0': 'mdi mdi-numeric-0-circle',
|
'icon num-0': 'mdi mdi-numeric-0-circle',
|
||||||
'icon num-1': 'mdi mdi-numeric-1-circle',
|
'icon num-1': 'mdi mdi-numeric-1-circle',
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ export interface TabDefinition {
|
|||||||
tabid: string;
|
tabid: string;
|
||||||
tabComponent: string;
|
tabComponent: string;
|
||||||
tabOrder?: number;
|
tabOrder?: number;
|
||||||
|
multiTabIndex?: number;
|
||||||
|
visibleSecondary?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function writableWithStorage<T>(defaultValue: T, storageName) {
|
export function writableWithStorage<T>(defaultValue: T, storageName) {
|
||||||
|
|||||||
17
packages/web/src/tabpanel/MultiTabsContainer.svelte
Normal file
17
packages/web/src/tabpanel/MultiTabsContainer.svelte
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<script>
|
||||||
|
import HorizontalSplitter from '../elements/HorizontalSplitter.svelte';
|
||||||
|
import { openedTabs } from '../stores';
|
||||||
|
import TabsContainer from './TabsContainer.svelte';
|
||||||
|
|
||||||
|
$: isLeft = !!$openedTabs.find(x => x.closedTime == null && !x.multiTabIndex);
|
||||||
|
$: isRight = !!$openedTabs.find(x => x.closedTime == null && x.multiTabIndex == 1);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<HorizontalSplitter hideFirst={!isLeft && isRight} isSplitter={isRight}>
|
||||||
|
<div class="container" slot="1">
|
||||||
|
<TabsContainer multiTabIndex={0} />
|
||||||
|
</div>
|
||||||
|
<div class="container" slot="2">
|
||||||
|
<TabsContainer multiTabIndex={1} />
|
||||||
|
</div>
|
||||||
|
</HorizontalSplitter>
|
||||||
@@ -4,8 +4,12 @@
|
|||||||
import TabContent from './TabContent.svelte';
|
import TabContent from './TabContent.svelte';
|
||||||
import tabs from '../tabs';
|
import tabs from '../tabs';
|
||||||
|
|
||||||
|
export let multiTabIndex;
|
||||||
|
|
||||||
let mountedTabs = {};
|
let mountedTabs = {};
|
||||||
$: selectedTab = $openedTabs.find(x => x.selected && x.closedTime == null);
|
$: selectedTab = $openedTabs.find(
|
||||||
|
x => (x.selected || x.visibleSecondary) && x.closedTime == null && (x.multiTabIndex || 0) == multiTabIndex
|
||||||
|
);
|
||||||
|
|
||||||
// cleanup closed tabs
|
// cleanup closed tabs
|
||||||
$: {
|
$: {
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
<script>
|
<script lang='ts'>
|
||||||
import TabRegister from './TabRegister.svelte';
|
import TabRegister from './TabRegister.svelte';
|
||||||
|
|
||||||
import TabsPanel from './TabsPanel.svelte';
|
import TabsPanel from './TabsPanel.svelte';
|
||||||
|
|
||||||
|
export let multiTabIndex;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="tabs">
|
<div class="tabs">
|
||||||
<TabsPanel />
|
<TabsPanel {multiTabIndex} />
|
||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<TabRegister />
|
<TabRegister {multiTabIndex} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|||||||
@@ -43,6 +43,8 @@
|
|||||||
const newFiles = files.map(x => ({
|
const newFiles = files.map(x => ({
|
||||||
...x,
|
...x,
|
||||||
closedTime: shouldShowTab(x) && closeCondition(x, active) ? new Date().getTime() : x.closedTime,
|
closedTime: shouldShowTab(x) && closeCondition(x, active) ? new Date().getTime() : x.closedTime,
|
||||||
|
selected: false,
|
||||||
|
visibleSecondary: false,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
if (newFiles.find(x => x.selected && shouldShowTab(x))) {
|
if (newFiles.find(x => x.selected && shouldShowTab(x))) {
|
||||||
@@ -72,6 +74,8 @@
|
|||||||
: files.map(x => ({
|
: files.map(x => ({
|
||||||
...x,
|
...x,
|
||||||
closedTime: shouldShowTab(x) && closeCondition(x) ? new Date().getTime() : x.closedTime,
|
closedTime: shouldShowTab(x) && closeCondition(x) ? new Date().getTime() : x.closedTime,
|
||||||
|
selected: false,
|
||||||
|
visibleSecondary: false,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
if (newFiles.find(x => x.selected && shouldShowTab(x))) {
|
if (newFiles.find(x => x.selected && shouldShowTab(x))) {
|
||||||
@@ -87,6 +91,17 @@
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function splitTab(multiTabIndex) {
|
||||||
|
openedTabs.update(tabs => {
|
||||||
|
const secondaryIndex = _.findLastIndex(tabs, x => shouldShowTab(x) && !x.selected);
|
||||||
|
return tabs.map((x, i) => ({
|
||||||
|
...x,
|
||||||
|
multiTabIndex: x.selected ? 1 - multiTabIndex : x.multiTabIndex,
|
||||||
|
visibleSecondary: i == secondaryIndex,
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const closeTab = closeTabFunc((x, active) => x.tabid == active.tabid);
|
const closeTab = closeTabFunc((x, active) => x.tabid == active.tabid);
|
||||||
const closeAll = async () => {
|
const closeAll = async () => {
|
||||||
const closeCandidates = getOpenedTabs()
|
const closeCandidates = getOpenedTabs()
|
||||||
@@ -101,6 +116,7 @@
|
|||||||
...tab,
|
...tab,
|
||||||
closedTime: shouldShowTab(tab) ? closedTime : tab.closedTime,
|
closedTime: shouldShowTab(tab) ? closedTime : tab.closedTime,
|
||||||
selected: false,
|
selected: false,
|
||||||
|
visibleSecondary: false,
|
||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -264,7 +280,10 @@
|
|||||||
import TabCloseButton from '../elements/TabCloseButton.svelte';
|
import TabCloseButton from '../elements/TabCloseButton.svelte';
|
||||||
import CloseTabModal from '../modals/CloseTabModal.svelte';
|
import CloseTabModal from '../modals/CloseTabModal.svelte';
|
||||||
|
|
||||||
$: showTabFilterFunc = tab => shouldShowTab(tab, $lockedDatabaseMode, $currentDatabase);
|
export let multiTabIndex;
|
||||||
|
|
||||||
|
$: showTabFilterFunc = tab =>
|
||||||
|
shouldShowTab(tab, $lockedDatabaseMode, $currentDatabase) && (tab.multiTabIndex || 0) == multiTabIndex;
|
||||||
$: connectionList = useConnectionList();
|
$: connectionList = useConnectionList();
|
||||||
|
|
||||||
$: currentDbKey =
|
$: currentDbKey =
|
||||||
@@ -287,6 +306,10 @@
|
|||||||
|
|
||||||
$: scrollInViewTab($activeTabId);
|
$: scrollInViewTab($activeTabId);
|
||||||
|
|
||||||
|
$: filteredTabsFromAllParts = $openedTabs.filter(x => shouldShowTab(x));
|
||||||
|
$: allowSplitTab =
|
||||||
|
_.uniq(filteredTabsFromAllParts.map(x => x.multiTabIndex || 0)).length == 1 && filteredTabsFromAllParts.length >= 2;
|
||||||
|
|
||||||
let draggingTab = null;
|
let draggingTab = null;
|
||||||
let draggingTabTarget = null;
|
let draggingTabTarget = null;
|
||||||
let draggingDbGroup = null;
|
let draggingDbGroup = null;
|
||||||
@@ -540,7 +563,16 @@
|
|||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
<div class="add-icon" on:click={() => newQuery({})} title="New query"><FontIcon icon="icon add" /></div>
|
<div class="icons-wrapper">
|
||||||
|
{#if allowSplitTab}
|
||||||
|
<div class="icon-button" on:click={() => splitTab(multiTabIndex)} title="Split window">
|
||||||
|
<FontIcon icon="icon split" />
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
<div class="icon-button" on:click={() => newQuery({})} title="New query">
|
||||||
|
<FontIcon icon="icon add" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
@@ -551,7 +583,7 @@
|
|||||||
right: 0;
|
right: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
}
|
}
|
||||||
.add-icon {
|
.icons-wrapper {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 5px;
|
right: 5px;
|
||||||
font-size: 20pt;
|
font-size: 20pt;
|
||||||
@@ -559,10 +591,13 @@
|
|||||||
bottom: 0;
|
bottom: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.icon-button {
|
||||||
color: var(--theme-font-2);
|
color: var(--theme-font-2);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
.add-icon:hover {
|
.icon-button:hover {
|
||||||
color: var(--theme-font-1);
|
color: var(--theme-font-1);
|
||||||
}
|
}
|
||||||
.tabs {
|
.tabs {
|
||||||
|
|||||||
@@ -29,9 +29,24 @@ export function markTabSaved(tabid) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function setSelectedTabFunc(files, tabid) {
|
export function setSelectedTabFunc(files, tabid) {
|
||||||
|
const oldSelected = files.find(x => x.selected);
|
||||||
|
const newSelected = files.find(x => x.tabid == tabid);
|
||||||
|
const changeVisibleSecondary = (oldSelected.multiTabIndex || 0) != (newSelected.multiTabIndex || 0);
|
||||||
return [
|
return [
|
||||||
...(files || []).filter(x => x.tabid != tabid).map(x => ({ ...x, selected: false })),
|
...(files || [])
|
||||||
...(files || []).filter(x => x.tabid == tabid).map(x => ({ ...x, selected: true })),
|
.filter(x => x.tabid != tabid)
|
||||||
|
.map(x => ({
|
||||||
|
...x,
|
||||||
|
selected: false,
|
||||||
|
visibleSecondary: changeVisibleSecondary ? x.selected : x.visibleSecondary,
|
||||||
|
})),
|
||||||
|
...(files || [])
|
||||||
|
.filter(x => x.tabid == tabid)
|
||||||
|
.map(x => ({
|
||||||
|
...x,
|
||||||
|
selected: true,
|
||||||
|
visibleSecondary: false,
|
||||||
|
})),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user