1
0
mirror of https://github.com/sasjs/server.git synced 2025-12-10 11:24:35 +00:00

chore: add custom tree view component

This commit is contained in:
2022-07-18 22:32:10 +05:00
parent 30d7a65358
commit 849b2dd468
3 changed files with 156 additions and 0 deletions

138
web/src/components/tree.tsx Normal file
View File

@@ -0,0 +1,138 @@
import React, { useEffect, useState } from 'react'
import { Menu, MenuItem } from '@mui/material'
import ExpandMoreIcon from '@mui/icons-material/ExpandMore'
import ChevronRightIcon from '@mui/icons-material/ChevronRight'
import { TreeNode } from '../utils/types'
type TreeViewProps = {
node: TreeNode
selectedFilePath: string
handleSelect: (filePath: string) => void
defaultExpanded?: string[]
}
const TreeView = ({
node,
selectedFilePath,
handleSelect,
defaultExpanded
}: TreeViewProps) => {
return (
<ul
style={{
listStyle: 'none',
padding: '0.75rem 1.25rem',
width: 'max-content'
}}
>
<TreeViewNode
node={node}
selectedFilePath={selectedFilePath}
handleSelect={handleSelect}
defaultExpanded={defaultExpanded}
/>
</ul>
)
}
export default TreeView
type TreeViewNodeProps = {
node: TreeNode
selectedFilePath: string
handleSelect: (filePath: string) => void
defaultExpanded?: string[]
}
const TreeViewNode = ({
node,
selectedFilePath,
handleSelect,
defaultExpanded
}: TreeViewNodeProps) => {
const [childVisible, setChildVisibility] = useState(false)
const [contextMenu, setContextMenu] = useState<{
mouseX: number
mouseY: number
} | null>(null)
const handleContextMenu = (event: React.MouseEvent) => {
event.preventDefault()
event.stopPropagation()
setContextMenu(
contextMenu === null
? {
mouseX: event.clientX + 2,
mouseY: event.clientY - 6
}
: null
)
}
const hasChild = node.children.length ? true : false
const handleItemClick = () => {
if (node.children.length) {
setChildVisibility((v) => !v)
return
}
if (!node.name.includes('.')) return
handleSelect(node.relativePath)
}
useEffect(() => {
if (defaultExpanded && defaultExpanded[0] === node.relativePath) {
setChildVisibility(true)
defaultExpanded.shift()
}
}, [defaultExpanded, node.relativePath])
return (
<div onContextMenu={handleContextMenu} style={{ cursor: 'context-menu' }}>
<li style={{ display: 'list-item' }}>
<div
className={`tree-item-label ${
selectedFilePath === node.relativePath ? 'selected' : ''
}`}
onClick={() => handleItemClick()}
>
{hasChild &&
(childVisible ? <ExpandMoreIcon /> : <ChevronRightIcon />)}
<div>{node.name}</div>
</div>
{hasChild &&
childVisible &&
node.children.map((child, index) => (
<TreeView
key={node.relativePath + '-' + index}
node={child}
selectedFilePath={selectedFilePath}
handleSelect={handleSelect}
defaultExpanded={defaultExpanded}
/>
))}
</li>
<Menu
open={contextMenu !== null}
onClose={() => setContextMenu(null)}
anchorReference="anchorPosition"
anchorPosition={
contextMenu !== null
? { top: contextMenu.mouseY, left: contextMenu.mouseX }
: undefined
}
>
{hasChild &&
['Add Folder', 'Add File'].map((item) => (
<MenuItem key={item}>{item}</MenuItem>
))}
<MenuItem>Rename</MenuItem>
<MenuItem>Delete</MenuItem>
</Menu>
</div>
)
}

View File

@@ -25,3 +25,15 @@ code {
padding: '5px 10px';
margin-top: '10px';
}
.tree-item-label {
display: flex;
}
.tree-item-label.selected {
background: lightgoldenrodyellow;
}
.tree-item-label:hover {
background: lightgray;
}

View File

@@ -30,3 +30,9 @@ export interface RegisterPermissionPayload {
principalType: string
principalId: number
}
export interface TreeNode {
name: string
relativePath: string
children: Array<TreeNode>
}