Skip to content

Commit ce1373d

Browse files
committed
Refactor CircularDraggableSlider and TimeRange components: Remove unused code, adjust styles, and enhance functionality
1 parent b14aa15 commit ce1373d

File tree

4 files changed

+116
-194
lines changed

4 files changed

+116
-194
lines changed

App.tsx

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,6 @@ import { TimeRange } from './src/components/time-range';
1313
const LinesAmount = 200;
1414

1515
export default function App() {
16-
const animatedNumber = useSharedValue(0);
17-
const previousTick = useSharedValue(0);
18-
1916
const circularSliderRef = useRef<CircularDraggableSliderRefType>(null);
2017
const selectedDuration = useSharedValue(1);
2118
return (
@@ -32,20 +29,7 @@ export default function App() {
3229
linesAmount={LinesAmount}
3330
maxLineHeight={20}
3431
minLineHeight={8}
35-
onProgressChange={sliderProgress => {
36-
'worklet';
37-
if (sliderProgress < 0) {
38-
return;
39-
}
40-
41-
// Only trigger haptics when crossing a line (when tick value changes)
42-
if (sliderProgress !== previousTick.value) {
43-
previousTick.value = sliderProgress;
44-
}
45-
46-
// Bind the progress value to the animated number
47-
animatedNumber.value = sliderProgress;
48-
}}
32+
selectedDuration={selectedDuration}
4933
/>
5034
<TimeRange
5135
onDurationChange={durationHours => {
@@ -55,7 +39,7 @@ export default function App() {
5539
/>
5640
</View>
5741
<View>
58-
42+
5943
</View>
6044
<BottomTab />
6145
</View>

src/components/circle-time/index.tsx

Lines changed: 113 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,19 @@
1-
import { Dimensions, StyleSheet, View } from 'react-native';
1+
import { Dimensions, StyleSheet, Text, View } from 'react-native';
22

3-
import { forwardRef, useImperativeHandle } from 'react';
3+
import { forwardRef } from 'react';
44

55
import { LinearGradient } from 'expo-linear-gradient';
6-
import { Gesture, GestureDetector } from 'react-native-gesture-handler';
6+
import type { SharedValue } from 'react-native-reanimated';
77
import Animated, {
88
cancelAnimation,
99
interpolate,
1010
useAnimatedReaction,
11+
useAnimatedStyle,
1112
useDerivedValue,
1213
useSharedValue,
13-
withDecay,
1414
withTiming,
1515
} from 'react-native-reanimated';
1616

17-
import { useTimer } from '../useTimer';
1817
import { LineTime } from './line-time';
1918

2019

@@ -30,8 +29,6 @@ export type DraggableSliderProps = {
3029
bigLineIndexOffset?: number;
3130
// Optional: The width of each line, defaults to 1.5
3231
lineWidth?: number;
33-
// Optional: Callback function called when the progress changes
34-
onProgressChange?: (progress: number) => void;
3532
// Optional: Shared value representing the color of the indicator line
3633
indicatorColor?: string;
3734
// Optional: The color of the lines (default is #c6c6c6)
@@ -40,7 +37,7 @@ export type DraggableSliderProps = {
4037
bigLineColor?: string;
4138
// Optional: The color of the medium lines (default is #c6c6c6)
4239
mediumLineColor?: string;
43-
onCompletion?: () => void;
40+
selectedDuration?: SharedValue<number>;
4441
};
4542

4643
const { height: WindowHeight } = Dimensions.get('window');
@@ -63,66 +60,20 @@ export const CircularDraggableSlider = forwardRef<
6360
minLineHeight,
6461
bigLineIndexOffset = 10,
6562
lineWidth = 1.5,
66-
onProgressChange,
6763
lineColor = '#c6c6c6',
6864
bigLineColor = '#c6c6c6',
6965
mediumLineColor = '#c6c6c6',
70-
onCompletion,
66+
selectedDuration,
7167
},
7268
ref,
7369
) => {
7470
const progress = useSharedValue(0);
75-
const previousProgress = useSharedValue(0);
76-
71+
const isTimerEnabled = useSharedValue(false);
7772
const distanceBetweenTwoTicksRad = (2 * Math.PI) / linesAmount;
7873
const diameter = 2 * Math.PI * radius;
7974
const distanceBetweenTwoTicks = diameter / linesAmount;
8075
const listWidth = diameter;
8176

82-
const { runTimer, stopTimer, resetTimer, isTimerEnabled } = useTimer({
83-
progress,
84-
incrementOffset: distanceBetweenTwoTicks,
85-
onCompletion: () => {
86-
isTimerEnabled.value = false;
87-
onCompletion?.();
88-
},
89-
});
90-
91-
useImperativeHandle(ref, () => {
92-
return {
93-
resetTimer,
94-
runTimer,
95-
stopTimer,
96-
};
97-
}, [resetTimer, runTimer, stopTimer]);
98-
99-
const panGesture = Gesture.Pan()
100-
.onBegin(() => {
101-
if (isTimerEnabled.value) {
102-
return;
103-
}
104-
cancelAnimation(progress);
105-
previousProgress.value = progress.value;
106-
})
107-
.onUpdate(event => {
108-
if (isTimerEnabled.value) {
109-
return;
110-
}
111-
progress.value = event.translationY + previousProgress.value;
112-
})
113-
.onFinalize(event => {
114-
if (isTimerEnabled.value) {
115-
return;
116-
}
117-
if (progress.value > 0) {
118-
cancelAnimation(progress);
119-
progress.value = withTiming(0, { duration: 500 });
120-
return;
121-
}
122-
progress.value = withDecay({
123-
velocity: event.velocityY,
124-
});
125-
});
12677

12778
// Offset set to 0 for 90-degree rotation (indicator at right side)
12879
const offset = 0;
@@ -140,10 +91,27 @@ export const CircularDraggableSlider = forwardRef<
14091
const amountOfSeconds = Math.round(
14192
(radiants - offset) / distanceBetweenTwoTicksRad,
14293
);
143-
if (onProgressChange) {
144-
onProgressChange(amountOfSeconds);
94+
},
95+
);
96+
97+
useAnimatedReaction(
98+
() => selectedDuration?.value ?? null,
99+
duration => {
100+
if (duration === null) {
101+
return;
145102
}
103+
104+
const clampedDuration = Math.max(0, duration);
105+
const targetIndex = Math.min(
106+
linesAmount - 1,
107+
clampedDuration * bigLineIndexOffset,
108+
);
109+
const targetProgress = -distanceBetweenTwoTicks * targetIndex;
110+
111+
cancelAnimation(progress);
112+
progress.value = withTiming(targetProgress, { duration: 250 });
146113
},
114+
[bigLineIndexOffset, distanceBetweenTwoTicks, selectedDuration],
147115
);
148116

149117
return (
@@ -205,6 +173,24 @@ export const CircularDraggableSlider = forwardRef<
205173
/>
206174
);
207175
})}
176+
{new Array(10).fill(0).map((_, hourIndex) => {
177+
const label = `${hourIndex + 1} hr`;
178+
const tickIndex = (hourIndex + 1) * bigLineIndexOffset;
179+
if (tickIndex > linesAmount) {
180+
return null;
181+
}
182+
183+
return (
184+
<HourLabel
185+
key={label}
186+
label={label}
187+
radius={radius}
188+
linesAmount={linesAmount}
189+
progressRadiants={progressRadiants}
190+
tickIndex={tickIndex}
191+
/>
192+
);
193+
})}
208194
</Animated.View>
209195
<LinearGradient
210196
// Background Linear Gradient
@@ -219,27 +205,70 @@ export const CircularDraggableSlider = forwardRef<
219205
}}
220206
style={{
221207
position: 'absolute',
222-
height: radius * 2 + 40,
223-
width: radius,
224-
top: -(WindowHeight / 2 - radius - 16)
208+
height: radius * 2 + 94,
209+
width: radius + 20,
210+
left: -30,
211+
top: -(WindowHeight / 2 - radius + 8),
212+
// backgroundColor: 'red',
213+
borderRadius: 1000
225214
}}
226215
/>
227216
</View>
228-
<GestureDetector gesture={panGesture}>
229-
<Animated.View
230-
style={[
231-
{
232-
height: WindowHeight / 2,
233-
},
234-
styles.timer,
235-
]}
236-
/>
237-
</GestureDetector>
217+
<Animated.View
218+
pointerEvents="none"
219+
style={[
220+
{
221+
height: WindowHeight / 2,
222+
},
223+
styles.timer,
224+
]}
225+
/>
238226
</View>
239227
);
240228
},
241229
);
242230

231+
type HourLabelProps = {
232+
label: string;
233+
progressRadiants: SharedValue<number>;
234+
linesAmount: number;
235+
tickIndex: number;
236+
radius: number;
237+
};
238+
239+
const LABEL_WIDTH = 60;
240+
const LABEL_HEIGHT = 28;
241+
const LABEL_RADIUS_OFFSET = 40;
242+
243+
const HourLabel: React.FC<HourLabelProps> = ({
244+
label,
245+
progressRadiants,
246+
linesAmount,
247+
tickIndex,
248+
radius,
249+
}) => {
250+
const rStyle = useAnimatedStyle(() => {
251+
const angle =
252+
((2 * Math.PI) / linesAmount) * tickIndex - progressRadiants.value;
253+
const labelRadius = radius + LABEL_RADIUS_OFFSET;
254+
const x = Math.cos(angle) * labelRadius;
255+
const y = Math.sin(angle) * labelRadius;
256+
257+
return {
258+
transform: [
259+
{ translateX: x - LABEL_WIDTH / 2 },
260+
{ translateY: y - LABEL_HEIGHT / 2 },
261+
],
262+
};
263+
}, [linesAmount, radius, tickIndex]);
264+
265+
return (
266+
<Animated.View pointerEvents="none" style={[styles.hourLabel, rStyle]}>
267+
<Text style={styles.hourLabelText}>{label}</Text>
268+
</Animated.View>
269+
);
270+
};
271+
243272
const styles = StyleSheet.create({
244273
container: {
245274
width: '100%',
@@ -249,4 +278,17 @@ const styles = StyleSheet.create({
249278
position: 'absolute',
250279
width: '100%',
251280
},
281+
hourLabel: {
282+
position: 'absolute',
283+
width: LABEL_WIDTH,
284+
height: LABEL_HEIGHT,
285+
alignItems: 'center',
286+
justifyContent: 'center',
287+
},
288+
hourLabelText: {
289+
color: '#d8d8d8',
290+
fontFamily: 'SF-Pro-Rounded-Bold',
291+
fontSize: 20,
292+
left: 10
293+
},
252294
});

src/components/time-range/index.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,11 +104,9 @@ const styles = StyleSheet.create({
104104
container: {
105105
height: ContainerHeight,
106106
position: "absolute",
107-
width: ListWidth - 20,
107+
width: ListWidth - 30,
108108
right: 0,
109109
justifyContent: 'space-between',
110-
// alignItems: 'center',
111-
// backgroundColor: "red"
112110
},
113111
arrowWrapper: {
114112
alignItems: 'center',

0 commit comments

Comments
 (0)