Лаб4
.docxМинистерство цифрового развития, связи и массовых коммуникаций РФ
ордена Трудового Красного Знамени федеральное государственное
бюджетное образовательное учреждение высшего образования
«МОСКОВСКИЙ ТЕХНИЧЕСКИЙ УНИВЕРСИТЕТ СВЯЗИ И
ИНФОРМАТИКИ»
Кафедра «МКиИТ»
Лабораторная № 4
по дисциплине «Разработка кроссплатформенного программного обеспечения»
Выполнил:
студент группы БФИ2203
Мячина Л. А.
Проверил:
Потапченко Т. Д.
Москва, 2024 г.
Цель работы:
Изучить принципы маршрутизации (routing) в React-приложениях и внедрить функциональность Drag-and-Drop (DND) через реализацию расширенной версии To-Do листа с несколькими колонками задач. Также студенты познакомятся с библиотеками для работы с DND в React, такими как reactbeautiful-dnd, и научатся управлять состояниями между компонентами с помощью маршрутизатора и системы перетаскивания задач.
Требования к работе:
1. Добавьте два дополнительных столбца. Студентам необходимо добавить два новых столбца (например, "Done" и "Blocked") в существующий DND функционал. Эти колонки должны поддерживать возможность перетаскивания задач.
2. Реализуйте возможность удаления задач. Добавьте в каждый элемент задачу удаления с помощью кнопки. При нажатии задача должна удаляться из списка.
3. Стилизация приложения. Улучшите внешний вид страниц, добавив стили с использованием CSS или библиотеки TailwindCSS.
4. Навигация между страницами. Добавьте в каждую страницу кнопку или ссылку для перехода на другую страницу. Например, на странице с DnD можно добавить ссылку "Перейти к обычному To-Do листу", и наоборот.
Выполнение работы:
Листинг кода
import React from 'react';
import { HashRouter as Router, Route, Routes} from 'react-router-dom'
import TodoPage from './pages/TodoPage';
import DndPage from './pages/DndPage';
function App() {
return (
<Router>
<Routes>
<Route path="/" element={<TodoPage/>} />
<Route path="/dnd" element={<DndPage/>} />
</Routes>
</Router>
);
}
export default App;
import React, { useState } from 'react';
import TodoList from '../components/TodoList';
import { useNavigate } from 'react-router-dom';
function TodoPage() {
const nav = useNavigate();
const [todos, setTodos] = useState([
{ id: 1, title: 'Buy groceries', completed: false },
{ id: 2, title: 'Read a book', completed: false },
]);
const [newTodo, setNewTodo] = useState('');
const [isFiltered, setFiltered] = useState(false);
const [beforefilteredTodos, setBeforeFilteredTodos] = useState([]);
const toggleTodo = (id) => {
setTodos(
todos.map((todo) =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
)
);
};
const changeFilter = (mode) => {
switch (mode)
{
case "completed":
if (isFiltered === false) setBeforeFilteredTodos(todos);
if (beforefilteredTodos.length !== 0) setTodos(beforefilteredTodos);
setTodos((td) => td.filter((todo) => todo.completed === true));
setFiltered(true);
break;
case "uncompleted":
if (isFiltered === false) setBeforeFilteredTodos(todos);
if (beforefilteredTodos.length !== 0) setTodos(beforefilteredTodos);
setTodos((td) => td.filter((todo) => todo.completed === false));
setFiltered(true);
break;
default:
if (beforefilteredTodos.length === 0) break;
setTodos(beforefilteredTodos);
setFiltered(false);
break;
}
}
const removeTodo = (id) => {
setTodos((prevTodos) => prevTodos.filter((todo) => todo.id !== id));
};
const addTodo = (e) => {
e.preventDefault();
if (!newTodo) return;
const newTodoItem = {
id: Date.now(),
title: newTodo,
completed: false,
};
setTodos([...todos, newTodoItem]);
setNewTodo('');
};
return (
<div>
<button
style={{
position: 'absolute',
top: '5px',
right: '5px',
}}
onClick={() => nav('/dnd')}
>
Dnd
</button>
<div id="main">
<h1>My To-Do List</h1>
<form onSubmit={addTodo}>
<input
type="text"
value={newTodo}
onChange={(e) => setNewTodo(e.target.value)}
placeholder="Add new task..."
/>
<button type="submit">Add</button>
</form>
<button type="button" onClick={() => changeFilter('none')}>Все</button>
<button type="button" onClick={() => changeFilter('completed')}>Выполненные</button>
<button type="button" onClick={() => changeFilter('uncompleted')}>Не выполненные</button>
<TodoList todos={todos} toggleTodo={toggleTodo} removeHandle={removeTodo} />
</div>
</div>
);
}
export default TodoPage;
import React, { useState } from 'react';
import { DragDropContext, Droppable, Draggable } from '@hello-pangea/dnd';
import { useNavigate } from 'react-router-dom';
function DndPage() {
const nav = useNavigate();
const [columns, setColumns] = useState({
todo: {
name: 'To Do',
items: [
{ id: '1', content: 'First task' },
{ id: '2', content: 'Second task' },
{ id: '3', content: 'Third task' },
{ id: '4', content: 'Fourth task' },
{ id: '5', content: 'Fifth task' },
],
},
inProgress: {
name: 'In Progress',
items: [],
},
done: {
name: 'Done',
items: [],
},
blocked: {
name: 'Blocked',
items: [],
}
});
const [newTask, setNewTask] = useState('');
const addTask = (e) => {
e.preventDefault();
if (!newTask) return;
const updatedColumn = columns['todo'];
const newTaskItem = {
id: Date.now().toString(), content: newTask,
};
setColumns({
...columns,
'todo': {
...updatedColumn,
items: [...updatedColumn.items, newTaskItem],
},
});
setNewTask('');
};
const onDragEnd = (result, columns, setColumns) => {
const { source, destination } = result;
if (!destination) return;
const sourceColumn = columns[source.droppableId];
const destColumn = columns[destination.droppableId];
const sourceItems = [...sourceColumn.items];
const destItems = [...destColumn.items];
const [removed] = sourceItems.splice(source.index, 1);
if (source.droppableId === destination.droppableId) {
sourceItems.splice(destination.index, 0, removed);
setColumns({
...columns,
[source.droppableId]: {
...sourceColumn,
items: sourceItems,
},
});
}
else {
destItems.splice(destination.index, 0, removed);
setColumns({
...columns,
[source.droppableId]: {
...sourceColumn,
items: sourceItems,
},
[destination.droppableId]: {
...destColumn,
items: destItems,
},
});
}
};
const removeItem = (columnId, itemId) => {
const updatedColumn = columns[columnId];
const updatedItems = updatedColumn.items.filter(item => item.id !== itemId);
setColumns({
...columns,
[columnId]: {
...updatedColumn,
items: updatedItems,
},
});
}
return (
<div style={{
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
height: '100vh',
width: '100vw',
padding: '20px',
boxSizing: 'border-box',
}}>
<h1>Drag And Drop Page</h1>
<form onSubmit={addTask}>
<input
type="text"
value={newTask}
onChange={(e) => setNewTask(e.target.value)}
placeholder="Add new task..."
/>
<button type="submit">Add new task</button>
</form>
<button
style={{
position: 'absolute',
top: '5px',
right: '5px',
}}
onClick={() => nav('/')}
>
Back to normal
</button>
<div style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
width: '100vw',
padding: '0 20px',
boxSizing: 'border-box',
border: '#f9f9f9',
borderStyle: 'groove',
borderRadius: '8px',
}}>
<DragDropContext onDragEnd={(result) => onDragEnd(result, columns, setColumns)}>
{Object.entries(columns).map(([columnId, column], index) => {
return (
<div style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
paddingBottom: '5px',
}} key={columnId}>
<h2>{column.name}</h2>
<Droppable droppableId={columnId} key={columnId}>
{(provided, snapshot) => {
return (
<div {...provided.droppableProps} ref={provided.innerRef}
style={{
background: snapshot.isDraggingOver ? 'rgba(10, 10, 30, 0.6)' : 'rgba(10, 10, 30, 0.4)',
padding: 4,
width: 250,
minHeight: 500,
}}>
{column.items.map((item, index) => (
<Draggable key={item.id} draggableId={item.id} index={index}>
{(provided, snapshot) => {
return (
<div ref={provided.innerRef} {...provided.draggableProps} {...provided.dragHandleProps}
style={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
userSelect: 'none',
padding: '4px',
margin: '0 0 8px 0',
minHeight: '50px',
backgroundColor: snapshot.isDragging ? 'rgba(81, 50, 143, 0.5)' : 'rgba(81, 50, 143, 1.0)',
borderRadius: '5px',
color: 'white',
...provided.draggableProps.style,
}}>
{item.content}
<button
style={{
backgroundColor: 'purple',
color: 'white',
border: 'none',
borderRadius: '3px',
cursor: 'pointer',
}}
onClick={() => removeItem(columnId, item.id)}
>
Remove
</button>
</div>
);
}}
</Draggable>
))}
{provided.placeholder}
</div>
);
}}
</Droppable>
</div>
);
})}
</DragDropContext>
</div>
</div>
);
}
export default DndPage;
Результат работы
Рисунок 1 – Результат работы
Вывод
С помощью полученных знаний, мы познакомились с основными принципами маршрутизации в React и расширили свое приложение, добавив Drag-and-Drop функциональность. Работая с реальными инструментами разработки, такими как React Router и react-beautiful-dnd, мы приобретаем практические навыки, которые помогут в дальнейшем создавать более сложные и функциональные веб-приложения.
