Skip to content

Commit eafb17f

Browse files
committed
Merge branch 'main' into docs/better-info-for-file-loading
2 parents e3caff7 + 1f342f3 commit eafb17f

File tree

78 files changed

+3825
-826
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

78 files changed

+3825
-826
lines changed

.eslintrc.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ module.exports = {
2424
'@typescript-eslint/no-unsafe-argument': 'off',
2525
'@typescript-eslint/no-require-imports': 'off',
2626
'@typescript-eslint/no-empty-object-type': 'off',
27+
'@typescript-eslint/no-inline-styles': 'off',
2728
'@typescript-eslint/no-explicit-any': 'warn',
2829
'@typescript-eslint/ban-ts-comment': [
2930
'error',

apps/common-app/package.json

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,12 @@
1515
"lucide-react-native": "^0.555.0",
1616
"react-native-audio-api": "workspace:*",
1717
"react-native-background-timer": "2.4.1",
18-
"react-native-gesture-handler": "2.28.0",
19-
"react-native-reanimated": "4.1.3",
18+
"react-native-gesture-handler": "2.30.0",
19+
"react-native-reanimated": "4.2.1",
2020
"react-native-safe-area-context": "5.6.1",
21-
"react-native-screens": "4.18.0",
21+
"react-native-screens": "4.19.0",
2222
"react-native-svg": "15.14.0",
23-
"react-native-worklets": "0.6.1"
23+
"react-native-worklets": "0.7.1"
2424
},
2525
"devDependencies": {
2626
"@babel/core": "^7.25.2",
@@ -29,20 +29,20 @@
2929
"@react-native-community/cli": "20.0.0",
3030
"@react-native-community/cli-platform-android": "20.0.0",
3131
"@react-native-community/cli-platform-ios": "20.0.0",
32-
"@react-native/babel-preset": "0.82.0",
33-
"@react-native/eslint-config": "0.82.0",
34-
"@react-native/metro-config": "0.82.0",
35-
"@react-native/typescript-config": "0.82.0",
32+
"@react-native/babel-preset": "0.83.1",
33+
"@react-native/eslint-config": "0.83.1",
34+
"@react-native/metro-config": "0.83.1",
35+
"@react-native/typescript-config": "0.83.1",
3636
"@types/jest": "^29.5.13",
37-
"@types/react": "^19.1.1",
37+
"@types/react": "^19.2.0",
3838
"@types/react-native-background-timer": "^2.0.2",
3939
"@types/react-test-renderer": "^19.1.0",
4040
"eslint": "^8.57.0",
4141
"jest": "^29.6.3",
4242
"prettier": "^3.3.3",
43-
"react": "19.1.1",
44-
"react-native": "0.82.0",
45-
"react-test-renderer": "19.1.1",
43+
"react": "19.2.0",
44+
"react-native": "0.83.1",
45+
"react-test-renderer": "19.2.0",
4646
"typescript": "~5.8.3"
4747
}
4848
}

apps/common-app/src/App.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,8 +170,7 @@ const App: FC = () => {
170170
headerTintColor: colors.white,
171171
headerBackTitle: 'Back',
172172
headerBackAccessibilityLabel: 'Go back',
173-
}}
174-
>
173+
}}>
175174
<Stack.Screen
176175
name="MainTabs"
177176
component={MainTabsScreen}
Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
import React, { useEffect } from 'react';
2+
import { View, Text, StyleSheet, TextInput } from 'react-native';
3+
import { GestureDetector, Gesture } from 'react-native-gesture-handler';
4+
import Animated, {
5+
useSharedValue,
6+
useAnimatedStyle,
7+
withSpring,
8+
useAnimatedProps,
9+
} from 'react-native-reanimated';
10+
import { runOnJS } from 'react-native-reanimated';
11+
import { scheduleOnRN } from 'react-native-worklets';
12+
13+
const SLIDER_HEIGHT = 100;
14+
const THUMB_SIZE = 30;
15+
const TRACK_HEIGHT = SLIDER_HEIGHT - THUMB_SIZE;
16+
17+
const AText = Animated.createAnimatedComponent(TextInput);
18+
19+
interface VerticalSliderProps {
20+
label: string;
21+
value: number;
22+
labelColor?: string;
23+
valueColor?: string;
24+
onValueChange: (val: number) => void;
25+
possibleValues?: number[];
26+
}
27+
28+
const VerticalSlider: React.FC<VerticalSliderProps> = ({
29+
label,
30+
value,
31+
labelColor = '#333',
32+
valueColor = '#555',
33+
onValueChange,
34+
possibleValues
35+
}) => {
36+
const progress = useSharedValue(value);
37+
const startValue = useSharedValue(0);
38+
39+
useEffect(() => {
40+
progress.value = withSpring(value);
41+
}, [value, progress]);
42+
43+
const findClosestValue = (currentValue: number): number => {
44+
'worklet';
45+
if (!possibleValues || possibleValues.length === 0) {
46+
return currentValue;
47+
}
48+
49+
let closest = possibleValues[0];
50+
let minDistance = Math.abs(currentValue - closest);
51+
52+
for (const possibleValue of possibleValues) {
53+
const distance = Math.abs(currentValue - possibleValue);
54+
if (distance < minDistance) {
55+
minDistance = distance;
56+
closest = possibleValue;
57+
}
58+
}
59+
60+
return closest;
61+
};
62+
63+
const handleValueChange = (val: number) => {
64+
onValueChange(val);
65+
};
66+
67+
const gesture = Gesture.Pan()
68+
.onStart(() => {
69+
startValue.value = progress.value;
70+
})
71+
.onUpdate((e) => {
72+
const change = -e.translationY / TRACK_HEIGHT;
73+
const newValue = startValue.value + change;
74+
const clampedValue = Math.min(Math.max(newValue, 0), 1);
75+
progress.value = clampedValue;
76+
})
77+
.onEnd(() => {
78+
if (possibleValues && possibleValues.length > 0) {
79+
const snappedValue = findClosestValue(progress.value);
80+
progress.value = withSpring(snappedValue);
81+
scheduleOnRN(handleValueChange, snappedValue);
82+
} else {
83+
const finalValue = Math.min(Math.max(progress.value, 0), 1);
84+
scheduleOnRN(handleValueChange, finalValue);
85+
}
86+
});
87+
88+
const thumbStyle = useAnimatedStyle(() => {
89+
const translateY = (1 - progress.value) * TRACK_HEIGHT;
90+
return {
91+
transform: [{ translateY }],
92+
};
93+
});
94+
95+
const renderTickMarks = () => {
96+
if (!possibleValues || possibleValues.length === 0) {
97+
return null;
98+
}
99+
100+
return possibleValues.map((val, index) => {
101+
const position = (1 - val) * TRACK_HEIGHT + THUMB_SIZE / 2;
102+
return (
103+
<View
104+
key={index}
105+
style={[
106+
styles.tickMark,
107+
{
108+
top: position, // Center the tick mark
109+
},
110+
]}
111+
/>
112+
);
113+
});
114+
};
115+
116+
const displayProps = useAnimatedProps(() => {
117+
if (possibleValues && possibleValues.length > 0) {
118+
return {
119+
defaultValue: '',
120+
text: '',
121+
};
122+
}
123+
124+
return {
125+
defaultValue: (value * 100).toString(),
126+
text: (progress.value * 100).toFixed(0),
127+
};
128+
});
129+
130+
return (
131+
<View style={styles.sliderContainer}>
132+
<Text style={[styles.sliderLabel, { color: labelColor }]}>{label}</Text>
133+
<View style={styles.sliderTrackContainer}>
134+
<View style={styles.sliderTrack} />
135+
{renderTickMarks()}
136+
<GestureDetector gesture={gesture}>
137+
<Animated.View style={[styles.sliderThumbHitArea, thumbStyle]}>
138+
<View style={styles.sliderThumb} />
139+
</Animated.View>
140+
</GestureDetector>
141+
</View>
142+
<AText style={[styles.sliderValue, { color: valueColor }]} editable={false} animatedProps={displayProps} />
143+
{/* <Text style={[styles.sliderValue, { color: valueColor }]}>
144+
{getDisplayValue()}
145+
</Text> */}
146+
</View>
147+
);
148+
};
149+
150+
const styles = StyleSheet.create({
151+
sliderContainer: {
152+
alignItems: 'center',
153+
gap: 5,
154+
height: SLIDER_HEIGHT + 40,
155+
},
156+
sliderLabel: {
157+
fontWeight: 'bold',
158+
fontSize: 12,
159+
},
160+
sliderTrackContainer: {
161+
width: 40,
162+
height: SLIDER_HEIGHT,
163+
justifyContent: 'center',
164+
alignItems: 'center',
165+
position: 'relative',
166+
},
167+
sliderTrack: {
168+
position: 'absolute',
169+
width: 4,
170+
height: '100%',
171+
backgroundColor: '#111',
172+
borderRadius: 2,
173+
},
174+
tickMark: {
175+
position: 'absolute',
176+
width: 12,
177+
height: 2,
178+
backgroundColor: '#fff',
179+
borderRadius: 1,
180+
left: '50%',
181+
marginLeft: -6, // Center horizontally
182+
},
183+
sliderThumbHitArea: {
184+
position: 'absolute',
185+
top: 0,
186+
width: 40,
187+
height: THUMB_SIZE,
188+
justifyContent: 'center',
189+
alignItems: 'center',
190+
},
191+
sliderThumb: {
192+
width: 30,
193+
height: 15,
194+
backgroundColor: '#222',
195+
borderWidth: 1,
196+
borderColor: '#fff',
197+
borderRadius: 2,
198+
shadowColor: '#000',
199+
shadowOffset: { width: 0, height: 2 },
200+
shadowOpacity: 0.5,
201+
shadowRadius: 2,
202+
},
203+
sliderValue: {
204+
fontSize: 10,
205+
fontVariant: ['tabular-nums'],
206+
},
207+
});
208+
209+
export default VerticalSlider;

apps/common-app/src/components/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ export { default as Switch } from './Switch';
55
export { default as Select } from './Select';
66
export { default as Container } from './Container';
77
export { default as BGGradient } from './BGGradient';
8+
export { default as VerticalSlider } from './VerticalSlider';

0 commit comments

Comments
 (0)