mirror of
https://github.com/dbeaver/cloudbeaver.git
synced 2026-09-24 16:04:36 +08:00
refactor: CB-1094 metadata editor
This commit is contained in:
+2
-4
@@ -29,11 +29,9 @@ export const NavigationNodeElement = observer<Props>(function NavigationNodeElem
|
||||
for (const renderer of context.tree.renderers) {
|
||||
const CustomRenderer = renderer(nodeId);
|
||||
|
||||
if (CustomRenderer === undefined) {
|
||||
continue;
|
||||
if (CustomRenderer) {
|
||||
return <CustomRenderer nodeId={nodeId} component={NavigationNodeElement} />;
|
||||
}
|
||||
|
||||
return <CustomRenderer nodeId={nodeId} component={NavigationNodeElement} />;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,11 +7,13 @@ export * from './TopNavBar/ConnectionSchemaManager/ConnectionSchemaManagerServic
|
||||
export * from './TopNavBar/TopNavBarService';
|
||||
export * from './TopNavBar/AdministrationTopAppBarBootstrapService';
|
||||
export * from './TopNavBar/shared/topMenuStyles';
|
||||
export * from './shared/NodesManager/ConnectionDialogsService';
|
||||
export * from './shared/NodesManager/extensions/IObjectCatalogProvider';
|
||||
export * from './shared/NodesManager/extensions/IObjectCatalogSetter';
|
||||
export * from './shared/NodesManager/extensions/IObjectSchemaProvider';
|
||||
export * from './shared/NodesManager/extensions/IObjectSchemaSetter';
|
||||
export * from './shared/NodesManager/NavNodeView/IFolderTransform';
|
||||
export * from './shared/NodesManager/NavNodeView/NavNodeViewService';
|
||||
export * from './shared/NodesManager/ConnectionDialogsService';
|
||||
export * from './shared/NodesManager/DBObjectService';
|
||||
export * from './shared/NodesManager/NavNodeInfoResource';
|
||||
export * from './shared/NodesManager/NavNodeManagerService';
|
||||
|
||||
+1
-1
@@ -80,7 +80,7 @@ export const NavigationTabsBar = observer(function NavigationTabsBar() {
|
||||
tabIndex={0}
|
||||
>
|
||||
{navigation.tabIdList.map(tabId => (
|
||||
<TabPanel key={tabId} tabId={tabId}>
|
||||
<TabPanel key={tabId} tabId={tabId} lazy>
|
||||
<TabHandlerPanel tabId={tabId} />
|
||||
</TabPanel>
|
||||
))}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2021 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 { ComponentStyle } from '@cloudbeaver/core-theming';
|
||||
|
||||
export type NavNodeTransformViewComponent = React.FC<{
|
||||
nodeId: string;
|
||||
folderId: string;
|
||||
style?: ComponentStyle;
|
||||
}>;
|
||||
|
||||
export type NavNodeTransformView = (nodeId: string, folderId: string) => NavNodeTransformViewComponent | undefined;
|
||||
|
||||
export type NavNodeFolderTransformFn = (nodeId: string, children: string[] | undefined) => string[] | undefined;
|
||||
|
||||
export interface INavNodeFolderTransform {
|
||||
tab: NavNodeTransformView;
|
||||
panel: NavNodeTransformView;
|
||||
transformer: NavNodeFolderTransformFn;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2021 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 { injectable } from '@cloudbeaver/core-di';
|
||||
|
||||
import { NavTreeResource } from '../NavTreeResource';
|
||||
import type { NavNodeTransformView, INavNodeFolderTransform, NavNodeFolderTransformFn } from './IFolderTransform';
|
||||
|
||||
@injectable()
|
||||
export class NavNodeViewService {
|
||||
get tabs(): NavNodeTransformView[] {
|
||||
return this.transformers.map(transform => transform.tab);
|
||||
}
|
||||
|
||||
get panels(): NavNodeTransformView[] {
|
||||
return this.transformers.map(transform => transform.panel);
|
||||
}
|
||||
|
||||
get transformations(): NavNodeFolderTransformFn[] {
|
||||
return this.transformers.map(transform => transform.transformer);
|
||||
}
|
||||
|
||||
private transformers: INavNodeFolderTransform[];
|
||||
|
||||
constructor(
|
||||
private navTreeResource: NavTreeResource
|
||||
) {
|
||||
this.transformers = [];
|
||||
}
|
||||
|
||||
getFolders(nodeId: string): string[] | undefined {
|
||||
const children = this.navTreeResource.get(nodeId);
|
||||
|
||||
return this.transformations.reduce(
|
||||
(children, transform) => transform(nodeId, children),
|
||||
children
|
||||
);
|
||||
}
|
||||
|
||||
addTransform(transform: INavNodeFolderTransform): void {
|
||||
this.transformers.push(transform);
|
||||
}
|
||||
}
|
||||
@@ -107,9 +107,10 @@ export const TabsState = observer(function TabsState<T = Record<string, any>>({
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (currentTabId !== undefined) {
|
||||
return;
|
||||
}
|
||||
// prevent from initial render handlers execution
|
||||
// if (currentTabId !== undefined) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
openExecutor.execute({
|
||||
tabId: state.selectedId!,
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
NavigationTreeService,
|
||||
NavNodeManagerService,
|
||||
DBObjectService,
|
||||
NavNodeViewService,
|
||||
NavNodeExtensionsService,
|
||||
NavNodeInfoResource,
|
||||
NavTreeResource,
|
||||
@@ -193,6 +194,7 @@ export const coreManifest: PluginManifest = {
|
||||
NavigationTreeContextMenuService,
|
||||
NavigationTreeService,
|
||||
NavNodeManagerService,
|
||||
NavNodeViewService,
|
||||
NavNodeExtensionsService,
|
||||
NavNodeInfoResource,
|
||||
NavTreeResource,
|
||||
|
||||
+1
-1
@@ -8,9 +8,9 @@
|
||||
|
||||
import { injectable } from '@cloudbeaver/core-di';
|
||||
import { TaskScheduler } from '@cloudbeaver/core-executor';
|
||||
import { ResourceKeyUtils } from '@cloudbeaver/core-sdk';
|
||||
import { MetadataMap } from '@cloudbeaver/core-utils';
|
||||
|
||||
import { ResourceKeyUtils } from '../../../core-sdk/src';
|
||||
import { ConnectionExecutionContext } from './ConnectionExecutionContext';
|
||||
import { ConnectionExecutionContextResource } from './ConnectionExecutionContextResource';
|
||||
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2021 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 type { NavNodeTransformViewComponent } from '@cloudbeaver/core-app';
|
||||
import { Tab, TabIcon, TabTitle } from '@cloudbeaver/core-blocks';
|
||||
import { useStyles } from '@cloudbeaver/core-theming';
|
||||
|
||||
export const DDLViewerTab: NavNodeTransformViewComponent = observer(function DDLViewerTab({
|
||||
folderId,
|
||||
style,
|
||||
}) {
|
||||
return styled(useStyles(style))(
|
||||
<Tab tabId={folderId}>
|
||||
<TabIcon icon="/icons/DDL.svg" />
|
||||
<TabTitle>DDL</TabTitle>
|
||||
</Tab>
|
||||
);
|
||||
});
|
||||
+15
-18
@@ -9,7 +9,8 @@
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import styled, { css } from 'reshadow';
|
||||
|
||||
import { Loader } from '@cloudbeaver/core-blocks';
|
||||
import type { NavNodeTransformViewComponent } from '@cloudbeaver/core-app';
|
||||
import { Loader, useTab } from '@cloudbeaver/core-blocks';
|
||||
import { useController } from '@cloudbeaver/core-di';
|
||||
import { SQLCodeEditorLoader } from '@cloudbeaver/plugin-sql-editor';
|
||||
|
||||
@@ -18,22 +19,20 @@ import { DdlViewerController } from './DdlViewerController';
|
||||
const styles = css`
|
||||
wrapper {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
overflow: auto;
|
||||
composes: theme-typography--body1 from global;
|
||||
}
|
||||
SQLCodeEditorLoader {
|
||||
height: 100%;
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
}
|
||||
`;
|
||||
|
||||
export interface DdlViewerTabPanelProps {
|
||||
nodeId: string;
|
||||
}
|
||||
|
||||
export const ddlViewer = (nodeId: string) => (<DdlViewerTabPanel nodeId={nodeId} />);
|
||||
|
||||
const DdlViewerTabPanel = observer<DdlViewerTabPanelProps>(function DdlViewerTabPanel({ nodeId }) {
|
||||
export const DDLViewerTabPanel: NavNodeTransformViewComponent = observer(function DDLViewerTabPanel({ nodeId, folderId }) {
|
||||
const controller = useController(DdlViewerController, nodeId);
|
||||
useTab(folderId, () => controller.load());
|
||||
|
||||
if (controller.isLoading) {
|
||||
return <Loader />;
|
||||
@@ -41,16 +40,14 @@ const DdlViewerTabPanel = observer<DdlViewerTabPanelProps>(function DdlViewerTab
|
||||
|
||||
return styled(styles)(
|
||||
<wrapper>
|
||||
{controller.metadata && (
|
||||
<SQLCodeEditorLoader
|
||||
bindings={{
|
||||
autoCursor: false,
|
||||
}}
|
||||
value={controller.metadata}
|
||||
dialect={controller.dialect}
|
||||
readonly
|
||||
/>
|
||||
)}
|
||||
<SQLCodeEditorLoader
|
||||
bindings={{
|
||||
autoCursor: false,
|
||||
}}
|
||||
value={controller.metadata}
|
||||
dialect={controller.dialect}
|
||||
readonly
|
||||
/>
|
||||
</wrapper>
|
||||
);
|
||||
});
|
||||
@@ -6,7 +6,7 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
import { observable, when, makeObservable } from 'mobx';
|
||||
import { observable, makeObservable } from 'mobx';
|
||||
|
||||
import { NodeManagerUtils, NavNodeManagerService } from '@cloudbeaver/core-app';
|
||||
import { IDestructibleController, IInitializableController, injectable } from '@cloudbeaver/core-di';
|
||||
@@ -37,12 +37,12 @@ export class DdlViewerController implements IInitializableController, IDestructi
|
||||
});
|
||||
}
|
||||
|
||||
init(nodeId: string) {
|
||||
init(nodeId: string): void {
|
||||
this.nodeId = nodeId;
|
||||
when(
|
||||
() => !!this.ddlViewerService.getMetadata(nodeId),
|
||||
() => this.showMetadata(nodeId)
|
||||
);
|
||||
}
|
||||
|
||||
async load(): Promise<void> {
|
||||
await this.showMetadata(this.nodeId);
|
||||
}
|
||||
|
||||
destruct(): void {
|
||||
@@ -51,7 +51,7 @@ export class DdlViewerController implements IInitializableController, IDestructi
|
||||
|
||||
private async showMetadata(nodeId: string): Promise<void> {
|
||||
try {
|
||||
this.metadata = await this.ddlViewerService.getMetadata(nodeId)!;
|
||||
this.metadata = await this.ddlViewerService.loadDdlMetadata(nodeId)!;
|
||||
await this.loadDialect(nodeId);
|
||||
} catch (error) {
|
||||
this.notificationService.logException(error, 'Failed to load DDL');
|
||||
|
||||
@@ -6,26 +6,47 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
import { EObjectFeature, NavNodeInfoResource, NavNodeViewService } from '@cloudbeaver/core-app';
|
||||
import { Bootstrap, injectable } from '@cloudbeaver/core-di';
|
||||
import { ITabBuilder, ObjectFoldersService } from '@cloudbeaver/plugin-object-viewer';
|
||||
|
||||
import { DdlViewerTabService } from './DdlViewerTabService';
|
||||
import { DDLViewerTab } from './DdlViewer/DDLViewerTab';
|
||||
import { DDLViewerTabPanel } from './DdlViewer/DDLViewerTabPanel';
|
||||
|
||||
const navNodeDDLId = 'object-viewer://ddl';
|
||||
|
||||
@injectable()
|
||||
export class DdlViewerBootstrap extends Bootstrap {
|
||||
constructor(
|
||||
private objectFoldersService: ObjectFoldersService,
|
||||
private ddlViewerTabService: DdlViewerTabService
|
||||
private readonly navNodeViewService: NavNodeViewService,
|
||||
private readonly navNodeInfoResource: NavNodeInfoResource
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
register(): void {
|
||||
const tabBuilder: ITabBuilder = {
|
||||
build: nodeId => this.ddlViewerTabService.buildTab(nodeId),
|
||||
};
|
||||
this.navNodeViewService.addTransform({
|
||||
tab: (nodeId, folderId) => {
|
||||
if (folderId.startsWith(navNodeDDLId)) {
|
||||
return DDLViewerTab;
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
panel: (nodeId, folderId) => {
|
||||
if (folderId.startsWith(navNodeDDLId)) {
|
||||
return DDLViewerTabPanel;
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
transformer: (nodeId, children) => {
|
||||
const node = this.navNodeInfoResource.get(nodeId);
|
||||
|
||||
this.objectFoldersService.registerTabConstructor(tabBuilder);
|
||||
if (!node?.objectFeatures?.includes(EObjectFeature.script)) {
|
||||
return children;
|
||||
}
|
||||
|
||||
return [...children || [], navNodeDDLId];
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
load(): void {}
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2021 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 {
|
||||
EObjectFeature, NavNodeManagerService, INodeNavigationData
|
||||
} from '@cloudbeaver/core-app';
|
||||
import type { ITab } from '@cloudbeaver/core-blocks';
|
||||
import { injectable } from '@cloudbeaver/core-di';
|
||||
import { NotificationService } from '@cloudbeaver/core-events';
|
||||
import type { IExecutionContextProvider } from '@cloudbeaver/core-executor';
|
||||
import { ObjectViewerTabService } from '@cloudbeaver/plugin-object-viewer';
|
||||
|
||||
import { ddlViewer } from './DdlViewer/DdlViewer';
|
||||
import { DdlViewerService } from './DdlViewerService';
|
||||
|
||||
const ddlViewerTabId = 'ddl-viewer';
|
||||
|
||||
@injectable()
|
||||
export class DdlViewerTabService {
|
||||
constructor(
|
||||
private navNodeManagerService: NavNodeManagerService,
|
||||
private objectViewerTabService: ObjectViewerTabService,
|
||||
private notificationService: NotificationService,
|
||||
private ddlViewerService: DdlViewerService
|
||||
) {
|
||||
this.navNodeManagerService.navigator.addHandler(this.navigationHandler.bind(this));
|
||||
}
|
||||
|
||||
buildTab(nodeId: string): ITab | null {
|
||||
const node = this.navNodeManagerService.getNode(nodeId);
|
||||
const isDdlRequired = node?.objectFeatures.includes(EObjectFeature.script);
|
||||
if (!isDdlRequired) {
|
||||
return null;
|
||||
}
|
||||
const ddlTab: ITab = {
|
||||
tabId: ddlViewerTabId,
|
||||
title: 'DDL',
|
||||
icon: '/icons/DDL.svg',
|
||||
onActivate: () => this.activateDDLTab(nodeId, node?.parentId as string),
|
||||
panel: () => ddlViewer(nodeId),
|
||||
};
|
||||
return ddlTab;
|
||||
}
|
||||
|
||||
private async navigationHandler(data: INodeNavigationData, contexts: IExecutionContextProvider<INodeNavigationData>) {
|
||||
try {
|
||||
const tabContext = await contexts.getContext(this.objectViewerTabService.objectViewerTabContext);
|
||||
|
||||
if (tabContext.nodeInfo.folderId === ddlViewerTabId) {
|
||||
this.ddlViewerService.loadDdlMetadata(tabContext.nodeInfo.nodeId);
|
||||
}
|
||||
} catch (exception) {
|
||||
this.notificationService.logException(exception, 'Object Viewer Error', 'Error in Object Viewer while processing action with ddl-viewer');
|
||||
}
|
||||
}
|
||||
|
||||
private activateDDLTab(nodeId: string, parentId: string) {
|
||||
this.navNodeManagerService.navToNode(nodeId, parentId, ddlViewerTabId);
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,6 @@ import type { PluginManifest } from '@cloudbeaver/core-di';
|
||||
|
||||
import { DdlViewerBootstrap } from './DdlViewerBootstrap';
|
||||
import { DdlViewerService } from './DdlViewerService';
|
||||
import { DdlViewerTabService } from './DdlViewerTabService';
|
||||
|
||||
export const manifest: PluginManifest = {
|
||||
info: {
|
||||
@@ -19,7 +18,6 @@ export const manifest: PluginManifest = {
|
||||
|
||||
providers: [
|
||||
DdlViewerBootstrap,
|
||||
DdlViewerTabService,
|
||||
DdlViewerService,
|
||||
],
|
||||
};
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2021 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 { NavNodeViewService, useNode } from '@cloudbeaver/core-app';
|
||||
import { useService } from '@cloudbeaver/core-di';
|
||||
import type { ComponentStyle } from '@cloudbeaver/core-theming';
|
||||
|
||||
import { ObjectPropertyTable } from './ObjectPropertyTable/ObjectPropertyTable';
|
||||
|
||||
interface IFolderPanelRendererProps {
|
||||
nodeId: string;
|
||||
folderId: string;
|
||||
style?: ComponentStyle;
|
||||
}
|
||||
|
||||
export const FolderPanelRenderer = observer<IFolderPanelRendererProps>(function FolderPanelRenderer({
|
||||
nodeId,
|
||||
folderId,
|
||||
style,
|
||||
}) {
|
||||
const navNodeViewService = useService(NavNodeViewService);
|
||||
|
||||
for (const panel of navNodeViewService.panels) {
|
||||
const Panel = panel(nodeId, folderId);
|
||||
|
||||
if (Panel) {
|
||||
return <Panel nodeId={nodeId} folderId={folderId} style={style} />;
|
||||
}
|
||||
}
|
||||
|
||||
return <NavNodePanel nodeId={folderId} style={style} />;
|
||||
});
|
||||
|
||||
interface INavNodePanelProps {
|
||||
nodeId: string;
|
||||
style?: ComponentStyle;
|
||||
}
|
||||
|
||||
const NavNodePanel = observer<INavNodePanelProps>(function NavNodeTab({ nodeId }) {
|
||||
const nodeInfo = useNode(nodeId);
|
||||
|
||||
if (!nodeInfo.node) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <ObjectPropertyTable objectId={nodeId} parentId={nodeInfo.node.parentId} />;
|
||||
});
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2021 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 { NavNodeViewService, useNode } from '@cloudbeaver/core-app';
|
||||
import { Tab, TabIcon, TabTitle } from '@cloudbeaver/core-blocks';
|
||||
import { useService } from '@cloudbeaver/core-di';
|
||||
import { ComponentStyle, useStyles } from '@cloudbeaver/core-theming';
|
||||
|
||||
interface IFolderTabRendererProps {
|
||||
nodeId: string;
|
||||
folderId: string;
|
||||
style?: ComponentStyle;
|
||||
}
|
||||
|
||||
export const FolderTabRenderer = observer<IFolderTabRendererProps>(function FolderTabRenderer({
|
||||
nodeId,
|
||||
folderId,
|
||||
style,
|
||||
}) {
|
||||
const navNodeViewService = useService(NavNodeViewService);
|
||||
|
||||
for (const tab of navNodeViewService.tabs) {
|
||||
const Tab = tab(nodeId, folderId);
|
||||
|
||||
if (Tab) {
|
||||
return <Tab nodeId={nodeId} folderId={folderId} style={style} />;
|
||||
}
|
||||
}
|
||||
|
||||
return <NavNodeTab nodeId={folderId} style={style} />;
|
||||
});
|
||||
|
||||
interface INavNodeTabProps {
|
||||
nodeId: string;
|
||||
style?: ComponentStyle;
|
||||
}
|
||||
|
||||
const NavNodeTab = observer<INavNodeTabProps>(function NavNodeTab({ nodeId, style }) {
|
||||
const nodeInfo = useNode(nodeId);
|
||||
|
||||
return styled(useStyles(style))(
|
||||
<Tab tabId={nodeId}>
|
||||
{nodeInfo.node?.icon && <TabIcon icon={nodeInfo.node.icon} />}
|
||||
<TabTitle>{nodeInfo.node?.name}</TabTitle>
|
||||
</Tab>
|
||||
);
|
||||
});
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2021 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 { NavNodeTransformViewComponent } from '@cloudbeaver/core-app';
|
||||
|
||||
import { ObjectProperties } from './ObjectProperties';
|
||||
|
||||
export const NavNodeMetadataPanel: NavNodeTransformViewComponent = function NavNodeMetadataPanel({
|
||||
nodeId,
|
||||
}) {
|
||||
return <ObjectProperties objectId={nodeId} />;
|
||||
};
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2021 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 { NavNodeTransformViewComponent, useNode } from '@cloudbeaver/core-app';
|
||||
import { Tab, TabIcon, TabTitle } from '@cloudbeaver/core-blocks';
|
||||
import { useTranslate } from '@cloudbeaver/core-localization';
|
||||
import { useStyles } from '@cloudbeaver/core-theming';
|
||||
|
||||
export const NavNodeMetadataTab: NavNodeTransformViewComponent = observer(function NavNodeMetadataTab({
|
||||
folderId,
|
||||
nodeId,
|
||||
style,
|
||||
}) {
|
||||
const translate = useTranslate();
|
||||
const styles = useStyles(style);
|
||||
const nodeInfo = useNode(nodeId);
|
||||
|
||||
if (!nodeInfo.node) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const icon = 'platform:/plugin/org.jkiss.dbeaver.model/icons/tree/info.png';
|
||||
|
||||
return styled(styles)(
|
||||
<Tab tabId={folderId}>
|
||||
<TabIcon icon={icon} />
|
||||
<TabTitle>{translate('plugin_object_viewer_object_info_tab')}</TabTitle>
|
||||
</Tab>
|
||||
);
|
||||
});
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2021 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 { ENodeFeature, NavNodeInfoResource, NavNodeViewService } from '@cloudbeaver/core-app';
|
||||
import { Bootstrap, injectable } from '@cloudbeaver/core-di';
|
||||
|
||||
import { NavNodeMetadataPanel } from './NavNodeMetadataPanel';
|
||||
import { NavNodeMetadataTab } from './NavNodeMetadataTab';
|
||||
|
||||
const navNodeMetadataId = 'object-viewer://metadata';
|
||||
|
||||
@injectable()
|
||||
export class NavNodeMetadataViewBootstrap extends Bootstrap {
|
||||
constructor(
|
||||
private readonly navNodeViewService: NavNodeViewService,
|
||||
private readonly navNodeInfoResource: NavNodeInfoResource
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
register(): void {
|
||||
this.navNodeViewService.addTransform({
|
||||
tab: (nodeId, folderId) => {
|
||||
if (folderId.startsWith(navNodeMetadataId)) {
|
||||
return NavNodeMetadataTab;
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
panel: (nodeId, folderId) => {
|
||||
if (folderId.startsWith(navNodeMetadataId)) {
|
||||
return NavNodeMetadataPanel;
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
transformer: (nodeId, children) => {
|
||||
const node = this.navNodeInfoResource.get(nodeId);
|
||||
const isDatabaseObject = (
|
||||
node?.features?.includes(ENodeFeature.item)
|
||||
|| node?.features?.includes(ENodeFeature.container)
|
||||
);
|
||||
|
||||
if (!isDatabaseObject) {
|
||||
return children;
|
||||
}
|
||||
|
||||
return [navNodeMetadataId, ...children || []];
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
load(): void { }
|
||||
}
|
||||
+2
-2
@@ -10,8 +10,8 @@ import { observer } from 'mobx-react-lite';
|
||||
|
||||
import { FieldCheckboxNew, InputFieldNew } from '@cloudbeaver/core-blocks';
|
||||
|
||||
import type { ObjectPropertyProps } from '../formControls';
|
||||
import { additionalProps, filterProperty, getValue, matchType } from '../helpers';
|
||||
import type { ObjectPropertyProps } from '../../formControls';
|
||||
import { additionalProps, filterProperty, getValue, matchType } from '../../helpers';
|
||||
|
||||
export const ObjectProperty = observer<ObjectPropertyProps>(function ObjectProperty({
|
||||
objectProperty,
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2021 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 styled, { css } from 'reshadow';
|
||||
|
||||
import { NavNodeInfoResource, NavNodeTransformViewComponent, useChildren } from '@cloudbeaver/core-app';
|
||||
import { useService } from '@cloudbeaver/core-di';
|
||||
import { resourceKeyList } from '@cloudbeaver/core-sdk';
|
||||
|
||||
import { ObjectChildrenPropertyTable } from '../../ObjectPropertyTable/ObjectChildrenPropertyTable';
|
||||
import { VirtualFolderUtils } from './VirtualFolderUtils';
|
||||
|
||||
const style = css`
|
||||
tab-wrapper {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
flex: 1 0 auto;
|
||||
}
|
||||
`;
|
||||
|
||||
export const VirtualFolderPanel: NavNodeTransformViewComponent = function VirtualFolderPanel({
|
||||
folderId,
|
||||
nodeId,
|
||||
}) {
|
||||
const nodeType = VirtualFolderUtils.getNodeType(folderId);
|
||||
const navNodeInfoResource = useService(NavNodeInfoResource);
|
||||
const children = useChildren(nodeId);
|
||||
|
||||
const nodeIds = navNodeInfoResource
|
||||
.get(resourceKeyList(children.children || []))
|
||||
.filter(node => node?.nodeType === nodeType)
|
||||
.map(node => node!.id);
|
||||
|
||||
return styled(style)(
|
||||
<tab-wrapper>
|
||||
<ObjectChildrenPropertyTable nodeIds={nodeIds} />
|
||||
</tab-wrapper>
|
||||
);
|
||||
};
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2021 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 styled from 'reshadow';
|
||||
|
||||
import type { NavNodeTransformViewComponent } from '@cloudbeaver/core-app';
|
||||
import { Tab, TabIcon, TabTitle } from '@cloudbeaver/core-blocks';
|
||||
import { useStyles } from '@cloudbeaver/core-theming';
|
||||
|
||||
import { VirtualFolderUtils } from './VirtualFolderUtils';
|
||||
|
||||
export const VirtualFolderTab: NavNodeTransformViewComponent = function VirtualFolderTab({
|
||||
folderId,
|
||||
style,
|
||||
}) {
|
||||
const nodeType = VirtualFolderUtils.getNodeType(folderId);
|
||||
const styles = useStyles(style);
|
||||
const icon = 'platform:/plugin/org.jkiss.dbeaver.model/icons/tree/folder.png';
|
||||
|
||||
return styled(styles)(
|
||||
<Tab tabId={folderId}>
|
||||
<TabIcon icon={icon} />
|
||||
<TabTitle>{nodeType}</TabTitle>
|
||||
</Tab>
|
||||
);
|
||||
};
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2021 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 VirtualFolderUtils = {
|
||||
prefix: 'object-viewer://virtual-folder/',
|
||||
|
||||
getFolderId(nodeType: string): string {
|
||||
return this.prefix + nodeType;
|
||||
},
|
||||
|
||||
getNodeType(folderId: string): string {
|
||||
return folderId.replace(this.prefix, '');
|
||||
},
|
||||
|
||||
isVirtualFolder(folderId: string): boolean {
|
||||
return folderId.startsWith(this.prefix);
|
||||
},
|
||||
};
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2021 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 { NavNodeInfoResource, NavNodeViewService } from '@cloudbeaver/core-app';
|
||||
import { Bootstrap, injectable } from '@cloudbeaver/core-di';
|
||||
|
||||
import { VirtualFolderPanel } from './VirtualFolderPanel';
|
||||
import { VirtualFolderTab } from './VirtualFolderTab';
|
||||
import { VirtualFolderUtils } from './VirtualFolderUtils';
|
||||
|
||||
@injectable()
|
||||
export class VirtualFolderViewBootstrap extends Bootstrap {
|
||||
constructor(
|
||||
private readonly navNodeViewService: NavNodeViewService,
|
||||
private readonly navNodeInfoResource: NavNodeInfoResource
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
register(): void {
|
||||
this.navNodeViewService.addTransform({
|
||||
tab: (nodeId, folderId) => {
|
||||
if (VirtualFolderUtils.isVirtualFolder(folderId)) {
|
||||
return VirtualFolderTab;
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
panel: (nodeId, folderId) => {
|
||||
if (VirtualFolderUtils.isVirtualFolder(folderId)) {
|
||||
return VirtualFolderPanel;
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
transformer: (nodeId, children) => {
|
||||
if (!children) {
|
||||
return children;
|
||||
}
|
||||
|
||||
const virtualFolders: string[] = [];
|
||||
const nextChildren = [];
|
||||
|
||||
for (const child of children) {
|
||||
const node = this.navNodeInfoResource.get(child);
|
||||
|
||||
if (!node || node?.folder) {
|
||||
nextChildren.push(child);
|
||||
} else if (node?.nodeType) {
|
||||
const virtualId = VirtualFolderUtils.getFolderId(node.nodeType);
|
||||
|
||||
if (!virtualFolders.includes(virtualId)) {
|
||||
virtualFolders.push(virtualId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nextChildren.push(...virtualFolders);
|
||||
|
||||
return nextChildren;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
load(): void { }
|
||||
}
|
||||
@@ -7,15 +7,17 @@
|
||||
*/
|
||||
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { useEffect } from 'react';
|
||||
import styled, { css } from 'reshadow';
|
||||
|
||||
import type { ITab } from '@cloudbeaver/core-app';
|
||||
import { Tab, TabIcon, TabList, TabPanel, TabsState, TabTitle, verticalTabStyles } from '@cloudbeaver/core-blocks';
|
||||
import { useController } from '@cloudbeaver/core-di';
|
||||
import { ITab, NavNodeManagerService, NavNodeViewService, NavTreeResource } from '@cloudbeaver/core-app';
|
||||
import { ITabData, TabList, TabPanel, TabsState, useMapResource, verticalTabStyles } from '@cloudbeaver/core-blocks';
|
||||
import { useService } from '@cloudbeaver/core-di';
|
||||
import { useStyles, composes } from '@cloudbeaver/core-theming';
|
||||
|
||||
import type { IObjectViewerTabState } from '../IObjectViewerTabState';
|
||||
import { ObjectFoldersController } from './ObjectFoldersController';
|
||||
import { FolderPanelRenderer } from './FolderPanelRenderer';
|
||||
import { FolderTabRenderer } from './FolderTabRenderer';
|
||||
|
||||
const styles = composes(
|
||||
css`
|
||||
@@ -46,34 +48,55 @@ const styles = composes(
|
||||
`
|
||||
);
|
||||
|
||||
const tabStyles = [verticalTabStyles, styles];
|
||||
|
||||
interface IProps {
|
||||
tab: ITab<IObjectViewerTabState>;
|
||||
}
|
||||
|
||||
export const ObjectFolders = observer<IProps>(function ObjectFolders({ tab }) {
|
||||
const controller = useController(ObjectFoldersController, tab);
|
||||
const navNodeManagerService = useService(NavNodeManagerService);
|
||||
const navNodeViewService = useService(NavNodeViewService);
|
||||
|
||||
const tabContainer = controller.getTabContainer();
|
||||
const nodeId = tab.handlerState.objectId;
|
||||
const parentId = tab.handlerState.parentId;
|
||||
let folderId = tab.handlerState.folderId;
|
||||
|
||||
useMapResource(NavTreeResource, nodeId, {
|
||||
onLoad: async resource => {
|
||||
for (const nodeId of tab.handlerState.parents) {
|
||||
await resource.load(nodeId);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const folders = navNodeViewService.getFolders(nodeId) || [];
|
||||
|
||||
function openFolder(tabData: ITabData) {
|
||||
navNodeManagerService.navToNode(nodeId, parentId, tabData.tabId);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!folders.includes(folderId) && folders.length > 0) {
|
||||
navNodeManagerService.navToNode(nodeId, parentId, folders[0]);
|
||||
}
|
||||
});
|
||||
|
||||
if (!folders.includes(folderId) && folders.length > 0) {
|
||||
folderId = folders[0];
|
||||
}
|
||||
|
||||
return styled(useStyles(verticalTabStyles, styles))(
|
||||
<TabsState currentTabId={tabContainer.currentTabId} orientation='vertical'>
|
||||
<TabsState currentTabId={folderId} orientation='vertical' onChange={openFolder}>
|
||||
<vertical-tabs>
|
||||
<TabList aria-label="Object folders">
|
||||
{tabContainer.tabs.map(tab => (
|
||||
<Tab
|
||||
key={tab.tabId}
|
||||
tabId={tab.tabId}
|
||||
onOpen={tab.onActivate}
|
||||
onClose={tab.onClose}
|
||||
>
|
||||
{tab.icon && <TabIcon icon={tab.icon} />}
|
||||
<TabTitle>{tab.title}</TabTitle>
|
||||
</Tab>
|
||||
{folders.map(folderId => (
|
||||
<FolderTabRenderer key={folderId} nodeId={nodeId} folderId={folderId} style={tabStyles} />
|
||||
))}
|
||||
</TabList>
|
||||
{tabContainer.tabs.map(tab => (
|
||||
<TabPanel key={tab.tabId} tabId={tab.tabId}>
|
||||
<tab.panel />
|
||||
{folders.map(folderId => (
|
||||
<TabPanel key={folderId} tabId={folderId}>
|
||||
<FolderPanelRenderer key={folderId} nodeId={nodeId} folderId={folderId} style={tabStyles} />
|
||||
</TabPanel>
|
||||
))}
|
||||
</vertical-tabs>
|
||||
|
||||
-64
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2021 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 { autorun } from 'mobx';
|
||||
|
||||
import { ITab, NavigationTabsService } from '@cloudbeaver/core-app';
|
||||
import { IDestructibleController, IInitializableController, injectable } from '@cloudbeaver/core-di';
|
||||
|
||||
import type { IObjectViewerTabState } from '../IObjectViewerTabState';
|
||||
import { ObjectFoldersService } from './ObjectFoldersService';
|
||||
import type { ObjectFoldersTabContainer } from './ObjectFoldersTabsContainer/ObjectFoldersTabContainer';
|
||||
|
||||
@injectable()
|
||||
export class ObjectFoldersController implements IInitializableController, IDestructibleController {
|
||||
private tabContainer!: ObjectFoldersTabContainer;
|
||||
private navigationTab!: ITab<IObjectViewerTabState>;
|
||||
private disposer!: ReturnType<typeof autorun>;
|
||||
|
||||
constructor(
|
||||
private objectFoldersService: ObjectFoldersService,
|
||||
private navigationTabsService: NavigationTabsService
|
||||
) { }
|
||||
|
||||
getTabContainer(): ObjectFoldersTabContainer {
|
||||
return this.tabContainer;
|
||||
}
|
||||
|
||||
init(tab: ITab<IObjectViewerTabState>): void {
|
||||
this.navigationTab = tab;
|
||||
|
||||
this.tabContainer = this.objectFoldersService.createTabsContainer(tab.handlerState.objectId);
|
||||
|
||||
this.disposer = autorun(() => {
|
||||
const currentFolderId = tab.handlerState.folderId || null;
|
||||
if (this.navigationTabsService.currentTabId === tab.id) {
|
||||
this.activateTab(currentFolderId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private activateTab(folderId: string | null): void {
|
||||
try {
|
||||
this.tabContainer.activateTab(folderId);
|
||||
} catch {
|
||||
// no tab with tabId
|
||||
this.activateFirstTab();
|
||||
}
|
||||
}
|
||||
|
||||
private activateFirstTab() {
|
||||
const firstTab = this.tabContainer.tabs[0];
|
||||
this.tabContainer.activateTab(firstTab?.tabId || null);
|
||||
}
|
||||
|
||||
destruct(): void {
|
||||
this.disposer();
|
||||
this.objectFoldersService.destroyTabContainer(this.navigationTab.handlerState.objectId);
|
||||
}
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2021 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 { action, makeObservable } from 'mobx';
|
||||
|
||||
import { TabEntity } from '@cloudbeaver/core-app';
|
||||
import type { ITab } from '@cloudbeaver/core-blocks';
|
||||
import { Entity, injectable, RootContainerService } from '@cloudbeaver/core-di';
|
||||
|
||||
import { ObjectFoldersTabService } from './ObjectFoldersTab/ObjectFoldersTabService';
|
||||
import { ObjectFoldersTabContainer } from './ObjectFoldersTabsContainer/ObjectFoldersTabContainer';
|
||||
import { ObjectInfoTabService } from './ObjectInfoTab/ObjectInfoTabService';
|
||||
import { VirtualFolderTabService } from './VirtualFolderTab/VirtualFolderTabService';
|
||||
|
||||
export interface ITabBuilder {
|
||||
build: (objectId: string) => ITab | ITab[] | null;
|
||||
}
|
||||
|
||||
@injectable()
|
||||
export class ObjectFoldersService {
|
||||
private tabBuilders: ITabBuilder[] = [];
|
||||
|
||||
// todo temporary solution while only ObjectFolders supports entities
|
||||
// later it will be integrated in common lifecycle of nested entities
|
||||
private tabContainersStorage = new Entity();
|
||||
|
||||
constructor(
|
||||
private objectInfoTabService: ObjectInfoTabService,
|
||||
private virtualFolderTabService: VirtualFolderTabService,
|
||||
private objectFoldersTabService: ObjectFoldersTabService,
|
||||
private rootContainerService: RootContainerService
|
||||
) {
|
||||
makeObservable(this, {
|
||||
createTabsContainer: action,
|
||||
});
|
||||
|
||||
this.rootContainerService.registerEntityInRootContainer(this.tabContainersStorage);
|
||||
}
|
||||
|
||||
createTabsContainer(nodeId: string): ObjectFoldersTabContainer {
|
||||
const tabContainer = new ObjectFoldersTabContainer(nodeId);
|
||||
this.tabContainersStorage.addChild(tabContainer);
|
||||
|
||||
const infoTab = this.objectInfoTabService.createTabEntity(nodeId);
|
||||
if (infoTab) {
|
||||
tabContainer.addTabEntity(infoTab);
|
||||
}
|
||||
|
||||
this.objectFoldersTabService.createTabEntities(nodeId)
|
||||
.forEach(tabEntity => tabContainer.addTabEntity(tabEntity));
|
||||
|
||||
this.virtualFolderTabService.createTabEntities(nodeId)
|
||||
.forEach(tabEntity => tabContainer.addTabEntity(tabEntity));
|
||||
|
||||
const tabs = this.getTabs(nodeId);
|
||||
tabs.forEach(tab => {
|
||||
const tabEntity = new TabEntity(tab);
|
||||
tabContainer.addTabEntity(tabEntity);
|
||||
});
|
||||
return tabContainer;
|
||||
}
|
||||
|
||||
destroyTabContainer(nodeId: string) {
|
||||
this.tabContainersStorage.removeChild(nodeId);
|
||||
}
|
||||
|
||||
/**
|
||||
* return tabs that should be created once for node id
|
||||
* @param nodeId
|
||||
*/
|
||||
getTabs(nodeId: string): ITab[] {
|
||||
const tabList: ITab[] = [];
|
||||
this.tabBuilders.forEach(builder => {
|
||||
const tab = builder.build(nodeId);
|
||||
if (tab) {
|
||||
const tabs = Array.isArray(tab) ? tab : [tab];
|
||||
tabList.push(...tabs);
|
||||
}
|
||||
});
|
||||
return tabList;
|
||||
}
|
||||
|
||||
registerTabConstructor(tabBuilder: ITabBuilder) {
|
||||
this.tabBuilders.push(tabBuilder);
|
||||
}
|
||||
}
|
||||
-34
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2021 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 { ReactElement } from 'react';
|
||||
|
||||
import type { NavNode } from '@cloudbeaver/core-app';
|
||||
import type { ITab } from '@cloudbeaver/core-blocks';
|
||||
|
||||
import { objectPropertyTablePanel } from '../ObjectPropertyTable/ObjectPropertyTable';
|
||||
|
||||
export class ObjectFolderTabModel implements ITab {
|
||||
readonly tabId: string;
|
||||
|
||||
get title(): string {
|
||||
return this.node.name || '';
|
||||
}
|
||||
|
||||
get icon(): string | undefined {
|
||||
return this.node.icon;
|
||||
}
|
||||
|
||||
panel: () => ReactElement | null;
|
||||
|
||||
constructor(private node: NavNode,
|
||||
public onActivate: () => void) {
|
||||
this.tabId = node.id;
|
||||
this.panel = () => objectPropertyTablePanel(node.parentId, node.id);
|
||||
}
|
||||
}
|
||||
-45
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2021 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 { NavNodeManagerService, TabEntity } from '@cloudbeaver/core-app';
|
||||
import type { ITab } from '@cloudbeaver/core-blocks';
|
||||
import { injectable } from '@cloudbeaver/core-di';
|
||||
|
||||
import { ObjectFolderTabModel } from './ObjectFolderTabModel';
|
||||
|
||||
@injectable()
|
||||
export class ObjectFoldersTabService {
|
||||
constructor(private navNodeManagerService: NavNodeManagerService) {
|
||||
}
|
||||
|
||||
createTabEntities(nodeId: string): TabEntity[] {
|
||||
return this.tabBuilder(nodeId).map(tab => new TabEntity(tab));
|
||||
}
|
||||
|
||||
private tabBuilder(nodeId: string): ITab[] {
|
||||
const children = this.navNodeManagerService.getTree(nodeId) || [];
|
||||
|
||||
const folderTabs = children.filter(nodeId => {
|
||||
const node = this.navNodeManagerService.getNode(nodeId);
|
||||
return node ? node.folder : false;
|
||||
});
|
||||
|
||||
const tabList: ITab[] = folderTabs.map(nodeId => {
|
||||
const node = this.navNodeManagerService.getNode(nodeId);
|
||||
if (!node) {
|
||||
throw Error(`node not found: ${nodeId}`);
|
||||
}
|
||||
|
||||
return new ObjectFolderTabModel(
|
||||
node,
|
||||
() => this.navNodeManagerService.navToNode(node.id, node.parentId)
|
||||
);
|
||||
});
|
||||
return tabList;
|
||||
}
|
||||
}
|
||||
-13
@@ -1,13 +0,0 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2021 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 class NodePropertiesMixin {
|
||||
constructor(
|
||||
public readonly nodeId: string
|
||||
) {}
|
||||
}
|
||||
-19
@@ -1,19 +0,0 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2021 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 { TabContainerEntity } from '@cloudbeaver/core-app';
|
||||
|
||||
import { NodePropertiesMixin } from './NodePropertiesMixin';
|
||||
|
||||
export class ObjectFoldersTabContainer extends TabContainerEntity {
|
||||
constructor(nodeId: string) {
|
||||
super([], nodeId);
|
||||
const nodePropertiesMixin = new NodePropertiesMixin(nodeId);
|
||||
this.addMixin(NodePropertiesMixin, nodePropertiesMixin);
|
||||
}
|
||||
}
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2021 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 { useService } from '@cloudbeaver/core-di';
|
||||
|
||||
import { NodePropertiesMixin } from '../ObjectFoldersTabsContainer/NodePropertiesMixin';
|
||||
import { ObjectProperties } from './ObjectProperties';
|
||||
|
||||
export const ObjectInfoTab = observer(function ObjectInfoTab() {
|
||||
const nodePropertiesMixin = useService(NodePropertiesMixin);
|
||||
return (
|
||||
<ObjectProperties objectId={nodePropertiesMixin.nodeId} />
|
||||
);
|
||||
});
|
||||
-23
@@ -1,23 +0,0 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2021 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 { ITab } from '@cloudbeaver/core-blocks';
|
||||
import { injectable } from '@cloudbeaver/core-di';
|
||||
|
||||
import { ObjectInfoTab } from './ObjectInfoTab';
|
||||
|
||||
@injectable()
|
||||
export class ObjectInfoTabModel implements ITab {
|
||||
readonly tabId = 'infoTab';
|
||||
title = 'Info';
|
||||
icon = 'platform:/plugin/org.jkiss.dbeaver.model/icons/tree/info.png';
|
||||
|
||||
panel = ObjectInfoTab;
|
||||
|
||||
constructor(public onActivate: () => void) { }
|
||||
}
|
||||
-37
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2021 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 { TabEntity, NavNodeManagerService, ENodeFeature } from '@cloudbeaver/core-app';
|
||||
import { injectable } from '@cloudbeaver/core-di';
|
||||
|
||||
import { ObjectInfoTabModel } from './ObjectInfoTabModel';
|
||||
|
||||
@injectable()
|
||||
export class ObjectInfoTabService {
|
||||
constructor(
|
||||
private navNodeManagerService: NavNodeManagerService
|
||||
) {}
|
||||
|
||||
createTabEntity(navNodeId: string): TabEntity | null {
|
||||
const node = this.navNodeManagerService.getNode(navNodeId);
|
||||
const isDatabaseObject = node?.features?.includes(ENodeFeature.item)
|
||||
|| node?.features?.includes(ENodeFeature.container);
|
||||
if (!isDatabaseObject) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new TabEntity(new ObjectInfoTabModel(
|
||||
() => {
|
||||
const node = this.navNodeManagerService.getNode(navNodeId);
|
||||
if (node) {
|
||||
this.navNodeManagerService.navToNode(node.id, node.parentId, 'infoTab');
|
||||
}
|
||||
}
|
||||
));
|
||||
}
|
||||
}
|
||||
-5
@@ -57,11 +57,6 @@ export class ObjectPropertiesPageService {
|
||||
|
||||
try {
|
||||
const tabContext = await contexts.getContext(this.objectViewerTabService.objectViewerTabContext);
|
||||
|
||||
if (tabContext.tab && !tabContext.tab.handlerState.folderId) {
|
||||
tabContext.tab.handlerState.folderId = 'infoTab';
|
||||
}
|
||||
|
||||
tabContext.trySwitchPage(this.page);
|
||||
} catch (exception) {
|
||||
this.notificationService.logException(exception, 'Object Viewer Error', 'Error in Object Viewer while processing action with database node');
|
||||
|
||||
-38
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2021 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, { css } from 'reshadow';
|
||||
|
||||
import { Loader } from '@cloudbeaver/core-blocks';
|
||||
import { useService } from '@cloudbeaver/core-di';
|
||||
|
||||
import { ObjectChildrenPropertyTable } from '../ObjectPropertyTable/ObjectChildrenPropertyTable';
|
||||
import { VirtualFolderTabMixin } from './VirtualFolderTabMixin';
|
||||
|
||||
const style = css`
|
||||
tab-wrapper {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
flex: 1 0 auto;
|
||||
}
|
||||
`;
|
||||
|
||||
export const VirtualFolderTab = observer(function VirtualFolderTab() {
|
||||
const folderMixin = useService(VirtualFolderTabMixin);
|
||||
|
||||
if (!folderMixin.isActivated) {
|
||||
return <Loader />;
|
||||
}
|
||||
|
||||
return styled(style)(
|
||||
<tab-wrapper>
|
||||
<ObjectChildrenPropertyTable nodeIds={folderMixin.getChildrenId()} />
|
||||
</tab-wrapper>
|
||||
);
|
||||
});
|
||||
-15
@@ -1,15 +0,0 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2021 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 class VirtualFolderTabData {
|
||||
constructor(
|
||||
public readonly nodeId: string,
|
||||
public readonly folderName: string,
|
||||
public readonly childrenIds: string[]
|
||||
) {}
|
||||
}
|
||||
-48
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2021 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 { observable, makeObservable } from 'mobx';
|
||||
|
||||
import { injectable } from '@cloudbeaver/core-di';
|
||||
|
||||
import { VirtualFolderTabData } from './VirtualFolderTabData';
|
||||
|
||||
@injectable()
|
||||
export class VirtualFolderTabMixin {
|
||||
isActivated = false;
|
||||
|
||||
constructor(
|
||||
private virtualFolderTabData: VirtualFolderTabData,
|
||||
) {
|
||||
makeObservable(this, {
|
||||
isActivated: observable,
|
||||
});
|
||||
}
|
||||
|
||||
getChildrenId() {
|
||||
return this.virtualFolderTabData.childrenIds;
|
||||
}
|
||||
|
||||
getTitle(): string {
|
||||
return this.virtualFolderTabData.folderName;
|
||||
}
|
||||
|
||||
async onActivate() {
|
||||
if (this.isActivated) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// for (const nodeId of this.getChildrenId()) {
|
||||
// await this.dbObjectService.load(nodeId);
|
||||
// }
|
||||
} finally {
|
||||
this.isActivated = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
-30
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2021 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 { ITab } from '@cloudbeaver/core-blocks';
|
||||
import { Entity, injectable } from '@cloudbeaver/core-di';
|
||||
|
||||
import { VirtualFolderTab } from './VirtualFolderTab';
|
||||
import { VirtualFolderTabMixin } from './VirtualFolderTabMixin';
|
||||
|
||||
@injectable()
|
||||
export class VirtualFolderTabModel implements ITab {
|
||||
readonly tabId: string;
|
||||
title: string;
|
||||
icon = 'platform:/plugin/org.jkiss.dbeaver.model/icons/tree/folder.png';
|
||||
|
||||
panel = VirtualFolderTab;
|
||||
|
||||
onActivate = () => this.virtualFolderTabMixin.onActivate();
|
||||
|
||||
constructor(private tabEntity: Entity,
|
||||
private virtualFolderTabMixin: VirtualFolderTabMixin) {
|
||||
this.tabId = this.tabEntity.id;
|
||||
this.title = this.virtualFolderTabMixin.getTitle();
|
||||
}
|
||||
}
|
||||
-64
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* CloudBeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020-2021 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 { NavNodeManagerService, NavNode, TabEntity } from '@cloudbeaver/core-app';
|
||||
import { injectable, MixinProvider } from '@cloudbeaver/core-di';
|
||||
|
||||
import { VirtualFolderTabData } from './VirtualFolderTabData';
|
||||
import { VirtualFolderTabMixin } from './VirtualFolderTabMixin';
|
||||
import { VirtualFolderTabModel } from './VirtualFolderTabModel';
|
||||
|
||||
@injectable()
|
||||
export class VirtualFolderTabService {
|
||||
constructor(private navNodeManagerService: NavNodeManagerService) { }
|
||||
|
||||
createTabEntities(nodeId: string): TabEntity[] {
|
||||
const children = this.navNodeManagerService.getTree(nodeId) || [];
|
||||
|
||||
const notFolderNodes = children.reduce<NavNode[]>(
|
||||
(nodes, nodeId) => {
|
||||
const node = this.navNodeManagerService.getNode(nodeId);
|
||||
if (node && !node.folder) {
|
||||
nodes.push(node);
|
||||
}
|
||||
return nodes;
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const virtualFolders = notFolderNodes.reduce<Map<string, VirtualFolderTabData>>(
|
||||
(map, node) => {
|
||||
const folderName = node?.nodeType || 'unknown';
|
||||
|
||||
let tabData = map.get(folderName);
|
||||
if (!tabData) {
|
||||
tabData = new VirtualFolderTabData(nodeId, folderName, []);
|
||||
map.set(folderName, tabData);
|
||||
}
|
||||
|
||||
tabData.childrenIds.push(node.id);
|
||||
return map;
|
||||
},
|
||||
new Map()
|
||||
);
|
||||
|
||||
return Array.from(virtualFolders.values())
|
||||
.map(tabData => this.createEntity(tabData));
|
||||
}
|
||||
|
||||
private createEntity(tabData: VirtualFolderTabData): TabEntity {
|
||||
const mixins: Array<MixinProvider<any>> = [
|
||||
{
|
||||
token: VirtualFolderTabData,
|
||||
value: tabData,
|
||||
},
|
||||
VirtualFolderTabMixin,
|
||||
];
|
||||
return new TabEntity(VirtualFolderTabModel, mixins);
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,5 @@ export default manifest;
|
||||
// All Services and Components that is provided by this plugin should be exported here
|
||||
export * from './ObjectPage/DBObjectPageService';
|
||||
export * from './ObjectPage/ObjectPage';
|
||||
export * from './ObjectPropertiesPage/ObjectFoldersService';
|
||||
export * from './ObjectViewerTabService';
|
||||
export * from './IObjectViewerTabState';
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export default [
|
||||
['plugin_object_viewer_properties_tab', 'Properties'],
|
||||
['plugin_object_viewer_object_info_tab', 'Info'],
|
||||
['plugin_object_viewer_table_name', 'Name'],
|
||||
['plugin_object_viewer_table_no_items', 'There are no items to show'],
|
||||
['plugin_object_viewer_error', 'Error occurred while tab loading'],
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export default [
|
||||
['plugin_object_viewer_properties_tab', 'Proprietà'],
|
||||
['plugin_object_viewer_object_info_tab', 'Informazioni'],
|
||||
['plugin_object_viewer_table_name', 'Nome'],
|
||||
['plugin_object_viewer_table_no_items', 'Non ci sono elementi da mostrare'],
|
||||
['plugin_object_viewer_error', 'Errore durante il caricamento della tab'],
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export default [
|
||||
['plugin_object_viewer_properties_tab', 'Свойства'],
|
||||
['plugin_object_viewer_object_info_tab', 'Информация'],
|
||||
['plugin_object_viewer_table_name', 'Название'],
|
||||
['plugin_object_viewer_table_no_items', 'Нет объектов для показа'],
|
||||
['plugin_object_viewer_error', 'Произошла ошибка при загрузке вкладки'],
|
||||
|
||||
@@ -10,11 +10,9 @@ import type { PluginManifest } from '@cloudbeaver/core-di';
|
||||
|
||||
import { LocaleService } from './LocaleService';
|
||||
import { DBObjectPageService } from './ObjectPage/DBObjectPageService';
|
||||
import { ObjectFoldersService } from './ObjectPropertiesPage/ObjectFoldersService';
|
||||
import { ObjectFoldersTabService } from './ObjectPropertiesPage/ObjectFoldersTab/ObjectFoldersTabService';
|
||||
import { ObjectInfoTabService } from './ObjectPropertiesPage/ObjectInfoTab/ObjectInfoTabService';
|
||||
import { NavNodeMetadataViewBootstrap } from './ObjectPropertiesPage/NavNodeView/NavNodeMetadata/NavNodeMetadataViewBootstrap';
|
||||
import { VirtualFolderViewBootstrap } from './ObjectPropertiesPage/NavNodeView/VirtualFolder/VirtualFolderViewBootstrap';
|
||||
import { ObjectPropertiesPageService } from './ObjectPropertiesPage/ObjectPropertiesPageService';
|
||||
import { VirtualFolderTabService } from './ObjectPropertiesPage/VirtualFolderTab/VirtualFolderTabService';
|
||||
import { ObjectViewerBootstrap } from './ObjectViewerBootstrap';
|
||||
import { ObjectViewerTabService } from './ObjectViewerTabService';
|
||||
|
||||
@@ -23,12 +21,10 @@ export const manifest: PluginManifest = {
|
||||
|
||||
providers: [
|
||||
ObjectViewerBootstrap,
|
||||
NavNodeMetadataViewBootstrap,
|
||||
VirtualFolderViewBootstrap,
|
||||
ObjectPropertiesPageService,
|
||||
ObjectFoldersService,
|
||||
ObjectViewerTabService,
|
||||
ObjectInfoTabService,
|
||||
ObjectFoldersTabService,
|
||||
VirtualFolderTabService,
|
||||
DBObjectPageService,
|
||||
LocaleService,
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user