src/lib/components/TaskItem.svelte (view raw)
1<script lang="ts">
2 import type { FormEventHandler } from "svelte/elements";
3 import tasksStore from "../store/tasksStore";
4 import { debounce } from "../utils";
5 import { getShortcutActions } from "./taskShortcuts.svelte";
6 import Scheduler from "./Scheduler.svelte";
7 import { DateTime } from "luxon";
8
9 let { task }: { task: Task } = $props();
10
11 const parseContent = (content: string) =>
12 content.replace(/#((\w|-)+)/g, '<span class="hashtag">#$1</span>');
13
14 const onUpdateName: FormEventHandler<HTMLDivElement> = event => {
15 const element = event.target as HTMLElement;
16 const newValue = element.innerText?.trim();
17 if (newValue) tasksStore.updateTask(task.id, { name: newValue });
18 else tasksStore.deleteTask(task.id);
19 };
20
21 const onUpdateDone: FormEventHandler<HTMLInputElement> = e => {
22 const element = e.target as HTMLInputElement;
23 const done = element.checked;
24 const endTime = done ? new Date() : null;
25 tasksStore.updateTask(task.id, { done, endTime });
26 };
27
28 const onUpdateDueDate: FormEventHandler<HTMLInputElement> = e => {
29 const element = e.target as HTMLInputElement;
30 const rawDate = element.value;
31 let dueDate;
32 if (rawDate) dueDate = new Date(rawDate);
33 tasksStore.updateTask(task.id, { dueDate });
34 };
35
36 const onDelete = () => {
37 tasksStore.deleteTask(task.id);
38 };
39
40 const { onKeyDown, onKeyUp } = getShortcutActions({
41 x: onDelete,
42 Backspace: onDelete,
43 l: () => console.log({ task }),
44 });
45</script>
46
47<li class="list-row flex items-center gap-4 p-2" id={task.id}>
48 <input
49 type="checkbox"
50 class="checkbox"
51 checked={task.done}
52 onchange={onUpdateDone}
53 />
54 <div class="flex w-full justify-between">
55 <div
56 role="form"
57 contenteditable
58 autocorrect="off"
59 oninput={debounce(onUpdateName, 750)}
60 class:line-through={task.done}
61 class:text-gray-400={task.done}
62 onkeydown={onKeyDown}
63 onkeyup={onKeyUp}
64 >
65 {@html parseContent(task.name)}
66 </div>
67 <div class="flex items-center gap-2">
68 <input
69 type="date"
70 value={task.dueDate?.toISOString().split("T")[0]}
71 onchange={onUpdateDueDate}
72 disabled={task.done}
73 class:text-red-400={!task.done &&
74 task.dueDate &&
75 DateTime.fromJSDate(task.dueDate).endOf("day") <=
76 DateTime.now().startOf("day")}
77 class:text-primary={task.dueDate && task.dueDate >= new Date()}
78 class="rounded px-1 h-fit"
79 />
80 <Scheduler {task} />
81 </div>
82 </div>
83</li>