Open seanmaisch opened 1 month ago
Hey @seanmaisch 👋, I'm here to help you out with any bugs, questions, or contributions you have. Let's tackle this carousel issue together!
To accommodate "disabled" items in the carousel and prevent landing on them, you can modify the next
and prev
functions in the carouselController
to skip disabled items. Here is an example of how you can achieve this:
disabled
property to your data items to indicate which items are disabled.next
and prev
functions to skip disabled items.Here is the modified code:
const Carousel = React.forwardRef<ICarouselInstance, TCarouselProps<any>>(
(_props, ref) => {
const props = useInitProps(_props);
const {
data,
dataLength,
loop,
onSnapToItem,
onScrollEnd,
onScrollStart,
scrollAnimationDuration,
withAnimation,
defaultIndex,
} = props;
const commonVariables = useCommonVariables(props);
const { size, handlerOffset } = commonVariables;
const offsetX = useDerivedValue(() => {
const totalSize = size * dataLength;
const x = handlerOffset.value % totalSize;
if (!loop) return handlerOffset.value;
return isNaN(x) ? 0 : x;
}, [loop, size, dataLength]);
const carouselController = useCarouselController({
loop,
size,
dataLength,
handlerOffset,
withAnimation,
defaultIndex,
duration: scrollAnimationDuration,
onScrollEnd: () => runOnJS(_onScrollEnd)(),
onScrollStart: () => !!onScrollStart && runOnJS(onScrollStart)(),
});
const { getCurrentIndex } = carouselController;
const next = React.useCallback(() => {
let currentIndex = getCurrentIndex();
let nextIndex = (currentIndex + 1) % dataLength;
while (data[nextIndex].disabled) {
nextIndex = (nextIndex + 1) % dataLength;
if (nextIndex === currentIndex) break; // Prevent infinite loop
}
carouselController.scrollTo({ index: nextIndex, animated: true });
}, [data, dataLength, getCurrentIndex, carouselController]);
const prev = React.useCallback(() => {
let currentIndex = getCurrentIndex();
let prevIndex = (currentIndex - 1 + dataLength) % dataLength;
while (data[prevIndex].disabled) {
prevIndex = (prevIndex - 1 + dataLength) % dataLength;
if (prevIndex === currentIndex) break; // Prevent infinite loop
}
carouselController.scrollTo({ index: prevIndex, animated: true });
}, [data, dataLength, getCurrentIndex, carouselController]);
const _onScrollEnd = React.useCallback(() => {
const _sharedIndex = Math.round(getSharedIndex());
const realIndex = computedRealIndexWithAutoFillData({
index: _sharedIndex,
dataLength: rawDataLength,
loop,
});
if (onSnapToItem) onSnapToItem(realIndex);
if (onScrollEnd) onScrollEnd(realIndex);
}, [
loop,
rawDataLength,
getSharedIndex,
onSnapToItem,
onScrollEnd,
]);
React.useImperativeHandle(
ref,
() => ({
next,
prev,
getCurrentIndex,
scrollTo: carouselController.scrollTo,
}),
[getCurrentIndex, next, prev, carouselController.scrollTo],
);
return (
<GestureHandlerRootView>
<ScrollViewGesture
size={size}
translation={handlerOffset}
onScrollStart={scrollViewGestureOnScrollStart}
onScrollEnd={scrollViewGestureOnScrollEnd}
>
<ItemRenderer
data={data}
dataLength={dataLength}
loop={loop}
size={size}
offsetX={offsetX}
handlerOffset={handlerOffset}
renderItem={renderItem}
/>
</ScrollViewGesture>
</GestureHandlerRootView>
);
},
);
export default Carousel;
In this implementation, the next
and prev
functions are modified to skip over disabled items by checking the disabled
property of each item in the data
array. This ensures that the carousel will not land on disabled items and will handle edge cases like consecutive disabled items and array bounds. The scrollTo
method in the carouselController
is used to ensure smooth animation when navigating to the next or previous item [1][2].
To continue talking to Dosu, mention @dosu.
Is your feature request related to a problem? Please describe.
Although this may be an odd scenario for some image carousels, it is rather frustrating when trying to use the carousel in the context of some selectable "content" that may need to be disabled. An example of this would be a list of products that are in store, but one or more of them are sold out. I would like to continue to display those options, but not allow the carousel to land on them as an index, and instead advance forward or backward towards the next non-disabled option.
Describe the solution you'd like
For my use case, I would like the ability prevent the user from "landing" on a carousel item that is disabled. There doesn't have to be any visual distinction included in this update, but allowing individual items to be set as disabled would have the scroll gesture advance to the next available option (if advancing "forward"), or move backwards towards the last available option.
There would need to be additional checks included as well:
Describe alternatives you've considered
I've tried utilizing an approach like this, however it results in a jerky, awkward animation, due to the fact it has to arrive at the disabled index and check against it before advancing to the next non-disabled option.
Thanks, and if you know of a way to achieve this cleanly, I'd be open to implementing your solution(s). I haven't found a decent way to do it myself.