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