1
0
Fork 0

Drag and drop

This commit is contained in:
Tomas Balsys 2024-10-27 02:02:19 +03:00
parent 7f86f64164
commit b9686aed78
5 changed files with 96 additions and 30 deletions

View File

@ -1,5 +1,5 @@
<!doctype html>
<html lang="en">
<html lang="en" class="bg-neutral-200 dark:bg-neutral-800">
<head>
<meta charset="UTF-8" />

View File

@ -2,11 +2,14 @@ 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<string[]>([]);
return (
<form className="tr" onSubmit={event => {
<form
className={["tr", ...classnames].join(' ')}
onSubmit={event => {
event.preventDefault();
const data = Object.fromEntries(new FormData(event.currentTarget));
@ -19,7 +22,40 @@ export function TaskRow({ task, updateTask, deleteTask }: { task: TodoTask, upda
});
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([])
}}
>
<div className="td">
<svg className="w-5 h-5 stroke-neutral-200" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path d="M21 10L3 10M21 14L3 14M12 4L12 10M12 14L12 20M15 18L12 21L9 18M15 6L12 3L9 6" strokeWidth="1" strokeLinecap="round" strokeLinejoin="round"></path>
</svg>
</div>
<div className="td">
{edit
? <EnumSelect entries={taskTypes} name="type" defaultValue={task.type} />
@ -40,7 +76,7 @@ export function TaskRow({ task, updateTask, deleteTask }: { task: TodoTask, upda
<div className="td">
{edit ? <>
<button type="submit" className="mr-3">
<svg className="w-5 h-5 fill-white" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50">
<svg className="w-5 h-5 fill-indigo-500" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50">
<path d="M 25 2 C 12.317 2 2 12.317 2 25 C 2 37.683 12.317 48 25 48 C 37.683 48 48 37.683 48 25 C 48 20.44 46.660281 16.189328 44.363281 12.611328 L 42.994141 14.228516 C 44.889141 17.382516 46 21.06 46 25 C 46 36.579 36.579 46 25 46 C 13.421 46 4 36.579 4 25 C 4 13.421 13.421 4 25 4 C 30.443 4 35.393906 6.0997656 39.128906 9.5097656 L 40.4375 7.9648438 C 36.3525 4.2598437 30.935 2 25 2 z M 43.236328 7.7539062 L 23.914062 30.554688 L 15.78125 22.96875 L 14.417969 24.431641 L 24.083984 33.447266 L 44.763672 9.046875 L 43.236328 7.7539062 z"></path>
</svg>
</button>
@ -48,7 +84,7 @@ export function TaskRow({ task, updateTask, deleteTask }: { task: TodoTask, upda
event.preventDefault()
setEdit(false)
}}>
<svg className="w-5 h-5 fill-white" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50">
<svg className="w-5 h-5 fill-indigo-500" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50">
<path d="M 25 2 C 12.309534 2 2 12.309534 2 25 C 2 37.690466 12.309534 48 25 48 C 37.690466 48 48 37.690466 48 25 C 48 12.309534 37.690466 2 25 2 z M 25 4 C 36.609534 4 46 13.390466 46 25 C 46 36.609534 36.609534 46 25 46 C 13.390466 46 4 36.609534 4 25 C 4 13.390466 13.390466 4 25 4 z M 32.990234 15.986328 A 1.0001 1.0001 0 0 0 32.292969 16.292969 L 25 23.585938 L 17.707031 16.292969 A 1.0001 1.0001 0 0 0 16.990234 15.990234 A 1.0001 1.0001 0 0 0 16.292969 17.707031 L 23.585938 25 L 16.292969 32.292969 A 1.0001 1.0001 0 1 0 17.707031 33.707031 L 25 26.414062 L 32.292969 33.707031 A 1.0001 1.0001 0 1 0 33.707031 32.292969 L 26.414062 25 L 33.707031 17.707031 A 1.0001 1.0001 0 0 0 32.990234 15.986328 z"></path>
</svg>
</button>
@ -57,12 +93,12 @@ export function TaskRow({ task, updateTask, deleteTask }: { task: TodoTask, upda
event.preventDefault()
setEdit(true)
}}>
<svg className="w-5 h-5 fill-white" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" width="100" height="100" viewBox="0 0 50 50">
<svg className="w-5 h-5 fill-indigo-500" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50">
<path d="M 43.125 2 C 41.878906 2 40.636719 2.488281 39.6875 3.4375 L 38.875 4.25 L 45.75 11.125 C 45.746094 11.128906 46.5625 10.3125 46.5625 10.3125 C 48.464844 8.410156 48.460938 5.335938 46.5625 3.4375 C 45.609375 2.488281 44.371094 2 43.125 2 Z M 37.34375 6.03125 C 37.117188 6.0625 36.90625 6.175781 36.75 6.34375 L 4.3125 38.8125 C 4.183594 38.929688 4.085938 39.082031 4.03125 39.25 L 2.03125 46.75 C 1.941406 47.09375 2.042969 47.457031 2.292969 47.707031 C 2.542969 47.957031 2.90625 48.058594 3.25 47.96875 L 10.75 45.96875 C 10.917969 45.914063 11.070313 45.816406 11.1875 45.6875 L 43.65625 13.25 C 44.054688 12.863281 44.058594 12.226563 43.671875 11.828125 C 43.285156 11.429688 42.648438 11.425781 42.25 11.8125 L 9.96875 44.09375 L 5.90625 40.03125 L 38.1875 7.75 C 38.488281 7.460938 38.578125 7.011719 38.410156 6.628906 C 38.242188 6.246094 37.855469 6.007813 37.4375 6.03125 C 37.40625 6.03125 37.375 6.03125 37.34375 6.03125 Z"></path>
</svg>
</button>
<button type="button" onClick={() => deleteTask(task.id)}>
<svg className="w-5 h-5 fill-white" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50">
<svg className="w-5 h-5 fill-indigo-500" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50">
<path d="M 21 2 C 19.354545 2 18 3.3545455 18 5 L 18 7 L 10.154297 7 A 1.0001 1.0001 0 0 0 9.984375 6.9863281 A 1.0001 1.0001 0 0 0 9.8398438 7 L 8 7 A 1.0001 1.0001 0 1 0 8 9 L 9 9 L 9 45 C 9 46.645455 10.354545 48 12 48 L 38 48 C 39.645455 48 41 46.645455 41 45 L 41 9 L 42 9 A 1.0001 1.0001 0 1 0 42 7 L 40.167969 7 A 1.0001 1.0001 0 0 0 39.841797 7 L 32 7 L 32 5 C 32 3.3545455 30.645455 2 29 2 L 21 2 z M 21 4 L 29 4 C 29.554545 4 30 4.4454545 30 5 L 30 7 L 20 7 L 20 5 C 20 4.4454545 20.445455 4 21 4 z M 11 9 L 18.832031 9 A 1.0001 1.0001 0 0 0 19.158203 9 L 30.832031 9 A 1.0001 1.0001 0 0 0 31.158203 9 L 39 9 L 39 45 C 39 45.554545 38.554545 46 38 46 L 12 46 C 11.445455 46 11 45.554545 11 45 L 11 9 z M 18.984375 13.986328 A 1.0001 1.0001 0 0 0 18 15 L 18 40 A 1.0001 1.0001 0 1 0 20 40 L 20 15 A 1.0001 1.0001 0 0 0 18.984375 13.986328 z M 24.984375 13.986328 A 1.0001 1.0001 0 0 0 24 15 L 24 40 A 1.0001 1.0001 0 1 0 26 40 L 26 15 A 1.0001 1.0001 0 0 0 24.984375 13.986328 z M 30.984375 13.986328 A 1.0001 1.0001 0 0 0 30 15 L 30 40 A 1.0001 1.0001 0 1 0 32 40 L 32 15 A 1.0001 1.0001 0 0 0 30.984375 13.986328 z"></path>
</svg>
</button>

View File

@ -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 (
<div className="w-full">
<div className="tr">
<div className="td"></div>
<div className="td">Tipas</div>
<div className="td">Pavadinimas</div>
<div className="td">Statusas</div>

View File

@ -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 {

View File

@ -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 (
<>
<AccordionItem title='Užduočių sąrašas' isOpen={expanded == 0} onClick={() => handleAccordion(0)}>
<TaskTable tasks={tasks} updateTask={updateTask} deleteTask={deleteTask} />
<TaskTable tasks={tasks} updateTask={updateTask} deleteTask={deleteTask} orderTasks={swapTasks} />
</AccordionItem>
<AccordionItem title='Nauja užduotis' isOpen={expanded == 1} onClick={() => handleAccordion(1)}>
<TaskForm createTask={createTask} />