diff --git a/webapp/packages/core-connections/src/NavTree/NAV_NODE_TYPE_CONNECTION.ts b/webapp/packages/core-connections/src/NavTree/NAV_NODE_TYPE_CONNECTION.ts
new file mode 100644
index 0000000000..88a7ac870e
--- /dev/null
+++ b/webapp/packages/core-connections/src/NavTree/NAV_NODE_TYPE_CONNECTION.ts
@@ -0,0 +1,9 @@
+/*
+ * CloudBeaver - Cloud Database Manager
+ * Copyright (C) 2020-2023 DBeaver Corp and others
+ *
+ * Licensed under the Apache License, Version 2.0.
+ * you may not use this file except in compliance with the License.
+ */
+
+export const NAV_NODE_TYPE_CONNECTION = 'Connection';
diff --git a/webapp/packages/core-connections/src/index.ts b/webapp/packages/core-connections/src/index.ts
index 8b380f25b6..c344d7f560 100644
--- a/webapp/packages/core-connections/src/index.ts
+++ b/webapp/packages/core-connections/src/index.ts
@@ -14,6 +14,7 @@ export * from './NavTree/getConnectionFolderIdFromNodeId';
export * from './NavTree/getConnectionFolderId';
export * from './NavTree/getConnectionParentId';
export * from './NavTree/getFolderNodeParents';
+export * from './NavTree/NAV_NODE_TYPE_CONNECTION';
export * from './extensions/IConnectionProvider';
export * from './extensions/IConnectionSetter';
diff --git a/webapp/packages/core-dialogs/src/CommonDialog/ConfirmationDialogDelete.tsx b/webapp/packages/core-dialogs/src/CommonDialog/ConfirmationDialogDelete.tsx
index bdc781d486..a84711fde1 100644
--- a/webapp/packages/core-dialogs/src/CommonDialog/ConfirmationDialogDelete.tsx
+++ b/webapp/packages/core-dialogs/src/CommonDialog/ConfirmationDialogDelete.tsx
@@ -13,5 +13,12 @@ export const ConfirmationDialogDelete: DialogComponent;
+ const bigIcon = payload.bigIcon ?? payload.subTitle === undefined ?? true;
+ let icon = payload.icon ?? '/icons/error_icon_sm.svg';
+
+ if (bigIcon) {
+ icon = '/icons/error_icon.svg';
+ }
+
+ return ;
};
diff --git a/webapp/packages/core-projects/src/ProjectInfoResource.ts b/webapp/packages/core-projects/src/ProjectInfoResource.ts
index 5ce6fa6ba9..b43bcf72c5 100644
--- a/webapp/packages/core-projects/src/ProjectInfoResource.ts
+++ b/webapp/packages/core-projects/src/ProjectInfoResource.ts
@@ -17,6 +17,8 @@ import {
ProjectInfo as SchemaProjectInfo,
} from '@cloudbeaver/core-sdk';
+import { createResourceOfType } from './createResourceOfType';
+
export type ProjectInfo = SchemaProjectInfo;
export type ProjectInfoResourceType = RmResourceType;
@@ -42,6 +44,37 @@ export class ProjectInfoResource extends CachedMapResource
});
}
+ getNameWithoutExtension(projectId: string, resourceTypeId: string, fileName: string): string {
+ const project = this.get(projectId);
+
+ if (project) {
+ const resourceType = this.getResourceType(project, resourceTypeId);
+
+ for (let ext of resourceType?.fileExtensions || []) {
+ ext = `.${ext}`;
+ if (fileName.toLowerCase().endsWith(ext)) {
+ return fileName.slice(0, fileName.length - ext.length);
+ }
+ }
+ }
+
+ return fileName;
+ }
+
+ getNameWithExtension(projectId: string, resourceTypeId: string, fileName: string): string {
+ const project = this.get(projectId);
+
+ if (project) {
+ const resourceType = this.getResourceType(project, resourceTypeId);
+
+ if (resourceType?.fileExtensions.length) {
+ return createResourceOfType(resourceType, fileName);
+ }
+ }
+
+ return fileName;
+ }
+
getUserProject(userId: string): ProjectInfo | undefined {
return this.get(`u_${userId}`);
}
diff --git a/webapp/packages/core-projects/src/createResourceOfType.ts b/webapp/packages/core-projects/src/createResourceOfType.ts
index a8a3adc34d..d6df4ab443 100644
--- a/webapp/packages/core-projects/src/createResourceOfType.ts
+++ b/webapp/packages/core-projects/src/createResourceOfType.ts
@@ -5,9 +5,14 @@
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
+import { isResourceOfType } from './isResourceOfType';
import type { ProjectInfoResourceType } from './ProjectInfoResource';
export function createResourceOfType(resourceType: ProjectInfoResourceType, name: string): string {
+ if (isResourceOfType(resourceType, name)) {
+ return name;
+ }
+
const extension = resourceType.fileExtensions[0];
return `${name}${extension ? '.' + extension : ''}`;
}
diff --git a/webapp/packages/plugin-resource-manager/src/NavNodes/ResourceFoldersBootstrap.ts b/webapp/packages/plugin-navigation-tree-rm/src/NavNodes/ResourceFoldersBootstrap.ts
similarity index 99%
rename from webapp/packages/plugin-resource-manager/src/NavNodes/ResourceFoldersBootstrap.ts
rename to webapp/packages/plugin-navigation-tree-rm/src/NavNodes/ResourceFoldersBootstrap.ts
index d9ddc87dc7..5e5c9955e2 100644
--- a/webapp/packages/plugin-resource-manager/src/NavNodes/ResourceFoldersBootstrap.ts
+++ b/webapp/packages/plugin-navigation-tree-rm/src/NavNodes/ResourceFoldersBootstrap.ts
@@ -46,11 +46,11 @@ import { DATA_CONTEXT_ELEMENTS_TREE, MENU_ELEMENTS_TREE_TOOLS } from '@cloudbeav
import { FolderDialog } from '@cloudbeaver/plugin-projects';
import { NavResourceNodeService } from '../NavResourceNodeService';
-import { ResourceManagerService } from '../ResourceManagerService';
import { DATA_CONTEXT_RESOURCE_MANAGER_TREE_RESOURCE_TYPE_ID } from '../Tree/DATA_CONTEXT_RESOURCE_MANAGER_TREE_RESOURCE_TYPE_ID';
import { getResourceKeyFromNodeId } from './getResourceKeyFromNodeId';
import { getResourceNodeId } from './getResourceNodeId';
import { getRmProjectNodeId } from './getRmProjectNodeId';
+import { ResourceManagerService } from '@cloudbeaver/plugin-resource-manager';
interface ITargetNode {
projectId: string;
diff --git a/webapp/packages/plugin-resource-manager/src/NavNodes/getResourceKeyFromNodeId.ts b/webapp/packages/plugin-navigation-tree-rm/src/NavNodes/getResourceKeyFromNodeId.ts
similarity index 100%
rename from webapp/packages/plugin-resource-manager/src/NavNodes/getResourceKeyFromNodeId.ts
rename to webapp/packages/plugin-navigation-tree-rm/src/NavNodes/getResourceKeyFromNodeId.ts
diff --git a/webapp/packages/plugin-resource-manager/src/NavNodes/getResourceNodeId.ts b/webapp/packages/plugin-navigation-tree-rm/src/NavNodes/getResourceNodeId.ts
similarity index 100%
rename from webapp/packages/plugin-resource-manager/src/NavNodes/getResourceNodeId.ts
rename to webapp/packages/plugin-navigation-tree-rm/src/NavNodes/getResourceNodeId.ts
diff --git a/webapp/packages/plugin-resource-manager/src/NavNodes/getRmProjectNodeId.ts b/webapp/packages/plugin-navigation-tree-rm/src/NavNodes/getRmProjectNodeId.ts
similarity index 100%
rename from webapp/packages/plugin-resource-manager/src/NavNodes/getRmProjectNodeId.ts
rename to webapp/packages/plugin-navigation-tree-rm/src/NavNodes/getRmProjectNodeId.ts
diff --git a/webapp/packages/plugin-resource-manager/src/NavResourceNodeService.ts b/webapp/packages/plugin-navigation-tree-rm/src/NavResourceNodeService.ts
similarity index 57%
rename from webapp/packages/plugin-resource-manager/src/NavResourceNodeService.ts
rename to webapp/packages/plugin-navigation-tree-rm/src/NavResourceNodeService.ts
index e18ca0cb7e..d83898d272 100644
--- a/webapp/packages/plugin-resource-manager/src/NavResourceNodeService.ts
+++ b/webapp/packages/plugin-navigation-tree-rm/src/NavResourceNodeService.ts
@@ -6,13 +6,20 @@
* you may not use this file except in compliance with the License.
*/
import { injectable } from '@cloudbeaver/core-di';
+import { CommonDialogService, ConfirmationDialogDelete, DialogueStateResult } from '@cloudbeaver/core-dialogs';
+import { LocalizationService } from '@cloudbeaver/core-localization';
import { ResourceManagerResource } from '@cloudbeaver/core-resource-manager';
+import { getPathName } from '@cloudbeaver/core-utils';
import { getResourceNodeId } from './NavNodes/getResourceNodeId';
@injectable()
export class NavResourceNodeService {
- constructor(private readonly resourceManagerResource: ResourceManagerResource) {}
+ constructor(
+ private readonly resourceManagerResource: ResourceManagerResource,
+ private readonly commonDialogService: CommonDialogService,
+ private readonly localizationService: LocalizationService,
+ ) {}
async move(key: string, newKey: string): Promise {
await this.resourceManagerResource.move(key, newKey);
@@ -21,6 +28,16 @@ export class NavResourceNodeService {
}
async delete(key: string) {
+ const result = await this.commonDialogService.open(ConfirmationDialogDelete, {
+ title: 'ui_data_delete_confirmation',
+ message: this.localizationService.translate('plugin_navigation_tree_rm_delete_confirmation_message', undefined, { name: getPathName(key) }),
+ confirmActionText: 'ui_delete',
+ });
+
+ if (result === DialogueStateResult.Rejected) {
+ return;
+ }
+
await this.resourceManagerResource.deleteResource(key);
}
diff --git a/webapp/packages/plugin-navigation-tree-rm/src/NavTreeRMContextMenuService.ts b/webapp/packages/plugin-navigation-tree-rm/src/NavTreeRMContextMenuService.ts
index 2aa8e52e10..fe7ca665d5 100644
--- a/webapp/packages/plugin-navigation-tree-rm/src/NavTreeRMContextMenuService.ts
+++ b/webapp/packages/plugin-navigation-tree-rm/src/NavTreeRMContextMenuService.ts
@@ -10,16 +10,20 @@ import { CommonDialogService, DialogueStateResult, RenameDialog } from '@cloudbe
import { NotificationService } from '@cloudbeaver/core-events';
import { LocalizationService } from '@cloudbeaver/core-localization';
import { DATA_CONTEXT_NAV_NODE } from '@cloudbeaver/core-navigation-tree';
+import { isResourceOfType, ProjectInfoResource, ProjectInfoResourceType } from '@cloudbeaver/core-projects';
import { getRmResourceKey, NAV_NODE_TYPE_RM_FOLDER, NAV_NODE_TYPE_RM_RESOURCE } from '@cloudbeaver/core-resource-manager';
import { createPath, getPathParent } from '@cloudbeaver/core-utils';
import { ACTION_DELETE, ACTION_RENAME, ActionService } from '@cloudbeaver/core-view';
import { DATA_CONTEXT_NAV_NODE_ACTIONS } from '@cloudbeaver/plugin-navigation-tree';
-import { getResourceKeyFromNodeId, NavResourceNodeService } from '@cloudbeaver/plugin-resource-manager';
+
+import { getResourceKeyFromNodeId } from './NavNodes/getResourceKeyFromNodeId';
+import { NavResourceNodeService } from './NavResourceNodeService';
@injectable()
export class NavTreeRMContextMenuService extends Bootstrap {
constructor(
private readonly actionService: ActionService,
+ private readonly projectInfoResource: ProjectInfoResource,
private readonly commonDialogService: CommonDialogService,
private readonly notificationService: NotificationService,
private readonly navResourceNodeService: NavResourceNodeService,
@@ -53,6 +57,17 @@ export class NavTreeRMContextMenuService extends Bootstrap {
}
const key = getRmResourceKey(resourceKey);
+ const project = this.projectInfoResource.get(key.projectId);
+
+ let resourceType: ProjectInfoResourceType | undefined = undefined;
+ if (project) {
+ for (const type of project.resourceTypes) {
+ if (isResourceOfType(type, node.id)) {
+ resourceType = type;
+ break;
+ }
+ }
+ }
switch (action) {
case ACTION_RENAME: {
@@ -60,6 +75,10 @@ export class NavTreeRMContextMenuService extends Bootstrap {
const save = async (newName: string) => {
if (key.name !== newName && newName.trim().length) {
+ if (resourceType && node.nodeType === NAV_NODE_TYPE_RM_RESOURCE && !node.folder) {
+ newName = this.projectInfoResource.getNameWithExtension(key.projectId, resourceType.id, newName);
+ }
+
try {
await this.navResourceNodeService.move(resourceKey, createPath(getPathParent(resourceKey), newName));
node.name = newName; // fix name flickering in tree
diff --git a/webapp/packages/plugin-resource-manager/src/Tree/DATA_CONTEXT_RESOURCE_MANAGER_TREE_RESOURCE_TYPE_ID.ts b/webapp/packages/plugin-navigation-tree-rm/src/Tree/DATA_CONTEXT_RESOURCE_MANAGER_TREE_RESOURCE_TYPE_ID.ts
similarity index 100%
rename from webapp/packages/plugin-resource-manager/src/Tree/DATA_CONTEXT_RESOURCE_MANAGER_TREE_RESOURCE_TYPE_ID.ts
rename to webapp/packages/plugin-navigation-tree-rm/src/Tree/DATA_CONTEXT_RESOURCE_MANAGER_TREE_RESOURCE_TYPE_ID.ts
diff --git a/webapp/packages/plugin-resource-manager/src/Tree/ProjectsRenderer/NavigationNodeProjectControl.tsx b/webapp/packages/plugin-navigation-tree-rm/src/Tree/ProjectsRenderer/NavigationNodeProjectControl.tsx
similarity index 98%
rename from webapp/packages/plugin-resource-manager/src/Tree/ProjectsRenderer/NavigationNodeProjectControl.tsx
rename to webapp/packages/plugin-navigation-tree-rm/src/Tree/ProjectsRenderer/NavigationNodeProjectControl.tsx
index 707fb66b13..c55550bf4f 100644
--- a/webapp/packages/plugin-resource-manager/src/Tree/ProjectsRenderer/NavigationNodeProjectControl.tsx
+++ b/webapp/packages/plugin-navigation-tree-rm/src/Tree/ProjectsRenderer/NavigationNodeProjectControl.tsx
@@ -25,8 +25,8 @@ import {
} from '@cloudbeaver/plugin-navigation-tree';
import { getRmProjectNodeId } from '../../NavNodes/getRmProjectNodeId';
-import { ResourceManagerService } from '../../ResourceManagerService';
import { DATA_CONTEXT_RESOURCE_MANAGER_TREE_RESOURCE_TYPE_ID } from '../DATA_CONTEXT_RESOURCE_MANAGER_TREE_RESOURCE_TYPE_ID';
+import { ResourceManagerService } from '@cloudbeaver/plugin-resource-manager';
const styles = css`
TreeNodeControl {
diff --git a/webapp/packages/plugin-resource-manager/src/Tree/ProjectsRenderer/ProjectsSettingsForm.tsx b/webapp/packages/plugin-navigation-tree-rm/src/Tree/ProjectsRenderer/ProjectsSettingsForm.tsx
similarity index 100%
rename from webapp/packages/plugin-resource-manager/src/Tree/ProjectsRenderer/ProjectsSettingsForm.tsx
rename to webapp/packages/plugin-navigation-tree-rm/src/Tree/ProjectsRenderer/ProjectsSettingsForm.tsx
diff --git a/webapp/packages/plugin-resource-manager/src/Tree/ProjectsRenderer/navigationTreeProjectFilter.ts b/webapp/packages/plugin-navigation-tree-rm/src/Tree/ProjectsRenderer/navigationTreeProjectFilter.ts
similarity index 97%
rename from webapp/packages/plugin-resource-manager/src/Tree/ProjectsRenderer/navigationTreeProjectFilter.ts
rename to webapp/packages/plugin-navigation-tree-rm/src/Tree/ProjectsRenderer/navigationTreeProjectFilter.ts
index ee1aec3ab6..c0a560552a 100644
--- a/webapp/packages/plugin-resource-manager/src/Tree/ProjectsRenderer/navigationTreeProjectFilter.ts
+++ b/webapp/packages/plugin-navigation-tree-rm/src/Tree/ProjectsRenderer/navigationTreeProjectFilter.ts
@@ -11,8 +11,7 @@ import { NAV_NODE_TYPE_RM_PROJECT, RESOURCES_NODE_PATH } from '@cloudbeaver/core
import { resourceKeyList } from '@cloudbeaver/core-sdk';
import { createPath, isDefined } from '@cloudbeaver/core-utils';
import type { IElementsTreeFilter } from '@cloudbeaver/plugin-navigation-tree';
-
-import type { ResourceManagerService } from '../../ResourceManagerService';
+import type { ResourceManagerService } from '@cloudbeaver/plugin-resource-manager';
export function navigationTreeProjectFilter(
projectsNavNodeService: ProjectsNavNodeService,
diff --git a/webapp/packages/plugin-resource-manager/src/Tree/ProjectsRenderer/navigationTreeProjectSearchCompare.ts b/webapp/packages/plugin-navigation-tree-rm/src/Tree/ProjectsRenderer/navigationTreeProjectSearchCompare.ts
similarity index 100%
rename from webapp/packages/plugin-resource-manager/src/Tree/ProjectsRenderer/navigationTreeProjectSearchCompare.ts
rename to webapp/packages/plugin-navigation-tree-rm/src/Tree/ProjectsRenderer/navigationTreeProjectSearchCompare.ts
diff --git a/webapp/packages/plugin-resource-manager/src/Tree/ProjectsRenderer/navigationTreeProjectsExpandStateGetter.ts b/webapp/packages/plugin-navigation-tree-rm/src/Tree/ProjectsRenderer/navigationTreeProjectsExpandStateGetter.ts
similarity index 100%
rename from webapp/packages/plugin-resource-manager/src/Tree/ProjectsRenderer/navigationTreeProjectsExpandStateGetter.ts
rename to webapp/packages/plugin-navigation-tree-rm/src/Tree/ProjectsRenderer/navigationTreeProjectsExpandStateGetter.ts
diff --git a/webapp/packages/plugin-resource-manager/src/Tree/ProjectsRenderer/navigationTreeProjectsRendererRenderer.tsx b/webapp/packages/plugin-navigation-tree-rm/src/Tree/ProjectsRenderer/navigationTreeProjectsRendererRenderer.tsx
similarity index 97%
rename from webapp/packages/plugin-resource-manager/src/Tree/ProjectsRenderer/navigationTreeProjectsRendererRenderer.tsx
rename to webapp/packages/plugin-navigation-tree-rm/src/Tree/ProjectsRenderer/navigationTreeProjectsRendererRenderer.tsx
index de8b70d99a..5143101c87 100644
--- a/webapp/packages/plugin-resource-manager/src/Tree/ProjectsRenderer/navigationTreeProjectsRendererRenderer.tsx
+++ b/webapp/packages/plugin-navigation-tree-rm/src/Tree/ProjectsRenderer/navigationTreeProjectsRendererRenderer.tsx
@@ -24,8 +24,8 @@ import {
useNode,
} from '@cloudbeaver/plugin-navigation-tree';
-import type { ResourceManagerService } from '../../ResourceManagerService';
import { NavigationNodeProjectControl } from './NavigationNodeProjectControl';
+import type { ResourceManagerService } from '@cloudbeaver/plugin-resource-manager';
const nestedStyles = css`
TreeNode {
diff --git a/webapp/packages/plugin-resource-manager/src/Tree/ProjectsRenderer/navigationTreeResourceTypeFilter.ts b/webapp/packages/plugin-navigation-tree-rm/src/Tree/ProjectsRenderer/navigationTreeResourceTypeFilter.ts
similarity index 100%
rename from webapp/packages/plugin-resource-manager/src/Tree/ProjectsRenderer/navigationTreeResourceTypeFilter.ts
rename to webapp/packages/plugin-navigation-tree-rm/src/Tree/ProjectsRenderer/navigationTreeResourceTypeFilter.ts
diff --git a/webapp/packages/plugin-resource-manager/src/Tree/ResourceFolderRenderer/navigationTreeResourceExpandStateGetter.ts b/webapp/packages/plugin-navigation-tree-rm/src/Tree/ResourceFolderRenderer/navigationTreeResourceExpandStateGetter.ts
similarity index 94%
rename from webapp/packages/plugin-resource-manager/src/Tree/ResourceFolderRenderer/navigationTreeResourceExpandStateGetter.ts
rename to webapp/packages/plugin-navigation-tree-rm/src/Tree/ResourceFolderRenderer/navigationTreeResourceExpandStateGetter.ts
index d051b22ebf..e9822f7a30 100644
--- a/webapp/packages/plugin-resource-manager/src/Tree/ResourceFolderRenderer/navigationTreeResourceExpandStateGetter.ts
+++ b/webapp/packages/plugin-navigation-tree-rm/src/Tree/ResourceFolderRenderer/navigationTreeResourceExpandStateGetter.ts
@@ -9,8 +9,7 @@ import type { NavNodeInfoResource, ProjectsNavNodeService } from '@cloudbeaver/c
import { RESOURCES_NODE_PATH } from '@cloudbeaver/core-resource-manager';
import { createPath } from '@cloudbeaver/core-utils';
import type { IElementsTreeNodeExpandInfoGetter } from '@cloudbeaver/plugin-navigation-tree';
-
-import type { ResourceManagerService } from '../../ResourceManagerService';
+import type { ResourceManagerService } from '@cloudbeaver/plugin-resource-manager';
export function navigationTreeResourceExpandStateGetter(
navNodeInfoResource: NavNodeInfoResource,
diff --git a/webapp/packages/plugin-resource-manager/src/Tree/ResourceManagerTree.tsx b/webapp/packages/plugin-navigation-tree-rm/src/Tree/ResourceManagerTree.tsx
similarity index 92%
rename from webapp/packages/plugin-resource-manager/src/Tree/ResourceManagerTree.tsx
rename to webapp/packages/plugin-navigation-tree-rm/src/Tree/ResourceManagerTree.tsx
index 0745ccb1bb..a9f86adb9f 100644
--- a/webapp/packages/plugin-resource-manager/src/Tree/ResourceManagerTree.tsx
+++ b/webapp/packages/plugin-navigation-tree-rm/src/Tree/ResourceManagerTree.tsx
@@ -26,7 +26,6 @@ import {
validateElementsTreeSettings,
} from '@cloudbeaver/plugin-navigation-tree';
-import { ResourceManagerService } from '../ResourceManagerService';
import { navigationTreeProjectFilter } from './ProjectsRenderer/navigationTreeProjectFilter';
import { navigationTreeProjectSearchCompare } from './ProjectsRenderer/navigationTreeProjectSearchCompare';
import { navigationTreeProjectsExpandStateGetter } from './ProjectsRenderer/navigationTreeProjectsExpandStateGetter';
@@ -35,6 +34,8 @@ import { navigationTreeResourceTypeFilter } from './ProjectsRenderer/navigationT
import { ProjectsSettingsPlaceholderElement } from './ProjectsRenderer/ProjectsSettingsForm';
import { navigationTreeResourceExpandStateGetter } from './ResourceFolderRenderer/navigationTreeResourceExpandStateGetter';
import { ResourceManagerTreeCaptureViewContext } from './ResourceManagerTreeCaptureViewContext';
+import { transformResourceNodeInfo } from './ResourceRenderer/transformResourceNodeInfo';
+import { ResourceManagerService } from '@cloudbeaver/plugin-resource-manager';
const styles = css`
CaptureView {
@@ -107,6 +108,10 @@ export const ResourceManagerTree: React.FC = observer(function ResourceMa
() => navigationTreeProjectsExpandStateGetter(navNodeInfoResource, projectsService, projectsNavNodeService),
[navNodeInfoResource, projectsService, projectsNavNodeService],
);
+ const transformResourceNode = useMemo(
+ () => transformResourceNodeInfo(projectInfoResource, projectsNavNodeService, navNodeInfoResource, resourceTypeId),
+ [projectInfoResource, projectsNavNodeService, navNodeInfoResource, resourceTypeId],
+ );
const projectFilter = useMemo(
() =>
navigationTreeProjectFilter(
@@ -134,6 +139,7 @@ export const ResourceManagerTree: React.FC = observer(function ResourceMa
getChildren={navTreeService.getChildren}
loadChildren={navTreeService.loadNestedNodes}
settings={settings}
+ nodeInfoTransformers={[transformResourceNode]}
filters={[resourceTypeFilter, projectFilter]}
renderers={[projectsRendererRenderer]}
expandStateGetters={[projectsExpandStateGetter, resourceExpandStateGetter]}
diff --git a/webapp/packages/plugin-resource-manager/src/Tree/ResourceManagerTreeCaptureViewContext.tsx b/webapp/packages/plugin-navigation-tree-rm/src/Tree/ResourceManagerTreeCaptureViewContext.tsx
similarity index 100%
rename from webapp/packages/plugin-resource-manager/src/Tree/ResourceManagerTreeCaptureViewContext.tsx
rename to webapp/packages/plugin-navigation-tree-rm/src/Tree/ResourceManagerTreeCaptureViewContext.tsx
diff --git a/webapp/packages/plugin-navigation-tree-rm/src/Tree/ResourceRenderer/transformResourceNodeInfo.ts b/webapp/packages/plugin-navigation-tree-rm/src/Tree/ResourceRenderer/transformResourceNodeInfo.ts
new file mode 100644
index 0000000000..e3ec88e8f5
--- /dev/null
+++ b/webapp/packages/plugin-navigation-tree-rm/src/Tree/ResourceRenderer/transformResourceNodeInfo.ts
@@ -0,0 +1,33 @@
+/*
+ * CloudBeaver - Cloud Database Manager
+ * Copyright (C) 2020-2023 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 type { NavNodeInfoResource, ProjectsNavNodeService } from '@cloudbeaver/core-navigation-tree';
+import type { ProjectInfoResource } from '@cloudbeaver/core-projects';
+import { NAV_NODE_TYPE_RM_RESOURCE } from '@cloudbeaver/core-resource-manager';
+import type { IElementsTreeCustomNodeInfo } from '@cloudbeaver/plugin-navigation-tree';
+
+export function transformResourceNodeInfo(
+ projectInfoResource: ProjectInfoResource,
+ projectsNavNodeService: ProjectsNavNodeService,
+ navNodeInfoResource: NavNodeInfoResource,
+ resourceTypeId: string | undefined,
+): IElementsTreeCustomNodeInfo {
+ return function transformResourceNodeInfo(nodeId, info) {
+ const node = navNodeInfoResource.get(nodeId);
+ if (node?.nodeType === NAV_NODE_TYPE_RM_RESOURCE && resourceTypeId) {
+ const project = projectsNavNodeService.getProject(node.id);
+
+ if (project) {
+ return {
+ ...info,
+ name: projectInfoResource.getNameWithoutExtension(project.id, resourceTypeId, info.name),
+ };
+ }
+ }
+ return info;
+ };
+}
diff --git a/webapp/packages/plugin-navigation-tree-rm/src/index.ts b/webapp/packages/plugin-navigation-tree-rm/src/index.ts
index fe9636e353..4a6b9741c9 100644
--- a/webapp/packages/plugin-navigation-tree-rm/src/index.ts
+++ b/webapp/packages/plugin-navigation-tree-rm/src/index.ts
@@ -1,4 +1,9 @@
import { navigationTreeRMPlugin } from './manifest';
+export * from './NavNodes/getResourceKeyFromNodeId';
+export * from './NavNodes/getResourceNodeId';
+export * from './NavNodes/getRmProjectNodeId';
+export * from './Tree/ResourceManagerTree';
+export * from './NavResourceNodeService';
export { navigationTreeRMPlugin };
export default navigationTreeRMPlugin;
diff --git a/webapp/packages/plugin-navigation-tree-rm/src/locales/en.ts b/webapp/packages/plugin-navigation-tree-rm/src/locales/en.ts
index d6d1738de6..19c2d0f8b3 100644
--- a/webapp/packages/plugin-navigation-tree-rm/src/locales/en.ts
+++ b/webapp/packages/plugin-navigation-tree-rm/src/locales/en.ts
@@ -1 +1 @@
-export default [];
+export default [['plugin_navigation_tree_rm_delete_confirmation_message', 'You\'re going to delete "{arg:name}" resource. Are you sure?']];
diff --git a/webapp/packages/plugin-navigation-tree-rm/src/locales/it.ts b/webapp/packages/plugin-navigation-tree-rm/src/locales/it.ts
index d6d1738de6..19c2d0f8b3 100644
--- a/webapp/packages/plugin-navigation-tree-rm/src/locales/it.ts
+++ b/webapp/packages/plugin-navigation-tree-rm/src/locales/it.ts
@@ -1 +1 @@
-export default [];
+export default [['plugin_navigation_tree_rm_delete_confirmation_message', 'You\'re going to delete "{arg:name}" resource. Are you sure?']];
diff --git a/webapp/packages/plugin-navigation-tree-rm/src/locales/ru.ts b/webapp/packages/plugin-navigation-tree-rm/src/locales/ru.ts
index d6d1738de6..c89dafc3c0 100644
--- a/webapp/packages/plugin-navigation-tree-rm/src/locales/ru.ts
+++ b/webapp/packages/plugin-navigation-tree-rm/src/locales/ru.ts
@@ -1 +1 @@
-export default [];
+export default [['plugin_navigation_tree_rm_delete_confirmation_message', 'Вы собираетесь удалить "{arg:name}" ресурс. Вы уверены?']];
diff --git a/webapp/packages/plugin-navigation-tree-rm/src/locales/zh.ts b/webapp/packages/plugin-navigation-tree-rm/src/locales/zh.ts
index d6d1738de6..19c2d0f8b3 100644
--- a/webapp/packages/plugin-navigation-tree-rm/src/locales/zh.ts
+++ b/webapp/packages/plugin-navigation-tree-rm/src/locales/zh.ts
@@ -1 +1 @@
-export default [];
+export default [['plugin_navigation_tree_rm_delete_confirmation_message', 'You\'re going to delete "{arg:name}" resource. Are you sure?']];
diff --git a/webapp/packages/plugin-navigation-tree-rm/src/manifest.ts b/webapp/packages/plugin-navigation-tree-rm/src/manifest.ts
index 5923b9fc30..69c2a4d9d1 100644
--- a/webapp/packages/plugin-navigation-tree-rm/src/manifest.ts
+++ b/webapp/packages/plugin-navigation-tree-rm/src/manifest.ts
@@ -9,8 +9,15 @@ import type { PluginManifest } from '@cloudbeaver/core-di';
import { LocaleService } from './LocaleService';
import { NavTreeRMContextMenuService } from './NavTreeRMContextMenuService';
+import { ResourceFoldersBootstrap } from './NavNodes/ResourceFoldersBootstrap';
+import { NavResourceNodeService } from './NavResourceNodeService';
export const navigationTreeRMPlugin: PluginManifest = {
info: { name: 'Navigation Tree RM plugin' },
- providers: [LocaleService, NavTreeRMContextMenuService],
+ providers: [
+ LocaleService,
+ NavResourceNodeService,
+ ResourceFoldersBootstrap,
+ NavTreeRMContextMenuService
+ ],
};
diff --git a/webapp/packages/plugin-navigation-tree/src/NavigationTree/ConnectionsRenderer/ConnectionNavNodeControl.tsx b/webapp/packages/plugin-navigation-tree/src/NavigationTree/ConnectionsRenderer/ConnectionNavNodeControl.tsx
new file mode 100644
index 0000000000..62690fb1fb
--- /dev/null
+++ b/webapp/packages/plugin-navigation-tree/src/NavigationTree/ConnectionsRenderer/ConnectionNavNodeControl.tsx
@@ -0,0 +1,83 @@
+/*
+ * CloudBeaver - Cloud Database Manager
+ * Copyright (C) 2020-2023 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 { observer } from 'mobx-react-lite';
+import React, { forwardRef, useContext } from 'react';
+import styled, { use } from 'reshadow';
+
+import {
+ ConnectionImageWithMask,
+ getComputed,
+ Loader,
+ TREE_NODE_STYLES,
+ TreeNodeContext,
+ TreeNodeControl,
+ TreeNodeIcon,
+ TreeNodeName,
+ useMouseContextMenu,
+} from '@cloudbeaver/core-blocks';
+import { useService } from '@cloudbeaver/core-di';
+import { EventContext, EventStopPropagationFlag } from '@cloudbeaver/core-events';
+import { EObjectFeature, NavNodeInfoResource, NavTreeResource } from '@cloudbeaver/core-navigation-tree';
+
+import type { NavTreeControlComponent, NavTreeControlProps } from '../ElementsTree/NavigationNodeComponent';
+import { NAVIGATION_NODE_CONTROL_STYLES } from '../ElementsTree/NavigationTreeNode/NavigationNode/NAVIGATION_NODE_CONTROL_STYLES';
+import { NavigationNodeExpand } from '../ElementsTree/NavigationTreeNode/NavigationNode/NavigationNodeExpand';
+import { TreeNodeMenuLoader } from '../ElementsTree/NavigationTreeNode/TreeNodeMenu/TreeNodeMenuLoader';
+
+export const ConnectionNavNodeControl: NavTreeControlComponent = observer(
+ forwardRef(function ConnectionNavNodeControl({ node, nodeInfo, dndElement, dndPlaceholder, className, onClick }, ref) {
+ const mouseContextMenu = useMouseContextMenu();
+ const treeNodeContext = useContext(TreeNodeContext);
+ const navNodeInfoResource = useService(NavNodeInfoResource);
+ const navTreeResource = useService(NavTreeResource);
+ const selected = treeNodeContext.selected;
+
+ const error = getComputed(() => !!navNodeInfoResource.getException(node.id) || !!navTreeResource.getException(node.id));
+ const connected = getComputed(() => node.objectFeatures.includes(EObjectFeature.dataSourceConnected));
+
+ let icon = nodeInfo.icon;
+ const name = nodeInfo.name;
+ const title = nodeInfo.name;
+
+ if (error) {
+ icon = '/icons/error_icon_sm.svg';
+ }
+
+ function handlePortalClick(event: React.MouseEvent) {
+ EventContext.set(event, EventStopPropagationFlag);
+ treeNodeContext.select();
+ }
+
+ function handleContextMenuOpen(event: React.MouseEvent) {
+ mouseContextMenu.handleContextMenuOpen(event);
+ treeNodeContext.select();
+ }
+
+ return styled(
+ TREE_NODE_STYLES,
+ NAVIGATION_NODE_CONTROL_STYLES,
+ )(
+
+
+
+
+
+
+
+ {name}
+
+
+ {!dndPlaceholder && (
+
+
+
+ )}
+ ,
+ );
+ }),
+);
diff --git a/webapp/packages/plugin-navigation-tree/src/NavigationTree/ConnectionsRenderer/navTreeConnectionRenderer.tsx b/webapp/packages/plugin-navigation-tree/src/NavigationTree/ConnectionsRenderer/navTreeConnectionRenderer.tsx
new file mode 100644
index 0000000000..a54e9eb53f
--- /dev/null
+++ b/webapp/packages/plugin-navigation-tree/src/NavigationTree/ConnectionsRenderer/navTreeConnectionRenderer.tsx
@@ -0,0 +1,63 @@
+/*
+ * CloudBeaver - Cloud Database Manager
+ * Copyright (C) 2020-2023 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 { observer } from 'mobx-react-lite';
+import styled from 'reshadow';
+
+import { Translate, TREE_NODE_STYLES, TreeNodeNestedMessage } from '@cloudbeaver/core-blocks';
+import { NAV_NODE_TYPE_CONNECTION } from '@cloudbeaver/core-connections';
+import { useService } from '@cloudbeaver/core-di';
+import { NavNodeInfoResource } from '@cloudbeaver/core-navigation-tree';
+
+import type { NavigationNodeRendererComponent } from '../ElementsTree/NavigationNodeComponent';
+import { NavigationNodeRendererLoader } from '../ElementsTree/NavigationTreeNode/NavigationNodeRendererLoader';
+import type { IElementsTreeCustomRenderer } from '../ElementsTree/useElementsTree';
+import { ConnectionNavNodeControl } from './ConnectionNavNodeControl';
+
+export function navTreeConnectionRenderer(navNodeInfoResource: NavNodeInfoResource): IElementsTreeCustomRenderer {
+ return nodeId => {
+ const node = navNodeInfoResource.get(nodeId);
+
+ if (node?.nodeType === NAV_NODE_TYPE_CONNECTION) {
+ return ConnectionRenderer;
+ }
+
+ return undefined;
+ };
+}
+
+const ConnectionRenderer: NavigationNodeRendererComponent = observer(function ManageableGroup({
+ nodeId,
+ path,
+ dragging,
+ component,
+ className,
+ expanded,
+}) {
+ const navNodeInfoResource = useService(NavNodeInfoResource);
+ const node = navNodeInfoResource.get(nodeId);
+
+ if (!node) {
+ return styled(TREE_NODE_STYLES)(
+
+
+ ,
+ );
+ }
+
+ return (
+
+ );
+});
diff --git a/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/ElementsTree.tsx b/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/ElementsTree.tsx
index 6090ce3c7f..64a676bfc4 100644
--- a/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/ElementsTree.tsx
+++ b/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/ElementsTree.tsx
@@ -129,6 +129,7 @@ export const ElementsTree = observer(function ElementsTree({
selectionTree = false,
emptyPlaceholder,
navNodeFilterCompare,
+ nodeInfoTransformers = [],
filters = [],
renderers = [],
expandStateGetters,
@@ -172,6 +173,7 @@ export const ElementsTree = observer(function ElementsTree({
root,
disabled,
localState,
+ nodeInfoTransformers,
filters: [nameFilter, ...filters, limitFilter],
renderers: [...renderers, elementsTreeLimitRenderer],
expandStateGetters,
diff --git a/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/INavTreeNodeInfo.ts b/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/INavTreeNodeInfo.ts
new file mode 100644
index 0000000000..10df90dfa6
--- /dev/null
+++ b/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/INavTreeNodeInfo.ts
@@ -0,0 +1,13 @@
+/*
+ * CloudBeaver - Cloud Database Manager
+ * Copyright (C) 2020-2023 DBeaver Corp and others
+ *
+ * Licensed under the Apache License, Version 2.0.
+ * you may not use this file except in compliance with the License.
+ */
+
+export interface INavTreeNodeInfo {
+ name: string;
+ tooltip?: string;
+ icon?: string;
+}
diff --git a/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/NavigationNodeComponent.ts b/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/NavigationNodeComponent.ts
index 4b9b016f9c..0d65fe803c 100644
--- a/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/NavigationNodeComponent.ts
+++ b/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/NavigationNodeComponent.ts
@@ -10,6 +10,8 @@ import type React from 'react';
import type { NavNode } from '@cloudbeaver/core-navigation-tree';
import type { ComponentStyle } from '@cloudbeaver/core-theming';
+import type { INavTreeNodeInfo } from './INavTreeNodeInfo';
+
export type NavTreeNodeComponent = React.FC<{
nodeId: string;
path: string[];
@@ -20,12 +22,14 @@ export type NavTreeNodeComponent = React.FC<{
export type NavTreeControlProps = {
node: NavNode;
+ nodeInfo: INavTreeNodeInfo;
dndElement?: boolean;
dndPlaceholder?: boolean;
expanded?: boolean;
className?: string;
style?: ComponentStyle;
ref?: React.Ref | undefined;
+ onClick?: (event: React.MouseEvent) => void;
};
export type NavTreeControlComponent = React.FC;
diff --git a/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/NavigationTreeNode/NavigationNode/NAVIGATION_NODE_CONTROL_STYLES.ts b/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/NavigationTreeNode/NavigationNode/NAVIGATION_NODE_CONTROL_STYLES.ts
new file mode 100644
index 0000000000..8757b6f40b
--- /dev/null
+++ b/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/NavigationTreeNode/NavigationNode/NAVIGATION_NODE_CONTROL_STYLES.ts
@@ -0,0 +1,39 @@
+/*
+ * CloudBeaver - Cloud Database Manager
+ * Copyright (C) 2020-2023 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 { css } from 'reshadow';
+
+export const NAVIGATION_NODE_CONTROL_STYLES = css`
+ TreeNodeControl:hover > portal,
+ TreeNodeControl:global([aria-selected='true']) > portal,
+ portal:focus-within {
+ visibility: visible;
+ }
+ TreeNodeName {
+ height: 100%;
+ max-width: 320px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+
+ &[|editing] {
+ padding: 0;
+ overflow: visible;
+ margin-left: 2px;
+ }
+ }
+ portal {
+ position: relative;
+ box-sizing: border-box;
+ margin-left: auto !important;
+ margin-right: 8px !important;
+ visibility: hidden;
+ }
+ name-box {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+`;
diff --git a/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/NavigationTreeNode/NavigationNode/NavigationNodeControl.tsx b/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/NavigationTreeNode/NavigationNode/NavigationNodeControl.tsx
index 5664da5c1c..c88ed1cedd 100644
--- a/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/NavigationTreeNode/NavigationNode/NavigationNodeControl.tsx
+++ b/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/NavigationTreeNode/NavigationNode/NavigationNodeControl.tsx
@@ -7,17 +7,15 @@
*/
import { action, observable } from 'mobx';
import { observer } from 'mobx-react-lite';
-import React, { forwardRef, useContext, useDeferredValue } from 'react';
-import styled, { css, use } from 'reshadow';
+import React, { forwardRef, useContext } from 'react';
+import styled, { use } from 'reshadow';
import {
- ConnectionImageWithMask,
getComputed,
Loader,
TREE_NODE_STYLES,
TreeNodeContext,
TreeNodeControl,
- TreeNodeExpand,
TreeNodeIcon,
TreeNodeName,
useMouseContextMenu,
@@ -26,53 +24,15 @@ import {
} from '@cloudbeaver/core-blocks';
import { useService } from '@cloudbeaver/core-di';
import { EventContext, EventStopPropagationFlag } from '@cloudbeaver/core-events';
-import { EObjectFeature, type INodeActions, NavNodeInfoResource, NavTreeResource } from '@cloudbeaver/core-navigation-tree';
+import { type INodeActions, NavNodeInfoResource, NavTreeResource } from '@cloudbeaver/core-navigation-tree';
-import { ElementsTreeContext } from '../../ElementsTreeContext';
import type { NavTreeControlComponent, NavTreeControlProps } from '../../NavigationNodeComponent';
import { TreeNodeMenuLoader } from '../TreeNodeMenu/TreeNodeMenuLoader';
import { DATA_ATTRIBUTE_NODE_EDITING } from './DATA_ATTRIBUTE_NODE_EDITING';
+import { NAVIGATION_NODE_CONTROL_STYLES } from './NAVIGATION_NODE_CONTROL_STYLES';
+import { NavigationNodeExpand } from './NavigationNodeExpand';
import { NavigationNodeEditorLoader } from './NavigationNodeLoaders';
-const styles = css`
- TreeNodeControl {
- transition: opacity 0.3s ease;
- opacity: 1;
-
- &[|outdated] {
- opacity: 0.5;
- }
- }
- TreeNodeControl:hover > portal,
- TreeNodeControl:global([aria-selected='true']) > portal,
- portal:focus-within {
- visibility: visible;
- }
- TreeNodeName {
- height: 100%;
- max-width: 320px;
- overflow: hidden;
- text-overflow: ellipsis;
-
- &[|editing] {
- padding: 0;
- overflow: visible;
- margin-left: 2px;
- }
- }
- portal {
- position: relative;
- box-sizing: border-box;
- margin-left: auto !important;
- margin-right: 8px !important;
- visibility: hidden;
- }
- name-box {
- overflow: hidden;
- text-overflow: ellipsis;
- }
-`;
-
interface IEditingState {
saving: boolean;
editing: boolean;
@@ -88,17 +48,13 @@ interface IEditingState {
}
export const NavigationNodeControl: NavTreeControlComponent = observer(
- forwardRef(function NavigationNodeControl({ node, dndElement, dndPlaceholder }, ref) {
+ forwardRef(function NavigationNodeControl({ node, nodeInfo, dndElement, dndPlaceholder, className, onClick }, ref) {
const mouseContextMenu = useMouseContextMenu();
const treeNodeContext = useContext(TreeNodeContext);
- const treeContext = useContext(ElementsTreeContext);
const navNodeInfoResource = useService(NavNodeInfoResource);
const navTreeResource = useService(NavTreeResource);
- const outdated = getComputed(() => navNodeInfoResource.isOutdated(node.id) && !treeNodeContext.loading);
const error = getComputed(() => !!navNodeInfoResource.getException(node.id) || !!navTreeResource.getException(node.id));
- const connected = getComputed(() => node.objectFeatures.includes(EObjectFeature.dataSourceConnected));
const selected = treeNodeContext.selected;
-
const editingState = useObservableRef(
() => ({
saving: false,
@@ -151,7 +107,9 @@ export const NavigationNodeControl: NavTreeControlComponent = observer) {
- treeNodeContext.select(event.ctrlKey || event.metaKey);
- }
-
function handleContextMenuOpen(event: React.MouseEvent) {
mouseContextMenu.handleContextMenuOpen(event);
treeNodeContext.select();
}
- const expandable = useDeferredValue(getComputed(() => treeContext?.tree.isNodeExpandable(node.id) ?? true));
- const filterActive = useDeferredValue(getComputed(() => treeContext?.tree.filtering));
const { editing, saving } = editingState;
const attributes = { [DATA_ATTRIBUTE_NODE_EDITING]: editing };
return styled(
TREE_NODE_STYLES,
- styles,
+ NAVIGATION_NODE_CONTROL_STYLES,
)(
- {expandable && }
-
-
-
-
+
+
+
{editing ? (
-
+
) : (
- {node.name}
+ {name}
)}
diff --git a/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/NavigationTreeNode/NavigationNode/NavigationNodeEditor.tsx b/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/NavigationTreeNode/NavigationNode/NavigationNodeEditor.tsx
index e6f1517850..234e1997e2 100644
--- a/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/NavigationTreeNode/NavigationNode/NavigationNodeEditor.tsx
+++ b/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/NavigationTreeNode/NavigationNode/NavigationNodeEditor.tsx
@@ -10,28 +10,29 @@ import { useState } from 'react';
import styled, { css } from 'reshadow';
import { EventContext, EventStopPropagationFlag } from '@cloudbeaver/core-events';
-import type { NavNode } from '@cloudbeaver/core-navigation-tree';
import { InlineEditor } from '@cloudbeaver/core-ui';
const styles = css`
InlineEditor {
height: 22px;
- & input {
+ & input,
+ & input[disabled],
+ & input[readonly] {
padding: 1px;
}
}
`;
interface Props {
- node: NavNode;
+ name: string;
disabled?: boolean;
onSave: (name: string) => void;
onClose: () => void;
}
-export const NavigationNodeEditor = observer(function NavigationNodeEditor({ node, disabled, onSave, onClose }) {
- const [name, setName] = useState(node.name || '');
+export const NavigationNodeEditor = observer(function NavigationNodeEditor({ name: initialName, disabled, onSave, onClose }) {
+ const [name, setName] = useState(initialName);
function save() {
onSave(name);
diff --git a/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/NavigationTreeNode/NavigationNode/NavigationNodeExpand.tsx b/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/NavigationTreeNode/NavigationNode/NavigationNodeExpand.tsx
new file mode 100644
index 0000000000..2a4581c8c0
--- /dev/null
+++ b/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/NavigationTreeNode/NavigationNode/NavigationNodeExpand.tsx
@@ -0,0 +1,31 @@
+/*
+ * CloudBeaver - Cloud Database Manager
+ * Copyright (C) 2020-2023 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 { observer } from 'mobx-react-lite';
+import { useContext, useDeferredValue } from 'react';
+import styled from 'reshadow';
+
+import { getComputed, TREE_NODE_STYLES, TreeNodeExpand } from '@cloudbeaver/core-blocks';
+
+import { ElementsTreeContext } from '../../ElementsTreeContext';
+
+interface Props {
+ nodeId: string;
+}
+
+export const NavigationNodeExpand = observer(function NavigationNodeExpand({ nodeId }) {
+ const treeContext = useContext(ElementsTreeContext);
+
+ const expandable = useDeferredValue(getComputed(() => treeContext?.tree.isNodeExpandable(nodeId) ?? true));
+ const filterActive = useDeferredValue(getComputed(() => treeContext?.tree.filtering ?? false));
+
+ if (!expandable) {
+ return null;
+ }
+
+ return styled(TREE_NODE_STYLES)();
+});
diff --git a/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/NavigationTreeNode/NavigationNodeControlRenderer.tsx b/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/NavigationTreeNode/NavigationNodeControlRenderer.tsx
index a6b7d29c15..08fc68d33c 100644
--- a/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/NavigationTreeNode/NavigationNodeControlRenderer.tsx
+++ b/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/NavigationTreeNode/NavigationNodeControlRenderer.tsx
@@ -7,13 +7,16 @@
*/
import { observer } from 'mobx-react-lite';
import { forwardRef, useContext, useDeferredValue, useEffect, useRef, useState } from 'react';
+import styled, { css, use } from 'reshadow';
-import { useObjectRef } from '@cloudbeaver/core-blocks';
-import type { NavNode } from '@cloudbeaver/core-navigation-tree';
+import { getComputed, TreeNodeContext, useObjectRef } from '@cloudbeaver/core-blocks';
+import { useService } from '@cloudbeaver/core-di';
+import { type NavNode, NavNodeInfoResource } from '@cloudbeaver/core-navigation-tree';
import type { ComponentStyle } from '@cloudbeaver/core-theming';
import { ElementsTreeContext } from '../ElementsTreeContext';
import type { NavTreeControlComponent } from '../NavigationNodeComponent';
+import { transformNodeInfo } from '../transformNodeInfo';
import { NavigationNodeControlLoader } from './NavigationNode/NavigationNodeLoaders';
import type { INavigationNode } from './useNavigationNode';
@@ -25,6 +28,17 @@ interface Props {
style?: ComponentStyle;
}
+const styles = css`
+ Control {
+ transition: opacity 0.3s ease;
+ opacity: 1;
+
+ &[|outdated] {
+ opacity: 0.5;
+ }
+ }
+`;
+
export const NavigationNodeControlRenderer = observer(
forwardRef(function NavigationNodeControlRenderer({ node, navNode, dragging, control: externalControl, style }, ref) {
const elementRef = useRef(null);
@@ -32,7 +46,8 @@ export const NavigationNodeControlRenderer = observer(
const contextRef = useObjectRef({
context: useContext(ElementsTreeContext),
});
- const Control = navNode.control || externalControl || NavigationNodeControlLoader;
+ const treeNodeContext = useContext(TreeNodeContext);
+ const navNodeInfoResource = useService(NavNodeInfoResource);
const observer = useRef(null);
useEffect(() => {
@@ -77,6 +92,16 @@ export const NavigationNodeControlRenderer = observer(
return ;
}
- return ;
+ const Control = navNode.control || externalControl || NavigationNodeControlLoader;
+ const outdated = getComputed(() => navNodeInfoResource.isOutdated(node.id) && !treeNodeContext.loading);
+ const nodeInfo = transformNodeInfo(node, contextRef.context?.tree.nodeInfoTransformers ?? []);
+
+ function onClickHandler(event: React.MouseEvent) {
+ treeNodeContext.select(event.ctrlKey || event.metaKey);
+ }
+
+ return styled(styles)(
+ ,
+ );
}),
);
diff --git a/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/NavigationTreeNode/NavigationNodeDragged.tsx b/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/NavigationTreeNode/NavigationNodeDragged.tsx
index ebc9a7b3d2..d1239079eb 100644
--- a/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/NavigationTreeNode/NavigationNodeDragged.tsx
+++ b/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/NavigationTreeNode/NavigationNodeDragged.tsx
@@ -13,6 +13,7 @@ import { getComputed, TreeNode, useObjectRef, useStyles } from '@cloudbeaver/cor
import { ElementsTreeContext } from '../ElementsTreeContext';
import type { NavigationNodeComponent } from '../NavigationNodeComponent';
+import { transformNodeInfo } from '../transformNodeInfo';
import { NavigationNodeControlLoader } from './NavigationNode/NavigationNodeLoaders';
export const NavigationNodeDragged: NavigationNodeComponent = observer(function NavigationNode({ node, className, control: externalControl, style }) {
@@ -22,11 +23,12 @@ export const NavigationNodeDragged: NavigationNodeComponent = observer(function
const control = getComputed(() => contextRef.context?.control);
const Control = control || externalControl || NavigationNodeControlLoader;
+ const nodeInfo = transformNodeInfo(node, contextRef.context?.tree.nodeInfoTransformers ?? []);
return styled(useStyles(style))(
{/* */}
-
+
,
);
});
diff --git a/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/transformNodeInfo.ts b/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/transformNodeInfo.ts
new file mode 100644
index 0000000000..6ccfc4905e
--- /dev/null
+++ b/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/transformNodeInfo.ts
@@ -0,0 +1,19 @@
+/*
+ * CloudBeaver - Cloud Database Manager
+ * Copyright (C) 2020-2023 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 type { NavNode } from '@cloudbeaver/core-navigation-tree';
+
+import type { INavTreeNodeInfo } from './INavTreeNodeInfo';
+import type { IElementsTreeCustomNodeInfo } from './useElementsTree';
+
+export function transformNodeInfo(node: NavNode, transformers: IElementsTreeCustomNodeInfo[]): INavTreeNodeInfo {
+ return transformers.reduce((info, transformer) => transformer(node.id, info), {
+ name: node.name,
+ tooltip: node.name,
+ icon: node.icon,
+ } as INavTreeNodeInfo);
+}
diff --git a/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/useElementsTree.ts b/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/useElementsTree.ts
index ca7c363d16..fd2271ad77 100644
--- a/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/useElementsTree.ts
+++ b/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/useElementsTree.ts
@@ -20,9 +20,11 @@ import type { IDNDData } from '@cloudbeaver/core-ui';
import { ILoadableState, MetadataMap, throttle } from '@cloudbeaver/core-utils';
import type { IElementsTreeAction } from './IElementsTreeAction';
+import type { INavTreeNodeInfo } from './INavTreeNodeInfo';
import type { NavigationNodeRendererComponent } from './NavigationNodeComponent';
export type IElementsTreeCustomRenderer = (nodeId: string) => NavigationNodeRendererComponent | undefined;
+export type IElementsTreeCustomNodeInfo = (nodeId: string, info: INavTreeNodeInfo) => INavTreeNodeInfo;
export interface IElementsTreeNodeExpandedInfo {
expanded: boolean;
@@ -66,6 +68,7 @@ export interface IElementsTreeOptions {
disabled?: boolean;
filters?: IElementsTreeFilter[];
renderers?: IElementsTreeCustomRenderer[];
+ nodeInfoTransformers?: IElementsTreeCustomNodeInfo[];
expandStateGetters?: IElementsTreeNodeExpandInfoGetter[];
localState?: MetadataMap;
getChildren: (id: string) => string[] | undefined;
@@ -97,6 +100,7 @@ export interface IElementsTree extends ILoadableState {
loading: boolean;
disabled: boolean;
activeDnDData: IDNDData[];
+ nodeInfoTransformers: IElementsTreeCustomNodeInfo[];
renderers: IElementsTreeCustomRenderer[];
state: MetadataMap;
userData: IElementsTreeUserState;
@@ -141,6 +145,7 @@ export function useElementsTree(options: IOptions): IElementsTree {
);
options = useObjectRef(options);
+ options.nodeInfoTransformers = useMemo(() => options.nodeInfoTransformers || [], [...(options.nodeInfoTransformers || [])]);
options.renderers = useMemo(() => options.renderers || [], [...(options.renderers || [])]);
options.filters = useMemo(() => options.filters || [], [...(options.filters || [])]);
options.expandStateGetters = useMemo(() => options.expandStateGetters || [], [...(options.expandStateGetters || [])]);
@@ -627,6 +632,7 @@ export function useElementsTree(options: IOptions): IElementsTree {
filtering: computed,
loading: observable.ref,
renderers: observable.ref,
+ nodeInfoTransformers: observable.ref,
baseRoot: observable.ref,
collapse: action.bound,
userData: observable.ref,
@@ -639,6 +645,7 @@ export function useElementsTree(options: IOptions): IElementsTree {
settings: options.settings,
baseRoot: options.baseRoot,
renderers: options.renderers,
+ nodeInfoTransformers: options.nodeInfoTransformers,
userData,
},
['isLoading', 'isLoaded'],
diff --git a/webapp/packages/plugin-navigation-tree/src/NavigationTree/NavigationTree.tsx b/webapp/packages/plugin-navigation-tree/src/NavigationTree/NavigationTree.tsx
index 5555342140..3da6973bef 100644
--- a/webapp/packages/plugin-navigation-tree/src/NavigationTree/NavigationTree.tsx
+++ b/webapp/packages/plugin-navigation-tree/src/NavigationTree/NavigationTree.tsx
@@ -9,7 +9,6 @@ import { observer } from 'mobx-react-lite';
import { useMemo } from 'react';
import styled, { css } from 'reshadow';
-import { AppAuthService } from '@cloudbeaver/core-authentication';
import { Translate, useUserData } from '@cloudbeaver/core-blocks';
import { useService } from '@cloudbeaver/core-di';
import { NavNodeInfoResource, NavTreeResource, ProjectsNavNodeService, ROOT_NODE_PATH } from '@cloudbeaver/core-navigation-tree';
@@ -19,6 +18,7 @@ import { CaptureView } from '@cloudbeaver/core-view';
import { NavNodeViewService } from '../NodesManager/NavNodeView/NavNodeViewService';
import { navigationTreeConnectionGroupFilter } from './ConnectionGroup/navigationTreeConnectionGroupFilter';
import { navigationTreeConnectionGroupRenderer } from './ConnectionGroup/navigationTreeConnectionGroupRenderer';
+import { navTreeConnectionRenderer } from './ConnectionsRenderer/navTreeConnectionRenderer';
import { ElementsTree } from './ElementsTree/ElementsTree';
import {
createElementsTreeSettings,
@@ -98,6 +98,7 @@ export const NavigationTree = observer(function NavigationTree() {
);
const duplicateFilter = useMemo(() => navigationTreeDuplicateFilter(navNodeViewService), [navNodeViewService]);
+ const connectionRenderer = useMemo(() => navTreeConnectionRenderer(navNodeInfoResource), [navNodeInfoResource]);
const projectsRendererRenderer = useMemo(() => navigationTreeProjectsRendererRenderer(navNodeInfoResource), [navNodeInfoResource]);
const projectsExpandStateGetter = useMemo(
() => navigationTreeProjectsExpandStateGetter(navNodeInfoResource, projectsService, projectsNavNodeService),
@@ -116,7 +117,7 @@ export const NavigationTree = observer(function NavigationTree() {
root={root}
localState={navTreeService.treeState}
filters={[duplicateFilter, connectionGroupFilter, projectFilter]}
- renderers={[projectsRendererRenderer, navigationTreeConnectionGroupRenderer]}
+ renderers={[projectsRendererRenderer, navigationTreeConnectionGroupRenderer, connectionRenderer]}
navNodeFilterCompare={navigationTreeProjectSearchCompare}
expandStateGetters={[projectsExpandStateGetter]}
settingsElements={settingsElements}
diff --git a/webapp/packages/plugin-resource-manager-scripts/package.json b/webapp/packages/plugin-resource-manager-scripts/package.json
index c70d3c1d4e..3e70dc65f0 100644
--- a/webapp/packages/plugin-resource-manager-scripts/package.json
+++ b/webapp/packages/plugin-resource-manager-scripts/package.json
@@ -16,6 +16,7 @@
"validate-dependencies": "core-cli-validate-dependencies"
},
"dependencies": {
+ "@cloudbeaver/plugin-navigation-tree-rm": "~0.1.0",
"@cloudbeaver/plugin-resource-manager": "~0.1.0",
"@cloudbeaver/core-di": "~0.1.0",
"@cloudbeaver/core-root": "~0.1.0",
diff --git a/webapp/packages/plugin-resource-manager-scripts/src/ResourceManagerScripts.tsx b/webapp/packages/plugin-resource-manager-scripts/src/ResourceManagerScripts.tsx
index 28986b9413..0c6168956e 100644
--- a/webapp/packages/plugin-resource-manager-scripts/src/ResourceManagerScripts.tsx
+++ b/webapp/packages/plugin-resource-manager-scripts/src/ResourceManagerScripts.tsx
@@ -10,7 +10,7 @@ import styled, { css } from 'reshadow';
import { useTranslate } from '@cloudbeaver/core-blocks';
import type { TabContainerPanelComponent } from '@cloudbeaver/core-ui';
-import { ResourceManagerTree } from '@cloudbeaver/plugin-resource-manager';
+import { ResourceManagerTree } from '@cloudbeaver/plugin-navigation-tree-rm';
import { SCRIPTS_TYPE_ID } from './SCRIPTS_TYPE_ID';
diff --git a/webapp/packages/plugin-resource-manager/src/index.ts b/webapp/packages/plugin-resource-manager/src/index.ts
index 22cb91e455..e339c1810b 100644
--- a/webapp/packages/plugin-resource-manager/src/index.ts
+++ b/webapp/packages/plugin-resource-manager/src/index.ts
@@ -3,10 +3,5 @@ import { resourceManagerPlugin } from './manifest';
export default resourceManagerPlugin;
export * from './ResourceManagerSettingsService';
-export * from './NavResourceNodeService';
export * from './RESOURCE_NAME_REGEX';
export * from './ResourceManagerService';
-export * from './NavNodes/getResourceKeyFromNodeId';
-export * from './NavNodes/getResourceNodeId';
-export * from './NavNodes/getRmProjectNodeId';
-export * from './Tree/ResourceManagerTree';
diff --git a/webapp/packages/plugin-resource-manager/src/manifest.ts b/webapp/packages/plugin-resource-manager/src/manifest.ts
index fc4e692f3c..95ef78c902 100644
--- a/webapp/packages/plugin-resource-manager/src/manifest.ts
+++ b/webapp/packages/plugin-resource-manager/src/manifest.ts
@@ -8,8 +8,6 @@
import type { PluginManifest } from '@cloudbeaver/core-di';
import { LocaleService } from './LocaleService';
-import { ResourceFoldersBootstrap } from './NavNodes/ResourceFoldersBootstrap';
-import { NavResourceNodeService } from './NavResourceNodeService';
import { PluginBootstrap } from './PluginBootstrap';
import { ResourceManagerService } from './ResourceManagerService';
import { ResourceManagerSettingsService } from './ResourceManagerSettingsService';
@@ -23,8 +21,6 @@ export const resourceManagerPlugin: PluginManifest = {
PluginBootstrap,
LocaleService,
ResourceManagerService,
- NavResourceNodeService,
- ResourceFoldersBootstrap,
// ResourceProjectsResource,
],
};
diff --git a/webapp/packages/plugin-sql-editor-navigation-tab-script/package.json b/webapp/packages/plugin-sql-editor-navigation-tab-script/package.json
index 1e6a5dd69b..757db16051 100644
--- a/webapp/packages/plugin-sql-editor-navigation-tab-script/package.json
+++ b/webapp/packages/plugin-sql-editor-navigation-tab-script/package.json
@@ -20,6 +20,7 @@
"@cloudbeaver/plugin-resource-manager": "~0.1.0",
"@cloudbeaver/plugin-sql-editor": "~0.1.0",
"@cloudbeaver/plugin-sql-editor-navigation-tab": "~0.1.0",
+ "@cloudbeaver/plugin-navigation-tree-rm": "~0.1.0",
"@cloudbeaver/core-connections": "~0.1.0",
"@cloudbeaver/core-di": "~0.1.0",
"@cloudbeaver/core-dialogs": "~0.1.0",
diff --git a/webapp/packages/plugin-sql-editor-navigation-tab-script/src/PluginBootstrap.ts b/webapp/packages/plugin-sql-editor-navigation-tab-script/src/PluginBootstrap.ts
index f5a36cdd06..98810934d9 100644
--- a/webapp/packages/plugin-sql-editor-navigation-tab-script/src/PluginBootstrap.ts
+++ b/webapp/packages/plugin-sql-editor-navigation-tab-script/src/PluginBootstrap.ts
@@ -10,13 +10,14 @@ import { CommonDialogService, DialogueStateResult } from '@cloudbeaver/core-dial
import { NotificationService } from '@cloudbeaver/core-events';
import type { IExecutionContextProvider } from '@cloudbeaver/core-executor';
import { type INodeNavigationData, NavNodeInfoResource, NavNodeManagerService } from '@cloudbeaver/core-navigation-tree';
-import { createResourceOfType, isResourceOfType, ProjectInfoResource, ProjectsService } from '@cloudbeaver/core-projects';
+import { isResourceOfType, ProjectInfoResource, ProjectsService } from '@cloudbeaver/core-projects';
import { NAV_NODE_TYPE_RM_RESOURCE, ResourceManagerResource, RESOURCES_NODE_PATH } from '@cloudbeaver/core-resource-manager';
import { CachedMapAllKey } from '@cloudbeaver/core-sdk';
import { createPath, getPathName } from '@cloudbeaver/core-utils';
import { ACTION_SAVE, ActionService, DATA_CONTEXT_MENU, MenuService } from '@cloudbeaver/core-view';
import { NavigationTabsService } from '@cloudbeaver/plugin-navigation-tabs';
-import { getResourceKeyFromNodeId, NavResourceNodeService, ResourceManagerService } from '@cloudbeaver/plugin-resource-manager';
+import { getResourceKeyFromNodeId, NavResourceNodeService } from '@cloudbeaver/plugin-navigation-tree-rm';
+import { ResourceManagerService } from '@cloudbeaver/plugin-resource-manager';
import { ResourceManagerScriptsService, SaveScriptDialog, SCRIPTS_TYPE_ID } from '@cloudbeaver/plugin-resource-manager-scripts';
import {
DATA_CONTEXT_SQL_EDITOR_STATE,
@@ -123,12 +124,7 @@ export class PluginBootstrap extends Bootstrap {
throw new Error('Project not found');
}
- const resourceType = this.projectInfoResource.getResourceType(project, SCRIPTS_TYPE_ID);
- if (!resourceType) {
- throw new Error('Script Resource type not found');
- }
-
- const scriptName = createResourceOfType(resourceType, result.name.trim());
+ const scriptName = this.projectInfoResource.getNameWithExtension(projectId, SCRIPTS_TYPE_ID, result.name.trim());
const scriptsRootFolder = this.resourceManagerScriptsService.getRootFolder(project);
const folderResourceKey = getResourceKeyFromNodeId(createPath(RESOURCES_NODE_PATH, projectId, scriptsRootFolder));
diff --git a/webapp/packages/plugin-sql-editor-navigation-tab-script/src/ResourceSqlDataSource.ts b/webapp/packages/plugin-sql-editor-navigation-tab-script/src/ResourceSqlDataSource.ts
index 35e980637e..85291d6de2 100644
--- a/webapp/packages/plugin-sql-editor-navigation-tab-script/src/ResourceSqlDataSource.ts
+++ b/webapp/packages/plugin-sql-editor-navigation-tab-script/src/ResourceSqlDataSource.ts
@@ -14,9 +14,11 @@ import {
NOT_INITIALIZED_CONTEXT_ID,
} from '@cloudbeaver/core-connections';
import { TaskScheduler } from '@cloudbeaver/core-executor';
+import type { ProjectInfoResource } from '@cloudbeaver/core-projects';
import { getRmResourceKey, ResourceManagerResource } from '@cloudbeaver/core-resource-manager';
import { isResourceAlias, ResourceKey, ResourceKeyUtils } from '@cloudbeaver/core-sdk';
import { createPath, debounce, getPathName, getPathParent, isArraysEqual, isObjectsEqual, isValuesEqual } from '@cloudbeaver/core-utils';
+import { SCRIPTS_TYPE_ID } from '@cloudbeaver/plugin-resource-manager-scripts';
import { BaseSqlDataSource, ESqlDataSourceFeatures } from '@cloudbeaver/plugin-sql-editor';
import type { IResourceSqlDataSourceState } from './IResourceSqlDataSourceState';
@@ -43,13 +45,13 @@ export class ResourceSqlDataSource extends BaseSqlDataSource {
static key = 'resource';
get name(): string | null {
- if (!this.resourceKey) {
+ if (!this.resourceKey || !this.projectId) {
return null;
}
const resource = this.resourceManagerResource.get(this.resourceKey);
- return resource?.name ?? getPathName(this.resourceKey);
+ return this.projectInfoResource.getNameWithoutExtension(this.projectId, SCRIPTS_TYPE_ID, resource?.name ?? getPathName(this.resourceKey));
}
get script(): string {
@@ -101,6 +103,7 @@ export class ResourceSqlDataSource extends BaseSqlDataSource {
private resourceUseKeyId: string | null;
constructor(
+ private readonly projectInfoResource: ProjectInfoResource,
private readonly connectionInfoResource: ConnectionInfoResource,
private readonly resourceManagerResource: ResourceManagerResource,
state: IResourceSqlDataSourceState,
@@ -269,17 +272,14 @@ export class ResourceSqlDataSource extends BaseSqlDataSource {
await this.write();
await this.scheduler.schedule(undefined, async () => {
- if (!this.actions || !this.resourceKey || !this.saved || !name?.trim()) {
+ if (!this.actions || !this.resourceKey || !this.projectId || !this.saved || !name?.trim()) {
return;
}
- // TODO: use createResourceOfType instead
- if (!name.toLowerCase().endsWith('.sql')) {
- name += '.sql';
- }
+ name = this.projectInfoResource.getNameWithExtension(this.projectId, SCRIPTS_TYPE_ID, name);
this.lastAction = this.rename.bind(this, name);
- this.message = 'Renaming script...';
+ this.message = 'plugin_sql_editor_navigation_tab_script_state_renaming';
try {
this.exception = null;
@@ -299,7 +299,7 @@ export class ResourceSqlDataSource extends BaseSqlDataSource {
}
this.lastAction = this.read.bind(this);
- this.message = 'Reading script...';
+ this.message = 'plugin_sql_editor_navigation_tab_script_state_reading';
try {
this.exception = null;
@@ -334,7 +334,7 @@ export class ResourceSqlDataSource extends BaseSqlDataSource {
}
this.lastAction = this.write.bind(this);
- this.message = 'Saving script...';
+ this.message = 'plugin_sql_editor_navigation_tab_script_state_saving';
try {
this.exception = null;
@@ -360,7 +360,7 @@ export class ResourceSqlDataSource extends BaseSqlDataSource {
}
this.lastAction = this.saveProperties.bind(this);
- this.message = 'Update info...';
+ this.message = 'plugin_sql_editor_navigation_tab_script_state_updating';
try {
this.exception = null;
diff --git a/webapp/packages/plugin-sql-editor-navigation-tab-script/src/ResourceSqlDataSourceBootstrap.ts b/webapp/packages/plugin-sql-editor-navigation-tab-script/src/ResourceSqlDataSourceBootstrap.ts
index 1a6a4448d1..5bc7f65def 100644
--- a/webapp/packages/plugin-sql-editor-navigation-tab-script/src/ResourceSqlDataSourceBootstrap.ts
+++ b/webapp/packages/plugin-sql-editor-navigation-tab-script/src/ResourceSqlDataSourceBootstrap.ts
@@ -18,7 +18,8 @@ import { CachedMapAllKey, resourceKeyList, ResourceKeySimple, ResourceKeyUtils }
import { LocalStorageSaveService } from '@cloudbeaver/core-settings';
import { throttle } from '@cloudbeaver/core-utils';
import { NavigationTabsService } from '@cloudbeaver/plugin-navigation-tabs';
-import { NavResourceNodeService, ResourceManagerService } from '@cloudbeaver/plugin-resource-manager';
+import { NavResourceNodeService } from '@cloudbeaver/plugin-navigation-tree-rm';
+import { ResourceManagerService } from '@cloudbeaver/plugin-resource-manager';
import { ResourceManagerScriptsService } from '@cloudbeaver/plugin-resource-manager-scripts';
import { getSqlEditorName, SqlDataSourceService } from '@cloudbeaver/plugin-sql-editor';
import { SqlEditorTabService } from '@cloudbeaver/plugin-sql-editor-navigation-tab';
@@ -90,7 +91,12 @@ export class ResourceSqlDataSourceBootstrap extends Bootstrap {
this.sqlDataSourceService.register({
key: ResourceSqlDataSource.key,
getDataSource: (editorId, options) => {
- const dataSource = new ResourceSqlDataSource(this.connectionInfoResource, this.resourceManagerResource, this.createState(editorId));
+ const dataSource = new ResourceSqlDataSource(
+ this.projectInfoResource,
+ this.connectionInfoResource,
+ this.resourceManagerResource,
+ this.createState(editorId),
+ );
if (options?.executionContext) {
dataSource.setExecutionContext(options.executionContext);
@@ -204,12 +210,15 @@ export class ResourceSqlDataSourceBootstrap extends Bootstrap {
return;
}
- const dataSource = this.sqlDataSourceService.dataSources
+ const dataSources = this.sqlDataSourceService.dataSources
.filter(([, dataSource]) => dataSource instanceof ResourceSqlDataSource)
- .map(([, dataSource]) => dataSource as ResourceSqlDataSource)
- .find(ds => ds.resourceKey && ds.resourceKey === data.from);
+ .map(([, dataSource]) => dataSource as ResourceSqlDataSource);
- dataSource?.setResourceKey(data.to);
+ for (const dataSource of dataSources) {
+ if (dataSource.resourceKey && dataSource.resourceKey.startsWith(data.from)) {
+ dataSource?.setResourceKey(data.to + dataSource.resourceKey.slice(data.from.length));
+ }
+ }
}
private resourceDeleteHandler(keyObj: ResourceKeySimple) {
@@ -219,14 +228,20 @@ export class ResourceSqlDataSourceBootstrap extends Bootstrap {
const tabs: string[] = [];
+ const dataSources = this.sqlDataSourceService.dataSources
+ .filter(([, dataSource]) => dataSource instanceof ResourceSqlDataSource)
+ .map(([, dataSource]) => dataSource as ResourceSqlDataSource);
+
ResourceKeyUtils.forEach(keyObj, key => {
- const tab = this.sqlEditorTabResourceService.getResourceTab(key);
+ for (const dataSource of dataSources) {
+ if (dataSource.resourceKey && dataSource.resourceKey.startsWith(key)) {
+ const tab = this.sqlEditorTabResourceService.getResourceTab(dataSource.resourceKey);
- if (tab) {
- const dataSource = this.sqlDataSourceService.get(tab.handlerState.editorId) as ResourceSqlDataSource;
-
- dataSource?.setResourceKey(undefined); // prevent deleted resource refresh
- tabs.push(tab.id);
+ if (tab) {
+ tabs.push(tab.id);
+ }
+ dataSource.setResourceKey(undefined); // prevent deleted resource refresh
+ }
}
});
diff --git a/webapp/packages/plugin-sql-editor-navigation-tab-script/src/locales/en.ts b/webapp/packages/plugin-sql-editor-navigation-tab-script/src/locales/en.ts
index 68205e400e..c8f3675c19 100644
--- a/webapp/packages/plugin-sql-editor-navigation-tab-script/src/locales/en.ts
+++ b/webapp/packages/plugin-sql-editor-navigation-tab-script/src/locales/en.ts
@@ -1,4 +1,8 @@
export default [
+ ['plugin_sql_editor_navigation_tab_script_state_renaming', 'Renaming script...'],
+ ['plugin_sql_editor_navigation_tab_script_state_reading', 'Reading script...'],
+ ['plugin_sql_editor_navigation_tab_script_state_saving', 'Saving script...'],
+ ['plugin_sql_editor_navigation_tab_script_state_updating', 'Updating script info...'],
['plugin_sql_editor_navigation_tab_resource_save_script_success', 'Script successfully saved'],
['plugin_sql_editor_navigation_tab_resource_open_script_error', 'Failed to open the script'],
['plugin_sql_editor_navigation_tab_resource_save_script_error', 'Error occurred while trying to save the script'],
diff --git a/webapp/packages/plugin-sql-editor-navigation-tab-script/src/locales/it.ts b/webapp/packages/plugin-sql-editor-navigation-tab-script/src/locales/it.ts
index 68205e400e..c8f3675c19 100644
--- a/webapp/packages/plugin-sql-editor-navigation-tab-script/src/locales/it.ts
+++ b/webapp/packages/plugin-sql-editor-navigation-tab-script/src/locales/it.ts
@@ -1,4 +1,8 @@
export default [
+ ['plugin_sql_editor_navigation_tab_script_state_renaming', 'Renaming script...'],
+ ['plugin_sql_editor_navigation_tab_script_state_reading', 'Reading script...'],
+ ['plugin_sql_editor_navigation_tab_script_state_saving', 'Saving script...'],
+ ['plugin_sql_editor_navigation_tab_script_state_updating', 'Updating script info...'],
['plugin_sql_editor_navigation_tab_resource_save_script_success', 'Script successfully saved'],
['plugin_sql_editor_navigation_tab_resource_open_script_error', 'Failed to open the script'],
['plugin_sql_editor_navigation_tab_resource_save_script_error', 'Error occurred while trying to save the script'],
diff --git a/webapp/packages/plugin-sql-editor-navigation-tab-script/src/locales/ru.ts b/webapp/packages/plugin-sql-editor-navigation-tab-script/src/locales/ru.ts
index a41e210560..d079876823 100644
--- a/webapp/packages/plugin-sql-editor-navigation-tab-script/src/locales/ru.ts
+++ b/webapp/packages/plugin-sql-editor-navigation-tab-script/src/locales/ru.ts
@@ -1,4 +1,8 @@
export default [
+ ['plugin_sql_editor_navigation_tab_script_state_renaming', 'Переименовывание скрипта...'],
+ ['plugin_sql_editor_navigation_tab_script_state_reading', 'Чтение скрипта...'],
+ ['plugin_sql_editor_navigation_tab_script_state_saving', 'Сохранение скрипта...'],
+ ['plugin_sql_editor_navigation_tab_script_state_updating', 'Обновление информации о скрипте...'],
['plugin_sql_editor_navigation_tab_resource_save_script_success', 'Скрипт успешно сохранен'],
['plugin_sql_editor_navigation_tab_resource_open_script_error', 'Не удалось открыть скрипт'],
['plugin_sql_editor_navigation_tab_resource_save_script_error', 'Возникла ошибка при попытки сохранить скрипт'],
diff --git a/webapp/packages/plugin-sql-editor-navigation-tab-script/src/locales/zh.ts b/webapp/packages/plugin-sql-editor-navigation-tab-script/src/locales/zh.ts
index 0980f9c143..a92d3bd1af 100644
--- a/webapp/packages/plugin-sql-editor-navigation-tab-script/src/locales/zh.ts
+++ b/webapp/packages/plugin-sql-editor-navigation-tab-script/src/locales/zh.ts
@@ -1,4 +1,8 @@
export default [
+ ['plugin_sql_editor_navigation_tab_script_state_renaming', 'Renaming script...'],
+ ['plugin_sql_editor_navigation_tab_script_state_reading', 'Reading script...'],
+ ['plugin_sql_editor_navigation_tab_script_state_saving', 'Saving script...'],
+ ['plugin_sql_editor_navigation_tab_script_state_updating', 'Updating script info...'],
['plugin_sql_editor_navigation_tab_resource_save_script_success', '脚本保存成功'],
['plugin_sql_editor_navigation_tab_resource_open_script_error', '打开脚本失败'],
['plugin_sql_editor_navigation_tab_resource_save_script_error', '打开脚本出错'],
diff --git a/webapp/packages/plugin-sql-editor-navigation-tab/src/SqlEditorBootstrap.ts b/webapp/packages/plugin-sql-editor-navigation-tab/src/SqlEditorBootstrap.ts
index b82151540d..81709f8b69 100644
--- a/webapp/packages/plugin-sql-editor-navigation-tab/src/SqlEditorBootstrap.ts
+++ b/webapp/packages/plugin-sql-editor-navigation-tab/src/SqlEditorBootstrap.ts
@@ -144,7 +144,7 @@ export class SqlEditorBootstrap extends Bootstrap {
const result = await this.commonDialogService.open(RenameDialog, {
value: regexp?.[1] ?? name,
objectName: name,
- icon: '/icons/sql_script_m.svg',
+ icon: dataSource.icon,
validation: name =>
!this.sqlEditorTabService.sqlEditorTabs.some(
tab => tab.handlerState.order !== state.order && this.sqlEditorService.getName(tab.handlerState) === name.trim(),
diff --git a/webapp/packages/plugin-sql-editor/src/SqlDataSource/BaseSqlDataSource.ts b/webapp/packages/plugin-sql-editor/src/SqlDataSource/BaseSqlDataSource.ts
index 946ba9600c..1410fc375f 100644
--- a/webapp/packages/plugin-sql-editor/src/SqlDataSource/BaseSqlDataSource.ts
+++ b/webapp/packages/plugin-sql-editor/src/SqlDataSource/BaseSqlDataSource.ts
@@ -42,16 +42,18 @@ export abstract class BaseSqlDataSource implements ISqlDataSource {
return [ESqlDataSourceFeatures.script, ESqlDataSourceFeatures.query, ESqlDataSourceFeatures.executable];
}
- protected saved: boolean;
+ readonly icon: string;
readonly history: ISqlDataSourceHistory;
readonly onUpdate: ISyncExecutor;
readonly onSetScript: ISyncExecutor;
readonly onDatabaseModelUpdate: ISyncExecutor[]>;
+ protected saved: boolean;
protected outdated: boolean;
protected editing: boolean;
- constructor() {
+ constructor(icon = '/icons/sql_script_m.svg') {
+ this.icon = icon;
this.databaseModels = [];
this.exception = undefined;
this.message = undefined;