diff --git a/packages/web/src/appobj/ConnectionAppObject.svelte b/packages/web/src/appobj/ConnectionAppObject.svelte index e0b0c08bf..dd9255277 100644 --- a/packages/web/src/appobj/ConnectionAppObject.svelte +++ b/packages/web/src/appobj/ConnectionAppObject.svelte @@ -83,6 +83,7 @@ currentDatabase, expandedConnections, extensions, + focusedConnectionOrDatabase, getCurrentConfig, getCurrentDatabase, getCurrentSettings, @@ -135,7 +136,7 @@ }); }; - const handleClick = async () => { + const handleDoubleClick = async () => { const config = getCurrentConfig(); if (config.runAsPortal) { await tick(); @@ -158,6 +159,10 @@ } }; + const handleClick = async (e) => { + focusedConnectionOrDatabase.set({ conid: data?._id }); + }; + const handleSqlRestore = () => { showModal(ImportDatabaseDumpModal, { connection: data, @@ -328,6 +333,7 @@ {extInfo} colorMark={passProps?.connectionColorFactory && passProps?.connectionColorFactory({ conid: data._id })} menu={getContextMenu} + on:dblclick={handleDoubleClick} on:click={handleClick} on:click on:expand @@ -337,4 +343,5 @@ .find(x => x.isNewQuery) .onClick(); }} + isChoosed={data._id == $focusedConnectionOrDatabase?.conid && !$focusedConnectionOrDatabase?.database} /> diff --git a/packages/web/src/stores.ts b/packages/web/src/stores.ts index 76dd67dc1..ef04ec619 100644 --- a/packages/web/src/stores.ts +++ b/packages/web/src/stores.ts @@ -158,6 +158,7 @@ export const appliedCurrentSchema = writable(null); export const loadingSchemaLists = writable({}); // dict [`${conid}::${database}`]: true export const selectedDatabaseObjectAppObject = writable(null); +export const focusedConnectionOrDatabase = writable<{ conid: string; database?: string }>(null); export const currentThemeDefinition = derived([currentTheme, extensions], ([$currentTheme, $extensions]) => $extensions.themes.find(x => x.themeClassName == $currentTheme) @@ -339,3 +340,9 @@ openedModals.subscribe(value => { openedModalsValue = value; }); export const getOpenedModals = () => openedModalsValue; + +let focusedConnectionOrDatabaseValue = null; +focusedConnectionOrDatabase.subscribe(value => { + focusedConnectionOrDatabaseValue = value; +}); +export const getFocusedConnectionOrDatabase = () => focusedConnectionOrDatabaseValue; \ No newline at end of file diff --git a/packages/web/src/widgets/AppObjectListHandler.svelte b/packages/web/src/widgets/AppObjectListHandler.svelte index 9fdb57a36..0f64ac685 100644 --- a/packages/web/src/widgets/AppObjectListHandler.svelte +++ b/packages/web/src/widgets/AppObjectListHandler.svelte @@ -6,7 +6,7 @@ export let selectedObjectStore; export let getSelectedObject; export let selectedObjectMatcher; - export let module; + export let handleObjectClick; export let onScrollTop = null; export let onFocusFilterBox = null; @@ -35,7 +35,7 @@ if (list[newIndex]) { selectedObjectStore.set(list[newIndex]); - module.handleObjectClick(list[newIndex], { tabPreviewMode: true }); + handleObjectClick?.(list[newIndex], { tabPreviewMode: true }); } if (newIndex == 0) { @@ -52,7 +52,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) { @@ -66,14 +66,14 @@ if (ev.keyCode == keycodes.home) { if (list[0]) { selectedObjectStore.set(list[0]); - module.handleObjectClick(list[0], { tabPreviewMode: true }); + handleObjectClick?.(list[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 }); + handleObjectClick?.(list[list.length - 1], { tabPreviewMode: true }); } } } @@ -82,7 +82,7 @@ domDiv?.focus(); if (list[0]) { selectedObjectStore.set(list[0]); - module.handleObjectClick(list[0], { tabPreviewMode: true }); + handleObjectClick?.(list[0], { tabPreviewMode: true }); onScrollTop?.(); } } diff --git a/packages/web/src/widgets/ConnectionList.svelte b/packages/web/src/widgets/ConnectionList.svelte index 03160390b..93149811d 100644 --- a/packages/web/src/widgets/ConnectionList.svelte +++ b/packages/web/src/widgets/ConnectionList.svelte @@ -16,6 +16,8 @@ openedTabs, emptyConnectionGroupNames, collapsedConnectionGroupNames, + focusedConnectionOrDatabase, + getFocusedConnectionOrDatabase, } from '../stores'; import runCommand from '../commands/runCommand'; import { getConnectionLabel } from 'dbgate-tools'; @@ -29,11 +31,15 @@ import { showModal } from '../modals/modalTools'; import InputTextModal from '../modals/InputTextModal.svelte'; import ConfirmModal from '../modals/ConfirmModal.svelte'; + import AppObjectListHandler from './AppObjectListHandler.svelte'; const connections = useConnectionList(); const serverStatus = useServerStatus(); let filter = ''; + let domListHandler; + let domContainer = null; + let domFilter = null; $: connectionsWithStatus = $connections && $serverStatus @@ -47,12 +53,23 @@ 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() + ); + + $: focusFlatList = [ + ...connectionsWithParent.map(x => ({ conid: x._id })), + ...connectionsWithoutParent.map(x => ({ conid: x._id })), + ]; const handleRefreshConnections = () => { for (const conid of $openedConnections) { @@ -112,7 +129,14 @@ - + { + domListHandler?.focusFirst(); + }} + /> {#if $commandsCustomized['new.connection']?.enabled} runCommand('new.connection')} title="Add new connection"> @@ -127,6 +151,7 @@ { var data = e.dataTransfer.getData('app_object_drag_data'); if (data) { @@ -134,43 +159,57 @@ } }} > - (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))); + o1.conid == o2.conid && o1.database == o2.database} + 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)} -
- {/if} - (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={() => { + domFilter?.focus(); }} - /> + > + $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))); + }} + 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)} +
+ {/if} + $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))); + }} + /> + {#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)} runCommand('new.connection')} fillHorizontal >Add new connection databaseObjectAppObject.handleObjectClick(data, options)} onScrollTop={() => { domContainer?.scrollTop(); }}