Skip to content

Commit

Permalink
carousel fixes (#1035)
Browse files Browse the repository at this point in the history
* conditional preventdefault on touch move

* fix: startIndex works now

* remove logs
  • Loading branch information
thejackshelton authored Jan 2, 2025
1 parent a4a64f9 commit 5fc1ad7
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 33 deletions.
4 changes: 4 additions & 0 deletions packages/kit-headless/src/components/carousel/carousel.css
Original file line number Diff line number Diff line change
Expand Up @@ -98,4 +98,8 @@
[data-qui-carousel-scroller]:hover [data-qui-scroll-start]::before {
scroll-snap-align: unset;
}

[data-initial] [hidden] {
display: none;
}
}
30 changes: 2 additions & 28 deletions packages/kit-headless/src/components/carousel/carousel.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ async function setup(page: Page, exampleName: string) {
};
}

test.describe.configure({ mode: 'serial' });

test.describe('Mouse Behavior', () => {
test(`GIVEN a carousel
WHEN clicking on the next button
Expand Down Expand Up @@ -978,34 +980,6 @@ test.describe('State', () => {
// checking that the slide changed
expect(d.getSlideAt(0)).not.toHaveAttribute('data-active');
});

test(`GIVEN a carousel with direction column and max slide height declared
WHEN the swipe up or down
THEN the attribute should move to the right slide
`, async ({ page }) => {
const { driver: d } = await setup(page, 'vertical-direction');
d;

const visibleSlide = d.getSlideAt(0);

const slideBox = await visibleSlide.boundingBox();

if (slideBox) {
const startX = slideBox.x + slideBox.width / 2;
const startY = slideBox.y + slideBox.height / 2;

// swipe up from the middle of the visible slide
await page.mouse.move(startX, startY);
await page.mouse.down();
await page.mouse.move(startX, -startY, { steps: 10 });

// finish the swiping and move the mouse back
await page.mouse.up();
await page.mouse.move(startX, startY, { steps: 10 });
}
// checking that the slide changed
expect(d.getSlideAt(0)).not.toHaveAttribute('data-active');
});
});

test.describe('Stepper', () => {
Expand Down
45 changes: 40 additions & 5 deletions packages/kit-headless/src/components/carousel/scroller.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
useOnWindow,
Slot,
useSignal,
sync$,
} from '@builder.io/qwik';
import { carouselContextId } from './context';
import { useStyles$ } from '@builder.io/qwik';
Expand Down Expand Up @@ -234,18 +235,51 @@ export const CarouselScroller = component$((props: PropsOf<'div'>) => {
initialLoadSig.value = false;
});

// This only works because we don't need to serialize refs or signals
let touchStartX = 0;
let touchStartY = 0;
let activeCarousel: HTMLElement | null = null;
let carouselOrientation: string | null = null;

const preventTouchStart = sync$((e: TouchEvent) => {
const touch = e.touches[0];
if (!touch) return;

const target = e.target as HTMLElement;
activeCarousel = target.closest('[data-qui-carousel-scroller]');
if (!activeCarousel) return;

carouselOrientation = activeCarousel.getAttribute('data-orientation');
touchStartX = touch.clientX;
touchStartY = touch.clientY;
});

const preventTouchMove = sync$((e: TouchEvent) => {
if (!activeCarousel || !carouselOrientation) return;

const touch = e.touches[0];
if (!touch) return;

const deltaX = Math.abs(touch.clientX - touchStartX);
const deltaY = Math.abs(touch.clientY - touchStartY);

if (carouselOrientation === 'horizontal' && deltaX > deltaY && deltaX > 5) {
e.preventDefault();
} else if (carouselOrientation === 'vertical' && deltaY > deltaX && deltaY > 5) {
e.preventDefault();
}
});

return (
<div
data-qui-carousel-viewport
onMouseDown$={[handleMouseDown, onMouseDown$]}
onTouchStart$={[handleTouchStart, onTouchStart$]}
onTouchMove$={[handleTouchMove, onTouchMove$]}
onTouchStart$={[preventTouchStart, handleTouchStart, onTouchStart$]}
onTouchMove$={[preventTouchMove, handleTouchMove, onTouchMove$]}
onTouchEnd$={[handleDragSnap, onTouchEnd$]}
preventdefault:touchstart
preventdefault:touchmove
onQVisible$={isNewPosOnLoadSig.value ? setInitialSlidePos : undefined}
onWheel$={handleWheel}
preventdefault:wheel
preventdefault:wheel={context.isMouseWheelSig.value}
>
<div
ref={context.scrollerRef}
Expand All @@ -254,6 +288,7 @@ export const CarouselScroller = component$((props: PropsOf<'div'>) => {
data-align={context.alignSig.value}
data-initial-touch={isTouchStartSig.value ? '' : undefined}
data-initial={isNewPosOnLoadSig.value ? '' : undefined}
data-orientation={context.orientationSig.value}
{...rest}
>
<Slot />
Expand Down
1 change: 1 addition & 0 deletions packages/kit-headless/src/components/carousel/slide.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export const CarouselSlide = component$(({ _index, ...props }: CarouselSlideProp
inert={!isVisibleSig.value}
hidden={isInactiveSig.value}
aria-roledescription="slide"
data-orientation={context.orientationSig.value}
role={context.bulletRefsArray.value.length > 0 ? 'tabpanel' : undefined}
data-qui-carousel-slide
data-active={isVisibleSig.value ? '' : undefined}
Expand Down

0 comments on commit 5fc1ad7

Please sign in to comment.