diff --git a/packages/kit-headless/src/components/carousel/carousel.css b/packages/kit-headless/src/components/carousel/carousel.css index 08f607c32..0755eb339 100644 --- a/packages/kit-headless/src/components/carousel/carousel.css +++ b/packages/kit-headless/src/components/carousel/carousel.css @@ -98,4 +98,8 @@ [data-qui-carousel-scroller]:hover [data-qui-scroll-start]::before { scroll-snap-align: unset; } + + [data-initial] [hidden] { + display: none; + } } diff --git a/packages/kit-headless/src/components/carousel/carousel.test.ts b/packages/kit-headless/src/components/carousel/carousel.test.ts index 797cf5a4b..b0a6a969c 100644 --- a/packages/kit-headless/src/components/carousel/carousel.test.ts +++ b/packages/kit-headless/src/components/carousel/carousel.test.ts @@ -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 @@ -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', () => { diff --git a/packages/kit-headless/src/components/carousel/scroller.tsx b/packages/kit-headless/src/components/carousel/scroller.tsx index 923042081..c0a9b338c 100644 --- a/packages/kit-headless/src/components/carousel/scroller.tsx +++ b/packages/kit-headless/src/components/carousel/scroller.tsx @@ -7,6 +7,7 @@ import { useOnWindow, Slot, useSignal, + sync$, } from '@builder.io/qwik'; import { carouselContextId } from './context'; import { useStyles$ } from '@builder.io/qwik'; @@ -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 (