Skip to content

Commit

Permalink
Implemented Workspace Tree Collapsing & Expanding
Browse files Browse the repository at this point in the history
  • Loading branch information
R1c4rdCo5t4 committed May 19, 2024
1 parent c4b0d11 commit d2b4a4d
Show file tree
Hide file tree
Showing 13 changed files with 186 additions and 48 deletions.
8 changes: 4 additions & 4 deletions code/client/src/domain/editor/operations/input/operations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { nodeInsert } from '@domain/editor/crdt/utils';
import { InlineStyle } from '@notespace/shared/src/document/types/styles';
import { Operation } from '@notespace/shared/src/document/types/operations';
import { Communication } from '@services/communication/communication';
import {isEqual} from "lodash";
import { isEqual } from 'lodash';

export default (fugue: Fugue, { socket }: Communication): InputDomainOperations => {
function insertCharacter(char: string, cursor: Cursor, styles: InlineStyle[] = []) {
Expand All @@ -29,10 +29,10 @@ export default (fugue: Fugue, { socket }: Communication): InputDomainOperations
}

function deleteSelection(selection: Selection) {
if(isEqual(selection.start, selection.end)) return;
if (isEqual(selection.start, selection.end)) return;

if(selection.start.column === 0) selection.start.column += 1;
if(selection.end.column === 0) selection.end.column += 1;
if (selection.start.column === 0) selection.start.column += 1;
if (selection.end.column === 0) selection.end.column += 1;

const operations = fugue.deleteLocal(selection);
socket.emit('operations', operations);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,14 +100,16 @@ function toHistoryOperations(editor: Editor, operations: Batch | undefined, reve
* @param operation
* @param selectionBefore
*/
function removeTextOperation(operation: BaseRemoveTextOperation, selectionBefore : BaseRange | null): RemoveTextOperation | undefined {
function removeTextOperation(
operation: BaseRemoveTextOperation,
selectionBefore: BaseRange | null
): RemoveTextOperation | undefined {
const offset = (line: number) => (line === 0 ? 0 : 1);

if (operation.text === '') return undefined;
if(!selectionBefore) return undefined;

const cursor = pointToCursor(editor, {...selectionBefore?.anchor});
if (!selectionBefore) return undefined;

const cursor = pointToCursor(editor, { ...selectionBefore?.anchor });

const start = {
line: operation.path[0],
Expand Down Expand Up @@ -137,8 +139,8 @@ function toHistoryOperations(editor: Editor, operations: Batch | undefined, reve

// Remove whole line
if (operation.path.length === 1) {
const start = { line: operation.path[0], column: 0}
const end = { line: operation.path[0], column: Infinity}
const start = { line: operation.path[0], column: 0 };
const end = { line: operation.path[0], column: Infinity };

const selection = { start, end };
return {
Expand Down
5 changes: 5 additions & 0 deletions code/client/src/domain/workspaces/tree/useWorkspaceTree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ function useWorkspaceTree() {
setNodes(nodesMap);
}

function getNode(id: string) {
return nodes.get(id);
}

function addNode(node: WorkspaceTreeNode) {
const newNodes = new Map(nodes);
const parentNode = newNodes.get(node.parent);
Expand Down Expand Up @@ -71,6 +75,7 @@ function useWorkspaceTree() {
nodes,
setNodes,
setTree,
getNode,
addNode,
updateNode,
removeNode,
Expand Down
83 changes: 78 additions & 5 deletions code/client/src/ui/components/sidebar/Sidebar.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
top: 0;
left: 0;
height: 100vh;
width: 400px;
width: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
overflow-x: hidden;
overflow-y: scroll;
transition: 0.5s;
Expand All @@ -18,19 +19,25 @@
ul {
margin: 0;
padding: 0;
width: 90%;
}

li {
list-style-type: none;
padding: 8px;
text-decoration: none;
font-size: 14px;
font-size: 16px;
font-weight: 500;
display: block;
transition: 0.3s;

display: flex;
flex-direction: row;
justify-content: start;
align-items: center;
gap: 10px;
}

button {
> button {
position: fixed;
top: 1vh;
left: 1vh;
Expand All @@ -41,7 +48,7 @@
padding: 0;
margin: 0;
transition: transform 1s ease-in-out;
z-index: 5 !important;
z-index: 1;

.icon {
transition: opacity 1s ease-in-out;
Expand All @@ -55,4 +62,70 @@
button:hover {
color: dimgray;
}

.workspace-tree {
li {
padding: 0;
}
}

.resource {
display: flex;
flex-direction: column;
width: 100%;

.resource-header {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
transition: 0.1s;

div {
display: flex;
flex-direction: row;
justify-content: start;
border-radius: 5px;
user-select: none;
}
}

.resource-header:hover {
background-color: rgba(0, 0, 0, 0.1);
cursor: pointer;
}

.resource-header > button {
display: none;
}

.resource-header:hover > button {
display: flex;
align-items: center;
padding: 0;
margin: 1vh;
}

.resource-header > button:hover {
color: gray;
}

.resource-children {
padding-left: 10px;
}

a {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
}

button {
background-color: transparent;
color: black;
padding: 5px;
margin: 0;
}
}
}
31 changes: 22 additions & 9 deletions code/client/src/ui/components/sidebar/Sidebar.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
import { IoMenu } from 'react-icons/io5';
import { IoMenu, IoTime } from 'react-icons/io5';
import { Link } from 'react-router-dom';
import { RiMenuFold2Line, RiMenuFoldLine } from 'react-icons/ri';
import { RiMenuFold2Line, RiMenuFoldLine, RiTeamFill } from 'react-icons/ri';
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 { FaHome } from 'react-icons/fa';
import { IoMdSettings } from 'react-icons/io';

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

return (
<div
className="sidebar"
style={{ width: isOpen ? '15%' : '0', transition: '0.3s' }}
style={{ width: isOpen ? '20%' : '0', transition: '0.3s' }}
onMouseLeave={handleMouseLeave}
>
<button onMouseEnter={handleMouseEnter} onClick={handleClick}>
Expand All @@ -27,17 +29,28 @@ function Sidebar() {
</button>
<ul>
<li>
<FaHome />
<Link to="/">Home</Link>
</li>
<li>Recent</li>
<li>Settings</li>
{workspace && (
<li>
<RiTeamFill />
<Link to="/workspaces">Workspaces</Link>
</li>
<li>
<IoTime />
<Link to="/recent">Recent</Link>
</li>
<li>
<IoMdSettings />
<Link to="/settings">Settings</Link>
</li>
<hr />
{workspace && operations && (
<>
<hr />
<h3>
<Link to={`/workspaces/${workspace.id}`}>{workspace.name}</Link>
</h3>
<WorkspaceTree workspace={workspace} nodes={nodes} />
<WorkspaceTree workspace={workspace} nodes={nodes} operations={operations} />
</>
)}
</ul>
Expand Down
46 changes: 34 additions & 12 deletions code/client/src/ui/components/sidebar/components/ResourceView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@ import { Link } from 'react-router-dom';
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';
import { TreeNode, WorkspaceTreeNode } from '@domain/workspaces/tree/types';
import { useState } from 'react';
import { RiArrowDownSFill, RiArrowRightSFill } from 'react-icons/ri';
import { FaPlusSquare } from 'react-icons/fa';

type ResourceViewProps = {
workspace: string;
resource: WorkspaceTreeNode;
onCreateResource: (parent?: string) => void;
children?: TreeNode[];
};

Expand All @@ -26,18 +29,37 @@ const ResourceComponents = {
),
};

function ResourceView({ resource, workspace, children }: ResourceViewProps) {
function ResourceView({ resource, workspace, children, onCreateResource }: ResourceViewProps) {
const [isOpen, setIsOpen] = useState(true);
const ResourceComponent = ResourceComponents[resource.type];

const handleToggle = () => {
setIsOpen(!isOpen);
};

return (
<div
style={{
paddingLeft: '1rem',
}}
>
<ResourceComponent workspace={workspace} resource={resource} />
{children?.map(child => (
<ResourceView key={child.node.id} workspace={workspace} resource={child.node} children={child.children} />
))}
<div className="resource">
<div className="resource-header">
<div>
<button onClick={handleToggle}>{isOpen ? <RiArrowDownSFill /> : <RiArrowRightSFill />}</button>
<ResourceComponent workspace={workspace} resource={resource} onCreateResource={onCreateResource} />
</div>
<button onClick={() => onCreateResource(resource.id)}>
<FaPlusSquare />
</button>
</div>
<div className="resource-children">
{isOpen &&
children?.map(child => (
<ResourceView
key={child.node.id}
workspace={workspace}
resource={child.node}
children={child.children}
onCreateResource={onCreateResource}
/>
))}
</div>
</div>
);
}
Expand Down
19 changes: 16 additions & 3 deletions code/client/src/ui/components/sidebar/components/WorkspaceTree.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,32 @@ 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';
import { ResourceOperationsType } from '@ui/contexts/workspace/WorkspaceContext';
import { ResourceType } from '@notespace/shared/src/workspace/types/resource';

type WorkspaceTreeProps = {
workspace: WorkspaceMetaData;
operations: ResourceOperationsType;
nodes?: WorkspaceTreeNodes;
};

function WorkspaceTree({ workspace, nodes }: WorkspaceTreeProps) {
function WorkspaceTree({ workspace, nodes, operations }: WorkspaceTreeProps) {
if (!nodes) return null;

async function onCreateResource(parent?: string) {
await operations.createResource('Untitled', ResourceType.DOCUMENT, parent);
}

return (
<ul>
<ul className="workspace-tree">
{getTree(nodes).children.map(node => (
<li key={node.node.id}>
<ResourceView workspace={workspace.id} resource={node.node} children={node.children} />
<ResourceView
workspace={workspace.id}
resource={node.node}
children={node.children}
onCreateResource={onCreateResource}
/>
</li>
))}
</ul>
Expand Down
2 changes: 1 addition & 1 deletion code/client/src/ui/contexts/workspace/WorkspaceContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { ResourceType, WorkspaceResource } from '@notespace/shared/src/workspace
import { WorkspaceTreeNodes } from '@domain/workspaces/tree/types';

export type ResourceOperationsType = {
createResource: (name: string, type: ResourceType) => Promise<void>;
createResource: (name: string, type: ResourceType, parent?: string) => Promise<void>;
deleteResource: (id: string) => Promise<void>;
updateResource: (id: string, newProps: Partial<WorkspaceResource>) => Promise<void>;
};
Expand Down
8 changes: 7 additions & 1 deletion code/client/src/ui/contexts/workspace/useResources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,13 @@ function useResources() {
}

function onDeleteResource(id: string) {
setResources(resources.filter(resource => resource.id !== id));
const getChildren = (id: string): string[] => {
const resource = tree.getNode(id);
if (!resource) return [];
return [id, ...resource.children.flatMap(childId => getChildren(childId))];
};
const idsToRemove = getChildren(id);
setResources(resources.filter(resource => !idsToRemove.includes(resource.id)));
tree.removeNode(id);
}

Expand Down
2 changes: 1 addition & 1 deletion code/client/src/ui/pages/home/Home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ function Home() {
key={workspace.id}
workspace={workspace}
onDelete={() => deleteWorkspace(workspace.id).catch(publishError)}
onRename={name => updateWorkspace(workspace.id, { ...workspace, name: name + '-copy' }).catch(publishError)}
onRename={name => updateWorkspace(workspace.id, { ...workspace, name }).catch(publishError)}
onInvite={() => {}}
/>
))}
Expand Down
Loading

0 comments on commit d2b4a4d

Please sign in to comment.