Migrate FileTree

This commit is contained in:
2024-10-09 22:34:19 +02:00
parent 859c460d84
commit 8fdc8daa0f
3 changed files with 117 additions and 47 deletions

View File

@@ -21,6 +21,7 @@
"@mantine/hooks": "^7.13.2",
"@mantine/modals": "^7.13.2",
"@mantine/notifications": "^7.13.2",
"@react-hook/resize-observer": "^2.0.2",
"@tabler/icons-react": "^3.19.0",
"codemirror": "^6.0.1",
"katex": "^0.16.11",
@@ -2568,6 +2569,37 @@
"integrity": "sha512-Pc/AFTdwZwEKJxFJvlxrSmGe/di+aAOBn60sremrpLo6VI/6cmiUYNNwlI5KNYttg7uypzA3ILPMPgxB2GYZEg==",
"license": "MIT"
},
"node_modules/@react-hook/latest": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@react-hook/latest/-/latest-1.0.3.tgz",
"integrity": "sha512-dy6duzl+JnAZcDbNTfmaP3xHiKtbXYOaz3G51MGVljh548Y8MWzTr+PHLOfvpypEVW9zwvl+VyKjbWKEVbV1Rg==",
"license": "MIT",
"peerDependencies": {
"react": ">=16.8"
}
},
"node_modules/@react-hook/passive-layout-effect": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@react-hook/passive-layout-effect/-/passive-layout-effect-1.2.1.tgz",
"integrity": "sha512-IwEphTD75liO8g+6taS+4oqz+nnroocNfWVHWz7j+N+ZO2vYrc6PV1q7GQhuahL0IOR7JccFTsFKQ/mb6iZWAg==",
"license": "MIT",
"peerDependencies": {
"react": ">=16.8"
}
},
"node_modules/@react-hook/resize-observer": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/@react-hook/resize-observer/-/resize-observer-2.0.2.tgz",
"integrity": "sha512-tzKKzxNpfE5TWmxuv+5Ae3IF58n0FQgQaWJmcbYkjXTRZATXxClnTprQ2uuYygYTpu1pqbBskpwMpj6jpT1djA==",
"license": "MIT",
"dependencies": {
"@react-hook/latest": "^1.0.2",
"@react-hook/passive-layout-effect": "^1.2.0"
},
"peerDependencies": {
"react": ">=18"
}
},
"node_modules/@tabler/icons": {
"version": "3.19.0",
"resolved": "https://registry.npmjs.org/@tabler/icons/-/icons-3.19.0.tgz",

View File

@@ -34,6 +34,7 @@
"@mantine/hooks": "^7.13.2",
"@mantine/modals": "^7.13.2",
"@mantine/notifications": "^7.13.2",
"@react-hook/resize-observer": "^2.0.2",
"@tabler/icons-react": "^3.19.0",
"codemirror": "^6.0.1",
"katex": "^0.16.11",

View File

@@ -1,65 +1,102 @@
import React from 'react';
import React, { useRef, useState, useLayoutEffect } from 'react';
import { Tree } from 'react-arborist';
import { Group, Text } from '@mantine/core';
import { IconFile, IconFolder, IconFolderOpen } from '@tabler/icons-react';
import { isImageFile } from '../utils/fileHelpers';
import { Tooltip } from '@mantine/core';
import useResizeObserver from '@react-hook/resize-observer';
const FileIcon = ({ isFolder, isOpen }) => {
if (isFolder) {
return isOpen ? (
<IconFolderOpen size={16} color="var(--mantine-color-yellow-filled)" />
) : (
<IconFolder size={16} color="var(--mantine-color-yellow-filled)" />
);
const useSize = (target) => {
const [size, setSize] = useState();
useLayoutEffect(() => {
setSize(target.current.getBoundingClientRect());
}, [target]);
useResizeObserver(target, (entry) => setSize(entry.contentRect));
return size;
};
const FileIcon = ({ node }) => {
if (node.isLeaf) {
return <IconFile size={16} />;
}
return <IconFile size={16} />;
return node.isOpen ? (
<IconFolderOpen size={16} color="var(--mantine-color-yellow-filled)" />
) : (
<IconFolder size={16} color="var(--mantine-color-yellow-filled)" />
);
};
const Node = ({ node, style, dragHandle }) => {
const isFolder = Array.isArray(node.data.children);
return (
<Group
ref={dragHandle}
style={style}
pl={node.level * 20}
py={4}
onClick={() => node.toggle()}
sx={(theme) => ({
cursor: 'pointer',
'&:hover': {
backgroundColor:
theme.colorScheme === 'dark'
? theme.colors.dark[6]
: theme.colors.gray[0],
},
})}
>
<FileIcon isFolder={isFolder} isOpen={node.isOpen} />
<Text size="sm">{node.data.name}</Text>
</Group>
<Tooltip label={node.data.name} openDelay={500}>
<div
ref={dragHandle}
style={{
...style,
paddingLeft: `${node.level * 20}px`,
display: 'flex',
alignItems: 'center',
cursor: 'pointer',
whiteSpace: 'nowrap',
overflow: 'hidden',
}}
onClick={() => {
if (node.isInternal) {
node.toggle();
} else {
node.tree.props.onNodeClick(node);
}
}}
>
<FileIcon node={node} />
<span
style={{
marginLeft: '8px',
fontSize: '14px',
overflow: 'hidden',
textOverflow: 'ellipsis',
flexGrow: 1,
}}
>
{node.data.name}
</span>
</div>
</Tooltip>
);
};
const FileTree = ({ files, handleFileSelect }) => {
const handleNodeClick = (node) => {
if (!node.isInternal) {
handleFileSelect(node.data.path);
}
};
const target = useRef(null);
const size = useSize(target);
return (
<Tree
data={files}
openByDefault={false}
width="100%"
height={400} // Adjust this value as needed
indent={24}
rowHeight={28}
onActivate={handleNodeClick}
<div
ref={target}
style={{ height: 'calc(100vh - 140px)', marginTop: '20px' }}
>
{Node}
</Tree>
{size && (
<Tree
data={files}
openByDefault={false}
width={size.width}
height={size.height}
indent={24}
rowHeight={28}
onActivate={(node) => {
if (!node.isInternal) {
handleFileSelect(node.data.path);
}
}}
onNodeClick={(node) => {
if (!node.isInternal) {
handleFileSelect(node.data.path);
}
}}
>
{Node}
</Tree>
)}
</div>
);
};