Skip to content
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
333 changes: 95 additions & 238 deletions src/components/Todo/Todo.tsx
Original file line number Diff line number Diff line change
@@ -1,249 +1,106 @@
import React, { useRef, useState, useEffect } from 'react';
import AddIcon from '@material-ui/icons/Add';
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
import { Checkbox, Container, makeStyles, TextField, Accordion, AccordionDetails, AccordionSummary, Typography, FormControl } from '@material-ui/core';
//@ts-ignore
import uuid from 'react-uuid';
import CloseIcon from '@material-ui/icons/Close';
import React, { useState } from "react";

import ExpandMoreIcon from "@material-ui/icons/ExpandMore";
import {
Container,
makeStyles,
Accordion,
AccordionDetails,
AccordionSummary,
Typography,
} from "@material-ui/core";
import { Item, TodoCompletedItem } from "./common";
import { Form } from "./common/Todo/Form";
import { TodoItem } from "./common/types";

const useStyles = makeStyles({
root: {
display: 'flex',
width: '100%'
},
underline: {
"&&&:before": {
borderBottom: "none"
accordion: {
boxShadow: "none",
borderTop: "1px solid black",
marginTop: "20px",
},
accordionDetails: {
display: "block",
paddingLeft: "0px",
},
accordionSummary: {
paddingLeft: "4.2%",
}
},
textFeild: {
padding: '10px 0px 7px'
},
subtitle1: {
textDecorationLine: 'line-through',
textDecorationStyle: 'solid',
padding: '10px 0px 7px'
},
accordion: {
boxShadow: 'none',
borderTop: '1px solid black',
marginTop: '20px'
},
accordionDetails: {
display: 'block',
paddingLeft: '0px'
},
accordionSummary: {
paddingLeft: '4.2%'
},
closeIcon: {
margin: '10px 0px 7px',
cursor: 'pointer'
},
plusIcon: {
margin: '5px 10px 0px 8px'
}
});

export interface TodoItem {
name: string;
isComplete: boolean;
uuid: string;
}

export interface AddProps {
addItem: (item: TodoItem | TodoItem[]) => void;
}
const Add = (props: AddProps) => {
const classes = useStyles();
const { addItem } = props;
const [itemName, setItemName] = useState('');

return (
<Container className={classes.root}>
<AddIcon titleAccess='Create Todo' className={classes.plusIcon} />{' '}
<FormControl fullWidth >
<TextField
onPaste={
(e) =>{
let clipboardData, pastedData;

// Stop data actually being pasted into div
e.stopPropagation();
e.preventDefault();

// Get pasted data via clipboard API
clipboardData = e.clipboardData;
pastedData = clipboardData.getData('Text').split('\n').reverse().filter((name)=> name.trim() !== "");

// Do whatever with pasteddata
const items = pastedData.map((name)=> {
return { name, uuid: uuid(), isComplete: false }
})
addItem(items);
}
}
onChange={(e) => {
addItem({ name: e.target.value, uuid: uuid(), isComplete: false });
setItemName('');
}}
placeholder="Add item."
value={itemName}
className="w-10/12"
autoFocus
/>
</FormControl>
</Container>
);
};

interface ItemProps {
items: TodoItem[];
setItemsCallback: (updatedItems: TodoItem[]) => void;
addItem: (item: TodoItem | TodoItem[]) => void;
itemIndex: number;
}

const Item = (props: ItemProps) => {
const { items, setItemsCallback, itemIndex, addItem } = props;
const inputRef = useRef(null);
const classes = useStyles();
const [itemText, setItemText] = useState('');
useEffect(() => {
// @ts-ignore
items[itemIndex].name.length < 2 && inputRef.current && inputRef.current.focus();
setItemText(items[itemIndex].name);
}, []);
if (items[itemIndex].isComplete) return null;
return (
<Container className={classes.root} >
<Checkbox
title='Mark as completed'
onChange={(e) => {
items[itemIndex].isComplete = true;
setItemsCallback([...items])
}}
/>
<FormControl fullWidth >
<TextField
className={classes.textFeild}
InputProps={{ classes: { underline: classes.underline } }}
inputRef={inputRef}
value={itemText} // innerHTML of the editable div
onPaste={
(e) =>{
let clipboardData, pastedData;

// Stop data actually being pasted into div
e.stopPropagation();
e.preventDefault();

// Get pasted data via clipboard API
clipboardData = e.clipboardData;
pastedData = clipboardData.getData('Text').split('\n').reverse().filter((name)=> name.trim() !== "");

// Do whatever with pasteddata
const items = pastedData.map((name)=> {
return { name, uuid: uuid(), isComplete: false }
})
addItem(items);
}
}
onChange={(e) => {
items[itemIndex].name = e.target.value;
setItemText(e.target.value);
}}
onBlur={(e) => {
setItemsCallback([...items])
}}
onKeyPress={(e) => e.key === 'Enter' && itemIndex < 1 && addItem({ name: '', uuid: uuid(), isComplete: false })}
/>
</FormControl>
<CloseIcon
titleAccess='Delete Todo'
className={classes.closeIcon}
onClick={(e) => {
items.splice(itemIndex, 1)
setItemsCallback([...items]);
}}
/>
</Container>
);
};

export interface TodoCompletedItemsProp {
items: TodoItem[];
setItemsCallback: (updatedItems: TodoItem[]) => void;
itemIndex: number;
export interface TodoAppProps {
defaultItems?: TodoItem[];
onChange: (items: TodoItem[]) => void;
}

const TodoCompletedItem = (props: TodoCompletedItemsProp) => {
const { items, setItemsCallback, itemIndex } = props;
const classes = useStyles();
if (!items[itemIndex].isComplete) return null;
function TodoApp(props: TodoAppProps) {
const { defaultItems = [], onChange } = props;
const [items, setItems] = useState<TodoItem[]>(defaultItems);
const classes = useStyles();
const setItemsCallback = (updatedItems: TodoItem[]) => {
setItems(updatedItems);
onChange(updatedItems);
};
const addItem = (item: TodoItem | TodoItem[]) => {
const itemsCopy = [...items];
if (Array.isArray(item)) {
item.forEach((it) => {
itemsCopy.unshift(it);
});
setItemsCallback([...itemsCopy]);
} else {
itemsCopy.unshift(item);
setItemsCallback([...itemsCopy]);
}
};
const completedItems = items.filter((item: TodoItem) => item.isComplete);
const completedItemsLength = completedItems.length;

return <Container className={classes.root}>
<Checkbox checked onChange={(e) => {
items[itemIndex].isComplete = false;
setItemsCallback([...items])
}} />
<Typography variant="subtitle1" className={classes.subtitle1}>
{items[itemIndex].name}
</Typography>
</Container>
const todoItems = items.filter((item: TodoItem) => !item.isComplete);

}
export interface TodoAppProps {
defaultItems?: TodoItem[];
onChange: (items: TodoItem[]) => void;
}
export default (props: TodoAppProps) => {
const { defaultItems = [], onChange } = props;
const [items, setItems] = useState<TodoItem[]>(defaultItems);
const classes = useStyles();
const setItemsCallback = (updatedItems: TodoItem[]) => {
setItems(updatedItems);
onChange(updatedItems);
}
const addItem = (item: TodoItem | TodoItem[]) => {
const itemsCopy = [...items];
if(Array.isArray(item)){
item.forEach(
(it)=>{
itemsCopy.unshift(it);
}
)
setItemsCallback([...itemsCopy])
}else {
itemsCopy.unshift(item);
setItemsCallback([...itemsCopy])
}
};
const completedItemsLength = items.filter((item: TodoItem) => item.isComplete).length;
return (
<Container >
<Add addItem={addItem} />
{items.map((item, index) => {
return <Item items={items} addItem={addItem} key={item.uuid} itemIndex={index} setItemsCallback={setItemsCallback} />
})}
{completedItemsLength > 0 &&
<Accordion className={classes.accordion} defaultExpanded={true}>
<AccordionSummary
className={classes.accordionSummary}
expandIcon={<ExpandMoreIcon />}
aria-controls="panel1a-content"
id="panel1a-header"
>
<Typography > {completedItemsLength} Completed items </Typography>
</AccordionSummary>
<AccordionDetails className={classes.accordionDetails}>
{items.map((item, index) => {
return <TodoCompletedItem items={items} key={item.uuid} itemIndex={index} setItemsCallback={setItemsCallback} />
return (
<Container>
<Form addItem={addItem} />
{todoItems.map((item, index) => {
return (
<Item
items={items}
addItem={addItem}
key={item.uuid}
itemIndex={index}
setItemsCallback={setItemsCallback}
/>
);
})}
</AccordionDetails>
</Accordion >
}
</Container >
);
};
{completedItemsLength > 0 && (
<Accordion className={classes.accordion} defaultExpanded={true}>
<AccordionSummary
className={classes.accordionSummary}
expandIcon={<ExpandMoreIcon />}
aria-controls="panel1a-content"
id="panel1a-header"
>
<Typography>
{" "}
{completedItemsLength} Completed items{" "}
</Typography>
</AccordionSummary>
<AccordionDetails className={classes.accordionDetails}>
{completedItems.map((item, index) => {
return (
<TodoCompletedItem
items={items}
key={item.uuid}
itemIndex={index}
setItemsCallback={setItemsCallback}
/>
);
})}
</AccordionDetails>
</Accordion>
)}
</Container>
);
}

export default TodoApp;
42 changes: 42 additions & 0 deletions src/components/Todo/common/Todo/CompletedList/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import React from "react";
import { Checkbox, Container, Typography, makeStyles } from "@material-ui/core";
import { TodoItem } from "../../types";

const useStyles = makeStyles({
root: {
display: "flex",
width: "100%",
},
subtitle1: {
textDecorationLine: "line-through",
textDecorationStyle: "solid",
padding: "10px 0px 7px",
},
});

export interface TodoCompletedItemsProp {
items: TodoItem[];
setItemsCallback: (updatedItems: TodoItem[]) => void;
itemIndex: number;
}

export const TodoCompletedItem = (props: TodoCompletedItemsProp) => {
const { items, setItemsCallback, itemIndex } = props;
const classes = useStyles();
if (!items[itemIndex].isComplete) return null;

return (
<Container className={classes.root}>
<Checkbox
checked
onChange={(e) => {
items[itemIndex].isComplete = false;
setItemsCallback([...items]);
}}
/>
<Typography variant="subtitle1" className={classes.subtitle1}>
{items[itemIndex].name}
</Typography>
</Container>
);
};
Loading