import { useState, useEffect } from 'react';
import { useTranslation } from 'react-i18next';
import { SimpleTreeView, TreeItem } from '@mui/x-tree-view';
import icons from '@/components/icons/Icons';
import { Tooltip, Typography, Grow, Popover } from '@mui/material';
import { useNavigate } from 'react-router-dom';
import { CollectionObject } from '@server/types';
import clcx from 'clsx';
import { formatNumber } from '@/utils';
import { useStyles } from './style';
import {
DatabaseTreeItem,
TreeNodeType,
DatabaseToolProps,
ContextMenu,
TreeNodeObject,
} from './types';
import { TreeContextMenu } from './TreeContextMenu';
// get expanded nodes from data
const getExpanded = (nodes: DatabaseTreeItem[]) => {
const expanded: string[] = [];
nodes.forEach(node => {
if (node.expanded) {
expanded.push(node.id);
}
if (node.children && node.children.length > 0) {
expanded.push(...getExpanded(node.children));
}
});
return expanded;
};
const CollectionNode: React.FC<{ data: CollectionObject }> = ({ data }) => {
// i18n collectionTrans
const { t: commonTrans } = useTranslation();
// styles
const classes = useStyles();
// class
const loadClass = clcx(classes.dot, {
[classes.loaded]: data.loaded,
[classes.unloaded]: !data.loaded,
[classes.loading]: data.status === 'loading',
[classes.noIndex]: !data.schema || !data.schema.hasVectorIndex,
});
// status tooltip
const hasIndex = data.schema && data.schema.hasVectorIndex;
const loadStatus = hasIndex
? data.loaded
? commonTrans('status.loaded')
: commonTrans('status.unloaded')
: commonTrans('status.noVectorIndex');
return (
{data.collection_name}
({formatNumber(data.rowCount || 0)})
);
};
const DatabaseTree: React.FC = props => {
// state
const [contextMenu, setContextMenu] = useState(null);
// props
const { database, collections, params } = props;
// format tree data
const children = collections.map(c => {
return {
id: `c_${c.collection_name}`,
name: c.collection_name,
type: 'collection' as TreeNodeType,
data: c,
};
});
// tree data
const tree: DatabaseTreeItem = {
id: database,
name: database,
expanded: children.length > 0,
type: 'db',
children: children,
};
// Icons
const DatabaseIcon = icons.database;
const CollectionIcon = icons.navCollection;
// hooks
const navigate = useNavigate();
const classes = useStyles();
// on node click
const onNodeClick = (node: DatabaseTreeItem) => {
navigate(
node.type === 'db'
? `/databases/${database}/${params.databasePage || 'collections'}`
: `/databases/${database}/${node.name}/${
params.collectionPage || 'schema'
}`
);
// close context menu
setContextMenu(null);
};
const handleContextMenu = (
event: any,
nodeId: string,
nodeType: string,
object: TreeNodeObject
) => {
// prevent default
event.preventDefault();
event.stopPropagation();
setContextMenu({
mouseX: event.clientX - 2,
mouseY: event.clientY - 4,
nodeId,
nodeType: nodeType as TreeNodeType,
object: object,
});
};
const handleClose = () => {
setContextMenu(null);
};
// render children
const renderTree = (nodes: DatabaseTreeItem[]) => {
return nodes.map(node => {
if (node.children && node.children.length > 0) {
return (
{
event.stopPropagation();
if (onNodeClick) {
onNodeClick(node);
}
}}
>
{renderTree(node.children)}
);
}
return (
}
onContextMenu={event =>
handleContextMenu(event, node.id, node.type, node.data!)
}
className={clcx(classes.treeItem, {
['right-selected-on']: contextMenu?.nodeId === node.id,
})}
onClick={(event: any) => {
event.stopPropagation();
if (onNodeClick) {
onNodeClick(node);
}
}}
/>
);
});
};
// useEffect
useEffect(() => {
// register click event on document, close context menu if click outside
document.addEventListener('click', handleClose);
return () => {
document.removeEventListener('click', handleClose);
};
}, []);
return (
<>
{
{tree.name}
}
className={classes.treeItem}
slots={{
icon: DatabaseIcon,
}}
onClick={(event: any) => {
event.stopPropagation();
if (onNodeClick) {
onNodeClick(tree);
}
}}
onContextMenu={event =>
handleContextMenu(event, tree.id, tree.type, null)
}
>
{tree.children && tree.children.length > 0
? renderTree(tree.children)
: []}
}
>
);
};
export default DatabaseTree;