single database mode

This commit is contained in:
Jan Prochazka
2022-12-23 11:03:03 +01:00
parent 45652cfc33
commit fa13990189
9 changed files with 154 additions and 78 deletions

View File

@@ -13,7 +13,7 @@
],
"devDependencies": {
"@ant-design/colors": "^5.0.0",
"@mdi/font": "^5.9.55",
"@mdi/font": "^7.1.96",
"@rollup/plugin-commonjs": "^20.0.0",
"@rollup/plugin-node-resolve": "^13.0.5",
"@rollup/plugin-replace": "^3.0.0",

View File

@@ -2,8 +2,8 @@
--dim-widget-icon-size: 60px;
--dim-statusbar-height: 22px;
--dim-left-panel-width: 300px;
--dim-tabs-panel-height: 53px;
--dim-tabs-height: 33px;
--dim-tabs-panel-height: calc( var(--dim-visible-tabs-databases) * 20px + var(--dim-tabs-height) );
--dim-splitter-thickness: 3px;
--dim-visible-left-panel: 1; /* set from JS */

View File

@@ -40,6 +40,9 @@
'icon columns': 'mdi mdi-view-column',
'icon columns-outline': 'mdi mdi-view-column-outline',
'icon single-database-mode': 'mdi mdi-database-lock',
'icon multi-database-mode': 'mdi mdi-database-eye',
'icon database': 'mdi mdi-database',
'icon server': 'mdi mdi-server',
'icon table': 'mdi mdi-table',

View File

@@ -3,6 +3,7 @@
import FormStyledButton from '../buttons/FormStyledButton.svelte';
import Link from '../elements/Link.svelte';
import TabControl from '../elements/TabControl.svelte';
import CheckboxField from '../forms/CheckboxField.svelte';
import FormCheckboxField from '../forms/FormCheckboxField.svelte';
import FormFieldTemplateLarge from '../forms/FormFieldTemplateLarge.svelte';
@@ -23,6 +24,7 @@
currentEditorTheme,
extensions,
selectedWidget,
singleDatabaseMode,
visibleWidgetSideBar,
} from '../stores';
import { isMac } from '../utility/common';
@@ -107,6 +109,19 @@ ORDER BY
/>
<div class="heading">Connection</div>
<FormFieldTemplateLarge
label="Show only tabs from selected database"
type="checkbox"
labelProps={{
onClick: () => {
$singleDatabaseMode = !$singleDatabaseMode;
},
}}
>
<CheckboxField checked={$singleDatabaseMode} on:change={e => ($singleDatabaseMode = e.target.checked)} />
</FormFieldTemplateLarge>
<FormCheckboxField
name="connection.autoRefresh"
label="Automatic refresh of database model on background"

View File

@@ -50,6 +50,7 @@ function subscribeCssVariable(store, transform, cssVariable) {
}
export const selectedWidget = writableWithStorage('database', 'selectedWidget');
export const singleDatabaseMode = writableWithStorage<boolean>(false, 'singleDatabaseMode');
export const visibleWidgetSideBar = writableWithStorage(true, 'visibleWidgetSideBar');
export const visibleSelectedWidget = derived(
[selectedWidget, visibleWidgetSideBar],
@@ -137,6 +138,7 @@ subscribeCssVariable(visibleSelectedWidget, x => (x ? 1 : 0), '--dim-visible-lef
// subscribeCssVariable(visibleToolbar, x => (x ? 1 : 0), '--dim-visible-toolbar');
subscribeCssVariable(leftPanelWidth, x => `${x}px`, '--dim-left-panel-width');
subscribeCssVariable(visibleTitleBar, x => (x ? 1 : 0), '--dim-visible-titlebar');
subscribeCssVariable(singleDatabaseMode, x => (x ? 0 : 1), '--dim-visible-tabs-databases');
let activeTabIdValue = null;
activeTabId.subscribe(value => {
@@ -198,6 +200,12 @@ pinnedDatabases.subscribe(value => {
});
export const getPinnedDatabases = () => _.compact(pinnedDatabasesValue);
let singleDatabaseModeValue = null;
singleDatabaseMode.subscribe(value => {
singleDatabaseModeValue = value;
});
export const getSingleDatabaseMode = () => singleDatabaseModeValue;
let currentDatabaseValue = null;
currentDatabase.subscribe(value => {
currentDatabaseValue = value;

View File

@@ -1,5 +1,6 @@
import _ from 'lodash';
import { currentDatabase, openedTabs } from '../stores';
import { currentDatabase, getCurrentDatabase, getSingleDatabaseMode, openedTabs } from '../stores';
import { shouldShowTab } from '../widgets/TabsPanel.svelte';
import { callWhenAppLoaded } from './appLoadManager';
import { getConnectionInfo } from './metadataLoaders';
@@ -8,6 +9,7 @@ let lastCurrentTab = null;
openedTabs.subscribe(value => {
const newCurrentTab = (value || []).find(x => x.selected);
if (newCurrentTab == lastCurrentTab) return;
if (getSingleDatabaseMode() && getCurrentDatabase()) return;
const lastTab = lastCurrentTab;
lastCurrentTab = newCurrentTab;
@@ -27,3 +29,22 @@ openedTabs.subscribe(value => {
}
}
});
currentDatabase.subscribe(currentDb => {
if (!getSingleDatabaseMode()) return;
openedTabs.update(tabs => {
const newTabs = tabs.map(tab => ({
...tab,
selected: tab.selected && shouldShowTab(tab, true, currentDb),
}));
if (newTabs.find(x => x.selected)) return newTabs;
const selectedIndex = _.findLastIndex(newTabs, x => shouldShowTab(x));
return newTabs.map((x, index) => ({
...x,
selected: index == selectedIndex,
}));
});
});

View File

@@ -1,4 +1,23 @@
<script lang="ts" context="module">
const getCurrentValueMarker: any = {};
export function shouldShowTab(tab, singleDbMode = getCurrentValueMarker, currentDb = getCurrentValueMarker) {
if (singleDbMode == getCurrentValueMarker) {
singleDbMode = getSingleDatabaseMode();
}
if (singleDbMode) {
if (currentDb == getCurrentValueMarker) {
currentDb = getCurrentDatabase();
}
return (
tab.closedTime == null &&
(!tab.props?.conid || tab.props?.conid == currentDb?.connection?._id) &&
(!tab.props?.database || tab.props?.database == currentDb?.name)
);
}
return tab.closedTime == null;
}
function allowCloseTabs(tabs) {
if (tabs.length == 0) return Promise.resolve(true);
return new Promise(resolve => {
@@ -14,7 +33,8 @@
const activeCandidate = getOpenedTabs().find(x => x.tabid == tabid);
const closeCandidates = getOpenedTabs()
.filter(x => closeCondition(x, activeCandidate))
.filter(x => x.unsaved && x.closedTime == null);
.filter(x => x.unsaved)
.filter(shouldShowTab);
if (!(await allowCloseTabs(closeCandidates))) return;
@@ -24,14 +44,14 @@
const newFiles = files.map(x => ({
...x,
closedTime: x.closedTime || (closeCondition(x, active) ? new Date().getTime() : undefined),
closedTime: shouldShowTab(x) && closeCondition(x, active) ? new Date().getTime() : x.closedTime,
}));
if (newFiles.find(x => x.selected && x.closedTime == null)) {
if (newFiles.find(x => x.selected && shouldShowTab(x))) {
return newFiles;
}
const selectedIndex = _.findLastIndex(newFiles, x => x.closedTime == null);
const selectedIndex = _.findLastIndex(newFiles, x => shouldShowTab(x));
return newFiles.map((x, index) => ({
...x,
@@ -43,7 +63,8 @@
export const closeMultipleTabs = async (closeCondition, deleteFromHistory = false) => {
const closeCandidates = getOpenedTabs()
.filter(x => closeCondition(x))
.filter(x => x.unsaved && x.closedTime == null);
.filter(x => x.unsaved)
.filter(shouldShowTab);
if (!(await allowCloseTabs(closeCandidates))) return;
@@ -52,14 +73,14 @@
? files.filter(x => !closeCondition(x))
: files.map(x => ({
...x,
closedTime: x.closedTime || (closeCondition(x) ? new Date().getTime() : undefined),
closedTime: shouldShowTab(x) && closeCondition(x) ? new Date().getTime() : x.closedTime,
}));
if (newFiles.find(x => x.selected && x.closedTime == null)) {
if (newFiles.find(x => x.selected && shouldShowTab(x))) {
return newFiles;
}
const selectedIndex = _.findLastIndex(newFiles, x => x.closedTime == null);
const selectedIndex = _.findLastIndex(newFiles, x => shouldShowTab(x));
return newFiles.map((x, index) => ({
...x,
@@ -70,7 +91,9 @@
const closeTab = closeTabFunc((x, active) => x.tabid == active.tabid);
const closeAll = async () => {
const closeCandidates = getOpenedTabs().filter(x => x.unsaved && x.closedTime == null);
const closeCandidates = getOpenedTabs()
.filter(x => x.unsaved)
.filter(shouldShowTab);
if (!(await allowCloseTabs(closeCandidates))) return;
@@ -78,7 +101,7 @@
openedTabs.update(tabs =>
tabs.map(tab => ({
...tab,
closedTime: tab.closedTime || closedTime,
closedTime: shouldShowTab(tab) ? closedTime : tab.closedTime,
selected: false,
}))
);
@@ -133,10 +156,7 @@
}
function switchTabByOrder(reverse) {
const tabs = _.sortBy(
get(openedTabs).filter(x => x.closedTime == null),
'tabOrder'
);
const tabs = _.sortBy(get(openedTabs).filter(shouldShowTab), 'tabOrder');
if (reverse) tabs.reverse();
const selectedTab = tabs.find(x => x.selected);
if (!selectedTab) return;
@@ -212,8 +232,7 @@
</script>
<script lang="ts">
import { LogarithmicScale } from 'chart.js';
import _, { map, slice, sortBy } from 'lodash';
import _ from 'lodash';
import { tick } from 'svelte';
import { derived, get } from 'svelte/store';
import registerCommand from '../commands/registerCommand';
@@ -231,6 +250,8 @@
activeTabId,
getActiveTabId,
getCurrentDatabase,
singleDatabaseMode,
getSingleDatabaseMode,
} from '../stores';
import tabs from '../tabs';
import { setSelectedTab } from '../utility/common';
@@ -243,6 +264,7 @@
import TabCloseButton from '../elements/TabCloseButton.svelte';
import CloseTabModal from '../modals/CloseTabModal.svelte';
$: showTabFilterFunc = tab => shouldShowTab(tab, $singleDatabaseMode, $currentDatabase);
$: connectionList = useConnectionList();
$: currentDbKey =
@@ -252,13 +274,11 @@
? `server://${$currentDatabase.connection._id}`
: '_no';
$: tabsWithDb = $openedTabs
.filter(x => !x.closedTime)
.map(tab => ({
...tab,
tabDbName: getTabDbName(tab, $connectionList),
tabDbKey: getTabDbKey(tab),
}));
$: tabsWithDb = $openedTabs.filter(showTabFilterFunc).map(tab => ({
...tab,
tabDbName: getTabDbName(tab, $connectionList),
tabDbKey: getTabDbKey(tab),
}));
$: groupedTabs = groupTabs(tabsWithDb);
@@ -423,54 +443,56 @@
<div class="tabs" on:wheel={handleTabsWheel} bind:this={domTabs}>
{#each groupedTabs as tabGroup}
<div class="db-wrapper">
<div
class="db-name"
class:selected={draggingDbGroup
? tabGroup.grpid == draggingDbGroupTarget?.grpid
: tabGroup.tabDbKey == currentDbKey}
on:mouseup={e => {
if (e.button == 1) {
closeMultipleTabs(tab => tabGroup.tabs.find(x => x.tabid == tab.tabid));
} else {
handleSetDb(tabGroup.tabs[0].props);
}
}}
use:contextMenu={getDatabaseContextMenu(tabGroup.tabs)}
style={$connectionColorFactory(
tabGroup.tabs[0].props,
(draggingDbGroup ? tabGroup.grpid == draggingDbGroupTarget?.grpid : tabGroup.tabDbKey == currentDbKey)
? 2
: 3
)}
draggable={true}
on:dragstart={e => {
draggingDbGroup = tabGroup;
}}
on:dragenter={e => {
draggingDbGroupTarget = tabGroup;
}}
on:drop={e => {
dragDropTabs(draggingDbGroup.tabs, tabGroup.tabs);
}}
on:dragend={e => {
draggingDbGroup = null;
draggingDbGroupTarget = null;
}}
>
<div class="db-name-inner">
<FontIcon icon={getDbIcon(tabGroup.tabDbKey)} />
{tabGroup.tabDbName}
{#if $connectionList?.find(x => x._id == tabGroup.tabs[0]?.props?.conid)?.isReadOnly}
<FontIcon icon="icon lock" />
{/if}
</div>
{#if !$singleDatabaseMode}
<div
class="close-button-right tabCloseButton"
on:click={e => closeMultipleTabs(tab => tabGroup.tabs.find(x => x.tabid == tab.tabid))}
class="db-name"
class:selected={draggingDbGroup
? tabGroup.grpid == draggingDbGroupTarget?.grpid
: tabGroup.tabDbKey == currentDbKey}
on:mouseup={e => {
if (e.button == 1) {
closeMultipleTabs(tab => tabGroup.tabs.find(x => x.tabid == tab.tabid));
} else {
handleSetDb(tabGroup.tabs[0].props);
}
}}
use:contextMenu={getDatabaseContextMenu(tabGroup.tabs)}
style={$connectionColorFactory(
tabGroup.tabs[0].props,
(draggingDbGroup ? tabGroup.grpid == draggingDbGroupTarget?.grpid : tabGroup.tabDbKey == currentDbKey)
? 2
: 3
)}
draggable={true}
on:dragstart={e => {
draggingDbGroup = tabGroup;
}}
on:dragenter={e => {
draggingDbGroupTarget = tabGroup;
}}
on:drop={e => {
dragDropTabs(draggingDbGroup.tabs, tabGroup.tabs);
}}
on:dragend={e => {
draggingDbGroup = null;
draggingDbGroupTarget = null;
}}
>
<FontIcon icon="icon close" />
<div class="db-name-inner">
<FontIcon icon={getDbIcon(tabGroup.tabDbKey)} />
{tabGroup.tabDbName}
{#if $connectionList?.find(x => x._id == tabGroup.tabs[0]?.props?.conid)?.isReadOnly}
<FontIcon icon="icon lock" />
{/if}
</div>
<div
class="close-button-right tabCloseButton"
on:click={e => closeMultipleTabs(tab => tabGroup.tabs.find(x => x.tabid == tab.tabid))}
>
<FontIcon icon="icon close" />
</div>
</div>
</div>
{/if}
<div class="db-group">
{#each tabGroup.tabs as tab}
<div

View File

@@ -7,9 +7,9 @@
visibleSelectedWidget,
visibleWidgetSideBar,
visibleHamburgerMenuWidget,
singleDatabaseMode,
} from '../stores';
import mainMenuDefinition from '../../../../app/src/mainMenuDefinition';
import { useConfig } from '../utility/metadataLoaders';
import hasPermission from '../utility/hasPermission';
let domSettings;
@@ -90,8 +90,6 @@
const items = mainMenuDefinition({ editMenu: false });
currentDropDownMenu.set({ left, top, items });
}
$: config = useConfig();
</script>
<div class="main">
@@ -112,6 +110,15 @@
<div class="flex1">&nbsp;</div>
<div
class="wrapper"
title={`Toggle whether tabs from all databases are visible. Currently - ${$singleDatabaseMode ? 'NO' : 'YES'}`}
on:click={() => {
$singleDatabaseMode = !$singleDatabaseMode;
}}
>
<FontIcon icon={$singleDatabaseMode ? 'icon single-database-mode' : 'icon multi-database-mode'} />
</div>
<div class="wrapper" on:click={handleSettingsMenu} bind:this={domSettings}>
<FontIcon icon="icon settings" />
</div>

View File

@@ -1069,10 +1069,10 @@
resolved "https://registry.yarnpkg.com/@js-joda/core/-/core-5.2.0.tgz#fcebb14cffbf25adaaeec20d4579530e896ecd4a"
integrity sha512-0OriPYIaMLB3XiLQMe0BXKVIqeriTn3H7JMOzTsHEtt7Zqq+TetCu97KnAhU3ckiQZKBxfZshft+H1OC4D1lXw==
"@mdi/font@^5.9.55":
version "5.9.55"
resolved "https://registry.yarnpkg.com/@mdi/font/-/font-5.9.55.tgz#41acd50b88073ded7095fc3029d8712b6e12f38e"
integrity sha512-jswRF6q3eq8NWpWiqct6q+6Fg/I7nUhrxYJfiEM8JJpap0wVJLQdbKtyS65GdlK7S7Ytnx3TTi/bmw+tBhkGmg==
"@mdi/font@^7.1.96":
version "7.1.96"
resolved "https://registry.yarnpkg.com/@mdi/font/-/font-7.1.96.tgz#211ca4acfa31964278e5085de595e8c73967d400"
integrity sha512-Imag6npmfkBDi2Ze2jiZVAPTDIKLxhz2Sx82xJ2zctyAU5LYJejLI5ChnDwiD9bMkQfVuzEsI98Q8toHyC+HCg==
"@mrmlnc/readdir-enhanced@^2.2.1":
version "2.2.1"