mirror of
https://github.com/remvze/moodist.git
synced 2025-12-17 08:54:13 +00:00
feat: add controls to pomodoro
This commit is contained in:
parent
9f7de336e5
commit
7ed016d855
7 changed files with 147 additions and 11 deletions
19
src/components/toolbox/pomodoro/button/button.module.css
Normal file
19
src/components/toolbox/pomodoro/button/button.module.css
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
.button {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
font-size: var(--font-xsm);
|
||||||
|
color: var(--color-foreground);
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: var(--color-neutral-100);
|
||||||
|
border: 1px solid var(--color-neutral-200);
|
||||||
|
border-radius: 4px;
|
||||||
|
outline: none;
|
||||||
|
transition: 0.2s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--color-neutral-200);
|
||||||
|
}
|
||||||
|
}
|
||||||
19
src/components/toolbox/pomodoro/button/button.tsx
Normal file
19
src/components/toolbox/pomodoro/button/button.tsx
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
import { Tooltip } from '@/components/tooltip';
|
||||||
|
|
||||||
|
import styles from './button.module.css';
|
||||||
|
|
||||||
|
interface ButtonProps {
|
||||||
|
icon: React.ReactElement;
|
||||||
|
onClick: () => void;
|
||||||
|
tooltip: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Button({ icon, onClick, tooltip }: ButtonProps) {
|
||||||
|
return (
|
||||||
|
<Tooltip content={tooltip} hideDelay={0} placement="bottom" showDelay={0}>
|
||||||
|
<button className={styles.button} onClick={onClick}>
|
||||||
|
{icon}
|
||||||
|
</button>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
}
|
||||||
1
src/components/toolbox/pomodoro/button/index.ts
Normal file
1
src/components/toolbox/pomodoro/button/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export { Button } from './button';
|
||||||
|
|
@ -1 +1,17 @@
|
||||||
/* TODO */
|
.control {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-top: 8px;
|
||||||
|
|
||||||
|
& .completed {
|
||||||
|
font-size: var(--font-xsm);
|
||||||
|
color: var(--color-foreground-subtle);
|
||||||
|
}
|
||||||
|
|
||||||
|
& .buttons {
|
||||||
|
display: flex;
|
||||||
|
column-gap: 4px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
import { useState } from 'react';
|
import { useState, useEffect, useRef, useMemo } from 'react';
|
||||||
|
import { FaUndo, FaPlay, FaPause } from 'react-icons/fa/index';
|
||||||
|
|
||||||
import { Modal } from '@/components/modal';
|
import { Modal } from '@/components/modal';
|
||||||
import { Tabs } from './tabs';
|
import { Tabs } from './tabs';
|
||||||
import { Timer } from './timer';
|
import { Timer } from './timer';
|
||||||
|
import { Button } from './button';
|
||||||
|
|
||||||
// import styles from './pomodoro.module.css';
|
import styles from './pomodoro.module.css';
|
||||||
|
|
||||||
interface PomodoroProps {
|
interface PomodoroProps {
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
|
|
@ -13,18 +15,84 @@ interface PomodoroProps {
|
||||||
|
|
||||||
export function Pomodoro({ onClose, show }: PomodoroProps) {
|
export function Pomodoro({ onClose, show }: PomodoroProps) {
|
||||||
const [selectedTab, setSelectedTab] = useState('pomodoro');
|
const [selectedTab, setSelectedTab] = useState('pomodoro');
|
||||||
|
const [running, setRunning] = useState(false);
|
||||||
|
const [timer, setTimer] = useState(10);
|
||||||
|
const interval = useRef<ReturnType<typeof setInterval> | null>(null);
|
||||||
|
|
||||||
const tabs = [
|
const tabs = useMemo(
|
||||||
{ id: 'pomodoro', label: 'Pomodoro' },
|
() => [
|
||||||
{ id: 'short', label: 'Break' },
|
{ id: 'pomodoro', label: 'Pomodoro', time: 60 },
|
||||||
{ id: 'long', label: 'Long Break' },
|
{ id: 'short', label: 'Break', time: 60 },
|
||||||
];
|
{ id: 'long', label: 'Long Break', time: 60 },
|
||||||
|
],
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (running) {
|
||||||
|
if (interval.current) clearInterval(interval.current);
|
||||||
|
|
||||||
|
interval.current = setInterval(() => {
|
||||||
|
setTimer(prev => prev - 1);
|
||||||
|
}, 1000);
|
||||||
|
} else {
|
||||||
|
if (interval.current) clearInterval(interval.current);
|
||||||
|
}
|
||||||
|
}, [running]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (timer <= 0) {
|
||||||
|
if (interval.current) clearInterval(interval.current);
|
||||||
|
|
||||||
|
setRunning(false);
|
||||||
|
}
|
||||||
|
}, [timer]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const time = tabs.find(tab => tab.id === selectedTab)?.time || 10;
|
||||||
|
|
||||||
|
if (interval.current) clearInterval(interval.current);
|
||||||
|
|
||||||
|
setRunning(false);
|
||||||
|
setTimer(time);
|
||||||
|
}, [selectedTab, tabs]);
|
||||||
|
|
||||||
|
const toggleRunning = () => {
|
||||||
|
if (running) setRunning(false);
|
||||||
|
else if (timer <= 0) {
|
||||||
|
const time = tabs.find(tab => tab.id === selectedTab)?.time || 10;
|
||||||
|
|
||||||
|
setTimer(time);
|
||||||
|
setRunning(true);
|
||||||
|
} else setRunning(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const restart = () => {
|
||||||
|
if (interval.current) clearInterval(interval.current);
|
||||||
|
|
||||||
|
const time = tabs.find(tab => tab.id === selectedTab)?.time || 10;
|
||||||
|
|
||||||
|
setRunning(false);
|
||||||
|
setTimer(time);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal show={show} onClose={onClose}>
|
<Modal show={show} onClose={onClose}>
|
||||||
<h1>Pomodoro Timer</h1>
|
<h1>Pomodoro Timer</h1>
|
||||||
<Tabs selectedTab={selectedTab} tabs={tabs} onSelect={setSelectedTab} />
|
<Tabs selectedTab={selectedTab} tabs={tabs} onSelect={setSelectedTab} />
|
||||||
<Timer />
|
<Timer timer={timer} />
|
||||||
|
|
||||||
|
<div className={styles.control}>
|
||||||
|
<p className={styles.completed}>0 completed</p>
|
||||||
|
<div className={styles.buttons}>
|
||||||
|
<Button icon={<FaUndo />} tooltip="Restart" onClick={restart} />
|
||||||
|
<Button
|
||||||
|
icon={running ? <FaPause /> : <FaPlay />}
|
||||||
|
tooltip={running ? 'Pause' : 'Start'}
|
||||||
|
onClick={toggleRunning}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,15 @@
|
||||||
|
import { padNumber } from '@/helpers/number';
|
||||||
|
|
||||||
import styles from './timer.module.css';
|
import styles from './timer.module.css';
|
||||||
|
|
||||||
export function Timer() {
|
interface TimerProps {
|
||||||
return <div className={styles.timer}>25:00</div>;
|
timer: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Timer({ timer }: TimerProps) {
|
||||||
|
return (
|
||||||
|
<div className={styles.timer}>
|
||||||
|
{padNumber(Math.floor(timer / 60))}:{padNumber(timer % 60)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
3
src/helpers/number.ts
Normal file
3
src/helpers/number.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
export function padNumber(number: number, maxLength: number = 2): string {
|
||||||
|
return number.toString().padStart(maxLength, '0');
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue