src/routes/Task.svelte (view raw)
1<script lang="ts">
2 import { db } from '$lib/db';
3 import { debounce } from '$lib/utils';
4 import type { FormEventHandler } from 'svelte/elements';
5
6 let { task }: { task: Task } = $props();
7
8 const parseContent = (content: string) =>
9 content.replace(/#((\w|-)+)/g, '<span class="hashtag">#$1</span>');
10
11 const onUpdateContent: FormEventHandler<HTMLDivElement> = (e) => {
12 const element = e.target as HTMLElement;
13 const content = element.innerText?.trim();
14 if (content) db.tasks.update(task.id, { content });
15 else db.tasks.delete(task.id);
16 };
17
18 const onUpdateDone: FormEventHandler<HTMLInputElement> = (e) => {
19 const element = e.target as HTMLInputElement;
20 const done = element.checked;
21 db.tasks.update(task.id, { done });
22 };
23
24 const onUpdateDueDate: FormEventHandler<HTMLInputElement> = (e) => {
25 const element = e.target as HTMLInputElement;
26 const rawDate = element.value;
27 let dueDate;
28 if (rawDate) dueDate = new Date(rawDate);
29 db.tasks.update(task.id, { dueDate });
30 };
31</script>
32
33<div class="flex items-center justify-between gap-2">
34 <div class="flex items-center gap-2">
35 <input
36 type="checkbox"
37 class="checkbox-primary checkbox checkbox-sm"
38 checked={task.done}
39 onchange={onUpdateDone}
40 />
41 <div
42 contenteditable
43 oninput={debounce(onUpdateContent, 1000)}
44 class:opacity-50={task.done}
45 class:line-through={task.done}
46 >
47 {@html parseContent(task.content)}
48 </div>
49 </div>
50 <input
51 type="date"
52 value={task.dueDate?.toISOString().split('T')[0]}
53 onchange={onUpdateDueDate}
54 class:text-red-400={task.dueDate && task.dueDate < new Date()}
55 class:text-primary={task.dueDate && task.dueDate >= new Date()}
56 />
57</div>