diff options
Diffstat (limited to 'old-apps/web-shared/src/components/stopwatch.svelte')
| -rw-r--r-- | old-apps/web-shared/src/components/stopwatch.svelte | 196 |
1 files changed, 196 insertions, 0 deletions
diff --git a/old-apps/web-shared/src/components/stopwatch.svelte b/old-apps/web-shared/src/components/stopwatch.svelte new file mode 100644 index 0000000..0e641e8 --- /dev/null +++ b/old-apps/web-shared/src/components/stopwatch.svelte @@ -0,0 +1,196 @@ +<script lang="ts"> + import Button from "$shared/components/button.svelte"; + import { Textarea } from "$shared/components/form"; + import { StorageKeys } from "$shared/lib/configuration"; + import { loadLocaleAsync } from "$shared/lib/i18n/i18n-util.async"; + import { i18nObject } from "$shared/lib/i18n/i18n-util"; + import { currentLocale, preffered_or_default } from "$shared/lib/locale"; + import { StoreType, writable_persistent } from "$shared/lib/persistent-store"; + import { Temporal } from "@js-temporal/polyfill"; + import { createEventDispatcher, onMount } from "svelte"; + + const state = writable_persistent({ + initialState: { + hours: 0, + minutes: 0, + seconds: 0, + startTime: null as Temporal.PlainDateTime, + lastStep: null as Temporal.PlainDateTime, + isRunning: false, + intervalId: 0, + note: "", + }, + options: { + store: StoreType.LOCAL + }, + name: StorageKeys.stopwatch, + }); + + let timeString; + let LL = i18nObject($currentLocale); + + $: if ($state.hours || $state.minutes || $state.seconds) { + timeString = $state.hours.toLocaleString(undefined, {minimumIntegerDigits: 2}) + + ":" + $state.minutes.toLocaleString(undefined, {minimumIntegerDigits: 2}) + + ":" + $state.seconds.toLocaleString(undefined, {minimumIntegerDigits: 2}); + } else { + timeString = "--:--:--"; + } + + currentLocale.subscribe(async locale => { + if (locale === "preffered") locale = preffered_or_default(); + await loadLocaleAsync(locale); + LL = i18nObject(locale); + }); + + onMount(async () => { + start_if_running(); + await loadLocaleAsync($currentLocale); + LL = i18nObject($currentLocale); + }); + + function start_if_running() { + if ($state.isRunning) { + if (Temporal.PlainDateTime.compare($state.lastStep, Temporal.Now.plainDateTimeISO()) == -1) { + const duration = Temporal.Now.plainDateTimeISO().since($state.lastStep, {smallestUnit: "second"}); + console.log("lastStep", $state.lastStep.toString()); + console.log("duration", duration.toString()); + console.log(duration.seconds); + // for (let i = 0; i < steps; i++) { + // step(); + // } + } + clearInterval($state.intervalId); + $state.intervalId = setInterval(step, 1000); + } + } + + window.addEventListener("focus", () => { + start_if_running(); + }); + + const dispatch = createEventDispatcher(); + + function step() { + $state.seconds = $state.seconds + 1; + + if ($state.seconds == 60) { + $state.minutes = $state.minutes + 1; + $state.seconds = 0; + } + + if ($state.minutes == 60) { + $state.hours = $state.hours + 1; + $state.minutes = 0; + $state.seconds = 0; + } + + if (!$state.startTime) $state.startTime = Temporal.Now.plainDateTimeISO(); + $state.lastStep = Temporal.Now.plainDateTimeISO(); + } + + function reset() { + clearInterval($state.intervalId); + $state.isRunning = false; + $state.hours = 0; + $state.minutes = 0; + $state.seconds = 0; + $state.startTime = null; + $state.intervalId = 0; + $state.note = ""; + } + + let roundUpToNearest = 30; + let roundDownToNearest = 30; + + function on_round_up() { + const newTime = Temporal.PlainTime + .from({hour: $state.hours, minute: $state.minutes, second: $state.seconds}) + .round({ + roundingIncrement: roundUpToNearest, + smallestUnit: "minute", + roundingMode: "ceil" + }); + $state.hours = newTime.hour; + $state.minutes = newTime.minute; + $state.seconds = newTime.second; + } + + function on_round_down() { + const newTime = Temporal.PlainTime + .from({hour: $state.hours, minute: $state.minutes, second: $state.seconds,}) + .round({ + roundingIncrement: roundDownToNearest, + smallestUnit: "minute", + roundingMode: "trunc" + }); + $state.hours = newTime.hour; + $state.minutes = newTime.minute; + $state.seconds = newTime.second; + } + + function on_start_stop() { + if ($state.isRunning) { + clearInterval($state.intervalId); + $state.isRunning = false; + return; + } + step(); + $state.intervalId = setInterval(step, 1000); + $state.isRunning = true; + } + + function on_create_entry() { + if (!$state.startTime) return; + const plainStartTime = Temporal.PlainDateTime.from($state.startTime); + dispatch("create", { + from: plainStartTime, + to: plainStartTime.add({hours: $state.hours, minutes: $state.minutes, seconds: $state.seconds}), + description: $state.note + }); + reset(); + } +</script> + +<div class="grid"> + <div class="col-6"> + <slot name="header"></slot> + <pre class="text-xxl padding-y-sm">{timeString}</pre> + </div> + <div class="col-6 flex align-bottom flex-column text-xs"> + <Button title="{$state.isRunning ? LL.stopwatch.stop() : LL.stopwatch.start()}" + text="{$state.isRunning ? LL.stopwatch.stop() : LL.stopwatch.start()}" + variant="link" + on:click={on_start_stop}/> + + {#if $state.startTime} + <Button title="{LL.stopwatch.reset()}" + text="{LL.stopwatch.reset()}" + variant="link" + class="bg-error-lighter@hover color-white@hover" + on:click={reset}/> + {#if !$state.isRunning} + <Button title="{LL.stopwatch.roundUp()}" + text="{LL.stopwatch.roundUp()}" + variant="link" + on:click={on_round_up}/> + <Button title="{LL.stopwatch.roundDown()}" + text="{LL.stopwatch.roundDown()}" + variant="link" + on:click={on_round_down}/> + {#if $state.minutes > 0 || $state.hours > 0} + <Button title="{LL.stopwatch.createEntry()}" + text="{LL.stopwatch.createEntry()}" + variant="link" + on:click={on_create_entry}/> + {/if} + {/if} + {/if} + </div> +</div> + +<Textarea class="width-100% margin-top-xs" + placeholder="{LL.stopwatch.whatsYourFocus()}" + rows="1" + bind:value={$state.note} +/> |
