Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions content/docs/components/carousel.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ To conditionally pause the carousel on mouse hover (desktop), or touch and hold

<Example name="carousel.pauseOnHover" />

## Hide controls

Hides the `leftControl` when the first `<Carousel>` item is active, and hides the `rightControl` when the last `<Carousel>` item is active. This feature can be used in conjunction with the `slide` prop set to `false` to enhance user experience. Default value is `false`.

<Example name="carousel.autoHideControls" />

## Slider content

Instead of images you can also use any type of markup and content inside the carousel such as simple text.
Expand Down
72 changes: 72 additions & 0 deletions examples/carousel/carousel.autoHideControls.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { type CodeData } from '~/components/code-demo';
import { Carousel } from '~/src';

const code = `
'use client';

import { Carousel } from 'flowbite-react';

function Component() {
return (
<div className="h-56 sm:h-64 xl:h-80 2xl:h-96">
<Carousel autoHideControls slide={false}>
<img src="https://flowbite.com/docs/images/carousel/carousel-1.svg" alt="..." />
<img src="https://flowbite.com/docs/images/carousel/carousel-2.svg" alt="..." />
<img src="https://flowbite.com/docs/images/carousel/carousel-3.svg" alt="..." />
<img src="https://flowbite.com/docs/images/carousel/carousel-4.svg" alt="..." />
<img src="https://flowbite.com/docs/images/carousel/carousel-5.svg" alt="..." />
</Carousel>
</div>
);
}
`;

const codeRSC = `
import { Carousel } from 'flowbite-react';

function Component() {
return (
<div className="h-56 sm:h-64 xl:h-80 2xl:h-96">
<Carousel autoHideControls slide={false}>
<img src="https://flowbite.com/docs/images/carousel/carousel-1.svg" alt="..." />
<img src="https://flowbite.com/docs/images/carousel/carousel-2.svg" alt="..." />
<img src="https://flowbite.com/docs/images/carousel/carousel-3.svg" alt="..." />
<img src="https://flowbite.com/docs/images/carousel/carousel-4.svg" alt="..." />
<img src="https://flowbite.com/docs/images/carousel/carousel-5.svg" alt="..." />
</Carousel>
</div>
);
}
`;

function Component() {
return (
<div className="h-56 sm:h-64 xl:h-80 2xl:h-96">
<Carousel autoHideControls slide={false}>
<img src="https://flowbite.com/docs/images/carousel/carousel-1.svg" alt="..." />
<img src="https://flowbite.com/docs/images/carousel/carousel-2.svg" alt="..." />
<img src="https://flowbite.com/docs/images/carousel/carousel-3.svg" alt="..." />
<img src="https://flowbite.com/docs/images/carousel/carousel-4.svg" alt="..." />
<img src="https://flowbite.com/docs/images/carousel/carousel-5.svg" alt="..." />
</Carousel>
</div>
);
}

export const autoHideControls: CodeData = {
type: 'single',
code: [
{
fileName: 'client',
language: 'tsx',
code,
},
{
fileName: 'server',
language: 'tsx',
code: codeRSC,
},
],
githubSlug: 'carousel/carousel.autoHideControls.tsx',
component: <Component />,
};
1 change: 1 addition & 0 deletions examples/carousel/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ export { root } from './carousel.root';
export { slide } from './carousel.slide';
export { slideInterval } from './carousel.slideInterval';
export { sliderContent } from './carousel.sliderContent';
export { autoHideControls } from './carousel.autoHideControls';
16 changes: 14 additions & 2 deletions src/components/Carousel/Carousel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export interface CarouselProps extends ComponentProps<'div'> {
theme?: DeepPartial<FlowbiteCarouselTheme>;
onSlideChange?: (slide: number) => void;
pauseOnHover?: boolean;
autoHideControls?: boolean;
}

export interface DefaultLeftRightControlProps extends ComponentProps<'div'> {
Expand All @@ -74,6 +75,7 @@ export const Carousel: FC<CarouselProps> = ({
theme: customTheme = {},
onSlideChange = null,
pauseOnHover = false,
autoHideControls = false,
...props
}) => {
const theme = mergeDeep(getTheme().carousel, customTheme);
Expand Down Expand Up @@ -181,7 +183,11 @@ export const Carousel: FC<CarouselProps> = ({

{items && (
<>
<div className={theme.root.leftControl}>
<div
className={`${theme.root.leftControl} transition-opacity duration-300 ${
activeItem === 0 && autoHideControls ? 'pointer-events-none hidden opacity-0' : 'block opacity-100'
}`}
>
<button
className="group"
data-testid="carousel-left-control"
Expand All @@ -192,7 +198,13 @@ export const Carousel: FC<CarouselProps> = ({
{leftControl ? leftControl : <DefaultLeftControl theme={customTheme} />}
</button>
</div>
<div className={theme.root.rightControl}>
<div
className={`${theme.root.rightControl} transition-opacity duration-300 ${
activeItem === items.length - 1 && autoHideControls
? 'pointer-events-none hidden opacity-0'
: 'block opacity-100'
}`}
>
Copy link
Contributor

@wheresdiasd wheresdiasd Jan 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your contribution.

Would opacity be the best approach in this case? W/opacity the controls will remain available to user interactions on the dom, while display:none would prevent interactions.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, yes, that's right.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello. If with controls remaining available you mean that the user will go from last image to first image even with control hided, in my tests when control hides click has no effect. In this video you will see a circle in the pointer when I click:

Grabacion.de.pantalla.2024-01-01.a.la.s.1.30.21.p.m.mov

Please correct me if I misunderstood.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would still change this to display:none because this may come up as still clickable for users navigating with Tab.

I can't confirm that since you didn't add an example to the docs, which, if you could, would be great.

With that said, I'm still not sure if we can merge this because this is a UX feature that isn't available in the vanilla library at all.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right! Thanks for your feedback I will implement this in my forked repo. Please let me know if you'd like this feature here so I can update the PR.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keep in mind that if the navigation button/icon disappears from the DOM, when the user spam clicks next (or prev) until he gets to the last, if in the future we'll implement click-to-fullscreen (or similar), then this behaviour would be very unpredictable.

So the case study is, the next/prev icons are wrapped in a button element in order to be focusable/accessible and the button becomes disabled=true when is last or first page, according to the direction, this way tabbing is not possible (CC @tulup-conner ) and also clicking the disabled button should stop event propagation.

#just_thoughts 🤔

<button
className="group"
data-testid="carousel-right-control"
Expand Down