Skip to content

Commit

Permalink
fix: prevent slider from getting stuck in non-anmiated mode
Browse files Browse the repository at this point in the history
  • Loading branch information
dbudzins committed Jan 19, 2024
1 parent 5efbe61 commit e827aff
Showing 1 changed file with 33 additions and 23 deletions.
56 changes: 33 additions & 23 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 @@ -19,10 +19,10 @@ export type TileDockProps<T> = {
animated?: 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 @@ -75,11 +75,11 @@ 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);
const [animationDone, setAnimationDone] = useState(false);
const [isAnimationRunning, setIsAnimationRunning] = useState(false);

const frameRef = useRef<HTMLUListElement>() as React.MutableRefObject<HTMLUListElement>;
const tileWidth: number = 100 / tilesToShow;
Expand All @@ -90,15 +90,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 && animated && 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 +121,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 (animated) {
setIsAnimationRunning(true);
}
// If not anmiated, trigger the post animation code right away
else {
setAnimationDone(true);
}
},
[animated, cycleMode, index, items.length, tileWidth, tilesToShow, hasTransition],
[animated, cycleMode, index, items.length, tileWidth, tilesToShow, isAnimationRunning],
);

const handleTouchStart = useCallback(
Expand All @@ -134,7 +141,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 +155,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 +189,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 +203,18 @@ function TileDock<T>({

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

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

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

Expand Down

0 comments on commit e827aff

Please sign in to comment.