11import { useState , useEffect } from "react"
2+ import PropTypes from "prop-types"
23import NumberFlow , { continuous } from "@number-flow/react"
34import { Spoiler } from "spoiled"
5+ import * as m from "motion/react-m"
6+ import { AnimatePresence } from "motion/react"
47
58import Train from "../../../../../components/Train"
69import Text from "../../../../../components/Text"
@@ -9,9 +12,29 @@ import { DURATION, COMPLEX_EASING } from "../../../../../utils/animations"
912
1013import * as styles from "./BalanceCard.module.scss"
1114
15+ const CHANGE_DATA = {
16+ today : {
17+ value : "+0.82" ,
18+ percent : "0.11%" ,
19+ label : "Today" ,
20+ arrowDirection : "up" ,
21+ } ,
22+ allTime : {
23+ value : "+124.56" ,
24+ percent : "12.34%" ,
25+ label : "All Time" ,
26+ arrowDirection : "up" ,
27+ } ,
28+ }
29+
30+ const crossFadeTransition = {
31+ duration : 0.1 ,
32+ ease : [ 0.32 , 0 , 0.67 , 0 ] ,
33+ }
34+
1235/**
1336 * BalanceCard - универсальный компонент для отображения баланса
14- *
37+ *
1538 * @param {string } label - Текст лейбла (например, "Balance" или "TON Wallet Balance")
1639 * @param {string } initialBalance - Начальное значение баланса
1740 * @param {"default" | "overlay" } variant - Визуальный вариант: default (светлый фон) или overlay (для тёмного фона)
@@ -27,6 +50,9 @@ export default function BalanceCard({
2750} ) {
2851 const [ balance , setBalance ] = useState ( initialBalance )
2952 const [ hidden , setHidden ] = useState ( false )
53+ const [ isToday , setIsToday ] = useState ( true )
54+
55+ const changeData = isToday ? CHANGE_DATA . today : CHANGE_DATA . allTime
3056
3157 useEffect ( ( ) => {
3258 const updateBalance = ( ) => {
@@ -41,15 +67,14 @@ export default function BalanceCard({
4167 return ( ) => clearInterval ( interval )
4268 } , [ hidden ] )
4369
44- const variantClass = variant === "overlay" ? styles . cardOverlay : styles . cardDefault
70+ const variantClass =
71+ variant === "overlay" ? styles . cardOverlay : styles . cardDefault
4572
4673 return (
4774 < div
48- className = { [
49- styles . card ,
50- variantClass ,
51- className
52- ] . filter ( Boolean ) . join ( " " ) }
75+ className = { [ styles . card , variantClass , className ]
76+ . filter ( Boolean )
77+ . join ( " " ) }
5378 >
5479 < div className = { styles . data } >
5580 < Text
@@ -85,36 +110,88 @@ export default function BalanceCard({
85110 } }
86111 />
87112 </ Spoiler >
88- < Train divider = "space" >
89- < Text
90- apple = { { variant : "subheadline2" , weight : "semibold" } }
91- style = { { color : "var(--text-confirm-color)" } }
92- >
93- +0.82
94- </ Text >
95- < Text . Badge
96- apple = { {
97- variant : "subheadline2" ,
98- weight : "semibold" ,
99- arrow : { direction : "up" } ,
100- } }
101- variant = "tinted"
102- circled
103- style = { {
104- color : "var(--text-confirm-color)" ,
105- } }
106- >
107- 0.11%
108- </ Text . Badge >
109- < Text
110- apple = { { variant : "subheadline2" , weight : "semibold" } }
111- style = { { color : "var(--tg-theme-subtitle-text-color)" } }
112- >
113- Today
114- </ Text >
113+ < Train
114+ divider = "space"
115+ onClick = { ( ) => setIsToday ( ( prev ) => ! prev ) }
116+ style = { { cursor : "pointer" } }
117+ >
118+ < AnimatePresence mode = "wait" initial = { false } >
119+ < m . span
120+ key = { `value-${ isToday } ` }
121+ initial = { { opacity : 0 } }
122+ animate = { { opacity : 1 } }
123+ exit = { { opacity : 0 } }
124+ transition = { crossFadeTransition }
125+ >
126+ < Text
127+ apple = { {
128+ variant : "subheadline2" ,
129+ weight : "semibold" ,
130+ } }
131+ style = { { color : "var(--text-confirm-color)" } }
132+ >
133+ { changeData . value }
134+ </ Text >
135+ </ m . span >
136+ </ AnimatePresence >
137+ < AnimatePresence mode = "wait" initial = { false } >
138+ < m . span
139+ key = { `percent-${ isToday } ` }
140+ initial = { { opacity : 0 } }
141+ animate = { { opacity : 1 } }
142+ exit = { { opacity : 0 } }
143+ transition = { crossFadeTransition }
144+ >
145+ < Text . Badge
146+ apple = { {
147+ variant : "subheadline2" ,
148+ weight : "semibold" ,
149+ arrow : {
150+ direction : changeData . arrowDirection ,
151+ } ,
152+ } }
153+ variant = "tinted"
154+ circled
155+ style = { {
156+ color : "var(--text-confirm-color)" ,
157+ } }
158+ >
159+ { changeData . percent }
160+ </ Text . Badge >
161+ </ m . span >
162+ </ AnimatePresence >
163+ < AnimatePresence mode = "wait" initial = { false } >
164+ < m . span
165+ key = { `label-${ isToday } ` }
166+ initial = { { opacity : 0 } }
167+ animate = { { opacity : 1 } }
168+ exit = { { opacity : 0 } }
169+ transition = { crossFadeTransition }
170+ >
171+ < Text
172+ apple = { {
173+ variant : "subheadline2" ,
174+ weight : "semibold" ,
175+ } }
176+ style = { {
177+ color : "var(--tg-theme-subtitle-text-color)" ,
178+ } }
179+ >
180+ { changeData . label }
181+ </ Text >
182+ </ m . span >
183+ </ AnimatePresence >
115184 </ Train >
116185 </ div >
117186 { actions && < div className = { styles . actions } > { actions } </ div > }
118187 </ div >
119188 )
120189}
190+
191+ BalanceCard . propTypes = {
192+ label : PropTypes . string . isRequired ,
193+ initialBalance : PropTypes . string ,
194+ variant : PropTypes . oneOf ( [ "default" , "overlay" ] ) ,
195+ actions : PropTypes . node ,
196+ className : PropTypes . string ,
197+ }
0 commit comments