Skip to content

Commit

Permalink
Progress on Workspace Management
Browse files Browse the repository at this point in the history
  • Loading branch information
R1c4rdCo5t4 committed May 23, 2024
1 parent f516eee commit ac92b9c
Show file tree
Hide file tree
Showing 30 changed files with 465 additions and 159 deletions.
1 change: 1 addition & 0 deletions code/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"@emotion/react": "^11.11.4",
"@emotion/styled": "^11.11.5",
"@mui/material": "^5.15.16",
"@mui/styles": "^5.15.18",
"@notespace/shared": "file:..\\shared",
"@testing-library/jest-dom": "^6.4.5",
"dotenv": "^16.4.5",
Expand Down
10 changes: 0 additions & 10 deletions code/client/src/App.scss
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,3 @@
}
}
}

.button > * {
text-transform: capitalize;
color: white;
transition: color 0.3s;
}

.button:hover > * {
color: lightgray;
}
16 changes: 15 additions & 1 deletion code/client/src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
color-scheme: light dark;
color: rgba(255, 255, 255, 0.87);
background-color: #f7f9fa;
--blue: #1976d2;

font-synthesis: none;
text-rendering: optimizeLegibility;
Expand Down Expand Up @@ -39,11 +40,24 @@ h1 {
}

button {
color: white;
background-color: black;
border-radius: 8px;
cursor: pointer;
padding: 2vh;
border: none;
display: flex;
align-items: center;
justify-content: center;
gap: 5vh;
padding: 2vh;
}

.MuiCheckbox-root {
transition: background-color 0.2s;
}

.MuiCheckbox-root:hover {
background-color: rgba(0, 0, 0, 0.05) !important;
}

@media (prefers-color-scheme: light) {
Expand Down
40 changes: 40 additions & 0 deletions code/client/src/ui/components/data-table/DataTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import useSortableData from '@ui/components/data-table/useSortableData';

type DataTableProps = {
columns: string[];
data: string[];
};

function DataTable({ columns, data }: DataTableProps) {
if (!data.length) throw new Error('No data provided');

const { items, sort, sortConfig } = useSortableData(data);
return (
<table>
<thead>
<tr>
{columns.map(column => (
<th
key={column}
onClick={() => sort(column)}
className={sortConfig && sortConfig.key === column ? sortConfig.direction : undefined}
>
{column}
</th>
))}
</tr>
</thead>
<tbody>
{items.map((item, index) => (
<tr key={index}>
{columns.map(column => (
<td key={column}>{item}</td>
))}
</tr>
))}
</tbody>
</table>
);
}

export default DataTable;
35 changes: 35 additions & 0 deletions code/client/src/ui/components/data-table/useSortableData.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { useState, useMemo } from 'react';

type SortConfig = {
key: string;
direction: 'asc' | 'desc';
};

function useSortableData(items: any[], config: SortConfig | null = null) {
const [sortConfig, setSortConfig] = useState<SortConfig | null>(config);

const sortedItems = useMemo(() => {
const sortableItems = [...items];
if (sortConfig !== null) {
sortableItems.sort((a, b) => {
if (a[sortConfig.key] < b[sortConfig.key]) {
return sortConfig.direction === 'asc' ? -1 : 1;
}
if (a[sortConfig.key] > b[sortConfig.key]) {
return sortConfig.direction === 'asc' ? 1 : -1;
}
return 0;
});
}
return sortableItems;
}, [items, sortConfig]);

const sort = (key: string) => {
const direction = sortConfig && sortConfig.key === key && sortConfig.direction === 'asc' ? 'desc' : 'asc';
setSortConfig({ key, direction });
};

return { items: sortedItems, sort, sortConfig };
}

export default useSortableData;
4 changes: 0 additions & 4 deletions code/client/src/ui/components/dialog/Dialog.scss
Original file line number Diff line number Diff line change
@@ -1,6 +1,2 @@
.dialog {
.button {
margin: 0 !important;
padding: 0 !important;
}
}
90 changes: 64 additions & 26 deletions code/client/src/ui/components/dialog/Dialog.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,50 @@
import React, { useState } from 'react';
import { Dialog as MaterialDialog, DialogTitle, DialogContent, TextField, DialogActions, Button } from '@mui/material';
import {
Dialog as MaterialDialog,
DialogTitle,
DialogContent,
TextField,
DialogActions,
Checkbox,
FormControlLabel,
} from '@mui/material';
import { makeStyles } from '@mui/styles';
import './Dialog.scss';

interface Field {
name: string;
label: string;
type?: string;
}

interface DialogProps {
title: string;
fields: Field[];
onSubmit: (values: { [key: string]: string }) => void;
onSubmit: (values: { [key: string]: any }) => void;
children: React.ReactNode;
}

const useStyles = makeStyles({
root: {
'& label.Mui-focused': {
color: 'black',
},
'& .MuiInput-underline:after': {
borderBottomColor: 'black',
},
'& .MuiOutlinedInput-root': {
'&.Mui-focused fieldset': {
borderColor: 'black',
},
},
},
});

function Dialog({ title, fields, onSubmit, children }: DialogProps) {
const classes = useStyles();
const [open, setOpen] = useState(false);
const [values, setValues] = useState<{ [key: string]: string }>(
fields.reduce((obj, item) => ({ ...obj, [item.name]: '' }), {})
const [values, setValues] = useState<{ [key: string]: any }>(
fields.reduce((obj, item) => ({ ...obj, [item.name]: item.type === 'checkbox' ? false : '' }), {})
);

const handleOpen = () => {
Expand All @@ -33,38 +60,49 @@ function Dialog({ title, fields, onSubmit, children }: DialogProps) {
handleClose();
};

const handleChange = (name: string) => (event: React.ChangeEvent<HTMLInputElement>) => {
setValues({ ...values, [name]: event.target.value });
const handleChange = (name: string, type: string) => (event: React.ChangeEvent<HTMLInputElement>) => {
setValues({ ...values, [name]: type === 'checkbox' ? event.target.checked : event.target.value });
};

return (
<div className="dialog">
<Button onClick={handleOpen} className="button">
<button onClick={handleOpen} className="button">
{children}
</Button>
</button>
<MaterialDialog open={open} onClose={handleClose}>
<DialogTitle>{title}</DialogTitle>
<DialogContent>
{fields.map(field => (
<TextField
key={field.name}
autoFocus
margin="dense"
label={field.label}
type="text"
fullWidth
value={values[field.name]}
onChange={handleChange(field.name)}
/>
))}
{fields.map(field =>
field.type === 'checkbox' ? (
<FormControlLabel
key={field.name}
control={
<Checkbox
checked={values[field.name]}
onChange={handleChange(field.name, field.type)}
name={field.name}
/>
}
label={field.label}
/>
) : (
<TextField
className={classes.root}
key={field.name}
autoFocus
margin="dense"
label={field.label}
type="text"
fullWidth
value={values[field.name]}
onChange={handleChange(field.name, field.type!)}
/>
)
)}
</DialogContent>
<DialogActions>
<Button onClick={handleClose} color="primary">
Cancel
</Button>
<Button onClick={handleSubmit} color="primary">
Submit
</Button>
<button onClick={handleClose}>Cancel</button>
<button onClick={handleSubmit}>Submit</button>
</DialogActions>
</MaterialDialog>
</div>
Expand Down
11 changes: 8 additions & 3 deletions code/client/src/ui/components/sidebar/Sidebar.scss
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,13 @@
width: 90%;
border-radius: 5px;

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

div {
display: flex;
flex-direction: row;
Expand All @@ -137,9 +144,7 @@
}

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

.resource-header > button:hover {
Expand Down
4 changes: 2 additions & 2 deletions code/client/src/ui/components/sidebar/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ 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';
import { TiHome } from 'react-icons/ti';

function Sidebar() {
const { isOpen, isLocked, isLoaded, handleClick, handleMouseEnter, handleMouseLeave } = useSidebarState();
Expand Down Expand Up @@ -34,7 +34,7 @@ function Sidebar() {

<ul>
<li>
<FaHome />
<TiHome />
<Link to="/">Home</Link>
</li>
<li>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import React, { useState } from 'react';
import { Link } from 'react-router-dom';
import { Resource, ResourceType } from '@notespace/shared/src/workspace/types/resource';
import { FaFile, FaFolder } from 'react-icons/fa6';
import { TreeNode } from '@domain/workspaces/tree/types';
import React, { useState } from 'react';
import { PiDotOutlineFill } from 'react-icons/pi';
import { RiArrowDownSFill, RiArrowRightSFill } from 'react-icons/ri';
import { FaPlusSquare } from 'react-icons/fa';
import CreateResourceContextMenu from '@ui/components/sidebar/components/CreateResourceContextMenu';

type ResourceViewProps = {
type TreeResourceViewProps = {
workspace: string;
resource: Resource;
onCreateResource?: (parent: string, type: ResourceType) => void;
Expand All @@ -16,7 +17,7 @@ type ResourceViewProps = {
children?: TreeNode[];
};

function ResourceView({ resource, workspace, children, onCreateResource, onDrag, onDrop }: ResourceViewProps) {
function TreeResourceView({ resource, workspace, children, onCreateResource, onDrag, onDrop }: TreeResourceViewProps) {
const [isOpen, setIsOpen] = useState(true);

const handleToggle = () => {
Expand All @@ -35,7 +36,14 @@ function ResourceView({ resource, workspace, children, onCreateResource, onDrag,
<div className="resource">
<div className="resource-header">
<div>
<button onClick={handleToggle}>{isOpen ? <RiArrowDownSFill /> : <RiArrowRightSFill />}</button>
<div>
{resource.children.length > 0 ? (
<button onClick={handleToggle}>{isOpen ? <RiArrowDownSFill /> : <RiArrowRightSFill />}</button>
) : (
<PiDotOutlineFill />
)}
</div>

<CreateResourceContextMenu
onCreateNew={(type: ResourceType) => onCreateResource!(resource.id, type)}
trigger={'create-new-resource-' + resource.id}
Expand All @@ -62,7 +70,7 @@ function ResourceView({ resource, workspace, children, onCreateResource, onDrag,
<div className="resource-children">
{isOpen &&
children?.map(child => (
<ResourceView
<TreeResourceView
key={child.node.id}
workspace={workspace}
resource={child.node}
Expand All @@ -77,4 +85,4 @@ function ResourceView({ resource, workspace, children, onCreateResource, onDrag,
);
}

export default ResourceView;
export default TreeResourceView;
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import ResourceView from '@ui/components/sidebar/components/ResourceView';
import TreeResourceView from '@ui/components/sidebar/components/TreeResourceView';
import { WorkspaceMeta } from '@notespace/shared/src/workspace/types/workspace';
import { getTree } from '@domain/workspaces/tree/utils';
import { ResourceType } from '@notespace/shared/src/workspace/types/resource';
Expand Down Expand Up @@ -34,7 +34,7 @@ function WorkspaceTree({ workspace, resources, operations }: WorkspaceTreeProps)
{resources[workspace.id] &&
getTree(workspace.id, resources).children.map(node => (
<li key={node.node.id}>
<ResourceView
<TreeResourceView
workspace={workspace.id}
resource={node.node}
children={node.children}
Expand Down
Loading

0 comments on commit ac92b9c

Please sign in to comment.