Skip to content
This repository was archived by the owner on Jan 10, 2024. It is now read-only.

file tree: open relevant folders on nav #233

Merged
merged 3 commits into from
Dec 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
172 changes: 77 additions & 95 deletions components/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,7 @@ export const Sidebar = ({
if (!tree.current) return;
tree.current.scrollToItem(activeFilePath, "center");
}, 100);
}, []);
useEffect(() => {
if (!tree.current) return;
tree.current.recomputeTree({
useDefaultOpenness: true,
});
}, [searchTerm]);
}, [activeFilePath]);

const nestedFiles = useMemo(() => {
try {
Expand All @@ -82,18 +76,27 @@ export const Sidebar = ({
}, [files]);

const filteredFiles = useMemo(() => {
if (!searchTerm) return nestedFiles;
const lowerSearchTerm = searchTerm.toLowerCase();
return nestedFiles
.map((file) => {
const children = file.children.filter((child) =>
child.name.toLowerCase().includes(lowerSearchTerm)
);
const isMatch = file.name.toLowerCase().includes(lowerSearchTerm);
if (!children.length && !isMatch) return null;
return { ...file, children };
})
.filter(Boolean) as NestedFileTree[];
return [
{
name: `${owner}/${repo}`,
path: "",
children: !searchTerm
? nestedFiles
: (nestedFiles
.map((file) => {
const children = file.children.filter((child) =>
child.name.toLowerCase().includes(lowerSearchTerm)
);
const isMatch = file.name
.toLowerCase()
.includes(lowerSearchTerm);
if (!children.length && !isMatch) return null;
return { ...file, children };
})
.filter(Boolean) as NestedFileTree[]),
},
];
}, [nestedFiles, searchTerm]);

const numberOfFiles = useMemo(() => {
Expand All @@ -106,80 +109,58 @@ export const Sidebar = ({
return runningCount;
}, [filteredFiles]);

const treeWalker = useMemo(
() =>
function* treeWalker(refresh) {
const stack = [];

// Remember all the necessary data of the first node in the stack.
stack.push({
depth: 0,
node: {
id: "/",
name: `${owner}/${repo}`,
path: "",
children: filteredFiles,
},
});
// This function prepares an object for yielding. We can yield an object
// that has `data` object with `id` and `isOpenByDefault` fields.
// We can also add any other data here.
const getNodeData = (node, nestingLevel) => {
const id = node.path || "/";
// don't close open folders
const existingIsOpen = tree.current?.state.records.get(id)?.public.isOpen;
return {
data: {
id,
isLeaf: node.children?.length === 0,
isOpenByDefault:
existingIsOpen ||
activeFilePath === id ||
activeFilePath.startsWith(`${id}/`) ||
numberOfFiles < 20 ||
nestingLevel < 1,
name: node.name,
depth: nestingLevel,
path: node.path,
activeFilePath,
canCollapse: nestingLevel > 0,
updatedContents,
currentPathname: router.pathname,
currentQuery: query,
currentBranch: branchName,
},
nestingLevel,
node,
};
};

function* treeWalker() {
// Step [1]: Define the root node of our tree. There can be one or
// multiple nodes.
for (let i = 0; i < filteredFiles.length; i++) {
yield getNodeData(filteredFiles[i], 0);
}

// Walk through the tree until we have no nodes available.
while (stack.length !== 0) {
const {
node: { children = [], path, name },
depth,
} = stack.pop();
const canCollapse = depth > 0;
const id = path || "/";
while (true) {
// Step [2]: Get the parent component back. It will be the object
// the `getNodeData` function constructed, so you can read any data from it.
const parent = yield;

// Here we are sending the information about the node to the Tree component
// and receive an information about the openness state from it. The
// `refresh` parameter tells us if the full update of the tree is requested;
// basing on it we decide to return the full node data or only the node
// id to update the nodes order.
const isOpened = yield refresh
? {
id,
isLeaf: children.length === 0,
isOpenByDefault:
activeFilePath === id ||
activeFilePath.startsWith(`${id}/`) ||
numberOfFiles < 20 ||
depth < 1,
name,
depth,
path,
activeFilePath,
canCollapse,
updatedContents,
currentPathname: router.pathname,
currentQuery: query,
currentBranch: branchName,
}
: id;
for (let i = 0; i < parent.node.children.length; i++) {
// Step [3]: Yielding all the children of the provided component. Then we
// will return for the step [2] with the first children.
yield getNodeData(parent.node.children[i], parent.nestingLevel + 1);
}
}
}

// Basing on the node openness state we are deciding if we need to render
// the child nodes (if they exist).
if (children.length !== 0 && isOpened) {
// Since it is a stack structure, we need to put nodes we want to render
// first to the end of the stack.
for (let i = children.length - 1; i >= 0; i--) {
stack.push({
depth: depth + 1,
node: children[i],
});
}
}
}
},
[
filteredFiles,
updatedContents,
numberOfFiles < 20,
activeFilePath,
query,
branchName,
]
);
if (!files.map) return null;

return (
Expand All @@ -202,6 +183,7 @@ export const Sidebar = ({
height={dimensions[1]}
width={dimensions[0]}
className="pb-10"
ref={tree}
>
{Node}
</Tree>
Expand All @@ -218,7 +200,6 @@ export const Sidebar = ({

const Node = ({
data: {
isLeaf,
name,
path,
activeFilePath,
Expand All @@ -228,10 +209,11 @@ const Node = ({
currentPathname,
currentQuery,
currentBranch,
isLeaf,
},
isOpen,
style,
toggle,
setOpen,
}) => {
return (
<div style={style} key={path || "/"}>
Expand All @@ -245,7 +227,7 @@ const Node = ({
currentPathname={currentPathname}
currentQuery={currentQuery}
currentBranch={currentBranch}
toggle={toggle}
setOpen={setOpen}
isOpen={isOpen}
isFolder={!isLeaf}
/>
Expand All @@ -265,7 +247,7 @@ type ItemProps = {
canCollapse?: boolean;
isOpen: boolean;
updatedContents: Record<string, unknown>;
toggle: () => void;
setOpen: (newState: boolean) => void;
};

const Item = ({
Expand All @@ -279,7 +261,7 @@ const Item = ({
currentBranch,
isOpen,
updatedContents,
toggle,
setOpen,
}: ItemProps) => {
const isActive = activeFilePath === path;

Expand Down Expand Up @@ -312,7 +294,7 @@ const Item = ({
// don't select the folder on toggle
e.stopPropagation();
e.preventDefault();
toggle();
setOpen(!isOpen);
}}
>
{depth > 0 && (
Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
"react-icons": "^4.3.1",
"react-merge-refs": "^2.0.1",
"react-query": "^3.34.16",
"react-vtree": "^2.0.4",
"react-vtree": "3.0.0-beta.3",
"react-window": "^1.8.7",
"streamifier": "^0.1.1",
"styled-components": "^5.3.3",
Expand Down Expand Up @@ -78,7 +78,8 @@
"resolutions": {
"trim": "0.0.3",
"diff": "3.5.0",
"prismjs": "1.27.0"
"prismjs": "1.27.0",
"@octokit/types": "8.0.0"
},
"volta": {
"node": "16.18.1"
Expand Down
28 changes: 23 additions & 5 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -817,6 +817,11 @@
resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-12.11.0.tgz#da5638d64f2b919bca89ce6602d059f1b52d3ef0"
integrity sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==

"@octokit/openapi-types@^14.0.0":
version "14.0.0"
resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-14.0.0.tgz#949c5019028c93f189abbc2fb42f333290f7134a"
integrity sha512-HNWisMYlR8VCnNurDU6os2ikx0s0VyEjDYHNS/h4cgb8DeOxQ0n72HyinUtdDVxJhFy3FWLGl0DJhfEWk3P5Iw==

"@octokit/plugin-paginate-rest@^2.16.8":
version "2.17.0"
resolved "https://registry.yarnpkg.com/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.17.0.tgz#32e9c7cab2a374421d3d0de239102287d791bce7"
Expand Down Expand Up @@ -868,7 +873,14 @@
"@octokit/plugin-request-log" "^1.0.4"
"@octokit/plugin-rest-endpoint-methods" "^5.12.0"

"@octokit/types@^6.0.0", "@octokit/types@^6.0.3", "@octokit/types@^6.10.0", "@octokit/types@^6.12.2", "@octokit/types@^6.16.1", "@octokit/types@^6.34.0":
"@octokit/[email protected]", "@octokit/types@^6.0.3", "@octokit/types@^6.10.0", "@octokit/types@^6.12.2", "@octokit/types@^6.16.1", "@octokit/types@^6.34.0":
version "8.0.0"
resolved "https://registry.yarnpkg.com/@octokit/types/-/types-8.0.0.tgz#93f0b865786c4153f0f6924da067fe0bb7426a9f"
integrity sha512-65/TPpOJP1i3K4lBJMnWqPUJ6zuOtzhtagDvydAWbEXpbFYA0oMKKyLb95NFZZP0lSh/4b6K+DQlzvYQJQQePg==
dependencies:
"@octokit/openapi-types" "^14.0.0"

"@octokit/types@^6.0.0":
version "6.41.0"
resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.41.0.tgz#e58ef78d78596d2fb7df9c6259802464b5f84a04"
integrity sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==
Expand Down Expand Up @@ -4021,6 +4033,11 @@ react-is@^16.12.0, react-is@^16.13.1, react-is@^16.7.0:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==

react-merge-refs@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/react-merge-refs/-/react-merge-refs-1.1.0.tgz#73d88b892c6c68cbb7a66e0800faa374f4c38b06"
integrity sha512-alTKsjEL0dKH/ru1Iyn7vliS2QRcBp9zZPGoWxUOvRGWPUYgjo+V01is7p04It6KhgrzhJGnIj9GgX8W4bZoCQ==

react-merge-refs@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/react-merge-refs/-/react-merge-refs-2.0.1.tgz#a1f8c2dadefa635333e9b91ec59f30b65228b006"
Expand Down Expand Up @@ -4077,12 +4094,13 @@ react-style-singleton@^2.1.0:
invariant "^2.2.4"
tslib "^1.0.0"

react-vtree@^2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/react-vtree/-/react-vtree-2.0.4.tgz#340e64255f5f4ec6f4c35dc44a7036f7fcd98bc5"
integrity sha512-UOld0VqyAZrryF06K753X4bcEVN6/wW831exvVlMZeZAVHk9KXnlHs4rpqDAeoiBgUwJqoW/rtn0hwsokRRxPA==
react-vtree@3.0.0-beta.3:
version "3.0.0-beta.3"
resolved "https://registry.yarnpkg.com/react-vtree/-/react-vtree-3.0.0-beta.3.tgz#9a2dfc31fa730c39d19b0dff7a9df81ead816bd5"
integrity sha512-BGC8kOT2Ti3rne0Nwu+n90TAo8lbYiWT36Cu47aj6bz+Bs7k5p3EVgBTinyuCdU5+n4a9wJOXHAdop/zsR1RAA==
dependencies:
"@babel/runtime" "^7.11.0"
react-merge-refs "^1.1.0"

react-window@^1.8.7:
version "1.8.7"
Expand Down