@@ -11,6 +11,7 @@ import {
1111 clockTypes ,
1212 getAngle ,
1313 getHours ,
14+ getMinutes ,
1415 isPM ,
1516 PossibleClockTypes ,
1617} from './timeUtils'
@@ -19,55 +20,82 @@ import { useCallback } from 'react'
1920import { useLatest } from '../utils'
2021import AnalogClockHours from './AnalogClockHours'
2122
23+ import AnimatedClockSwitcher from './AnimatedClockSwitcher'
24+ import AnalogClockMinutes from './AnalogClockMinutes'
25+
2226export const circleSize = 215
2327
24- export default function AnalogClock ( {
28+ function AnalogClock ( {
2529 hours,
2630 minutes,
2731 focused,
2832 is24Hour,
33+ onChange,
2934} : {
3035 hours : number
3136 minutes : number
3237 focused : PossibleClockTypes
3338 is24Hour : boolean
39+ onChange : ( {
40+ hours,
41+ minutes,
42+ focused,
43+ } : {
44+ hours : number
45+ minutes : number
46+ focused ?: undefined | PossibleClockTypes
47+ } ) => any
3448} ) {
3549 const theme = useTheme ( )
36- const pointerNumber = focused === clockTypes . hours ? hours : minutes
50+
3751 const clockRef = React . useRef < View | null > ( null )
3852 const elementX = React . useRef < number > ( 0 )
3953 const elementY = React . useRef < number > ( 0 )
4054
41- // We need the latest values
55+ // Hooks are nice, sometimes :-)..
56+ // We need the latest values, since the onPointerMove uses a closure to the function
4257 const hoursRef = useLatest ( hours )
58+ const onChangeRef = useLatest ( onChange )
59+ const minutesRef = useLatest ( minutes )
4360 const focusedRef = useLatest ( focused )
4461 const is24HourRef = useLatest ( is24Hour )
62+
4563 const onPointerMove = React . useCallback (
46- ( e : GestureResponderEvent ) => {
64+ ( e : GestureResponderEvent , final : boolean ) => {
4765 let x = e . nativeEvent . pageX - elementX . current
4866 let y = e . nativeEvent . pageY - elementY . current
4967
5068 let angle = getAngle ( x , y , circleSize )
5169 if ( focusedRef . current === clockTypes . hours ) {
5270 let pickedHours = getHours ( angle )
53-
5471 if ( is24HourRef . current && isPM ( x , y , circleSize ) ) {
5572 pickedHours += 12
5673 }
57- if ( hoursRef . current !== pickedHours ) {
58- // TODO: add onChange
59- // setHours(pickedHours)
74+ if ( hoursRef . current !== pickedHours || final ) {
75+ onChangeRef . current ( {
76+ hours : pickedHours ,
77+ minutes : minutesRef . current ,
78+ focused : final ? clockTypes . minutes : undefined ,
79+ } )
80+ }
81+ } else if ( focusedRef . current === clockTypes . minutes ) {
82+ let pickedMinutes = getMinutes ( angle )
83+ if ( minutesRef . current !== pickedMinutes ) {
84+ onChangeRef . current ( {
85+ hours : hoursRef . current ,
86+ minutes : pickedMinutes ,
87+ } )
6088 }
6189 }
6290 } ,
63- [ hoursRef , focusedRef , is24HourRef ]
91+ [ focusedRef , is24HourRef , hoursRef , onChangeRef , minutesRef ]
6492 )
6593
6694 const panResponder = React . useRef (
6795 PanResponder . create ( {
68- onPanResponderGrant : onPointerMove ,
69- onPanResponderMove : onPointerMove ,
70- onPanResponderRelease : onPointerMove ,
96+ onPanResponderGrant : ( e ) => onPointerMove ( e , false ) ,
97+ onPanResponderMove : ( e ) => onPointerMove ( e , false ) ,
98+ onPanResponderRelease : ( e ) => onPointerMove ( e , true ) ,
7199
72100 onStartShouldSetPanResponder : returnTrue ,
73101 onStartShouldSetPanResponderCapture : returnTrue ,
@@ -91,6 +119,11 @@ export default function AnalogClock({
91119 } ,
92120 [ elementX , elementY ]
93121 )
122+
123+ // used to make pointer shorter if hours are selected and above 12
124+ const dynamicSize = focused === clockTypes . hours && hours > 12 ? 33 : 0
125+ const pointerNumber = focused === clockTypes . hours ? hours : minutes
126+ const degreesPerNumber = focused === clockTypes . hours ? 30 : 6
94127 return (
95128 < View
96129 ref = { clockRef }
@@ -108,30 +141,23 @@ export default function AnalogClock({
108141 cursor = { 'pointer' }
109142 >
110143 < View
111- style = { {
112- position : 'absolute' ,
113- width : circleSize / 2 - 4 - ( hours > 12 ? 33 : 0 ) ,
114- marginBottom : - 1 ,
115- height : 2 ,
116- borderRadius : 4 ,
117- backgroundColor : theme . colors . primary ,
118- transform : [
119- { rotate : - 90 + pointerNumber * 30 + 'deg' } ,
120- { translateX : circleSize / 4 - 4 - ( hours > 12 ? 33 / 2 : 0 ) } ,
121- ] ,
122- } }
144+ style = { [
145+ styles . line ,
146+ {
147+ backgroundColor : theme . colors . primary ,
148+ transform : [
149+ { rotate : - 90 + pointerNumber * degreesPerNumber + 'deg' } ,
150+ {
151+ translateX : circleSize / 4 - 4 - dynamicSize / 2 ,
152+ } ,
153+ ] ,
154+ width : circleSize / 2 - 4 - dynamicSize ,
155+ } ,
156+ ] }
123157 pointerEvents = "none"
124158 >
125159 < View
126- style = { {
127- borderRadius : 15 ,
128- height : 30 ,
129- width : 30 ,
130- position : 'absolute' ,
131- backgroundColor : theme . colors . primary ,
132- right : 0 ,
133- bottom : - 14 ,
134- } }
160+ style = { [ styles . endPoint , { backgroundColor : theme . colors . primary } ] }
135161 />
136162 </ View >
137163 < View
@@ -147,7 +173,11 @@ export default function AnalogClock({
147173 ] }
148174 />
149175 </ View >
150- < AnalogClockHours is24Hour = { is24Hour } hours = { hours } />
176+ < AnimatedClockSwitcher
177+ focused = { focused }
178+ hours = { < AnalogClockHours is24Hour = { is24Hour } hours = { hours } /> }
179+ minutes = { < AnalogClockMinutes minutes = { minutes } /> }
180+ />
151181 </ View >
152182 )
153183}
@@ -167,8 +197,25 @@ const styles = StyleSheet.create({
167197 width : 8 ,
168198 } ,
169199 center : { justifyContent : 'center' , alignItems : 'center' } ,
200+ endPoint : {
201+ borderRadius : 15 ,
202+ height : 30 ,
203+ width : 30 ,
204+ position : 'absolute' ,
205+ right : 0 ,
206+ bottom : - 14 ,
207+ } ,
208+ line : {
209+ position : 'absolute' ,
210+
211+ marginBottom : - 1 ,
212+ height : 2 ,
213+ borderRadius : 4 ,
214+ } ,
170215} )
171216
172217function returnTrue ( ) {
173218 return true
174219}
220+
221+ export default React . memo ( AnalogClock )
0 commit comments