-
Notifications
You must be signed in to change notification settings - Fork 2.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Carousel: Fix autoplay and enable callback on autoplay index change #33291
base: master
Are you sure you want to change the base?
Carousel: Fix autoplay and enable callback on autoplay index change #33291
Conversation
🕵 fluentui-web-components-v3 No visual regressions between this PR and main |
🕵 FluentUIV0 No visual regressions between this PR and main |
📊 Bundle size reportUnchanged fixtures
|
f1af038
to
f427173
Compare
Pull request demo site: URL |
packages/react-components/react-carousel/stories/src/Carousel/CarouselActionCards.stories.tsx
Outdated
Show resolved
Hide resolved
packages/react-components/react-carousel/stories/src/Carousel/CarouselEventing.stories.tsx
Outdated
Show resolved
Hide resolved
packages/react-components/react-carousel/library/src/components/useEmblaCarousel.ts
Outdated
Show resolved
Hide resolved
packages/react-components/react-carousel/library/src/components/useEmblaCarousel.ts
Outdated
Show resolved
Hide resolved
packages/react-components/react-carousel/library/src/components/useEmblaCarousel.ts
Outdated
Show resolved
Hide resolved
packages/react-components/react-carousel/library/src/components/useEmblaCarousel.ts
Outdated
Show resolved
Hide resolved
@@ -142,22 +189,27 @@ export function useEmblaCarousel( | |||
} | |||
}); | |||
|
|||
const handleIndexChange = React.useCallback(() => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
type EmblaEventHandler = Parameters<EmblaCarouselType['on']>[1];
// ...
and then we can do:
const handleIndexChange: EmblaEventHandler = useEventCallback((_, eventType) => {
const newIndex = emblaApi.current?.selectedScrollSnap() ?? 0;
const slides = emblaApi.current?.slideNodes();
const actualIndex = emblaApi.current?.internalEngine().slideRegistry[newIndex][0] ?? 0;
// We set the active or first index of group on-screen as the selected tabster index
slides?.forEach((slide, slideIndex) => {
setTabsterDefault(slide, slideIndex === actualIndex);
});
setActiveIndex(newIndex);
if (eventType === 'autoplay:select') {
onAutoplayIndexChange?.(null, { event: null, type: 'autoplay', index: newIndex });
}
});
🐱
packages/react-components/react-carousel/library/etc/react-carousel.api.md
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Everything's looking good on the v-build side!
const enableAutoplay = React.useCallback( | ||
(_autoplay: boolean, temporary?: boolean) => { | ||
if (!temporary) { | ||
autoplayRef.current = _autoplay; | ||
} | ||
|
||
if (_autoplay && autoplayRef.current) { | ||
// Autoplay should only enable in the case where underlying state is true, temporary should not override | ||
emblaApi.current?.plugins().autoplay?.play(); | ||
// Reset after play to ensure timing and any focus/mouse pause state is reset. | ||
resetAutoplay(); | ||
} else if (!_autoplay) { | ||
emblaApi.current?.plugins().autoplay?.stop(); | ||
} | ||
}, | ||
[resetAutoplay], | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const enableAutoplay = React.useCallback( | |
(_autoplay: boolean, temporary?: boolean) => { | |
if (!temporary) { | |
autoplayRef.current = _autoplay; | |
} | |
if (_autoplay && autoplayRef.current) { | |
// Autoplay should only enable in the case where underlying state is true, temporary should not override | |
emblaApi.current?.plugins().autoplay?.play(); | |
// Reset after play to ensure timing and any focus/mouse pause state is reset. | |
resetAutoplay(); | |
} else if (!_autoplay) { | |
emblaApi.current?.plugins().autoplay?.stop(); | |
} | |
}, | |
[resetAutoplay], | |
); | |
const enableAutoplay = React.useCallback( | |
(autoplay: boolean, temporary?: boolean) => { | |
if (!temporary) { | |
autoplayRef.current = autoplay; | |
} | |
if (autoplay && autoplayRef.current) { | |
// Autoplay should only enable in the case where underlying state is true, temporary should not override | |
emblaApi.current?.plugins().autoplay?.play(); | |
// Reset after play to ensure timing and any focus/mouse pause state is reset. | |
resetAutoplay(); | |
} else if (!autoplay) { | |
emblaApi.current?.plugins().autoplay?.stop(); | |
} | |
}, | |
[resetAutoplay], | |
); |
nit: we don't need _
after the latest changes 🐱
const _event = new Event('autoplay'); | ||
onAutoplayIndexChange?.(_event, { event: _event, type: 'autoplay', index: newIndex }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const _event = new Event('autoplay'); | |
onAutoplayIndexChange?.(_event, { event: _event, type: 'autoplay', index: newIndex }); | |
// There is no event object for autoplay as there is no a DOM event | |
const noopEvent = new Event('autoplay'); | |
onAutoplayIndexChange?.(noopEvent, { event: noopEvent, type: 'autoplay', index: newIndex }); |
Previous Behavior
No autoplay callback making controlled scenarios not usable with autoplay.
Autoplay would start with 'stopOnInteraction' based on its autoplay value, this caused three issues:
New Behavior
Autoplay callback enables controlled autoplay callbacks
Autoplay will now be attached always, but conditionally enabled by stop/start calls, removing need for a reinitialize of carousel
Autoplay logic can be paused/resumed without overriding play button state for temporary interaction pauses like focus or mouse hover