import React, { useRef, useState } from 'react';
import styled from '@emotion/styled/macro';
import { useRecoilCallback, useRecoilState, useRecoilValue } from 'recoil'
import { v4 as uuidv4} from 'uuid';
import { todoFormModalOpenState } from './atom';
import Modal from '../../components/Modal';
import { selectedDateState, todoListState } from '../TodoList/atom';
import { getSimpleDateFormat } from '../../utils/date';
const Container = styled.div`
width: 100vw;
max-width: 386px;
padding: 8px;
`;
const Date = styled.small`
display: block;
color: #C9C8CC;
`;
const InputTodo = styled.input`
padding: 16px 24px;
border: none;
width: 100%;
box-sizing: border-box;
background-color: transparent;
color: #C9C8CC;
caret-color: #C9C8CC;
`;
const Card = styled.div`
width: 100%;
max-width: 370px;
border-radius: 16px;
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1);
padding: 24px;
box-sizing: border-box;
background-color: #19181A;
${Date} + ${InputTodo} {
margin-top: 24px;
}
;
`;
const TodoFormModal: React.FC = () => {
const inputRef = useRef<HTMLInputElement | null>(null);
const [todo, setTodo] = useState<string>('');
const selectedDate = useRecoilValue(selectedDateState);
const todoList = useRecoilValue(todoListState);
const [isOpen, setIsOpen] = useRecoilState(todoFormModalOpenState);
const reset = () => {
setTodo('');
inputRef.current?.focus();
}
const handleClose = () => setIsOpen(false);
const addTodo = useRecoilCallback(({ snapshot, set }) => () => {
const todoList = snapshot.getLoadable(todoListState).getValue();
const newTodo = { id: uuidv4(), content: todo, done: false, date: selectedDate };
set(todoListState, [...todoList, newTodo]);
}, [todo, selectedDate, todoList]);
const handleKeyPress = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Enter') {
addTodo();
reset();
handleClose();
}
}
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setTodo(e.target.value);
}
return (
<Modal isOpen={isOpen} onClose={handleClose}>
<Container>
<Card>
<Date>{getSimpleDateFormat(selectedDate)}</Date>
<InputTodo ref={inputRef} placeholder="새로운 이벤트" onKeyPress={handleKeyPress} value={todo} onChange={handleChange} />
</Card>
</Container>
</Modal>
)
}
export default TodoFormModal;
src/features/TodoList/index.tsx
import React from 'react';
import styled from '@emotion/styled/macro';
import { useRecoilValue, useSetRecoilState } from 'recoil';
import { Todo, selectedTodoState } from './atom';
import { todoStatisticsModalOpenState } from "../TodoStatisticsModal/atom";
const EtcItem = styled.li`
padding: 2px 4px;
margin: 0;
font-size: 10px;
cursor: pointer;
`;
const TodoItem = styled.li<{ done?: boolean; selected?: boolean; }>`
max-width: 100px;
width: 100%;
overflow: hidden;
text-overflow: ellipsis;
background-color: ${({ done, selected }) => selected ? 'rgba(112, 71, 235, 1)' : done ? 'transparent' : 'rgba(112, 71, 235, 0.4)'};
padding: 2px 4px;
margin: 0;
border-radius: 8px;
font-size: 10px;
text-decoration: ${({ done }) => done && 'line-through'};
cursor: pointer;
`;
const Base = styled.ul`
list-style: none;
margin: 36px 0 0 0;
padding: 0;
width: 100%;
height: 60px;
${TodoItem} + ${TodoItem} {
margin-top: 1px;
}
${TodoItem} + ${EtcItem} {
margin-top: 1px;
}
`;
interface Props {
items: Array<Todo>;
}
const MAX_TODO_LIST_LENGTH = 3;
const TodoList: React.FC<Props> = ({ items }) => {
const selectedTodo = useRecoilValue(selectedTodoState);
const setSelectedTodo = useSetRecoilState(selectedTodoState);
const setTodoStatisticsModalOpen = useSetRecoilState(todoStatisticsModalOpenState);
const handleClick = (event: React.SyntheticEvent<HTMLLIElement>, todo: Todo) => {
event.stopPropagation();
setSelectedTodo(selectedTodo?.id === todo.id && selectedTodo.date === todo.date ? null : todo);
}
const handleTodoStatisticsModalOpen = (event: React.SyntheticEvent<HTMLLIElement>) => {
event.stopPropagation();
setTodoStatisticsModalOpen(true);
}
return (
<Base>
{
items.slice(0, MAX_TODO_LIST_LENGTH).map((item, index) => (
<TodoItem
key={item.id}
done={item.done}
selected={item.date === selectedTodo?.date && item.id === selectedTodo?.id}
onClick={(event: React.SyntheticEvent<HTMLLIElement>) => handleClick(event, item)}
>
{item.content}
</TodoItem>
))
}
{items.length > MAX_TODO_LIST_LENGTH && (
<EtcItem onClick={handleTodoStatisticsModalOpen}>{`그 외 ${items.length - MAX_TODO_LIST_LENGTH}개...`}</EtcItem>
)}
</Base>
)
}
export default TodoList;