From 8db6580987194291e947f14cf96a0d59dbd1b68e Mon Sep 17 00:00:00 2001 From: Cory LaViska Date: Tue, 5 Nov 2024 11:43:32 -0500 Subject: [PATCH] prevent clicks while dragging; fixes #2196 (#2259) --- docs/pages/resources/changelog.md | 1 + src/components/carousel/carousel.component.ts | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/docs/pages/resources/changelog.md b/docs/pages/resources/changelog.md index b50f906ed..a0d39a5da 100644 --- a/docs/pages/resources/changelog.md +++ b/docs/pages/resources/changelog.md @@ -18,6 +18,7 @@ New versions of Shoelace are released as-needed and generally occur when a criti - Fixed a bug in `` that caused the navigation icons to be reversed - Fixed a bug in `` that prevented label changes in `` from updating the controller [#1971] - Fixed a bug in `` that caused a console warning in Firefox when typing [#2107] +- Fixed a bug in `` that caused interactive elements to be activated when dragging [#2196] - Improved performance of `` by skipping positioning logic when tooltip isn't shown [#2064] ## 2.18.0 diff --git a/src/components/carousel/carousel.component.ts b/src/components/carousel/carousel.component.ts index 40234ae30..c9883fa34 100644 --- a/src/components/carousel/carousel.component.ts +++ b/src/components/carousel/carousel.component.ts @@ -92,6 +92,7 @@ export default class SlCarousel extends ShoelaceElement { @state() dragging = false; private autoplayController = new AutoplayController(this, () => this.next()); + private dragStartPosition: [number, number] = [-1, -1]; private readonly localize = new LocalizeController(this); private mutationObserver: MutationObserver; private pendingSlideChange = false; @@ -151,6 +152,20 @@ export default class SlCarousel extends ShoelaceElement { ) as SlCarouselItem[]; } + private handleClick(event: MouseEvent) { + if (this.dragging && this.dragStartPosition[0] > 0 && this.dragStartPosition[1] > 0) { + const deltaX = Math.abs(this.dragStartPosition[0] - event.clientX); + const deltaY = Math.abs(this.dragStartPosition[1] - event.clientY); + const delta = Math.sqrt(deltaX * deltaX + deltaY * deltaY); + + // Prevents clicks on interactive elements while dragging if the click is within a small range. This prevents + // accidental drags from interfering with intentional clicks. + if (delta >= 10) { + event.preventDefault(); + } + } + } + private handleKeyDown(event: KeyboardEvent) { if (['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'Home', 'End'].includes(event.key)) { const target = event.target as HTMLElement; @@ -208,6 +223,7 @@ export default class SlCarousel extends ShoelaceElement { // Start dragging if it hasn't yet this.scrollContainer.style.setProperty('scroll-snap-type', 'none'); this.dragging = true; + this.dragStartPosition = [event.clientX, event.clientY]; } this.scrollContainer.scrollBy({ @@ -255,6 +271,7 @@ export default class SlCarousel extends ShoelaceElement { scrollContainer.style.removeProperty('scroll-snap-type'); this.dragging = false; + this.dragStartPosition = [-1, -1]; this.handleScrollEnd(); }); }; @@ -533,6 +550,7 @@ export default class SlCarousel extends ShoelaceElement { @mousedown="${this.handleMouseDragStart}" @scroll="${this.handleScroll}" @scrollend=${this.handleScrollEnd} + @click=${this.handleClick} >