This commit is contained in:
Martti Malmi 2023-08-30 19:39:19 +03:00
parent e409d244d9
commit 3642f285cf

View File

@ -1,63 +1,107 @@
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { ChevronRightIcon } from '@heroicons/react/20/solid';
import Node from '@/state/Node'; import Show from '@/components/helpers/Show.tsx';
import Node, { DIR_VALUE } from '@/state/Node';
type Props = { type Props = {
node: Node; node: Node;
value?: any;
level?: number; level?: number;
expanded?: boolean; expanded?: boolean;
name?: string; name?: string;
parentCounter?: number;
}; };
export default function ExplorerNode({ node, level = 0, expanded = false, name }: Props) { const VALUE_TRUNCATE_LENGTH = 50;
const [children, setChildren] = useState<{ [key: string]: Node }>({});
export default function ExplorerNode({
node,
value = DIR_VALUE,
level = 0,
expanded = false,
name,
parentCounter = 0,
}: Props) {
const [children, setChildren] = useState<{ [key: string]: { node: Node; value: any } }>({});
const [isOpen, setIsOpen] = useState(expanded); const [isOpen, setIsOpen] = useState(expanded);
const [showMore, setShowMore] = useState(false);
const isDirectory = value === DIR_VALUE;
useEffect(() => { useEffect(() => {
node.map((_value, key) => { if (!isDirectory) return;
return node.map((value, key) => {
if (!children[key]) { if (!children[key]) {
setChildren((prev) => { const childName = key.split('/').pop()!;
const childName = key.split('/').pop()!; setChildren((prev) => ({
return { ...prev, [key]: node.get(childName) }; ...prev,
}); [key]: { node: node.get(childName), value },
}));
} }
}); });
}, [node.id]); }, [node.id, value]);
const toggleOpen = () => { const toggleOpen = () => setIsOpen(!isOpen);
setIsOpen(!isOpen); const rowColor = parentCounter % 2 === 0 ? 'bg-gray-800' : 'bg-gray-700';
};
const isEven = level % 2 === 0;
const displayName = name || node.id.split('/').pop()!; const displayName = name || node.id.split('/').pop()!;
const renderValue = (value) => {
if (typeof value === 'string') {
return (
<span className="text-xs text-blue-400">
{value.length > VALUE_TRUNCATE_LENGTH && (
<span
className="text-xs text-blue-200 cursor-pointer"
onClick={() => setShowMore(!showMore)}
>
Show {showMore ? 'less' : 'more'}{' '}
</span>
)}
"
{showMore
? value
: value.length > VALUE_TRUNCATE_LENGTH
? `${value.substring(0, VALUE_TRUNCATE_LENGTH)}...`
: value}
"
</span>
);
}
return <span className="text-xs text-green-400">{JSON.stringify(value)}</span>;
};
return ( return (
<div className={`relative ${isEven ? 'bg-gray-800' : 'bg-gray-700'}`}> <div className={`relative w-full ${rowColor}`}>
<div className="flex items-center cursor-pointer text-white" onClick={toggleOpen}> <div
<div className={`transition ${isOpen ? 'transform rotate-90' : ''}`}> className={`flex items-center text-white ${isDirectory ? 'cursor-pointer' : null}`}
<svg onClick={toggleOpen}
className="w-4 h-4" style={{ paddingLeft: `${level * 15}px` }}
xmlns="http://www.w3.org/2000/svg" >
fill="none" <Show when={isDirectory}>
viewBox="0 0 24 24" <ChevronRightIcon
stroke="currentColor" className={`w-4 h-4 transition ${isOpen ? 'transform rotate-90' : ''}`}
aria-hidden="true" />
> </Show>
<path <span className="ml-2 w-1/3 truncate">{displayName}</span>
strokeLinecap="round" <Show when={!isDirectory}>
strokeLinejoin="round" <div className="ml-auto w-1/2">{renderValue(value)}</div>
strokeWidth="2" </Show>
d="M9 5l7 7-7 7" </div>
></path> {isOpen ? (
</svg> <div>
{Object.values(children).map((child, index) => (
<ExplorerNode
key={node.id + child.node.id}
node={child.node}
level={level + 1}
expanded={false}
value={child.value}
parentCounter={parentCounter + index + 1}
/>
))}
</div> </div>
<span className="ml-2">{displayName}</span> ) : null}
</div>
<div className={`ml-6 ${isOpen ? 'block' : 'hidden'}`}>
{Object.values(children).map((child) => (
<ExplorerNode key={node.id + child.id} node={child} level={level + 1} expanded={false} />
))}
</div>
</div> </div>
); );
} }