maxmarinich / react-alice-carousel

React responsive component for building content galleries, content rotators and any React carousels
MIT License
832 stars 91 forks source link

Navigation issue #300

Closed OnlinePresence closed 9 months ago

OnlinePresence commented 9 months ago

Hey, I've ran into a problem with navigation button for next item not being disabled when the last item is fully in view during autoWidth prop setting. I tried fining the source of the issue before creating a new file and checking it with the code from the autoWidth live demo example, with the only change being the items prop set to an array of 8 images. The issue still exists on my local basic autoWidth example code. With the code from live demo, whenever there's let's say 6 items, and active index is set to the 3rd item, but the 6th item is already fully visible and there's no next items after the 6th one to scroll through, the navigation button for the next item is disabled. For my local example in that situation, the button is not disabled and I'm able to click on it 3 more times to change the index, with items not moving whenever the active index is set to anywhere between 3rd and 6th item, so there's no visible change in items, only the index that's set to active. What I want to achieve is the behaviour from the live demo on autoWidth prop and variable number of items being passed, so whenever you use the component and pass a random number of items with different widths, the navigation to the next item will become disabled when the last item is fully visible inside the carousel.

Not sure if that's an issue with "react-alice-carousel": "^2.8.0" version or something else, but I'm not sure how can I get the desired outcome

https://github.com/maxmarinich/react-alice-carousel/assets/17722927/0780a009-5429-4fa4-89e0-5d817d9799e4

maxmarinich commented 9 months ago

Hi, @OnlinePresence! Please provide the code sample.

OnlinePresence commented 9 months ago
import React from 'react';
import AliceCarousel from 'react-alice-carousel';
import "react-alice-carousel/lib/scss/alice-carousel.scss";

const items = [
        <img
            src="https://picsum.photos/100/75"
            style={{ width: 100, height: 75 }}
            role="presentation"
            alt="carousel item"
        />,
<img
    src="https://picsum.photos/120/75"
    style={{ width: 120, height: 75 }}
    role="presentation"
    alt="carousel item"
/>,
    <img src="https://picsum.photos/70/75" style={{ width: 70, height: 75 }} role="presentation" alt="carousel item" />,
        <img
            src="https://picsum.photos/135/75"
            style={{ width: 135, height: 75 }}
            role="presentation"
            alt="carousel item"
        />,
        <img src="https://picsum.photos/90/75" style={{ width: 90, height: 75 }} role="presentation" alt="carousel item" />,
        <img
            src="https://picsum.photos/115/75"
            style={{ width: 115, height: 75 }}
            role="presentation"
            alt="carousel item"
        />,
        <img
            src="https://picsum.photos/180/75"
            style={{ width: 180, height: 75 }}
            role="presentation"
            alt="carousel item"
        />,
        <img
            src="https://picsum.photos/105/75"
            style={{ width: 105, height: 75 }}
            role="presentation"
            alt="carousel item"
        />,
    ]

const Carousel = () => (
    <AliceCarousel
        autoWidth
        mouseTracking
        items={items}
    />
);

export default Carousel;
maxmarinich commented 9 months ago

This is a specific case, and I think this behavior is correct. Technically, you can navigate through elements of a slide. And the index of the active element changes when moved.

https://github.com/maxmarinich/react-alice-carousel/assets/9882979/21232196-0244-46d6-b894-c0534168f256

To solve this problem you need special handlers:

import React, { useState, useRef } from 'react';
import AliceCarousel from 'react-alice-carousel';
import "react-alice-carousel/lib/scss/alice-carousel.scss";

const sizes = [300, 320, 270, 335, 290, 315, 380, 305];

const items = sizes.map((width) => (
    <img
        src={'//github.com/maxmarinich/react-alice-carousel/raw/master/src/assets/img/1200x200.jpg'}
        style={{ width }}
        role="presentation"
        alt="carousel item"
    />
));

const checkIsInsideTheSlide = (activeIndex = 0, stageWidth = 0) => {
    const totalWidth = sizes.reduce((acc, curr) => acc + curr, 0);
    const currenPosition = sizes.slice(0, activeIndex).reduce((acc, curr) => acc + curr, 0);

    return totalWidth - currenPosition > stageWidth;
};

const BasicPage = () => {
    const [activeIndex, setActiveIndex] = useState(0);
    const [isPrevDisabled, setIsPrevDisabled] = useState(true);
    const [isNextDisabled, setIsNextDisabled] = useState(true);
    const ref = useRef<AliceCarousel>(null);

    return (
        <section className="p-basic">
            <AliceCarousel
                mouseTracking
                items={items}
                autoWidth
                ref={ref}
                activeIndex={activeIndex}
                onInitialized={(e) => {
                    const stageWidth = ref.current?.state.stageWidth || 0;
                    const isNextButtonDisabled = !checkIsInsideTheSlide(e.item, stageWidth);

                    setIsPrevDisabled(e.isPrevSlideDisabled);
                    setIsNextDisabled(isNextButtonDisabled);
                }}
                onSlideChanged={(e) => {
                    const stageWidth = ref.current?.state.stageWidth || 0;
                    const isNextButtonDisabled = !checkIsInsideTheSlide(e.item, stageWidth);

                    setActiveIndex(e.item);
                    setIsPrevDisabled(e.isPrevSlideDisabled);
                    setIsNextDisabled(isNextButtonDisabled);
                }}
                disableButtonsControls
            />
            <div style={{ textAlign: 'center' }}>
                <button disabled={isPrevDisabled} onClick={() => setActiveIndex(activeIndex - 1)}>
                    prev
                </button>
                <button disabled={isNextDisabled} onClick={() => setActiveIndex(activeIndex + 1)}>
                    next
                </button>
            </div>
        </section>
    );
};

export default BasicPage;

https://github.com/maxmarinich/react-alice-carousel/assets/9882979/c0c2b158-e42a-4d80-ab6a-854407fb0246