fix(segment-view): allow moving the indicator left on scroll without touch

This commit is contained in:
Brandy Carney
2024-09-25 12:00:52 -04:00
parent 1d645c9f3f
commit c7bae079c2
6 changed files with 91 additions and 13 deletions

View File

@ -1613,6 +1613,8 @@ ion-segment-content,prop,disabled,boolean,false,false,false
ion-segment-view,shadow
ion-segment-view,prop,disabled,boolean,false,false,false
ion-segment-view,method,setContent,setContent(id: string, smoothScroll?: boolean) => Promise<void>
ion-segment-view,event,ionSegmentViewScroll,{ scrollDirection: string; scrollDistance: number; },true
ion-segment-view,event,ionSegmentViewScrollEnd,void,true
ion-select,shadow
ion-select,prop,cancelText,string,'Cancel',false,false

View File

@ -4443,6 +4443,7 @@ declare global {
};
interface HTMLIonSegmentViewElementEventMap {
"ionSegmentViewScroll": { scrollDirection: string; scrollDistance: number };
"ionSegmentViewScrollEnd": void;
}
interface HTMLIonSegmentViewElement extends Components.IonSegmentView, HTMLStencilElement {
addEventListener<K extends keyof HTMLIonSegmentViewElementEventMap>(type: K, listener: (this: HTMLIonSegmentViewElement, ev: IonSegmentViewCustomEvent<HTMLIonSegmentViewElementEventMap[K]>) => any, options?: boolean | AddEventListenerOptions): void;
@ -7534,7 +7535,14 @@ declare namespace LocalJSX {
* If `true`, the segment view cannot be interacted with.
*/
"disabled"?: boolean;
/**
* Emitted when the segment view is scrolled.
*/
"onIonSegmentViewScroll"?: (event: IonSegmentViewCustomEvent<{ scrollDirection: string; scrollDistance: number }>) => void;
/**
* Emitted when the segment view scroll has ended.
*/
"onIonSegmentViewScrollEnd"?: (event: IonSegmentViewCustomEvent<void>) => void;
}
interface IonSelect {
/**

View File

@ -10,8 +10,10 @@ import { Component, Element, Event, Host, Listen, Method, Prop, h } from '@stenc
shadow: true,
})
export class SegmentView implements ComponentInterface {
private initialScrollLeft = 0;
private initialScrollLeft?: number;
private previousScrollLeft = 0;
private scrollEndTimeout: ReturnType<typeof setTimeout> | null = null;
private isTouching = false;
@Element() el!: HTMLElement;
@ -20,21 +22,31 @@ export class SegmentView implements ComponentInterface {
*/
@Prop() disabled = false;
/**
* Emitted when the segment view is scrolled.
*/
@Event() ionSegmentViewScroll!: EventEmitter<{ scrollDirection: string; scrollDistance: number }>;
/**
* Emitted when the segment view scroll has ended.
*/
@Event() ionSegmentViewScrollEnd!: EventEmitter<void>;
@Listen('scroll')
handleScroll(ev: Event) {
const { initialScrollLeft, previousScrollLeft } = this;
const { scrollLeft, offsetWidth } = ev.target as HTMLElement;
if (initialScrollLeft === undefined) {
this.initialScrollLeft = scrollLeft;
}
const scrollDirection = scrollLeft > previousScrollLeft ? 'right' : 'left';
this.previousScrollLeft = scrollLeft;
let scrollDistance = scrollLeft;
if (scrollDirection === 'left') {
scrollDistance = initialScrollLeft - scrollLeft;
}
// If the scroll direction is left then we need to calculate where we started and subtract
// the current scrollLeft to get the distance scrolled. Otherwise, we use the scrollLeft.
const scrollDistance = scrollDirection === 'left' ? initialScrollLeft! - scrollLeft : scrollLeft;
// Emit the scroll direction and distance
this.ionSegmentViewScroll.emit({
@ -59,11 +71,54 @@ export class SegmentView implements ComponentInterface {
if (segment) {
segment.value = segmentButton.value;
}
this.resetScrollEndTimeout();
}
/**
* Handle touch start event to know when the user is actively dragging the segment view.
*/
@Listen('touchstart')
handleTouchStart() {
this.initialScrollLeft = this.el.scrollLeft;
handleScrollStart() {
if (this.scrollEndTimeout) {
clearTimeout(this.scrollEndTimeout);
this.scrollEndTimeout = null;
}
this.isTouching = true;
}
/**
* Handle touch end event to know when the user is no longer dragging the segment view.
*/
@Listen('touchend')
handleTouchEnd() {
this.isTouching = false;
}
/**
* Reset the scroll end detection timer. This is called on every scroll event.
*/
private resetScrollEndTimeout() {
if (this.scrollEndTimeout) {
clearTimeout(this.scrollEndTimeout);
this.scrollEndTimeout = null;
}
this.scrollEndTimeout = setTimeout(() => {
this.checkForScrollEnd();
}, 150);
}
/**
* Check if the scroll has ended and the user is not actively touching.
* If both conditions are met, reset the initial scroll position and
* emit the scroll end event.
*/
private checkForScrollEnd() {
if (!this.isTouching) {
this.ionSegmentViewScrollEnd.emit();
this.initialScrollLeft = undefined;
}
}
/**

View File

@ -2043,14 +2043,20 @@ export class IonSegmentView {
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
c.detach();
this.el = r.nativeElement;
proxyOutputs(this, this.el, ['ionSegmentViewScroll']);
proxyOutputs(this, this.el, ['ionSegmentViewScroll', 'ionSegmentViewScrollEnd']);
}
}
export declare interface IonSegmentView extends Components.IonSegmentView {
/**
* Emitted when the segment view is scrolled.
*/
ionSegmentViewScroll: EventEmitter<CustomEvent<{ scrollDirection: string; scrollDistance: number }>>;
/**
* Emitted when the segment view scroll has ended.
*/
ionSegmentViewScrollEnd: EventEmitter<CustomEvent<void>>;
}

View File

@ -1882,14 +1882,20 @@ export class IonSegmentView {
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
c.detach();
this.el = r.nativeElement;
proxyOutputs(this, this.el, ['ionSegmentViewScroll']);
proxyOutputs(this, this.el, ['ionSegmentViewScroll', 'ionSegmentViewScrollEnd']);
}
}
export declare interface IonSegmentView extends Components.IonSegmentView {
/**
* Emitted when the segment view is scrolled.
*/
ionSegmentViewScroll: EventEmitter<CustomEvent<{ scrollDirection: string; scrollDistance: number }>>;
/**
* Emitted when the segment view scroll has ended.
*/
ionSegmentViewScrollEnd: EventEmitter<CustomEvent<void>>;
}

View File

@ -762,7 +762,8 @@ export const IonSegmentContent = /*@__PURE__*/ defineContainer<JSX.IonSegmentCon
export const IonSegmentView = /*@__PURE__*/ defineContainer<JSX.IonSegmentView>('ion-segment-view', defineIonSegmentView, [
'disabled',
'ionSegmentViewScroll'
'ionSegmentViewScroll',
'ionSegmentViewScrollEnd'
]);