It's a todo list.
1<script module>
2 let selectedTab = $state("set");
3</script>
4
5<script lang="ts">
6 import { useInterval } from "runed";
7 import type { Task } from "./stores.svelte";
8 import { formatSecondsToDuration } from "./utils";
9
10 let { task = $bindable(), onDelete }: { task: Task, onDelete: (taskId: string) => void } = $props();
11
12 const interval = useInterval(1000, {
13 immediate: false,
14 callback: () => {
15 task.duration++;
16 }
17 });
18
19 function toggleInterval() {
20 if (interval.isActive) {
21 interval.pause();
22 }
23 else {
24 interval.resume();
25 }
26 }
27
28 let timerDialog: HTMLDialogElement | undefined = $state();
29 let hourInput = $state(0);
30 let minuteInput = $state(0);
31 let secondsInput = $state(0);
32 let inputDuration = $derived((hourInput * 3600) + (minuteInput * 60) + secondsInput);
33
34 function handleTimeSubmit() {
35 if (selectedTab === "set") {
36 task.duration = inputDuration;
37 }
38 else if (selectedTab === "add") {
39 task.duration += inputDuration;
40 }
41 }
42</script>
43
44<dialog bind:this={timerDialog} id={`timer_${task.id}`} class="fixed border inset-0 m-auto w-1/2 p-8 rounded-xl">
45 <form method="dialog" onsubmit={handleTimeSubmit}>
46 <button type="button" onclick={() => timerDialog?.close()} class="absolute right-12">×</button>
47 <div class="flex flex-col items-center gap-4">
48 <div class="flex items-center text-sm border rounded-full bg-gray-300">
49 <button type="button" onclick={() => selectedTab = "set"} class={[selectedTab === "set" && "border-black bg-white", "border border-gray-300 px-3 py-1 rounded-l-full bg-gray-300"]}>
50 Set
51 </button>
52 <button type="button" onclick={() => selectedTab = "add"} class={[selectedTab === "add" && "border-black bg-white", "border border-gray-300 px-3 py-1 rounded-r-full bg-gray-300"]}>
53 Add
54 </button>
55 </div>
56
57 <div class="flex w-fit items-center justify-center">
58 <input type="number" min="0" max="99" bind:value={hourInput} class="text-right" />
59 <p class="mr-4">:</p>
60 <input type="number" min="0" max="59" bind:value={minuteInput} class="text-right" />
61 <p class="mr-4">:</p>
62 <input type="number" min="0" max="59" bind:value={secondsInput} class="text-right" />
63 </div>
64
65 <p>
66 <span class="text-gray-600">{formatSecondsToDuration(task.duration)}</span> ➜
67 {#if selectedTab === "set"}
68 {formatSecondsToDuration(inputDuration)}
69 {:else if selectedTab === "add"}
70 {formatSecondsToDuration(task.duration + inputDuration)}
71 {/if}
72 </p>
73 <button type="submit" class="bg-green-400 px-4 py-1 text-lg rounded-lg">
74 Confirm
75 </button>
76 </div>
77 </form>
78</dialog>
79
80<li class="group flex justify-between h-fit items-center gap-4">
81 <div class="flex w-full gap-4 items-center pr-4 py-2">
82 <input
83 type="checkbox"
84 bind:checked={task.completed}
85 class="w-6 h-6 bg-transparent"
86 />
87 <input
88 type="text"
89 bind:value={task.description}
90 class={`w-full hover:underline text-ellipsis overflow-hidden bg-transparent ${task.completed && "text-white/50"}`}
91 />
92 </div>
93
94 <div class="flex gap-4 w-fit h-fit items-center">
95 <button
96 command="show-modal"
97 commandfor={`timer_${task.id}`}
98 class="w-fit h-fit tabular-nums text-lg hover:bg-gray-50/20 border rounded-lg p-2"
99 >
100 {formatSecondsToDuration(task.duration!)}
101 </button>
102 <button
103 onclick={toggleInterval}
104 class="w-full h-fit bg-white hover:bg-gray-200 border rounded-full p-2"
105 >
106 {#if interval.isActive}
107 <img src="/basil--pause-solid.svg" alt="Pause" class="w-8" />
108 {:else}
109 <img src="/basil--play-solid.svg" alt="Play" class="w-8" />
110 {/if}
111 </button>
112 <button
113 onclick={() => onDelete(task.id)}
114 class="p-2 bg-red-500 rounded-xl text-white w-full h-fit"
115 >
116 <img src="/basil--trash-solid.svg" alt="Trash" class="w-24" />
117 </button>
118 </div>
119</li>