vertical split tabs #394

This commit is contained in:
Jan Prochazka
2023-03-05 10:43:04 +01:00
parent 1061d2aba2
commit 2dadd1f437
8 changed files with 88 additions and 12 deletions

View File

@@ -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

View File

@@ -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',

View File

@@ -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) {

View 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>

View File

@@ -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
$: { $: {

View File

@@ -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>

View File

@@ -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 {

View File

@@ -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,
})),
]; ];
} }