datajunction-ui
Version:
DataJunction UI
99 lines (96 loc) • 3.17 kB
JSX
import React, { useState } from 'react';
import ChevronIcon from '../../icons/ChevronIcon';
// Recursive folder tree for the namespace rail. `nodes` are hierarchy nodes
// ({ namespace, path, children }) — the descendants of the current namespace.
// Clicking the chevron expands/collapses that level in place (no navigation);
// clicking the name navigates into it (onSelect(path)). Collapsed beyond the
// top level so a deep tree only grows on demand.
function FolderTreeRows({ nodes, depth, expanded, onToggle, onSelect }) {
return nodes.map(node => {
const hasChildren = node.children?.length > 0;
const isOpen = expanded.has(node.path);
return (
<React.Fragment key={node.path}>
<div
className="dj-ns-nav-item"
role="button"
tabIndex={0}
title={node.path}
style={{ paddingLeft: `${depth * 14}px` }}
onClick={() => onSelect(node.path)}
onKeyDown={e => {
if (e.key === 'Enter') onSelect(node.path);
}}
>
{hasChildren ? (
<button
type="button"
aria-label={isOpen ? 'Collapse' : 'Expand'}
aria-expanded={isOpen}
onClick={e => {
e.stopPropagation();
onToggle(node.path);
}}
style={{
width: '18px',
minWidth: '18px',
flexShrink: 0,
padding: 0,
margin: 0,
border: 'none',
background: 'none',
cursor: 'pointer',
color: '#64748b',
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
<ChevronIcon open={isOpen} />
</button>
) : (
<span style={{ width: '18px', minWidth: '18px', flexShrink: 0 }} />
)}
<span className="dj-ns-nav-name">{node.namespace}</span>
</div>
{hasChildren && isOpen ? (
<FolderTreeRows
nodes={node.children}
depth={depth + 1}
expanded={expanded}
onToggle={onToggle}
onSelect={onSelect}
/>
) : null}
</React.Fragment>
);
});
}
// Rail folder tree rooted at the current namespace's children. Remount it
// (via a `key` on the current path) to reset expansion when the root changes.
export default function FolderTree({ folders, onSelect }) {
const [expanded, setExpanded] = useState(() => new Set());
if (!folders || folders.length === 0) return null;
const onToggle = path =>
setExpanded(prev => {
const next = new Set(prev);
if (next.has(path)) {
next.delete(path);
} else {
next.add(path);
}
return next;
});
return (
<div className="dj-ns-folder-nav">
<div className="dj-ns-tree-heading">Folders</div>
<FolderTreeRows
nodes={folders}
depth={0}
expanded={expanded}
onToggle={onToggle}
onSelect={onSelect}
/>
</div>
);
}