Skip to content

Commit

Permalink
Merge pull request #599 from nexB/fix/scrolltofile
Browse files Browse the repository at this point in the history
Fix irregular scroll behavior for files in deeply nested FileTree
  • Loading branch information
OmkarPh authored Oct 2, 2023
2 parents 96d85b8 + ab53936 commit a3dd1b5
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 19 deletions.
14 changes: 14 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@
"react-select": "^5.7.3",
"react-toastify": "^9.0.8",
"react-tooltip": "^5.10.4",
"scroll-into-view-if-needed": "^3.1.0",
"sequelize": "^6.23.2",
"sequelize-cli": "^6.5.1",
"sqlite3": "^5.1.6",
Expand Down
5 changes: 1 addition & 4 deletions src/components/FileTree/FileTree.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
padding: 15px;
border: 0;
min-height: 80vh !important;
/* border-right: 2px solid black; */
}

.file-tree-loader {
Expand All @@ -18,9 +17,7 @@
}

.filetree-node-wrapper {
scroll-margin-left: 150px;
scroll-margin-top: 150px;
scroll-margin-bottom: 150px;
scroll-margin-left: 20px;
}

.rc-tree-child-tree {
Expand Down
66 changes: 51 additions & 15 deletions src/components/FileTree/FileTree.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import RcTree from "rc-tree";
import { Key } from "rc-tree/lib/interface";
import React, { useEffect, useState } from "react";
import React, { useEffect, useLayoutEffect, useState } from "react";
import { Element } from "react-scroll";
import scrollIntoView from "scroll-into-view-if-needed";

import EllipticLoader from "../EllipticLoader";
import SwitcherIcon from "./SwitcherIcon";
Expand All @@ -10,38 +11,76 @@ import { FileDataNode } from "../../services/workbenchDB";

import "./FileTree.css";

const FOCUS_ATTEMPT_DELAY = 500;

const FileTree = (props: React.HTMLProps<HTMLDivElement>) => {
const {
db,
initialized,
importedSqliteFilePath,
currentPath,
startProcessing,
endProcessing,
updateCurrentPath,
} = useWorkbenchDB();

const [treeData, setTreeData] = useState<FileDataNode[] | null>(null);
const [expandedKeys, setExpandedKeys] = useState<Key[]>([]);

useEffect(() => {
useLayoutEffect(() => {
if (currentPath.length === 0) return;

// Show working indicator while the FileTree Node is being rendered and focused
startProcessing();

setExpandedKeys((keys) => {
return [...keys, currentPath.substring(0, currentPath.lastIndexOf("/"))];
});
if (currentPath.length) {
setTimeout(() => {

function scrollTreeNode(targetNode: HTMLElement) {
scrollIntoView(targetNode, {
scrollMode: "if-needed",
behavior: "smooth",
block: "center",
inline: "start",
});
}

// Timeout ensures that targetNode is accessed only after its rendered
let pendingScrollerTimeoutId: NodeJS.Timeout;

const alreadyRenderedTargetNode =
document.getElementsByName(currentPath)[0];

if (alreadyRenderedTargetNode) {
// Immediate scroll possible
scrollTreeNode(alreadyRenderedTargetNode);
} else {
// Wait for target node to render
pendingScrollerTimeoutId = setTimeout(() => {
const targetNode = document.getElementsByName(currentPath)[0];

if (targetNode) {
targetNode.scrollIntoView({
behavior: "smooth",
block: "nearest",
inline: "start",
});
pendingScrollerTimeoutId = setTimeout(() => {
scrollTreeNode(targetNode);
}, FOCUS_ATTEMPT_DELAY);

// Hide working indicator after the FileTree Node is focused
endProcessing();
}
}, 1500);
});
}

return () => {
clearTimeout(pendingScrollerTimeoutId);
};
}, [currentPath]);

useEffect(() => {
if (!initialized || !db || !importedSqliteFilePath) return;
if (!initialized || !db || !importedSqliteFilePath) {
setTreeData(null);
return;
}

db.findAllJSTree().then((treeData) => {
// Wrap with react-scroll wrapper
Expand Down Expand Up @@ -98,10 +137,7 @@ const FileTree = (props: React.HTMLProps<HTMLDivElement>) => {
selectedKeys={[currentPath]}
onSelect={(keys, info) => {
if (keys && keys[0]) {
selectPath(
keys[0].toString(),
(info.node as any as FileDataNode).type as PathType
);
selectPath(keys[0].toString(), info.node.type as PathType);
}
}}
motion={{
Expand Down

0 comments on commit a3dd1b5

Please sign in to comment.