Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

carousel fixes #1035

Merged
merged 3 commits into from
Jan 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading