Skip to content

Commit 5fc1ad7

Browse files
carousel fixes (#1035)
* conditional preventdefault on touch move * fix: startIndex works now * remove logs
1 parent a4a64f9 commit 5fc1ad7

File tree

4 files changed

+47
-33
lines changed

4 files changed

+47
-33
lines changed

packages/kit-headless/src/components/carousel/carousel.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,4 +98,8 @@
9898
[data-qui-carousel-scroller]:hover [data-qui-scroll-start]::before {
9999
scroll-snap-align: unset;
100100
}
101+
102+
[data-initial] [hidden] {
103+
display: none;
104+
}
101105
}

packages/kit-headless/src/components/carousel/carousel.test.ts

Lines changed: 2 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ async function setup(page: Page, exampleName: string) {
1212
};
1313
}
1414

15+
test.describe.configure({ mode: 'serial' });
16+
1517
test.describe('Mouse Behavior', () => {
1618
test(`GIVEN a carousel
1719
WHEN clicking on the next button
@@ -978,34 +980,6 @@ test.describe('State', () => {
978980
// checking that the slide changed
979981
expect(d.getSlideAt(0)).not.toHaveAttribute('data-active');
980982
});
981-
982-
test(`GIVEN a carousel with direction column and max slide height declared
983-
WHEN the swipe up or down
984-
THEN the attribute should move to the right slide
985-
`, async ({ page }) => {
986-
const { driver: d } = await setup(page, 'vertical-direction');
987-
d;
988-
989-
const visibleSlide = d.getSlideAt(0);
990-
991-
const slideBox = await visibleSlide.boundingBox();
992-
993-
if (slideBox) {
994-
const startX = slideBox.x + slideBox.width / 2;
995-
const startY = slideBox.y + slideBox.height / 2;
996-
997-
// swipe up from the middle of the visible slide
998-
await page.mouse.move(startX, startY);
999-
await page.mouse.down();
1000-
await page.mouse.move(startX, -startY, { steps: 10 });
1001-
1002-
// finish the swiping and move the mouse back
1003-
await page.mouse.up();
1004-
await page.mouse.move(startX, startY, { steps: 10 });
1005-
}
1006-
// checking that the slide changed
1007-
expect(d.getSlideAt(0)).not.toHaveAttribute('data-active');
1008-
});
1009983
});
1010984

1011985
test.describe('Stepper', () => {

packages/kit-headless/src/components/carousel/scroller.tsx

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
useOnWindow,
88
Slot,
99
useSignal,
10+
sync$,
1011
} from '@builder.io/qwik';
1112
import { carouselContextId } from './context';
1213
import { useStyles$ } from '@builder.io/qwik';
@@ -234,18 +235,51 @@ export const CarouselScroller = component$((props: PropsOf<'div'>) => {
234235
initialLoadSig.value = false;
235236
});
236237

238+
// This only works because we don't need to serialize refs or signals
239+
let touchStartX = 0;
240+
let touchStartY = 0;
241+
let activeCarousel: HTMLElement | null = null;
242+
let carouselOrientation: string | null = null;
243+
244+
const preventTouchStart = sync$((e: TouchEvent) => {
245+
const touch = e.touches[0];
246+
if (!touch) return;
247+
248+
const target = e.target as HTMLElement;
249+
activeCarousel = target.closest('[data-qui-carousel-scroller]');
250+
if (!activeCarousel) return;
251+
252+
carouselOrientation = activeCarousel.getAttribute('data-orientation');
253+
touchStartX = touch.clientX;
254+
touchStartY = touch.clientY;
255+
});
256+
257+
const preventTouchMove = sync$((e: TouchEvent) => {
258+
if (!activeCarousel || !carouselOrientation) return;
259+
260+
const touch = e.touches[0];
261+
if (!touch) return;
262+
263+
const deltaX = Math.abs(touch.clientX - touchStartX);
264+
const deltaY = Math.abs(touch.clientY - touchStartY);
265+
266+
if (carouselOrientation === 'horizontal' && deltaX > deltaY && deltaX > 5) {
267+
e.preventDefault();
268+
} else if (carouselOrientation === 'vertical' && deltaY > deltaX && deltaY > 5) {
269+
e.preventDefault();
270+
}
271+
});
272+
237273
return (
238274
<div
239275
data-qui-carousel-viewport
240276
onMouseDown$={[handleMouseDown, onMouseDown$]}
241-
onTouchStart$={[handleTouchStart, onTouchStart$]}
242-
onTouchMove$={[handleTouchMove, onTouchMove$]}
277+
onTouchStart$={[preventTouchStart, handleTouchStart, onTouchStart$]}
278+
onTouchMove$={[preventTouchMove, handleTouchMove, onTouchMove$]}
243279
onTouchEnd$={[handleDragSnap, onTouchEnd$]}
244-
preventdefault:touchstart
245-
preventdefault:touchmove
246280
onQVisible$={isNewPosOnLoadSig.value ? setInitialSlidePos : undefined}
247281
onWheel$={handleWheel}
248-
preventdefault:wheel
282+
preventdefault:wheel={context.isMouseWheelSig.value}
249283
>
250284
<div
251285
ref={context.scrollerRef}
@@ -254,6 +288,7 @@ export const CarouselScroller = component$((props: PropsOf<'div'>) => {
254288
data-align={context.alignSig.value}
255289
data-initial-touch={isTouchStartSig.value ? '' : undefined}
256290
data-initial={isNewPosOnLoadSig.value ? '' : undefined}
291+
data-orientation={context.orientationSig.value}
257292
{...rest}
258293
>
259294
<Slot />

packages/kit-headless/src/components/carousel/slide.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ export const CarouselSlide = component$(({ _index, ...props }: CarouselSlideProp
5252
inert={!isVisibleSig.value}
5353
hidden={isInactiveSig.value}
5454
aria-roledescription="slide"
55+
data-orientation={context.orientationSig.value}
5556
role={context.bulletRefsArray.value.length > 0 ? 'tabpanel' : undefined}
5657
data-qui-carousel-slide
5758
data-active={isVisibleSig.value ? '' : undefined}

0 commit comments

Comments
 (0)