mirror of
https://github.com/dbeaver/cloudbeaver.git
synced 2026-11-03 18:01:48 +08:00
* CB-3372 feat: codemirror6 autocomplete * CB-3372 feat: autocomplete icons * CB-3371 feat: sql editor syntax highlight * CB-3371 fix: syntax highlight blinking * CB-3371 fix: editor extensions state * CB-3372 chore: code cleanup * CB-3372 fix: remove useSqlDIalectAutocompletion * CB-3372 fix: add useSqlDIalectAutocompletion * CB-3372 feat: experimental context menu * CB-3372 feat: use new sql editor * CB-3372 chore: remove sql script parser * CB-3372 fix: rounded menu * CB-3372 fix: event preventDefault on dialog * CB-3372 fix: context menu styles * CB-3372 feat: remove old codemirror * CB-3372 fix: numbers completion & extensions caching * CB-3372 fix: table display * CB-3372 fix: decrease sql editor font size * CB-3372 fix: copy in readonly mode & autocompletion in safari on MacOS * CB-3372 fix: value panel styles * CB-3372 fix: menu bar styles * CB-3372 fix: menu bar and inline editor styles * CB-3372 fix: menu and inline editor styles * CB-3372 chore: update bundle --------- Co-authored-by: dariamarutkina <125263541+dariamarutkina@users.noreply.github.com>
163 lines
5.3 KiB
TypeScript
163 lines
5.3 KiB
TypeScript
/*
|
|
* 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, useState } from 'react';
|
|
import styled, { css, use } from 'reshadow';
|
|
|
|
import { getComputed, TreeNodeContext, TreeNodeControl, TreeNodeName, TREE_NODE_STYLES, useObjectRef, useMouseContextMenu } from '@cloudbeaver/core-blocks';
|
|
import { useService } from '@cloudbeaver/core-di';
|
|
import { EventContext, EventStopPropagationFlag } from '@cloudbeaver/core-events';
|
|
import { NavNodeInfoResource, type INodeActions } from '@cloudbeaver/core-navigation-tree';
|
|
import { ProjectInfoResource } from '@cloudbeaver/core-projects';
|
|
import { NAV_NODE_TYPE_RM_PROJECT } from '@cloudbeaver/core-resource-manager';
|
|
import { CaptureViewContext } from '@cloudbeaver/core-view';
|
|
import { ElementsTreeContext, isDraggingInsideProject, NavigationNodeEditorLoader, NavTreeControlComponent, NavTreeControlProps, TreeNodeMenuLoader } 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';
|
|
|
|
|
|
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 {
|
|
composes: theme-text-text-hint-on-light theme-typography--caption from global;
|
|
height: 100%;
|
|
max-width: 250px;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
portal {
|
|
position: relative;
|
|
box-sizing: border-box;
|
|
margin-left: auto !important;
|
|
margin-right: 8px !important;
|
|
visibility: hidden;
|
|
}
|
|
name-box {
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
`;
|
|
|
|
export const NavigationNodeProjectControl: NavTreeControlComponent = observer<NavTreeControlProps, HTMLDivElement>(forwardRef(function NavigationNodeProjectControl({
|
|
node,
|
|
dndElement,
|
|
dndPlaceholder,
|
|
className,
|
|
}, ref) {
|
|
const mouseContextMenu = useMouseContextMenu();
|
|
const viewContext = useContext(CaptureViewContext);
|
|
const elementsTreeContext = useContext(ElementsTreeContext);
|
|
const treeNodeContext = useContext(TreeNodeContext);
|
|
|
|
const projectInfoResource = useService(ProjectInfoResource);
|
|
const navNodeInfoResource = useService(NavNodeInfoResource);
|
|
const resourceManagerService = useService(ResourceManagerService);
|
|
|
|
const outdated = getComputed(() => navNodeInfoResource.isOutdated(node.id) && !treeNodeContext.loading);
|
|
const selected = treeNodeContext.selected;
|
|
const resourceType = viewContext?.tryGet(DATA_CONTEXT_RESOURCE_MANAGER_TREE_RESOURCE_TYPE_ID);
|
|
|
|
const isDragging = getComputed(() => {
|
|
if (!node.projectId || !elementsTreeContext?.tree.activeDnDData) {
|
|
return false;
|
|
}
|
|
|
|
return isDraggingInsideProject(node.projectId, elementsTreeContext.tree.activeDnDData);
|
|
});
|
|
|
|
const [editing, setEditing] = useState(false);
|
|
let name = node.name;
|
|
|
|
const nodeActions = useObjectRef<INodeActions>({
|
|
rename: () => {
|
|
setEditing(true);
|
|
},
|
|
});
|
|
|
|
function handlePortalClick(event: React.MouseEvent<HTMLDivElement>) {
|
|
EventContext.set(event, EventStopPropagationFlag);
|
|
treeNodeContext.select();
|
|
}
|
|
|
|
function handleContextMenuOpen(event: React.MouseEvent<HTMLDivElement>) {
|
|
mouseContextMenu.handleContextMenuOpen(event);
|
|
treeNodeContext.select();
|
|
}
|
|
|
|
function handleClick(event: React.MouseEvent<HTMLDivElement>) {
|
|
treeNodeContext.select(event.ctrlKey || event.metaKey);
|
|
}
|
|
|
|
if (node.projectId && resourceType !== undefined) {
|
|
if (node.nodeType === NAV_NODE_TYPE_RM_PROJECT) {
|
|
const project = projectInfoResource.get(node.projectId);
|
|
if (project) {
|
|
const resourceFolder = resourceManagerService.getRootFolder(project, resourceType);
|
|
|
|
if (resourceFolder !== undefined) {
|
|
return null;
|
|
}
|
|
}
|
|
} else {
|
|
const project = getRmProjectNodeId(node.projectId);
|
|
const projectName = navNodeInfoResource.get(project)?.name;
|
|
|
|
if (projectName) {
|
|
name = projectName;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (elementsTreeContext?.tree.settings?.projects === false && !isDragging) {
|
|
return null;
|
|
}
|
|
|
|
return styled(TREE_NODE_STYLES, styles)(
|
|
<TreeNodeControl
|
|
ref={ref}
|
|
onClick={handleClick}
|
|
onContextMenu={handleContextMenuOpen}
|
|
{...use({ outdated, editing, dragging: dndElement })}
|
|
className={className}
|
|
>
|
|
<TreeNodeName title={name}>
|
|
{editing ? (
|
|
<NavigationNodeEditorLoader node={node} onClose={() => setEditing(false)} />
|
|
) : (
|
|
<name-box>{name}</name-box>
|
|
)}
|
|
</TreeNodeName>
|
|
{!editing && !dndPlaceholder && (
|
|
<portal onClick={handlePortalClick}>
|
|
<TreeNodeMenuLoader
|
|
mouseContextMenu={mouseContextMenu}
|
|
node={node}
|
|
actions={nodeActions}
|
|
selected={selected}
|
|
/>
|
|
</portal>
|
|
)}
|
|
</TreeNodeControl>
|
|
);
|
|
}));
|