From f3cb2a1b63e40f4f742ee2591b9353aa373f9783 Mon Sep 17 00:00:00 2001 From: MAZE Date: Sun, 25 Feb 2024 17:19:06 +0330 Subject: [PATCH] feat: implement time setting --- src/components/about/about.module.css | 2 + src/components/modal/modal.module.css | 2 +- .../toolbox/pomodoro/button/button.module.css | 6 +- .../toolbox/pomodoro/button/button.tsx | 10 +- .../toolbox/pomodoro/pomodoro.module.css | 19 ++++ src/components/toolbox/pomodoro/pomodoro.tsx | 93 ++++++++++++++----- .../toolbox/pomodoro/setting/index.ts | 1 + .../pomodoro/setting/setting.module.css | 66 +++++++++++++ .../toolbox/pomodoro/setting/setting.tsx | 92 ++++++++++++++++++ .../toolbox/pomodoro/tabs/tabs.module.css | 2 +- 10 files changed, 267 insertions(+), 26 deletions(-) create mode 100644 src/components/toolbox/pomodoro/setting/index.ts create mode 100644 src/components/toolbox/pomodoro/setting/setting.module.css create mode 100644 src/components/toolbox/pomodoro/setting/setting.tsx diff --git a/src/components/about/about.module.css b/src/components/about/about.module.css index 29adc68..3df2e3b 100644 --- a/src/components/about/about.module.css +++ b/src/components/about/about.module.css @@ -1,4 +1,6 @@ .about { + padding-top: 10px; + & .effect { position: sticky; top: 0; diff --git a/src/components/modal/modal.module.css b/src/components/modal/modal.module.css index 5530eda..5542b12 100644 --- a/src/components/modal/modal.module.css +++ b/src/components/modal/modal.module.css @@ -1,7 +1,7 @@ .overlay { position: fixed; inset: 0; - z-index: 19; + z-index: 20; background-color: rgb(9 9 11 / 40%); backdrop-filter: blur(5px); } diff --git a/src/components/toolbox/pomodoro/button/button.module.css b/src/components/toolbox/pomodoro/button/button.module.css index 6b7c628..ad4ecd4 100644 --- a/src/components/toolbox/pomodoro/button/button.module.css +++ b/src/components/toolbox/pomodoro/button/button.module.css @@ -4,7 +4,7 @@ justify-content: center; width: 30px; height: 30px; - font-size: var(--font-xsm); + font-size: var(--font-sm); color: var(--color-foreground); cursor: pointer; background-color: var(--color-neutral-100); @@ -16,4 +16,8 @@ &:hover { background-color: var(--color-neutral-200); } + + &.smallIcon { + font-size: var(--font-xsm); + } } diff --git a/src/components/toolbox/pomodoro/button/button.tsx b/src/components/toolbox/pomodoro/button/button.tsx index 5ecef5d..391b67d 100644 --- a/src/components/toolbox/pomodoro/button/button.tsx +++ b/src/components/toolbox/pomodoro/button/button.tsx @@ -1,17 +1,23 @@ import { Tooltip } from '@/components/tooltip'; +import { cn } from '@/helpers/styles'; + import styles from './button.module.css'; interface ButtonProps { icon: React.ReactElement; onClick: () => void; + smallIcon?: boolean; tooltip: string; } -export function Button({ icon, onClick, tooltip }: ButtonProps) { +export function Button({ icon, onClick, smallIcon, tooltip }: ButtonProps) { return ( - diff --git a/src/components/toolbox/pomodoro/pomodoro.module.css b/src/components/toolbox/pomodoro/pomodoro.module.css index 54351e5..33109cf 100644 --- a/src/components/toolbox/pomodoro/pomodoro.module.css +++ b/src/components/toolbox/pomodoro/pomodoro.module.css @@ -1,3 +1,22 @@ +.header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 8px; + + & .title { + font-size: var(--font-sm); + font-weight: 500; + color: var(--color-foreground-subtle); + } + + & .buttons { + display: flex; + column-gap: 4px; + align-items: center; + } +} + .control { display: flex; align-items: center; diff --git a/src/components/toolbox/pomodoro/pomodoro.tsx b/src/components/toolbox/pomodoro/pomodoro.tsx index a61314c..a4b4306 100644 --- a/src/components/toolbox/pomodoro/pomodoro.tsx +++ b/src/components/toolbox/pomodoro/pomodoro.tsx @@ -1,10 +1,12 @@ import { useState, useEffect, useRef, useMemo } from 'react'; import { FaUndo, FaPlay, FaPause } from 'react-icons/fa/index'; +import { IoMdSettings } from 'react-icons/io/index'; import { Modal } from '@/components/modal'; import { Tabs } from './tabs'; import { Timer } from './timer'; import { Button } from './button'; +import { Setting } from './setting'; import styles from './pomodoro.module.css'; @@ -14,11 +16,25 @@ interface PomodoroProps { } export function Pomodoro({ onClose, show }: PomodoroProps) { + const [showSetting, setShowSetting] = useState(false); + const [selectedTab, setSelectedTab] = useState('pomodoro'); const [running, setRunning] = useState(false); - const [timer, setTimer] = useState(10); + const [timer, setTimer] = useState(0); const interval = useRef | null>(null); + const [times, setTimes] = useState>({ + long: 60, + pomodoro: 60, + short: 60, + }); + + const [completions, setCompletions] = useState>({ + long: 0, + pomodoro: 0, + short: 0, + }); + const tabs = useMemo( () => [ { id: 'pomodoro', label: 'Pomodoro', time: 60 }, @@ -45,22 +61,26 @@ export function Pomodoro({ onClose, show }: PomodoroProps) { if (interval.current) clearInterval(interval.current); setRunning(false); + setCompletions(prev => ({ + ...prev, + [selectedTab]: prev[selectedTab] + 1, + })); } - }, [timer]); + }, [timer, selectedTab]); useEffect(() => { - const time = tabs.find(tab => tab.id === selectedTab)?.time || 10; + const time = times[selectedTab] || 10; if (interval.current) clearInterval(interval.current); setRunning(false); setTimer(time); - }, [selectedTab, tabs]); + }, [selectedTab, times]); const toggleRunning = () => { if (running) setRunning(false); else if (timer <= 0) { - const time = tabs.find(tab => tab.id === selectedTab)?.time || 10; + const time = times[selectedTab] || 10; setTimer(time); setRunning(true); @@ -70,29 +90,60 @@ export function Pomodoro({ onClose, show }: PomodoroProps) { const restart = () => { if (interval.current) clearInterval(interval.current); - const time = tabs.find(tab => tab.id === selectedTab)?.time || 10; + const time = times[selectedTab] || 10; setRunning(false); setTimer(time); }; return ( - -

Pomodoro Timer

- - + <> + +
+

Pomodoro Timer

-
-

0 completed

-
-
+
+ + + + +
+

+ {completions[selectedTab] || 0} completed +

+
+
- -
+
+ + setShowSetting(false)} + onChange={times => { + setShowSetting(false); + setTimes(times); + }} + /> + ); } diff --git a/src/components/toolbox/pomodoro/setting/index.ts b/src/components/toolbox/pomodoro/setting/index.ts new file mode 100644 index 0000000..0394f8b --- /dev/null +++ b/src/components/toolbox/pomodoro/setting/index.ts @@ -0,0 +1 @@ +export { Setting } from './setting'; diff --git a/src/components/toolbox/pomodoro/setting/setting.module.css b/src/components/toolbox/pomodoro/setting/setting.module.css new file mode 100644 index 0000000..5f3c55a --- /dev/null +++ b/src/components/toolbox/pomodoro/setting/setting.module.css @@ -0,0 +1,66 @@ +.title { + margin-bottom: 16px; + font-family: var(--font-heading); + font-size: var(--font-md); + font-weight: 600; +} + +& .form { + display: flex; + flex-direction: column; + + & .field { + display: flex; + flex-direction: column; + row-gap: 8px; + margin-bottom: 16px; + + & .label { + font-size: var(--font-sm); + color: var(--color-foreground); + + & span { + color: var(--color-foreground-subtle); + } + } + + & .input { + display: block; + height: 40px; + padding: 0 8px; + color: var(--color-foreground); + background-color: var(--color-neutral-50); + border: 1px solid var(--color-neutral-200); + border-radius: 4px; + outline: none; + } + } + + & .buttons { + display: flex; + column-gap: 8px; + align-items: center; + justify-content: flex-end; + + & button { + display: flex; + align-items: center; + justify-content: center; + height: 40px; + padding: 0 16px; + font-size: var(--font-sm); + font-weight: 500; + color: var(--color-foreground); + cursor: pointer; + background-color: var(--color-neutral-200); + border: none; + border-radius: 4px; + outline: none; + + &.primary { + color: var(--color-neutral-100); + background-color: var(--color-neutral-950); + } + } + } +} diff --git a/src/components/toolbox/pomodoro/setting/setting.tsx b/src/components/toolbox/pomodoro/setting/setting.tsx new file mode 100644 index 0000000..1322481 --- /dev/null +++ b/src/components/toolbox/pomodoro/setting/setting.tsx @@ -0,0 +1,92 @@ +import { useEffect, useState } from 'react'; + +import { Modal } from '@/components/modal'; + +import styles from './setting.module.css'; + +interface SettingProps { + onChange: (newTimes: Record) => void; + onClose: () => void; + show: boolean; + times: Record; +} + +export function Setting({ onChange, onClose, show, times }: SettingProps) { + const [values, setValues] = useState(times); + + useEffect(() => setValues(times), [times]); + + const handleChange = (id: string) => (value: number) => { + setValues(prev => ({ ...prev, [id]: value * 60 })); + }; + + const handleSubmit = e => { + e.preventDefault(); + + onChange(values); + }; + + return ( + +

Change Times

+ +
+ + + + +
+ + +
+ +
+ ); +} + +interface FieldProps { + id: string; + label: string; + onChange: (value: number) => void; + value: number; +} + +function Field({ id, label, onChange, value }: FieldProps) { + return ( +
+ + onChange(Number(e.target.value))} + /> +
+ ); +} diff --git a/src/components/toolbox/pomodoro/tabs/tabs.module.css b/src/components/toolbox/pomodoro/tabs/tabs.module.css index 57504ad..d181e20 100644 --- a/src/components/toolbox/pomodoro/tabs/tabs.module.css +++ b/src/components/toolbox/pomodoro/tabs/tabs.module.css @@ -28,7 +28,7 @@ border-color: var(--color-neutral-300); } - &:hover { + &:not(.selected):hover { color: var(--color-foreground); background-color: var(--color-neutral-100); }