Skip to content

Commit

Permalink
Merge pull request #437 from jwplayer/dbudzins/fix-slider
Browse files Browse the repository at this point in the history
#438 - Fix non-anmiated slider
  • Loading branch information
AntonLantukh authored Jan 24, 2024
2 parents 5efbe61 + 9bed444 commit 22b9507
Showing 1 changed file with 37 additions and 25 deletions.
62 changes: 37 additions & 25 deletions src/components/TileDock/TileDock.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import classNames from 'classnames';
import React, { useCallback, useLayoutEffect, useMemo, useRef, useState } from 'react';
import React, { type ReactNode, useCallback, useLayoutEffect, useMemo, useRef, useState } from 'react';

import styles from './TileDock.module.scss';

Expand All @@ -16,13 +16,13 @@ export type TileDockProps<T> = {
minimalTouchMovement?: number;
showControls?: boolean;
showDots?: boolean;
animated?: boolean;
animationModeOverride?: boolean;
wrapWithEmptyTiles?: boolean;
transitionTime?: string;
renderTile: (item: T, isInView: boolean) => JSX.Element;
renderLeftControl?: (handleClick: () => void) => JSX.Element;
renderRightControl?: (handleClick: () => void) => JSX.Element;
renderPaginationDots?: (index: number, pageIndex: number) => JSX.Element;
renderTile: (item: T, isInView: boolean) => ReactNode;
renderLeftControl?: (handleClick: () => void) => ReactNode;
renderRightControl?: (handleClick: () => void) => ReactNode;
renderPaginationDots?: (index: number, pageIndex: number) => ReactNode;
};

type Tile<T> = {
Expand Down Expand Up @@ -66,7 +66,7 @@ function TileDock<T>({
spacing = 12,
minimalTouchMovement = 30,
showControls = true,
animated = !window.matchMedia('(prefers-reduced-motion)').matches,
animationModeOverride,
transitionTime = '0.6s',
wrapWithEmptyTiles = false,
showDots = false,
Expand All @@ -75,11 +75,13 @@ function TileDock<T>({
renderRightControl,
renderPaginationDots,
}: TileDockProps<T>) {
const [index, setIndex] = useState<number>(0);
const [slideToIndex, setSlideToIndex] = useState<number>(0);
const [transform, setTransform] = useState<number>(-100);
const [doAnimationReset, setDoAnimationReset] = useState<boolean>(false);
const [hasTransition, setHasTransition] = useState(false);
const [index, setIndex] = useState(0);
const [slideToIndex, setSlideToIndex] = useState(0);
const [transform, setTransform] = useState(-100);
// Prevent animation mode from changing after first load
const [isAnimated] = useState(animationModeOverride ?? !window.matchMedia('(prefers-reduced-motion)').matches);
const [isAnimationDone, setIsAnimationDone] = useState(false);
const [isAnimationRunning, setIsAnimationRunning] = useState(false);

const frameRef = useRef<HTMLUListElement>() as React.MutableRefObject<HTMLUListElement>;
const tileWidth: number = 100 / tilesToShow;
Expand All @@ -90,15 +92,16 @@ function TileDock<T>({
return sliceItems<T>(items, isMultiPage, index, tilesToShow, cycleMode);
}, [items, isMultiPage, index, tilesToShow, cycleMode]);

const transitionBasis: string = isMultiPage && animated && hasTransition ? `transform ${transitionTime} ease` : '';
const transitionBasis: string = isMultiPage && isAnimated && isAnimationRunning ? `transform ${transitionTime} ease` : '';

const needControls: boolean = showControls && isMultiPage;
const showLeftControl: boolean = needControls && !(cycleMode === 'stop' && index === 0);
const showRightControl: boolean = needControls && !(cycleMode === 'stop' && index === items.length - tilesToShow);

const slide = useCallback(
(direction: Direction): void => {
if (hasTransition) {
// Debounce slide events based on if the animation is running
if (isAnimationRunning) {
return;
}

Expand All @@ -120,11 +123,17 @@ function TileDock<T>({

setSlideToIndex(nextIndex);
setTransform(-100 + movement);
setHasTransition(true);

if (!animated) setDoAnimationReset(true);
// If this is an animated slider, start the animation 'slide'
if (isAnimated) {
setIsAnimationRunning(true);
}
// If not anmiated, trigger the post animation code right away
else {
setIsAnimationDone(true);
}
},
[animated, cycleMode, index, items.length, tileWidth, tilesToShow, hasTransition],
[isAnimated, cycleMode, index, items.length, tileWidth, tilesToShow, isAnimationRunning],
);

const handleTouchStart = useCallback(
Expand All @@ -134,7 +143,7 @@ function TileDock<T>({
y: event.touches[0].clientY,
};

function handleTouchMove(this: HTMLDocument, event: TouchEvent): void {
function handleTouchMove(this: Document, event: TouchEvent): void {
const newPosition: Position = {
x: event.changedTouches[0].clientX,
y: event.changedTouches[0].clientY,
Expand All @@ -148,7 +157,7 @@ function TileDock<T>({
}
}

function handleTouchEnd(this: HTMLDocument, event: TouchEvent): void {
function handleTouchEnd(this: Document, event: TouchEvent): void {
const newPosition = {
x: event.changedTouches[0].clientX,
y: event.changedTouches[0].clientY,
Expand Down Expand Up @@ -182,8 +191,9 @@ function TileDock<T>({
[minimalTouchMovement, slide],
);

// Run code after the slide animation to set the new index
useLayoutEffect(() => {
const resetAnimation = (): void => {
const postAnimationCleanup = (): void => {
let resetIndex: number = slideToIndex;

resetIndex = resetIndex >= items.length ? slideToIndex - items.length : resetIndex;
Expand All @@ -195,16 +205,18 @@ function TileDock<T>({

setIndex(resetIndex);
setTransform(-100);
setDoAnimationReset(false);
setIsAnimationRunning(false);
setIsAnimationDone(false);
};

if (doAnimationReset) resetAnimation();
}, [doAnimationReset, index, items.length, slideToIndex, tileWidth, tilesToShow, transitionBasis]);
if (isAnimationDone) {
postAnimationCleanup();
}
}, [isAnimationDone, index, items.length, slideToIndex, tileWidth, tilesToShow, transitionBasis]);

const handleTransitionEnd = (event: React.TransitionEvent<HTMLUListElement>) => {
if (event.target === frameRef.current) {
setDoAnimationReset(true);
setHasTransition(false);
setIsAnimationDone(true);
}
};

Expand Down

0 comments on commit 22b9507

Please sign in to comment.