/** * 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 { FaChevronDown, FaChevronRight } from 'react-icons/fa'; import { isNonNullable } from '@finos/legend-studio-shared'; import clsx from 'clsx'; const DEFAULT_STEP_PADDING_IN_REM = 1; type InnerProps = Record; export interface TreeNodeData { isSelected?: boolean; isOpen?: boolean; id: string; label: string; childrenIds?: string[]; } export interface TreeData { rootIds: string[]; nodes: Map; } /** * Default tree node container */ export interface TreeNodeContainerProps< T extends TreeNodeData, S extends InnerProps, > { node: T; classPrefix?: string; level: number; onNodeSelect?: (node: T) => void; stepPaddingInRem?: number; innerProps: S; } const DefaultTreeNodeContainer = ( props: TreeNodeContainerProps, ): React.ReactElement> => { const { node, level, stepPaddingInRem, classPrefix, onNodeSelect } = props; const selectNode = (): void => onNodeSelect?.(node); return (
{Boolean(node.childrenIds?.length) && (node.isOpen ? : )}
{node.label}
); }; /** * Tree View Components (default) */ interface TreeViewComponents { TreeNodeView: React.FC>; TreeNodeContainer: React.FC>; } /** * Default tree node view */ export interface TreeNodeViewProps< T extends TreeNodeData, S extends InnerProps, > { node: T; classPrefix?: string; level: number; components?: Partial>; onNodeSelect?: (node: T) => void; getChildNodes: (node: T) => T[]; stepPaddingInRem?: number; innerProps: S; } const DefaultTreeNodeView = ( props: TreeNodeViewProps, ): React.ReactElement> => { const { node, level, onNodeSelect, getChildNodes, classPrefix, components, stepPaddingInRem, innerProps, } = props; const defaultTreeComponents = { TreeNodeView: DefaultTreeNodeView, TreeNodeContainer: DefaultTreeNodeContainer, }; const treeComponents = components ? { ...defaultTreeComponents, ...components } : defaultTreeComponents; return ( // NOTE: if we want block-tree instead of padded tree, we can set the padding for it and zero out the step padding for each container
{node.isOpen && getChildNodes(node).map((childNode) => ( ))}
); }; /** * Tree View */ export interface TreeViewProps { treeData: TreeData; getChildNodes: (node: T) => T[]; getRootNodes?: (treeData: TreeData) => T[]; onNodeSelect?: (node: T) => void; components?: Partial>; className?: string; classPrefix?: string; innerProps: S; } export const TreeView = ( props: TreeViewProps, ): React.ReactElement> => { const { treeData, className, classPrefix, components, onNodeSelect, getChildNodes, getRootNodes, innerProps, } = props; const rootNodes = getRootNodes?.(treeData) ?? treeData.rootIds .map((rootId) => treeData.nodes.get(rootId)) .filter(isNonNullable); const defaultTreeComponents = { TreeNodeView: DefaultTreeNodeView, TreeNodeContainer: DefaultTreeNodeContainer, }; const treeComponents = components ? { ...defaultTreeComponents, ...components } : defaultTreeComponents; return (
{rootNodes.map((node) => ( ))}
); };