mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-17 23:45:59 +00:00
single database mode
This commit is contained in:
@@ -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",
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"> </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>
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user