all repos — kōi @ e252fb0b8f069ac47108b7307af4ab921d5b7ff7

Minimalist task manager

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    const endTime = done ? new Date() : null;
23    tasksStore.updateTask(task.id, { done, endTime });
24  };
25
26  const onUpdateDueDate: FormEventHandler<HTMLInputElement> = e => {
27    const element = e.target as HTMLInputElement;
28    const rawDate = element.value;
29    let dueDate;
30    if (rawDate) dueDate = new Date(rawDate);
31    tasksStore.updateTask(task.id, { dueDate });
32  };
33
34  const onDelete = () => {
35    tasksStore.deleteTask(task.id);
36  };
37
38  const { onKeyDown, onKeyUp } = getShortcutActions({
39    x: onDelete,
40    Backspace: onDelete,
41    l: () => console.log({ task }),
42  });
43</script>
44
45<li class="list-row flex items-center gap-4 p-2" id={task.id}>
46  <input
47    type="checkbox"
48    class="checkbox"
49    checked={task.done}
50    onchange={onUpdateDone}
51  />
52  <div class="flex w-full justify-between">
53    <div
54      role="form"
55      contenteditable
56      autocorrect="off"
57      oninput={debounce(onUpdateName, 750)}
58      class:line-through={task.done}
59      class:text-gray-400={task.done}
60      onkeydown={onKeyDown}
61      onkeyup={onKeyUp}
62    >
63      {@html parseContent(task.name)}
64    </div>
65    <input
66      type="date"
67      value={task.dueDate?.toISOString().split("T")[0]}
68      onchange={onUpdateDueDate}
69      disabled={task.done}
70      class:text-red-400={!task.done &&
71        task.dueDate &&
72        task.dueDate < new Date()}
73      class:text-primary={task.dueDate && task.dueDate >= new Date()}
74      class="rounded px-1 h-fit"
75    />
76  </div>
77</li>