Skip to content

Commit 269a0ef

Browse files
committed
feat: add text transition
1 parent 22b076d commit 269a0ef

File tree

5 files changed

+36
-52
lines changed

5 files changed

+36
-52
lines changed

app/[lang]/(home)/Hero/StatsSymbol.tsx

Lines changed: 12 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
'use client';
22

33
import type {CSSProperties, ReactElement} from 'react';
4-
import {cloneElement, useEffect, useRef, useState} from 'react';
4+
import {cloneElement, useState} from 'react';
55
import {track} from '@amplitude/analytics-browser';
66
import clsx from 'clsx';
7+
import TextTransition, {presets} from 'react-text-transition';
78

89
import type {StatsInfo} from '../../../../src/fetches/github';
910

@@ -46,11 +47,9 @@ type StatName = (typeof statTypes)[number]['name'];
4647
const PluginStatsInfo = ({
4748
statsInfo,
4849
selectedStatName,
49-
isTransitioning,
5050
}: {
5151
statsInfo: StatsInfo;
5252
selectedStatName: StatName;
53-
isTransitioning: boolean;
5453
}): React.ReactElement => {
5554
const {name, description} = statsInfo[selectedStatName];
5655

@@ -61,22 +60,20 @@ const PluginStatsInfo = ({
6160
'flex-1 flex flex-col relative',
6261
)}
6362
>
64-
<div
65-
className={clsx(
66-
'body1 font-bold mb-3 text-left transition-opacity duration-300',
67-
isTransitioning ? 'opacity-0' : 'opacity-100'
68-
)}
63+
<TextTransition
64+
springConfig={presets.gentle}
65+
direction="down"
66+
className="body1 font-bold mb-3 text-left"
6967
>
7068
{name}
71-
</div>
72-
<div
73-
className={clsx(
74-
'body3 leading-[160%] text-left transition-opacity duration-500',
75-
isTransitioning ? 'opacity-0' : 'opacity-100'
76-
)}
69+
</TextTransition>
70+
<TextTransition
71+
springConfig={presets.wobbly}
72+
direction="down"
73+
className="body3 leading-[160%] text-left"
7774
>
7875
{description}
79-
</div>
76+
</TextTransition>
8077
</div>
8178
);
8279
};
@@ -91,28 +88,12 @@ const StatsSymbols = ({
9188
statsInfo: StatsInfo;
9289
}): ReactElement => {
9390
const [selectedStatName, setSelectedStatName] = useState<StatName>('tree');
94-
const [isTransitioning, setIsTransitioning] = useState(false);
95-
const transitionTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
9691

9792
const pressStat = (type: StatName): void => {
98-
if (transitionTimer.current) {
99-
clearTimeout(transitionTimer.current);
100-
}
101-
102-
setIsTransitioning(true);
103-
transitionTimer.current = setTimeout(() => setIsTransitioning(false), 50);
10493
setSelectedStatName(type);
10594
track('Press Stat Info', {type});
10695
};
10796

108-
useEffect(() => {
109-
return () => {
110-
if (transitionTimer.current) {
111-
clearTimeout(transitionTimer.current);
112-
}
113-
};
114-
}, []);
115-
11697
return (
11798
<div className={`flex flex-col ${className}`} style={style}>
11899
<div className={clsx('max-w-[220px]', 'flex flex-row')}>
@@ -139,7 +120,6 @@ const StatsSymbols = ({
139120
<PluginStatsInfo
140121
selectedStatName={selectedStatName}
141122
statsInfo={statsInfo}
142-
isTransitioning={isTransitioning}
143123
/>
144124
</div>
145125
);

bun.lock

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
"react-hamburger-menu": "^1.2.1",
2727
"react-hook-form": "^7.66.1",
2828
"react-hot-toast": "^2.6.0",
29+
"react-text-transition": "^3.1.0",
2930
"server-only": "^0.0.1",
3031
"tiny-invariant": "^1.3.3",
3132
"typescript": "5.9.3",
@@ -542,6 +543,18 @@
542543

543544
"@prisma/get-platform": ["@prisma/[email protected]", "", { "dependencies": { "@prisma/debug": "6.19.0" } }, "sha512-ym85WDO2yDhC3fIXHWYpG3kVMBA49cL1XD2GCsCF8xbwoy2OkDQY44gEbAt2X46IQ4Apq9H6g0Ex1iFfPqEkHA=="],
544545

546+
"@react-spring/animated": ["@react-spring/[email protected]", "", { "dependencies": { "@react-spring/shared": "~9.7.5", "@react-spring/types": "~9.7.5" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, "sha512-Tqrwz7pIlsSDITzxoLS3n/v/YCUHQdOIKtOJf4yL6kYVSDTSmVK1LI1Q3M/uu2Sx4X3pIWF3xLUhlsA6SPNTNg=="],
547+
548+
"@react-spring/core": ["@react-spring/[email protected]", "", { "dependencies": { "@react-spring/animated": "~9.7.5", "@react-spring/shared": "~9.7.5", "@react-spring/types": "~9.7.5" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, "sha512-rmEqcxRcu7dWh7MnCcMXLvrf6/SDlSokLaLTxiPlAYi11nN3B5oiCUAblO72o+9z/87j2uzxa2Inm8UbLjXA+w=="],
549+
550+
"@react-spring/rafz": ["@react-spring/[email protected]", "", {}, "sha512-5ZenDQMC48wjUzPAm1EtwQ5Ot3bLIAwwqP2w2owG5KoNdNHpEJV263nGhCeKKmuA3vG2zLLOdu3or6kuDjA6Aw=="],
551+
552+
"@react-spring/shared": ["@react-spring/[email protected]", "", { "dependencies": { "@react-spring/rafz": "~9.7.5", "@react-spring/types": "~9.7.5" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, "sha512-wdtoJrhUeeyD/PP/zo+np2s1Z820Ohr/BbuVYv+3dVLW7WctoiN7std8rISoYoHpUXtbkpesSKuPIw/6U1w1Pw=="],
553+
554+
"@react-spring/types": ["@react-spring/[email protected]", "", {}, "sha512-HVj7LrZ4ReHWBimBvu2SKND3cDVUPWKLqRTmWe/fNY6o1owGOX0cAHbdPDTMelgBlVbrTKrre6lFkhqGZErK/g=="],
555+
556+
"@react-spring/web": ["@react-spring/[email protected]", "", { "dependencies": { "@react-spring/animated": "~9.7.5", "@react-spring/core": "~9.7.5", "@react-spring/shared": "~9.7.5", "@react-spring/types": "~9.7.5" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, "sha512-lmvqGwpe+CSttsWNZVr+Dg62adtKhauGwLyGE/RRyZ8AAMLgb9x3NDMA5RMElXo+IMyTkPp7nxTB8ZQlmhb6JQ=="],
557+
545558
"@rolldown/pluginutils": ["@rolldown/[email protected]", "", {}, "sha512-8QagwMH3kNCuzD8EWL8R2YPW5e4OrHNSAHRFDdmFqEwEaD/KcNKjVoumo+gP2vW5eKB2UPbM6vTYiGZX0ixLnw=="],
546559

547560
"@rollup/plugin-babel": ["@rollup/[email protected]", "", { "dependencies": { "@babel/helper-module-imports": "^7.10.4", "@rollup/pluginutils": "^3.1.0" }, "peerDependencies": { "@babel/core": "^7.0.0", "@types/babel__core": "^7.1.9", "rollup": "^1.20.0||^2.0.0" }, "optionalPeers": ["@types/babel__core"] }, "sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q=="],
@@ -1746,6 +1759,8 @@
17461759

17471760
"react-refresh": ["[email protected]", "", {}, "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw=="],
17481761

1762+
"react-text-transition": ["[email protected]", "", { "dependencies": { "@react-spring/web": "^9.7.2" }, "peerDependencies": { "react": ">=18.0.0" } }, "sha512-NtXEVAXvSh78+8JAnrVjpbftzD4kPowacv4GB2Nyq9C/8ko6fSm6M/XvKWQLCaZi68i9F28b++Sp8uVThlzLyg=="],
1763+
17491764
"read-cache": ["[email protected]", "", { "dependencies": { "pify": "^2.3.0" } }, "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA=="],
17501765

17511766
"read-cmd-shim": ["[email protected]", "", {}, "sha512-1zM5HuOfagXCBWMN83fuFI/x+T/UhZ7k+KIzhrHXcQoeX5+7gmaDYjELQHmmzIodumBHeByBJT4QYS7ufAgs7A=="],

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
"react-hamburger-menu": "^1.2.1",
4444
"react-hook-form": "^7.66.1",
4545
"react-hot-toast": "^2.6.0",
46+
"react-text-transition": "^3.1.0",
4647
"server-only": "^0.0.1",
4748
"tiny-invariant": "^1.3.3",
4849
"typescript": "5.9.3",

src/hooks/useSearchHistory.ts

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,30 @@
11
'use client';
22

3-
import {useState, useCallback, useEffect} from 'react';
3+
import {useState, useCallback} from 'react';
44

55
const STORAGE_KEY = 'github-stats-search-history';
66
const MAX_HISTORY_ITEMS = 10;
77

8-
const readFromStorage = (): string[] => {
8+
const getInitialHistory = (): string[] => {
99
if (typeof window === 'undefined') return [];
1010

11-
const stored = localStorage.getItem(STORAGE_KEY);
11+
try {
12+
const stored = localStorage.getItem(STORAGE_KEY);
1213

13-
if (stored) {
14-
try {
14+
if (stored) {
1515
const parsed = JSON.parse(stored);
1616

1717
return Array.isArray(parsed) ? parsed : [];
18-
} catch {
19-
return [];
2018
}
19+
} catch {
20+
// Ignore parse errors
2121
}
2222

2323
return [];
2424
};
2525

2626
export function useSearchHistory() {
27-
const [history, setHistory] = useState<string[]>([]);
28-
const [isInitialized, setIsInitialized] = useState(false);
29-
30-
useEffect(() => {
31-
if (!isInitialized) {
32-
setHistory(readFromStorage());
33-
setIsInitialized(true);
34-
}
35-
}, [isInitialized]);
27+
const [history, setHistory] = useState<string[]>(getInitialHistory);
3628

3729
const addToHistory = useCallback((item: string) => {
3830
if (!item.trim()) return;

styles/output.css

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2376,10 +2376,6 @@ input[type='search']::-webkit-search-decoration,
23762376
transition-duration: 300ms;
23772377
}
23782378

2379-
.duration-500 {
2380-
transition-duration: 500ms;
2381-
}
2382-
23832379
@font-face {
23842380
font-family: 'doobooui';
23852381

0 commit comments

Comments
 (0)