index.tsx 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. import TreeView from '@material-ui/lab/TreeView';
  2. import TreeItem from '@material-ui/lab/TreeItem';
  3. import icons from '@/components/icons/Icons';
  4. import { makeStyles, Theme } from '@material-ui/core';
  5. import { useNavigate, Params } from 'react-router-dom';
  6. import { CollectionObject } from '@server/types';
  7. export type TreeNodeType = 'db' | 'collection' | 'partition' | 'segment';
  8. export interface DatabaseTreeItem {
  9. children?: DatabaseTreeItem[];
  10. id: string;
  11. name: string;
  12. type: TreeNodeType;
  13. expanded?: boolean;
  14. }
  15. interface DatabaseToolProps {
  16. database: string;
  17. collections: CollectionObject[];
  18. params: Readonly<Params<string>>;
  19. }
  20. // get expanded nodes from data
  21. const getExpanded = (nodes: DatabaseTreeItem[]) => {
  22. const expanded: string[] = [];
  23. nodes.forEach(node => {
  24. if (node.expanded) {
  25. expanded.push(node.id);
  26. }
  27. if (node.children && node.children.length > 0) {
  28. expanded.push(...getExpanded(node.children));
  29. }
  30. });
  31. return expanded;
  32. };
  33. const useStyles = makeStyles((theme: Theme) => ({
  34. root: {
  35. '& .MuiTreeItem-iconContainer': {
  36. width: 'auto',
  37. },
  38. '& .MuiTreeItem-group': {
  39. marginLeft: 0,
  40. '& .MuiTreeItem-content': {
  41. padding: '0 0 0 16px',
  42. },
  43. },
  44. '& .MuiTreeItem-label:hover': {
  45. backgroundColor: 'none',
  46. },
  47. '& .MuiTreeItem-content': {
  48. '&:hover': {
  49. backgroundColor: 'rgba(10, 206, 130, 0.08)',
  50. },
  51. '& .MuiTreeItem-label': {
  52. background: 'none',
  53. },
  54. },
  55. '& .Mui-selected': {
  56. '& > .MuiTreeItem-content': {
  57. backgroundColor: 'rgba(10, 206, 130, 0.08)',
  58. '& .MuiTreeItem-label': {
  59. background: 'none',
  60. },
  61. },
  62. '&:focus': {
  63. '& .MuiTreeItem-content': {
  64. '& .MuiTreeItem-label': {
  65. background: 'none',
  66. },
  67. },
  68. },
  69. },
  70. },
  71. treeItem: {
  72. '& .MuiTreeItem-iconContainer': {
  73. color: '#888',
  74. },
  75. },
  76. }));
  77. const DatabaseTree: React.FC<DatabaseToolProps> = props => {
  78. // props
  79. const { database, collections, params } = props;
  80. // format tree data
  81. const children = collections.map(c => {
  82. return {
  83. id: `c_${c.collection_name}`,
  84. name: c.collection_name,
  85. type: 'collection' as TreeNodeType,
  86. };
  87. });
  88. // tree data
  89. const tree: DatabaseTreeItem = {
  90. id: database,
  91. name: database,
  92. expanded: children.length > 0,
  93. type: 'db',
  94. children: children,
  95. };
  96. // Icons
  97. const DatabaseIcon = icons.database;
  98. const CollectionIcon = icons.navCollection;
  99. // hooks
  100. const navigate = useNavigate();
  101. const classes = useStyles();
  102. // on node click
  103. const onNodeClick = (node: DatabaseTreeItem) => {
  104. navigate(
  105. node.type === 'db'
  106. ? `/databases/${database}/${params.databasePage || 'collections'}`
  107. : `/databases/${database}/${node.name}/${params.collectionPage || 'data'}`
  108. );
  109. };
  110. // render children
  111. const renderTree = (nodes: DatabaseTreeItem[]) => {
  112. return nodes.map(node => {
  113. if (node.children && node.children.length > 0) {
  114. return (
  115. <TreeItem
  116. key={node.id}
  117. nodeId={node.id}
  118. icon={<CollectionIcon />}
  119. label={node.name}
  120. className={classes.treeItem}
  121. onClick={event => {
  122. event.stopPropagation();
  123. if (onNodeClick) {
  124. onNodeClick(node);
  125. }
  126. }}
  127. >
  128. {renderTree(node.children)}
  129. </TreeItem>
  130. );
  131. }
  132. return (
  133. <TreeItem
  134. key={node.id}
  135. nodeId={node.id}
  136. icon={<CollectionIcon />}
  137. label={node.name}
  138. className={classes.treeItem}
  139. onClick={event => {
  140. event.stopPropagation();
  141. if (onNodeClick) {
  142. onNodeClick(node);
  143. }
  144. }}
  145. />
  146. );
  147. });
  148. };
  149. return (
  150. <TreeView
  151. expanded={[database]}
  152. multiSelect={false}
  153. disableSelection={false}
  154. selected={params.collectionName || params.databaseName}
  155. className={classes.root}
  156. >
  157. {
  158. <TreeItem
  159. key={tree.id}
  160. nodeId={tree.id}
  161. label={tree.name}
  162. className={classes.treeItem}
  163. icon={<DatabaseIcon />}
  164. onClick={event => {
  165. event.stopPropagation();
  166. if (onNodeClick) {
  167. onNodeClick(tree);
  168. }
  169. }}
  170. >
  171. {tree.children && tree.children.length > 0
  172. ? renderTree(tree.children)
  173. : [<div key="stub" />]}
  174. </TreeItem>
  175. }
  176. </TreeView>
  177. );
  178. };
  179. export default DatabaseTree;