Files
cloudbeaver/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/NavigationTreeNode/useNavigationNode.ts
T
Alexey e659a64cbd CB-3274 fix folders sync (#1568)
* CB-3274 fix: connection / resource folders sync

* CB-3274 fix: connection folders caching

* CB-3274 fix: dialog error handling

* CB-3274 fix: nav tree folders mode

* CB-3274 fix: nav tree performance

* CB-3263 fix: new folders validation

* CB-3263 fix: commit includes to alias transformation

* CB-3274 chore: clear code
2023-04-04 12:52:35 +03:00

150 lines
5.2 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 React, { useContext, useEffect, useRef } from 'react';
import { getComputed, useExecutor, useObjectRef } from '@cloudbeaver/core-blocks';
import { useService } from '@cloudbeaver/core-di';
import { SyncExecutor } from '@cloudbeaver/core-executor';
import { type NavNode, NavNodeInfoResource, EObjectFeature } from '@cloudbeaver/core-navigation-tree';
import { resourceKeyList } from '@cloudbeaver/core-sdk';
import type { IDNDData } from '@cloudbeaver/core-ui';
import { useChildren } from '../../../NodesManager/useChildren';
import { useNode } from '../../../NodesManager/useNode';
import { ElementsTreeContext } from '../ElementsTreeContext';
import type { IElementsTreeAction } from '../IElementsTreeAction';
import type { NavTreeControlComponent } from '../NavigationNodeComponent';
import type { IElementsTree } from '../useElementsTree';
export interface INavigationNode {
ref: React.RefObject<HTMLDivElement>;
control?: NavTreeControlComponent;
disabled: boolean;
group: boolean;
showInFilter: boolean;
selected: boolean;
indeterminateSelected: boolean;
loading: boolean;
loaded: boolean;
outdated: boolean;
expanded: boolean;
leaf: boolean;
empty: boolean;
expand: () => Promise<void>;
open: (leaf: boolean) => Promise<void>;
click: (leaf: boolean) => Promise<void>;
select: (isMultiple?: boolean, nested?: boolean) => Promise<void>;
getSelected: () => NavNode[];
setDnDState: (data: IDNDData, dragging: boolean) => void;
}
export function useNavigationNode(node: NavNode, path: string[]): INavigationNode {
const elementRef = useRef<HTMLDivElement>(null);
const navNodeInfoResource = useService(NavNodeInfoResource);
const contextRef = useObjectRef({
context: useContext(ElementsTreeContext),
});
const { isLoading, isLoaded, isOutdated } = useNode(node.id);
const children = useChildren(node.id);
const outdated = getComputed(() => isOutdated() || children.isOutdated());
const loading = getComputed(() => isLoading() || children.isLoading());
const loaded = getComputed(() => children.children !== undefined && children.isLoaded() && isLoaded());
const showInFilter = getComputed(() => contextRef.context?.tree.getNodeState(node.id).showInFilter || false);
const isExpanded = getComputed(() => contextRef.context?.tree.isNodeExpanded(node.id) || false);
const leaf = getComputed(() => isLeaf(node, children.children, contextRef.context?.tree, outdated));
const group = getComputed(() => contextRef.context?.tree.isGroup?.(node) || false);
const empty = getComputed(() => children.children?.length === 0);
const expanded = getComputed(() => isExpanded);
const control = getComputed(() => contextRef.context?.control);
const disabled = getComputed(() => contextRef.context?.tree.disabled || false);
const selected = getComputed(() => contextRef.context?.tree.isNodeSelected(node.id) || false);
const getSelected = () => navNodeInfoResource
.get(resourceKeyList(contextRef.context?.tree.getSelected() || []))
.filter(Boolean) as NavNode[];
const indeterminateSelected = getComputed(
() => contextRef.context?.tree.isNodeIndeterminateSelected(node.id) || false
);
const handleClick = async (leaf: boolean) => await contextRef.context?.tree.click(node, path, leaf);
const handleOpen = async (leaf: boolean) => await contextRef.context?.tree.open(node, path, leaf);
const handleExpand = async () => await contextRef.context?.tree.expand(node, !expanded);
const handleSelect = async (
multiple = false,
nested = false
) => await contextRef.context?.tree.select(node, multiple, nested);
function setDnDState(data: IDNDData, dragging: boolean): void {
contextRef.context?.tree.setDnDData(data, dragging);
}
useEffect(() => () => {
if (!contextRef.context?.selectionTree) {
if (contextRef.context?.tree.isNodeSelected(node.id)) {
contextRef.context.tree.select(node, true, false);
}
}
}, [node]);
useEffect(() => {
if (contextRef.context?.tree.isNodeSelected(node.id)) {
elementRef.current?.scrollIntoView();
}
}, []);
useExecutor({
executor: contextRef.context?.tree.actions || new SyncExecutor<IElementsTreeAction>(),
handlers: [function refreshRoot({ type, nodeId }) {
if (type === 'show' && nodeId === node.id) {
elementRef.current?.scrollIntoView();
}
}],
});
return useObjectRef({
ref: elementRef,
showInFilter,
empty,
group,
control,
disabled,
selected,
indeterminateSelected,
loading,
outdated,
loaded,
expanded,
leaf,
expand: handleExpand,
click: handleClick,
open: handleOpen,
select: handleSelect,
getSelected,
setDnDState,
});
}
export function isLeaf(
node: NavNode,
children: string[] | undefined,
tree: IElementsTree | undefined,
outdated: boolean
): boolean {
if (node.folder && tree?.settings?.foldersTree) {
return false;
}
return (
node.objectFeatures.includes(EObjectFeature.entity)
|| !node.hasChildren
|| (children?.length === 0 && !outdated)
);
}