Skip to content

Commit

Permalink
new counter mechanism
Browse files Browse the repository at this point in the history
  • Loading branch information
danikova committed Dec 3, 2023
1 parent 6b74350 commit 1398dbd
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 79 deletions.
122 changes: 56 additions & 66 deletions src/components/counter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,45 +2,22 @@ import { useMemo } from "react";
import { DateTime } from "luxon";
import { cn } from "@/lib/utils";
import { ScoreWheel } from "./score-wheel";
import { useFormData, useRemainingTime } from "@/lib/hooks";
import { useFormData, useDeltaTime } from "@/lib/hooks";
import { Tooltip, TooltipContent, TooltipTrigger } from "./ui/tooltip";

function CounterDisplayNumber({
num,
max,
tooltip,
className,
}: {
num: number;
max?: number[];
tooltip?: string;
className?: string;
}) {
const nums = useMemo(() => {
const _nums = num
.toString()
.split("")
.map((n) => parseInt(n));
return _nums.length < 2 ? [0, ..._nums] : [..._nums];
}, [num]);

return (
<Tooltip>
<TooltipTrigger disabled={!tooltip} asChild>
<div className={cn("flex", className)}>
{nums.map((num, i) => (
<ScoreWheel key={`${i}`} num={num} max={max ? max[i] : 9} />
))}
</div>
</TooltipTrigger>
<TooltipContent side="bottom" className="[text-shadow:none]">
{tooltip}
</TooltipContent>
</Tooltip>
function DaysScrollWheel({ days }: { days: number }) {
const nums = useMemo(
() =>
String(days)
.padStart(2, "0")
.split("")
.map((n) => parseInt(n)),
[days]
);
return nums.map((num, i) => <ScoreWheel key={`${i}`} num={num} />);
}

function CounterSegment({
function TextSegment({
text,
className,
}: {
Expand All @@ -62,56 +39,69 @@ function CounterSegment({
export function Counter() {
const [{ endDate, digits }] = useFormData();
const isTMinus = useMemo(() => endDate > DateTime.now(), [endDate]);
const remaining = useRemainingTime();
const deltaTime = useDeltaTime();

const displays = useMemo(() => {
const _displays = [];
if (digits.includes("d"))
_displays.push(
<CounterDisplayNumber
key="d"
num={remaining.days}
tooltip={`${isTMinus ? "Remaining" : "Elapsed"} days`}
className="px-2"
/>
<Tooltip key="d">
<TooltipTrigger asChild>
<div className="flex px-2">
<DaysScrollWheel days={deltaTime.days} />
</div>
</TooltipTrigger>
<TooltipContent side="bottom" className="[text-shadow:none]">
{isTMinus ? "Remaining" : "Elapsed"} days
</TooltipContent>
</Tooltip>
);
if (digits.includes("h"))
_displays.push(
<CounterDisplayNumber
key="h"
num={remaining.hours}
max={[2, 9]}
tooltip={`${isTMinus ? "Remaining" : "Elapsed"} hours`}
className="px-2"
/>
<Tooltip key="h">
<TooltipTrigger asChild>
<div className="flex px-2">
<ScoreWheel num={deltaTime.hours} max={23} />
</div>
</TooltipTrigger>
<TooltipContent side="bottom" className="[text-shadow:none]">
{isTMinus ? "Remaining" : "Elapsed"} hours
</TooltipContent>
</Tooltip>
);
if (digits.includes("m"))
_displays.push(
<CounterDisplayNumber
key="m"
num={remaining.minutes}
max={[5, 9]}
tooltip={`${isTMinus ? "Remaining" : "Elapsed"} minutes`}
className="px-2"
/>
<Tooltip key="m">
<TooltipTrigger asChild>
<div className="flex px-2">
<ScoreWheel num={deltaTime.minutes} max={59} />
</div>
</TooltipTrigger>
<TooltipContent side="bottom" className="[text-shadow:none]">
{isTMinus ? "Remaining" : "Elapsed"} minutes
</TooltipContent>
</Tooltip>
);
if (digits.includes("s"))
_displays.push(
<CounterDisplayNumber
key="s"
num={remaining.seconds}
max={[5, 9]}
tooltip={`${isTMinus ? "Remaining" : "Elapsed"} seconds`}
className="px-2"
/>
<Tooltip key="s">
<TooltipTrigger asChild>
<div className="flex px-2">
<ScoreWheel num={deltaTime.seconds} max={59} />
</div>
</TooltipTrigger>
<TooltipContent side="bottom" className="[text-shadow:none]">
{isTMinus ? "Remaining" : "Elapsed"} seconds
</TooltipContent>
</Tooltip>
);
return _displays.length
? _displays.reduce(
(acc, val, index) => [
...acc,
...(index > 0
? [
<CounterSegment
<TextSegment
key={`del-${index}`}
text={":"}
className="w-[20px]"
Expand All @@ -123,12 +113,12 @@ export function Counter() {
[] as JSX.Element[]
)
: _displays;
}, [remaining, digits, isTMinus]);
}, [deltaTime, digits, isTMinus]);

return (
<div className="flex">
<CounterSegment text="T" />
<CounterSegment text={isTMinus ? "-" : "+"} />
<TextSegment text="T" />
<TextSegment text={isTMinus ? "-" : "+"} />
{displays}
</div>
);
Expand Down
9 changes: 5 additions & 4 deletions src/components/score-wheel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export function ScoreWheel({
max?: number;
}) {
nextNum = Math.min(Math.max(nextNum, 0), max);
const digitCount = useMemo(() => String(max).length, [max]);
const params = useMemo(() => getParams(max + 1), [max]);
const rotationMap = useMemo(() => {
const nums = Array.from({ length: params.itemCount }).map((_, i) => i);
Expand Down Expand Up @@ -84,7 +85,7 @@ export function ScoreWheel({
className="flex justify-center items-center box-border overflow-hidden"
style={{
height: ITEM_HEIGHT,
width: ITEM_WIDTH,
width: ITEM_WIDTH * digitCount,
}}
>
<div>
Expand All @@ -94,7 +95,7 @@ export function ScoreWheel({
transformOrigin: `50% calc(50% + ${ITEM_HEIGHT / 2}px)`,
marginTop: `-${ITEM_HEIGHT}px`,
height: ITEM_HEIGHT,
width: ITEM_WIDTH,
width: ITEM_WIDTH * digitCount,
transform: to([styles.rotateX], (x) => {
return `rotateX(${x}deg)`;
}),
Expand All @@ -107,10 +108,10 @@ export function ScoreWheel({
style={{
transform: `rotateX(${rotation.spawnPosition}deg) translateZ(${params.radius}px)`,
height: ITEM_HEIGHT,
width: ITEM_WIDTH,
width: ITEM_WIDTH * digitCount,
}}
>
<span>{num}</span>
<span>{String(num).padStart(digitCount, "0")}</span>
</div>
))}
</animated.div>
Expand Down
18 changes: 9 additions & 9 deletions src/lib/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import { DateTime } from "luxon";
import { useSearchParams } from "react-router-dom";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";

export function useRemainingTime() {
export function useDeltaTime() {
const [{ endDate: targetDate }] = useFormData();
const [remaining, setRemaining] = useState({
const [deltaTime, setDeltaTime] = useState({
days: 0,
hours: 0,
minutes: 0,
Expand All @@ -19,17 +19,17 @@ export function useRemainingTime() {
"minutes",
"second",
]);
setRemaining({
days: Math.floor(Math.abs(timeDiff.days)),
hours: Math.floor(Math.abs(timeDiff.hours)),
minutes: Math.floor(Math.abs(timeDiff.minutes)),
seconds: Math.floor(Math.abs(timeDiff.seconds)),
setDeltaTime({
days: ~~Math.abs(timeDiff.days),
hours: ~~Math.abs(timeDiff.hours),
minutes: ~~Math.abs(timeDiff.minutes),
seconds: ~~Math.abs(timeDiff.seconds),
});
}, 1000);
return () => clearInterval(id);
}, [remaining, targetDate]);
}, [deltaTime, targetDate]);

return remaining;
return deltaTime;
}

export interface FormData {
Expand Down

0 comments on commit 1398dbd

Please sign in to comment.