mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-28 21:05:59 +00:00
Merge branch 'feature/connection-keyboard-browse'
This commit is contained in:
@@ -50,7 +50,7 @@
|
||||
<svelte:component
|
||||
this={module.default}
|
||||
{data}
|
||||
on:click={handleExpand}
|
||||
on:dblclick={handleExpand}
|
||||
on:expand={handleExpandButton}
|
||||
expandIcon={getExpandIcon(!isExpandedBySearch && expandable, subItemsComponent, isExpanded, expandIconFunc)}
|
||||
{checkedObjectsStore}
|
||||
|
||||
@@ -12,6 +12,16 @@
|
||||
return filterName(filter, ...databases.map(x => x.name));
|
||||
};
|
||||
export function openConnection(connection) {
|
||||
if (connection.singleDatabase) {
|
||||
if (getOpenedSingleDatabaseConnections().includes(connection._id)) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (getOpenedConnections().includes(connection._id)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const config = getCurrentConfig();
|
||||
if (connection.singleDatabase) {
|
||||
switchCurrentDatabase({ connection, name: connection.defaultDatabase });
|
||||
@@ -83,10 +93,12 @@
|
||||
currentDatabase,
|
||||
expandedConnections,
|
||||
extensions,
|
||||
focusedConnectionOrDatabase,
|
||||
getCurrentConfig,
|
||||
getCurrentDatabase,
|
||||
getCurrentSettings,
|
||||
getOpenedConnections,
|
||||
getOpenedSingleDatabaseConnections,
|
||||
getOpenedTabs,
|
||||
openedConnections,
|
||||
openedSingleDatabaseConnections,
|
||||
@@ -135,7 +147,7 @@
|
||||
});
|
||||
};
|
||||
|
||||
const handleClick = async () => {
|
||||
const handleDoubleClick = async () => {
|
||||
const config = getCurrentConfig();
|
||||
if (config.runAsPortal) {
|
||||
await tick();
|
||||
@@ -158,6 +170,19 @@
|
||||
}
|
||||
};
|
||||
|
||||
const handleClick = async e => {
|
||||
focusedConnectionOrDatabase.set({ conid: data?._id });
|
||||
openNewTab({
|
||||
title: getConnectionLabel(data),
|
||||
icon: 'img connection',
|
||||
tabComponent: 'ConnectionTab',
|
||||
tabPreviewMode: true,
|
||||
props: {
|
||||
conid: data._id,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const handleSqlRestore = () => {
|
||||
showModal(ImportDatabaseDumpModal, {
|
||||
connection: data,
|
||||
@@ -329,7 +354,7 @@
|
||||
colorMark={passProps?.connectionColorFactory && passProps?.connectionColorFactory({ conid: data._id })}
|
||||
menu={getContextMenu}
|
||||
on:click={handleClick}
|
||||
on:click
|
||||
on:dblclick
|
||||
on:expand
|
||||
on:dblclick={handleConnect}
|
||||
on:middleclick={() => {
|
||||
@@ -337,4 +362,5 @@
|
||||
.find(x => x.isNewQuery)
|
||||
.onClick();
|
||||
}}
|
||||
isChoosed={data._id == $focusedConnectionOrDatabase?.conid && !$focusedConnectionOrDatabase?.database}
|
||||
/>
|
||||
|
||||
@@ -446,6 +446,7 @@ await dbgateApi.dropAllDbObjects(${JSON.stringify(
|
||||
currentArchive,
|
||||
currentDatabase,
|
||||
extensions,
|
||||
focusedConnectionOrDatabase,
|
||||
getCurrentDatabase,
|
||||
getExtensions,
|
||||
getOpenedTabs,
|
||||
@@ -512,7 +513,14 @@ await dbgateApi.dropAllDbObjects(${JSON.stringify(
|
||||
passProps?.connectionColorFactory({ conid: data?.connection?._id, database: data.name }, null, null, false)}
|
||||
isBold={$currentDatabase?.connection?._id == data?.connection?._id &&
|
||||
extractDbNameFromComposite($currentDatabase?.name) == data.name}
|
||||
on:click={() => switchCurrentDatabase(data)}
|
||||
on:dblclick={() => {
|
||||
switchCurrentDatabase(data);
|
||||
// passProps?.onFocusSqlObjectList?.();
|
||||
}}
|
||||
on:click={() => {
|
||||
// switchCurrentDatabase(data);
|
||||
$focusedConnectionOrDatabase = { conid: data.connection?._id, database: data.name, connection: data.connection };
|
||||
}}
|
||||
on:dragstart
|
||||
on:dragenter
|
||||
on:dragend
|
||||
@@ -532,4 +540,6 @@ await dbgateApi.dropAllDbObjects(${JSON.stringify(
|
||||
list.filter(x => x?.name != data?.name || x?.connection?._id != data?.connection?._id)
|
||||
)
|
||||
: null}
|
||||
isChoosed={data.connection?._id == $focusedConnectionOrDatabase?.conid &&
|
||||
data.name == $focusedConnectionOrDatabase?.database}
|
||||
/>
|
||||
|
||||
@@ -15,13 +15,11 @@
|
||||
|
||||
$: databases = useDatabaseList({ conid: isExpandedOnlyBySearch ? null : data._id });
|
||||
$: dbList = isExpandedOnlyBySearch ? getLocalStorage(`database_list_${data._id}`) || [] : $databases || [];
|
||||
|
||||
$: connectionLabel = getConnectionLabel(data);
|
||||
</script>
|
||||
|
||||
<AppObjectList
|
||||
list={_.sortBy(
|
||||
dbList.filter(x => filterName(filter, x.name, connectionLabel)),
|
||||
dbList.filter(x => filterName(filter, x.name, data.displayName, data.server)),
|
||||
x => x.sortOrder ?? x.name
|
||||
).map(db => ({ ...db, connection: data }))}
|
||||
module={databaseAppObject}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
export let disabled = false;
|
||||
export let value;
|
||||
export let title = null;
|
||||
export let skipWidth = false;
|
||||
|
||||
function handleClick() {
|
||||
if (!disabled) dispatch('click');
|
||||
@@ -19,19 +20,22 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<input {type} {value} {title} class:disabled {...$$restProps} on:click={handleClick} bind:this={domButton} />
|
||||
<input {type} {value} {title} class:disabled {...$$restProps} on:click={handleClick} bind:this={domButton} class:skipWidth />
|
||||
|
||||
<style>
|
||||
input {
|
||||
border: 1px solid var(--theme-bg-button-inv-2);
|
||||
padding: 5px;
|
||||
margin: 2px;
|
||||
width: 100px;
|
||||
background-color: var(--theme-bg-button-inv);
|
||||
color: var(--theme-font-inv-1);
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
input:not(.skipWidth) {
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
input:hover:not(.disabled) {
|
||||
background-color: var(--theme-bg-button-inv-2);
|
||||
}
|
||||
|
||||
@@ -23,8 +23,12 @@
|
||||
|
||||
const debouncedSet = _.debounce(x => (value = x), 500);
|
||||
|
||||
export function focus() {
|
||||
export function focus(text) {
|
||||
domInput.focus();
|
||||
if (text) {
|
||||
domInput.value = text;
|
||||
value = text;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -158,6 +158,7 @@ export const appliedCurrentSchema = writable<string>(null);
|
||||
export const loadingSchemaLists = writable({}); // dict [`${conid}::${database}`]: true
|
||||
|
||||
export const selectedDatabaseObjectAppObject = writable(null);
|
||||
export const focusedConnectionOrDatabase = writable<{ conid: string; database?: string; connection: any }>(null);
|
||||
|
||||
export const currentThemeDefinition = derived([currentTheme, extensions], ([$currentTheme, $extensions]) =>
|
||||
$extensions.themes.find(x => x.themeClassName == $currentTheme)
|
||||
@@ -339,3 +340,15 @@ openedModals.subscribe(value => {
|
||||
openedModalsValue = value;
|
||||
});
|
||||
export const getOpenedModals = () => openedModalsValue;
|
||||
|
||||
let focusedConnectionOrDatabaseValue = null;
|
||||
focusedConnectionOrDatabase.subscribe(value => {
|
||||
focusedConnectionOrDatabaseValue = value;
|
||||
});
|
||||
export const getFocusedConnectionOrDatabase = () => focusedConnectionOrDatabaseValue;
|
||||
|
||||
let openedSingleDatabaseConnectionsValue = [];
|
||||
openedSingleDatabaseConnections.subscribe(value => {
|
||||
openedSingleDatabaseConnectionsValue = value;
|
||||
});
|
||||
export const getOpenedSingleDatabaseConnections = () => openedSingleDatabaseConnectionsValue;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import _ from 'lodash';
|
||||
import {
|
||||
currentDatabase,
|
||||
focusedConnectionOrDatabase,
|
||||
getActiveTab,
|
||||
getCurrentDatabase,
|
||||
getLockedDatabaseMode,
|
||||
@@ -78,6 +79,16 @@ export async function handleAfterTabClick() {
|
||||
}
|
||||
|
||||
currentDatabase.subscribe(currentDb => {
|
||||
if (currentDb) {
|
||||
focusedConnectionOrDatabase.set({
|
||||
conid: currentDb.connection?._id,
|
||||
database: currentDb.name,
|
||||
connection: currentDb.connection,
|
||||
});
|
||||
} else {
|
||||
focusedConnectionOrDatabase.set(null);
|
||||
}
|
||||
|
||||
if (!getLockedDatabaseMode()) return;
|
||||
if (!currentDb && !getAppLoaded()) return;
|
||||
openedTabs.update(tabs => {
|
||||
|
||||
@@ -1,23 +1,28 @@
|
||||
<script lang="ts">
|
||||
import keycodes from '../utility/keycodes';
|
||||
import _ from 'lodash';
|
||||
import { sleep } from '../utility/common';
|
||||
|
||||
export let list;
|
||||
export let selectedObjectStore;
|
||||
export let getSelectedObject;
|
||||
export let selectedObjectMatcher;
|
||||
export let module;
|
||||
export let handleObjectClick;
|
||||
|
||||
export let onScrollTop = null;
|
||||
export let onFocusFilterBox = null;
|
||||
export let getDefaultFocusedItem = null;
|
||||
|
||||
let isListFocused = false;
|
||||
let domDiv = null;
|
||||
export let hideContent = false;
|
||||
|
||||
function handleKeyDown(ev) {
|
||||
const listInstance = _.isFunction(list) ? list() : list;
|
||||
|
||||
function selectByDiff(diff) {
|
||||
const selected = getSelectedObject();
|
||||
const index = _.findIndex(list, x => selectedObjectMatcher(x, selected));
|
||||
const index = _.findIndex(listInstance, x => selectedObjectMatcher(x, selected));
|
||||
|
||||
if (index == 0 && diff < 0) {
|
||||
onFocusFilterBox?.();
|
||||
@@ -26,16 +31,16 @@
|
||||
|
||||
if (index >= 0) {
|
||||
let newIndex = index + diff;
|
||||
if (newIndex >= list.length) {
|
||||
newIndex = list.length - 1;
|
||||
if (newIndex >= listInstance.length) {
|
||||
newIndex = listInstance.length - 1;
|
||||
}
|
||||
if (newIndex < 0) {
|
||||
newIndex = 0;
|
||||
}
|
||||
|
||||
if (list[newIndex]) {
|
||||
selectedObjectStore.set(list[newIndex]);
|
||||
module.handleObjectClick(list[newIndex], { tabPreviewMode: true });
|
||||
if (listInstance[newIndex]) {
|
||||
selectedObjectStore.set(listInstance[newIndex]);
|
||||
handleObjectClick?.(listInstance[newIndex], { tabPreviewMode: true });
|
||||
}
|
||||
|
||||
if (newIndex == 0) {
|
||||
@@ -52,7 +57,7 @@
|
||||
ev.preventDefault();
|
||||
}
|
||||
if (ev.keyCode == keycodes.enter) {
|
||||
module.handleObjectClick(getSelectedObject(), { tabPreviewMode: false, focusTab: true });
|
||||
handleObjectClick?.(getSelectedObject(), { tabPreviewMode: false, focusTab: true });
|
||||
ev.preventDefault();
|
||||
}
|
||||
if (ev.keyCode == keycodes.pageDown) {
|
||||
@@ -64,28 +69,66 @@
|
||||
ev.preventDefault();
|
||||
}
|
||||
if (ev.keyCode == keycodes.home) {
|
||||
if (list[0]) {
|
||||
selectedObjectStore.set(list[0]);
|
||||
module.handleObjectClick(list[0], { tabPreviewMode: true });
|
||||
if (listInstance[0]) {
|
||||
selectedObjectStore.set(listInstance[0]);
|
||||
handleObjectClick?.(listInstance[0], { tabPreviewMode: true });
|
||||
onScrollTop?.();
|
||||
}
|
||||
}
|
||||
if (ev.keyCode == keycodes.end) {
|
||||
if (list[list.length - 1]) {
|
||||
selectedObjectStore.set(list[list.length - 1]);
|
||||
module.handleObjectClick(list[list.length - 1], { tabPreviewMode: true });
|
||||
if (listInstance[listInstance.length - 1]) {
|
||||
selectedObjectStore.set(listInstance[listInstance.length - 1]);
|
||||
handleObjectClick?.(listInstance[listInstance.length - 1], { tabPreviewMode: true });
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
!ev.ctrlKey &&
|
||||
!ev.altKey &&
|
||||
!ev.metaKey &&
|
||||
((ev.keyCode >= keycodes.a && ev.keyCode <= keycodes.z) ||
|
||||
(ev.keyCode >= keycodes.n0 && ev.keyCode <= keycodes.n9) ||
|
||||
(ev.keyCode >= keycodes.numPad0 && ev.keyCode <= keycodes.numPad9) ||
|
||||
ev.keyCode == keycodes.dash)
|
||||
) {
|
||||
const text = ev.key;
|
||||
onFocusFilterBox?.(text);
|
||||
ev.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
export function focusFirst() {
|
||||
const listInstance = _.isFunction(list) ? list() : list;
|
||||
|
||||
domDiv?.focus();
|
||||
if (list[0]) {
|
||||
selectedObjectStore.set(list[0]);
|
||||
module.handleObjectClick(list[0], { tabPreviewMode: true });
|
||||
if (listInstance[0]) {
|
||||
selectedObjectStore.set(listInstance[0]);
|
||||
handleObjectClick?.(listInstance[0], { tabPreviewMode: true });
|
||||
onScrollTop?.();
|
||||
}
|
||||
}
|
||||
|
||||
async function handleFocus() {
|
||||
isListFocused = true;
|
||||
// await tick();
|
||||
await sleep(100);
|
||||
// console.log('ON FOCUS AFTER SLEEP');
|
||||
const listInstance = _.isFunction(list) ? list() : list;
|
||||
const selected = getSelectedObject();
|
||||
const index = _.findIndex(listInstance, x => selectedObjectMatcher(x, selected));
|
||||
if (index < 0) {
|
||||
const focused = getDefaultFocusedItem?.();
|
||||
if (focused) {
|
||||
const index2 = _.findIndex(listInstance, x => selectedObjectMatcher(x, focused));
|
||||
if (index2 >= 0) {
|
||||
selectedObjectStore.set(focused);
|
||||
handleObjectClick?.(focused, { tabPreviewMode: true });
|
||||
return;
|
||||
}
|
||||
}
|
||||
focusFirst();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div
|
||||
@@ -93,13 +136,12 @@
|
||||
on:keydown={handleKeyDown}
|
||||
class="wrapper"
|
||||
class:app-object-list-focused={isListFocused}
|
||||
on:focus={() => {
|
||||
isListFocused = true;
|
||||
}}
|
||||
on:focus={handleFocus}
|
||||
on:blur={() => {
|
||||
isListFocused = false;
|
||||
}}
|
||||
bind:this={domDiv}
|
||||
class:hideContent
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
@@ -108,4 +150,8 @@
|
||||
.wrapper:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.hideContent {
|
||||
visibility: hidden;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -16,9 +16,12 @@
|
||||
openedTabs,
|
||||
emptyConnectionGroupNames,
|
||||
collapsedConnectionGroupNames,
|
||||
focusedConnectionOrDatabase,
|
||||
getFocusedConnectionOrDatabase,
|
||||
currentDatabase,
|
||||
} from '../stores';
|
||||
import runCommand from '../commands/runCommand';
|
||||
import { getConnectionLabel } from 'dbgate-tools';
|
||||
import { filterName, getConnectionLabel } from 'dbgate-tools';
|
||||
import { useConnectionColorFactory } from '../utility/useConnectionColor';
|
||||
import FontIcon from '../icons/FontIcon.svelte';
|
||||
import CloseSearchButton from '../buttons/CloseSearchButton.svelte';
|
||||
@@ -29,11 +32,21 @@
|
||||
import { showModal } from '../modals/modalTools';
|
||||
import InputTextModal from '../modals/InputTextModal.svelte';
|
||||
import ConfirmModal from '../modals/ConfirmModal.svelte';
|
||||
import AppObjectListHandler from './AppObjectListHandler.svelte';
|
||||
import { getLocalStorage } from '../utility/storageCache';
|
||||
import { switchCurrentDatabase } from '../utility/common';
|
||||
import openNewTab from '../utility/openNewTab';
|
||||
import { openConnection } from '../appobj/ConnectionAppObject.svelte';
|
||||
|
||||
const connections = useConnectionList();
|
||||
const serverStatus = useServerStatus();
|
||||
|
||||
export let passProps: any = {};
|
||||
|
||||
let filter = '';
|
||||
let domListHandler;
|
||||
let domContainer = null;
|
||||
let domFilter = null;
|
||||
|
||||
$: connectionsWithStatus =
|
||||
$connections && $serverStatus
|
||||
@@ -47,12 +60,52 @@
|
||||
x => !x.unsaved || $openedConnections.includes(x._id) || $openedSingleDatabaseConnections.includes(x._id)
|
||||
);
|
||||
|
||||
$: connectionsWithParent = connectionsWithStatusFiltered
|
||||
? connectionsWithStatusFiltered?.filter(x => x.parent !== undefined && x.parent !== null && x.parent.length !== 0)
|
||||
: [];
|
||||
$: connectionsWithoutParent = connectionsWithStatusFiltered
|
||||
? connectionsWithStatusFiltered?.filter(x => x.parent === undefined || x.parent === null || x.parent.length === 0)
|
||||
: [];
|
||||
$: connectionsWithParent = _.sortBy(
|
||||
connectionsWithStatusFiltered
|
||||
? connectionsWithStatusFiltered?.filter(x => x.parent !== undefined && x.parent !== null && x.parent.length !== 0)
|
||||
: [],
|
||||
connection => (getConnectionLabel(connection) || '').toUpperCase()
|
||||
);
|
||||
$: connectionsWithoutParent = _.sortBy(
|
||||
connectionsWithStatusFiltered
|
||||
? connectionsWithStatusFiltered?.filter(x => x.parent === undefined || x.parent === null || x.parent.length === 0)
|
||||
: [],
|
||||
connection => (getConnectionLabel(connection) || '').toUpperCase()
|
||||
);
|
||||
|
||||
function getFocusFlatList() {
|
||||
const expanded = $expandedConnections;
|
||||
const opened = $openedConnections;
|
||||
|
||||
const res = [];
|
||||
for (const con of [...connectionsWithParent, ...connectionsWithoutParent]) {
|
||||
const databases = getLocalStorage(`database_list_${con._id}`) || [];
|
||||
if (!filterName(filter, con.displayName, con.server, ...databases.map(x => x.name))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
res.push({
|
||||
connection: con,
|
||||
conid: con._id,
|
||||
});
|
||||
|
||||
if ((expanded.includes(con._id) && opened.includes(con._id)) || filter) {
|
||||
for (const db of _.sortBy(databases, x => x.sortOrder ?? x.name)) {
|
||||
if (!filterName(filter, con.displayName, con.server, db.name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
res.push({
|
||||
conid: con._id,
|
||||
database: db.name,
|
||||
connection: con,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
const handleRefreshConnections = () => {
|
||||
for (const conid of $openedConnections) {
|
||||
@@ -112,7 +165,14 @@
|
||||
</script>
|
||||
|
||||
<SearchBoxWrapper>
|
||||
<SearchInput placeholder="Search connection or database" bind:value={filter} />
|
||||
<SearchInput
|
||||
placeholder="Search connection or database"
|
||||
bind:value={filter}
|
||||
bind:this={domFilter}
|
||||
onFocusFilteredList={() => {
|
||||
domListHandler?.focusFirst();
|
||||
}}
|
||||
/>
|
||||
<CloseSearchButton bind:filter />
|
||||
{#if $commandsCustomized['new.connection']?.enabled}
|
||||
<InlineButton on:click={() => runCommand('new.connection')} title="Add new connection">
|
||||
@@ -127,6 +187,7 @@
|
||||
</InlineButton>
|
||||
</SearchBoxWrapper>
|
||||
<WidgetsInnerContainer
|
||||
bind:this={domContainer}
|
||||
on:drop={e => {
|
||||
var data = e.dataTransfer.getData('app_object_drag_data');
|
||||
if (data) {
|
||||
@@ -134,43 +195,88 @@
|
||||
}
|
||||
}}
|
||||
>
|
||||
<AppObjectList
|
||||
list={_.sortBy(connectionsWithParent, connection => (getConnectionLabel(connection) || '').toUpperCase())}
|
||||
module={connectionAppObject}
|
||||
subItemsComponent={SubDatabaseList}
|
||||
expandOnClick
|
||||
isExpandable={data => $openedConnections.includes(data._id) && !data.singleDatabase}
|
||||
{filter}
|
||||
passProps={{ connectionColorFactory: $connectionColorFactory, showPinnedInsteadOfUnpin: true }}
|
||||
getIsExpanded={data => $expandedConnections.includes(data._id) && !data.singleDatabase}
|
||||
setIsExpanded={(data, value) => {
|
||||
expandedConnections.update(old => (value ? [...old, data._id] : old.filter(x => x != data._id)));
|
||||
<AppObjectListHandler
|
||||
bind:this={domListHandler}
|
||||
list={getFocusFlatList}
|
||||
selectedObjectStore={focusedConnectionOrDatabase}
|
||||
getSelectedObject={getFocusedConnectionOrDatabase}
|
||||
selectedObjectMatcher={(o1, o2) => o1?.conid == o2?.conid && o1?.database == o2?.database}
|
||||
getDefaultFocusedItem={() =>
|
||||
$currentDatabase
|
||||
? {
|
||||
conid: $currentDatabase?.connection?._id,
|
||||
database: $currentDatabase?.name,
|
||||
connection: $currentDatabase?.connection,
|
||||
}
|
||||
: null}
|
||||
onScrollTop={() => {
|
||||
domContainer?.scrollTop();
|
||||
}}
|
||||
groupIconFunc={chevronExpandIcon}
|
||||
groupFunc={data => data.parent}
|
||||
expandIconFunc={plusExpandIcon}
|
||||
onDropOnGroup={handleDropOnGroup}
|
||||
emptyGroupNames={$emptyConnectionGroupNames}
|
||||
sortGroups
|
||||
groupContextMenu={createGroupContextMenu}
|
||||
collapsedGroupNames={collapsedConnectionGroupNames}
|
||||
/>
|
||||
{#if (connectionsWithParent?.length > 0 && connectionsWithoutParent?.length > 0) || ($emptyConnectionGroupNames.length > 0 && connectionsWithoutParent?.length > 0)}
|
||||
<div class="br" />
|
||||
{/if}
|
||||
<AppObjectList
|
||||
list={_.sortBy(connectionsWithoutParent, connection => (getConnectionLabel(connection) || '').toUpperCase())}
|
||||
module={connectionAppObject}
|
||||
subItemsComponent={SubDatabaseList}
|
||||
expandOnClick
|
||||
isExpandable={data => $openedConnections.includes(data._id) && !data.singleDatabase}
|
||||
{filter}
|
||||
passProps={{ connectionColorFactory: $connectionColorFactory, showPinnedInsteadOfUnpin: true }}
|
||||
getIsExpanded={data => $expandedConnections.includes(data._id) && !data.singleDatabase}
|
||||
setIsExpanded={(data, value) => {
|
||||
expandedConnections.update(old => (value ? [...old, data._id] : old.filter(x => x != data._id)));
|
||||
onFocusFilterBox={text => {
|
||||
domFilter?.focus(text);
|
||||
}}
|
||||
/>
|
||||
handleObjectClick={(data, options) => {
|
||||
if (data.database) {
|
||||
if (options.focusTab) {
|
||||
switchCurrentDatabase({ connection: data.connection, name: data.database });
|
||||
// console.log('FOCUSING DB', passProps);
|
||||
// passProps?.onFocusSqlObjectList?.();
|
||||
}
|
||||
} else {
|
||||
if (options.focusTab) {
|
||||
openConnection(data.connection);
|
||||
} else {
|
||||
openNewTab({
|
||||
title: getConnectionLabel(data.connection),
|
||||
icon: 'img connection',
|
||||
tabComponent: 'ConnectionTab',
|
||||
tabPreviewMode: options.tabPreviewMode,
|
||||
props: {
|
||||
conid: data.conid,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}}
|
||||
>
|
||||
<AppObjectList
|
||||
list={connectionsWithParent}
|
||||
module={connectionAppObject}
|
||||
subItemsComponent={SubDatabaseList}
|
||||
expandOnClick
|
||||
isExpandable={data => $openedConnections.includes(data._id) && !data.singleDatabase}
|
||||
{filter}
|
||||
passProps={{ ...passProps, connectionColorFactory: $connectionColorFactory, showPinnedInsteadOfUnpin: true }}
|
||||
getIsExpanded={data => $expandedConnections.includes(data._id) && !data.singleDatabase}
|
||||
setIsExpanded={(data, value) => {
|
||||
expandedConnections.update(old => (value ? [...old, data._id] : old.filter(x => x != data._id)));
|
||||
}}
|
||||
groupIconFunc={chevronExpandIcon}
|
||||
groupFunc={data => data.parent}
|
||||
expandIconFunc={plusExpandIcon}
|
||||
onDropOnGroup={handleDropOnGroup}
|
||||
emptyGroupNames={$emptyConnectionGroupNames}
|
||||
sortGroups
|
||||
groupContextMenu={createGroupContextMenu}
|
||||
collapsedGroupNames={collapsedConnectionGroupNames}
|
||||
/>
|
||||
{#if (connectionsWithParent?.length > 0 && connectionsWithoutParent?.length > 0) || ($emptyConnectionGroupNames.length > 0 && connectionsWithoutParent?.length > 0)}
|
||||
<div class="br" />
|
||||
{/if}
|
||||
<AppObjectList
|
||||
list={connectionsWithoutParent}
|
||||
module={connectionAppObject}
|
||||
subItemsComponent={SubDatabaseList}
|
||||
expandOnClick
|
||||
isExpandable={data => $openedConnections.includes(data._id) && !data.singleDatabase}
|
||||
{filter}
|
||||
passProps={{ connectionColorFactory: $connectionColorFactory, showPinnedInsteadOfUnpin: true }}
|
||||
getIsExpanded={data => $expandedConnections.includes(data._id) && !data.singleDatabase}
|
||||
setIsExpanded={(data, value) => {
|
||||
expandedConnections.update(old => (value ? [...old, data._id] : old.filter(x => x != data._id)));
|
||||
}}
|
||||
/>
|
||||
</AppObjectListHandler>
|
||||
{#if $connections && !$connections.find(x => !x.unsaved) && $openedConnections.length == 0 && $commandsCustomized['new.connection']?.enabled && !$openedTabs.find(x => !x.closedTime && x.tabComponent == 'ConnectionTab' && !x.props?.conid)}
|
||||
<LargeButton icon="icon new-connection" on:click={() => runCommand('new.connection')} fillHorizontal
|
||||
>Add new connection</LargeButton
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
import _ from 'lodash';
|
||||
|
||||
export let hidden = false;
|
||||
let domSqlObjectList = null;
|
||||
|
||||
$: conid = $currentDatabase?.connection?._id;
|
||||
$: connection = useConnectionInfo({ conid });
|
||||
@@ -32,7 +33,7 @@
|
||||
</WidgetColumnBarItem>
|
||||
{:else if !$config?.singleDbConnection}
|
||||
<WidgetColumnBarItem title="Connections" name="connections" height="35%" storageName="connectionsWidget">
|
||||
<ConnectionList />
|
||||
<ConnectionList passProps={{ onFocusSqlObjectList: () => domSqlObjectList.focus() }} />
|
||||
</WidgetColumnBarItem>
|
||||
{/if}
|
||||
<WidgetColumnBarItem
|
||||
@@ -48,7 +49,7 @@
|
||||
|
||||
<WidgetColumnBarItem
|
||||
title={driver?.databaseEngineTypes?.includes('document')
|
||||
? driver?.collectionPluralLabel ?? 'Collections/containers'
|
||||
? (driver?.collectionPluralLabel ?? 'Collections/containers')
|
||||
: 'Tables, views, functions'}
|
||||
name="dbObjects"
|
||||
storageName="dbObjectsWidget"
|
||||
@@ -58,7 +59,7 @@
|
||||
(driver?.databaseEngineTypes?.includes('sql') || driver?.databaseEngineTypes?.includes('document'))
|
||||
)}
|
||||
>
|
||||
<SqlObjectList {conid} {database} />
|
||||
<SqlObjectList {conid} {database} bind:this={domSqlObjectList} />
|
||||
</WidgetColumnBarItem>
|
||||
|
||||
<WidgetColumnBarItem
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
import { chevronExpandIcon } from '../icons/expandIcons';
|
||||
import ErrorInfo from '../elements/ErrorInfo.svelte';
|
||||
import LoadingInfo from '../elements/LoadingInfo.svelte';
|
||||
import { getObjectTypeFieldLabel } from '../utility/common';
|
||||
import { getObjectTypeFieldLabel, switchCurrentDatabase } from '../utility/common';
|
||||
import DropDownButton from '../buttons/DropDownButton.svelte';
|
||||
import FontIcon from '../icons/FontIcon.svelte';
|
||||
import CloseSearchButton from '../buttons/CloseSearchButton.svelte';
|
||||
@@ -39,6 +39,7 @@
|
||||
import {
|
||||
currentDatabase,
|
||||
extensions,
|
||||
focusedConnectionOrDatabase,
|
||||
getSelectedDatabaseObjectAppObject,
|
||||
selectedDatabaseObjectAppObject,
|
||||
} from '../stores';
|
||||
@@ -50,6 +51,8 @@
|
||||
import { appliedCurrentSchema } from '../stores';
|
||||
import AppObjectListHandler from './AppObjectListHandler.svelte';
|
||||
import { matchDatabaseObjectAppObject } from '../appobj/appObjectTools';
|
||||
import FormStyledButton from '../buttons/FormStyledButton.svelte';
|
||||
import clickOutside from '../utility/clickOutside';
|
||||
|
||||
export let conid;
|
||||
export let database;
|
||||
@@ -125,6 +128,15 @@
|
||||
if (matcher && !matcher(filter)) return false;
|
||||
return true;
|
||||
});
|
||||
|
||||
export function focus() {
|
||||
domListHandler?.focusFirst();
|
||||
}
|
||||
|
||||
$: differentFocusedDb =
|
||||
$focusedConnectionOrDatabase &&
|
||||
$focusedConnectionOrDatabase?.database &&
|
||||
($focusedConnectionOrDatabase.conid != conid || $focusedConnectionOrDatabase?.database != database);
|
||||
</script>
|
||||
|
||||
{#if $status && $status.name == 'error'}
|
||||
@@ -185,7 +197,36 @@
|
||||
negativeMarginTop
|
||||
/>
|
||||
|
||||
<WidgetsInnerContainer bind:this={domContainer}>
|
||||
{#if differentFocusedDb}
|
||||
<div class="no-focused-info">
|
||||
<div class="m-1">Current database:</div>
|
||||
<div class="m-1 ml-3 mb-3">
|
||||
<b>{database}</b>
|
||||
</div>
|
||||
<FormStyledButton
|
||||
value={`Switch to ${$focusedConnectionOrDatabase?.database}`}
|
||||
skipWidth
|
||||
on:click={() =>
|
||||
switchCurrentDatabase({
|
||||
connection: $focusedConnectionOrDatabase?.connection,
|
||||
name: $focusedConnectionOrDatabase?.database,
|
||||
})}
|
||||
/>
|
||||
<FormStyledButton
|
||||
value={`Show ${database}`}
|
||||
skipWidth
|
||||
on:click={() => {
|
||||
$focusedConnectionOrDatabase = {
|
||||
conid,
|
||||
database,
|
||||
connection: $connection,
|
||||
};
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<WidgetsInnerContainer bind:this={domContainer} hideContent={differentFocusedDb}>
|
||||
{#if ($status && ($status.name == 'pending' || $status.name == 'checkStructure' || $status.name == 'loadStructure') && $objects) || !$objects}
|
||||
<LoadingInfo message={$status?.feedback?.analysingMessage || 'Loading database structure'} />
|
||||
{:else}
|
||||
@@ -195,12 +236,12 @@
|
||||
selectedObjectStore={selectedDatabaseObjectAppObject}
|
||||
getSelectedObject={getSelectedDatabaseObjectAppObject}
|
||||
selectedObjectMatcher={matchDatabaseObjectAppObject}
|
||||
module={databaseObjectAppObject}
|
||||
handleObjectClick={(data, options) => databaseObjectAppObject.handleObjectClick(data, options)}
|
||||
onScrollTop={() => {
|
||||
domContainer?.scrollTop();
|
||||
}}
|
||||
onFocusFilterBox={() => {
|
||||
domFilter?.focus();
|
||||
onFocusFilterBox={text => {
|
||||
domFilter?.focus(text);
|
||||
}}
|
||||
>
|
||||
<AppObjectList
|
||||
@@ -224,3 +265,12 @@
|
||||
{/if}
|
||||
</WidgetsInnerContainer>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.no-focused-info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
<script lang="ts">
|
||||
let domDiv;
|
||||
|
||||
export let hideContent = false;
|
||||
|
||||
export function scrollTop() {
|
||||
domDiv.scrollTop = 0;
|
||||
}
|
||||
</script>
|
||||
|
||||
<div on:drop bind:this={domDiv}><slot /></div>
|
||||
<div on:drop bind:this={domDiv} class:hideContent><slot /></div>
|
||||
|
||||
<style>
|
||||
div {
|
||||
@@ -15,4 +17,8 @@
|
||||
overflow-y: auto;
|
||||
width: var(--dim-left-panel-width);
|
||||
}
|
||||
|
||||
.hideContent {
|
||||
visibility: hidden;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user