From ac98a09008985d84380beb9bb7ce9e72edd54e83 Mon Sep 17 00:00:00 2001 From: Paul Date: Wed, 27 Sep 2023 22:58:26 +0300 Subject: [PATCH 01/17] added editable todolist and task titles --- src/components/Task.tsx | 56 ++++++++++++++++++++++++++----------- src/components/Todolist.tsx | 31 +++++++++++++++++--- 2 files changed, 67 insertions(+), 20 deletions(-) diff --git a/src/components/Task.tsx b/src/components/Task.tsx index 77f4b3e..6d42f77 100644 --- a/src/components/Task.tsx +++ b/src/components/Task.tsx @@ -1,5 +1,5 @@ import styled from "@emotion/styled" -import React from "react" +import React, { ChangeEvent, useState } from "react" import { Text } from "./Text" import bin from "../assets/icons/Bin.svg" import SizedBox from "./SizeBox" @@ -64,20 +64,44 @@ const StyledRow = styled(Row)` align-items: center; ` -const Task: React.FC = ({ ...props }) => ( - - - task title - - - - task description - - - Critical - - - -) +const Task: React.FC = ({ ...props }) => { + const [taskTitle, setTaskTitle] = useState("enter task title") + const [editing, setEditing] = useState(false) + + const handleTaskTitleClick = () => { + setEditing(true) + } + + const handleTaskTitleChange = (event: ChangeEvent) => { + setTaskTitle(event?.target.value) + } + const handleTaskTitleFix = () => { + setEditing(false) + } + return ( + + + {editing ? ( + + ) : ( + {taskTitle} + )} + + + + task description + + + Critical + + + + ) +} export default Task diff --git a/src/components/Todolist.tsx b/src/components/Todolist.tsx index d3e45b7..7fafbda 100644 --- a/src/components/Todolist.tsx +++ b/src/components/Todolist.tsx @@ -1,7 +1,6 @@ import styled from "@emotion/styled" -import React from "react" +import React, { ChangeEvent, useState } from "react" import { Text } from "./Text" -import { Row } from "./Flex" import bin from "../assets/icons/Bin.svg" import SizedBox from "./SizeBox" import Task from "./Task" @@ -37,7 +36,7 @@ const Root = styled.div` } ` -const Title = styled(Text)` +const TodoListTitle = styled(Text)` font-size: 28px; font-weight: 600; opacity: 0.8; @@ -66,10 +65,34 @@ const TasksContainer = styled.div` ` const TodoList: React.FC = () => { + const [todolistTitle, setTodolistTitle] = useState("Frontend") + const [editing, setEditing] = useState(false) + + const handleTodolistTitleClick = () => { + setEditing(true) + } + + const handleTodolistTitleChange = (event: ChangeEvent) => { + setTodolistTitle(event?.target.value) + } + const handleTodolistTitleFix = () => { + setEditing(false) + } + return ( - Frontend + {editing ? ( + + ) : ( + {todolistTitle} + )} + From 61215ca5f0b1ad04c4959590e9aba4f4e3356553 Mon Sep 17 00:00:00 2001 From: Paul Date: Thu, 28 Sep 2023 06:02:48 +0300 Subject: [PATCH 02/17] styling inputs --- package.json | 5 ++++- src/components/Task.tsx | 10 +++++++++- src/components/Todolist.tsx | 15 +++++++++++++-- 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 3570d3b..b7e3196 100644 --- a/package.json +++ b/package.json @@ -2,6 +2,7 @@ "name": "todolist2.0", "version": "0.1.0", "private": true, + "homepage": "https://paulzhemanov.github.io/todolist2.0/", "dependencies": { "@babel/core": "^7.16.0", "@emotion/styled": "^11.11.0", @@ -66,7 +67,9 @@ "scripts": { "start": "node scripts/start.js", "build": "node scripts/build.js", - "test": "node scripts/test.js" + "test": "node scripts/test.js", + "deploy": "gh-pages -d build", + "predeploy": "npm run build" }, "eslintConfig": { "extends": [ diff --git a/src/components/Task.tsx b/src/components/Task.tsx index 6d42f77..12e3312 100644 --- a/src/components/Task.tsx +++ b/src/components/Task.tsx @@ -54,6 +54,14 @@ const TaskTitle = styled(Text)` font-weight: 700; text-transform: uppercase; ` +const EditableTaskTitle = styled.input` + font-size: 28px; + font-weight: 600; + opacity: 0.8; + border: none; + outline: none; + background: transparent; +` const Description = styled(Text)` font-size: 16px; @@ -82,7 +90,7 @@ const Task: React.FC = ({ ...props }) => { {editing ? ( - = () => { {editing ? ( - ) : ( - {todolistTitle} + + {" "} + {todolistTitle} + )} From 1bcf24362f2372c202a48b929b7ea492fd3613d8 Mon Sep 17 00:00:00 2001 From: Paul Date: Fri, 29 Sep 2023 13:55:13 +0300 Subject: [PATCH 03/17] styling inputs | useRef, focus --- src/components/Task.tsx | 29 ++++++++++++++++++++++++----- src/components/Todolist.tsx | 29 +++++++++++++++++++++++------ 2 files changed, 47 insertions(+), 11 deletions(-) diff --git a/src/components/Task.tsx b/src/components/Task.tsx index 12e3312..80c2e75 100644 --- a/src/components/Task.tsx +++ b/src/components/Task.tsx @@ -1,5 +1,5 @@ import styled from "@emotion/styled" -import React, { ChangeEvent, useState } from "react" +import React, { ChangeEvent, useEffect, useRef, useState } from "react" import { Text } from "./Text" import bin from "../assets/icons/Bin.svg" import SizedBox from "./SizeBox" @@ -52,13 +52,13 @@ const Check = styled.div` const TaskTitle = styled(Text)` font-size: 20px; font-weight: 700; - text-transform: uppercase; ` const EditableTaskTitle = styled.input` font-size: 28px; font-weight: 600; opacity: 0.8; border: none; + border-bottom: 1px solid #000; outline: none; background: transparent; ` @@ -73,8 +73,9 @@ const StyledRow = styled(Row)` ` const Task: React.FC = ({ ...props }) => { - const [taskTitle, setTaskTitle] = useState("enter task title") + const [taskTitle, setTaskTitle] = useState("Enter task title") const [editing, setEditing] = useState(false) + const inputRef = useRef(null) const handleTaskTitleClick = () => { setEditing(true) @@ -86,18 +87,36 @@ const Task: React.FC = ({ ...props }) => { const handleTaskTitleFix = () => { setEditing(false) } + const handleTaskTitleKeyDown = ( + event: React.KeyboardEvent + ) => { + if (event.key === "Enter" || event.key === "Escape") { + setEditing(false) + } + } + useEffect(() => { + if (editing && inputRef.current) { + inputRef.current.focus() + const length = taskTitle.length + inputRef.current.setSelectionRange(length, length) + } + }, [editing, taskTitle]) + return ( {editing ? ( ) : ( - {taskTitle} + + {taskTitle} + )} diff --git a/src/components/Todolist.tsx b/src/components/Todolist.tsx index 2ac22c9..964150e 100644 --- a/src/components/Todolist.tsx +++ b/src/components/Todolist.tsx @@ -1,5 +1,5 @@ import styled from "@emotion/styled" -import React, { ChangeEvent, useState } from "react" +import React, { ChangeEvent, useEffect, useRef, useState } from "react" import { Text } from "./Text" import bin from "../assets/icons/Bin.svg" import SizedBox from "./SizeBox" @@ -42,10 +42,11 @@ const TodoListTitle = styled(Text)` opacity: 0.8; ` const EditableTodoListTitle = styled.input` - font-size: 28px; + font-size: 34px; + opacity: 0.6; font-weight: 600; - opacity: 0.8; border: none; + border-bottom: 2px solid #000; outline: none; background: transparent; ` @@ -73,8 +74,9 @@ const TasksContainer = styled.div` ` const TodoList: React.FC = () => { - const [todolistTitle, setTodolistTitle] = useState("Frontend") + const [todolistTitle, setTodolistTitle] = useState("Enter todolist title") const [editing, setEditing] = useState(false) + const inputRef = useRef(null) const handleTodolistTitleClick = () => { setEditing(true) @@ -86,19 +88,34 @@ const TodoList: React.FC = () => { const handleTodolistTitleFix = () => { setEditing(false) } + const handleTodolistTitleKeyDown = ( + event: React.KeyboardEvent + ) => { + if (event.key === "Enter" || event.key === "Escape") { + setEditing(false) + } + } + useEffect(() => { + if (editing && inputRef.current) { + inputRef.current.focus() + const length = todolistTitle.length + inputRef.current.setSelectionRange(length, length) + } + }, [editing, todolistTitle]) return ( {editing ? ( ) : ( - + {" "} {todolistTitle} From 95ada24c02f510fadc82189220e087c10d06f587 Mon Sep 17 00:00:00 2001 From: Paul Date: Fri, 29 Sep 2023 19:32:57 +0300 Subject: [PATCH 04/17] new component Input --- src/components/Input.tsx | 71 ++++++++++++++++++++++++++++++++++++++++ src/components/Task.tsx | 58 ++------------------------------ 2 files changed, 73 insertions(+), 56 deletions(-) create mode 100644 src/components/Input.tsx diff --git a/src/components/Input.tsx b/src/components/Input.tsx new file mode 100644 index 0000000..d0f4d65 --- /dev/null +++ b/src/components/Input.tsx @@ -0,0 +1,71 @@ +import styled from "@emotion/styled" +import React, { ChangeEvent, useEffect, useRef, useState } from "react" +import { Text } from "./Text" + +interface IProps {} + +const Root = styled.div` + display: flex; + flex-direction: column; +` +const TaskTitle = styled(Text)` + font-size: 20px; + font-weight: 700; +` +const EditableTaskTitle = styled.input` + font-size: 28px; + font-weight: 600; + opacity: 0.8; + border: none; + border-bottom: 1px solid #000; + outline: none; + background: transparent; +` + +const Input: React.FC = () => { + const [editing, setEditing] = useState(false) + const [taskTitle, setTaskTitle] = useState("Enter task title") + + const inputRef = useRef(null) + const handleTaskTitleClick = () => { + setEditing(true) + } + + const handleTaskTitleChange = (event: ChangeEvent) => { + setTaskTitle(event?.target.value) + } + const handleTaskTitleFix = () => { + setEditing(false) + } + const handleTaskTitleKeyDown = ( + event: React.KeyboardEvent + ) => { + if (event.key === "Enter" || event.key === "Escape") { + setEditing(false) + } + } + useEffect(() => { + if (editing && inputRef.current) { + inputRef.current.focus() + const length = taskTitle.length + inputRef.current.setSelectionRange(length, length) + } + }, [editing, taskTitle]) + + return ( + + {editing ? ( + + ) : ( + {taskTitle} + )} + + ) +} +export default Input diff --git a/src/components/Task.tsx b/src/components/Task.tsx index 80c2e75..5b74ed8 100644 --- a/src/components/Task.tsx +++ b/src/components/Task.tsx @@ -6,6 +6,7 @@ import SizedBox from "./SizeBox" import check from "../assets/icons/Check.svg" import { Tag } from "./Tag" import { Row } from "./Flex" +import Input from "./Input" interface IProps { align?: "row" | "column" @@ -49,20 +50,6 @@ const Check = styled.div` flex-shrink: 0; ` -const TaskTitle = styled(Text)` - font-size: 20px; - font-weight: 700; -` -const EditableTaskTitle = styled.input` - font-size: 28px; - font-weight: 600; - opacity: 0.8; - border: none; - border-bottom: 1px solid #000; - outline: none; - background: transparent; -` - const Description = styled(Text)` font-size: 16px; font-weight: 400; @@ -73,51 +60,10 @@ const StyledRow = styled(Row)` ` const Task: React.FC = ({ ...props }) => { - const [taskTitle, setTaskTitle] = useState("Enter task title") - const [editing, setEditing] = useState(false) - const inputRef = useRef(null) - - const handleTaskTitleClick = () => { - setEditing(true) - } - - const handleTaskTitleChange = (event: ChangeEvent) => { - setTaskTitle(event?.target.value) - } - const handleTaskTitleFix = () => { - setEditing(false) - } - const handleTaskTitleKeyDown = ( - event: React.KeyboardEvent - ) => { - if (event.key === "Enter" || event.key === "Escape") { - setEditing(false) - } - } - useEffect(() => { - if (editing && inputRef.current) { - inputRef.current.focus() - const length = taskTitle.length - inputRef.current.setSelectionRange(length, length) - } - }, [editing, taskTitle]) - return ( - {editing ? ( - - ) : ( - - {taskTitle} - - )} + From 7edb4e545334ea995a303e586b18b94b21dc3121 Mon Sep 17 00:00:00 2001 From: Paul Date: Sat, 30 Sep 2023 12:58:58 +0300 Subject: [PATCH 05/17] added component editabletitle in todolist --- src/components/EditableTitle.tsx | 65 +++++++++++++++++++++++++++++ src/components/Input.tsx | 71 -------------------------------- src/components/Task.tsx | 11 ++--- src/components/Text.ts | 1 - src/components/Todolist.tsx | 64 ++-------------------------- 5 files changed, 74 insertions(+), 138 deletions(-) create mode 100644 src/components/EditableTitle.tsx delete mode 100644 src/components/Input.tsx diff --git a/src/components/EditableTitle.tsx b/src/components/EditableTitle.tsx new file mode 100644 index 0000000..c02896a --- /dev/null +++ b/src/components/EditableTitle.tsx @@ -0,0 +1,65 @@ +import styled from "@emotion/styled" +import React, { ChangeEvent, useEffect, useRef, useState } from "react" +import { Text } from "./Text" + +interface IProps { + +} + +const Root = styled.div` + display: flex; + flex-direction: column; +` +const StyledInput = styled.input` + border: none; + outline: none; + background: transparent; +` + +const EditableTitle: React.FC = () => { + const [title, setTitle] = useState("Enter title") + const [editing, setEditing] = useState(false) + const inputRef = useRef(null) + + const handleTitleClick = () => { + setEditing(true) + } + + const handleTitleChange = (event: ChangeEvent) => { + setTitle(event?.target.value) + } + + const handleTitleFix = () => { + setEditing(false) + } + + const handleTitleKeyDown = (event: React.KeyboardEvent) => { + if (event.key === "Enter" || event.key === "Escape") { + setEditing(false) + } + } + + useEffect(() => { + if (editing && inputRef.current) { + inputRef.current.focus() + const length = title.length + inputRef.current.setSelectionRange(length, length) + } + }, [editing, title]) + return ( + + {editing ? ( + + ) : ( + {title} + )} + + ) +} +export default EditableTitle diff --git a/src/components/Input.tsx b/src/components/Input.tsx deleted file mode 100644 index d0f4d65..0000000 --- a/src/components/Input.tsx +++ /dev/null @@ -1,71 +0,0 @@ -import styled from "@emotion/styled" -import React, { ChangeEvent, useEffect, useRef, useState } from "react" -import { Text } from "./Text" - -interface IProps {} - -const Root = styled.div` - display: flex; - flex-direction: column; -` -const TaskTitle = styled(Text)` - font-size: 20px; - font-weight: 700; -` -const EditableTaskTitle = styled.input` - font-size: 28px; - font-weight: 600; - opacity: 0.8; - border: none; - border-bottom: 1px solid #000; - outline: none; - background: transparent; -` - -const Input: React.FC = () => { - const [editing, setEditing] = useState(false) - const [taskTitle, setTaskTitle] = useState("Enter task title") - - const inputRef = useRef(null) - const handleTaskTitleClick = () => { - setEditing(true) - } - - const handleTaskTitleChange = (event: ChangeEvent) => { - setTaskTitle(event?.target.value) - } - const handleTaskTitleFix = () => { - setEditing(false) - } - const handleTaskTitleKeyDown = ( - event: React.KeyboardEvent - ) => { - if (event.key === "Enter" || event.key === "Escape") { - setEditing(false) - } - } - useEffect(() => { - if (editing && inputRef.current) { - inputRef.current.focus() - const length = taskTitle.length - inputRef.current.setSelectionRange(length, length) - } - }, [editing, taskTitle]) - - return ( - - {editing ? ( - - ) : ( - {taskTitle} - )} - - ) -} -export default Input diff --git a/src/components/Task.tsx b/src/components/Task.tsx index 5b74ed8..cf0914b 100644 --- a/src/components/Task.tsx +++ b/src/components/Task.tsx @@ -1,12 +1,12 @@ import styled from "@emotion/styled" -import React, { ChangeEvent, useEffect, useRef, useState } from "react" +import React from "react" import { Text } from "./Text" import bin from "../assets/icons/Bin.svg" import SizedBox from "./SizeBox" import check from "../assets/icons/Check.svg" import { Tag } from "./Tag" import { Row } from "./Flex" -import Input from "./Input" +import EditableTitle from "./EditableTitle" interface IProps { align?: "row" | "column" @@ -38,6 +38,7 @@ const Root = styled.div` } ` + const Bin = styled.div` background: url(${bin}); width: 25px; @@ -59,11 +60,11 @@ const StyledRow = styled(Row)` align-items: center; ` -const Task: React.FC = ({ ...props }) => { +const Task: React.FC = () => { return ( - + - + diff --git a/src/components/Text.ts b/src/components/Text.ts index 559903f..4cc98fb 100644 --- a/src/components/Text.ts +++ b/src/components/Text.ts @@ -1,7 +1,6 @@ import styled from "@emotion/styled" export const Text = styled.div` - color: #313131; font-family: Open Sans; font-style: normal; line-height: normal; diff --git a/src/components/Todolist.tsx b/src/components/Todolist.tsx index 964150e..16a5e4a 100644 --- a/src/components/Todolist.tsx +++ b/src/components/Todolist.tsx @@ -1,9 +1,9 @@ import styled from "@emotion/styled" -import React, { ChangeEvent, useEffect, useRef, useState } from "react" -import { Text } from "./Text" +import React from "react" import bin from "../assets/icons/Bin.svg" import SizedBox from "./SizeBox" import Task from "./Task" +import EditableTitle from "./EditableTitle" const Bin = styled.div` background: url(${bin}); @@ -36,21 +36,6 @@ const Root = styled.div` } ` -const TodoListTitle = styled(Text)` - font-size: 28px; - font-weight: 600; - opacity: 0.8; -` -const EditableTodoListTitle = styled.input` - font-size: 34px; - opacity: 0.6; - font-weight: 600; - border: none; - border-bottom: 2px solid #000; - outline: none; - background: transparent; -` - const HeaderContainer = styled.div` display: flex; justify-content: space-between; @@ -74,53 +59,10 @@ const TasksContainer = styled.div` ` const TodoList: React.FC = () => { - const [todolistTitle, setTodolistTitle] = useState("Enter todolist title") - const [editing, setEditing] = useState(false) - const inputRef = useRef(null) - - const handleTodolistTitleClick = () => { - setEditing(true) - } - - const handleTodolistTitleChange = (event: ChangeEvent) => { - setTodolistTitle(event?.target.value) - } - const handleTodolistTitleFix = () => { - setEditing(false) - } - const handleTodolistTitleKeyDown = ( - event: React.KeyboardEvent - ) => { - if (event.key === "Enter" || event.key === "Escape") { - setEditing(false) - } - } - useEffect(() => { - if (editing && inputRef.current) { - inputRef.current.focus() - const length = todolistTitle.length - inputRef.current.setSelectionRange(length, length) - } - }, [editing, todolistTitle]) - return ( - {editing ? ( - - ) : ( - - {" "} - {todolistTitle} - - )} - + From f1c4178010c33288b8af51321a228e0d7660b57b Mon Sep 17 00:00:00 2001 From: Paul Date: Sun, 1 Oct 2023 13:10:15 +0300 Subject: [PATCH 06/17] css for titles is done --- src/components/EditableTitle.tsx | 57 ++++++++++++++++++++++++++++---- src/components/Task.tsx | 10 ++++-- src/components/Text.ts | 2 +- src/components/Todolist.tsx | 8 ++++- 4 files changed, 67 insertions(+), 10 deletions(-) diff --git a/src/components/EditableTitle.tsx b/src/components/EditableTitle.tsx index c02896a..80f07ff 100644 --- a/src/components/EditableTitle.tsx +++ b/src/components/EditableTitle.tsx @@ -3,22 +3,52 @@ import React, { ChangeEvent, useEffect, useRef, useState } from "react" import { Text } from "./Text" interface IProps { - + fontSize?: string + color?: string + fontWeight?: string + textTransform?: string + showUnderline?: boolean + opacity?: string } const Root = styled.div` display: flex; flex-direction: column; ` -const StyledInput = styled.input` +const StyledInput = styled.input` border: none; outline: none; background: transparent; + + font-size: ${(props) => (props.fontSize ? props.fontSize : "inherit")}; + opacity: ${(props) => (props.opacity ? props.opacity : "inherit")}; + color: ${(props) => (props.color ? props.color : "inherit")}; + font-weight: ${(props) => (props.fontWeight ? props.fontWeight : "inherit")}; + text-transform: ${(props) => + props.textTransform ? props.textTransform : "inherit"}; + border-bottom: ${(props) => + props.showUnderline ? "2px solid #000" : "none"}; +` + +const StyledText = styled(Text)` + font-size: ${(props) => (props.fontSize ? props.fontSize : "inherit")}; + text-transform: ${(props) => + props.textTransform ? props.textTransform : "inherit"}; + opacity: ${(props) => (props.opacity ? props.opacity : "inherit")}; + font-weight: ${(props) => (props.fontWeight ? props.fontWeight : "inherit")}; + color: ${(props) => (props.color ? props.color : "inherit")}; ` -const EditableTitle: React.FC = () => { - const [title, setTitle] = useState("Enter title") - const [editing, setEditing] = useState(false) +const EditableTitle: React.FC = ({ + fontSize, + color, + fontWeight, + textTransform, + showUnderline = false, + opacity, +}) => { + const [title, setTitle] = useState("Enter title") + const [editing, setEditing] = useState(false) const inputRef = useRef(null) const handleTitleClick = () => { @@ -55,9 +85,24 @@ const EditableTitle: React.FC = () => { onChange={handleTitleChange} onBlur={handleTitleFix} onKeyDown={handleTitleKeyDown} + fontSize={fontSize} + color={color} + fontWeight={fontWeight} + textTransform={textTransform} + showUnderline={showUnderline} + opacity={opacity} /> ) : ( - {title} + + {title} + )} ) diff --git a/src/components/Task.tsx b/src/components/Task.tsx index cf0914b..81f8b99 100644 --- a/src/components/Task.tsx +++ b/src/components/Task.tsx @@ -38,7 +38,6 @@ const Root = styled.div` } ` - const Bin = styled.div` background: url(${bin}); width: 25px; @@ -55,6 +54,7 @@ const Description = styled(Text)` font-size: 16px; font-weight: 400; ` + const StyledRow = styled(Row)` justify-content: space-between; align-items: center; @@ -64,7 +64,13 @@ const Task: React.FC = () => { return ( - + diff --git a/src/components/Text.ts b/src/components/Text.ts index 4cc98fb..8409c63 100644 --- a/src/components/Text.ts +++ b/src/components/Text.ts @@ -4,4 +4,4 @@ export const Text = styled.div` font-family: Open Sans; font-style: normal; line-height: normal; -` +` \ No newline at end of file diff --git a/src/components/Todolist.tsx b/src/components/Todolist.tsx index 16a5e4a..52578e8 100644 --- a/src/components/Todolist.tsx +++ b/src/components/Todolist.tsx @@ -62,7 +62,13 @@ const TodoList: React.FC = () => { return ( - + From 3bd13919d652edbdcf72f13aa5cd14f683296096 Mon Sep 17 00:00:00 2001 From: Paul Date: Sun, 1 Oct 2023 14:10:12 +0300 Subject: [PATCH 07/17] handleTitleFix upgrade --- src/components/EditableTitle.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/components/EditableTitle.tsx b/src/components/EditableTitle.tsx index 80f07ff..2c767d0 100644 --- a/src/components/EditableTitle.tsx +++ b/src/components/EditableTitle.tsx @@ -56,16 +56,18 @@ const EditableTitle: React.FC = ({ } const handleTitleChange = (event: ChangeEvent) => { - setTitle(event?.target.value) + setTitle(event?.target.value) } const handleTitleFix = () => { - setEditing(false) + if (title.trim() !== "") { + setEditing(false); + } } const handleTitleKeyDown = (event: React.KeyboardEvent) => { if (event.key === "Enter" || event.key === "Escape") { - setEditing(false) + handleTitleFix() } } From 5bef79eebf0af03e4bc0207be7b7840cf6592024 Mon Sep 17 00:00:00 2001 From: Paul Date: Sun, 1 Oct 2023 15:25:07 +0300 Subject: [PATCH 08/17] fix sizes --- src/components/EditableTitle.tsx | 6 ++++-- src/components/Text.ts | 1 + src/components/Todolist.tsx | 4 ++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/components/EditableTitle.tsx b/src/components/EditableTitle.tsx index 2c767d0..7998442 100644 --- a/src/components/EditableTitle.tsx +++ b/src/components/EditableTitle.tsx @@ -14,6 +14,8 @@ interface IProps { const Root = styled.div` display: flex; flex-direction: column; + width: 165px; + height: 27px; ` const StyledInput = styled.input` border: none; @@ -56,12 +58,12 @@ const EditableTitle: React.FC = ({ } const handleTitleChange = (event: ChangeEvent) => { - setTitle(event?.target.value) + setTitle(event?.target.value) } const handleTitleFix = () => { if (title.trim() !== "") { - setEditing(false); + setEditing(false) } } diff --git a/src/components/Text.ts b/src/components/Text.ts index 8409c63..90d8a6b 100644 --- a/src/components/Text.ts +++ b/src/components/Text.ts @@ -4,4 +4,5 @@ export const Text = styled.div` font-family: Open Sans; font-style: normal; line-height: normal; + ` \ No newline at end of file diff --git a/src/components/Todolist.tsx b/src/components/Todolist.tsx index 52578e8..8b44b55 100644 --- a/src/components/Todolist.tsx +++ b/src/components/Todolist.tsx @@ -66,12 +66,12 @@ const TodoList: React.FC = () => { color="#d71919" fontSize="28px" fontWeight="600" - opacity="0.3" + opacity="0.7" showUnderline={true} /> - + From 0c5bb4357a2d146b5e07ad6f5054afecb4219b4d Mon Sep 17 00:00:00 2001 From: Paul Date: Sun, 1 Oct 2023 19:14:01 +0300 Subject: [PATCH 09/17] fix sizes2 --- src/components/Task.tsx | 2 +- src/components/Todolist.tsx | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/Task.tsx b/src/components/Task.tsx index 81f8b99..b29bd4f 100644 --- a/src/components/Task.tsx +++ b/src/components/Task.tsx @@ -18,7 +18,6 @@ const Root = styled.div` display: flex; flex-direction: column; padding: 20px; - width: 423px; background: #f3f5f6; border-radius: 12px; @@ -58,6 +57,7 @@ const Description = styled(Text)` const StyledRow = styled(Row)` justify-content: space-between; align-items: center; + width: 333px; ` const Task: React.FC = () => { diff --git a/src/components/Todolist.tsx b/src/components/Todolist.tsx index 8b44b55..8e434db 100644 --- a/src/components/Todolist.tsx +++ b/src/components/Todolist.tsx @@ -39,7 +39,8 @@ const Root = styled.div` const HeaderContainer = styled.div` display: flex; justify-content: space-between; - align-items: center; + align-items: start; + width: 373px; ` const TasksContainer = styled.div` From f1920c38b4040ee03d66f376e2cece0ce1e576ed Mon Sep 17 00:00:00 2001 From: Paul Date: Sun, 1 Oct 2023 22:28:05 +0300 Subject: [PATCH 10/17] added start titles --- src/components/EditableTitle.tsx | 7 +++++-- src/components/Task.tsx | 10 ++++++++-- src/components/Todolist.tsx | 1 + 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/components/EditableTitle.tsx b/src/components/EditableTitle.tsx index 7998442..3eafb9e 100644 --- a/src/components/EditableTitle.tsx +++ b/src/components/EditableTitle.tsx @@ -9,12 +9,13 @@ interface IProps { textTransform?: string showUnderline?: boolean opacity?: string + startTitle?: string } const Root = styled.div` display: flex; flex-direction: column; - width: 165px; + width: auto; height: 27px; ` const StyledInput = styled.input` @@ -48,8 +49,9 @@ const EditableTitle: React.FC = ({ textTransform, showUnderline = false, opacity, + startTitle = "", }) => { - const [title, setTitle] = useState("Enter title") + const [title, setTitle] = useState(startTitle) const [editing, setEditing] = useState(false) const inputRef = useRef(null) @@ -95,6 +97,7 @@ const EditableTitle: React.FC = ({ textTransform={textTransform} showUnderline={showUnderline} opacity={opacity} + maxLength={20} /> ) : ( = () => { = () => { - task description + Critical diff --git a/src/components/Todolist.tsx b/src/components/Todolist.tsx index 8e434db..4baffc1 100644 --- a/src/components/Todolist.tsx +++ b/src/components/Todolist.tsx @@ -64,6 +64,7 @@ const TodoList: React.FC = () => { Date: Sun, 1 Oct 2023 23:31:18 +0300 Subject: [PATCH 11/17] input settings --- src/components/EditableTitle.tsx | 5 ++++- src/components/Task.tsx | 7 +++---- src/components/Todolist.tsx | 1 + 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/components/EditableTitle.tsx b/src/components/EditableTitle.tsx index 3eafb9e..bfa3653 100644 --- a/src/components/EditableTitle.tsx +++ b/src/components/EditableTitle.tsx @@ -10,6 +10,7 @@ interface IProps { showUnderline?: boolean opacity?: string startTitle?: string + inputLength?: number | undefined } const Root = styled.div` @@ -31,6 +32,7 @@ const StyledInput = styled.input` props.textTransform ? props.textTransform : "inherit"}; border-bottom: ${(props) => props.showUnderline ? "2px solid #000" : "none"}; + maxlength: ${(props) => (props.inputLength ? props.inputLength : "inherit")}; ` const StyledText = styled(Text)` @@ -50,6 +52,7 @@ const EditableTitle: React.FC = ({ showUnderline = false, opacity, startTitle = "", + inputLength, }) => { const [title, setTitle] = useState(startTitle) const [editing, setEditing] = useState(false) @@ -97,7 +100,7 @@ const EditableTitle: React.FC = ({ textTransform={textTransform} showUnderline={showUnderline} opacity={opacity} - maxLength={20} + maxLength={inputLength} /> ) : ( = () => { textTransform="uppercase" color="#363636" showUnderline={true} + inputLength={20} /> @@ -80,6 +78,7 @@ const Task: React.FC = () => { color="#1cd719" fontSize="16px" fontWeight="400" + inputLength={40} /> diff --git a/src/components/Todolist.tsx b/src/components/Todolist.tsx index 4baffc1..da95661 100644 --- a/src/components/Todolist.tsx +++ b/src/components/Todolist.tsx @@ -70,6 +70,7 @@ const TodoList: React.FC = () => { fontWeight="600" opacity="0.7" showUnderline={true} + inputLength={20} /> From 44823c3b2df86c2492f4d5eda092ef1950bf100b Mon Sep 17 00:00:00 2001 From: Paul Date: Tue, 3 Oct 2023 12:07:53 +0300 Subject: [PATCH 12/17] some magic --- config/webpack.config.js | 309 ++++++++++++++++++------------------ src/App.tsx | 8 +- src/components/Header.tsx | 2 +- src/components/Task.tsx | 7 +- src/components/Todolist.tsx | 2 +- tsconfig.json | 14 +- 6 files changed, 178 insertions(+), 164 deletions(-) diff --git a/config/webpack.config.js b/config/webpack.config.js index 207d075..abc77ae 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -1,144 +1,144 @@ -'use strict'; +"use strict" -const fs = require('fs'); -const path = require('path'); -const webpack = require('webpack'); -const resolve = require('resolve'); -const HtmlWebpackPlugin = require('html-webpack-plugin'); -const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin'); -const InlineChunkHtmlPlugin = require('react-dev-utils/InlineChunkHtmlPlugin'); -const TerserPlugin = require('terser-webpack-plugin'); -const MiniCssExtractPlugin = require('mini-css-extract-plugin'); -const CssMinimizerPlugin = require('css-minimizer-webpack-plugin'); -const { WebpackManifestPlugin } = require('webpack-manifest-plugin'); -const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin'); -const WorkboxWebpackPlugin = require('workbox-webpack-plugin'); -const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin'); -const getCSSModuleLocalIdent = require('react-dev-utils/getCSSModuleLocalIdent'); -const ESLintPlugin = require('eslint-webpack-plugin'); -const paths = require('./paths'); -const modules = require('./modules'); -const getClientEnvironment = require('./env'); -const ModuleNotFoundPlugin = require('react-dev-utils/ModuleNotFoundPlugin'); +const fs = require("fs") +const path = require("path") +const webpack = require("webpack") +const resolve = require("resolve") +const HtmlWebpackPlugin = require("html-webpack-plugin") +const CaseSensitivePathsPlugin = require("case-sensitive-paths-webpack-plugin") +const InlineChunkHtmlPlugin = require("react-dev-utils/InlineChunkHtmlPlugin") +const TerserPlugin = require("terser-webpack-plugin") +const MiniCssExtractPlugin = require("mini-css-extract-plugin") +const CssMinimizerPlugin = require("css-minimizer-webpack-plugin") +const { WebpackManifestPlugin } = require("webpack-manifest-plugin") +const InterpolateHtmlPlugin = require("react-dev-utils/InterpolateHtmlPlugin") +const WorkboxWebpackPlugin = require("workbox-webpack-plugin") +const ModuleScopePlugin = require("react-dev-utils/ModuleScopePlugin") +const getCSSModuleLocalIdent = require("react-dev-utils/getCSSModuleLocalIdent") +const ESLintPlugin = require("eslint-webpack-plugin") +const paths = require("./paths") +const modules = require("./modules") +const getClientEnvironment = require("./env") +const ModuleNotFoundPlugin = require("react-dev-utils/ModuleNotFoundPlugin") const ForkTsCheckerWebpackPlugin = - process.env.TSC_COMPILE_ON_ERROR === 'true' - ? require('react-dev-utils/ForkTsCheckerWarningWebpackPlugin') - : require('react-dev-utils/ForkTsCheckerWebpackPlugin'); -const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin'); + process.env.TSC_COMPILE_ON_ERROR === "true" + ? require("react-dev-utils/ForkTsCheckerWarningWebpackPlugin") + : require("react-dev-utils/ForkTsCheckerWebpackPlugin") +const ReactRefreshWebpackPlugin = require("@pmmmwh/react-refresh-webpack-plugin") -const createEnvironmentHash = require('./webpack/persistentCache/createEnvironmentHash'); +const createEnvironmentHash = require("./webpack/persistentCache/createEnvironmentHash") // Source maps are resource heavy and can cause out of memory issue for large source files. -const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== 'false'; +const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== "false" -const reactRefreshRuntimeEntry = require.resolve('react-refresh/runtime'); +const reactRefreshRuntimeEntry = require.resolve("react-refresh/runtime") const reactRefreshWebpackPluginRuntimeEntry = require.resolve( - '@pmmmwh/react-refresh-webpack-plugin' -); -const babelRuntimeEntry = require.resolve('babel-preset-react-app'); + "@pmmmwh/react-refresh-webpack-plugin" +) +const babelRuntimeEntry = require.resolve("babel-preset-react-app") const babelRuntimeEntryHelpers = require.resolve( - '@babel/runtime/helpers/esm/assertThisInitialized', + "@babel/runtime/helpers/esm/assertThisInitialized", { paths: [babelRuntimeEntry] } -); -const babelRuntimeRegenerator = require.resolve('@babel/runtime/regenerator', { +) +const babelRuntimeRegenerator = require.resolve("@babel/runtime/regenerator", { paths: [babelRuntimeEntry], -}); +}) // Some apps do not need the benefits of saving a web request, so not inlining the chunk // makes for a smoother build process. -const shouldInlineRuntimeChunk = process.env.INLINE_RUNTIME_CHUNK !== 'false'; +const shouldInlineRuntimeChunk = process.env.INLINE_RUNTIME_CHUNK !== "false" -const emitErrorsAsWarnings = process.env.ESLINT_NO_DEV_ERRORS === 'true'; -const disableESLintPlugin = process.env.DISABLE_ESLINT_PLUGIN === 'true'; +const emitErrorsAsWarnings = process.env.ESLINT_NO_DEV_ERRORS === "true" +const disableESLintPlugin = process.env.DISABLE_ESLINT_PLUGIN === "true" const imageInlineSizeLimit = parseInt( - process.env.IMAGE_INLINE_SIZE_LIMIT || '10000' -); + process.env.IMAGE_INLINE_SIZE_LIMIT || "10000" +) // Check if TypeScript is setup -const useTypeScript = fs.existsSync(paths.appTsConfig); +const useTypeScript = fs.existsSync(paths.appTsConfig) // Check if Tailwind config exists const useTailwind = fs.existsSync( - path.join(paths.appPath, 'tailwind.config.js') -); + path.join(paths.appPath, "tailwind.config.js") +) // Get the path to the uncompiled service worker (if it exists). -const swSrc = paths.swSrc; +const swSrc = paths.swSrc // style files regexes -const cssRegex = /\.css$/; -const cssModuleRegex = /\.module\.css$/; -const sassRegex = /\.(scss|sass)$/; -const sassModuleRegex = /\.module\.(scss|sass)$/; +const cssRegex = /\.css$/ +const cssModuleRegex = /\.module\.css$/ +const sassRegex = /\.(scss|sass)$/ +const sassModuleRegex = /\.module\.(scss|sass)$/ const hasJsxRuntime = (() => { - if (process.env.DISABLE_NEW_JSX_TRANSFORM === 'true') { - return false; + if (process.env.DISABLE_NEW_JSX_TRANSFORM === "true") { + return false } try { - require.resolve('react/jsx-runtime'); - return true; + require.resolve("react/jsx-runtime") + return true } catch (e) { - return false; + return false } -})(); +})() // This is the production and development configuration. // It is focused on developer experience, fast rebuilds, and a minimal bundle. module.exports = function (webpackEnv) { - const isEnvDevelopment = webpackEnv === 'development'; - const isEnvProduction = webpackEnv === 'production'; + const isEnvDevelopment = webpackEnv === "development" + const isEnvProduction = webpackEnv === "production" // Variable used for enabling profiling in Production // passed into alias object. Uses a flag if passed into the build command const isEnvProductionProfile = - isEnvProduction && process.argv.includes('--profile'); + isEnvProduction && process.argv.includes("--profile") // We will provide `paths.publicUrlOrPath` to our app // as %PUBLIC_URL% in `index.html` and `process.env.PUBLIC_URL` in JavaScript. // Omit trailing slash as %PUBLIC_URL%/xyz looks better than %PUBLIC_URL%xyz. // Get environment variables to inject into our app. - const env = getClientEnvironment(paths.publicUrlOrPath.slice(0, -1)); + const env = getClientEnvironment(paths.publicUrlOrPath.slice(0, -1)) - const shouldUseReactRefresh = env.raw.FAST_REFRESH; + const shouldUseReactRefresh = env.raw.FAST_REFRESH // common function to get style loaders const getStyleLoaders = (cssOptions, preProcessor) => { const loaders = [ - isEnvDevelopment && require.resolve('style-loader'), + isEnvDevelopment && require.resolve("style-loader"), isEnvProduction && { loader: MiniCssExtractPlugin.loader, // css is located in `static/css`, use '../../' to locate index.html folder // in production `paths.publicUrlOrPath` can be a relative path - options: paths.publicUrlOrPath.startsWith('.') - ? { publicPath: '../../' } + options: paths.publicUrlOrPath.startsWith(".") + ? { publicPath: "../../" } : {}, }, { - loader: require.resolve('css-loader'), + loader: require.resolve("css-loader"), options: cssOptions, }, { // Options for PostCSS as we reference these options twice // Adds vendor prefixing based on your specified browser support in // package.json - loader: require.resolve('postcss-loader'), + loader: require.resolve("postcss-loader"), options: { postcssOptions: { // Necessary for external CSS imports to work // https://github.com/facebook/create-react-app/issues/2677 - ident: 'postcss', + ident: "postcss", config: false, plugins: !useTailwind ? [ - 'postcss-flexbugs-fixes', + "postcss-flexbugs-fixes", [ - 'postcss-preset-env', + "postcss-preset-env", { autoprefixer: { - flexbox: 'no-2009', + flexbox: "no-2009", }, stage: 3, }, @@ -146,16 +146,16 @@ module.exports = function (webpackEnv) { // Adds PostCSS Normalize as the reset css with default options, // so that it honors browserslist config in package.json // which in turn let's users customize the target behavior as per their needs. - 'postcss-normalize', + "postcss-normalize", ] : [ - 'tailwindcss', - 'postcss-flexbugs-fixes', + "tailwindcss", + "postcss-flexbugs-fixes", [ - 'postcss-preset-env', + "postcss-preset-env", { autoprefixer: { - flexbox: 'no-2009', + flexbox: "no-2009", }, stage: 3, }, @@ -165,11 +165,11 @@ module.exports = function (webpackEnv) { sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment, }, }, - ].filter(Boolean); + ].filter(Boolean) if (preProcessor) { loaders.push( { - loader: require.resolve('resolve-url-loader'), + loader: require.resolve("resolve-url-loader"), options: { sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment, root: paths.appSrc, @@ -181,23 +181,23 @@ module.exports = function (webpackEnv) { sourceMap: true, }, } - ); + ) } - return loaders; - }; + return loaders + } return { - target: ['browserslist'], + target: ["browserslist"], // Webpack noise constrained to errors and warnings - stats: 'errors-warnings', - mode: isEnvProduction ? 'production' : isEnvDevelopment && 'development', + stats: "errors-warnings", + mode: isEnvProduction ? "production" : isEnvDevelopment && "development", // Stop compilation early in production bail: isEnvProduction, devtool: isEnvProduction ? shouldUseSourceMap - ? 'source-map' + ? "source-map" : false - : isEnvDevelopment && 'cheap-module-source-map', + : isEnvDevelopment && "cheap-module-source-map", // These are the "entry points" to our application. // This means they will be the "root" imports that are included in JS bundle. entry: paths.appIndexJs, @@ -209,41 +209,42 @@ module.exports = function (webpackEnv) { // There will be one main bundle, and one file per asynchronous chunk. // In development, it does not produce real files. filename: isEnvProduction - ? 'static/js/[name].[contenthash:8].js' - : isEnvDevelopment && 'static/js/bundle.js', + ? "static/js/[name].[contenthash:8].js" + : isEnvDevelopment && "static/js/bundle.js", // There are also additional JS chunk files if you use code splitting. chunkFilename: isEnvProduction - ? 'static/js/[name].[contenthash:8].chunk.js' - : isEnvDevelopment && 'static/js/[name].chunk.js', - assetModuleFilename: 'static/media/[name].[hash][ext]', + ? "static/js/[name].[contenthash:8].chunk.js" + : isEnvDevelopment && "static/js/[name].chunk.js", + assetModuleFilename: "static/media/[name].[hash][ext]", // webpack uses `publicPath` to determine where the app is being served from. // It requires a trailing slash, or the file assets will get an incorrect path. // We inferred the "public path" (such as / or /my-project) from homepage. publicPath: paths.publicUrlOrPath, // Point sourcemap entries to original disk location (format as URL on Windows) devtoolModuleFilenameTemplate: isEnvProduction - ? info => + ? (info) => path .relative(paths.appSrc, info.absoluteResourcePath) - .replace(/\\/g, '/') + .replace(/\\/g, "/") : isEnvDevelopment && - (info => path.resolve(info.absoluteResourcePath).replace(/\\/g, '/')), + ((info) => + path.resolve(info.absoluteResourcePath).replace(/\\/g, "/")), }, cache: { - type: 'filesystem', + type: "filesystem", version: createEnvironmentHash(env.raw), cacheDirectory: paths.appWebpackCache, - store: 'pack', + store: "pack", buildDependencies: { - defaultWebpack: ['webpack/lib/'], + defaultWebpack: ["webpack/lib/"], config: [__filename], - tsconfig: [paths.appTsConfig, paths.appJsConfig].filter(f => + tsconfig: [paths.appTsConfig, paths.appJsConfig].filter((f) => fs.existsSync(f) ), }, }, infrastructureLogging: { - level: 'none', + level: "none", }, optimization: { minimize: isEnvProduction, @@ -297,7 +298,7 @@ module.exports = function (webpackEnv) { // We placed these paths second because we want `node_modules` to "win" // if there are any conflicts. This matches Node resolution mechanism. // https://github.com/facebook/create-react-app/issues/253 - modules: ['node_modules', paths.appNodeModules].concat( + modules: ["node_modules", paths.appNodeModules].concat( modules.additionalModulePaths || [] ), // These are the reasonable defaults supported by the Node ecosystem. @@ -307,18 +308,22 @@ module.exports = function (webpackEnv) { // `web` extension prefixes have been added for better support // for React Native Web. extensions: paths.moduleFileExtensions - .map(ext => `.${ext}`) - .filter(ext => useTypeScript || !ext.includes('ts')), + .map((ext) => `.${ext}`) + .filter((ext) => useTypeScript || !ext.includes("ts")), alias: { // Support React Native Web // https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/ - 'react-native': 'react-native-web', + "react-native": "react-native-web", // Allows for better profiling with ReactDevTools ...(isEnvProductionProfile && { - 'react-dom$': 'react-dom/profiling', - 'scheduler/tracing': 'scheduler/tracing-profiling', + "react-dom$": "react-dom/profiling", + "scheduler/tracing": "scheduler/tracing-profiling", }), ...(modules.webpackAliases || {}), + "@src": path.resolve(__dirname, "../src"), + "@components": path.resolve(__dirname, "../src/components"), + "@stores": path.resolve(__dirname, "../src/stores"), + "@screens": path.resolve(__dirname, "../src/screens"), }, plugins: [ // Prevents users from importing files from outside of src/ (or node_modules/). @@ -341,10 +346,10 @@ module.exports = function (webpackEnv) { rules: [ // Handle node_modules packages that contain sourcemaps shouldUseSourceMap && { - enforce: 'pre', + enforce: "pre", exclude: /@babel(?:\/|\\{1,2})runtime/, test: /\.(js|mjs|jsx|ts|tsx|css)$/, - loader: require.resolve('source-map-loader'), + loader: require.resolve("source-map-loader"), }, { // "oneOf" will traverse all following loaders until one will @@ -355,8 +360,8 @@ module.exports = function (webpackEnv) { // https://github.com/jshttp/mime-db { test: [/\.avif$/], - type: 'asset', - mimetype: 'image/avif', + type: "asset", + mimetype: "image/avif", parser: { dataUrlCondition: { maxSize: imageInlineSizeLimit, @@ -368,7 +373,7 @@ module.exports = function (webpackEnv) { // A missing `test` is equivalent to a match. { test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/], - type: 'asset', + type: "asset", parser: { dataUrlCondition: { maxSize: imageInlineSizeLimit, @@ -379,7 +384,7 @@ module.exports = function (webpackEnv) { test: /\.svg$/, use: [ { - loader: require.resolve('@svgr/webpack'), + loader: require.resolve("@svgr/webpack"), options: { prettier: false, svgo: false, @@ -391,9 +396,9 @@ module.exports = function (webpackEnv) { }, }, { - loader: require.resolve('file-loader'), + loader: require.resolve("file-loader"), options: { - name: 'static/media/[name].[hash].[ext]', + name: "static/media/[name].[hash].[ext]", }, }, ], @@ -406,24 +411,24 @@ module.exports = function (webpackEnv) { { test: /\.(js|mjs|jsx|ts|tsx)$/, include: paths.appSrc, - loader: require.resolve('babel-loader'), + loader: require.resolve("babel-loader"), options: { customize: require.resolve( - 'babel-preset-react-app/webpack-overrides' + "babel-preset-react-app/webpack-overrides" ), presets: [ [ - require.resolve('babel-preset-react-app'), + require.resolve("babel-preset-react-app"), { - runtime: hasJsxRuntime ? 'automatic' : 'classic', + runtime: hasJsxRuntime ? "automatic" : "classic", }, ], ], - + plugins: [ isEnvDevelopment && shouldUseReactRefresh && - require.resolve('react-refresh/babel'), + require.resolve("react-refresh/babel"), ].filter(Boolean), // This is a feature of `babel-loader` for webpack (not Babel itself). // It enables caching results in ./node_modules/.cache/babel-loader/ @@ -439,21 +444,21 @@ module.exports = function (webpackEnv) { { test: /\.(js|mjs)$/, exclude: /@babel(?:\/|\\{1,2})runtime/, - loader: require.resolve('babel-loader'), + loader: require.resolve("babel-loader"), options: { babelrc: false, configFile: false, compact: false, presets: [ [ - require.resolve('babel-preset-react-app/dependencies'), + require.resolve("babel-preset-react-app/dependencies"), { helpers: true }, ], ], cacheDirectory: true, // See #6846 for context on why cacheCompression is disabled cacheCompression: false, - + // Babel sourcemaps are needed for debugging into node_modules // code. Without the options below, debuggers like VSCode // show incorrect code and set breakpoints on the wrong lines. @@ -477,7 +482,7 @@ module.exports = function (webpackEnv) { ? shouldUseSourceMap : isEnvDevelopment, modules: { - mode: 'icss', + mode: "icss", }, }), // Don't consider CSS imports dead code even if the @@ -496,7 +501,7 @@ module.exports = function (webpackEnv) { ? shouldUseSourceMap : isEnvDevelopment, modules: { - mode: 'local', + mode: "local", getLocalIdent: getCSSModuleLocalIdent, }, }), @@ -514,10 +519,10 @@ module.exports = function (webpackEnv) { ? shouldUseSourceMap : isEnvDevelopment, modules: { - mode: 'icss', + mode: "icss", }, }, - 'sass-loader' + "sass-loader" ), // Don't consider CSS imports dead code even if the // containing package claims to have no side effects. @@ -536,11 +541,11 @@ module.exports = function (webpackEnv) { ? shouldUseSourceMap : isEnvDevelopment, modules: { - mode: 'local', + mode: "local", getLocalIdent: getCSSModuleLocalIdent, }, }, - 'sass-loader' + "sass-loader" ), }, // "file" loader makes sure those assets get served by WebpackDevServer. @@ -554,7 +559,7 @@ module.exports = function (webpackEnv) { // Also exclude `html` and `json` extensions so they get processed // by webpacks internal loaders. exclude: [/^$/, /\.(js|mjs|jsx|ts|tsx)$/, /\.html$/, /\.json$/], - type: 'asset/resource', + type: "asset/resource", }, // ** STOP ** Are you adding a new loader? // Make sure to add the new loader(s) before the "file" loader. @@ -625,8 +630,8 @@ module.exports = function (webpackEnv) { new MiniCssExtractPlugin({ // Options similar to the same options in webpackOptions.output // both options are optional - filename: 'static/css/[name].[contenthash:8].css', - chunkFilename: 'static/css/[name].[contenthash:8].chunk.css', + filename: "static/css/[name].[contenthash:8].css", + chunkFilename: "static/css/[name].[contenthash:8].chunk.css", }), // Generate an asset manifest file with the following content: // - "files" key: Mapping of all asset filenames to their corresponding @@ -635,21 +640,21 @@ module.exports = function (webpackEnv) { // - "entrypoints" key: Array of files which are included in `index.html`, // can be used to reconstruct the HTML if necessary new WebpackManifestPlugin({ - fileName: 'asset-manifest.json', + fileName: "asset-manifest.json", publicPath: paths.publicUrlOrPath, generate: (seed, files, entrypoints) => { const manifestFiles = files.reduce((manifest, file) => { - manifest[file.name] = file.path; - return manifest; - }, seed); + manifest[file.name] = file.path + return manifest + }, seed) const entrypointFiles = entrypoints.main.filter( - fileName => !fileName.endsWith('.map') - ); + (fileName) => !fileName.endsWith(".map") + ) return { files: manifestFiles, entrypoints: entrypointFiles, - }; + } }, }), // Moment.js is an extremely popular library that bundles large locale files @@ -679,7 +684,7 @@ module.exports = function (webpackEnv) { new ForkTsCheckerWebpackPlugin({ async: isEnvDevelopment, typescript: { - typescriptPath: resolve.sync('typescript', { + typescriptPath: resolve.sync("typescript", { basedir: paths.appNodeModules, }), configOverwrite: { @@ -699,7 +704,7 @@ module.exports = function (webpackEnv) { diagnosticOptions: { syntactic: true, }, - mode: 'write-references', + mode: "write-references", // profile: true, }, issue: { @@ -708,41 +713,41 @@ module.exports = function (webpackEnv) { // '../cra-template-typescript/template/src/App.tsx' // otherwise. include: [ - { file: '../**/src/**/*.{ts,tsx}' }, - { file: '**/src/**/*.{ts,tsx}' }, + { file: "../**/src/**/*.{ts,tsx}" }, + { file: "**/src/**/*.{ts,tsx}" }, ], exclude: [ - { file: '**/src/**/__tests__/**' }, - { file: '**/src/**/?(*.){spec|test}.*' }, - { file: '**/src/setupProxy.*' }, - { file: '**/src/setupTests.*' }, + { file: "**/src/**/__tests__/**" }, + { file: "**/src/**/?(*.){spec|test}.*" }, + { file: "**/src/setupProxy.*" }, + { file: "**/src/setupTests.*" }, ], }, logger: { - infrastructure: 'silent', + infrastructure: "silent", }, }), !disableESLintPlugin && new ESLintPlugin({ // Plugin options - extensions: ['js', 'mjs', 'jsx', 'ts', 'tsx'], - formatter: require.resolve('react-dev-utils/eslintFormatter'), - eslintPath: require.resolve('eslint'), + extensions: ["js", "mjs", "jsx", "ts", "tsx"], + formatter: require.resolve("react-dev-utils/eslintFormatter"), + eslintPath: require.resolve("eslint"), failOnError: !(isEnvDevelopment && emitErrorsAsWarnings), context: paths.appSrc, cache: true, cacheLocation: path.resolve( paths.appNodeModules, - '.cache/.eslintcache' + ".cache/.eslintcache" ), // ESLint class options cwd: paths.appPath, resolvePluginsRelativeTo: __dirname, baseConfig: { - extends: [require.resolve('eslint-config-react-app/base')], + extends: [require.resolve("eslint-config-react-app/base")], rules: { ...(!hasJsxRuntime && { - 'react/react-in-jsx-scope': 'error', + "react/react-in-jsx-scope": "error", }), }, }, @@ -751,5 +756,5 @@ module.exports = function (webpackEnv) { // Turn off performance processing because we utilize // our own hints via the FileSizeReporter performance: false, - }; -}; + } +} diff --git a/src/App.tsx b/src/App.tsx index 164e9be..39d6241 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,9 +1,9 @@ import React from "react" -import "./App.css" +import "@App.css" import styled from "@emotion/styled" -import TodoList from "./components/Todolist" -import SizedBox from "./components/SizeBox" -import Header from "./components/Header" +import TodoList from "@components/Todolist" +import SizedBox from "@components/SizeBox" +import Header from "@components/Header" const Root = styled.div` display: flex; diff --git a/src/components/Header.tsx b/src/components/Header.tsx index 4fc9cc3..208854d 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -3,7 +3,7 @@ import React from "react" import { Text } from "./Text" import { Row } from "./Flex" import SizedBox from "./SizeBox" -import add from "../assets/icons/Add.svg" +import add from "@assets/icons/Add.svg" interface IProps {} diff --git a/src/components/Task.tsx b/src/components/Task.tsx index 80bbab9..23f60df 100644 --- a/src/components/Task.tsx +++ b/src/components/Task.tsx @@ -1,9 +1,8 @@ import styled from "@emotion/styled" import React from "react" -import { Text } from "./Text" -import bin from "../assets/icons/Bin.svg" +import bin from "@assets/icons/Bin.svg" import SizedBox from "./SizeBox" -import check from "../assets/icons/Check.svg" +import check from "@assets/icons/Check.svg" import { Tag } from "./Tag" import { Row } from "./Flex" import EditableTitle from "./EditableTitle" @@ -49,8 +48,6 @@ const Check = styled.div` flex-shrink: 0; ` - - const StyledRow = styled(Row)` justify-content: space-between; align-items: center; diff --git a/src/components/Todolist.tsx b/src/components/Todolist.tsx index da95661..bde55a3 100644 --- a/src/components/Todolist.tsx +++ b/src/components/Todolist.tsx @@ -1,6 +1,6 @@ import styled from "@emotion/styled" import React from "react" -import bin from "../assets/icons/Bin.svg" +import bin from "@/assets/icons/Bin.svg" import SizedBox from "./SizeBox" import Task from "./Task" import EditableTitle from "./EditableTitle" diff --git a/tsconfig.json b/tsconfig.json index a273b0c..88a11ab 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -18,8 +18,20 @@ "resolveJsonModule": true, "isolatedModules": true, "noEmit": true, - "jsx": "react-jsx" + "jsx": "react-jsx", + "baseUrl": ".", + "paths": { + "@src": ["src"], + "@src/*": ["src/*"], + "@components": ["src/components/"], + "@components/*": ["src/components/*"], + "@stores": ["src/stores"], + "@stores/*": ["src/stores/*"], + "@screens": ["src/screens"], + "@screens/*": ["src/screens/*"] + } }, + "include": [ "src" ] From f3c97eef769ec3c4b4c1201da6285e56f809e2ad Mon Sep 17 00:00:00 2001 From: Paul Date: Tue, 3 Oct 2023 12:45:40 +0300 Subject: [PATCH 13/17] right some magic --- config/webpack.config.js | 1 + src/App.tsx | 2 +- src/components/Todolist.tsx | 2 +- tsconfig.json | 4 +++- 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/config/webpack.config.js b/config/webpack.config.js index abc77ae..5c286cb 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -324,6 +324,7 @@ module.exports = function (webpackEnv) { "@components": path.resolve(__dirname, "../src/components"), "@stores": path.resolve(__dirname, "../src/stores"), "@screens": path.resolve(__dirname, "../src/screens"), + "@assets": path.resolve(__dirname, "../src/assets"), }, plugins: [ // Prevents users from importing files from outside of src/ (or node_modules/). diff --git a/src/App.tsx b/src/App.tsx index 39d6241..b963916 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,5 +1,5 @@ import React from "react" -import "@App.css" +import "./App.css" import styled from "@emotion/styled" import TodoList from "@components/Todolist" import SizedBox from "@components/SizeBox" diff --git a/src/components/Todolist.tsx b/src/components/Todolist.tsx index bde55a3..87f8924 100644 --- a/src/components/Todolist.tsx +++ b/src/components/Todolist.tsx @@ -1,6 +1,6 @@ import styled from "@emotion/styled" import React from "react" -import bin from "@/assets/icons/Bin.svg" +import bin from "@assets/icons/Bin.svg" import SizedBox from "./SizeBox" import Task from "./Task" import EditableTitle from "./EditableTitle" diff --git a/tsconfig.json b/tsconfig.json index 88a11ab..068aa17 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -28,7 +28,9 @@ "@stores": ["src/stores"], "@stores/*": ["src/stores/*"], "@screens": ["src/screens"], - "@screens/*": ["src/screens/*"] + "@screens/*": ["src/screens/*"], + "@assets": ["src/assets"], + "@assets/*": ["src/assets/*"], } }, From d4df049ed46a24dd7980fdef4181a294c8f2917d Mon Sep 17 00:00:00 2001 From: Paul Date: Wed, 4 Oct 2023 01:03:07 +0300 Subject: [PATCH 14/17] storm --- src/components/Flex.ts | 20 ++++++++++---------- src/components/Task.tsx | 6 ++---- src/components/Todolist.tsx | 4 ++-- 3 files changed, 14 insertions(+), 16 deletions(-) diff --git a/src/components/Flex.ts b/src/components/Flex.ts index d03dcd4..ebf7a5c 100644 --- a/src/components/Flex.ts +++ b/src/components/Flex.ts @@ -32,13 +32,13 @@ export const Row = styled.div` mainAxisSize === "fit-content" ? "fit-content" : "100%"}; `; -export const Column = styled.div` - display: flex; - flex-direction: column; - justify-content: ${({ justifyContent }) => justifyContent ?? "start"}; - align-items: ${({ alignItems }) => alignItems ?? "start"}; - width: ${({ crossAxisSize }) => - crossAxisSize === "max" ? "100%" : "fit-content"}; - height: ${({ mainAxisSize }) => - mainAxisSize === "stretch" ? "100%" : "fit-content"}; -`; +// export const Column = styled.div` +// display: flex; +// flex-direction: column; +// justify-content: ${({ justifyContent }) => justifyContent ?? "start"}; +// align-items: ${({ alignItems }) => alignItems ?? "start"}; +// width: ${({ crossAxisSize }) => +// crossAxisSize === "max" ? "100%" : "fit-content"}; +// height: ${({ mainAxisSize }) => +// mainAxisSize === "stretch" ? "100%" : "fit-content"}; +// `; diff --git a/src/components/Task.tsx b/src/components/Task.tsx index 23f60df..ff10e85 100644 --- a/src/components/Task.tsx +++ b/src/components/Task.tsx @@ -17,10 +17,8 @@ const Root = styled.div` display: flex; flex-direction: column; padding: 20px; - background: #f3f5f6; - - border-radius: 12px; background: #fff; + border-radius: 12px; box-shadow: 0px 10px 0px 0px rgba(0, 0, 0, 0.15); & .remove-task-button, @@ -64,7 +62,7 @@ const Task: React.FC = () => { fontWeight="700" textTransform="uppercase" color="#363636" - showUnderline={true} + showUnderline inputLength={20} /> diff --git a/src/components/Todolist.tsx b/src/components/Todolist.tsx index 87f8924..a3defe8 100644 --- a/src/components/Todolist.tsx +++ b/src/components/Todolist.tsx @@ -69,7 +69,7 @@ const TodoList: React.FC = () => { fontSize="28px" fontWeight="600" opacity="0.7" - showUnderline={true} + showUnderline inputLength={20} /> @@ -83,4 +83,4 @@ const TodoList: React.FC = () => { ) } -export default TodoList +export default TodoList \ No newline at end of file From 283e3edbd7580a1a7b51b634b685c726b18aa58a Mon Sep 17 00:00:00 2001 From: Paul Date: Wed, 4 Oct 2023 16:16:41 +0300 Subject: [PATCH 15/17] need help --- src/components/EditableTitle.tsx | 15 ++++++++++----- src/components/Task.tsx | 6 ++++++ src/components/Todolist.tsx | 4 ++++ src/stores/Store.ts | 18 ++++++++++++++++++ 4 files changed, 38 insertions(+), 5 deletions(-) create mode 100644 src/stores/Store.ts diff --git a/src/components/EditableTitle.tsx b/src/components/EditableTitle.tsx index bfa3653..9dea2eb 100644 --- a/src/components/EditableTitle.tsx +++ b/src/components/EditableTitle.tsx @@ -1,6 +1,7 @@ import styled from "@emotion/styled" import React, { ChangeEvent, useEffect, useRef, useState } from "react" import { Text } from "./Text" +import { InputStore } from "@src/stores/Store" interface IProps { fontSize?: string @@ -11,6 +12,7 @@ interface IProps { opacity?: string startTitle?: string inputLength?: number | undefined + inputStore: InputStore } const Root = styled.div` @@ -23,7 +25,6 @@ const StyledInput = styled.input` border: none; outline: none; background: transparent; - font-size: ${(props) => (props.fontSize ? props.fontSize : "inherit")}; opacity: ${(props) => (props.opacity ? props.opacity : "inherit")}; color: ${(props) => (props.color ? props.color : "inherit")}; @@ -33,6 +34,7 @@ const StyledInput = styled.input` border-bottom: ${(props) => props.showUnderline ? "2px solid #000" : "none"}; maxlength: ${(props) => (props.inputLength ? props.inputLength : "inherit")}; + ` const StyledText = styled(Text)` @@ -53,8 +55,11 @@ const EditableTitle: React.FC = ({ opacity, startTitle = "", inputLength, + inputStore, }) => { - const [title, setTitle] = useState(startTitle) + // const [title, setTitle] = useState(startTitle) + // const inputStore = new InputStore() + const title = inputStore.inputValue const [editing, setEditing] = useState(false) const inputRef = useRef(null) @@ -63,11 +68,11 @@ const EditableTitle: React.FC = ({ } const handleTitleChange = (event: ChangeEvent) => { - setTitle(event?.target.value) + inputStore.setInputValue(event?.target.value) } const handleTitleFix = () => { - if (title.trim() !== "") { + if (inputStore.inputValue.trim() !== "") { setEditing(false) } } @@ -90,7 +95,7 @@ const EditableTitle: React.FC = ({ {editing ? ( = () => { + const inputTaskStore = new InputStore() + return ( = () => { = () => { + const inputTodolistStore = new InputStore() return ( Date: Thu, 5 Oct 2023 14:32:54 +0200 Subject: [PATCH 16/17] added mobx --- package-lock.json | 67 ++++++++++++++++++++++++++++++++ package.json | 2 + src/App.tsx | 8 ++-- src/components/EditableTitle.tsx | 51 +++++------------------- src/components/Header.tsx | 11 +++++- src/components/StyledInput.tsx | 39 +++++++++++++++++++ src/components/Task.tsx | 19 +++++---- src/components/Todolist.tsx | 25 +++++++----- src/index.tsx | 18 ++++++++- src/stores/RootStore.ts | 20 ++++++++++ src/stores/Store.ts | 18 --------- src/stores/TaskStore.ts | 35 +++++++++++++++++ src/stores/index.tsx | 11 ++++++ src/stores/useStores.ts | 12 ++++++ src/utils/localStorage.ts | 12 ++++++ 15 files changed, 264 insertions(+), 84 deletions(-) create mode 100644 src/components/StyledInput.tsx create mode 100644 src/stores/RootStore.ts delete mode 100644 src/stores/Store.ts create mode 100644 src/stores/TaskStore.ts create mode 100644 src/stores/index.tsx create mode 100644 src/stores/useStores.ts create mode 100644 src/utils/localStorage.ts diff --git a/package-lock.json b/package-lock.json index d87b276..fbcaf56 100644 --- a/package-lock.json +++ b/package-lock.json @@ -42,6 +42,8 @@ "jest-resolve": "^27.4.2", "jest-watch-typeahead": "^1.0.0", "mini-css-extract-plugin": "^2.4.5", + "mobx": "^6.10.2", + "mobx-react": "^9.0.1", "postcss": "^8.4.4", "postcss-flexbugs-fixes": "^5.0.2", "postcss-loader": "^6.2.1", @@ -12393,6 +12395,63 @@ "mkdirp": "bin/cmd.js" } }, + "node_modules/mobx": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/mobx/-/mobx-6.10.2.tgz", + "integrity": "sha512-B1UGC3ieK3boCjnMEcZSwxqRDMdzX65H/8zOHbuTY8ZhvrIjTUoLRR2TP2bPqIgYRfb3+dUigu8yMZufNjn0LQ==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mobx" + } + }, + "node_modules/mobx-react": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/mobx-react/-/mobx-react-9.0.1.tgz", + "integrity": "sha512-0idiElBgNMJg20YqGgHvYEnlqIJpPDQaOkxj2dHJIZeqCvUh+zBkBkMkpUFw/uEd1OdPUvT0y+AFBqsWIUAXww==", + "dependencies": { + "mobx-react-lite": "^4.0.4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mobx" + }, + "peerDependencies": { + "mobx": "^6.9.0", + "react": "^16.8.0 || ^17 || ^18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, + "node_modules/mobx-react-lite": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/mobx-react-lite/-/mobx-react-lite-4.0.5.tgz", + "integrity": "sha512-StfB2wxE8imKj1f6T8WWPf4lVMx3cYH9Iy60bbKXEs21+HQ4tvvfIBZfSmMXgQAefi8xYEwQIz4GN9s0d2h7dg==", + "dependencies": { + "use-sync-external-store": "^1.2.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mobx" + }, + "peerDependencies": { + "mobx": "^6.9.0", + "react": "^16.8.0 || ^17 || ^18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -16721,6 +16780,14 @@ "requires-port": "^1.0.0" } }, + "node_modules/use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", diff --git a/package.json b/package.json index b7e3196..8f4e218 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,8 @@ "jest-resolve": "^27.4.2", "jest-watch-typeahead": "^1.0.0", "mini-css-extract-plugin": "^2.4.5", + "mobx": "^6.10.2", + "mobx-react": "^9.0.1", "postcss": "^8.4.4", "postcss-flexbugs-fixes": "^5.0.2", "postcss-loader": "^6.2.1", diff --git a/src/App.tsx b/src/App.tsx index b963916..4a5667d 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -30,10 +30,10 @@ function App() {
- - - - + {/**/} + {/**/} + {/**/} + {/**/} ) diff --git a/src/components/EditableTitle.tsx b/src/components/EditableTitle.tsx index 9dea2eb..62154c7 100644 --- a/src/components/EditableTitle.tsx +++ b/src/components/EditableTitle.tsx @@ -1,18 +1,9 @@ import styled from "@emotion/styled" import React, { ChangeEvent, useEffect, useRef, useState } from "react" -import { Text } from "./Text" -import { InputStore } from "@src/stores/Store" +import {IEditableInputProps, StyledInput, StyledText} from "@components/StyledInput"; -interface IProps { - fontSize?: string - color?: string - fontWeight?: string - textTransform?: string - showUnderline?: boolean - opacity?: string - startTitle?: string - inputLength?: number | undefined - inputStore: InputStore +interface IProps extends IEditableInputProps{ + onChange?: (str: string) => void } const Root = styled.div` @@ -21,30 +12,6 @@ const Root = styled.div` width: auto; height: 27px; ` -const StyledInput = styled.input` - border: none; - outline: none; - background: transparent; - font-size: ${(props) => (props.fontSize ? props.fontSize : "inherit")}; - opacity: ${(props) => (props.opacity ? props.opacity : "inherit")}; - color: ${(props) => (props.color ? props.color : "inherit")}; - font-weight: ${(props) => (props.fontWeight ? props.fontWeight : "inherit")}; - text-transform: ${(props) => - props.textTransform ? props.textTransform : "inherit"}; - border-bottom: ${(props) => - props.showUnderline ? "2px solid #000" : "none"}; - maxlength: ${(props) => (props.inputLength ? props.inputLength : "inherit")}; - -` - -const StyledText = styled(Text)` - font-size: ${(props) => (props.fontSize ? props.fontSize : "inherit")}; - text-transform: ${(props) => - props.textTransform ? props.textTransform : "inherit"}; - opacity: ${(props) => (props.opacity ? props.opacity : "inherit")}; - font-weight: ${(props) => (props.fontWeight ? props.fontWeight : "inherit")}; - color: ${(props) => (props.color ? props.color : "inherit")}; -` const EditableTitle: React.FC = ({ fontSize, @@ -55,11 +22,10 @@ const EditableTitle: React.FC = ({ opacity, startTitle = "", inputLength, - inputStore, + ...rest }) => { - // const [title, setTitle] = useState(startTitle) + const [title, setTitle] = useState(startTitle) // const inputStore = new InputStore() - const title = inputStore.inputValue const [editing, setEditing] = useState(false) const inputRef = useRef(null) @@ -68,11 +34,12 @@ const EditableTitle: React.FC = ({ } const handleTitleChange = (event: ChangeEvent) => { - inputStore.setInputValue(event?.target.value) + setTitle(event?.target.value) + rest.onChange && rest.onChange(event?.target.value ?? "") } const handleTitleFix = () => { - if (inputStore.inputValue.trim() !== "") { + if (title.trim() !== "") { setEditing(false) } } @@ -95,7 +62,7 @@ const EditableTitle: React.FC = ({ {editing ? ( = () => { + const {taskStore} = useStores() + let defaultTask: TTask = { + title: "New task", + description: "Blablabla", + status: TASK_STATUS.ACTIVE + } return ( Project name @@ -45,7 +54,7 @@ const Header: React.FC = () => { Add new column - + taskStore.addTask(defaultTask)} /> ) diff --git a/src/components/StyledInput.tsx b/src/components/StyledInput.tsx new file mode 100644 index 0000000..ae12a20 --- /dev/null +++ b/src/components/StyledInput.tsx @@ -0,0 +1,39 @@ +import {Text} from "@components/Text"; +import styled from "@emotion/styled"; + +export interface IEditableInputProps { + fontSize?: string + color?: string + fontWeight?: string + textTransform?: string + showUnderline?: boolean + opacity?: string + startTitle?: string + inputLength?: number | undefined +} + +export const StyledInput = styled.input` + border: none; + outline: none; + background: transparent; + font-size: ${(props) => (props.fontSize ? props.fontSize : "inherit")}; + opacity: ${(props) => (props.opacity ? props.opacity : "inherit")}; + color: ${(props) => (props.color ? props.color : "inherit")}; + font-weight: ${(props) => (props.fontWeight ? props.fontWeight : "inherit")}; + text-transform: ${(props) => + props.textTransform ? props.textTransform : "inherit"}; + border-bottom: ${(props) => + props.showUnderline ? "2px solid #000" : "none"}; + maxlength: ${(props) => (props.inputLength ? props.inputLength : "inherit")}; + +` + + +export const StyledText = styled(Text)` + font-size: ${(props) => (props.fontSize ? props.fontSize : "inherit")}; + text-transform: ${(props) => + props.textTransform ? props.textTransform : "inherit"}; + opacity: ${(props) => (props.opacity ? props.opacity : "inherit")}; + font-weight: ${(props) => (props.fontWeight ? props.fontWeight : "inherit")}; + color: ${(props) => (props.color ? props.color : "inherit")}; +` diff --git a/src/components/Task.tsx b/src/components/Task.tsx index bb78162..89b6cd8 100644 --- a/src/components/Task.tsx +++ b/src/components/Task.tsx @@ -6,10 +6,14 @@ import check from "@assets/icons/Check.svg" import { Tag } from "./Tag" import { Row } from "./Flex" import EditableTitle from "./EditableTitle" -import { InputStore } from "@src/stores/Store" +import {TTask} from "@stores/TaskStore"; interface IProps { + task: TTask + onEdit: (task: TTask) => void + onRemove: () => void + align?: "row" | "column" style?: React.CSSProperties bodyStyle?: React.CSSProperties @@ -54,32 +58,31 @@ const StyledRow = styled(Row)` width: 333px; ` -const Task: React.FC = () => { - const inputTaskStore = new InputStore() +const Task: React.FC = ({task, onRemove,onEdit }) => { return ( onEdit({...task, title})} /> - + onEdit({...task, description})} /> diff --git a/src/components/Todolist.tsx b/src/components/Todolist.tsx index e3bc833..1135abc 100644 --- a/src/components/Todolist.tsx +++ b/src/components/Todolist.tsx @@ -4,7 +4,8 @@ import bin from "@assets/icons/Bin.svg" import SizedBox from "./SizeBox" import Task from "./Task" import EditableTitle from "./EditableTitle" -import { InputStore } from "@src/stores/Store" +import {useStores} from "@stores"; +import { observer } from "mobx-react"; const Bin = styled.div` @@ -25,7 +26,7 @@ const Root = styled.div` padding: 25px 25px 40px 25px; background: #f3f5f6; justify-content: space-between; - width: 100%; + width: fit-content; & .remove-todolist-button { display: none; @@ -61,13 +62,12 @@ const TasksContainer = styled.div` } ` -const TodoList: React.FC = () => { - const inputTodolistStore = new InputStore() +const TodoList: React.FC = observer(() => { + const {taskStore} = useStores() return ( = () => { - - - + {taskStore.tasks.map((task, index) => + taskStore.editTask(index, task)} + onRemove={() => taskStore.removeTask(index)} + /> + )} ) -} -export default TodoList \ No newline at end of file +}) +export default TodoList diff --git a/src/index.tsx b/src/index.tsx index 032464f..44615db 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -3,13 +3,29 @@ import ReactDOM from 'react-dom/client'; import './index.css'; import App from './App'; import reportWebVitals from './reportWebVitals'; +import {loadState, saveState} from "@src/utils/localStorage"; +import {autorun} from "mobx"; +import { RootStore, storesContext } from "@stores"; + +const initState = loadState(); + +const mobxStore = new RootStore(initState); +autorun( + () => { + console.dir(mobxStore); + saveState(mobxStore.serialize()); + }, + { delay: 1000 } +); const root = ReactDOM.createRoot( document.getElementById('root') as HTMLElement ); root.render( - + + + ); diff --git a/src/stores/RootStore.ts b/src/stores/RootStore.ts new file mode 100644 index 0000000..031a460 --- /dev/null +++ b/src/stores/RootStore.ts @@ -0,0 +1,20 @@ +import { makeAutoObservable } from "mobx"; +import {TaskStore} from "@stores"; + +// const initState = { +// taskStore: { +// tasks: Array +// } +// } + +export default class RootStore { + public taskStore: TaskStore; + constructor(initState?: any) { + makeAutoObservable(this); + this.taskStore = new TaskStore(this, initState?.taskStore) + } + + serialize = () => ({ + taskStore: this.taskStore.serialize() + }); +} diff --git a/src/stores/Store.ts b/src/stores/Store.ts deleted file mode 100644 index 030918d..0000000 --- a/src/stores/Store.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { makeObservable, observable, action } from "mobx"; - -export class InputStore { - inputValue = "enter task title"; // Начальное значение input - - constructor() { - makeObservable(this, { - inputValue: observable, - setInputValue: action, - }); - } - - setInputValue(title: string) { - this.inputValue = title; - } -} - - diff --git a/src/stores/TaskStore.ts b/src/stores/TaskStore.ts new file mode 100644 index 0000000..42cb226 --- /dev/null +++ b/src/stores/TaskStore.ts @@ -0,0 +1,35 @@ +import {makeAutoObservable} from "mobx"; +import RootStore from "@stores/RootStore"; + +export enum TASK_STATUS { + ACTIVE, + FINISHED +} + +export type TTask = { + title: string, + description: string, + status: TASK_STATUS +} + +export default class TaskStore { + public readonly rootStore: RootStore; + + public tasks: Array = [] + private setTasks = (tasks: Array) => this.tasks = tasks + public addTask = (task: TTask) => this.tasks.push(task) + public removeTask = (index: number) => this.tasks.splice(index, 1) + public editTask = (index: number, task: TTask) => this.tasks[index] = task + + constructor(rootStore: RootStore, initState?: any) { + this.rootStore = rootStore; + makeAutoObservable(this); + if (initState?.tasks != null && initState.tasks.length > 0){ + this.setTasks(initState.tasks) + } + } + + serialize = () => ({tasks: this.tasks}); +} + + diff --git a/src/stores/index.tsx b/src/stores/index.tsx new file mode 100644 index 0000000..f36db88 --- /dev/null +++ b/src/stores/index.tsx @@ -0,0 +1,11 @@ +import { storesContext, useStores } from "@stores/useStores"; + +import RootStore from "./RootStore"; +import TaskStore from "./TaskStore"; + +export { + RootStore, + TaskStore, + storesContext, + useStores, +}; diff --git a/src/stores/useStores.ts b/src/stores/useStores.ts new file mode 100644 index 0000000..d2b6bd6 --- /dev/null +++ b/src/stores/useStores.ts @@ -0,0 +1,12 @@ +import React from "react"; +import RootStore from "@stores/RootStore"; + +export const storesContext = React.createContext(null); + +export const useStores = () => { + const rootStore = React.useContext(storesContext); + if (rootStore == null) { + throw new Error("No RootStore found i context"); + } + return rootStore; +}; diff --git a/src/utils/localStorage.ts b/src/utils/localStorage.ts new file mode 100644 index 0000000..2330190 --- /dev/null +++ b/src/utils/localStorage.ts @@ -0,0 +1,12 @@ +export const loadState = (): any | undefined => { + try { + const state = JSON.parse(localStorage.getItem("todolist-store") as string); + return state || undefined; + } catch (error) { + console.dir(error); + return undefined; + } +}; +export const saveState = (state: any): void => { + localStorage.setItem("todolist-store", JSON.stringify(state)); +}; From e73718b112b77893144e8c26a3bec399339f68f0 Mon Sep 17 00:00:00 2001 From: alexey Date: Fri, 13 Oct 2023 15:57:43 +0100 Subject: [PATCH 17/17] royal help from the lord of defi --- src/components/EditableTitle.tsx | 6 +++--- src/components/Task.tsx | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/EditableTitle.tsx b/src/components/EditableTitle.tsx index 62154c7..c857705 100644 --- a/src/components/EditableTitle.tsx +++ b/src/components/EditableTitle.tsx @@ -4,6 +4,7 @@ import {IEditableInputProps, StyledInput, StyledText} from "@components/StyledIn interface IProps extends IEditableInputProps{ onChange?: (str: string) => void + title?: string } const Root = styled.div` @@ -20,11 +21,11 @@ const EditableTitle: React.FC = ({ textTransform, showUnderline = false, opacity, - startTitle = "", + title = "", inputLength, ...rest }) => { - const [title, setTitle] = useState(startTitle) + // const [title, setTitle] = useState(startTitle) // const inputStore = new InputStore() const [editing, setEditing] = useState(false) const inputRef = useRef(null) @@ -34,7 +35,6 @@ const EditableTitle: React.FC = ({ } const handleTitleChange = (event: ChangeEvent) => { - setTitle(event?.target.value) rest.onChange && rest.onChange(event?.target.value ?? "") } diff --git a/src/components/Task.tsx b/src/components/Task.tsx index 89b6cd8..f2d38f8 100644 --- a/src/components/Task.tsx +++ b/src/components/Task.tsx @@ -64,7 +64,7 @@ const Task: React.FC = ({task, onRemove,onEdit }) => { = ({task, onRemove,onEdit }) => {