1
1
import { PotentialSet } from '@/models/session-models' ;
2
2
import BigNumber from 'bignumber.js' ;
3
- import { useState } from 'react' ;
4
- import { TouchableRipple } from 'react-native-paper' ;
5
- import { Text , View } from 'react-native' ;
3
+ import { useEffect , useState } from 'react' ;
4
+ import { TouchableRipple , useTheme } from 'react-native-paper' ;
5
+ import {
6
+ Animated ,
7
+ Easing ,
8
+ Text ,
9
+ Touchable ,
10
+ useAnimatedValue ,
11
+ View ,
12
+ } from 'react-native' ;
6
13
import WeightFormat from '@/components/presentation/weight-format' ;
7
14
import WeightDialog from '@/components/presentation/weight-dialog' ;
15
+ import { useBaseThemeset } from '@/hooks/useBaseThemeset' ;
16
+ import { PressableProps } from 'react-native-paper/lib/typescript/components/TouchableRipple/Pressable' ;
8
17
9
18
interface PotentialSetCounterProps {
10
19
set : PotentialSet ;
@@ -20,86 +29,138 @@ interface PotentialSetCounterProps {
20
29
}
21
30
22
31
export default function PotentialSetCounter ( props : PotentialSetCounterProps ) {
32
+ const theme = useBaseThemeset ( ) ;
33
+ const scaleValue = useAnimatedValue ( 1 ) ;
34
+ const weightScaleValue = useAnimatedValue ( props . showWeight ? 1 : 0 ) ;
23
35
const [ isHolding , setIsHolding ] = useState ( false ) ;
24
36
const [ isWeightDialogOpen , setIsWeightDialogOpen ] = useState ( false ) ;
25
37
const repCountValue = props . set ?. set ?. repsCompleted ;
26
38
const repCountRounding = props . showWeight ? 'rounded-t-xl' : 'rounded-xl' ;
27
- const weightScale = props . showWeight ? 'p-2 h-8 w-14' : 'h-0 w-0 p-0' ;
28
39
const boxShadowFill = repCountValue === undefined ? '2rem' : '0' ;
29
40
const holdingClass = isHolding ? 'scale-110' : '' ;
30
- const colorClass =
31
- repCountValue !== undefined ? 'bg-primary' : 'bg-secondary-container' ;
41
+
42
+ useEffect ( ( ) => {
43
+ Animated . timing ( scaleValue , {
44
+ toValue : isHolding ? 1.1 : 1 ,
45
+ duration : 200 ,
46
+ useNativeDriver : true ,
47
+ } ) . start ( ) ;
48
+ } , [ isHolding , scaleValue ] ) ;
49
+
50
+ useEffect ( ( ) => {
51
+ Animated . timing ( weightScaleValue , {
52
+ toValue : props . showWeight ? 1 : 0 ,
53
+ duration : 150 ,
54
+ easing : Easing . cubic ,
55
+ useNativeDriver : false ,
56
+ } ) . start ( ) ;
57
+ } , [ props . showWeight , weightScaleValue ] ) ;
58
+
32
59
const textColorClass =
33
60
repCountValue !== undefined
34
61
? 'text-on-primary'
35
62
: 'text-on-secondary-container' ;
36
63
37
64
const callbacks = props . isReadonly
38
65
? { }
39
- : {
66
+ : ( {
40
67
onPress : props . onTap ,
41
68
onLongPress : props . onHold ,
69
+ onTouchStart : ( ) => setIsHolding ( true ) ,
42
70
onPointerDown : ( ) => setIsHolding ( true ) ,
43
71
onPointerUp : ( ) => setIsHolding ( false ) ,
44
72
onPointerLeave : ( ) => setIsHolding ( false ) ,
45
- } ;
73
+ onTouchEnd : ( ) => setIsHolding ( false ) ,
74
+ } satisfies Touchable & Omit < PressableProps , 'children' > ) ;
46
75
47
76
return (
48
- < View className = "select-none justify-center items-center" >
77
+ < Animated . View
78
+ style = { {
79
+ justifyContent : 'center' ,
80
+ alignItems : 'center' ,
81
+ userSelect : 'none' ,
82
+ transform : [
83
+ {
84
+ scale : scaleValue ,
85
+ } ,
86
+ ] ,
87
+ } }
88
+ >
49
89
< TouchableRipple
50
- className = { `
51
- aspect-square
52
- flex-shrink-0
53
- text-center
54
- p-0
55
- h-14
56
- w-14
57
- ${ repCountRounding }
58
- [transition:border-radius_150ms_cubic-bezier(0.4,0,0.2,1),background-color_150ms_cubic-bezier(0.4,0,0.2,1),transform_400ms]
59
- items-center
60
- align-middle
61
- justify-center
62
- ${ holdingClass }
63
- ${ colorClass } ` }
90
+ style = { {
91
+ aspectRatio : 1 ,
92
+ flexShrink : 0 ,
93
+ padding : 0 ,
94
+ height : 56 ,
95
+ width : 56 ,
96
+ borderTopLeftRadius : 10 ,
97
+ borderTopRightRadius : 10 ,
98
+ borderBottomRightRadius : props . showWeight ? 0 : 10 ,
99
+ borderBottomLeftRadius : props . showWeight ? 0 : 10 ,
100
+ alignItems : 'center' ,
101
+ justifyContent : 'center' ,
102
+ backgroundColor :
103
+ repCountValue !== undefined
104
+ ? theme . primary
105
+ : theme . secondaryContainer ,
106
+ } }
64
107
{ ...callbacks }
65
108
disabled = { props . isReadonly }
66
109
>
67
- < Text className = { textColorClass } >
68
- < Text className = "font-bold" > { repCountValue ?? '-' } </ Text >
69
- < Text className = "inline text-sm align-text-top" >
110
+ < Text
111
+ style = { {
112
+ color :
113
+ repCountValue !== undefined
114
+ ? theme . onPrimary
115
+ : theme . onSecondaryContainer ,
116
+ } }
117
+ >
118
+ < Text style = { { fontWeight : 'bold' } } > { repCountValue ?? '-' } </ Text >
119
+ < Text style = { { fontSize : 12 , verticalAlign : 'top' } } >
70
120
/{ props . maxReps }
71
121
</ Text >
72
122
</ Text >
73
123
</ TouchableRipple >
74
- < TouchableRipple
75
- className = { `
76
- rounded-b-xl
77
- ${ weightScale }
78
- overflow-hidden
79
- ${ holdingClass }
80
- text-xs
81
- [transition:height_150ms_cubic-bezier(0.4,0,0.2,1),padding_150ms_cubic-bezier(0.4,0,0.2,1),width_150ms_cubic-bezier(0.4,0,0.2,1),transform_400ms]
82
- flex
83
- flex-row
84
- border-t
85
- justify-center
86
- border-outline
87
- bg-surface-container-high
88
- text-on-surface-variant` }
89
- onPress = {
90
- props . isReadonly ? undefined : ( ) => setIsWeightDialogOpen ( true )
91
- }
92
- disabled = { props . isReadonly }
124
+ < Animated . View
125
+ style = { {
126
+ height : weightScaleValue . interpolate ( {
127
+ inputRange : [ 0 , 1 ] ,
128
+ outputRange : [ 0 , 36 ] ,
129
+ } ) ,
130
+ paddingBlock : weightScaleValue . interpolate ( {
131
+ inputRange : [ 0 , 1 ] ,
132
+ outputRange : [ 0 , 8 ] ,
133
+ } ) ,
134
+ width : weightScaleValue . interpolate ( {
135
+ inputRange : [ 0 , 1 ] ,
136
+ outputRange : [ 0 , 56 ] ,
137
+ } ) ,
138
+ borderTopWidth : weightScaleValue ,
139
+ borderColor : theme . outline ,
140
+ backgroundColor : theme . surfaceContainerHigh ,
141
+ borderBottomLeftRadius : 10 ,
142
+ borderBottomRightRadius : 10 ,
143
+ } }
93
144
>
94
- < WeightFormat weight = { props . set . weight } suffixClass = "" />
95
- </ TouchableRipple >
145
+ < TouchableRipple
146
+ style = { {
147
+ alignItems : 'center' ,
148
+ } }
149
+ onPress = {
150
+ props . isReadonly ? undefined : ( ) => setIsWeightDialogOpen ( true )
151
+ }
152
+ disabled = { props . isReadonly }
153
+ >
154
+ < WeightFormat weight = { props . set . weight } suffixClass = "" />
155
+ </ TouchableRipple >
156
+ </ Animated . View >
96
157
< WeightDialog
97
158
open = { isWeightDialogOpen }
98
159
increment = { props . weightIncrement }
99
160
weight = { props . set . weight }
100
161
onClose = { ( ) => setIsWeightDialogOpen ( false ) }
101
162
updateWeight = { props . onUpdateWeight }
102
163
/>
103
- </ View >
164
+ </ Animated . View >
104
165
) ;
105
166
}
0 commit comments