/
android113
/
react_todo
Обзор
Документация
Войти
/
android113
/
react_todo
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
dev
src/App.js
250 строк
9 KB
Скороходов Андрей Андреевич
added background pic and fog fx
08 дек 2025, 15:44
08 дек 2025, 15:44
5094188
Код
Авторство
О чём код?
import React, { useState, useEffect } from 'react'; import './App.css'; const DEFAULT_CATEGORIES = [ { id: '1', name: 'Общие', emoji: '📌' }, { id: '2', name: 'Работа', emoji: '💼' }, { id: '3', name: 'Идеи', emoji: '💡' }, ]; function App() { const [todos, setTodos] = useState(() => { const saved = localStorage.getItem('myTodoList'); return saved ? JSON.parse(saved) : []; }); const [categories, setCategories] = useState(() => { const savedCats = localStorage.getItem('myCategories'); return savedCats ? JSON.parse(savedCats) : DEFAULT_CATEGORIES; }); const [collapsedGroups, setCollapsedGroups] = useState({}); const [value, setValue] = useState(''); const [selectedCategoryName, setSelectedCategoryName] = useState(DEFAULT_CATEGORIES[0].name); // Стейты для создания НОВОЙ категории const [isCreatingCat, setIsCreatingCat] = useState(false); const [newCatName, setNewCatName] = useState(''); const [newCatEmoji, setNewCatEmoji] = useState('📂'); useEffect(() => { localStorage.setItem('myTodoList', JSON.stringify(todos)); }, [todos]); useEffect(() => { localStorage.setItem('myCategories', JSON.stringify(categories)); }, [categories]); const addTodo = () => { if (value.trim()) { setTodos([ ...todos, { id: Date.now(), text: value, completed: false, category: selectedCategoryName } ]); setValue(''); } }; const removeTodo = (id) => { setTodos(todos.filter(todo => todo.id !== id)); }; const toggleComplete = (id) => { setTodos( todos.map(todo => { if (todo.id !== id) return todo; return { ...todo, completed: !todo.completed }; }) ); }; const toggleGroupCollapse = (categoryName) => { setCollapsedGroups(prev => ({ ...prev, [categoryName]: !prev[categoryName] })); }; const createCategory = () => { if (newCatName.trim()) { const newCat = { id: Date.now().toString(), name: newCatName.trim(), emoji: newCatEmoji }; setCategories([...categories, newCat]); setNewCatName(''); setIsCreatingCat(false); setSelectedCategoryName(newCat.name); } }; const deleteCategory = (catName) => { if (window.confirm('Удалить эту группу задач?')) { const newCats = categories.filter(c => c.name !== catName); setCategories(newCats); setTodos(todos.map(t => t.category === catName ? {...t, category: newCats[0].name} : t)); setSelectedCategoryName(newCats[0].name); } } const handleKeyPress = (e) => { if (e.key === 'Enter') { if (isCreatingCat) createCategory(); else addTodo(); } } return ( <div className="app-container"> {/* ДОБАВЛЕНЫ ЭЛЕМЕНТЫ ФОНА: bg-image - картинка гор fog-container - для анимации тумана */} <div className="bg-image"></div> <div className="fog-container"> <div className="fog-img fog-img-first"></div> <div className="fog-img fog-img-second"></div> </div> <div className="card glass-effect"> <h1 className="header">📋 Планировщик</h1> <div className="controls-area"> {!isCreatingCat ? ( <div className="input-group"> <select value={selectedCategoryName} onChange={(e) => { if (e.target.value === 'ADD_NEW') setIsCreatingCat(true); else setSelectedCategoryName(e.target.value); }} className="category-select" > {categories.map(cat => ( <option key={cat.id} value={cat.name}>{cat.emoji} {cat.name}</option> ))} <option value="ADD_NEW" style={{fontWeight: 'bold', color: '#5d6cfc'}}>+ СОЗДАТЬ...</option> </select> <input type="text" placeholder="Новая задача..." value={value} onChange={(e) => setValue(e.target.value)} onKeyDown={handleKeyPress} /> <button onClick={addTodo} className="action-btn main-btn">+</button> </div> ) : ( <div className="new-cat-group fade-in"> <select className="emoji-select" value={newCatEmoji} onChange={(e) => setNewCatEmoji(e.target.value)} > {['📁', '🛒', '🎓', '🏥', '✈️', '💪', '🎮', '❤️', '🔥', '⭐'].map(em => ( <option key={em} value={em}>{em}</option> ))} </select> <input type="text" placeholder="Название группы..." autoFocus value={newCatName} onChange={(e) => setNewCatName(e.target.value)} onKeyDown={handleKeyPress} /> <button onClick={createCategory} className="action-btn success-btn">✔</button> <button onClick={() => setIsCreatingCat(false)} className="action-btn cancel-btn">✖</button> </div> )} </div> <div className="todo-list-container"> {categories.map(cat => { const catTasks = todos.filter(t => t.category === cat.name); const isCollapsed = collapsedGroups[cat.name]; if (catTasks.length === 0 && cat.name !== selectedCategoryName) return null; return ( <div key={cat.id} className="category-group"> <div className="category-header"> <div className="cat-title-left"> <span className="cat-emoji">{cat.emoji}</span> <span className="cat-name">{cat.name}</span> <span className="cat-count-badge">{catTasks.length}</span> </div> <div className="cat-controls"> <button className={`collapse-btn ${isCollapsed ? 'collapsed' : ''}`} onClick={() => toggleGroupCollapse(cat.name)} title={isCollapsed ? "Развернуть" : "Свернуть"} > ▼ </button> {cat.id.length > 3 && ( <button className="delete-cat-btn" onClick={() => deleteCategory(cat.name)} title="Удалить пустую группу" >✖</button> )} </div> </div> {!isCollapsed && ( <ul className="todo-list slide-down"> {catTasks.length > 0 ? catTasks.map((todo) => ( <li key={todo.id} className={`todo-item ${todo.completed ? 'completed' : ''}`}> <div className="todo-text" onClick={() => toggleComplete(todo.id)} > <span className="check-circle"></span> {todo.text} </div> <button onClick={() => removeTodo(todo.id)} className="delete-todo-btn" >✖</button> </li> )) : ( <li className="empty-cat-placeholder">Пока нет задач</li> )} </ul> )} </div> ) })} {todos.length === 0 && ( <div className="empty-state"> <h3>Список пуст</h3> <p>Начните добавлять свои дела!</p> </div> )} </div> <div className="footer"> Создать задачу: Enter | Создать группу: '+' </div> </div> </div> ); } export default App;