From b9686aed782846341052dfa59f2c50e2d9aa8e85 Mon Sep 17 00:00:00 2001 From: Tomas Balsys Date: Sun, 27 Oct 2024 02:02:19 +0300 Subject: [PATCH] Drag and drop --- index.html | 4 +-- src/components/task-row.tsx | 68 ++++++++++++++++++++++++++--------- src/components/task-table.tsx | 3 +- src/index.css | 32 +++++++++++------ src/routes/list.tsx | 19 +++++++++- 5 files changed, 96 insertions(+), 30 deletions(-) diff --git a/index.html b/index.html index b2fc2fd..9c004dd 100644 --- a/index.html +++ b/index.html @@ -1,5 +1,5 @@ - + @@ -8,7 +8,7 @@ Vite + React + TS - +
diff --git a/src/components/task-row.tsx b/src/components/task-row.tsx index b930568..ad8de6d 100644 --- a/src/components/task-row.tsx +++ b/src/components/task-row.tsx @@ -2,24 +2,60 @@ import { useState } from 'react' import { taskTypes, taskStatuses } from './consts'; import { EnumSelect } from './enum-select'; -export function TaskRow({ task, updateTask, deleteTask }: { task: TodoTask, updateTask: (task: TodoTask) => void, deleteTask: (id: number) => void }): JSX.Element { +export function TaskRow({ task, updateTask, deleteTask, orderTasks }: { task: TodoTask, updateTask: (task: TodoTask) => void, deleteTask: (id: number) => void, orderTasks: (from: number, to: number) => void }): JSX.Element { const [edit, setEdit] = useState(false); + const [classnames, setClassnames] = useState([]); return ( -
{ - event.preventDefault(); + { + event.preventDefault(); - const data = Object.fromEntries(new FormData(event.currentTarget)); + const data = Object.fromEntries(new FormData(event.currentTarget)); - updateTask({ - id: task.id, - type: data.type as TaskType, - title: data.title as string, - status: data.status as TaskStatus, - }); + updateTask({ + id: task.id, + type: data.type as TaskType, + title: data.title as string, + status: data.status as TaskStatus, + }); - setEdit(false) - }}> + setEdit(false) + }} + draggable + onDragStart={event => { + event.dataTransfer.setData('text/plain', task.id.toString()); + event.dataTransfer.effectAllowed = 'move'; + setClassnames(['dragged']) + }} + onDragOver={event => { + event.preventDefault(); + const fromId = +event.dataTransfer.getData('text/plain'); + if (fromId !== task.id) { + setClassnames(['dropover']); + } + }} + onDragLeave={event => { + const fromId = +event.dataTransfer.getData('text/plain'); + if (fromId !== task.id) { + setClassnames([]) + } + }} + onDrop={event => { + event.stopPropagation(); + setClassnames([]) + orderTasks(+event.dataTransfer.getData('text/plain'), task.id); + }} + onDragEnd={() => { + setClassnames([]) + }} + > +
+ + + +
{edit ? @@ -40,7 +76,7 @@ export function TaskRow({ task, updateTask, deleteTask }: { task: TodoTask, upda
{edit ? <> @@ -48,7 +84,7 @@ export function TaskRow({ task, updateTask, deleteTask }: { task: TodoTask, upda event.preventDefault() setEdit(false) }}> - + @@ -57,12 +93,12 @@ export function TaskRow({ task, updateTask, deleteTask }: { task: TodoTask, upda event.preventDefault() setEdit(true) }}> - + diff --git a/src/components/task-table.tsx b/src/components/task-table.tsx index b752ca0..ab8147a 100644 --- a/src/components/task-table.tsx +++ b/src/components/task-table.tsx @@ -1,10 +1,11 @@ import { } from 'react' import { TaskRow } from './task-row'; -export function TaskTable({ tasks, ...rest }: { tasks: TodoTasks, updateTask: (task: TodoTask) => void, deleteTask: (id: number) => void }): JSX.Element { +export function TaskTable({ tasks, ...rest }: { tasks: TodoTasks, updateTask: (task: TodoTask) => void, deleteTask: (id: number) => void, orderTasks: (from: number, to: number) => void }): JSX.Element { return (
+
Tipas
Pavadinimas
Statusas
diff --git a/src/index.css b/src/index.css index bcd754c..7ea4a3d 100644 --- a/src/index.css +++ b/src/index.css @@ -10,35 +10,39 @@ } a.nav-button { - @apply bg-neutral-100 dark:bg-neutral-900 cursor-pointer px-5 py-2 rounded-lg border border-gray-300 hover:border-indigo-500 transition-colors duration-700; + @apply cursor-pointer px-5 py-2 rounded-lg border border-gray-300 hover:border-indigo-500 transition-colors duration-700; } .tr { - @apply flex border-b pb-3 border-slate-100 dark:border-slate-700 text-slate-500 dark:text-slate-400; + @apply flex border-b pb-3 border-slate-100 dark:border-slate-700 text-neutral-700 dark:text-neutral-100; .td { @apply pl-4 pt-4 content-center; } .td:nth-child(1) { - @apply flex-auto w-32; + @apply flex-auto w-2 pl-0; } .td:nth-child(2) { - @apply flex-auto w-64; + @apply flex-auto w-28; } .td:nth-child(3) { - @apply flex-auto w-32; + @apply flex-auto w-64; } .td:nth-child(4) { - @apply flex-auto w-16 flex justify-center; + @apply flex-auto w-36; + } + + .td:nth-child(5) { + @apply flex-auto w-24 flex justify-center; } } .tr:first-child { - @apply dark:border-slate-600 text-slate-400 dark:text-slate-200; + @apply dark:border-slate-600 font-bold; .td { @apply pt-0; @@ -46,16 +50,24 @@ } .tr:last-child { - @apply pb-0 border-b-0; + @apply border-b-0; + } + + .dropover, .dragged { + @apply ring-2 ring-inset ring-indigo-500; + } + + .dragged { + @apply border-b-transparent; } select, input { - @apply h-14 w-full rounded-lg border-0 px-3 py-4 ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-indigo-500 focus:outline-none hover:ring-indigo-500 transition-shadow duration-700; + @apply bg-neutral-100 dark:bg-neutral-700 h-14 w-full rounded-lg border-0 px-3 py-4 ring-1 ring-inset ring-neutral-400 focus:ring-2 focus:ring-indigo-500 focus:outline-none hover:ring-indigo-500 transition-shadow duration-700; } .accordion-title { - @apply text-2xl flex items-center justify-between w-full p-5 font-medium border border-b-0 border-slate-200 dark:focus:ring-slate-800 dark:border-slate-700 hover:bg-slate-100 dark:hover:bg-slate-800 gap-3; + @apply text-2xl flex items-center justify-between w-full p-5 font-medium border border-b-0 border-neutral-200 dark:focus:ring-slate-800 dark:border-slate-700 hover:bg-neutral-100 dark:hover:bg-neutral-700 gap-3; } .accordion-title:first-child { diff --git a/src/routes/list.tsx b/src/routes/list.tsx index 43915e3..042df93 100644 --- a/src/routes/list.tsx +++ b/src/routes/list.tsx @@ -54,10 +54,27 @@ function App() { setTasks(tasks.filter(t => t.id != id)); }; + const swapTasks = (fromId: number, toId: number) => { + const from = tasks.findIndex(t => t.id == fromId); + const to = tasks.findIndex(t => t.id == toId); + const length = tasks.length; + const task = tasks[from] + + if (from === to || from > length || to > length) { + return; + } + + setTasks(tasks.flatMap((item, index) => { + if (index === from) return []; + if (index === to) return from < to ? [item, task] : [task, item]; + return item; + })); + }; + return ( <> handleAccordion(0)}> - + handleAccordion(1)}>