@finos/legend-application-pure-ide
Version:
Legend Pure IDE application core
166 lines • 11.2 kB
JavaScript
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
/**
* Copyright (c) 2020-present, Goldman Sachs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { forwardRef, useState } from 'react';
import { observer } from 'mobx-react-lite';
import { flowResult } from 'mobx';
import { FileEditorState } from '../../stores/FileEditorState.js';
import { CreateNewFilePrompt } from './CreateNewFilePrompt.js';
import { CreateNewDirectoryPrompt } from './CreateNewDirectoryPrompt.js';
import { useApplicationStore } from '@finos/legend-application';
import { clsx, BlankPanelContent, ContextMenu, PanelLoadingIndicator, TreeView, ChevronDownIcon, ChevronRightIcon, CircleNotchIcon, RefreshIcon, CrosshairsIcon, CompressIcon, FileAltIcon, FolderIcon, FolderOpenIcon, WrenchIcon, MenuContent, MenuContentItem, MenuContentDivider, } from '@finos/legend-art';
import { isNonNullable } from '@finos/legend-shared';
import { DirectoryNode, } from '../../server/models/DirectoryTree.js';
import { usePureIDEStore } from '../PureIDEStoreProvider.js';
import { RenameFilePrompt } from './RenameFilePrompt.js';
const FileExplorerContextMenu = observer(forwardRef(function FileExplorerContextMenu(props, ref) {
const { node } = props;
const applicationStore = useApplicationStore();
const ideStore = usePureIDEStore();
const isDir = node.data.isFolderNode;
const hasChildContent = Boolean(node.data.children);
const createNewFile = () => ideStore.directoryTreeState.setNodeForCreateNewFile(node);
const createNewDirectory = () => ideStore.directoryTreeState.setNodeForCreateNewDirectory(node);
const deleteFileOrDirectory = () => {
flowResult(ideStore.deleteDirectoryOrFile(node.data.li_attr.path, isDir, hasChildContent)).catch(applicationStore.alertUnhandledError);
};
const renameFile = () => ideStore.directoryTreeState.setNodeForRenameFile(node);
const copyPath = () => {
applicationStore.clipboardService
.copyTextToClipboard(node.data.li_attr.path)
.catch(applicationStore.alertUnhandledError);
};
return (_jsxs(MenuContent, { ref: ref, children: [_jsx(MenuContentItem, { onClick: copyPath, children: "Copy Path" }), !node.data.li_attr.RO && (_jsxs(_Fragment, { children: [_jsx(MenuContentDivider, {}), isDir && (_jsx(MenuContentItem, { onClick: createNewFile, children: "New File" })), isDir && (_jsx(MenuContentItem, { onClick: createNewDirectory, children: "New Directory" })), !isDir && (_jsx(MenuContentItem, { onClick: renameFile, children: "Rename" })), _jsx(MenuContentItem, { disabled: Boolean(node.data instanceof DirectoryNode && node.data.children), onClick: deleteFileOrDirectory, children: "Delete" })] }))] }));
}));
const FileTreeNodeContainer = (props) => {
const { node, level, stepPaddingInRem, onNodeSelect, innerProps } = props;
const [isSelectedFromContextMenu, setIsSelectedFromContextMenu] = useState(false);
const { onNodeOpen, onNodeExpand, onNodeCompress } = innerProps;
const isPlatformDirectory = node.data instanceof DirectoryNode &&
node.data.isRepoNode &&
node.data.li_attr.path === '/platform';
const isReadOnly = node.data.li_attr.RO;
const isDirectory = node.data.isFolderNode;
const isChildlessDirectory = node.data instanceof DirectoryNode && !node.data.children;
const nodeIcon = isPlatformDirectory ||
(node.data.isRepoNode &&
node.data.li_attr.path.startsWith('/platform_')) ? (_jsx(WrenchIcon, { className: "explorer__icon--readonly" })) : isDirectory ? (isChildlessDirectory ? (_jsx(FolderIcon, { className: clsx('explorer__icon--folder', {
'explorer__icon--readonly': isReadOnly,
}) })) : node.isOpen ? (_jsx(FolderOpenIcon, { className: clsx('explorer__icon--folder', {
'explorer__icon--readonly': isReadOnly,
}) })) : (_jsx(FolderIcon, { className: clsx('explorer__icon--folder', {
'explorer__icon--readonly': isReadOnly,
}) }))) : (_jsx(FileAltIcon, { className: clsx('explorer__icon--file', {
'explorer__icon--readonly': isReadOnly,
}) }));
const selectNode = (event) => {
event.stopPropagation();
event.preventDefault();
onNodeSelect?.(node);
};
const onContextMenuOpen = () => setIsSelectedFromContextMenu(true);
const onContextMenuClose = () => setIsSelectedFromContextMenu(false);
const toggleExpansion = () => {
if (isChildlessDirectory) {
return;
}
if (node.isLoading) {
return;
}
if (node.isOpen) {
onNodeCompress(node);
}
else {
onNodeExpand(node);
}
};
const onDoubleClick = () => {
if (node.isLoading) {
return;
}
if (isDirectory) {
toggleExpansion();
}
else {
onNodeOpen(node);
}
};
return (_jsx(ContextMenu, { content: _jsx(FileExplorerContextMenu, { node: node }), menuProps: { elevation: 7 }, onOpen: onContextMenuOpen, onClose: onContextMenuClose, children: _jsxs("div", { id: node.id, className: clsx('tree-view__node__container explorer__package-tree__node__container', {
'explorer__package-tree__node__container--selected-from-context-menu': !node.isSelected && isSelectedFromContextMenu,
}, {
'explorer__package-tree__node__container--selected': node.isSelected,
}), onClick: selectNode, onDoubleClick: onDoubleClick, style: {
paddingLeft: `${level * (stepPaddingInRem ?? 1)}rem`,
display: 'flex',
}, children: [_jsxs("div", { className: "tree-view__node__icon explorer__package-tree__node__icon", children: [node.isLoading && (_jsx("div", { className: "explorer__package-tree__node__icon__expand explorer__package-tree__node__icon__expand--is-loading", children: _jsx(CircleNotchIcon, {}) })), !node.isLoading && (_jsx("div", { className: "explorer__package-tree__node__icon__expand", onClick: toggleExpansion, children: !isDirectory || isChildlessDirectory ? (_jsx("div", {})) : node.isOpen ? (_jsx(ChevronDownIcon, {})) : (_jsx(ChevronRightIcon, {})) })), _jsx("div", { className: "explorer__package-tree__node__icon__type", children: nodeIcon })] }), _jsx("button", { className: "tree-view__node__label explorer__package-tree__node__label", tabIndex: -1, children: isPlatformDirectory ? 'platform' : node.label })] }) }));
};
const FileExplorerTree = observer(() => {
const ideStore = usePureIDEStore();
const applicationStore = useApplicationStore();
const treeState = ideStore.directoryTreeState;
const treeData = ideStore.directoryTreeState.getTreeData();
const onNodeSelect = (node) => treeState.setSelectedNode(node);
const onNodeOpen = (node) => {
flowResult(treeState.openNode(node)).catch(applicationStore.alertUnhandledError);
};
const onNodeExpand = (node) => {
flowResult(treeState.expandNode(node)).catch(applicationStore.alertUnhandledError);
};
const onNodeCompress = (node) => {
node.isOpen = false;
treeState.refreshTree();
};
const getChildNodes = (node) => {
if (node.isLoading || !node.childrenIds) {
return [];
}
return node.childrenIds
.map((childId) => treeData.nodes.get(childId))
.filter(isNonNullable);
};
const deselectTreeNode = () => treeState.setSelectedNode(undefined);
return (_jsxs("div", { className: "explorer__content", onClick: deselectTreeNode, children: [_jsx(TreeView, { components: {
TreeNodeContainer: FileTreeNodeContainer,
}, treeData: treeData, onNodeSelect: onNodeSelect, getChildNodes: getChildNodes, innerProps: {
onNodeOpen,
onNodeExpand,
onNodeCompress,
} }), treeState.nodeForCreateNewFile && (_jsx(CreateNewFilePrompt, { node: treeState.nodeForCreateNewFile })), treeState.nodeForCreateNewDirectory && (_jsx(CreateNewDirectoryPrompt, { node: treeState.nodeForCreateNewDirectory })), treeState.nodeForRenameFile && (_jsx(RenameFilePrompt, { node: treeState.nodeForRenameFile }))] }));
});
export const DirectoryTreeExplorer = observer(() => {
const ideStore = usePureIDEStore();
const applicationStore = useApplicationStore();
const treeState = ideStore.directoryTreeState;
const refreshTree = () => {
flowResult(treeState.refreshTreeData()).catch(applicationStore.alertUnhandledError);
};
const focus = () => {
const currentTab = ideStore.tabManagerState.currentTab;
if (currentTab instanceof FileEditorState) {
flowResult(treeState.revealPath(currentTab.filePath)).catch(applicationStore.alertUnhandledError);
}
};
const collapseTree = () => {
const treeData = treeState.getTreeData();
treeData.nodes.forEach((node) => {
node.isOpen = false;
});
treeState.setSelectedNode(undefined);
treeState.refreshTree();
};
return (_jsxs("div", { className: "panel explorer", children: [_jsx("div", { className: "panel__header side-bar__header", children: _jsx("div", { className: "panel__header__title", children: _jsx("div", { className: "panel__header__title__content side-bar__header__title__content", children: "FILES" }) }) }), _jsx("div", { className: "panel__content side-bar__content", children: _jsxs("div", { className: "panel explorer", children: [_jsxs("div", { className: "panel__header explorer__header", children: [_jsx("div", { className: "panel__header__title" }), _jsxs("div", { className: "panel__header__actions", children: [_jsx("button", { className: "panel__header__action explorer__btn__refresh", onClick: refreshTree, title: "Refresh Tree", children: _jsx(RefreshIcon, {}) }), _jsx("button", { className: "panel__header__action", onClick: focus, title: "Focus", children: _jsx(CrosshairsIcon, {}) }), _jsx("button", { className: "panel__header__action", onClick: collapseTree, title: "Collapse All", children: _jsx(CompressIcon, {}) })] })] }), _jsxs("div", { className: "panel__content explorer__content__container", children: [_jsx(PanelLoadingIndicator, { isLoading: treeState.loadInitialDataState.isInProgress }), treeState.loadInitialDataState.hasSucceeded && (_jsx(FileExplorerTree, {})), treeState.loadInitialDataState.hasFailed && (_jsx(BlankPanelContent, { children: "Failed to build directory tree" }))] })] }) })] }));
});
//# sourceMappingURL=DirectoryTreeExplorer.js.map