Skip to content

Commit

Permalink
Fixed Workspace Tree
Browse files Browse the repository at this point in the history
  • Loading branch information
R1c4rdCo5t4 committed May 18, 2024
1 parent a860e67 commit 494dcd3
Show file tree
Hide file tree
Showing 14 changed files with 200 additions and 169 deletions.
94 changes: 0 additions & 94 deletions code/client/src/domain/workspaces/tree/WorkspaceTree.ts

This file was deleted.

10 changes: 10 additions & 0 deletions code/client/src/domain/workspaces/tree/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { WorkspaceResourceMetadata } from '@notespace/shared/src/workspace/types/resource';

export type TreeNode = {
node: WorkspaceTreeNode;
children: TreeNode[];
};

export type WorkspaceTreeNode = WorkspaceResourceMetadata;

export type WorkspaceTreeNodes = Map<string, WorkspaceTreeNode>;
79 changes: 76 additions & 3 deletions code/client/src/domain/workspaces/tree/useWorkspaceTree.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,81 @@
import { useMemo } from 'react';
import { WorkspaceTree } from '@domain/workspaces/tree/WorkspaceTree';
import { useState } from 'react';
import { rootNode } from '@domain/workspaces/tree/utils';
import { WorkspaceTreeNode } from '@domain/workspaces/tree/types';

function useWorkspaceTree() {
return useMemo(() => new WorkspaceTree(), []);
const [nodes, setNodes] = useState<Map<string, WorkspaceTreeNode>>(new Map());

function setTree(nodes: WorkspaceTreeNode[]) {
const nodesMap = new Map(nodes.map(node => [node.id, node]));
const root = rootNode(
Array.from(nodes?.values() || [])
.filter(node => node.parent === 'root')
.map(node => node.id)
);
nodesMap.set('root', root);
setNodes(nodesMap);
}

function addNode(node: WorkspaceTreeNode) {
const newNodes = new Map(nodes);
const parentNode = newNodes.get(node.parent);
if (!parentNode) throw new Error('Invalid parent id: ' + node.parent);
newNodes.set(node.id, node);
newNodes.set(node.parent, { ...parentNode, children: [...parentNode.children, node.id] });
setNodes(newNodes);
}

function removeNode(id: string) {
const node = nodes.get(id);
if (!node) throw new Error('Invalid id: ' + id);
const { parent } = node;
const parentNode = nodes.get(parent);
if (!parentNode) throw new Error('Invalid parent id: ' + parent);
const newNodes = new Map(nodes);
const index = parentNode.children.indexOf(id);
if (index !== -1) parentNode.children.splice(index, 1);
newNodes.delete(id);
newNodes.set(parent, parentNode);
setNodes(newNodes);
}

function updateNode(id: string, name: string) {
const node = nodes.get(id);
if (!node) throw new Error('Invalid id: ' + id);
const newNode = { ...node, name };
nodes.set(id, newNode);
setNodes(new Map(nodes));
}

function moveNode(id: string, newParent: string) {
const node = nodes.get(id);
if (!node) throw new Error('Invalid id: ' + id);
const { parent } = node;
const parentNode = nodes.get(parent);

if (parentNode) {
const index = parentNode.children.indexOf(node.id);
if (index !== -1) parentNode.children.splice(index, 1);
nodes.set(parent, parentNode);
}
const newParentNode = nodes.get(newParent);
if (!newParentNode) throw new Error('Invalid parent id: ' + newParent);
newParentNode.children.push(node.id);
node.parent = newParent;
nodes.set(id, node);
nodes.set(newParent, newParentNode);
setNodes(new Map(nodes));
}

return {
nodes,
setNodes,
setTree,
addNode,
updateNode,
removeNode,
moveNode,
};
}

export default useWorkspaceTree;
20 changes: 20 additions & 0 deletions code/client/src/domain/workspaces/tree/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { ResourceType } from '@notespace/shared/src/workspace/types/resource';
import { TreeNode, WorkspaceTreeNode } from '@domain/workspaces/tree/types';

export function getTree(nodes: Map<string, WorkspaceTreeNode>, id: string = 'root'): TreeNode {
const root = nodes.get(id)!;
return {
node: root,
children: root.children.map(id => getTree(nodes, id)),
};
}

export function rootNode(children?: string[]): WorkspaceTreeNode {
return {
id: 'root',
name: 'root',
parent: '',
children: children || [],
type: ResourceType.FOLDER,
};
}
24 changes: 21 additions & 3 deletions code/client/src/ui/components/error/Error.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,38 @@
import './Error.scss';
import { MdError } from 'react-icons/md';
import { useEffect, useRef, useState } from 'react';

type ErrorProps = {
error?: Error;
};

function Error({ error }: ErrorProps) {
if (!error) {
const [err, setErr] = useState<Error | undefined>(error);
const errorRef = useRef<HTMLDivElement>(null);

useEffect(() => {
function handleClickOutside(event: MouseEvent) {
if (errorRef.current && !errorRef.current.contains(event.target as Node)) {
setErr(undefined);
}
}

document.addEventListener('mousedown', handleClickOutside);
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, []);

if (!err) {
return null;
}

return (
<div className="error" role="alert">
<div className="error" role="alert" ref={errorRef}>
<span>
<MdError />
</span>
<p>{error.message}</p>
<p>{err.message}</p>
</div>
);
}
Expand Down
4 changes: 2 additions & 2 deletions code/client/src/ui/components/sidebar/Sidebar.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
top: 0;
left: 0;
height: 100vh;
width: 320px;
width: 400px;
display: flex;
flex-direction: column;
align-items: center;
Expand All @@ -22,7 +22,7 @@

li {
list-style-type: none;
padding: 8px 8px 8px 16px;
padding: 8px;
text-decoration: none;
font-size: 14px;
font-weight: 500;
Expand Down
5 changes: 2 additions & 3 deletions code/client/src/ui/components/sidebar/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,10 @@ import useWorkspace from '@ui/contexts/workspace/useWorkspace';
import useSidebarState from '@ui/components/sidebar/hooks/useSidebarState';
import './Sidebar.scss';
import WorkspaceTree from '@ui/components/sidebar/components/WorkspaceTree';
import { useEffect } from 'react';

function Sidebar() {
const { isOpen, isLocked, handleClick, handleMouseEnter, handleMouseLeave } = useSidebarState();
const { workspace, tree } = useWorkspace();
const { workspace, nodes } = useWorkspace();

return (
<div
Expand Down Expand Up @@ -38,7 +37,7 @@ function Sidebar() {
<h3>
<Link to={`/workspaces/${workspace.id}`}>{workspace.name}</Link>
</h3>
<WorkspaceTree workspace={workspace} tree={tree} />
<WorkspaceTree workspace={workspace} nodes={nodes} />
</>
)}
</ul>
Expand Down
13 changes: 10 additions & 3 deletions code/client/src/ui/components/sidebar/components/ResourceView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ import { ResourceType } from '@notespace/shared/src/workspace/types/resource';
import { IoDocumentText } from 'react-icons/io5';
import { FaFolder } from 'react-icons/fa6';
import { WorkspaceTreeNode } from '@domain/workspaces/tree/WorkspaceTree';
import { TreeNode } from '@domain/workspaces/tree/utils';

type ResourceViewProps = {
workspace: string;
resource: WorkspaceTreeNode;
children?: WorkspaceTreeNode[];
children?: TreeNode[];
};

const ResourceComponents = {
Expand All @@ -28,9 +29,15 @@ const ResourceComponents = {
function ResourceView({ resource, workspace, children }: ResourceViewProps) {
const ResourceComponent = ResourceComponents[resource.type];
return (
<div>
<div
style={{
paddingLeft: '1rem',
}}
>
<ResourceComponent workspace={workspace} resource={resource} />
{children?.map(child => <ResourceView key={child.id} workspace={workspace} resource={child} />)}
{children?.map(child => (
<ResourceView key={child.node.id} workspace={workspace} resource={child.node} children={child.children} />
))}
</div>
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
import { WorkspaceTree as Tree } from '@domain/workspaces/tree/WorkspaceTree';
import ResourceView from '@ui/components/sidebar/components/ResourceView';
import { WorkspaceMetaData } from '@notespace/shared/src/workspace/types/workspace';
import { getTree } from '@domain/workspaces/tree/utils';
import { WorkspaceTreeNodes } from '@domain/workspaces/tree/types';

type WorkspaceTreeProps = {
workspace: WorkspaceMetaData;
tree?: Tree;
nodes?: WorkspaceTreeNodes;
};

function WorkspaceTree({ workspace, tree }: WorkspaceTreeProps) {
if (!tree) return null;
function WorkspaceTree({ workspace, nodes }: WorkspaceTreeProps) {
if (!nodes) return null;
return (
<ul>
{Array.from(tree.traverse()).map(([node, children]) => (
<li key={node.id}>
<ResourceView workspace={workspace.id} resource={node} children={children} />
{getTree(nodes).children.map(node => (
<li key={node.node.id}>
<ResourceView workspace={workspace.id} resource={node.node} children={node.children} />
</li>
))}
</ul>
Expand Down
Loading

0 comments on commit 494dcd3

Please sign in to comment.