diff --git a/src/features/TaskCard/CreateOrEditTaskCard.js b/src/features/TaskCard/CreateOrEditTaskCard.js
new file mode 100644
index 0000000..9d2b789
--- /dev/null
+++ b/src/features/TaskCard/CreateOrEditTaskCard.js
@@ -0,0 +1,98 @@
+import React, { useState } from "react";
+import { useDispatch } from "react-redux";
+import { createTask, editTask } from "../../storeSlices/boardSlice";
+import useAutoHeightTextArea from "../../utils/useAutoHeightTextArea";
+
+import styles from "./TaskCard.module.css";
+
+export function CreateOrEditTaskCard({
+ mode = "create" /* "create" | "edit" */,
+ listID,
+ cardID,
+ defaultCardText = "",
+ onSubmit: parentOnSubmit = () => {},
+ onCancel: parentOnCancel = () => {}
+}) {
+ /*
+ ---------
+ Init here
+ ----------
+ */
+ const textAreaRef = useAutoHeightTextArea();
+ const dispatch = useDispatch();
+
+ const isCreateMode = mode === "create";
+
+ const [newCardText, setNewCardText] = useState(defaultCardText);
+ const [isFormOpen, setIsFormOpen] = useState(isCreateMode ? false : true);
+
+ const isTextNonEmpty = newCardText.trim().length > 0;
+
+ /*
+ ---------
+ Create evt handlers here
+ ----------
+ */
+ const onComplete = () => {
+ setNewCardText(defaultCardText);
+ setIsFormOpen(false);
+ };
+
+ const onSubmit = e => {
+ e.preventDefault();
+
+ if (isTextNonEmpty > 0) {
+ if (isCreateMode) {
+ dispatch(createTask({ text: newCardText, listID }));
+ } else {
+ const payload = { text: newCardText, cardID: cardID, listID };
+
+ dispatch(editTask(payload));
+ }
+ }
+
+ onComplete();
+
+ parentOnSubmit();
+ };
+
+ const onCancel = e => {
+ onComplete();
+
+ parentOnCancel();
+ };
+
+ /*
+ ---------
+ Render here
+ ----------
+ */
+ return (
+
+ {!isFormOpen ? (
+
+ ) : (
+
+ )}
+
+ );
+}
diff --git a/src/features/TaskCard/NewTaskCardPrompt.js b/src/features/TaskCard/NewTaskCardPrompt.js
deleted file mode 100644
index c5264f9..0000000
--- a/src/features/TaskCard/NewTaskCardPrompt.js
+++ /dev/null
@@ -1,29 +0,0 @@
-import React, { useState } from "react";
-import { useDispatch } from "react-redux";
-import { createTask } from "../../storeSlices/boardSlice";
-
-import styles from "./TaskCard.module.css";
-
-export function NewTaskCardPrompt({ listID }) {
- const dispatch = useDispatch();
-
- const [newCardText, setNewCardText] = useState("");
-
- return (
-
- setNewCardText(e.target.value)}
- onKeyPress={e => {
- if (newCardText.trim().length > 0 && e.key === "Enter") {
- dispatch(createTask({ text: newCardText, listID }));
-
- setNewCardText("");
- }
- }}
- >
-
- );
-}
diff --git a/src/features/TaskCard/TaskCard.js b/src/features/TaskCard/TaskCard.js
index 65061c7..a236c17 100644
--- a/src/features/TaskCard/TaskCard.js
+++ b/src/features/TaskCard/TaskCard.js
@@ -1,15 +1,30 @@
import React, { useState } from "react";
-import { useSelector, useDispatch } from "react-redux";
-import { createList } from "../../storeSlices/boardSlice";
+
+import { CreateOrEditTaskCard } from "./CreateOrEditTaskCard";
import styles from "./TaskCard.module.css";
-export function TaskCard({ cardData }) {
- const dispatch = useDispatch();
+export function TaskCard({ cardData, listID }) {
+ const [isEditing, setIsEditing] = useState(false);
- return (
-
+ const stopEditing = () => setIsEditing(false);
+
+ return !isEditing ? (
+
setIsEditing(true)}
+ >
{cardData.text}
+ ) : (
+
);
}
diff --git a/src/features/TaskCard/TaskCard.module.css b/src/features/TaskCard/TaskCard.module.css
index 52558de..0d916d6 100644
--- a/src/features/TaskCard/TaskCard.module.css
+++ b/src/features/TaskCard/TaskCard.module.css
@@ -5,3 +5,41 @@
padding: 8px;
margin: 8px 0;
}
+
+.createCardContainer {
+ width: 100%;
+ resize: vertical;
+ font-size: 1em;
+}
+
+.createCardForm {
+ margin: 8px 0;
+}
+
+.createInitBtn {
+ width: 100%;
+ padding: 8px 0;
+ background: none;
+ border: none;
+ text-align: left;
+ font-size: 0.85em;
+}
+
+.createActionBtnContainer {
+ display: flex;
+ justify-content: space-evenly;
+}
+
+.createActionBtnContainer > input {
+ flex: 1 0 50px;
+ margin: 0 8px;
+ padding: 4px 0;
+}
+
+.createActionBtnContainer > input:first-child {
+ margin-left: 0;
+}
+
+.createActionBtnContainer > input:last-child {
+ margin-right: 0;
+}
diff --git a/src/features/TaskList/TaskList.js b/src/features/TaskList/TaskList.js
index c781258..0156300 100644
--- a/src/features/TaskList/TaskList.js
+++ b/src/features/TaskList/TaskList.js
@@ -4,7 +4,7 @@ import { useSelector, useDispatch } from "react-redux";
import { createList } from "../../storeSlices/boardSlice";
import { TaskCard } from "../TaskCard/TaskCard";
-import { NewTaskCardPrompt } from "../TaskCard/NewTaskCardPrompt";
+import { CreateOrEditTaskCard } from "../TaskCard/CreateOrEditTaskCard";
import styles from "./TaskList.module.css";
@@ -16,9 +16,9 @@ export function TaskList({ listData }) {
{listData.name}
{listData.cards.map(cardData => (
-
+
))}
-
+
);
diff --git a/src/storeSlices/boardSlice.js b/src/storeSlices/boardSlice.js
index d428c4a..91e445e 100644
--- a/src/storeSlices/boardSlice.js
+++ b/src/storeSlices/boardSlice.js
@@ -72,7 +72,7 @@ export const slice = createSlice({
const { text: cardText, cardID, listID: parentListID } = action.payload;
const parentList = state.lists.find(({ id }) => id === parentListID);
- const existingCard = parentList.find(({ id }) => id === cardID);
+ const existingCard = parentList.cards.find(({ id }) => id === cardID);
existingCard.text = cardText;
}
diff --git a/src/utils/useAutoHeightTextArea.js b/src/utils/useAutoHeightTextArea.js
new file mode 100644
index 0000000..b2720e4
--- /dev/null
+++ b/src/utils/useAutoHeightTextArea.js
@@ -0,0 +1,42 @@
+import { createRef, useEffect, useCallback } from "react";
+
+// Souce - http://bdadam.com/blog/automatically-adapting-the-height-textarea.html
+
+function adjustHeight(el, minHeight) {
+ // compute the height difference which is caused by border and outline
+ var outerHeight = parseInt(window.getComputedStyle(el).height, 10);
+ var diff = outerHeight - el.clientHeight;
+
+ // set the height to 0 in case of it has to be shrinked
+ el.style.height = 0;
+
+ // set the correct height
+ // el.scrollHeight is the full height of the content, not just the visible part
+ el.style.height = Math.max(minHeight, el.scrollHeight + diff) + "px";
+}
+
+export default function useAutoHeightTextArea(minHeight = 54) {
+ const eleRef = createRef(null);
+ const onChange = useCallback(() => {
+ if (eleRef.current !== null) {
+ adjustHeight(eleRef.current, minHeight);
+ }
+ }, [minHeight, eleRef]);
+
+ useEffect(() => {
+ if (eleRef.current !== null) {
+ const el = eleRef.current;
+
+ el.addEventListener("input", onChange);
+
+ // we adjust height to the initial content
+ adjustHeight(el, minHeight);
+
+ return () => {
+ el.removeEventListener("input", onChange);
+ };
+ }
+ });
+
+ return eleRef;
+}