Skip to content

Commit

Permalink
feat: add move up and down functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
remvze committed Jun 25, 2024
1 parent d356d77 commit 3e11fb6
Show file tree
Hide file tree
Showing 10 changed files with 178 additions and 13 deletions.
6 changes: 6 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"dependencies": {
"@astrojs/react": "3.6.0",
"@floating-ui/react": "0.26.0",
"@formkit/auto-animate": "0.8.2",
"@radix-ui/react-dropdown-menu": "2.0.6",
"@radix-ui/react-tooltip": "1.0.7",
"@types/howler": "2.2.10",
Expand Down
8 changes: 6 additions & 2 deletions src/components/toolbox/countdown-timer/countdown-timer.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { useAutoAnimate } from '@formkit/auto-animate/react';

import { Modal } from '@/components/modal';

import { Form } from './form';
Expand All @@ -11,11 +13,13 @@ interface TimerProps {
}

export function CountdownTimer({ onClose, show }: TimerProps) {
const [containerRef, enableAnimations] = useAutoAnimate<HTMLDivElement>();

return (
<Modal persist show={show} onClose={onClose}>
<h2 className={styles.title}>Countdown Timer</h2>
<Form />
<Timers />
<Form enableAnimations={enableAnimations} />
<Timers enableAnimations={enableAnimations} ref={containerRef} />
</Modal>
);
}
10 changes: 9 additions & 1 deletion src/components/toolbox/countdown-timer/form/form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ import { waitUntil } from '@/helpers/wait';

import styles from './form.module.css';

export function Form() {
interface FormProps {
enableAnimations: (enabled: boolean) => void;
}

export function Form({ enableAnimations }: FormProps) {
const [name, setName] = useState('');
const [hours, setHours] = useState(0);
const [minutes, setMinutes] = useState(10);
Expand All @@ -25,6 +29,8 @@ export function Form() {

if (totalSeconds === 0) return;

enableAnimations(false);

const id = add({
name,
total: totalSeconds,
Expand All @@ -37,6 +43,8 @@ export function Form() {
document
.getElementById(`timer-${id}`)
?.scrollIntoView({ behavior: 'smooth' });

enableAnimations(true);
};

return (
Expand Down
12 changes: 10 additions & 2 deletions src/components/toolbox/countdown-timer/timers/timer/timer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
} from 'react-icons/io5/index';

import { ReverseTimer } from './reverse-timer';
import { Toolbar } from './toolbar';

import { useCountdownTimers } from '@/stores/countdown-timers';
import { useAlarm } from '@/hooks/use-alarm';
Expand All @@ -17,17 +18,18 @@ import { cn } from '@/helpers/styles';
import styles from './timer.module.css';

interface TimerProps {
enableAnimations: (enabled: boolean) => void;
id: string;
}

export function Timer({ id }: TimerProps) {
export function Timer({ enableAnimations, id }: TimerProps) {
const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null);
const lastActiveTimeRef = useRef<number | null>(null);
const lastStateRef = useRef<{ spent: number; total: number } | null>(null);

const [isRunning, setIsRunning] = useState(false);

const { name, spent, total } = useCountdownTimers(state =>
const { first, last, name, spent, total } = useCountdownTimers(state =>
state.getTimer(id),
) || { name: '', spent: 0, total: 0 };

Expand Down Expand Up @@ -75,10 +77,14 @@ export function Timer({ id }: TimerProps) {
const handleDelete = () => {
if (isRunning) return showSnackbar('Please first stop the timer.');

enableAnimations(false);

setIsDeleting(true);
setSnapshot({ spent, total });

deleteTimer(id);

setTimeout(() => enableAnimations(true), 100);
};

useEffect(() => {
Expand Down Expand Up @@ -149,6 +155,8 @@ export function Timer({ id }: TimerProps) {
</div>
</header>

<Toolbar first={first} id={id} last={last} />

<ReverseTimer spent={spent} />

<div className={styles.left}>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { Toolbar } from './toolbar';
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
.toolbar {
position: absolute;
top: 12px;
right: 12px;
display: flex;
column-gap: 4px;
align-items: center;
height: 30px;
padding: 4px;
background-color: var(--color-neutral-50);
border: 1px solid var(--color-neutral-200);
border-radius: 4px;

& button {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
aspect-ratio: 1 / 1;
font-size: var(--font-xsm);
color: var(--color-foreground-subtle);
cursor: pointer;
background-color: transparent;
border: none;
transition: 0.2s;

&:disabled {
cursor: not-allowed;
opacity: 0.2;
}

&:not(:disabled):hover {
color: var(--color-foreground);
background-color: var(--color-neutral-100);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { IoIosArrowDown, IoIosArrowUp } from 'react-icons/io/index';

import { useCountdownTimers } from '@/stores/countdown-timers';

import styles from './toolbar.module.css';

interface ToolbarProps {
first: boolean;
id: string;
last: boolean;
}

export function Toolbar({ first, id, last }: ToolbarProps) {
const moveUp = useCountdownTimers(state => state.moveUp);
const moveDown = useCountdownTimers(state => state.moveDown);

return (
<div className={styles.toolbar}>
<button
disabled={first}
onClick={e => {
e.stopPropagation();
moveUp(id);
}}
>
<IoIosArrowUp />
</button>
<button
disabled={last}
onClick={e => {
e.stopPropagation();
moveDown(id);
}}
>
<IoIosArrowDown />
</button>
</div>
);
}
25 changes: 19 additions & 6 deletions src/components/toolbox/countdown-timer/timers/timers.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useMemo } from 'react';
import { useMemo, forwardRef } from 'react';

import { Timer } from './timer';
import { Notice } from './notice';
Expand All @@ -7,7 +7,14 @@ import { useCountdownTimers } from '@/stores/countdown-timers';

import styles from './timers.module.css';

export function Timers() {
interface TimersProps {
enableAnimations: (enabled: boolean) => void;
}

export const Timers = forwardRef(function Timers(
{ enableAnimations }: TimersProps,
ref: React.ForwardedRef<HTMLDivElement>,
) {
const timers = useCountdownTimers(state => state.timers);
const spent = useCountdownTimers(state => state.spent());
const total = useCountdownTimers(state => state.total());
Expand All @@ -30,13 +37,19 @@ export function Timers() {
)}
</header>

{timers.map(timer => (
<Timer id={timer.id} key={timer.id} />
))}
<div ref={ref}>
{timers.map(timer => (
<Timer
enableAnimations={enableAnimations}
id={timer.id}
key={timer.id}
/>
))}
</div>

<Notice />
</div>
) : null}
</div>
);
}
});
52 changes: 50 additions & 2 deletions src/stores/countdown-timers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ interface State {
interface Actions {
add: (timer: { name: string; total: number }) => string;
delete: (id: string) => void;
getTimer: (id: string) => Timer;
getTimer: (id: string) => Timer & { first: boolean; last: boolean };
moveDown: (id: string) => void;
moveUp: (id: string) => void;
rename: (id: string, newName: string) => void;
reset: (id: string) => void;
tick: (id: string, amount?: number) => void;
Expand Down Expand Up @@ -52,7 +54,53 @@ export const useCountdownTimers = create<State & Actions>()(
},

getTimer(id) {
return get().timers.filter(timer => timer.id === id)[0];
const timers = get().timers;
const timer = timers.filter(timer => timer.id === id)[0];
const index = timers.indexOf(timer);

return {
...timer,
first: index === 0,
last: index === timers.length - 1,
};
},

moveDown(id) {
set(state => {
const index = state.timers.findIndex(timer => timer.id === id);

if (index < state.timers.length - 1) {
const newTimers = [...state.timers];

[newTimers[index + 1], newTimers[index]] = [
newTimers[index],
newTimers[index + 1],
];

return { timers: newTimers };
}

return state;
});
},

moveUp(id) {
set(state => {
const index = state.timers.findIndex(timer => timer.id === id);

if (index > 0) {
const newTimers = [...state.timers];

[newTimers[index - 1], newTimers[index]] = [
newTimers[index],
newTimers[index - 1],
];

return { timers: newTimers };
}

return state;
});
},

rename(id, newName) {
Expand Down

0 comments on commit 3e11fb6

Please sign in to comment.