diff --git a/README.md b/README.md index cf85583..4772f34 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,6 @@ npm run dev ## TODO -- Add task saving on Enter key press, remapping new line to Shift+Enter - Convert to monorepo and add backend for tasks syncing - Add ServiceWorker - Switch to IndexedDB diff --git a/src/components/AddTask.tsx b/src/components/AddTask.tsx index 5a533f3..1f8ff54 100644 --- a/src/components/AddTask.tsx +++ b/src/components/AddTask.tsx @@ -12,18 +12,19 @@ import { close as closeAction, } from "../store/slices/uiState"; import { add } from "../store/slices/todo"; +import { enterHandler } from "../utils"; export const AddTask: React.FC = () => { const open = useAppSelector((state) => state.uiState.addBarOpen); const dispatch = useAppDispatch(); - const { value, onChange, submit } = useInputValue("", (submitValue) => + const { value, change, submit } = useInputValue("", (submitValue) => dispatch(add(submitValue)) ); const save = () => { submit(); - onChange(""); + change(""); dispatch(closeAction()); }; @@ -58,6 +59,7 @@ export const AddTask: React.FC = () => { }} /> { onFocus={(e) => { e.currentTarget.setSelectionRange(value.length, value.length); }} - onChange={(e) => onChange(e.currentTarget.value)} + onChange={(e) => change(e.currentTarget.value)} + onKeyDown={enterHandler(save)} multiline /> theme.spacing(1) }}> diff --git a/src/components/TodoItem.tsx b/src/components/TodoItem.tsx index cb95766..7a7ee75 100644 --- a/src/components/TodoItem.tsx +++ b/src/components/TodoItem.tsx @@ -11,6 +11,7 @@ import DeleteOutlined from "@mui/icons-material/DeleteOutlined"; import { TaskItemT } from "../types"; import { useAppDispatch, useInputValue } from "../hooks"; import { updateText, markDone, remove } from "../store/slices/todo"; +import { enterHandler } from "../utils"; export type TodoItemProps = { task: TaskItemT; index: number }; @@ -20,10 +21,15 @@ export const TodoItem: React.FC = ({ task, index }) => { const dispatch = useAppDispatch(); const [editing, setEditing] = useState(false); - const { value, onChange, submit } = useInputValue(text, (submitValue) => + const { value, change, submit } = useInputValue(text, (submitValue) => dispatch(updateText({ index, text: submitValue })) ); + const save = () => { + setEditing(false); + submit(); + }; + return ( = ({ task, index }) => { onFocus={(e) => { e.currentTarget.setSelectionRange(value.length, value.length); }} - onChange={(e) => onChange(e.currentTarget.value)} - onBlur={() => { - setEditing(false); - submit(); - }} + onChange={(e) => change(e.currentTarget.value)} + onBlur={save} + inputProps={{ onKeyDown: (e) => enterHandler(save) }} multiline /> ) : ( setEditing(true)} primary={text} diff --git a/src/hooks.ts b/src/hooks.ts index ecc1f5b..e04ea95 100644 --- a/src/hooks.ts +++ b/src/hooks.ts @@ -4,7 +4,7 @@ import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux"; import { AppDispatch, RootState } from "./store"; export type UseInputValueReturnT = { - onChange: (value: string) => void; + change: (value: string) => void; submit: () => void; value: string; }; @@ -16,7 +16,7 @@ export const useInputValue = ( const [value, setValue] = useState(initialValue); return { - onChange: (value) => setValue(value), + change: (value) => setValue(value), submit: () => onSubmit(value), value, }; diff --git a/src/utils.ts b/src/utils.ts new file mode 100644 index 0000000..1370936 --- /dev/null +++ b/src/utils.ts @@ -0,0 +1,19 @@ +import { KeyboardEventHandler } from "react"; + +export const enterHandler = + ( + save: () => void + ): KeyboardEventHandler => + (e) => { + if (e.code === "Enter") { + e.preventDefault(); + if (e.shiftKey) { + const value = e.currentTarget.value, + selEnd = e.currentTarget.selectionEnd ?? 0, + selStart = e.currentTarget.selectionStart ?? 0; + + e.currentTarget.value = + value.slice(0, selStart) + "\n" + value.slice(selEnd); + } else save(); + } + };