diff --git a/package.json b/package.json index adc4a01..26207de 100644 --- a/package.json +++ b/package.json @@ -26,5 +26,6 @@ "typescript": "~5.9.3", "typescript-eslint": "^8.45.0", "vite": "^7.1.7" - } + }, + "packageManager": "pnpm@10.19.0+sha512.c9fc7236e92adf5c8af42fd5bf1612df99c2ceb62f27047032f4720b33f8eacdde311865e91c411f2774f618d82f320808ecb51718bfa82c060c4ba7c76a32b8" } diff --git a/src/App.tsx b/src/App.tsx index 42be7aa..b61f4ec 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,128 +1,27 @@ -import { useEffect, useRef, useState } from "react"; +import Counter from "./components/Counter/Counter"; +import UserForm from "./components/Form/UserForm/UserForm"; +import { useItems } from "./hooks/useItems"; +import Button from "./components/Button/Button"; +import { ItemList } from "./components/Item/ItemList/ItemList"; +import { ThemeToggle } from './components/ThemeToggle/ThemeToggle'; +import AddItem from "./components/Item/AddItem/AddItem"; export default function App() { - const [count, setCount] = useState(0); - const [isDarkMode, setIsDarkMode] = useState(false); - const [name, setName] = useState(""); - const [email, setEmail] = useState(""); - const [isVisible, setIsVisible] = useState(true); - const [items, setItems] = useState([]); - const [selectedItem, setSelectedItem] = useState(null); - - const inputRef = useRef(null); - - useEffect(() => { - const storedCount = localStorage.getItem("count"); - if (storedCount) { - setCount(parseInt(storedCount)); - } - }, []); - - useEffect(() => { - localStorage.setItem("count", count.toString()); - }, [count]); - - useEffect(() => { - document.title = `Hello ${name}`; - }, [name]); - - useEffect(() => { - if (selectedItem) { - console.log(selectedItem); - } - }, [selectedItem]); - - useEffect(() => { - document.body.classList.toggle("dark", isDarkMode); - }, [isDarkMode]); - - const increment = () => { - setCount(count + 1); - }; - - const decrement = () => { - setCount(count - 1); - }; - - const handleNameChange = (e: React.ChangeEvent) => { - setName(e.target.value); - }; - - const handleEmailChange = (e: React.ChangeEvent) => { - setEmail(e.target.value); - }; - - const toggleVisibility = () => { - setIsVisible(!isVisible); - }; - - function addItem() { - if (inputRef.current && inputRef.current.value) { - setItems([...items, inputRef.current.value]); - inputRef.current.value = ""; - } - } - - async function handleSubmit() { - // Do something with data... - } - + const { items, isVisible, inputValue, setInputValue,setSelectedItem, addItem, toggleVisibility } = useItems(); return ( -
-

Outside

-
- - -

Count: {count}

-
-
-
- -

Name: {name}

-
-
- -

Email: {email}

-
-
-
- - {isVisible && ( -
- - -
    - {items.map((item, index) => ( -
  • setSelectedItem(item)}> - {item} -
  • - ))} -
-
- )} -
-
-

Selected Item: {selectedItem}

-
-
-
-
- +
+ + +
+ + {isVisible && ( + <> + + + + )}
+
); -} +} \ No newline at end of file diff --git a/src/components/Button/Button.tsx b/src/components/Button/Button.tsx new file mode 100644 index 0000000..de7db5d --- /dev/null +++ b/src/components/Button/Button.tsx @@ -0,0 +1,15 @@ +import type React from "react"; + +interface ButtonProps { + value: string; + onClick?: (event: React.MouseEvent) => void; + className?:string; +} + +const Button = ({ value, onClick, className }: ButtonProps) => { + return ( + + ); +}; + +export default Button; \ No newline at end of file diff --git a/src/components/Counter/Counter.tsx b/src/components/Counter/Counter.tsx new file mode 100644 index 0000000..3b62e09 --- /dev/null +++ b/src/components/Counter/Counter.tsx @@ -0,0 +1,21 @@ +import Button from "../Button/Button"; +import styles from "./style.module.css" +import { useCounter } from "../../hooks/useCounters"; + + +const Counter = () => { + const { count, handleIncrement, handleDecrement } = useCounter(); + return ( +
+
+

COUNT: {count}

+
+
+
+
+ ) +} + +export default Counter \ No newline at end of file diff --git a/src/components/Counter/style.module.css b/src/components/Counter/style.module.css new file mode 100644 index 0000000..986a21f --- /dev/null +++ b/src/components/Counter/style.module.css @@ -0,0 +1,40 @@ +.increment, .decrement{ + background-color: rgb(32, 175, 32); + padding: 15px 20px; + color: white; + border-radius: 12px; + border: none; + font-size: 15px; + font-weight: 700; + margin: 10px 10px; + cursor: pointer; + box-shadow: + 1px 1px rgb(161, 161, 161), + -0.4em 0 0.4em rgb(155, 155, 154); +} + +.increment:hover, +.decrement:hover{ + opacity: 0.7; +} + +.decrement{ + background-color: rgb(240, 66, 35); +} + +.count{ + color: rgb(72, 72, 240); + font-size: 3rem; + text-align: center; + font-weight: 700; +} + +.background{ + /* background-color: rgb(217, 217, 218); */ + padding: 10px 10px; +} + +.buttonSection{ + display: flex; + justify-content: center; +} diff --git a/src/components/Form/InputField/InputField.tsx b/src/components/Form/InputField/InputField.tsx new file mode 100644 index 0000000..1ca2b5e --- /dev/null +++ b/src/components/Form/InputField/InputField.tsx @@ -0,0 +1,28 @@ +import type React from "react"; +import styles from "./style.module.css"; + +interface InputFieldProps { + label: string; + type: string; + value: string; + onChange: (e: React.ChangeEvent) => void; +} + +const InputField = ({ label, type, value, onChange }: InputFieldProps) => { + return ( +
+ + +
+ ); +}; + +export default InputField; diff --git a/src/components/Form/InputField/style.module.css b/src/components/Form/InputField/style.module.css new file mode 100644 index 0000000..69bc831 --- /dev/null +++ b/src/components/Form/InputField/style.module.css @@ -0,0 +1,37 @@ +.input-field { + display: flex; + flex-direction: column; + margin-bottom: 16px; + width: 100%; + max-width: 400px; + margin: auto; + padding: 20px 20px; +} + +.input-field__label { + font-weight: 600; + margin-bottom: 6px; + color: #333; + font-size: 14px; +} + +.input-field__input { + padding: 8px 12px; + border: 1px solid #ccc; + border-radius: 6px; + font-size: 14px; + transition: border-color 0.2s, box-shadow 0.2s; +} + +.input-field__input:focus { + outline: none; + border-color: #007bff; + box-shadow: 0 0 5px rgba(0, 123, 255, 0.5); +} + +.input-field__value { + margin-top: 4px; + font-size: 12px; + color: #555; + font-style: italic; +} diff --git a/src/components/Form/UserForm/UserForm.tsx b/src/components/Form/UserForm/UserForm.tsx new file mode 100644 index 0000000..f868a3a --- /dev/null +++ b/src/components/Form/UserForm/UserForm.tsx @@ -0,0 +1,36 @@ +import React, { useState } from 'react'; +import InputField from '../InputField/InputField'; +import useFormFields from '../../../hooks/useFormFields'; +import Button from '../../Button/Button'; +import styles from './style.module.css'; + +const UserForm = () => { + const { name, email, setName, setEmail } = useFormFields(); + const [submittedName, setSubmittedName] = useState(null); + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + setSubmittedName(name); + setName(''); + setEmail(''); + }; + + return ( +
+
+
+ setName(e.target.value)} /> + setEmail(e.target.value)} /> +
+
+ ); +}; + +export default UserForm; diff --git a/src/components/Form/UserForm/style.module.css b/src/components/Form/UserForm/style.module.css new file mode 100644 index 0000000..65b47f9 --- /dev/null +++ b/src/components/Form/UserForm/style.module.css @@ -0,0 +1,50 @@ +.user-form { + display: flex; + flex-direction: column; + align-items: center; + padding: 24px; + background-color: #f9f9f9; + border-radius: 12px; + max-width: 450px; + margin: 20px auto; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); +} + +.user-form__form { + display: flex; + flex-direction: column; + width:30%; + margin: auto; +} + +.user-form__button { + margin-top: 12px; + padding: 10px 16px; + background-color: #007bff; + border: none; + border-radius: 6px; + color: #fff; + font-weight: 600; + cursor: pointer; + transition: background-color 0.2s; +} + +.user-form__button:hover { + background-color: #0056b3; +} + +.user-form__welcome{ + margin-top: 16px; + font-size: 16px; + font-weight: 500; + color: black; + text-align: center; +} + +.user-form__welcome .dark { + margin-top: 16px; + font-size: 16px; + font-weight: 500; + color: white; + text-align: center;; +} diff --git a/src/components/Item/AddItem/AddItem.tsx b/src/components/Item/AddItem/AddItem.tsx new file mode 100644 index 0000000..f1c46c0 --- /dev/null +++ b/src/components/Item/AddItem/AddItem.tsx @@ -0,0 +1,18 @@ +import Button from "../../Button/Button"; +import styles from './style.module.css' +interface AddItemProps { + addItem: () => void; + inputValue: string; + setInputValue: (value: string) => void; +} + +const AddItem = ({ addItem, inputValue, setInputValue }: AddItemProps) =>{ + return ( +
+ setInputValue(e.target.value)} type="text" placeholder="Add item" className={styles["add-item__input"]}/> +
); + +}; + +export default AddItem; \ No newline at end of file diff --git a/src/components/Item/AddItem/style.module.css b/src/components/Item/AddItem/style.module.css new file mode 100644 index 0000000..72dcbbc --- /dev/null +++ b/src/components/Item/AddItem/style.module.css @@ -0,0 +1,36 @@ +.add-item { + display: flex; + align-items: center; + gap: 8px; + margin-bottom: 16px; +} + +.add-item__input { + flex: 1; + padding: 8px 12px; + border: 1px solid #ccc; + border-radius: 6px; + font-size: 14px; + transition: border-color 0.2s, box-shadow 0.2s; +} + +.add-item__input:focus { + outline: none; + border-color: #007bff; + box-shadow: 0 0 5px rgba(0, 123, 255, 0.5); +} + +.add-item__button { + padding: 8px 16px; + background-color: #007bff; + color: #fff; + border: none; + border-radius: 6px; + font-weight: 600; + cursor: pointer; + transition: background-color 0.2s; +} + +.add-item__button:hover { + background-color: #0056b3; +} diff --git a/src/components/Item/ItemList/ItemList.tsx b/src/components/Item/ItemList/ItemList.tsx new file mode 100644 index 0000000..14508b9 --- /dev/null +++ b/src/components/Item/ItemList/ItemList.tsx @@ -0,0 +1,18 @@ +import styles from './style.module.css' +interface ItemListProps { + items: string[]; + setSelectedItem: (item: string) => void; +} + +export const ItemList = ({ items, setSelectedItem }: ItemListProps) => ( +
+ +
    + {items.map((item, index) => ( +
  • setSelectedItem(item)}> + {item} +
  • + ))} +
+
+); \ No newline at end of file diff --git a/src/components/Item/ItemList/style.module.css b/src/components/Item/ItemList/style.module.css new file mode 100644 index 0000000..6d1e9b3 --- /dev/null +++ b/src/components/Item/ItemList/style.module.css @@ -0,0 +1,39 @@ +.item-list { + list-style: none; + padding: 0; + margin: 0; + width: fit-content; +} + +.item-list__item { + position: relative; + padding: 6px 12px 6px 26px; + border-radius: 6px; + margin-bottom: 6px; + cursor: pointer; + transition: + background-color 0.25s ease, + transform 0.15s ease; +} + +.item-list__item::before { + content: ""; + position: absolute; + left: 10px; + top: 50%; + transform: translateY(-50%); + width: 6px; + height: 6px; + border-radius: 50%; + background-color: #007bff55; + transition: background-color 0.2s ease; +} + +.item-list__item:hover { + background-color: #eef6ff; + transform: translateX(3px); +} + +.item-list__item:hover::before { + background-color: #007bff; +} diff --git a/src/components/ThemeToggle/ThemeToggle.tsx b/src/components/ThemeToggle/ThemeToggle.tsx new file mode 100644 index 0000000..07f3e13 --- /dev/null +++ b/src/components/ThemeToggle/ThemeToggle.tsx @@ -0,0 +1,10 @@ +import Button from "../Button/Button"; +import { useDarkMode } from "../../hooks/useDarkMode"; +import styles from './style.module.css' + +export const ThemeToggle = () => { + const { isDarkMode, toggleDarkMode } = useDarkMode(); + return ( +