/
vshmidt
/
cloudbeaver
Обзор
Документация
Войти
/
vshmidt
/
cloudbeaver
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
devel
webapp/packages/plugin-datasource-context-switch/src/ConnectionSchemaManager/ConnectionSchemaManagerBootstrap.ts
447 строк
17 KB
sergeyteleshev
No translation and tooltips for connection switcher (#4050)
13 янв 2026, 18:51
Не верифицирован
13 янв 2026, 18:51
b91ca06
Код
Авторство
О чём код?
/* * CloudBeaver - Cloud Database Manager * Copyright (C) 2020-2026 DBeaver Corp and others * * Licensed under the Apache License, Version 2.0. * you may not use this file except in compliance with the License. */ import { AppAuthService } from '@cloudbeaver/core-authentication'; import { importLazyComponent } from '@cloudbeaver/core-blocks'; import { compareConnectionsInfo, ConnectionInfoActiveProjectKey, ConnectionInfoResource, ConnectionsManagerService, ConnectionsSettingsService, ContainerResource, createConnectionParam, serializeConnectionParam, } from '@cloudbeaver/core-connections'; import { Bootstrap, injectable } from '@cloudbeaver/core-di'; import { LocalizationService } from '@cloudbeaver/core-localization'; import { EObjectFeature, NodeManagerUtils } from '@cloudbeaver/core-navigation-tree'; import { ProjectsService } from '@cloudbeaver/core-projects'; import { getCachedMapResourceLoaderState } from '@cloudbeaver/core-resource'; import { ContextMenuSearchItem, DATA_CONTEXT_MENU_SEARCH, OptionsPanelService } from '@cloudbeaver/core-ui'; import { getMenuLabelClipped, MenuBaseItem, menuExtractItems, MenuSeparatorItem, MenuService } from '@cloudbeaver/core-view'; import { MENU_APP_ACTIONS } from '@cloudbeaver/plugin-top-app-bar'; import { ConnectionSchemaManagerService } from './ConnectionSchemaManagerService.js'; import type { IConnectionSelectorExtraProps } from './ConnectionSelector/IConnectionSelectorExtraProps.js'; import { MENU_CONNECTION_DATA_CONTAINER_SELECTOR } from './MENU_CONNECTION_DATA_CONTAINER_SELECTOR.js'; import { MENU_CONNECTION_SELECTOR } from './MENU_CONNECTION_SELECTOR.js'; const ConnectionIcon = importLazyComponent(() => import('./ConnectionSelector/ConnectionIcon.js').then(module => module.ConnectionIcon)); const ConnectionIconSmall = importLazyComponent(() => import('./ConnectionSelector/ConnectionIconSmall.js').then(module => module.ConnectionIconSmall), ); @injectable(() => [ ConnectionInfoResource, ConnectionSchemaManagerService, ConnectionsManagerService, OptionsPanelService, AppAuthService, ContainerResource, MenuService, ConnectionsSettingsService, LocalizationService, ProjectsService, ]) export class ConnectionSchemaManagerBootstrap extends Bootstrap { get connectionSelectorLoading(): boolean { return this.connectionSchemaManagerService.isChangingConnection || this.connectionsManagerService.containerContainers.isLoading(); } constructor( private readonly connectionInfoResource: ConnectionInfoResource, private readonly connectionSchemaManagerService: ConnectionSchemaManagerService, private readonly connectionsManagerService: ConnectionsManagerService, private readonly optionsPanelService: OptionsPanelService, private readonly appAuthService: AppAuthService, private readonly containerResource: ContainerResource, private readonly menuService: MenuService, private readonly connectionsSettingsService: ConnectionsSettingsService, private readonly localizationService: LocalizationService, private readonly projectsService: ProjectsService, ) { super(); } override register(): void { this.addTopAppMenuItems(); this.connectionInfoResource.onDataUpdate.addHandler( this.connectionSchemaManagerService.onConnectionUpdate.bind(this.connectionSchemaManagerService), ); this.menuService.setHandler<IConnectionSelectorExtraProps>({ id: 'connection-selector-base', menus: [MENU_CONNECTION_SELECTOR], isLoading: () => this.connectionSelectorLoading, isHidden: () => this.isHidden() || !this.appAuthService.authenticated, isDisabled: () => !this.connectionSchemaManagerService.isConnectionChangeable || this.connectionSelectorLoading || !this.connectionsManagerService.hasAnyConnection() || this.connectionSchemaManagerService.isChangingConnectionContainer, getInfo: (context, menu) => { const connection = this.connectionSchemaManagerService.currentConnection; const label = connection?.name || this.localizationService.translate('plugin_datasource_context_switch_select_connection'); const { clippedLabel, tooltip } = getMenuLabelClipped(label); return { ...menu, label: clippedLabel, tooltip, }; }, iconComponent: () => ConnectionIcon, getExtraProps: () => ({ connectionKey: this.connectionSchemaManagerService.currentConnectionKey, small: true }), getLoader: () => { if (this.isHidden()) { return []; } const activeConnectionKey = this.connectionSchemaManagerService.activeConnectionKey; if (!activeConnectionKey) { return [...this.appAuthService.loaders, getCachedMapResourceLoaderState(this.connectionInfoResource, () => ConnectionInfoActiveProjectKey)]; } return [ ...this.appAuthService.loaders, ...this.connectionSchemaManagerService.currentObjectLoaders, getCachedMapResourceLoaderState(this.connectionInfoResource, () => ConnectionInfoActiveProjectKey), getCachedMapResourceLoaderState(this.containerResource, () => ({ ...activeConnectionKey, catalogId: this.connectionSchemaManagerService.activeObjectCatalogId, })), ]; }, }); this.menuService.addCreator({ menus: [MENU_CONNECTION_SELECTOR], getItems: (context, items) => { const filter = context.get(DATA_CONTEXT_MENU_SEARCH); items = [new ContextMenuSearchItem(), ...items]; const userProjectId = this.projectsService.userProject?.id; const activeProjectId = this.connectionSchemaManagerService.activeProjectId; const connections = this.connectionsManagerService.projectConnections .filter(connection => { // we want to show connections from active project and user project if ( !this.connectionSchemaManagerService.isProjectChangeable && activeProjectId && activeProjectId !== connection.projectId && activeProjectId !== userProjectId && connection.projectId !== userProjectId ) { return false; } if (filter) { return connection.name.toLowerCase().includes(filter.toLowerCase()); } return true; }) .sort((a, b) => { if (a.connected === b.connected) { return compareConnectionsInfo(a, b); } return Number(b.connected) - Number(a.connected); }); for (const connection of connections) { const connectionKey = createConnectionParam(connection); const { clippedLabel, tooltip: tooltipConnectionName } = getMenuLabelClipped(connection.name, 16, 50); const tooltipFull = [tooltipConnectionName, connection.description].filter(Boolean).join('\n'); items.push( new MenuBaseItem<IConnectionSelectorExtraProps>( { id: serializeConnectionParam(connectionKey), label: clippedLabel, tooltip: tooltipFull, }, { onSelect: () => { this.connectionSchemaManagerService.selectConnection(connectionKey); }, }, { iconComponent: () => ConnectionIconSmall, isDisabled: () => this.connectionSchemaManagerService.currentConnectionKey !== null && this.connectionSchemaManagerService.currentConnectionKey !== undefined && this.connectionInfoResource.isKeyEqual(this.connectionSchemaManagerService.currentConnectionKey, connectionKey), getExtraProps: () => ({ connectionKey }), }, ), ); } return items; }, }); this.menuService.setHandler({ id: 'connection-data-container-selector-base', menus: [MENU_CONNECTION_DATA_CONTAINER_SELECTOR], isDisabled: () => !this.connectionSchemaManagerService.currentConnection?.connected || this.connectionSelectorLoading || this.connectionSchemaManagerService.isChangingConnectionContainer || (!this.connectionSchemaManagerService.isObjectCatalogChangeable && !this.connectionSchemaManagerService.isObjectSchemaChangeable), isLoading: () => !this.connectionSelectorLoading && this.connectionSchemaManagerService.isChangingConnectionContainer, isHidden: () => this.isHidden() || !this.appAuthService.authenticated || !this.connectionSchemaManagerService.objectContainerList || (this.connectionSchemaManagerService.currentObjectSchemaId === undefined && this.connectionSchemaManagerService.currentObjectCatalogId === undefined && !this.connectionSchemaManagerService.isObjectCatalogChangeable && !this.connectionSchemaManagerService.isObjectSchemaChangeable) || (this.connectionSchemaManagerService.objectContainerList.schemaList.length === 0 && this.connectionSchemaManagerService.objectContainerList.catalogList.length === 0), getLoader: () => { if (this.isHidden()) { return []; } return this.appAuthService.loaders; }, getInfo: (context, menu) => { const connectionSchemaManagerService = this.connectionSchemaManagerService; let icon: string | undefined = '/icons/plugin_datasource_context_switch_database_m.svg'; let label = NodeManagerUtils.concatSchemaAndCatalog( connectionSchemaManagerService.currentObjectCatalogId, connectionSchemaManagerService.currentObjectSchemaId, ); if (!label) { label = 'plugin_datasource_context_switch_select_container'; } const { clippedLabel } = getMenuLabelClipped(label); if (!connectionSchemaManagerService.currentObjectSchema && !connectionSchemaManagerService.currentObjectCatalog) { icon = undefined; } else if (connectionSchemaManagerService.currentObjectSchema?.object?.features?.includes(EObjectFeature.schema)) { // TODO move such kind of icon paths to a set of constants icon = '/icons/plugin_datasource_context_switch_schema_m.svg'; } else if (connectionSchemaManagerService.currentObjectCatalog?.object?.features?.includes(EObjectFeature.catalog)) { icon = '/icons/plugin_datasource_context_switch_database_m.svg'; } const tooltip = this.localizationService.translate('plugin_datasource_context_switch_select_container_tooltip', undefined, { name: connectionSchemaManagerService.currentConnection?.name, }); return { ...menu, icon, label: clippedLabel, tooltip, }; }, }); this.menuService.addCreator({ menus: [MENU_CONNECTION_DATA_CONTAINER_SELECTOR], getItems: (context, items) => { const filter = context.get(DATA_CONTEXT_MENU_SEARCH); items = [new ContextMenuSearchItem(), ...items]; if (!this.connectionSchemaManagerService.objectContainerList) { return []; } const schemaList = this.connectionSchemaManagerService.objectContainerList.schemaList.slice().sort((a, b) => { if (a.name === b.name) { return 0; } if (a.name === this.connectionSchemaManagerService.currentObjectSchemaId) { return -1; } if (b.name === this.connectionSchemaManagerService.currentObjectSchemaId) { return 1; } return 0; }); const catalogList = this.connectionSchemaManagerService.objectContainerList.catalogList.slice().sort((a, b) => { if (a.catalog.name === b.catalog.name) { return 0; } if (a.catalog.name === this.connectionSchemaManagerService.currentObjectCatalogId) { return -1; } if (b.catalog.name === this.connectionSchemaManagerService.currentObjectCatalogId) { return 1; } return 0; }); let previousSelected: boolean | null = null; for (const schema of schemaList) { const schemaName = schema.name?.trim() || ''; if (!schemaName) { continue; } const selected = this.connectionSchemaManagerService.currentObjectSchemaId === schemaName; if (previousSelected && !selected) { items.push(new MenuSeparatorItem()); } previousSelected = selected; const excluded = !!filter && !schemaName.toLowerCase().includes(filter.toLowerCase()); if (excluded) { continue; } const { clippedLabel, tooltip } = getMenuLabelClipped(schemaName); items.push( new MenuBaseItem( { id: schemaName, label: clippedLabel, tooltip, icon: '/icons/plugin_datasource_context_switch_schema_contrast_sm.svg', }, { onSelect: async () => { await this.connectionSchemaManagerService.selectSchema(schemaName); }, }, { isDisabled: () => this.connectionSchemaManagerService.currentObjectSchemaId === schemaName, }, ), ); } for (const catalogData of catalogList) { const catalog = catalogData.catalog; const catalogName = catalog.name?.trim() || ''; if (!catalogName) { continue; } const selected = this.connectionSchemaManagerService.currentObjectCatalogId === catalogName; if (previousSelected && !selected) { items.push(new MenuSeparatorItem()); } previousSelected = selected; if (catalogData.schemaList.length === 0) { const excluded = !!filter && !catalogName.toLowerCase().includes(filter.toLowerCase()); const { clippedLabel, tooltip } = getMenuLabelClipped(catalogName); if (excluded) { continue; } items.push( new MenuBaseItem( { id: catalogName, label: clippedLabel, tooltip, icon: '/icons/plugin_datasource_context_switch_database_contrast_sm.svg', }, { onSelect: async () => { await this.connectionSchemaManagerService.selectCatalog(catalogName); }, }, { isDisabled: () => this.connectionSchemaManagerService.currentObjectCatalogId === catalogName, }, ), ); } for (const schema of catalogData.schemaList) { if (!schema.name) { continue; } const title = NodeManagerUtils.concatSchemaAndCatalog(catalog.name, schema.name); const excluded = !!filter && !title.toLowerCase().includes(filter.toLowerCase()); const { clippedLabel, tooltip } = getMenuLabelClipped(title); if (excluded) { continue; } items.push( new MenuBaseItem( { id: title, label: clippedLabel, tooltip, icon: '/icons/plugin_datasource_context_switch_schema_contrast_sm.svg', }, { onSelect: async () => { await this.connectionSchemaManagerService.selectSchema(schema.name!, catalog.name!); }, }, { isDisabled: () => this.connectionSchemaManagerService.currentObjectSchemaId === schema.name && this.connectionSchemaManagerService.currentObjectCatalogId === catalog.name, }, ), ); } } return items; }, }); } private isHidden(): boolean { return ( this.connectionsSettingsService.disabled || this.optionsPanelService.active || (!this.connectionSchemaManagerService.isConnectionChangeable && !this.connectionSchemaManagerService.currentConnectionKey) ); } private addTopAppMenuItems() { this.menuService.addCreator({ menus: [MENU_APP_ACTIONS], getItems: (context, items) => [...items, MENU_CONNECTION_SELECTOR, MENU_CONNECTION_DATA_CONTAINER_SELECTOR], orderItems: (context, items) => { items.push(...menuExtractItems(items, [MENU_CONNECTION_SELECTOR, MENU_CONNECTION_DATA_CONTAINER_SELECTOR])); return items; }, }); } }