/* * 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 { action, observable } from 'mobx'; import { observer } from 'mobx-react-lite'; import React, { forwardRef, useContext } from 'react'; import styled, { use } from 'reshadow'; import { getComputed, Loader, TREE_NODE_STYLES, TreeNodeContext, TreeNodeControl, TreeNodeIcon, TreeNodeName, useMouseContextMenu, useObjectRef, useObservableRef, } from '@cloudbeaver/core-blocks'; import { useService } from '@cloudbeaver/core-di'; import { EventContext, EventStopPropagationFlag } from '@cloudbeaver/core-events'; import { type INodeActions, NavNodeInfoResource, NavTreeResource } from '@cloudbeaver/core-navigation-tree'; 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'; interface IEditingState { saving: boolean; editing: boolean; resolve: (value: string) => Promise; startEditing(resolve: (value: string) => Promise): void; save(name: string): void; setSaveStatus(saving: boolean): void; finish(): void; cancel(): void; } export const NavigationNodeControl: NavTreeControlComponent = observer( forwardRef(function NavigationNodeControl({ node, nodeInfo, dndElement, dndPlaceholder, className, onClick }, ref) { const mouseContextMenu = useMouseContextMenu(); const treeNodeContext = useContext(TreeNodeContext); const navNodeInfoResource = useService(NavNodeInfoResource); const navTreeResource = useService(NavTreeResource); const error = getComputed(() => !!navNodeInfoResource.getException(node.id) || !!navTreeResource.getException(node.id)); const selected = treeNodeContext.selected; const editingState = useObservableRef( () => ({ saving: false, editing: false, resolve: () => Promise.resolve(false), startEditing(resolve) { this.editing = true; this.saving = false; this.resolve = resolve; }, async save(name: string) { this.setSaveStatus(true); try { const saved = await this.resolve(name); if (saved) { this.finish(); } } finally { this.setSaveStatus(false); } }, setSaveStatus(saving: boolean) { this.saving = saving; }, finish() { this.editing = false; this.saving = false; this.resolve = () => Promise.resolve(false); }, cancel() { this.finish(); }, }), { saving: observable.ref, editing: observable.ref, startEditing: action.bound, save: action.bound, setSaveStatus: action.bound, finish: action.bound, cancel: action.bound, }, false, ); const nodeActions = useObjectRef({ rename: editingState.startEditing, }); let icon = nodeInfo.icon; const name = nodeInfo.name; const title = nodeInfo.tooltip; 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(); } const { editing, saving } = editingState; const attributes = { [DATA_ATTRIBUTE_NODE_EDITING]: editing }; return styled( TREE_NODE_STYLES, NAVIGATION_NODE_CONTROL_STYLES, )( {editing ? ( ) : ( {name} )} {!editing && !dndPlaceholder && ( )} , ); }), );