summaryrefslogtreecommitdiffstats
path: root/src/components/Timer.tsx
diff options
context:
space:
mode:
authorMaksymilian Jopek <maks@jopek.eu>2025-04-28 18:03:28 +0200
committerMaksymilian Jopek <maks@jopek.eu>2025-04-28 18:03:28 +0200
commit0539d6963ebedd554cd3cd36b6c94df9659d6a6b (patch)
tree9408c7928d0725b114d0adfa811ba2bb3859f495 /src/components/Timer.tsx
downloadrustberry-frontend-master.tar.gz
rustberry-frontend-master.tar.zst
rustberry-frontend-master.zip
Initial commitHEADmaster
Simple frontend version with mock API
Diffstat (limited to 'src/components/Timer.tsx')
-rw-r--r--src/components/Timer.tsx178
1 files changed, 178 insertions, 0 deletions
diff --git a/src/components/Timer.tsx b/src/components/Timer.tsx
new file mode 100644
index 0000000..61a98f5
--- /dev/null
+++ b/src/components/Timer.tsx
@@ -0,0 +1,178 @@
+import { createSignal, For, Match, Show, Switch } from "solid-js";
+import styles from "./Timer.module.css";
+
+export const TIME_UNITS = ["s", "m", "h", "d"] as const;
+export type Timer = {
+ title: string;
+ options: {
+ time: number;
+ unit: (typeof TIME_UNITS)[number];
+ }[];
+ backgroundColor: string;
+ onTill: number | null;
+ secondsLeftText: string;
+ extraStopActions: { title: string }[];
+};
+
+export default function Timer(props: Timer) {
+ const [onTill, setOnTill] = createSignal(props.onTill);
+ const [unit, setUnit] = createSignal<(typeof TIME_UNITS)[number]>(
+ TIME_UNITS[1],
+ );
+ const [formattedTime, setFormattedTime] = createSignal("");
+ const [disabled, setDisabled] = createSignal(onTill() !== null);
+ const [customValue, setCustomValue] = createSignal<number | "">("");
+
+ let _intervalId = null;
+ function toggleAutoUpdate() {
+ if (_intervalId === null) {
+ updateTime();
+ _intervalId = setInterval(updateTime, 1000);
+ } else {
+ clearInterval(_intervalId);
+ _intervalId = null;
+ }
+ }
+ if (props.onTill !== null) {
+ toggleAutoUpdate();
+ }
+ function updateTime() {
+ const s = Math.floor(onTill() - Date.now() / 1000);
+ const days = Math.floor(s / (24 * 3600));
+ const hours = Math.floor((s % (24 * 3600)) / 3600);
+ const minutes = Math.floor((s % 3600) / 60);
+ const seconds = s % 60;
+
+ let text = "";
+ if (days > 0) {
+ text += days + "d : ";
+ }
+ if (hours > 0 || text.length > 0) {
+ text += hours + "h : ";
+ }
+ if (minutes > 0 || text.length > 0) {
+ text += minutes + "m : ";
+ }
+ if (seconds > 0 || text.length > 0) {
+ text += seconds + "s";
+ }
+ setFormattedTime(text);
+ }
+ function startStopClick() {
+ const val = customValue();
+ if (disabled() === false && val !== "") {
+ optionClick({ time: val, unit: unit() });
+ } else if (disabled() === true) {
+ setDisabled(false);
+ setOnTill(null);
+ toggleAutoUpdate();
+ }
+ }
+
+ function optionClick(option: Timer["options"][number]) {
+ setDisabled(true);
+ let newOnTill = Math.floor(Date.now() / 1000);
+ if (option.unit === "s") {
+ newOnTill += option.time;
+ } else if (option.unit === "m") {
+ newOnTill += option.time * 60;
+ } else if (option.unit === "h") {
+ newOnTill += option.time * 3600;
+ } else {
+ newOnTill += option.time * 3600 * 24;
+ }
+ setOnTill(newOnTill);
+ toggleAutoUpdate();
+ }
+
+ function extraStopClick(act: Timer["extraStopActions"][number]) {
+ // Some api call for the extra
+ startStopClick();
+ }
+ return (
+ <div class={styles.cont}>
+ <h2>{props.title}</h2>
+
+ <div class={styles.customOption}>
+ <input
+ type="number"
+ value={customValue()}
+ on:input={(e) =>
+ setCustomValue(e.target.value ? parseFloat(e.target.value) : "")
+ }
+ disabled={disabled()}
+ min="1"
+ />
+ <div class={styles.units}>
+ <For each={TIME_UNITS}>
+ {(u) => (
+ <span
+ on:click={() => (disabled() ? null : setUnit(u))}
+ classList={{
+ [styles.selectedUnit]: unit() === u,
+ [styles.notSelectedUnit]: unit() !== u,
+ [styles.darkgray]: disabled() === true,
+ }}
+ >
+ {u}
+ </span>
+ )}
+ </For>
+ </div>
+ <button
+ class={styles.customOptionBtn}
+ on:click={startStopClick}
+ disabled={
+ disabled()
+ ? false
+ : customValue() === "" || (customValue() as number) <= 0
+ }
+ style={{ "background-color": disabled() ? "red" : "green" }}
+ >
+ {disabled() ? "Wyłącz" : "Włącz"}
+ </button>
+ {/*<Show when={disabled() === true}>*/}
+ <For each={props.extraStopActions}>
+ {(act) => (
+ <button
+ disabled={disabled() === false}
+ on:click={[extraStopClick, act]}
+ class={styles.extraStopBtn}
+ >
+ {act.title}
+ </button>
+ )}
+ </For>
+ {/*</Show>*/}
+ </div>
+
+ <div class={styles.timeLeft}>
+ <Switch>
+ <Match when={onTill() === null}>
+ <span>Wyłączone</span>
+ <h2> </h2>
+ </Match>
+ <Match when={onTill() > 0}>
+ <span>{props.secondsLeftText} </span>
+ <h2>{formattedTime()}</h2>
+ </Match>
+ </Switch>
+ </div>
+
+ <div class={styles.options}>
+ <For each={props.options}>
+ {(option) => (
+ <button
+ class={styles.option}
+ on:click={[optionClick, option]}
+ style={{ "background-color": props.backgroundColor }}
+ disabled={disabled()}
+ >
+ {option.time} {option.unit}
+ </button>
+ )}
+ </For>
+ </div>
+ </div>
+ );
+}