I’m trying to always center the selectDay when it first loads (useEffect), at the moment I have arrived at the following conclusion (represented on the image).
The goal in which I could not yet reach, is to center / calculate the width of the item and list so the item always centers. Items like 1,2,3,4,28,29,31 will probably not be centered since the days of month have ended
The line represents the center of the scrollview. The number is the selected day and the width under it is the width I have to insert in itemWidth
so it centers on the screen.
Although I could use something as the following, I don’t tink it would be the best approach since we can get different devices widths:
const getItemWidth = (day: number) => {
if (day === 28) return 46;
if (day === 25) return 44.8;
if (day === 20) return 42;
if (day === 15) return 37.6;
if (day === 10) return 28.6;
if (day === 8) return 22.6;
return 46; // default width
};
This is what I got:
interface Props {
date: Date;
selectedDay: number;
setSelectedDay: Dispatch<SetStateAction<number>>;
}
const DateSelector = ({
date,
selectedDay,
setSelectedDay,
}: Props) => {
const scrollViewRef = useRef<ScrollView>(null);
const daysArray = useMemo(() => {
const dateMoment = moment(date);
const year = dateMoment.year();
const month = dateMoment.month();
const firstDayOfMonth = moment().year(year).month(month).startOf('month');
const daysInMonth = firstDayOfMonth.daysInMonth();
const days = Array.from({ length: daysInMonth }, (_, i) => {
const dayAbbr = moment({ year, month, day: i + 1 }).format('ddd');
return {
day: i + 1,
abbr: dayAbbr.charAt(0).toUpperCase() + dayAbbr.slice(1),
};
});
return days;
}, [date]);
useEffect(() => {
const scrollToSelectedDay = () => {
if (scrollViewRef.current) {
const index = daysArray.findIndex(day => day.day === selectedDay);
const itemWidth = 46; // Approximate width of each item in pixels
const offset =
index * itemWidth -
itemWidth / 2 +
(scrollViewRef.current
?.getScrollResponder()
?.scrollResponder?.getLayoutMetrics().width / 2 || 0);
scrollViewRef.current.scrollTo({ x: offset, animated: true });
}
};
scrollToSelectedDay();
}, [selectedDay, daysArray]);
return (
<ScrollView
ref={scrollViewRef}
style={styles.container}
horizontal
showsHorizontalScrollIndicator={false}>
{daysArray.map(({ day, abbr }) => {
const isSelected = day === selectedDay;
return (
<TouchableOpacity
key={day}
style={styles.itemContainer}
onPress={() => setSelectedDay(day)}>
<Text
size={18}
weight="semibold"
style={isSelected ? styles.selected : styles.unSelected}
color={
isSelected ? colors.primary : colors.black
}>
{day}
</Text>
<Text size={14}>{abbr}</Text>
</TouchableOpacity>
);
})}
</ScrollView>
);
};
const styles = StyleSheet.create({
container: {
width: '100%',
flexDirection: 'row',
},
itemContainer: {
alignItems: 'center',
justifyContent: 'center',
marginRight: 10,
},
selected: {
backgroundColor: colors.lightGreen,
width: '100%',
textAlign: 'center',
paddingHorizontal: 12,
borderRadius: 50,
},
unSelected: {
paddingHorizontal: 12,
},
});
export default DateSelector;
This is how it looks on screen: