From 0a13ab449ae8cc741012ad74a64ff89b907ee929 Mon Sep 17 00:00:00 2001 From: Tanner Reits Date: Fri, 27 Sep 2024 18:27:32 -0400 Subject: [PATCH] fix(segment): clear transform styles on scroll end --- .../components/segment-view/segment-view.tsx | 33 +++++++++---------- core/src/components/segment/segment.tsx | 33 ++++++++++++++++--- 2 files changed, 45 insertions(+), 21 deletions(-) diff --git a/core/src/components/segment-view/segment-view.tsx b/core/src/components/segment-view/segment-view.tsx index 7b268c236c..ed8e792b2c 100644 --- a/core/src/components/segment-view/segment-view.tsx +++ b/core/src/components/segment-view/segment-view.tsx @@ -25,12 +25,20 @@ export class SegmentView implements ComponentInterface { /** * Emitted when the segment view is scrolled. */ - @Event() ionSegmentViewScroll!: EventEmitter<{ scrollDirection: string; scrollDistancePercentage: number }>; + @Event() ionSegmentViewScroll!: EventEmitter<{ + scrollDirection: string; + scrollDistance: number; + scrollDistancePercentage: number; + }>; /** * Emitted when the segment view scroll has ended. */ - @Event() ionSegmentViewScrollEnd!: EventEmitter; + @Event() ionSegmentViewScrollEnd!: EventEmitter<{ activeContentId: string }>; + + @Event() ionSegmentViewScrollStart!: EventEmitter; + + private activeContentId = ''; @Listen('scroll') handleScroll(ev: Event) { @@ -53,6 +61,7 @@ export class SegmentView implements ComponentInterface { // Emit the scroll direction and distance this.ionSegmentViewScroll.emit({ scrollDirection, + scrollDistance, scrollDistancePercentage, }); @@ -67,12 +76,8 @@ export class SegmentView implements ComponentInterface { return; } - const segmentButton = this.getSegmentButtonById(segmentContent.id) as HTMLIonSegmentButtonElement; - const segment = this.getParentSegment(segmentButton); - - if (segment) { - segment.value = segmentButton.value; - } + // Store the active `ion-segment-content` id so we can emit it when the scroll ends + this.activeContentId = segmentContent.id; this.resetScrollEndTimeout(); } @@ -82,6 +87,8 @@ export class SegmentView implements ComponentInterface { */ @Listen('touchstart') handleScrollStart() { + this.ionSegmentViewScrollStart.emit(); + if (this.scrollEndTimeout) { clearTimeout(this.scrollEndTimeout); this.scrollEndTimeout = null; @@ -118,7 +125,7 @@ export class SegmentView implements ComponentInterface { */ private checkForScrollEnd() { if (!this.isTouching) { - this.ionSegmentViewScrollEnd.emit(); + this.ionSegmentViewScrollEnd.emit({ activeContentId: this.activeContentId }); this.initialScrollLeft = undefined; } } @@ -149,14 +156,6 @@ export class SegmentView implements ComponentInterface { return Array.from(this.el.querySelectorAll('ion-segment-content:not([disabled])')); } - private getSegmentButtonById(id: string) { - return document.querySelector(`ion-segment-button[content-id="${id}"]`); - } - - private getParentSegment(button: Element) { - return button.closest('ion-segment'); - } - render() { const { disabled } = this; diff --git a/core/src/components/segment/segment.tsx b/core/src/components/segment/segment.tsx index 2b21b216f1..7cd8699488 100644 --- a/core/src/components/segment/segment.tsx +++ b/core/src/components/segment/segment.tsx @@ -28,6 +28,7 @@ export class Segment implements ComponentInterface { private valueBeforeGesture?: SegmentValue; private segmentViewEl?: HTMLIonSegmentViewElement | null = null; + private scrolledIndicator?: HTMLDivElement | null = null; @Element() el!: HTMLIonSegmentElement; @@ -87,7 +88,11 @@ export class Segment implements ComponentInterface { const current = buttons.find((button) => button.value === value); if (previous && current) { - this.checkButton(previous, current); + if (!this.segmentViewEl) { + this.checkButton(previous, current); + } else { + this.setCheckedClasses(); + } } } @@ -96,7 +101,11 @@ export class Segment implements ComponentInterface { * Used by `ion-segment-button` to determine if the button should be checked. */ this.ionSelect.emit({ value }); - this.scrollActiveButtonIntoView(); + + // The scroll listener should handle scrolling the active button into view as needed + if (!this.segmentViewEl) { + this.scrollActiveButtonIntoView(); + } } /** @@ -357,10 +366,13 @@ export class Segment implements ComponentInterface { const index = buttons.findIndex((button) => button.value === this.value); const current = buttons[index]; const indicatorEl = this.getIndicator(current); + this.scrolledIndicator = indicatorEl; - const { scrollDirection, scrollDistancePercentage } = ev.detail; + const { scrollDirection, scrollDistancePercentage, scrollDistance } = ev.detail; - if (indicatorEl) { + console.log('scroll', scrollDistancePercentage, scrollDistance); + + if (indicatorEl && !isNaN(scrollDistancePercentage)) { indicatorEl.style.transition = 'transform 0.3s ease-out'; const scrollDistance = scrollDistancePercentage * current.getBoundingClientRect().width; @@ -390,6 +402,19 @@ export class Segment implements ComponentInterface { } } + @Listen('ionSegmentViewScrollStart', { target: 'body' }) + onScrollStart() {} + + @Listen('ionSegmentViewScrollEnd', { target: 'body' }) + onScrollEnd(ev: CustomEvent<{ activeContentId: string }>) { + this.value = ev.detail.activeContentId; + + if (this.scrolledIndicator) { + this.scrolledIndicator.style.transition = ''; + this.scrolledIndicator.style.transform = ''; + } + } + /** * Finds the related segment view and sets its current content * based on the selected segment button. This method