From bc2fb371f649b5a1c9a755fc1b573bf752752e69 Mon Sep 17 00:00:00 2001 From: Brandy Carney Date: Tue, 17 Sep 2024 13:15:54 -0400 Subject: [PATCH 01/46] fix(segment): animate the highlight with value changes --- core/src/components/segment/segment.tsx | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/core/src/components/segment/segment.tsx b/core/src/components/segment/segment.tsx index 3ddf8645896..e172d308447 100644 --- a/core/src/components/segment/segment.tsx +++ b/core/src/components/segment/segment.tsx @@ -78,8 +78,18 @@ export class Segment implements ComponentInterface { @Prop({ mutable: true }) value?: SegmentValue; @Watch('value') - protected valueChanged(value: SegmentValue | undefined) { - /** + protected valueChanged(value: SegmentValue | undefined, oldValue?: SegmentValue | undefined) { + if (oldValue !== undefined && value !== undefined) { + const buttons = this.getButtons(); + const previous = buttons.find(button => button.value === oldValue); + const current = buttons.find(button => button.value === value); + + if (previous && current) { + this.checkButton(previous, current); + } + } + + /** * `ionSelect` is emitted every time the value changes (internal or external changes). * Used by `ion-segment-button` to determine if the button should be checked. */ @@ -208,7 +218,7 @@ export class Segment implements ComponentInterface { this.ionChange.emit({ value }); } - private getButtons() { + private getButtons(): HTMLIonSegmentButtonElement[] { return Array.from(this.el.querySelectorAll('ion-segment-button')); } From 6833092ff155c5ae8c9710d37203a0e4cb7b072c Mon Sep 17 00:00:00 2001 From: Brandy Carney Date: Tue, 17 Sep 2024 13:51:51 -0400 Subject: [PATCH 02/46] feat(segment): add segment view and content components --- core/api.txt | 4 + core/src/components.d.ts | 26 ++++++ .../segment-content/segment-content.scss | 10 +++ .../segment-content/segment-content.tsx | 17 ++++ .../components/segment-view/segment-view.scss | 20 +++++ .../components/segment-view/segment-view.tsx | 90 +++++++++++++++++++ .../segment-view/test/basic/index.html | 55 ++++++++++++ .../angular/src/directives/proxies-list.ts | 2 + packages/angular/src/directives/proxies.ts | 42 +++++++++ .../standalone/src/directives/proxies.ts | 48 ++++++++++ packages/react/src/components/proxies.ts | 4 + packages/vue/src/proxies.ts | 8 ++ 12 files changed, 326 insertions(+) create mode 100644 core/src/components/segment-content/segment-content.scss create mode 100644 core/src/components/segment-content/segment-content.tsx create mode 100644 core/src/components/segment-view/segment-view.scss create mode 100644 core/src/components/segment-view/segment-view.tsx create mode 100644 core/src/components/segment-view/test/basic/index.html diff --git a/core/api.txt b/core/api.txt index de7c3531974..377addefd4f 100644 --- a/core/api.txt +++ b/core/api.txt @@ -1607,6 +1607,10 @@ ion-segment-button,part,indicator ion-segment-button,part,indicator-background ion-segment-button,part,native +ion-segment-content,shadow + +ion-segment-view,shadow + ion-select,shadow ion-select,prop,cancelText,string,'Cancel',false,false ion-select,prop,color,"danger" | "dark" | "light" | "medium" | "primary" | "secondary" | "success" | "tertiary" | "warning" | string & Record | undefined,undefined,false,true diff --git a/core/src/components.d.ts b/core/src/components.d.ts index f0cd3d081ad..a05e592af69 100644 --- a/core/src/components.d.ts +++ b/core/src/components.d.ts @@ -2715,6 +2715,10 @@ export namespace Components { */ "value": SegmentValue; } + interface IonSegmentContent { + } + interface IonSegmentView { + } interface IonSelect { /** * The text to display on the cancel button. @@ -4412,6 +4416,18 @@ declare global { prototype: HTMLIonSegmentButtonElement; new (): HTMLIonSegmentButtonElement; }; + interface HTMLIonSegmentContentElement extends Components.IonSegmentContent, HTMLStencilElement { + } + var HTMLIonSegmentContentElement: { + prototype: HTMLIonSegmentContentElement; + new (): HTMLIonSegmentContentElement; + }; + interface HTMLIonSegmentViewElement extends Components.IonSegmentView, HTMLStencilElement { + } + var HTMLIonSegmentViewElement: { + prototype: HTMLIonSegmentViewElement; + new (): HTMLIonSegmentViewElement; + }; interface HTMLIonSelectElementEventMap { "ionChange": SelectChangeEventDetail; "ionCancel": void; @@ -4721,6 +4737,8 @@ declare global { "ion-searchbar": HTMLIonSearchbarElement; "ion-segment": HTMLIonSegmentElement; "ion-segment-button": HTMLIonSegmentButtonElement; + "ion-segment-content": HTMLIonSegmentContentElement; + "ion-segment-view": HTMLIonSegmentViewElement; "ion-select": HTMLIonSelectElement; "ion-select-option": HTMLIonSelectOptionElement; "ion-select-popover": HTMLIonSelectPopoverElement; @@ -7471,6 +7489,10 @@ declare namespace LocalJSX { */ "value"?: SegmentValue; } + interface IonSegmentContent { + } + interface IonSegmentView { + } interface IonSelect { /** * The text to display on the cancel button. @@ -8162,6 +8184,8 @@ declare namespace LocalJSX { "ion-searchbar": IonSearchbar; "ion-segment": IonSegment; "ion-segment-button": IonSegmentButton; + "ion-segment-content": IonSegmentContent; + "ion-segment-view": IonSegmentView; "ion-select": IonSelect; "ion-select-option": IonSelectOption; "ion-select-popover": IonSelectPopover; @@ -8261,6 +8285,8 @@ declare module "@stencil/core" { "ion-searchbar": LocalJSX.IonSearchbar & JSXBase.HTMLAttributes; "ion-segment": LocalJSX.IonSegment & JSXBase.HTMLAttributes; "ion-segment-button": LocalJSX.IonSegmentButton & JSXBase.HTMLAttributes; + "ion-segment-content": LocalJSX.IonSegmentContent & JSXBase.HTMLAttributes; + "ion-segment-view": LocalJSX.IonSegmentView & JSXBase.HTMLAttributes; "ion-select": LocalJSX.IonSelect & JSXBase.HTMLAttributes; "ion-select-option": LocalJSX.IonSelectOption & JSXBase.HTMLAttributes; "ion-select-popover": LocalJSX.IonSelectPopover & JSXBase.HTMLAttributes; diff --git a/core/src/components/segment-content/segment-content.scss b/core/src/components/segment-content/segment-content.scss new file mode 100644 index 00000000000..00ca64f3041 --- /dev/null +++ b/core/src/components/segment-content/segment-content.scss @@ -0,0 +1,10 @@ +// Segment Content +// -------------------------------------------------- + +:host { + scroll-snap-align: center; + + flex-shrink: 0; + + width: 100%; +} diff --git a/core/src/components/segment-content/segment-content.tsx b/core/src/components/segment-content/segment-content.tsx new file mode 100644 index 00000000000..2b5fefebff2 --- /dev/null +++ b/core/src/components/segment-content/segment-content.tsx @@ -0,0 +1,17 @@ +import type { ComponentInterface } from '@stencil/core'; +import { Component, Host, h } from '@stencil/core'; + +@Component({ + tag: 'ion-segment-content', + styleUrl: 'segment-content.scss', + shadow: true, +}) +export class SegmentContent implements ComponentInterface { + render() { + return ( + + + + ); + } +} diff --git a/core/src/components/segment-view/segment-view.scss b/core/src/components/segment-view/segment-view.scss new file mode 100644 index 00000000000..eef3aa34c67 --- /dev/null +++ b/core/src/components/segment-view/segment-view.scss @@ -0,0 +1,20 @@ +// Segment View +// -------------------------------------------------- + +:host { + display: flex; + + overflow-x: scroll; + scroll-snap-type: x mandatory; + + /* Hide scrollbar in Firefox */ + scrollbar-width: none; + + /* Hide scrollbar in IE and Edge */ + -ms-overflow-style: none; +} + +/* Hide scrollbar in webkit */ +:host::-webkit-scrollbar { + display: none; +} diff --git a/core/src/components/segment-view/segment-view.tsx b/core/src/components/segment-view/segment-view.tsx new file mode 100644 index 00000000000..322efbc894f --- /dev/null +++ b/core/src/components/segment-view/segment-view.tsx @@ -0,0 +1,90 @@ +import type { ComponentInterface } from '@stencil/core'; +import { Component, Element, Host, Listen, h } from '@stencil/core'; +import { addEventListener, removeEventListener } from '@utils/helpers'; + +@Component({ + tag: 'ion-segment-view', + styleUrl: 'segment-view.scss', + shadow: true, +}) +export class SegmentView implements ComponentInterface { + private segmentEl: HTMLIonSegmentElement | null = null; + + @Element() el!: HTMLElement; + + @Listen('scroll') + segmentViewScroll(ev: any) { + const { segmentEl } = this; + + const atSnappingPoint = ev.target.scrollLeft % ev.target.offsetWidth === 0; + + if (atSnappingPoint) { + const index = Math.round(ev.target.scrollLeft / ev.target.offsetWidth); + const segmentButton = this.getSegmentButtonAtIndex(index); + + if (segmentEl) { + segmentEl.value = segmentButton.value; + } + } + } + + connectedCallback() { + const segmentEl = (this.segmentEl = document.querySelector(`ion-segment[view=${this.el.id}]`)); + if (segmentEl) { + addEventListener(segmentEl, 'ionChange', this.updateSection); + } + } + + disconnectedCallback() { + const segmentEl = this.segmentEl; + if (segmentEl) { + removeEventListener(segmentEl, 'ionChange', this.updateSection); + this.segmentEl = null; + } + } + + private updateSection = () => { + const { segmentEl } = this; + + if (segmentEl) { + const value = segmentEl.value; + const index = this.getSegmentButtonIndexWithValue(value); + this.setSection(index); + } + }; + + private setSection = (index: number) => { + const sectionWidth = this.el.offsetWidth; + this.el.scrollTo({ + top: 0, + left: index * sectionWidth, + behavior: 'smooth', + }); + }; + + private getSegmentButtons(): HTMLIonSegmentButtonElement[] { + const { segmentEl } = this; + + if (!segmentEl) { + return []; + } + + return Array.from(segmentEl.querySelectorAll('ion-segment-button')); + } + + private getSegmentButtonAtIndex(index: number) { + return this.getSegmentButtons()[index]; + } + + private getSegmentButtonIndexWithValue(value: any) { + return this.getSegmentButtons().findIndex((b) => b.value === value); + } + + render() { + return ( + + + + ); + } +} diff --git a/core/src/components/segment-view/test/basic/index.html b/core/src/components/segment-view/test/basic/index.html new file mode 100644 index 00000000000..7ead70ba96d --- /dev/null +++ b/core/src/components/segment-view/test/basic/index.html @@ -0,0 +1,55 @@ + + + + + Segment View - Basic + + + + + + + + + + + + + Segment View - Basic + + + + + + Paid + + + Free + + + Top + + + + + + + + + Paid + + + Free + + + Top + + + + + + + diff --git a/packages/angular/src/directives/proxies-list.ts b/packages/angular/src/directives/proxies-list.ts index 1874d0bfe2d..69247d01427 100644 --- a/packages/angular/src/directives/proxies-list.ts +++ b/packages/angular/src/directives/proxies-list.ts @@ -69,6 +69,8 @@ export const DIRECTIVES = [ d.IonSearchbar, d.IonSegment, d.IonSegmentButton, + d.IonSegmentContent, + d.IonSegmentView, d.IonSelect, d.IonSelectOption, d.IonSkeletonText, diff --git a/packages/angular/src/directives/proxies.ts b/packages/angular/src/directives/proxies.ts index 89558cb46a1..58535c1b3e8 100644 --- a/packages/angular/src/directives/proxies.ts +++ b/packages/angular/src/directives/proxies.ts @@ -2008,6 +2008,48 @@ export class IonSegmentButton { export declare interface IonSegmentButton extends Components.IonSegmentButton {} +@ProxyCmp({ +}) +@Component({ + selector: 'ion-segment-content', + changeDetection: ChangeDetectionStrategy.OnPush, + template: '', + // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property + inputs: [], +}) +export class IonSegmentContent { + protected el: HTMLElement; + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { + c.detach(); + this.el = r.nativeElement; + } +} + + +export declare interface IonSegmentContent extends Components.IonSegmentContent {} + + +@ProxyCmp({ +}) +@Component({ + selector: 'ion-segment-view', + changeDetection: ChangeDetectionStrategy.OnPush, + template: '', + // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property + inputs: [], +}) +export class IonSegmentView { + protected el: HTMLElement; + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { + c.detach(); + this.el = r.nativeElement; + } +} + + +export declare interface IonSegmentView extends Components.IonSegmentView {} + + @ProxyCmp({ inputs: ['cancelText', 'color', 'compareWith', 'disabled', 'expandedIcon', 'fill', 'interface', 'interfaceOptions', 'justify', 'label', 'labelPlacement', 'mode', 'multiple', 'name', 'okText', 'placeholder', 'selectedText', 'shape', 'toggleIcon', 'value'], methods: ['open'] diff --git a/packages/angular/standalone/src/directives/proxies.ts b/packages/angular/standalone/src/directives/proxies.ts index b0426e56968..4df267da8f7 100644 --- a/packages/angular/standalone/src/directives/proxies.ts +++ b/packages/angular/standalone/src/directives/proxies.ts @@ -65,6 +65,8 @@ import { defineCustomElement as defineIonReorderGroup } from '@ionic/core/compon import { defineCustomElement as defineIonRippleEffect } from '@ionic/core/components/ion-ripple-effect.js'; import { defineCustomElement as defineIonRow } from '@ionic/core/components/ion-row.js'; import { defineCustomElement as defineIonSegmentButton } from '@ionic/core/components/ion-segment-button.js'; +import { defineCustomElement as defineIonSegmentContent } from '@ionic/core/components/ion-segment-content.js'; +import { defineCustomElement as defineIonSegmentView } from '@ionic/core/components/ion-segment-view.js'; import { defineCustomElement as defineIonSelectOption } from '@ionic/core/components/ion-select-option.js'; import { defineCustomElement as defineIonSkeletonText } from '@ionic/core/components/ion-skeleton-text.js'; import { defineCustomElement as defineIonSpinner } from '@ionic/core/components/ion-spinner.js'; @@ -1841,6 +1843,52 @@ export class IonSegmentButton { export declare interface IonSegmentButton extends Components.IonSegmentButton {} +@ProxyCmp({ + defineCustomElementFn: defineIonSegmentContent +}) +@Component({ + selector: 'ion-segment-content', + changeDetection: ChangeDetectionStrategy.OnPush, + template: '', + // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property + inputs: [], + standalone: true +}) +export class IonSegmentContent { + protected el: HTMLElement; + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { + c.detach(); + this.el = r.nativeElement; + } +} + + +export declare interface IonSegmentContent extends Components.IonSegmentContent {} + + +@ProxyCmp({ + defineCustomElementFn: defineIonSegmentView +}) +@Component({ + selector: 'ion-segment-view', + changeDetection: ChangeDetectionStrategy.OnPush, + template: '', + // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property + inputs: [], + standalone: true +}) +export class IonSegmentView { + protected el: HTMLElement; + constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { + c.detach(); + this.el = r.nativeElement; + } +} + + +export declare interface IonSegmentView extends Components.IonSegmentView {} + + @ProxyCmp({ defineCustomElementFn: defineIonSelectOption, inputs: ['disabled', 'value'] diff --git a/packages/react/src/components/proxies.ts b/packages/react/src/components/proxies.ts index 05800f38773..e85e9f0ec18 100644 --- a/packages/react/src/components/proxies.ts +++ b/packages/react/src/components/proxies.ts @@ -61,6 +61,8 @@ import { defineCustomElement as defineIonRow } from '@ionic/core/components/ion- import { defineCustomElement as defineIonSearchbar } from '@ionic/core/components/ion-searchbar.js'; import { defineCustomElement as defineIonSegment } from '@ionic/core/components/ion-segment.js'; import { defineCustomElement as defineIonSegmentButton } from '@ionic/core/components/ion-segment-button.js'; +import { defineCustomElement as defineIonSegmentContent } from '@ionic/core/components/ion-segment-content.js'; +import { defineCustomElement as defineIonSegmentView } from '@ionic/core/components/ion-segment-view.js'; import { defineCustomElement as defineIonSelect } from '@ionic/core/components/ion-select.js'; import { defineCustomElement as defineIonSelectOption } from '@ionic/core/components/ion-select-option.js'; import { defineCustomElement as defineIonSkeletonText } from '@ionic/core/components/ion-skeleton-text.js'; @@ -130,6 +132,8 @@ export const IonRow = /*@__PURE__*/createReactComponent('ion-searchbar', undefined, undefined, defineIonSearchbar); export const IonSegment = /*@__PURE__*/createReactComponent('ion-segment', undefined, undefined, defineIonSegment); export const IonSegmentButton = /*@__PURE__*/createReactComponent('ion-segment-button', undefined, undefined, defineIonSegmentButton); +export const IonSegmentContent = /*@__PURE__*/createReactComponent('ion-segment-content', undefined, undefined, defineIonSegmentContent); +export const IonSegmentView = /*@__PURE__*/createReactComponent('ion-segment-view', undefined, undefined, defineIonSegmentView); export const IonSelect = /*@__PURE__*/createReactComponent('ion-select', undefined, undefined, defineIonSelect); export const IonSelectOption = /*@__PURE__*/createReactComponent('ion-select-option', undefined, undefined, defineIonSelectOption); export const IonSkeletonText = /*@__PURE__*/createReactComponent('ion-skeleton-text', undefined, undefined, defineIonSkeletonText); diff --git a/packages/vue/src/proxies.ts b/packages/vue/src/proxies.ts index 347673a7461..4e2b1c624f7 100644 --- a/packages/vue/src/proxies.ts +++ b/packages/vue/src/proxies.ts @@ -67,6 +67,8 @@ import { defineCustomElement as defineIonRow } from '@ionic/core/components/ion- import { defineCustomElement as defineIonSearchbar } from '@ionic/core/components/ion-searchbar.js'; import { defineCustomElement as defineIonSegment } from '@ionic/core/components/ion-segment.js'; import { defineCustomElement as defineIonSegmentButton } from '@ionic/core/components/ion-segment-button.js'; +import { defineCustomElement as defineIonSegmentContent } from '@ionic/core/components/ion-segment-content.js'; +import { defineCustomElement as defineIonSegmentView } from '@ionic/core/components/ion-segment-view.js'; import { defineCustomElement as defineIonSelect } from '@ionic/core/components/ion-select.js'; import { defineCustomElement as defineIonSelectOption } from '@ionic/core/components/ion-select-option.js'; import { defineCustomElement as defineIonSkeletonText } from '@ionic/core/components/ion-skeleton-text.js'; @@ -752,6 +754,12 @@ export const IonSegmentButton = /*@__PURE__*/ defineContainer('ion-segment-content', defineIonSegmentContent); + + +export const IonSegmentView = /*@__PURE__*/ defineContainer('ion-segment-view', defineIonSegmentView); + + export const IonSelect = /*@__PURE__*/ defineContainer('ion-select', defineIonSelect, [ 'cancelText', 'color', From bf3a503dfb43c5235041fd2d56ab8c10a9313958 Mon Sep 17 00:00:00 2001 From: Brandy Carney Date: Tue, 17 Sep 2024 14:17:45 -0400 Subject: [PATCH 03/46] style: lint --- .../components/segment-view/test/basic/index.html | 13 +++---------- core/src/components/segment/segment.tsx | 6 +++--- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/core/src/components/segment-view/test/basic/index.html b/core/src/components/segment-view/test/basic/index.html index 7ead70ba96d..1b4c46bb175 100644 --- a/core/src/components/segment-view/test/basic/index.html +++ b/core/src/components/segment-view/test/basic/index.html @@ -38,17 +38,10 @@ - - Paid - - - Free - - - Top - + Paid + Free + Top - diff --git a/core/src/components/segment/segment.tsx b/core/src/components/segment/segment.tsx index e172d308447..9a403a0ddbc 100644 --- a/core/src/components/segment/segment.tsx +++ b/core/src/components/segment/segment.tsx @@ -81,15 +81,15 @@ export class Segment implements ComponentInterface { protected valueChanged(value: SegmentValue | undefined, oldValue?: SegmentValue | undefined) { if (oldValue !== undefined && value !== undefined) { const buttons = this.getButtons(); - const previous = buttons.find(button => button.value === oldValue); - const current = buttons.find(button => button.value === value); + const previous = buttons.find((button) => button.value === oldValue); + const current = buttons.find((button) => button.value === value); if (previous && current) { this.checkButton(previous, current); } } - /** + /** * `ionSelect` is emitted every time the value changes (internal or external changes). * Used by `ion-segment-button` to determine if the button should be checked. */ From 2ddfab7c6f23de4347d934cf262f38881cb77774 Mon Sep 17 00:00:00 2001 From: Brandy Carney Date: Wed, 18 Sep 2024 15:32:48 -0400 Subject: [PATCH 04/46] refactor(segment): link the button and content with content-id and id --- core/api.txt | 2 + core/src/components.d.ts | 9 +++ .../segment-button/segment-button.tsx | 5 ++ .../components/segment-view/segment-view.scss | 2 + .../components/segment-view/segment-view.tsx | 78 +++++++------------ .../segment-view/test/basic/index.html | 43 +++++++--- core/src/components/segment/segment.tsx | 19 +++++ packages/angular/src/directives/proxies.ts | 5 +- .../standalone/src/directives/proxies.ts | 7 +- packages/vue/src/proxies.ts | 1 + 10 files changed, 106 insertions(+), 65 deletions(-) diff --git a/core/api.txt b/core/api.txt index 377addefd4f..e4125bf4211 100644 --- a/core/api.txt +++ b/core/api.txt @@ -1542,6 +1542,7 @@ ion-segment,css-prop,--background,ios ion-segment,css-prop,--background,md ion-segment-button,shadow +ion-segment-button,prop,contentId,string | undefined,undefined,false,true ion-segment-button,prop,disabled,boolean,false,false,false ion-segment-button,prop,layout,"icon-bottom" | "icon-end" | "icon-hide" | "icon-start" | "icon-top" | "label-hide" | undefined,'icon-top',false,false ion-segment-button,prop,mode,"ios" | "md",undefined,false,false @@ -1610,6 +1611,7 @@ ion-segment-button,part,native ion-segment-content,shadow ion-segment-view,shadow +ion-segment-view,method,setContent,setContent(id: string) => Promise ion-select,shadow ion-select,prop,cancelText,string,'Cancel',false,false diff --git a/core/src/components.d.ts b/core/src/components.d.ts index a05e592af69..3296458c1b6 100644 --- a/core/src/components.d.ts +++ b/core/src/components.d.ts @@ -2693,6 +2693,10 @@ export namespace Components { "value"?: SegmentValue; } interface IonSegmentButton { + /** + * The `id` of the segment content. + */ + "contentId"?: string; /** * If `true`, the user cannot interact with the segment button. */ @@ -2718,6 +2722,7 @@ export namespace Components { interface IonSegmentContent { } interface IonSegmentView { + "setContent": (id: string) => Promise; } interface IonSelect { /** @@ -7468,6 +7473,10 @@ declare namespace LocalJSX { "value"?: SegmentValue; } interface IonSegmentButton { + /** + * The `id` of the segment content. + */ + "contentId"?: string; /** * If `true`, the user cannot interact with the segment button. */ diff --git a/core/src/components/segment-button/segment-button.tsx b/core/src/components/segment-button/segment-button.tsx index 26954fb6cbd..c1dc1d29148 100644 --- a/core/src/components/segment-button/segment-button.tsx +++ b/core/src/components/segment-button/segment-button.tsx @@ -36,6 +36,11 @@ export class SegmentButton implements ComponentInterface, ButtonInterface { @State() checked = false; + /** + * The `id` of the segment content. + */ + @Prop({ reflect: true }) contentId?: string; + /** * If `true`, the user cannot interact with the segment button. */ diff --git a/core/src/components/segment-view/segment-view.scss b/core/src/components/segment-view/segment-view.scss index eef3aa34c67..b87b6463410 100644 --- a/core/src/components/segment-view/segment-view.scss +++ b/core/src/components/segment-view/segment-view.scss @@ -4,6 +4,8 @@ :host { display: flex; + height: 100%; + overflow-x: scroll; scroll-snap-type: x mandatory; diff --git a/core/src/components/segment-view/segment-view.tsx b/core/src/components/segment-view/segment-view.tsx index 322efbc894f..3646013ca43 100644 --- a/core/src/components/segment-view/segment-view.tsx +++ b/core/src/components/segment-view/segment-view.tsx @@ -1,6 +1,5 @@ import type { ComponentInterface } from '@stencil/core'; -import { Component, Element, Host, Listen, h } from '@stencil/core'; -import { addEventListener, removeEventListener } from '@utils/helpers'; +import { Component, Element, Host, Listen, Method, h } from '@stencil/core'; @Component({ tag: 'ion-segment-view', @@ -8,76 +7,59 @@ import { addEventListener, removeEventListener } from '@utils/helpers'; shadow: true, }) export class SegmentView implements ComponentInterface { - private segmentEl: HTMLIonSegmentElement | null = null; - @Element() el!: HTMLElement; @Listen('scroll') - segmentViewScroll(ev: any) { - const { segmentEl } = this; + handleScroll(ev: any) { + const { scrollLeft, offsetWidth } = ev.target; + const atSnappingPoint = scrollLeft % offsetWidth === 0; - const atSnappingPoint = ev.target.scrollLeft % ev.target.offsetWidth === 0; + if (!atSnappingPoint) return; - if (atSnappingPoint) { - const index = Math.round(ev.target.scrollLeft / ev.target.offsetWidth); - const segmentButton = this.getSegmentButtonAtIndex(index); + const index = Math.round(scrollLeft / offsetWidth); + const segmentContent = this.getSegmentContentAtIndex(index); - if (segmentEl) { - segmentEl.value = segmentButton.value; - } + if (segmentContent === null || segmentContent === undefined) { + return; } - } - connectedCallback() { - const segmentEl = (this.segmentEl = document.querySelector(`ion-segment[view=${this.el.id}]`)); - if (segmentEl) { - addEventListener(segmentEl, 'ionChange', this.updateSection); - } - } + const segmentButton = this.getSegmentButtonById(segmentContent.id) as HTMLIonSegmentButtonElement; + const segment = this.getParentSegment(segmentButton); - disconnectedCallback() { - const segmentEl = this.segmentEl; - if (segmentEl) { - removeEventListener(segmentEl, 'ionChange', this.updateSection); - this.segmentEl = null; + if (segment) { + segment.value = segmentButton.value; } } - private updateSection = () => { - const { segmentEl } = this; + @Method() + async setContent(id: string) { + const contents = this.getSegmentContents(); + const index = contents.findIndex((content) => content.id === id); - if (segmentEl) { - const value = segmentEl.value; - const index = this.getSegmentButtonIndexWithValue(value); - this.setSection(index); - } - }; + if (index === -1) return; - private setSection = (index: number) => { - const sectionWidth = this.el.offsetWidth; + const contentWidth = this.el.offsetWidth; this.el.scrollTo({ top: 0, - left: index * sectionWidth, + left: index * contentWidth, behavior: 'smooth', }); - }; - - private getSegmentButtons(): HTMLIonSegmentButtonElement[] { - const { segmentEl } = this; + } - if (!segmentEl) { - return []; - } + private getSegmentContents(): HTMLIonSegmentContentElement[] { + return Array.from(this.el.querySelectorAll('ion-segment-content')); + } - return Array.from(segmentEl.querySelectorAll('ion-segment-button')); + private getSegmentContentAtIndex(index: number) { + return this.getSegmentContents()[index]; } - private getSegmentButtonAtIndex(index: number) { - return this.getSegmentButtons()[index]; + private getSegmentButtonById(id: string) { + return document.querySelector(`ion-segment-button[content-id="${id}"]`); } - private getSegmentButtonIndexWithValue(value: any) { - return this.getSegmentButtons().findIndex((b) => b.value === value); + private getParentSegment(button: Element) { + return button.closest('ion-segment'); } render() { diff --git a/core/src/components/segment-view/test/basic/index.html b/core/src/components/segment-view/test/basic/index.html index 1b4c46bb175..600cbe654cc 100644 --- a/core/src/components/segment-view/test/basic/index.html +++ b/core/src/components/segment-view/test/basic/index.html @@ -12,37 +12,56 @@ + + - + Segment View - Basic + + - - + + Paid - + Free - + Top - - - - - Paid - Free - Top + + + Free + Top + + + + Footer + + diff --git a/core/src/components/segment/segment.tsx b/core/src/components/segment/segment.tsx index 9a403a0ddbc..1a4d1c3b2f0 100644 --- a/core/src/components/segment/segment.tsx +++ b/core/src/components/segment/segment.tsx @@ -307,6 +307,7 @@ export class Segment implements ComponentInterface { this.value = current.value; this.setCheckedClasses(); + this.updateSegmentView(); } private setCheckedClasses() { @@ -322,6 +323,24 @@ export class Segment implements ComponentInterface { } } + private updateSegmentView() { + const buttons = this.getButtons(); + const button = buttons.find((btn) => btn.value === this.value); + + // If the button does not have a contentId then there is + // no associated segment view to update + if (!button?.contentId) { + return; + } + + const content = document.getElementById(button.contentId); + const segmentView = content?.closest('ion-segment-view'); + + if (segmentView) { + segmentView.setContent(button.contentId); + } + } + private scrollActiveButtonIntoView(smoothScroll = true) { const { scrollable, value, el } = this; diff --git a/packages/angular/src/directives/proxies.ts b/packages/angular/src/directives/proxies.ts index 58535c1b3e8..1f03aea98eb 100644 --- a/packages/angular/src/directives/proxies.ts +++ b/packages/angular/src/directives/proxies.ts @@ -1987,14 +1987,14 @@ This event will not emit when programmatically setting the `value` property. @ProxyCmp({ - inputs: ['disabled', 'layout', 'mode', 'type', 'value'] + inputs: ['contentId', 'disabled', 'layout', 'mode', 'type', 'value'] }) @Component({ selector: 'ion-segment-button', changeDetection: ChangeDetectionStrategy.OnPush, template: '', // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property - inputs: ['disabled', 'layout', 'mode', 'type', 'value'], + inputs: ['contentId', 'disabled', 'layout', 'mode', 'type', 'value'], }) export class IonSegmentButton { protected el: HTMLElement; @@ -2030,6 +2030,7 @@ export declare interface IonSegmentContent extends Components.IonSegmentContent @ProxyCmp({ + methods: ['setContent'] }) @Component({ selector: 'ion-segment-view', diff --git a/packages/angular/standalone/src/directives/proxies.ts b/packages/angular/standalone/src/directives/proxies.ts index 4df267da8f7..aafa5b0e8d5 100644 --- a/packages/angular/standalone/src/directives/proxies.ts +++ b/packages/angular/standalone/src/directives/proxies.ts @@ -1821,14 +1821,14 @@ export declare interface IonRow extends Components.IonRow {} @ProxyCmp({ defineCustomElementFn: defineIonSegmentButton, - inputs: ['disabled', 'layout', 'mode', 'type', 'value'] + inputs: ['contentId', 'disabled', 'layout', 'mode', 'type', 'value'] }) @Component({ selector: 'ion-segment-button', changeDetection: ChangeDetectionStrategy.OnPush, template: '', // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property - inputs: ['disabled', 'layout', 'mode', 'type', 'value'], + inputs: ['contentId', 'disabled', 'layout', 'mode', 'type', 'value'], standalone: true }) export class IonSegmentButton { @@ -1867,7 +1867,8 @@ export declare interface IonSegmentContent extends Components.IonSegmentContent @ProxyCmp({ - defineCustomElementFn: defineIonSegmentView + defineCustomElementFn: defineIonSegmentView, + methods: ['setContent'] }) @Component({ selector: 'ion-segment-view', diff --git a/packages/vue/src/proxies.ts b/packages/vue/src/proxies.ts index 4e2b1c624f7..07979e8c037 100644 --- a/packages/vue/src/proxies.ts +++ b/packages/vue/src/proxies.ts @@ -746,6 +746,7 @@ export const IonSegment = /*@__PURE__*/ defineContainer('ion-segment-button', defineIonSegmentButton, [ + 'contentId', 'disabled', 'layout', 'type', From 4e4856283a5bac65d17e5d3d244a29123dd8a9ee Mon Sep 17 00:00:00 2001 From: Brandy Carney Date: Wed, 18 Sep 2024 15:39:19 -0400 Subject: [PATCH 05/46] refactor(segment): remove uneccessary function --- core/src/components/segment-view/segment-view.tsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/core/src/components/segment-view/segment-view.tsx b/core/src/components/segment-view/segment-view.tsx index 3646013ca43..b63b2348d49 100644 --- a/core/src/components/segment-view/segment-view.tsx +++ b/core/src/components/segment-view/segment-view.tsx @@ -17,7 +17,7 @@ export class SegmentView implements ComponentInterface { if (!atSnappingPoint) return; const index = Math.round(scrollLeft / offsetWidth); - const segmentContent = this.getSegmentContentAtIndex(index); + const segmentContent = this.getSegmentContents()[index]; if (segmentContent === null || segmentContent === undefined) { return; @@ -50,10 +50,6 @@ export class SegmentView implements ComponentInterface { return Array.from(this.el.querySelectorAll('ion-segment-content')); } - private getSegmentContentAtIndex(index: number) { - return this.getSegmentContents()[index]; - } - private getSegmentButtonById(id: string) { return document.querySelector(`ion-segment-button[content-id="${id}"]`); } From 4e9c3ecb3d4a7ef33dcdd4a181873b8ba7c23d02 Mon Sep 17 00:00:00 2001 From: Brandy Carney Date: Thu, 19 Sep 2024 14:12:34 -0400 Subject: [PATCH 06/46] feat(segment-view): support disabled --- core/src/components.d.ts | 8 ++++++++ .../components/segment-view/segment-view.scss | 9 +++++++++ .../components/segment-view/segment-view.tsx | 15 +++++++++++++-- .../segment-view/test/basic/index.html | 19 +++++++++++++++++++ packages/angular/src/directives/proxies.ts | 3 ++- .../standalone/src/directives/proxies.ts | 3 ++- packages/vue/src/proxies.ts | 4 +++- 7 files changed, 56 insertions(+), 5 deletions(-) diff --git a/core/src/components.d.ts b/core/src/components.d.ts index 3296458c1b6..9f57de6a505 100644 --- a/core/src/components.d.ts +++ b/core/src/components.d.ts @@ -2722,6 +2722,10 @@ export namespace Components { interface IonSegmentContent { } interface IonSegmentView { + /** + * If `true`, the segment view cannot be interacted with. + */ + "disabled": boolean; "setContent": (id: string) => Promise; } interface IonSelect { @@ -7501,6 +7505,10 @@ declare namespace LocalJSX { interface IonSegmentContent { } interface IonSegmentView { + /** + * If `true`, the segment view cannot be interacted with. + */ + "disabled"?: boolean; } interface IonSelect { /** diff --git a/core/src/components/segment-view/segment-view.scss b/core/src/components/segment-view/segment-view.scss index b87b6463410..2e08f279ab4 100644 --- a/core/src/components/segment-view/segment-view.scss +++ b/core/src/components/segment-view/segment-view.scss @@ -20,3 +20,12 @@ :host::-webkit-scrollbar { display: none; } + +:host(.segment-view-disabled) { + scroll-snap-type: none; + + touch-action: none; + overflow-x: hidden; + + opacity: 0.7; +} diff --git a/core/src/components/segment-view/segment-view.tsx b/core/src/components/segment-view/segment-view.tsx index b63b2348d49..ad822640268 100644 --- a/core/src/components/segment-view/segment-view.tsx +++ b/core/src/components/segment-view/segment-view.tsx @@ -1,5 +1,5 @@ import type { ComponentInterface } from '@stencil/core'; -import { Component, Element, Host, Listen, Method, h } from '@stencil/core'; +import { Component, Element, Host, Listen, Method, Prop, h } from '@stencil/core'; @Component({ tag: 'ion-segment-view', @@ -9,6 +9,11 @@ import { Component, Element, Host, Listen, Method, h } from '@stencil/core'; export class SegmentView implements ComponentInterface { @Element() el!: HTMLElement; + /** + * If `true`, the segment view cannot be interacted with. + */ + @Prop() disabled = false; + @Listen('scroll') handleScroll(ev: any) { const { scrollLeft, offsetWidth } = ev.target; @@ -59,8 +64,14 @@ export class SegmentView implements ComponentInterface { } render() { + const { disabled } = this; + return ( - + ); diff --git a/core/src/components/segment-view/test/basic/index.html b/core/src/components/segment-view/test/basic/index.html index 600cbe654cc..e34a5e24d77 100644 --- a/core/src/components/segment-view/test/basic/index.html +++ b/core/src/components/segment-view/test/basic/index.html @@ -55,6 +55,25 @@ Free Top + + + + + Bookmarks + + + Reading List + + + Shared Links + + + + + Bookmarks + Reading List + Shared Links + diff --git a/packages/angular/src/directives/proxies.ts b/packages/angular/src/directives/proxies.ts index 1f03aea98eb..5bb75a21b40 100644 --- a/packages/angular/src/directives/proxies.ts +++ b/packages/angular/src/directives/proxies.ts @@ -2030,6 +2030,7 @@ export declare interface IonSegmentContent extends Components.IonSegmentContent @ProxyCmp({ + inputs: ['disabled'], methods: ['setContent'] }) @Component({ @@ -2037,7 +2038,7 @@ export declare interface IonSegmentContent extends Components.IonSegmentContent changeDetection: ChangeDetectionStrategy.OnPush, template: '', // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property - inputs: [], + inputs: ['disabled'], }) export class IonSegmentView { protected el: HTMLElement; diff --git a/packages/angular/standalone/src/directives/proxies.ts b/packages/angular/standalone/src/directives/proxies.ts index aafa5b0e8d5..4dd5e44ad5f 100644 --- a/packages/angular/standalone/src/directives/proxies.ts +++ b/packages/angular/standalone/src/directives/proxies.ts @@ -1868,6 +1868,7 @@ export declare interface IonSegmentContent extends Components.IonSegmentContent @ProxyCmp({ defineCustomElementFn: defineIonSegmentView, + inputs: ['disabled'], methods: ['setContent'] }) @Component({ @@ -1875,7 +1876,7 @@ export declare interface IonSegmentContent extends Components.IonSegmentContent changeDetection: ChangeDetectionStrategy.OnPush, template: '', // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property - inputs: [], + inputs: ['disabled'], standalone: true }) export class IonSegmentView { diff --git a/packages/vue/src/proxies.ts b/packages/vue/src/proxies.ts index 07979e8c037..a6810993253 100644 --- a/packages/vue/src/proxies.ts +++ b/packages/vue/src/proxies.ts @@ -758,7 +758,9 @@ export const IonSegmentButton = /*@__PURE__*/ defineContainer('ion-segment-content', defineIonSegmentContent); -export const IonSegmentView = /*@__PURE__*/ defineContainer('ion-segment-view', defineIonSegmentView); +export const IonSegmentView = /*@__PURE__*/ defineContainer('ion-segment-view', defineIonSegmentView, [ + 'disabled' +]); export const IonSelect = /*@__PURE__*/ defineContainer('ion-select', defineIonSelect, [ From e5323bd805c30a2a589d5708ee62d26b02d25ecf Mon Sep 17 00:00:00 2001 From: Brandy Carney Date: Mon, 23 Sep 2024 14:29:03 -0400 Subject: [PATCH 07/46] fix(segment): only call updateSegmentView when gesture ends or button is clicked --- core/src/components/segment/segment.tsx | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/core/src/components/segment/segment.tsx b/core/src/components/segment/segment.tsx index 1a4d1c3b2f0..081ebddb447 100644 --- a/core/src/components/segment/segment.tsx +++ b/core/src/components/segment/segment.tsx @@ -202,6 +202,7 @@ export class Segment implements ComponentInterface { if (value !== undefined) { if (this.valueBeforeGesture !== value) { this.emitValueChange(); + this.updateSegmentView(); } } this.valueBeforeGesture = undefined; @@ -234,11 +235,7 @@ export class Segment implements ComponentInterface { const buttons = this.getButtons(); buttons.forEach((button) => { - if (activated) { - button.classList.add('segment-button-activated'); - } else { - button.classList.remove('segment-button-activated'); - } + button.classList.toggle('segment-button-activated', activated); }); this.activated = activated; } @@ -307,7 +304,6 @@ export class Segment implements ComponentInterface { this.value = current.value; this.setCheckedClasses(); - this.updateSegmentView(); } private setCheckedClasses() { @@ -323,6 +319,13 @@ export class Segment implements ComponentInterface { } } + /** + * Finds the related segment view and sets its current content + * based on the selected segment button. This method + * should be called only after the gesture is completed + * (if dragging between segments) or when a segment button + * is clicked directly. + */ private updateSegmentView() { const buttons = this.getButtons(); const button = buttons.find((btn) => btn.value === this.value); @@ -519,6 +522,7 @@ export class Segment implements ComponentInterface { if (current !== previous) { this.emitValueChange(); + this.updateSegmentView(); } if (this.scrollable || !this.swipeGesture) { From 97c486b9a5c6b3ed5123607f8db1493c3020b1dc Mon Sep 17 00:00:00 2001 From: Brandy Carney Date: Mon, 23 Sep 2024 15:40:38 -0400 Subject: [PATCH 08/46] fix(segment): set the view to the initial value without scrolling --- core/api.txt | 3 +- core/src/components.d.ts | 2 +- .../components/segment-view/segment-view.tsx | 4 +- .../segment-view/test/basic/index.html | 62 +++++++++++++++---- core/src/components/segment/segment.tsx | 14 +++-- 5 files changed, 64 insertions(+), 21 deletions(-) diff --git a/core/api.txt b/core/api.txt index e4125bf4211..f3c22d30a47 100644 --- a/core/api.txt +++ b/core/api.txt @@ -1611,7 +1611,8 @@ ion-segment-button,part,native ion-segment-content,shadow ion-segment-view,shadow -ion-segment-view,method,setContent,setContent(id: string) => Promise +ion-segment-view,prop,disabled,boolean,false,false,false +ion-segment-view,method,setContent,setContent(id: string, smoothScroll?: boolean) => Promise ion-select,shadow ion-select,prop,cancelText,string,'Cancel',false,false diff --git a/core/src/components.d.ts b/core/src/components.d.ts index 9f57de6a505..d9d1410660a 100644 --- a/core/src/components.d.ts +++ b/core/src/components.d.ts @@ -2726,7 +2726,7 @@ export namespace Components { * If `true`, the segment view cannot be interacted with. */ "disabled": boolean; - "setContent": (id: string) => Promise; + "setContent": (id: string, smoothScroll?: boolean) => Promise; } interface IonSelect { /** diff --git a/core/src/components/segment-view/segment-view.tsx b/core/src/components/segment-view/segment-view.tsx index ad822640268..81678a33edf 100644 --- a/core/src/components/segment-view/segment-view.tsx +++ b/core/src/components/segment-view/segment-view.tsx @@ -37,7 +37,7 @@ export class SegmentView implements ComponentInterface { } @Method() - async setContent(id: string) { + async setContent(id: string, smoothScroll = true) { const contents = this.getSegmentContents(); const index = contents.findIndex((content) => content.id === id); @@ -47,7 +47,7 @@ export class SegmentView implements ComponentInterface { this.el.scrollTo({ top: 0, left: index * contentWidth, - behavior: 'smooth', + behavior: smoothScroll ? 'smooth' : 'auto', }); } diff --git a/core/src/components/segment-view/test/basic/index.html b/core/src/components/segment-view/test/basic/index.html index e34a5e24d77..093d1d064b3 100644 --- a/core/src/components/segment-view/test/basic/index.html +++ b/core/src/components/segment-view/test/basic/index.html @@ -15,16 +15,26 @@ @@ -38,14 +48,42 @@ - - + + + No + + + Value + + + + + No + Value + + + + + All + + + Favorites + + + + + All + Favorites + + + + Paid - + Free - + Top @@ -57,14 +95,14 @@ - - + + Bookmarks - + Reading List - + Shared Links diff --git a/core/src/components/segment/segment.tsx b/core/src/components/segment/segment.tsx index 081ebddb447..84c42e87429 100644 --- a/core/src/components/segment/segment.tsx +++ b/core/src/components/segment/segment.tsx @@ -180,6 +180,10 @@ export class Segment implements ComponentInterface { if (this.disabled) { this.disabledChanged(); } + + // Update segment view based on the initial value, + // but do not animate the scroll + this.updateSegmentView(false); } onStart(detail: GestureDetail) { @@ -322,11 +326,11 @@ export class Segment implements ComponentInterface { /** * Finds the related segment view and sets its current content * based on the selected segment button. This method - * should be called only after the gesture is completed - * (if dragging between segments) or when a segment button - * is clicked directly. + * should be called on initial load of the segment, + * after the gesture is completed (if dragging between segments) + * and when a segment button is clicked directly. */ - private updateSegmentView() { + private updateSegmentView(smoothScroll = true) { const buttons = this.getButtons(); const button = buttons.find((btn) => btn.value === this.value); @@ -340,7 +344,7 @@ export class Segment implements ComponentInterface { const segmentView = content?.closest('ion-segment-view'); if (segmentView) { - segmentView.setContent(button.contentId); + segmentView.setContent(button.contentId, smoothScroll); } } From d46b3bfd76b5a7010bf4cc76d50e8ce728542e69 Mon Sep 17 00:00:00 2001 From: Brandy Carney Date: Mon, 23 Sep 2024 15:56:33 -0400 Subject: [PATCH 09/46] test(segment-view): add a test for the proper content being displayed --- .../test/basic/segment-view.e2e.ts | 95 +++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 core/src/components/segment-view/test/basic/segment-view.e2e.ts diff --git a/core/src/components/segment-view/test/basic/segment-view.e2e.ts b/core/src/components/segment-view/test/basic/segment-view.e2e.ts new file mode 100644 index 00000000000..4644abc207b --- /dev/null +++ b/core/src/components/segment-view/test/basic/segment-view.e2e.ts @@ -0,0 +1,95 @@ +import { expect } from '@playwright/test'; +import { configs, test } from '@utils/test/playwright'; + +/** + * This behavior does not vary across modes/directions + */ +configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => { + test.describe(title('segment-view: basic'), () => { + test('should show the first content with no initial value', async ({ page }) => { + await page.setContent( + ` + + + Paid + + + Free + + + Top + + + + + + Free + Top + + `, + config + ); + + const segmentContent = page.locator('ion-segment-content[id="paid"]'); + await expect(segmentContent).toBeInViewport(); + }); + + test('should show the content matching the initial value', async ({ page }) => { + await page.setContent( + ` + + + Paid + + + Free + + + Top + + + + + + Free + Top + + `, + config + ); + + const segmentContent = page.locator('ion-segment-content[id="free"]'); + await expect(segmentContent).toBeInViewport(); + }); + + test('should update the content when changing the value by clicking a segment button', async ({ page }) => { + await page.setContent( + ` + + + Paid + + + Free + + + Top + + + + + + Free + Top + + `, + config + ); + + await page.click('ion-segment-button[value="top"]'); + + const segmentContent = page.locator('ion-segment-content[id="top"]'); + await expect(segmentContent).toBeInViewport(); + }); + }); +}); From 370a57a4deebc6595af6caedecfe3b5ca354cc45 Mon Sep 17 00:00:00 2001 From: Brandy Carney Date: Mon, 23 Sep 2024 15:57:52 -0400 Subject: [PATCH 10/46] style: lint --- core/src/components/segment-view/segment-view.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/components/segment-view/segment-view.scss b/core/src/components/segment-view/segment-view.scss index 2e08f279ab4..e6075fd2143 100644 --- a/core/src/components/segment-view/segment-view.scss +++ b/core/src/components/segment-view/segment-view.scss @@ -24,8 +24,8 @@ :host(.segment-view-disabled) { scroll-snap-type: none; + opacity: 0.7; + touch-action: none; overflow-x: hidden; - - opacity: 0.7; } From d60d54b41fb31bfe35e68092ffc4458fdf2731fa Mon Sep 17 00:00:00 2001 From: Brandy Carney Date: Mon, 23 Sep 2024 16:18:37 -0400 Subject: [PATCH 11/46] docs(segment-view): document setContent method and add example --- core/src/components.d.ts | 5 +++ .../components/segment-view/segment-view.tsx | 7 ++++ .../segment-view/test/basic/index.html | 33 +++++++++++++++++-- 3 files changed, 43 insertions(+), 2 deletions(-) diff --git a/core/src/components.d.ts b/core/src/components.d.ts index d9d1410660a..19984e2fb43 100644 --- a/core/src/components.d.ts +++ b/core/src/components.d.ts @@ -2726,6 +2726,11 @@ export namespace Components { * If `true`, the segment view cannot be interacted with. */ "disabled": boolean; + /** + * This method is used to programmatically set the displayed segment content in the segment view. Calling this method will update the `value` of the corresponding segment button. + * @param id : The id of the segment content to display. + * @param smoothScroll : Whether to animate the scroll transition. + */ "setContent": (id: string, smoothScroll?: boolean) => Promise; } interface IonSelect { diff --git a/core/src/components/segment-view/segment-view.tsx b/core/src/components/segment-view/segment-view.tsx index 81678a33edf..f519de30ffc 100644 --- a/core/src/components/segment-view/segment-view.tsx +++ b/core/src/components/segment-view/segment-view.tsx @@ -36,6 +36,13 @@ export class SegmentView implements ComponentInterface { } } + /** + * This method is used to programmatically set the displayed segment content + * in the segment view. Calling this method will update the `value` of the + * corresponding segment button. + * @param id: The id of the segment content to display. + * @param smoothScroll: Whether to animate the scroll transition. + */ @Method() async setContent(id: string, smoothScroll = true) { const contents = this.getSegmentContents(); diff --git a/core/src/components/segment-view/test/basic/index.html b/core/src/components/segment-view/test/basic/index.html index 093d1d064b3..922e865901f 100644 --- a/core/src/components/segment-view/test/basic/index.html +++ b/core/src/components/segment-view/test/basic/index.html @@ -48,7 +48,7 @@ - + No @@ -57,7 +57,7 @@ - + No Value @@ -76,6 +76,7 @@ Favorites + Paid @@ -112,6 +113,14 @@ Reading List Shared Links + + + + @@ -119,6 +128,26 @@ Footer + + From 4bb4a8c2de86bcfbdf3705191db93538212db78a Mon Sep 17 00:00:00 2001 From: Brandy Carney Date: Mon, 23 Sep 2024 16:23:37 -0400 Subject: [PATCH 12/46] test(segment-view): fix test --- core/src/components/segment-view/test/basic/index.html | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/src/components/segment-view/test/basic/index.html b/core/src/components/segment-view/test/basic/index.html index 922e865901f..e0128ccee83 100644 --- a/core/src/components/segment-view/test/basic/index.html +++ b/core/src/components/segment-view/test/basic/index.html @@ -130,11 +130,12 @@ From 4b27738e4f66c67245d63d187858035c1b165521 Mon Sep 17 00:00:00 2001 From: Brandy Carney Date: Mon, 23 Sep 2024 18:42:46 -0400 Subject: [PATCH 14/46] feat(segment-content): add disabled prop and hide the content --- core/api.txt | 1 + core/src/components.d.ts | 8 ++++++++ .../segment-content/segment-content.scss | 4 ++++ .../segment-content/segment-content.tsx | 15 +++++++++++++-- packages/angular/src/directives/proxies.ts | 3 ++- .../angular/standalone/src/directives/proxies.ts | 5 +++-- packages/vue/src/proxies.ts | 4 +++- 7 files changed, 34 insertions(+), 6 deletions(-) diff --git a/core/api.txt b/core/api.txt index f3c22d30a47..827fb83ef7a 100644 --- a/core/api.txt +++ b/core/api.txt @@ -1609,6 +1609,7 @@ ion-segment-button,part,indicator-background ion-segment-button,part,native ion-segment-content,shadow +ion-segment-content,prop,disabled,boolean,false,false,false ion-segment-view,shadow ion-segment-view,prop,disabled,boolean,false,false,false diff --git a/core/src/components.d.ts b/core/src/components.d.ts index 19984e2fb43..1fbabdda79f 100644 --- a/core/src/components.d.ts +++ b/core/src/components.d.ts @@ -2720,6 +2720,10 @@ export namespace Components { "value": SegmentValue; } interface IonSegmentContent { + /** + * If `true`, the segment content will not be displayed. + */ + "disabled": boolean; } interface IonSegmentView { /** @@ -7508,6 +7512,10 @@ declare namespace LocalJSX { "value"?: SegmentValue; } interface IonSegmentContent { + /** + * If `true`, the segment content will not be displayed. + */ + "disabled"?: boolean; } interface IonSegmentView { /** diff --git a/core/src/components/segment-content/segment-content.scss b/core/src/components/segment-content/segment-content.scss index 00ca64f3041..6a1818a8b79 100644 --- a/core/src/components/segment-content/segment-content.scss +++ b/core/src/components/segment-content/segment-content.scss @@ -8,3 +8,7 @@ width: 100%; } + +:host(.segment-content-disabled) { + display: none; +} diff --git a/core/src/components/segment-content/segment-content.tsx b/core/src/components/segment-content/segment-content.tsx index 2b5fefebff2..4566e8ff803 100644 --- a/core/src/components/segment-content/segment-content.tsx +++ b/core/src/components/segment-content/segment-content.tsx @@ -1,5 +1,5 @@ import type { ComponentInterface } from '@stencil/core'; -import { Component, Host, h } from '@stencil/core'; +import { Component, Host, Prop, h } from '@stencil/core'; @Component({ tag: 'ion-segment-content', @@ -7,9 +7,20 @@ import { Component, Host, h } from '@stencil/core'; shadow: true, }) export class SegmentContent implements ComponentInterface { + /** + * If `true`, the segment content will not be displayed. + */ + @Prop() disabled = false; + render() { + const { disabled } = this; + return ( - + ); diff --git a/packages/angular/src/directives/proxies.ts b/packages/angular/src/directives/proxies.ts index 5bb75a21b40..2e06b813b67 100644 --- a/packages/angular/src/directives/proxies.ts +++ b/packages/angular/src/directives/proxies.ts @@ -2009,13 +2009,14 @@ export declare interface IonSegmentButton extends Components.IonSegmentButton {} @ProxyCmp({ + inputs: ['disabled'] }) @Component({ selector: 'ion-segment-content', changeDetection: ChangeDetectionStrategy.OnPush, template: '', // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property - inputs: [], + inputs: ['disabled'], }) export class IonSegmentContent { protected el: HTMLElement; diff --git a/packages/angular/standalone/src/directives/proxies.ts b/packages/angular/standalone/src/directives/proxies.ts index 4dd5e44ad5f..0f04a3ba642 100644 --- a/packages/angular/standalone/src/directives/proxies.ts +++ b/packages/angular/standalone/src/directives/proxies.ts @@ -1844,14 +1844,15 @@ export declare interface IonSegmentButton extends Components.IonSegmentButton {} @ProxyCmp({ - defineCustomElementFn: defineIonSegmentContent + defineCustomElementFn: defineIonSegmentContent, + inputs: ['disabled'] }) @Component({ selector: 'ion-segment-content', changeDetection: ChangeDetectionStrategy.OnPush, template: '', // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property - inputs: [], + inputs: ['disabled'], standalone: true }) export class IonSegmentContent { diff --git a/packages/vue/src/proxies.ts b/packages/vue/src/proxies.ts index a6810993253..b433be92fec 100644 --- a/packages/vue/src/proxies.ts +++ b/packages/vue/src/proxies.ts @@ -755,7 +755,9 @@ export const IonSegmentButton = /*@__PURE__*/ defineContainer('ion-segment-content', defineIonSegmentContent); +export const IonSegmentContent = /*@__PURE__*/ defineContainer('ion-segment-content', defineIonSegmentContent, [ + 'disabled' +]); export const IonSegmentView = /*@__PURE__*/ defineContainer('ion-segment-view', defineIonSegmentView, [ From 0ecf886d79d62d64cd392c6cc1eae90b8336c68b Mon Sep 17 00:00:00 2001 From: Brandy Carney Date: Mon, 23 Sep 2024 18:44:14 -0400 Subject: [PATCH 15/46] test(segment-view): split out disabled segment view / content test --- .../segment-view/test/basic/index.html | 27 +----- .../segment-view/test/disabled/index.html | 94 +++++++++++++++++++ 2 files changed, 96 insertions(+), 25 deletions(-) create mode 100644 core/src/components/segment-view/test/disabled/index.html diff --git a/core/src/components/segment-view/test/basic/index.html b/core/src/components/segment-view/test/basic/index.html index c9b28cbc14f..43a2d756531 100644 --- a/core/src/components/segment-view/test/basic/index.html +++ b/core/src/components/segment-view/test/basic/index.html @@ -96,32 +96,9 @@ Top - - - - Bookmarks - - - Reading List - - - Shared Links - - - - - Bookmarks - Reading List - Shared Links - - - + - + diff --git a/core/src/components/segment-view/test/disabled/index.html b/core/src/components/segment-view/test/disabled/index.html new file mode 100644 index 00000000000..59653cf94e1 --- /dev/null +++ b/core/src/components/segment-view/test/disabled/index.html @@ -0,0 +1,94 @@ + + + + + Segment View - Disabled + + + + + + + + + + + + + + + Segment View - Disabled + + + + + + + + Paid + + + Free + + + Top + + + + + + Free + Top + + + + + + Bookmarks + + + Reading List + + + Shared Links + + + + + Bookmarks + Reading List + Shared Links + + + + + From a76477c64900075c80bfd6c533a17d4f65cda04b Mon Sep 17 00:00:00 2001 From: Brandy Carney Date: Mon, 23 Sep 2024 18:50:39 -0400 Subject: [PATCH 16/46] fix(segment-view): split opacity by mode vars to match segment --- core/src/components/segment-view/segment-view.ios.scss | 9 +++++++++ core/src/components/segment-view/segment-view.md.scss | 9 +++++++++ core/src/components/segment-view/segment-view.scss | 4 ---- core/src/components/segment-view/segment-view.tsx | 5 ++++- 4 files changed, 22 insertions(+), 5 deletions(-) create mode 100644 core/src/components/segment-view/segment-view.ios.scss create mode 100644 core/src/components/segment-view/segment-view.md.scss diff --git a/core/src/components/segment-view/segment-view.ios.scss b/core/src/components/segment-view/segment-view.ios.scss new file mode 100644 index 00000000000..a8c51304565 --- /dev/null +++ b/core/src/components/segment-view/segment-view.ios.scss @@ -0,0 +1,9 @@ +@import "./segment-view"; +@import "../segment-button/segment-button.ios.vars"; + +// iOS Segment View +// -------------------------------------------------- + +:host(.segment-view-disabled) { + opacity: $segment-button-ios-opacity-disabled; +} diff --git a/core/src/components/segment-view/segment-view.md.scss b/core/src/components/segment-view/segment-view.md.scss new file mode 100644 index 00000000000..4a141cf5c89 --- /dev/null +++ b/core/src/components/segment-view/segment-view.md.scss @@ -0,0 +1,9 @@ +@import "./segment-view"; +@import "../segment-button/segment-button.md.vars"; + +// Material Design Segment View +// -------------------------------------------------- + +:host(.segment-view-disabled) { + opacity: $segment-button-md-opacity-disabled; +} diff --git a/core/src/components/segment-view/segment-view.scss b/core/src/components/segment-view/segment-view.scss index e6075fd2143..e9eacc5a24f 100644 --- a/core/src/components/segment-view/segment-view.scss +++ b/core/src/components/segment-view/segment-view.scss @@ -22,10 +22,6 @@ } :host(.segment-view-disabled) { - scroll-snap-type: none; - - opacity: 0.7; - touch-action: none; overflow-x: hidden; } diff --git a/core/src/components/segment-view/segment-view.tsx b/core/src/components/segment-view/segment-view.tsx index f519de30ffc..af6cda820c6 100644 --- a/core/src/components/segment-view/segment-view.tsx +++ b/core/src/components/segment-view/segment-view.tsx @@ -3,7 +3,10 @@ import { Component, Element, Host, Listen, Method, Prop, h } from '@stencil/core @Component({ tag: 'ion-segment-view', - styleUrl: 'segment-view.scss', + styleUrls: { + ios: 'segment-view.ios.scss', + md: 'segment-view.md.scss', + }, shadow: true, }) export class SegmentView implements ComponentInterface { From 6da1cdd0a6a96a64e6b7b6210784a4adf37ffef3 Mon Sep 17 00:00:00 2001 From: Brandy Carney Date: Mon, 23 Sep 2024 19:05:00 -0400 Subject: [PATCH 17/46] fix(segment-view): don't query for disabled contents --- core/src/components/segment-view/segment-view.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/components/segment-view/segment-view.tsx b/core/src/components/segment-view/segment-view.tsx index af6cda820c6..4ab4088560b 100644 --- a/core/src/components/segment-view/segment-view.tsx +++ b/core/src/components/segment-view/segment-view.tsx @@ -62,7 +62,7 @@ export class SegmentView implements ComponentInterface { } private getSegmentContents(): HTMLIonSegmentContentElement[] { - return Array.from(this.el.querySelectorAll('ion-segment-content')); + return Array.from(this.el.querySelectorAll('ion-segment-content:not([disabled])')); } private getSegmentButtonById(id: string) { From 8e177978624c083a98f2b00fbdbfcd3b29e40201 Mon Sep 17 00:00:00 2001 From: Brandy Carney Date: Tue, 24 Sep 2024 11:26:49 -0400 Subject: [PATCH 18/46] test(segment-view): remove toolbars --- .../segment-view/test/basic/index.html | 60 ++++++++-------- .../test/basic/segment-view.e2e.ts | 69 +++++++++---------- .../segment-view/test/disabled/index.html | 61 +++++++++------- 3 files changed, 95 insertions(+), 95 deletions(-) diff --git a/core/src/components/segment-view/test/basic/index.html b/core/src/components/segment-view/test/basic/index.html index 43a2d756531..0eefd5a5474 100644 --- a/core/src/components/segment-view/test/basic/index.html +++ b/core/src/components/segment-view/test/basic/index.html @@ -47,49 +47,43 @@ - - - - No - - - Value - - - + + + No + + + Value + + No Value - - - - All - - - Favorites - - - + + + All + + + Favorites + + All Favorites - - - - Paid - - - Free - - - Top - - - + + + Paid + + + Free + + + Top + + Free diff --git a/core/src/components/segment-view/test/basic/segment-view.e2e.ts b/core/src/components/segment-view/test/basic/segment-view.e2e.ts index 4644abc207b..5d7457743ad 100644 --- a/core/src/components/segment-view/test/basic/segment-view.e2e.ts +++ b/core/src/components/segment-view/test/basic/segment-view.e2e.ts @@ -9,18 +9,17 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => { test('should show the first content with no initial value', async ({ page }) => { await page.setContent( ` - - - Paid - - - Free - - - Top - - - + + + Paid + + + Free + + + Top + + Free @@ -37,18 +36,17 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => { test('should show the content matching the initial value', async ({ page }) => { await page.setContent( ` - - - Paid - - - Free - - - Top - - - + + + Paid + + + Free + + + Top + + Free @@ -65,18 +63,17 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => { test('should update the content when changing the value by clicking a segment button', async ({ page }) => { await page.setContent( ` - - - Paid - - - Free - - - Top - - - + + + Paid + + + Free + + + Top + + Free diff --git a/core/src/components/segment-view/test/disabled/index.html b/core/src/components/segment-view/test/disabled/index.html index 59653cf94e1..b2e676d34a1 100644 --- a/core/src/components/segment-view/test/disabled/index.html +++ b/core/src/components/segment-view/test/disabled/index.html @@ -51,38 +51,47 @@ - - - - Paid - - - Free - - - Top - - - + + + All + + + Favorites + + + + All + Favorites + + + + + Paid + + + Free + + + Top + + Free Top - - - - Bookmarks - - - Reading List - - - Shared Links - - - + + + Bookmarks + + + Reading List + + + Shared Links + + Bookmarks Reading List From 1c3f7ab8ea6b5be3de2e324244df32eeed09f082 Mon Sep 17 00:00:00 2001 From: Brandy Carney Date: Tue, 24 Sep 2024 11:27:13 -0400 Subject: [PATCH 19/46] test(segment-view): add tests for disabled content scrolling --- .../test/disabled/segment-view.e2e.ts | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 core/src/components/segment-view/test/disabled/segment-view.e2e.ts diff --git a/core/src/components/segment-view/test/disabled/segment-view.e2e.ts b/core/src/components/segment-view/test/disabled/segment-view.e2e.ts new file mode 100644 index 00000000000..421c5cd316f --- /dev/null +++ b/core/src/components/segment-view/test/disabled/segment-view.e2e.ts @@ -0,0 +1,99 @@ +import { expect } from '@playwright/test'; +import { configs, test } from '@utils/test/playwright'; + +/** + * This behavior does not vary across directions + */ +configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => { + test.describe(title('segment-view: disabled'), () => { + test('should not have visual regressions', async ({ page }) => { + await page.setContent( + ` + + + + + Free + Top + + `, + config + ); + + const segment = page.locator('ion-segment-view'); + + await expect(segment).toHaveScreenshot(screenshot(`segment-view-disabled`)); + }); + }); +}); + +/** + * This behavior does not vary across modes/directions + */ +configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => { + test.describe(title('segment-view: disabled'), () => { + test('should show the second content when first segment content is disabled', async ({ page }) => { + await page.setContent( + ` + + + Paid + + + Free + + + Top + + + + + Free + Top + + `, + config + ); + + const segmentContent = page.locator('ion-segment-content[id="free"]'); + await expect(segmentContent).toBeInViewport(); + }); + + test.only('should scroll to the third content and update the segment value when the second segment content is disabled', async ({ page }) => { + await page.setContent( + ` + + + Paid + + + Free + + + Top + + + + + Free + Top + + `, + config + ); + + const segmentContent = page.locator('ion-segment-content[id="top"]'); + await segmentContent.scrollIntoViewIfNeeded(); + await expect(segmentContent).toBeInViewport(); + + const segment = page.locator('ion-segment'); + expect(await segment.evaluate((el: HTMLIonSegmentElement) => el.value)).toBe('top'); + }); + }); +}); From e68f55f24a5eb97ae9f481a3d3517963f983fd9a Mon Sep 17 00:00:00 2001 From: Brandy Carney Date: Tue, 24 Sep 2024 11:27:25 -0400 Subject: [PATCH 20/46] chore(): add updated snapshots --- ...view-disabled-ios-ltr-Mobile-Chrome-linux.png | Bin 0 -> 1132 bytes ...iew-disabled-ios-ltr-Mobile-Firefox-linux.png | Bin 0 -> 1939 bytes ...view-disabled-ios-ltr-Mobile-Safari-linux.png | Bin 0 -> 1121 bytes ...-view-disabled-md-ltr-Mobile-Chrome-linux.png | Bin 0 -> 1124 bytes ...view-disabled-md-ltr-Mobile-Firefox-linux.png | Bin 0 -> 1866 bytes ...-view-disabled-md-ltr-Mobile-Safari-linux.png | Bin 0 -> 1067 bytes 6 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 core/src/components/segment-view/test/disabled/segment-view.e2e.ts-snapshots/segment-view-disabled-ios-ltr-Mobile-Chrome-linux.png create mode 100644 core/src/components/segment-view/test/disabled/segment-view.e2e.ts-snapshots/segment-view-disabled-ios-ltr-Mobile-Firefox-linux.png create mode 100644 core/src/components/segment-view/test/disabled/segment-view.e2e.ts-snapshots/segment-view-disabled-ios-ltr-Mobile-Safari-linux.png create mode 100644 core/src/components/segment-view/test/disabled/segment-view.e2e.ts-snapshots/segment-view-disabled-md-ltr-Mobile-Chrome-linux.png create mode 100644 core/src/components/segment-view/test/disabled/segment-view.e2e.ts-snapshots/segment-view-disabled-md-ltr-Mobile-Firefox-linux.png create mode 100644 core/src/components/segment-view/test/disabled/segment-view.e2e.ts-snapshots/segment-view-disabled-md-ltr-Mobile-Safari-linux.png diff --git a/core/src/components/segment-view/test/disabled/segment-view.e2e.ts-snapshots/segment-view-disabled-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/segment-view/test/disabled/segment-view.e2e.ts-snapshots/segment-view-disabled-ios-ltr-Mobile-Chrome-linux.png new file mode 100644 index 0000000000000000000000000000000000000000..09790806a7f6bdb3209e102338805d38e6fe6f83 GIT binary patch literal 1132 zcmeAS@N?(olHy`uVBq!ia0y~yVC)34Q#hD_G#?KmW}oqDNmws`2ms-dlQetN*h)>xzH1cXnNisQUkQt=NIkhb}31?#q3z z6E{>+5^_=TvR>Qk@2?xeF}0=sZe{4=x+@ViueI3bbY{=UnDW8tal!{XoD z@5}T1|h?@0+Z;!Usejr`bs-$6WKgZZBuI|9SUfjgu}*v_3uB@OpKjNV?~5 zTVdJrvv|sSHx_2^{r6(^CW(^TiMvZ~roNB)v6W$mP1@~{&4p@8`_3#@naxwye*T?n z@3z91JND0gZy&ekV{0j|pyc=Yw=~w>4iWvC@cedwXyC1xGIqt8;Y|MO3>D6wZ-+el ztt6z%trYaE-Ynew@>XBLkD-Mho}B)8&~H~&cAD3|@7*0b_x5DRDzlu7xBHN4_x%Y& z!K%k*tnp%p!@s_LtM$IO%3tcV`Kz;m+xD5t<}SXmRC}MqxjCvVnrwGVa#znw>M^;# z?bzS9-Fw9trXRnbtL5Hozi;*Z+e@y-+|4QLD|g!`(Ob2DYUTOqiyv@*4Bd2PmSwlT zyHL%Q4FCQcCnC$2u?sRhSaS3J%e0mU{N?)$124U~v#BsE$EN1vv!Bo=w_eZX4A1YoXS;#Dfg$T7G^% zv^-}{*T%=q%gypSTQ2^4JoD_ukm9F{Yu+bL(K+&#H(utce!YQFz-Oa-6zFbwQ|C+t;`{vTjvcG=&zJI8>#`EBlar)2xDFRLU z$FG^j|DRD|YZNm1*L5p~isa>QW@jCnp^-PQaEYQ1!$0GH%B&4;TR2BCHNv2taoZ~H V3eg!X^1$MQ!PC{xWt~$(69D3Y1Tp{s literal 0 HcmV?d00001 diff --git a/core/src/components/segment-view/test/disabled/segment-view.e2e.ts-snapshots/segment-view-disabled-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/segment-view/test/disabled/segment-view.e2e.ts-snapshots/segment-view-disabled-ios-ltr-Mobile-Firefox-linux.png new file mode 100644 index 0000000000000000000000000000000000000000..4dcb813e3ae693df9accc96dfd82efea2c99b330 GIT binary patch literal 1939 zcmeAS@N?(olHy`uVBq!ia0y~yVC)34Q#jawWO2cnPzDC}C{Gv1kczmscW&p0go>~~ zh^=Z);?`*DSkU0++YvawXZn#97k@mcU)?X2-)Sge>el1MASk+X+O{ooGp%L6TRxSF zzT_Q|qI!SN2ZQ2sQGdgV>+Yt{PkeAX{`cQUt_=)~Oe|r75&}YX`WylZ4hg!9iVw@1 zL88}0fP$7F!3X~oFGlYOoF@MKeN9Hf0v^8iI=9=(b2`@T-xHB`u0Gi9yNu>y;r44b zWrg2@UdC959^>D;apC9R20&9dj&NO>X-sw&&}&to!^+0Az-ZOyh0Ei+@V@zdm7| zEB^Oi(aX|x+r>ZSIxkNBQ}9{*|5}EdRg0aHUbmfdXkcjMXbg3J6!x%HTKv?T`|78T zTr9tR+y88K=D(=OS-Yc}mxQyqtDidYqp_N4N%i0UT}+j0_L-jL7T`1Q*OH#M)^Yjfws*_S>c4)!wDVK8yEBVmcPRNCt&94?ep7?^WTfHowCWIdgGlRuWwgbzdBp? ze^KRKS)skHGmUD$ectr=qxk7>?(=i+=bN2OKUW+2>$JDiBS-)1KRE>yHn=ugD;gFr z?6Lj$>D0?tYnLqDrG9Z|*{*dQd{a06)n6O9EMBet_l~^0{XuSO^M1v9l&qVV6)v~C zfA9TuHhFuW>CN3T>Bgf6GJfE&ygEN`*H4Z&CypGPDth27_n)BjO>uY5Pm3$xrSpB4 zsrR&74w>^BqI6Z#{{P*$PA2Wtk%!yg{fyid{QPuG?fJLMIs{jPg7GZ(o$qCr?=F9P zb#F^rebSP5>L!gFatpRhsp8K1btXM~oysit?Ag4pJJav&GgryFVDg@`^DO_li7Xrf z9Rd;M9&6tC&(YaE|Mvzxowi%|X8mn_x!Koa&Av%5{N`#Gi`Q%I?%NkGzjW{CqkGci z7gs(B^Kj{j;01%$zoh@2+~o>W9C--A~J3yffDH&xuB(-{;Gnlv0j#fwI$QCh33t|9+j``|HQ6 zB;L-q(yMmeiC!UT7{SKIW?nLfUs%DR;lN?x8wDOVd%{B1K*=|QQBhELog6UT3vqIE zx*Wcw1!OK_ZE@MMqY4-t9x4hdLcHFqK_Me1AawE3D?tSZg^3LlJWO=f3IUl>4jwMu xtKESnxiC2?UEHw6Yt+!uFda=&7&+=6vyA1_$JIjoGk}#XgQu&X%Q~loCIC)E5PARr literal 0 HcmV?d00001 diff --git a/core/src/components/segment-view/test/disabled/segment-view.e2e.ts-snapshots/segment-view-disabled-ios-ltr-Mobile-Safari-linux.png b/core/src/components/segment-view/test/disabled/segment-view.e2e.ts-snapshots/segment-view-disabled-ios-ltr-Mobile-Safari-linux.png new file mode 100644 index 0000000000000000000000000000000000000000..9574679268db28e89e6410a3d0a08be782606b77 GIT binary patch literal 1121 zcmeAS@N?(olHy`uVBq!ia0y~yU~B`jQ#hDFB9aXKK#DEN+ueoXKL{?^yL>VO1B;la zi(^Q|oVR!Gc1wp!G<^I%$8z!+mDy`&zq__pW9_XlZl$jBrVIb0KX|z;T;b}SeXuU# z?Xlb}vpl28XO3IOoslx^eHT{tRn>0)Y=g;ljax*O?g($a@Ge zxX$Uga`Z}gWpU1t8)3(HeNmacym@_Z>0IvjYwF71dtcvs;a<(xEorP9zRl@4bDaPA zV)mq-4i65cN0;ipKl**VUGs|2zfV@HXZ0>FdoPj>R2K7DXsel-{(XUo0#3HlzUYSy*5Bs(?x)XBI_wR>+~dNZqEe(u*V4A;&C zPO6#eU-!$&#l=UYeDde;+kegL>$A?R;3w~-7|yt^T?f^GWd?(%tDnm{ Hr-UW|7@M#G literal 0 HcmV?d00001 diff --git a/core/src/components/segment-view/test/disabled/segment-view.e2e.ts-snapshots/segment-view-disabled-md-ltr-Mobile-Chrome-linux.png b/core/src/components/segment-view/test/disabled/segment-view.e2e.ts-snapshots/segment-view-disabled-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 0000000000000000000000000000000000000000..ce5c69da743f06dea2a01d1290d8c2d5e8174e45 GIT binary patch literal 1124 zcmeAS@N?(olHy`uVBq!ia0y~yVC)34Q#hD_9DcyW%s}T>j*<$lZVTv$M?G) zHugpIjVZmbEVx}FDi(bZFb6vaD{pg1U60*Beyj@n+SV+EU)!Oc<9(ilSyPB&h z8}f6elw5ngJ=E@mf4tXd}+jclF!|F2KlY(IbJv7qwDog4Kk>?=NoYJASNWM$xfvB!7c znYP{cyF54!`nT$;s?SasTT*H`z@iF5Z>h4Jtobvw2zz<1gHrFAR^ zRo`aMl|AriSH|S*W!e1ca)!759@~-j{$Q}Qsq2#`*UvBOH#8HQ_+Yj6Jh215hR=5v z#QUwPnefHzl|ie3(+oic>E%f|m1QM6)hp^*FEj1XOpE88K$G$R=k`HP0`c=CrQ_{Nf5z8Xavt2h zD4F4xGIjG)ZaZnz`6ohcY P*kJH<^>bP0l+XkK-~-|C literal 0 HcmV?d00001 diff --git a/core/src/components/segment-view/test/disabled/segment-view.e2e.ts-snapshots/segment-view-disabled-md-ltr-Mobile-Firefox-linux.png b/core/src/components/segment-view/test/disabled/segment-view.e2e.ts-snapshots/segment-view-disabled-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 0000000000000000000000000000000000000000..cd6be072b37ffd523f0c6176f1ed2418c7ae4280 GIT binary patch literal 1866 zcmeAS@N?(olHy`uVBq!ia0y~yVC)34Q#jawWO2cnPzDBeK~ERQkczmscW&l~hKjI1 zxZCJ=L1{8iAfqeWVvSDzeHk*3=Ib1BS^V&*gplwYWe$m{jsjNG`L>tNch*!8iEvn_ z_IkIz@9f>@EMG_a*56E@e|z3!`}J!?^qE*V1Qb3nG|DMH{NBXC$i#9@SU{-G94P3J z5Zh>`vP^%TR(J69cU8guFJ&b(rOn=H9lp&!ZQecJe%BqF_JpXNuMce7Ca>2#JH962 ztXKNRp82+cZe@Ez?|;6OEPH)6$e;)N1tYjS&)unzy!Z3h+SMy=i_C3ZX;lCB`la28 zOM2ebuK)7oi`_4-?7MHKUQS)`@8f&y{7fZr$Z%@x4$2>72j=8 zyYkGR%2hdcK9@;K-2Ln$wQ*8~S`VL714H9F4jU7dS-0IEr}tft_G>?VVO#UkpLOqU z`KK-e|GND zlGJmK=AUx&^zyrTuTE9WZ2<<0zxsy@6JDJ0pLUWve%i#j?x!CV|339aKK{D>x5=p$ z$0uI8Z66}GOn>@M>F1{-_R3y0-?H=G(p}~%vjS#Xe6M`@^7ZCww)%Vq(MjTPJ>Ec7|}?Y+aVCM~!-dB)$@zrVeH_E&!E!VMP1TVr`mv^#FE|7fiK zD(jJhG}C{+&e`VYrtYlj+4xs}-Bpw0*=E~wW6D$a`GLH@TqxrC%Vp8W-$=`>-QL$X z?cc=Q)i*cBJUf1Vdh5yV)XKtbc?IRSq65zAE_o=s{;s%?@$`L};d05p@5S%-s21`! z)jtXh?-#2M%w0bF)xCqq?j8^Gzju3`#H_1_@2*eNU%2z=6&9!Zy?d`*z8$YtZ++A5 z)UO2-X8iiQI!L}-Yq9Nd^UB}$jeFm7b*3BZyDK_0IE1sxS*uL5{+zmq?a*7_H_x=JO&QemukfGG zt^C=jcrhpIeB9rKIdxATU%G96!|d9fWwR{4&$R7a*LU+IDDkE+^~(yKeK;|oNzJUiZ$O$g6}_A7FE1t@1dQm?WZl>`P%8x$!ANuKrtZ7 z(dv?3xU6o`uid}g|GnR3B#|*cBWl*(uvg}(kJlxCZ*|#yYsc$iq1vyPZ#QSppImWy z|I-(*R?O`IYA$5_|3E))87SXmvb4C&3e27Y7F8594|W7bi3yXF(%BWJUO?uKh6x?B zS1JH~E5)cdG3|i0X;Us&B0@dWNwmWOi_KbnckR12PG(jnLSftIydIk~sACGsvRkH)ukqn-$elF{r5}E)S80jeh literal 0 HcmV?d00001 diff --git a/core/src/components/segment-view/test/disabled/segment-view.e2e.ts-snapshots/segment-view-disabled-md-ltr-Mobile-Safari-linux.png b/core/src/components/segment-view/test/disabled/segment-view.e2e.ts-snapshots/segment-view-disabled-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 0000000000000000000000000000000000000000..ae81335014ffa8cffdf1cb9bda4f116fee0e62d7 GIT binary patch literal 1067 zcmeAS@N?(olHy`uVBq!ia0y~yU~B`jQ#hDFB9aXKK#DEN+ueoXKL{?^yL>VO1M>q< z7srr_IdAWH=L@HcG(0qZyiwM=O@mX_vwH%UmXOw}sEr9%L;si5Gwhz07p0}d7^JDH zsKU}DtiI3J{9%w#;{>THsd?!?d-q@b_-yKPC2{lc*9<2X{#nn?P~khN!DUs)i}iP< z_C{F8XmoA5nR`yJRBXLS>eqK?|GL{x?JX;RIeT&Yw|RHuzFvR6@6W^gauOD2`4$I< z%{}*Xy|U7xc`|z}ZtSzaciX1;e`(hI zxAwm(x9q<6YVDrn&u9KToji~K#}b+P%&^k)u189CNDF=~vAMCw_|^LBlM`>gI;#^` zDY{;qKTqteU;fi77ndu|XRkIt=U+6>Bz^j#g6Uj85{|bgTgkb&T+DDi^6r-Y;(w3N z?OeT=+kDl#NAGT}J$dllMcv=Gzki>9FL&vp4qd zYRb0GnyKA&>|FC~_wz<`Wn5eW&(`!zY`pyXsqpqqfu&-7a_e5dJa}&Y-Otx=rR&S+ z$$pP=xS8SZVkgTe~DWM4fj$P<# literal 0 HcmV?d00001 From 6aa694b711359d2608b4915746a2e8419a364d45 Mon Sep 17 00:00:00 2001 From: Brandy Carney Date: Tue, 24 Sep 2024 11:38:13 -0400 Subject: [PATCH 21/46] test: remove only --- .../components/segment-view/test/disabled/segment-view.e2e.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/components/segment-view/test/disabled/segment-view.e2e.ts b/core/src/components/segment-view/test/disabled/segment-view.e2e.ts index 421c5cd316f..c00d483090b 100644 --- a/core/src/components/segment-view/test/disabled/segment-view.e2e.ts +++ b/core/src/components/segment-view/test/disabled/segment-view.e2e.ts @@ -65,7 +65,7 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => { await expect(segmentContent).toBeInViewport(); }); - test.only('should scroll to the third content and update the segment value when the second segment content is disabled', async ({ page }) => { + test('should scroll to the third content and update the segment value when the second segment content is disabled', async ({ page }) => { await page.setContent( ` From 6d2422481f094b0969ebb357bed68689dcef1e15 Mon Sep 17 00:00:00 2001 From: Brandy Carney Date: Tue, 24 Sep 2024 19:22:46 -0400 Subject: [PATCH 22/46] feat(segment): move indicator with scroll --- core/src/components.d.ts | 16 +++++ .../segment-button/segment-button.tsx | 5 +- .../components/segment-view/segment-view.tsx | 26 ++++++-- core/src/components/segment/segment.tsx | 62 +++++++++++++++++++ packages/angular/src/directives/proxies.ts | 6 +- .../standalone/src/directives/proxies.ts | 6 +- packages/vue/src/proxies.ts | 3 +- 7 files changed, 112 insertions(+), 12 deletions(-) diff --git a/core/src/components.d.ts b/core/src/components.d.ts index 1fbabdda79f..54e9aca549b 100644 --- a/core/src/components.d.ts +++ b/core/src/components.d.ts @@ -3438,6 +3438,10 @@ export interface IonSegmentCustomEvent extends CustomEvent { detail: T; target: HTMLIonSegmentElement; } +export interface IonSegmentViewCustomEvent extends CustomEvent { + detail: T; + target: HTMLIonSegmentViewElement; +} export interface IonSelectCustomEvent extends CustomEvent { detail: T; target: HTMLIonSelectElement; @@ -4440,7 +4444,18 @@ declare global { prototype: HTMLIonSegmentContentElement; new (): HTMLIonSegmentContentElement; }; + interface HTMLIonSegmentViewElementEventMap { + "ionSegmentViewScroll": { scrollDirection: string; scrollDistance: number }; + } interface HTMLIonSegmentViewElement extends Components.IonSegmentView, HTMLStencilElement { + addEventListener(type: K, listener: (this: HTMLIonSegmentViewElement, ev: IonSegmentViewCustomEvent) => any, options?: boolean | AddEventListenerOptions): void; + addEventListener(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void; + addEventListener(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void; + addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void; + removeEventListener(type: K, listener: (this: HTMLIonSegmentViewElement, ev: IonSegmentViewCustomEvent) => any, options?: boolean | EventListenerOptions): void; + removeEventListener(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | EventListenerOptions): void; + removeEventListener(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | EventListenerOptions): void; + removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void; } var HTMLIonSegmentViewElement: { prototype: HTMLIonSegmentViewElement; @@ -7522,6 +7537,7 @@ declare namespace LocalJSX { * If `true`, the segment view cannot be interacted with. */ "disabled"?: boolean; + "onIonSegmentViewScroll"?: (event: IonSegmentViewCustomEvent<{ scrollDirection: string; scrollDistance: number }>) => void; } interface IonSelect { /** diff --git a/core/src/components/segment-button/segment-button.tsx b/core/src/components/segment-button/segment-button.tsx index c1dc1d29148..d12dc4369cf 100644 --- a/core/src/components/segment-button/segment-button.tsx +++ b/core/src/components/segment-button/segment-button.tsx @@ -168,10 +168,7 @@ export class SegmentButton implements ComponentInterface, ButtonInterface {
diff --git a/core/src/components/segment-view/segment-view.tsx b/core/src/components/segment-view/segment-view.tsx index 4ab4088560b..7df8193d124 100644 --- a/core/src/components/segment-view/segment-view.tsx +++ b/core/src/components/segment-view/segment-view.tsx @@ -1,5 +1,5 @@ -import type { ComponentInterface } from '@stencil/core'; -import { Component, Element, Host, Listen, Method, Prop, h } from '@stencil/core'; +import type { ComponentInterface, EventEmitter } from '@stencil/core'; +import { Component, Element, Event, Host, Listen, Method, Prop, h } from '@stencil/core'; @Component({ tag: 'ion-segment-view', @@ -10,6 +10,8 @@ import { Component, Element, Host, Listen, Method, Prop, h } from '@stencil/core shadow: true, }) export class SegmentView implements ComponentInterface { + private previousScrollLeft = 0; + @Element() el!: HTMLElement; /** @@ -17,9 +19,23 @@ export class SegmentView implements ComponentInterface { */ @Prop() disabled = false; + @Event() ionSegmentViewScroll!: EventEmitter<{ scrollDirection: string; scrollDistance: number }>; + @Listen('scroll') - handleScroll(ev: any) { - const { scrollLeft, offsetWidth } = ev.target; + handleScroll(ev: Event) { + const { scrollLeft, offsetWidth } = ev.target as HTMLElement; + + const scrollDirection = scrollLeft > this.previousScrollLeft ? 'right' : 'left'; + this.previousScrollLeft = scrollLeft; + + const scrollDistance = scrollLeft; + + // Emit the scroll direction and distance + this.ionSegmentViewScroll.emit({ + scrollDirection, + scrollDistance + }); + const atSnappingPoint = scrollLeft % offsetWidth === 0; if (!atSnappingPoint) return; @@ -57,7 +73,7 @@ export class SegmentView implements ComponentInterface { this.el.scrollTo({ top: 0, left: index * contentWidth, - behavior: smoothScroll ? 'smooth' : 'auto', + behavior: smoothScroll ? 'smooth' : 'instant', }); } diff --git a/core/src/components/segment/segment.tsx b/core/src/components/segment/segment.tsx index 84c42e87429..f2b25403b7b 100644 --- a/core/src/components/segment/segment.tsx +++ b/core/src/components/segment/segment.tsx @@ -27,6 +27,8 @@ export class Segment implements ComponentInterface { // Value before the segment is dragged private valueBeforeGesture?: SegmentValue; + private segmentViewEl?: HTMLIonSegmentViewElement | null = null; + @Element() el!: HTMLIonSegmentElement; @State() activated = false; @@ -142,6 +144,12 @@ export class Segment implements ComponentInterface { connectedCallback() { this.emitStyle(); + + this.segmentViewEl = this.getSegmentView(); + } + + disconnectedCallback() { + this.segmentViewEl = null; } componentWillLoad() { @@ -323,6 +331,60 @@ export class Segment implements ComponentInterface { } } + private getSegmentView() { + const buttons = this.getButtons(); + // Get the first button with a contentId + const firstContentId = buttons.find((button: HTMLIonSegmentButtonElement) => button.contentId); + // Get the segment content with an id matching the button's contentId + const segmentContent = document.querySelector(`ion-segment-content[id="${firstContentId?.contentId}"]`); + // Return the segment view for that matching segment content + return segmentContent?.closest('ion-segment-view'); + } + + @Listen('ionSegmentViewScroll', { target: 'body' }) + handleSegmentViewScroll(ev: CustomEvent) { + const dispatchedFrom = ev.target as HTMLElement; + const segmentViewEl = this.segmentViewEl as EventTarget; + const segmentEl = this.el; + + // Only update the indicator if the event was dispatched from the segment view + // containing the segment contents that matches this segment's buttons + if (ev.composedPath().includes(segmentViewEl) || dispatchedFrom?.contains(segmentEl)) { + const buttons = this.getButtons(); + + // If no buttons are found or there is no value set then do nothing + if (!buttons.length || this.value === undefined) return; + + const index = buttons.findIndex((button) => button.value === this.value); + const current = buttons[index]; + const indicatorEl = this.getIndicator(current); + + const { scrollDirection, scrollDistance } = ev.detail; + + // Transform the indicator element to match the scroll of the segment view. + if (indicatorEl) { + indicatorEl.style.transition = 'transform 0.3s ease-out'; + + // Get dimensions of the segment and the button + const segmentRect = segmentEl.getBoundingClientRect(); + const buttonRect = current.getBoundingClientRect(); + + // Calculate the potential transform value based on scroll direction + const transformValue = scrollDirection === 'left' ? -scrollDistance : scrollDistance; + + // Calculate the max allowed transformation (indicator should not move beyond the segment boundaries) + const maxTransform = segmentRect.width - buttonRect.width; + const minTransform = 0; + + // Clamp the transform value to ensure it doesn't go out of bounds + const clampedTransform = Math.max(minTransform, Math.min(transformValue, maxTransform)); + + // Apply the clamped transform value to the indicator element + indicatorEl.style.transform = `translate3d(${clampedTransform}px, 0, 0)`; + } + } + } + /** * Finds the related segment view and sets its current content * based on the selected segment button. This method diff --git a/packages/angular/src/directives/proxies.ts b/packages/angular/src/directives/proxies.ts index 2e06b813b67..f8f2c5a122e 100644 --- a/packages/angular/src/directives/proxies.ts +++ b/packages/angular/src/directives/proxies.ts @@ -2046,11 +2046,15 @@ export class IonSegmentView { constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; + proxyOutputs(this, this.el, ['ionSegmentViewScroll']); } } -export declare interface IonSegmentView extends Components.IonSegmentView {} +export declare interface IonSegmentView extends Components.IonSegmentView { + + ionSegmentViewScroll: EventEmitter>; +} @ProxyCmp({ diff --git a/packages/angular/standalone/src/directives/proxies.ts b/packages/angular/standalone/src/directives/proxies.ts index 0f04a3ba642..994c8d5d153 100644 --- a/packages/angular/standalone/src/directives/proxies.ts +++ b/packages/angular/standalone/src/directives/proxies.ts @@ -1885,11 +1885,15 @@ export class IonSegmentView { constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; + proxyOutputs(this, this.el, ['ionSegmentViewScroll']); } } -export declare interface IonSegmentView extends Components.IonSegmentView {} +export declare interface IonSegmentView extends Components.IonSegmentView { + + ionSegmentViewScroll: EventEmitter>; +} @ProxyCmp({ diff --git a/packages/vue/src/proxies.ts b/packages/vue/src/proxies.ts index b433be92fec..b90135296ce 100644 --- a/packages/vue/src/proxies.ts +++ b/packages/vue/src/proxies.ts @@ -761,7 +761,8 @@ export const IonSegmentContent = /*@__PURE__*/ defineContainer('ion-segment-view', defineIonSegmentView, [ - 'disabled' + 'disabled', + 'ionSegmentViewScroll' ]); From 3d53f71fb6e65b9216003cf5d11ea53f3d3f4a96 Mon Sep 17 00:00:00 2001 From: Brandy Carney Date: Tue, 24 Sep 2024 19:54:44 -0400 Subject: [PATCH 23/46] fix(segment): properly move the indicator when direction starts out on the left --- core/src/components/segment-view/segment-view.tsx | 15 +++++++++++++-- core/src/components/segment/segment.tsx | 10 ++++++---- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/core/src/components/segment-view/segment-view.tsx b/core/src/components/segment-view/segment-view.tsx index 7df8193d124..ffca4d51494 100644 --- a/core/src/components/segment-view/segment-view.tsx +++ b/core/src/components/segment-view/segment-view.tsx @@ -10,6 +10,7 @@ import { Component, Element, Event, Host, Listen, Method, Prop, h } from '@stenc shadow: true, }) export class SegmentView implements ComponentInterface { + private initialScrollLeft = 0; private previousScrollLeft = 0; @Element() el!: HTMLElement; @@ -23,12 +24,17 @@ export class SegmentView implements ComponentInterface { @Listen('scroll') handleScroll(ev: Event) { + const { initialScrollLeft, previousScrollLeft } = this; const { scrollLeft, offsetWidth } = ev.target as HTMLElement; - const scrollDirection = scrollLeft > this.previousScrollLeft ? 'right' : 'left'; + const scrollDirection = scrollLeft > previousScrollLeft ? 'right' : 'left'; this.previousScrollLeft = scrollLeft; - const scrollDistance = scrollLeft; + let scrollDistance = scrollLeft; + + if (scrollDirection === 'left') { + scrollDistance = initialScrollLeft - scrollLeft; + } // Emit the scroll direction and distance this.ionSegmentViewScroll.emit({ @@ -55,6 +61,11 @@ export class SegmentView implements ComponentInterface { } } + @Listen('touchstart') + handleTouchStart() { + this.initialScrollLeft = this.el.scrollLeft; + } + /** * This method is used to programmatically set the displayed segment content * in the segment view. Calling this method will update the `value` of the diff --git a/core/src/components/segment/segment.tsx b/core/src/components/segment/segment.tsx index f2b25403b7b..a4f9186f682 100644 --- a/core/src/components/segment/segment.tsx +++ b/core/src/components/segment/segment.tsx @@ -372,15 +372,17 @@ export class Segment implements ComponentInterface { // Calculate the potential transform value based on scroll direction const transformValue = scrollDirection === 'left' ? -scrollDistance : scrollDistance; - // Calculate the max allowed transformation (indicator should not move beyond the segment boundaries) - const maxTransform = segmentRect.width - buttonRect.width; - const minTransform = 0; + // Calculate the max and min allowed transformations based on the scroll direction + const maxTransform = scrollDirection === 'left' ? 0 : segmentRect.width - buttonRect.width; + const minTransform = scrollDirection === 'left' ? + -(segmentRect.width - buttonRect.width) : 0; // Clamp the transform value to ensure it doesn't go out of bounds const clampedTransform = Math.max(minTransform, Math.min(transformValue, maxTransform)); // Apply the clamped transform value to the indicator element - indicatorEl.style.transform = `translate3d(${clampedTransform}px, 0, 0)`; + const transform = `translate3d(${clampedTransform}px, 0, 0)`; + indicatorEl.style.setProperty('transform', transform); } } } From 10acd9e871245c9629275e0c1889ab464fe17937 Mon Sep 17 00:00:00 2001 From: Brandy Carney Date: Tue, 24 Sep 2024 19:55:25 -0400 Subject: [PATCH 24/46] style: lint --- core/src/components/segment-button/segment-button.tsx | 5 +---- core/src/components/segment-view/segment-view.tsx | 2 +- .../segment-view/test/disabled/segment-view.e2e.ts | 4 +++- core/src/components/segment/segment.tsx | 3 +-- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/core/src/components/segment-button/segment-button.tsx b/core/src/components/segment-button/segment-button.tsx index d12dc4369cf..a92fcbbd31a 100644 --- a/core/src/components/segment-button/segment-button.tsx +++ b/core/src/components/segment-button/segment-button.tsx @@ -166,10 +166,7 @@ export class SegmentButton implements ComponentInterface, ButtonInterface { {mode === 'md' && } -
+
diff --git a/core/src/components/segment-view/segment-view.tsx b/core/src/components/segment-view/segment-view.tsx index ffca4d51494..822a581266d 100644 --- a/core/src/components/segment-view/segment-view.tsx +++ b/core/src/components/segment-view/segment-view.tsx @@ -39,7 +39,7 @@ export class SegmentView implements ComponentInterface { // Emit the scroll direction and distance this.ionSegmentViewScroll.emit({ scrollDirection, - scrollDistance + scrollDistance, }); const atSnappingPoint = scrollLeft % offsetWidth === 0; diff --git a/core/src/components/segment-view/test/disabled/segment-view.e2e.ts b/core/src/components/segment-view/test/disabled/segment-view.e2e.ts index c00d483090b..6582b1e8c31 100644 --- a/core/src/components/segment-view/test/disabled/segment-view.e2e.ts +++ b/core/src/components/segment-view/test/disabled/segment-view.e2e.ts @@ -65,7 +65,9 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => { await expect(segmentContent).toBeInViewport(); }); - test('should scroll to the third content and update the segment value when the second segment content is disabled', async ({ page }) => { + test('should scroll to the third content and update the segment value when the second segment content is disabled', async ({ + page, + }) => { await page.setContent( ` diff --git a/core/src/components/segment/segment.tsx b/core/src/components/segment/segment.tsx index a4f9186f682..8397065bbe6 100644 --- a/core/src/components/segment/segment.tsx +++ b/core/src/components/segment/segment.tsx @@ -374,8 +374,7 @@ export class Segment implements ComponentInterface { // Calculate the max and min allowed transformations based on the scroll direction const maxTransform = scrollDirection === 'left' ? 0 : segmentRect.width - buttonRect.width; - const minTransform = scrollDirection === 'left' ? - -(segmentRect.width - buttonRect.width) : 0; + const minTransform = scrollDirection === 'left' ? -(segmentRect.width - buttonRect.width) : 0; // Clamp the transform value to ensure it doesn't go out of bounds const clampedTransform = Math.max(minTransform, Math.min(transformValue, maxTransform)); From 7b83b454929bc4d2930b9286600dc5e62a58af7b Mon Sep 17 00:00:00 2001 From: Brandy Carney Date: Wed, 25 Sep 2024 12:00:52 -0400 Subject: [PATCH 25/46] fix(segment-view): allow moving the indicator left on scroll without touch --- core/api.txt | 2 + core/src/components.d.ts | 8 +++ .../components/segment-view/segment-view.tsx | 71 ++++++++++++++++--- packages/angular/src/directives/proxies.ts | 10 ++- .../standalone/src/directives/proxies.ts | 10 ++- packages/vue/src/proxies.ts | 3 +- 6 files changed, 91 insertions(+), 13 deletions(-) diff --git a/core/api.txt b/core/api.txt index 827fb83ef7a..c802ae1939c 100644 --- a/core/api.txt +++ b/core/api.txt @@ -1614,6 +1614,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 +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 diff --git a/core/src/components.d.ts b/core/src/components.d.ts index 54e9aca549b..ce3c78330ab 100644 --- a/core/src/components.d.ts +++ b/core/src/components.d.ts @@ -4446,6 +4446,7 @@ declare global { }; interface HTMLIonSegmentViewElementEventMap { "ionSegmentViewScroll": { scrollDirection: string; scrollDistance: number }; + "ionSegmentViewScrollEnd": void; } interface HTMLIonSegmentViewElement extends Components.IonSegmentView, HTMLStencilElement { addEventListener(type: K, listener: (this: HTMLIonSegmentViewElement, ev: IonSegmentViewCustomEvent) => any, options?: boolean | AddEventListenerOptions): void; @@ -7537,7 +7538,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; } interface IonSelect { /** diff --git a/core/src/components/segment-view/segment-view.tsx b/core/src/components/segment-view/segment-view.tsx index 822a581266d..abc932b8cec 100644 --- a/core/src/components/segment-view/segment-view.tsx +++ b/core/src/components/segment-view/segment-view.tsx @@ -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 | 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; + @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; + } } /** diff --git a/packages/angular/src/directives/proxies.ts b/packages/angular/src/directives/proxies.ts index f8f2c5a122e..30b3f816e64 100644 --- a/packages/angular/src/directives/proxies.ts +++ b/packages/angular/src/directives/proxies.ts @@ -2046,14 +2046,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>; + /** + * Emitted when the segment view scroll has ended. + */ + ionSegmentViewScrollEnd: EventEmitter>; } diff --git a/packages/angular/standalone/src/directives/proxies.ts b/packages/angular/standalone/src/directives/proxies.ts index 994c8d5d153..a871e3f7cef 100644 --- a/packages/angular/standalone/src/directives/proxies.ts +++ b/packages/angular/standalone/src/directives/proxies.ts @@ -1885,14 +1885,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>; + /** + * Emitted when the segment view scroll has ended. + */ + ionSegmentViewScrollEnd: EventEmitter>; } diff --git a/packages/vue/src/proxies.ts b/packages/vue/src/proxies.ts index b90135296ce..a0a56e1cf7a 100644 --- a/packages/vue/src/proxies.ts +++ b/packages/vue/src/proxies.ts @@ -762,7 +762,8 @@ export const IonSegmentContent = /*@__PURE__*/ defineContainer('ion-segment-view', defineIonSegmentView, [ 'disabled', - 'ionSegmentViewScroll' + 'ionSegmentViewScroll', + 'ionSegmentViewScrollEnd' ]); From 510ae395c88d6fe98b3c1ab92bebd132f33d19d1 Mon Sep 17 00:00:00 2001 From: Brandy Carney Date: Wed, 25 Sep 2024 15:16:54 -0400 Subject: [PATCH 26/46] fix(segment-view): always check the scrollLeft against the initial to get scrollDistance --- core/src/components/segment-view/segment-view.tsx | 6 +++--- core/src/components/segment/segment.tsx | 5 +---- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/core/src/components/segment-view/segment-view.tsx b/core/src/components/segment-view/segment-view.tsx index abc932b8cec..16c6687804d 100644 --- a/core/src/components/segment-view/segment-view.tsx +++ b/core/src/components/segment-view/segment-view.tsx @@ -41,12 +41,12 @@ export class SegmentView implements ComponentInterface { this.initialScrollLeft = scrollLeft; } + // Determine the scroll direction based on the previous scroll position const scrollDirection = scrollLeft > previousScrollLeft ? 'right' : 'left'; this.previousScrollLeft = 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; + // Calculate the distance scrolled based on the initial scroll position + const scrollDistance = scrollLeft - initialScrollLeft!; // Emit the scroll direction and distance this.ionSegmentViewScroll.emit({ diff --git a/core/src/components/segment/segment.tsx b/core/src/components/segment/segment.tsx index 8397065bbe6..07376e2a2d2 100644 --- a/core/src/components/segment/segment.tsx +++ b/core/src/components/segment/segment.tsx @@ -369,15 +369,12 @@ export class Segment implements ComponentInterface { const segmentRect = segmentEl.getBoundingClientRect(); const buttonRect = current.getBoundingClientRect(); - // Calculate the potential transform value based on scroll direction - const transformValue = scrollDirection === 'left' ? -scrollDistance : scrollDistance; - // Calculate the max and min allowed transformations based on the scroll direction const maxTransform = scrollDirection === 'left' ? 0 : segmentRect.width - buttonRect.width; const minTransform = scrollDirection === 'left' ? -(segmentRect.width - buttonRect.width) : 0; // Clamp the transform value to ensure it doesn't go out of bounds - const clampedTransform = Math.max(minTransform, Math.min(transformValue, maxTransform)); + const clampedTransform = Math.max(minTransform, Math.min(scrollDistance, maxTransform)); // Apply the clamped transform value to the indicator element const transform = `translate3d(${clampedTransform}px, 0, 0)`; From c8b765a0d2c51b57afdc76b2684d4656903ac252 Mon Sep 17 00:00:00 2001 From: Brandy Carney Date: Wed, 25 Sep 2024 15:50:39 -0400 Subject: [PATCH 27/46] fix(segment): properly bound indicator transform for more than 2 contents --- core/src/components/segment/segment.tsx | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/core/src/components/segment/segment.tsx b/core/src/components/segment/segment.tsx index 07376e2a2d2..1b11390bf56 100644 --- a/core/src/components/segment/segment.tsx +++ b/core/src/components/segment/segment.tsx @@ -347,8 +347,7 @@ export class Segment implements ComponentInterface { const segmentViewEl = this.segmentViewEl as EventTarget; const segmentEl = this.el; - // Only update the indicator if the event was dispatched from the segment view - // containing the segment contents that matches this segment's buttons + // Only update the indicator if the event was dispatched from the correct segment view if (ev.composedPath().includes(segmentViewEl) || dispatchedFrom?.contains(segmentEl)) { const buttons = this.getButtons(); @@ -359,19 +358,24 @@ export class Segment implements ComponentInterface { const current = buttons[index]; const indicatorEl = this.getIndicator(current); - const { scrollDirection, scrollDistance } = ev.detail; + const { scrollDistance } = ev.detail; - // Transform the indicator element to match the scroll of the segment view. if (indicatorEl) { indicatorEl.style.transition = 'transform 0.3s ease-out'; - // Get dimensions of the segment and the button - const segmentRect = segmentEl.getBoundingClientRect(); - const buttonRect = current.getBoundingClientRect(); + // Calculate total width of buttons to the left of the current button + const totalButtonWidthBefore = buttons + .slice(0, index) + .reduce((acc, button) => acc + button.getBoundingClientRect().width, 0); - // Calculate the max and min allowed transformations based on the scroll direction - const maxTransform = scrollDirection === 'left' ? 0 : segmentRect.width - buttonRect.width; - const minTransform = scrollDirection === 'left' ? -(segmentRect.width - buttonRect.width) : 0; + // Calculate total width of buttons to the right of the current button + const totalButtonWidthAfter = buttons + .slice(index + 1) + .reduce((acc, button) => acc + button.getBoundingClientRect().width, 0); + + // Set minTransform and maxTransform + const minTransform = -totalButtonWidthBefore; + const maxTransform = totalButtonWidthAfter; // Clamp the transform value to ensure it doesn't go out of bounds const clampedTransform = Math.max(minTransform, Math.min(scrollDistance, maxTransform)); From 3e66a38bdcae8303040444414d122ecf7eac2d2d Mon Sep 17 00:00:00 2001 From: Brandy Carney Date: Wed, 25 Sep 2024 16:52:37 -0400 Subject: [PATCH 28/46] fix(segment): move indicator as a percentage of the width on scroll --- core/api.txt | 2 +- core/src/components.d.ts | 4 ++-- core/src/components/segment-view/segment-view.tsx | 6 ++++-- core/src/components/segment/segment.tsx | 7 +++++-- packages/angular/src/directives/proxies.ts | 2 +- packages/angular/standalone/src/directives/proxies.ts | 2 +- 6 files changed, 14 insertions(+), 9 deletions(-) diff --git a/core/api.txt b/core/api.txt index c802ae1939c..ecb023eb7ce 100644 --- a/core/api.txt +++ b/core/api.txt @@ -1614,7 +1614,7 @@ 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 -ion-segment-view,event,ionSegmentViewScroll,{ scrollDirection: string; scrollDistance: number; },true +ion-segment-view,event,ionSegmentViewScroll,{ scrollDirection: string; scrollDistancePercentage: number; },true ion-segment-view,event,ionSegmentViewScrollEnd,void,true ion-select,shadow diff --git a/core/src/components.d.ts b/core/src/components.d.ts index ce3c78330ab..e8040e0cf27 100644 --- a/core/src/components.d.ts +++ b/core/src/components.d.ts @@ -4445,7 +4445,7 @@ declare global { new (): HTMLIonSegmentContentElement; }; interface HTMLIonSegmentViewElementEventMap { - "ionSegmentViewScroll": { scrollDirection: string; scrollDistance: number }; + "ionSegmentViewScroll": { scrollDirection: string; scrollDistancePercentage: number }; "ionSegmentViewScrollEnd": void; } interface HTMLIonSegmentViewElement extends Components.IonSegmentView, HTMLStencilElement { @@ -7541,7 +7541,7 @@ declare namespace LocalJSX { /** * Emitted when the segment view is scrolled. */ - "onIonSegmentViewScroll"?: (event: IonSegmentViewCustomEvent<{ scrollDirection: string; scrollDistance: number }>) => void; + "onIonSegmentViewScroll"?: (event: IonSegmentViewCustomEvent<{ scrollDirection: string; scrollDistancePercentage: number }>) => void; /** * Emitted when the segment view scroll has ended. */ diff --git a/core/src/components/segment-view/segment-view.tsx b/core/src/components/segment-view/segment-view.tsx index 16c6687804d..7b268c236cc 100644 --- a/core/src/components/segment-view/segment-view.tsx +++ b/core/src/components/segment-view/segment-view.tsx @@ -25,7 +25,7 @@ export class SegmentView implements ComponentInterface { /** * Emitted when the segment view is scrolled. */ - @Event() ionSegmentViewScroll!: EventEmitter<{ scrollDirection: string; scrollDistance: number }>; + @Event() ionSegmentViewScroll!: EventEmitter<{ scrollDirection: string; scrollDistancePercentage: number }>; /** * Emitted when the segment view scroll has ended. @@ -46,12 +46,14 @@ export class SegmentView implements ComponentInterface { this.previousScrollLeft = scrollLeft; // Calculate the distance scrolled based on the initial scroll position + // and then transform it to a percentage of the segment view width const scrollDistance = scrollLeft - initialScrollLeft!; + const scrollDistancePercentage = Math.abs(scrollDistance) / offsetWidth; // Emit the scroll direction and distance this.ionSegmentViewScroll.emit({ scrollDirection, - scrollDistance, + scrollDistancePercentage, }); const atSnappingPoint = scrollLeft % offsetWidth === 0; diff --git a/core/src/components/segment/segment.tsx b/core/src/components/segment/segment.tsx index 1b11390bf56..57b07aad37d 100644 --- a/core/src/components/segment/segment.tsx +++ b/core/src/components/segment/segment.tsx @@ -358,11 +358,14 @@ export class Segment implements ComponentInterface { const current = buttons[index]; const indicatorEl = this.getIndicator(current); - const { scrollDistance } = ev.detail; + const { scrollDirection, scrollDistancePercentage } = ev.detail; if (indicatorEl) { indicatorEl.style.transition = 'transform 0.3s ease-out'; + const scrollDistance = scrollDistancePercentage * current.getBoundingClientRect().width; + const transformValue = scrollDirection === 'left' ? -scrollDistance : scrollDistance; + // Calculate total width of buttons to the left of the current button const totalButtonWidthBefore = buttons .slice(0, index) @@ -378,7 +381,7 @@ export class Segment implements ComponentInterface { const maxTransform = totalButtonWidthAfter; // Clamp the transform value to ensure it doesn't go out of bounds - const clampedTransform = Math.max(minTransform, Math.min(scrollDistance, maxTransform)); + const clampedTransform = Math.max(minTransform, Math.min(transformValue, maxTransform)); // Apply the clamped transform value to the indicator element const transform = `translate3d(${clampedTransform}px, 0, 0)`; diff --git a/packages/angular/src/directives/proxies.ts b/packages/angular/src/directives/proxies.ts index 30b3f816e64..c6682a0ad73 100644 --- a/packages/angular/src/directives/proxies.ts +++ b/packages/angular/src/directives/proxies.ts @@ -2055,7 +2055,7 @@ export declare interface IonSegmentView extends Components.IonSegmentView { /** * Emitted when the segment view is scrolled. */ - ionSegmentViewScroll: EventEmitter>; + ionSegmentViewScroll: EventEmitter>; /** * Emitted when the segment view scroll has ended. */ diff --git a/packages/angular/standalone/src/directives/proxies.ts b/packages/angular/standalone/src/directives/proxies.ts index a871e3f7cef..91354ace230 100644 --- a/packages/angular/standalone/src/directives/proxies.ts +++ b/packages/angular/standalone/src/directives/proxies.ts @@ -1894,7 +1894,7 @@ export declare interface IonSegmentView extends Components.IonSegmentView { /** * Emitted when the segment view is scrolled. */ - ionSegmentViewScroll: EventEmitter>; + ionSegmentViewScroll: EventEmitter>; /** * Emitted when the segment view scroll has ended. */ From b751f04240c71cdb0a06efb5d4cfa3344f7d61c4 Mon Sep 17 00:00:00 2001 From: Tanner Reits Date: Fri, 27 Sep 2024 18:27:32 -0400 Subject: [PATCH 29/46] 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 7b268c236cc..ed8e792b2c7 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 57b07aad37d..ca06831bee9 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, scrollDistance } = ev.detail; - const { scrollDirection, scrollDistancePercentage } = ev.detail; + console.log('scroll', scrollDistancePercentage, scrollDistance); - if (indicatorEl) { + 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 From f81a6133e615534af79d31e6774fe58bda6575f7 Mon Sep 17 00:00:00 2001 From: Tanner Reits Date: Fri, 27 Sep 2024 18:34:19 -0400 Subject: [PATCH 30/46] fix(segment): handle change of direction scrolling --- core/src/components.d.ts | 18 ++++++++++++++---- core/src/components/segment/segment.tsx | 6 ++---- packages/angular/src/directives/proxies.ts | 8 +++++--- .../standalone/src/directives/proxies.ts | 8 +++++--- packages/vue/src/proxies.ts | 3 ++- 5 files changed, 28 insertions(+), 15 deletions(-) diff --git a/core/src/components.d.ts b/core/src/components.d.ts index e8040e0cf27..716e5517f4f 100644 --- a/core/src/components.d.ts +++ b/core/src/components.d.ts @@ -4445,8 +4445,13 @@ declare global { new (): HTMLIonSegmentContentElement; }; interface HTMLIonSegmentViewElementEventMap { - "ionSegmentViewScroll": { scrollDirection: string; scrollDistancePercentage: number }; - "ionSegmentViewScrollEnd": void; + "ionSegmentViewScroll": { + scrollDirection: string; + scrollDistance: number; + scrollDistancePercentage: number; + }; + "ionSegmentViewScrollEnd": { activeContentId: string }; + "ionSegmentViewScrollStart": void; } interface HTMLIonSegmentViewElement extends Components.IonSegmentView, HTMLStencilElement { addEventListener(type: K, listener: (this: HTMLIonSegmentViewElement, ev: IonSegmentViewCustomEvent) => any, options?: boolean | AddEventListenerOptions): void; @@ -7541,11 +7546,16 @@ declare namespace LocalJSX { /** * Emitted when the segment view is scrolled. */ - "onIonSegmentViewScroll"?: (event: IonSegmentViewCustomEvent<{ scrollDirection: string; scrollDistancePercentage: number }>) => void; + "onIonSegmentViewScroll"?: (event: IonSegmentViewCustomEvent<{ + scrollDirection: string; + scrollDistance: number; + scrollDistancePercentage: number; + }>) => void; /** * Emitted when the segment view scroll has ended. */ - "onIonSegmentViewScrollEnd"?: (event: IonSegmentViewCustomEvent) => void; + "onIonSegmentViewScrollEnd"?: (event: IonSegmentViewCustomEvent<{ activeContentId: string }>) => void; + "onIonSegmentViewScrollStart"?: (event: IonSegmentViewCustomEvent) => void; } interface IonSelect { /** diff --git a/core/src/components/segment/segment.tsx b/core/src/components/segment/segment.tsx index ca06831bee9..2d6b50a17c9 100644 --- a/core/src/components/segment/segment.tsx +++ b/core/src/components/segment/segment.tsx @@ -368,15 +368,13 @@ export class Segment implements ComponentInterface { const indicatorEl = this.getIndicator(current); this.scrolledIndicator = indicatorEl; - const { scrollDirection, scrollDistancePercentage, scrollDistance } = ev.detail; - - console.log('scroll', scrollDistancePercentage, scrollDistance); + const { scrollDistancePercentage, scrollDistance: evScrollDistance } = ev.detail; if (indicatorEl && !isNaN(scrollDistancePercentage)) { indicatorEl.style.transition = 'transform 0.3s ease-out'; const scrollDistance = scrollDistancePercentage * current.getBoundingClientRect().width; - const transformValue = scrollDirection === 'left' ? -scrollDistance : scrollDistance; + const transformValue = evScrollDistance < 0 ? -scrollDistance : scrollDistance; // Calculate total width of buttons to the left of the current button const totalButtonWidthBefore = buttons diff --git a/packages/angular/src/directives/proxies.ts b/packages/angular/src/directives/proxies.ts index c6682a0ad73..ab3d9c061c6 100644 --- a/packages/angular/src/directives/proxies.ts +++ b/packages/angular/src/directives/proxies.ts @@ -2046,7 +2046,7 @@ export class IonSegmentView { constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; - proxyOutputs(this, this.el, ['ionSegmentViewScroll', 'ionSegmentViewScrollEnd']); + proxyOutputs(this, this.el, ['ionSegmentViewScroll', 'ionSegmentViewScrollEnd', 'ionSegmentViewScrollStart']); } } @@ -2055,11 +2055,13 @@ export declare interface IonSegmentView extends Components.IonSegmentView { /** * Emitted when the segment view is scrolled. */ - ionSegmentViewScroll: EventEmitter>; + ionSegmentViewScroll: EventEmitter>; /** * Emitted when the segment view scroll has ended. */ - ionSegmentViewScrollEnd: EventEmitter>; + ionSegmentViewScrollEnd: EventEmitter>; + + ionSegmentViewScrollStart: EventEmitter>; } diff --git a/packages/angular/standalone/src/directives/proxies.ts b/packages/angular/standalone/src/directives/proxies.ts index 91354ace230..65c071dc093 100644 --- a/packages/angular/standalone/src/directives/proxies.ts +++ b/packages/angular/standalone/src/directives/proxies.ts @@ -1885,7 +1885,7 @@ export class IonSegmentView { constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; - proxyOutputs(this, this.el, ['ionSegmentViewScroll', 'ionSegmentViewScrollEnd']); + proxyOutputs(this, this.el, ['ionSegmentViewScroll', 'ionSegmentViewScrollEnd', 'ionSegmentViewScrollStart']); } } @@ -1894,11 +1894,13 @@ export declare interface IonSegmentView extends Components.IonSegmentView { /** * Emitted when the segment view is scrolled. */ - ionSegmentViewScroll: EventEmitter>; + ionSegmentViewScroll: EventEmitter>; /** * Emitted when the segment view scroll has ended. */ - ionSegmentViewScrollEnd: EventEmitter>; + ionSegmentViewScrollEnd: EventEmitter>; + + ionSegmentViewScrollStart: EventEmitter>; } diff --git a/packages/vue/src/proxies.ts b/packages/vue/src/proxies.ts index a0a56e1cf7a..d72ec0378c5 100644 --- a/packages/vue/src/proxies.ts +++ b/packages/vue/src/proxies.ts @@ -763,7 +763,8 @@ export const IonSegmentContent = /*@__PURE__*/ defineContainer('ion-segment-view', defineIonSegmentView, [ 'disabled', 'ionSegmentViewScroll', - 'ionSegmentViewScrollEnd' + 'ionSegmentViewScrollEnd', + 'ionSegmentViewScrollStart' ]); From 0a7f4bac4e7dce29e060f1234990cea9db0f4025 Mon Sep 17 00:00:00 2001 From: Tanner Reits Date: Sat, 28 Sep 2024 20:29:03 -0400 Subject: [PATCH 31/46] fix(segment): don't trigger scroll listener on segment button click --- core/src/components/segment/segment.tsx | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/core/src/components/segment/segment.tsx b/core/src/components/segment/segment.tsx index 2d6b50a17c9..28918310558 100644 --- a/core/src/components/segment/segment.tsx +++ b/core/src/components/segment/segment.tsx @@ -29,6 +29,7 @@ export class Segment implements ComponentInterface { private segmentViewEl?: HTMLIonSegmentViewElement | null = null; private scrolledIndicator?: HTMLDivElement | null = null; + private isScrolling = false; @Element() el!: HTMLIonSegmentElement; @@ -352,6 +353,10 @@ export class Segment implements ComponentInterface { @Listen('ionSegmentViewScroll', { target: 'body' }) handleSegmentViewScroll(ev: CustomEvent) { + if (!this.isScrolling) { + return; + } + const dispatchedFrom = ev.target as HTMLElement; const segmentViewEl = this.segmentViewEl as EventTarget; const segmentEl = this.el; @@ -401,7 +406,9 @@ export class Segment implements ComponentInterface { } @Listen('ionSegmentViewScrollStart', { target: 'body' }) - onScrollStart() {} + onScrollStart() { + this.isScrolling = true; + } @Listen('ionSegmentViewScrollEnd', { target: 'body' }) onScrollEnd(ev: CustomEvent<{ activeContentId: string }>) { @@ -411,6 +418,8 @@ export class Segment implements ComponentInterface { this.scrolledIndicator.style.transition = ''; this.scrolledIndicator.style.transform = ''; } + + this.isScrolling = false; } /** From 6635c84c32b7b474bc2a03db2a997fad9055215c Mon Sep 17 00:00:00 2001 From: Tanner Reits Date: Sat, 28 Sep 2024 20:33:48 -0400 Subject: [PATCH 32/46] fix(segment): only handle events for correct instance --- core/src/components/segment/segment.tsx | 28 ++++++++++++++++++------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/core/src/components/segment/segment.tsx b/core/src/components/segment/segment.tsx index 28918310558..122e355a855 100644 --- a/core/src/components/segment/segment.tsx +++ b/core/src/components/segment/segment.tsx @@ -406,20 +406,32 @@ export class Segment implements ComponentInterface { } @Listen('ionSegmentViewScrollStart', { target: 'body' }) - onScrollStart() { - this.isScrolling = true; + onScrollStart(ev: CustomEvent) { + const dispatchedFrom = ev.target as HTMLElement; + const segmentViewEl = this.segmentViewEl as EventTarget; + const segmentEl = this.el; + + if (ev.composedPath().includes(segmentViewEl) || dispatchedFrom?.contains(segmentEl)) { + this.isScrolling = true; + } } @Listen('ionSegmentViewScrollEnd', { target: 'body' }) onScrollEnd(ev: CustomEvent<{ activeContentId: string }>) { - this.value = ev.detail.activeContentId; + const dispatchedFrom = ev.target as HTMLElement; + const segmentViewEl = this.segmentViewEl as EventTarget; + const segmentEl = this.el; - if (this.scrolledIndicator) { - this.scrolledIndicator.style.transition = ''; - this.scrolledIndicator.style.transform = ''; - } + if (ev.composedPath().includes(segmentViewEl) || dispatchedFrom?.contains(segmentEl)) { + this.value = ev.detail.activeContentId; - this.isScrolling = false; + if (this.scrolledIndicator) { + this.scrolledIndicator.style.transition = ''; + this.scrolledIndicator.style.transform = ''; + } + + this.isScrolling = false; + } } /** From 43845c277e0f7ea0b4f52a0d14d48eca80ed4e55 Mon Sep 17 00:00:00 2001 From: Tanner Reits Date: Sat, 28 Sep 2024 20:59:22 -0400 Subject: [PATCH 33/46] fix(segment): scroll segment button into view if appropriate --- core/src/components/segment/segment.tsx | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/core/src/components/segment/segment.tsx b/core/src/components/segment/segment.tsx index 122e355a855..1a11aef3a3a 100644 --- a/core/src/components/segment/segment.tsx +++ b/core/src/components/segment/segment.tsx @@ -401,6 +401,23 @@ export class Segment implements ComponentInterface { // Apply the clamped transform value to the indicator element const transform = `translate3d(${clampedTransform}px, 0, 0)`; indicatorEl.style.setProperty('transform', transform); + + // Scroll the buttons if the indicator is out of view + const indicatorX = indicatorEl.getBoundingClientRect().x; + const buttonWidth = current.getBoundingClientRect().width; + if (evScrollDistance < 0 && indicatorX < 0) { + this.el.scrollBy({ + top: 0, + left: indicatorX, + behavior: 'instant', + }); + } else if (evScrollDistance > 0 && indicatorX + buttonWidth > this.el.offsetWidth) { + this.el.scrollBy({ + top: 0, + left: indicatorX + buttonWidth - this.el.offsetWidth, + behavior: 'instant', + }); + } } } } From 1fbddf8b3f9ec835c1b5d7ddfa74c784bd2d47be Mon Sep 17 00:00:00 2001 From: Brandy Carney Date: Mon, 30 Sep 2024 09:51:19 -0400 Subject: [PATCH 34/46] chore: build --- core/api.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/api.txt b/core/api.txt index ecb023eb7ce..46452ee06f3 100644 --- a/core/api.txt +++ b/core/api.txt @@ -1614,8 +1614,9 @@ 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 -ion-segment-view,event,ionSegmentViewScroll,{ scrollDirection: string; scrollDistancePercentage: number; },true -ion-segment-view,event,ionSegmentViewScrollEnd,void,true +ion-segment-view,event,ionSegmentViewScroll,{ scrollDirection: string; scrollDistance: number; scrollDistancePercentage: number; },true +ion-segment-view,event,ionSegmentViewScrollEnd,{ activeContentId: string; },true +ion-segment-view,event,ionSegmentViewScrollStart,void,true ion-select,shadow ion-select,prop,cancelText,string,'Cancel',false,false From 2069f7e4011e7c260de3429a8799605e62e615bc Mon Sep 17 00:00:00 2001 From: Brandy Carney Date: Mon, 30 Sep 2024 09:57:22 -0400 Subject: [PATCH 35/46] style: naming --- core/src/components/segment/segment.tsx | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/core/src/components/segment/segment.tsx b/core/src/components/segment/segment.tsx index 1a11aef3a3a..7818a5dec22 100644 --- a/core/src/components/segment/segment.tsx +++ b/core/src/components/segment/segment.tsx @@ -373,13 +373,15 @@ export class Segment implements ComponentInterface { const indicatorEl = this.getIndicator(current); this.scrolledIndicator = indicatorEl; - const { scrollDistancePercentage, scrollDistance: evScrollDistance } = ev.detail; + const { scrollDistancePercentage, scrollDistance } = ev.detail; if (indicatorEl && !isNaN(scrollDistancePercentage)) { indicatorEl.style.transition = 'transform 0.3s ease-out'; - const scrollDistance = scrollDistancePercentage * current.getBoundingClientRect().width; - const transformValue = evScrollDistance < 0 ? -scrollDistance : scrollDistance; + // Calculate the amount the indicator should move based on the scroll percentage + // and the width of the current button + const scrollAmount = scrollDistancePercentage * current.getBoundingClientRect().width; + const transformValue = scrollDistance < 0 ? -scrollAmount : scrollAmount; // Calculate total width of buttons to the left of the current button const totalButtonWidthBefore = buttons @@ -405,13 +407,13 @@ export class Segment implements ComponentInterface { // Scroll the buttons if the indicator is out of view const indicatorX = indicatorEl.getBoundingClientRect().x; const buttonWidth = current.getBoundingClientRect().width; - if (evScrollDistance < 0 && indicatorX < 0) { + if (scrollDistance < 0 && indicatorX < 0) { this.el.scrollBy({ top: 0, left: indicatorX, behavior: 'instant', }); - } else if (evScrollDistance > 0 && indicatorX + buttonWidth > this.el.offsetWidth) { + } else if (scrollDistance > 0 && indicatorX + buttonWidth > this.el.offsetWidth) { this.el.scrollBy({ top: 0, left: indicatorX + buttonWidth - this.el.offsetWidth, From a91a5a4c1388b875d0991591d80a6ee13005ed2e Mon Sep 17 00:00:00 2001 From: Brandy Carney Date: Tue, 1 Oct 2024 16:04:29 -0400 Subject: [PATCH 36/46] refactor(segment-content): use opacity for disabled content --- .../components/segment-content/segment-content.ios.scss | 9 +++++++++ .../components/segment-content/segment-content.md.scss | 9 +++++++++ core/src/components/segment-content/segment-content.scss | 4 ---- core/src/components/segment-content/segment-content.tsx | 5 ++++- 4 files changed, 22 insertions(+), 5 deletions(-) create mode 100644 core/src/components/segment-content/segment-content.ios.scss create mode 100644 core/src/components/segment-content/segment-content.md.scss diff --git a/core/src/components/segment-content/segment-content.ios.scss b/core/src/components/segment-content/segment-content.ios.scss new file mode 100644 index 00000000000..026e16b1f97 --- /dev/null +++ b/core/src/components/segment-content/segment-content.ios.scss @@ -0,0 +1,9 @@ +@import "./segment-content"; +@import "../segment-button/segment-button.ios.vars"; + +// iOS Segment Content +// -------------------------------------------------- + +:host(.segment-content-disabled) { + opacity: $segment-button-ios-opacity-disabled; +} diff --git a/core/src/components/segment-content/segment-content.md.scss b/core/src/components/segment-content/segment-content.md.scss new file mode 100644 index 00000000000..1432941bc19 --- /dev/null +++ b/core/src/components/segment-content/segment-content.md.scss @@ -0,0 +1,9 @@ +@import "./segment-content"; +@import "../segment-button/segment-button.md.vars"; + +// Material Design Segment Content +// -------------------------------------------------- + +:host(.segment-content-disabled) { + opacity: $segment-button-md-opacity-disabled; +} diff --git a/core/src/components/segment-content/segment-content.scss b/core/src/components/segment-content/segment-content.scss index 6a1818a8b79..00ca64f3041 100644 --- a/core/src/components/segment-content/segment-content.scss +++ b/core/src/components/segment-content/segment-content.scss @@ -8,7 +8,3 @@ width: 100%; } - -:host(.segment-content-disabled) { - display: none; -} diff --git a/core/src/components/segment-content/segment-content.tsx b/core/src/components/segment-content/segment-content.tsx index 4566e8ff803..a4db11101ee 100644 --- a/core/src/components/segment-content/segment-content.tsx +++ b/core/src/components/segment-content/segment-content.tsx @@ -3,7 +3,10 @@ import { Component, Host, Prop, h } from '@stencil/core'; @Component({ tag: 'ion-segment-content', - styleUrl: 'segment-content.scss', + styleUrls: { + ios: 'segment-content.ios.scss', + md: 'segment-content.md.scss', + }, shadow: true, }) export class SegmentContent implements ComponentInterface { From 35337edadf78260df59248bcd70c831f85c54c40 Mon Sep 17 00:00:00 2001 From: Brandy Carney Date: Wed, 2 Oct 2024 11:41:36 -0400 Subject: [PATCH 37/46] test(segment-view): remove not disabled styles --- core/src/components/segment-view/test/disabled/index.html | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/core/src/components/segment-view/test/disabled/index.html b/core/src/components/segment-view/test/disabled/index.html index b2e676d34a1..cc48642fedb 100644 --- a/core/src/components/segment-view/test/disabled/index.html +++ b/core/src/components/segment-view/test/disabled/index.html @@ -18,11 +18,7 @@ height: 100px; } - /** - * TODO the :not is required otherwise it - * overrides the disabled styles - */ - ion-segment-content:not([disabled]) { + ion-segment-content { display: flex; justify-content: center; align-items: center; From b58f9a741ca244579cff4f12f11aec7328b0af5e Mon Sep 17 00:00:00 2001 From: Brandy Carney Date: Tue, 8 Oct 2024 17:35:14 -0400 Subject: [PATCH 38/46] fix(segment): update segment view to scroll past disabled content --- .../components/segment-view/segment-view.tsx | 44 +++++++++++++------ .../segment-view/test/disabled/index.html | 25 +++++++++++ core/src/components/segment/segment.tsx | 38 ++++++++++++++-- 3 files changed, 89 insertions(+), 18 deletions(-) diff --git a/core/src/components/segment-view/segment-view.tsx b/core/src/components/segment-view/segment-view.tsx index ed8e792b2c7..75a20419c80 100644 --- a/core/src/components/segment-view/segment-view.tsx +++ b/core/src/components/segment-view/segment-view.tsx @@ -45,9 +45,8 @@ export class SegmentView implements ComponentInterface { const { initialScrollLeft, previousScrollLeft } = this; const { scrollLeft, offsetWidth } = ev.target as HTMLElement; - if (initialScrollLeft === undefined) { - this.initialScrollLeft = scrollLeft; - } + // Set initial scroll position if it's undefined + this.initialScrollLeft = initialScrollLeft ?? scrollLeft; // Determine the scroll direction based on the previous scroll position const scrollDirection = scrollLeft > previousScrollLeft ? 'right' : 'left'; @@ -55,7 +54,7 @@ export class SegmentView implements ComponentInterface { // Calculate the distance scrolled based on the initial scroll position // and then transform it to a percentage of the segment view width - const scrollDistance = scrollLeft - initialScrollLeft!; + const scrollDistance = scrollLeft - this.initialScrollLeft; const scrollDistancePercentage = Math.abs(scrollDistance) / offsetWidth; // Emit the scroll direction and distance @@ -65,20 +64,28 @@ export class SegmentView implements ComponentInterface { scrollDistancePercentage, }); + // Check if the scroll is at a snapping point and return if not const atSnappingPoint = scrollLeft % offsetWidth === 0; - if (!atSnappingPoint) return; - const index = Math.round(scrollLeft / offsetWidth); - const segmentContent = this.getSegmentContents()[index]; + // Find the current segment content based on the scroll position + const currentIndex = Math.round(scrollLeft / offsetWidth); + let segmentContent = this.getSegmentContents()[currentIndex]; - if (segmentContent === null || segmentContent === undefined) { - return; + // Exit if no valid segment content found + if (!segmentContent) return; + + // If the current content is disabled, find the next enabled content + if (segmentContent.disabled) { + const nextIndex = scrollDirection === 'right' ? currentIndex + 1 : currentIndex - 1; + segmentContent = this.getNextSegmentContent(nextIndex); } - // Store the active `ion-segment-content` id so we can emit it when the scroll ends + // Update active content ID and scroll to the segment content this.activeContentId = segmentContent.id; + this.setContent(segmentContent.id); + // Reset the timeout to check for scroll end this.resetScrollEndTimeout(); } @@ -120,11 +127,15 @@ export class SegmentView implements ComponentInterface { /** * 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. + * If the conditions are met (active content is enabled and no active touch), + * reset the scroll position and emit the scroll end event. */ private checkForScrollEnd() { - if (!this.isTouching) { + const activeContent = this.getSegmentContents().find(content => content.id === this.activeContentId); + + // Only emit scroll end event if the active content is not disabled and + // the user is not touching the segment view + if (activeContent?.disabled === false && !this.isTouching) { this.ionSegmentViewScrollEnd.emit({ activeContentId: this.activeContentId }); this.initialScrollLeft = undefined; } @@ -153,7 +164,12 @@ export class SegmentView implements ComponentInterface { } private getSegmentContents(): HTMLIonSegmentContentElement[] { - return Array.from(this.el.querySelectorAll('ion-segment-content:not([disabled])')); + return Array.from(this.el.querySelectorAll('ion-segment-content')); + } + + private getNextSegmentContent(index: number): HTMLIonSegmentContentElement { + const contents = this.getSegmentContents(); + return contents[index]; } render() { diff --git a/core/src/components/segment-view/test/disabled/index.html b/core/src/components/segment-view/test/disabled/index.html index cc48642fedb..d5a3cbb3f7b 100644 --- a/core/src/components/segment-view/test/disabled/index.html +++ b/core/src/components/segment-view/test/disabled/index.html @@ -35,6 +35,10 @@ ion-segment-content:nth-of-type(3) { background: lightgreen; } + + ion-segment-content:nth-of-type(4) { + background: lightgoldenrodyellow; + } @@ -77,6 +81,27 @@ Top + + + a + + + b + + + c + + + d + + + + a + b + c + d + + Bookmarks diff --git a/core/src/components/segment/segment.tsx b/core/src/components/segment/segment.tsx index 7818a5dec22..6bb0ba50ef2 100644 --- a/core/src/components/segment/segment.tsx +++ b/core/src/components/segment/segment.tsx @@ -442,17 +442,47 @@ export class Segment implements ComponentInterface { const segmentEl = this.el; if (ev.composedPath().includes(segmentViewEl) || dispatchedFrom?.contains(segmentEl)) { - this.value = ev.detail.activeContentId; - if (this.scrolledIndicator) { - this.scrolledIndicator.style.transition = ''; - this.scrolledIndicator.style.transform = ''; + const computedStyle = window.getComputedStyle(this.scrolledIndicator); + const isTransitioning = computedStyle.transitionDuration !== '0s'; + + if (isTransitioning) { + // Add a transitionend listener if the indicator is transitioning + this.waitForTransitionEnd(this.scrolledIndicator, () => { + this.updateValueAfterTransition(ev.detail.activeContentId); + }); + } else { + // Immediately update the value if there's no transition + this.updateValueAfterTransition(ev.detail.activeContentId); + } + } else { + // Immediately update the value if there's no indicator + this.updateValueAfterTransition(ev.detail.activeContentId); } this.isScrolling = false; } } + // Wait for the transition to end, then execute the callback + private waitForTransitionEnd(indicator: HTMLElement, callback: () => void) { + const onTransitionEnd = () => { + indicator.removeEventListener('transitionend', onTransitionEnd); + callback(); + }; + indicator.addEventListener('transitionend', onTransitionEnd); + } + + // Update the Segment value after the ionSegmentViewScrollEnd transition has ended + private updateValueAfterTransition(activeContentId: string) { + this.value = 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 From a9adb82bcbb1154e93cf1805ff05757d0666f13c Mon Sep 17 00:00:00 2001 From: Brandy Carney Date: Wed, 9 Oct 2024 18:05:17 -0400 Subject: [PATCH 39/46] fix(segment): update segment content to disabled when button is --- .../segment-button/segment-button.tsx | 21 +++++++++++++++++++ .../components/segment-view/segment-view.tsx | 2 ++ .../segment-view/test/disabled/index.html | 8 +++---- 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/core/src/components/segment-button/segment-button.tsx b/core/src/components/segment-button/segment-button.tsx index a92fcbbd31a..004c853a7ea 100644 --- a/core/src/components/segment-button/segment-button.tsx +++ b/core/src/components/segment-button/segment-button.tsx @@ -72,6 +72,27 @@ export class SegmentButton implements ComponentInterface, ButtonInterface { addEventListener(segmentEl, 'ionSelect', this.updateState); addEventListener(segmentEl, 'ionStyle', this.updateStyle); } + + // Return if there is no contentId defined + if (!this.contentId) return; + + // Attempt to find the Segment Content by its contentId + const segmentContent = document.getElementById(this.contentId) as HTMLIonSegmentContentElement | null; + + // If no associated Segment Content exists, log an error and return + if (!segmentContent) { + console.error(`Segment Button: Unable to find Segment Content with id="${this.contentId}".`); + return; + } + + // Ensure the found element is a valid ION-SEGMENT-CONTENT + if (segmentContent.tagName !== 'ION-SEGMENT-CONTENT') { + console.error(`Segment Button: Element with id="${this.contentId}" is not an element.`); + return; + } + + // Set the disabled state of the Segment Content based on the button's disabled state + segmentContent.disabled = this.disabled; } disconnectedCallback() { diff --git a/core/src/components/segment-view/segment-view.tsx b/core/src/components/segment-view/segment-view.tsx index 75a20419c80..d7a47b7191b 100644 --- a/core/src/components/segment-view/segment-view.tsx +++ b/core/src/components/segment-view/segment-view.tsx @@ -82,6 +82,8 @@ export class SegmentView implements ComponentInterface { } // Update active content ID and scroll to the segment content + // TODO this is erroring because all of the segment contents are disabled + // in the last example this.activeContentId = segmentContent.id; this.setContent(segmentContent.id); diff --git a/core/src/components/segment-view/test/disabled/index.html b/core/src/components/segment-view/test/disabled/index.html index d5a3cbb3f7b..dd30d3c38c0 100644 --- a/core/src/components/segment-view/test/disabled/index.html +++ b/core/src/components/segment-view/test/disabled/index.html @@ -60,7 +60,7 @@ - All + All Favorites @@ -77,7 +77,7 @@ - Free + Free Top @@ -97,8 +97,8 @@ a - b - c + b + c d From 9494d430cfdd74c8eeccbb878a5a08ecbf79aa50 Mon Sep 17 00:00:00 2001 From: Brandy Carney Date: Thu, 10 Oct 2024 12:04:03 -0400 Subject: [PATCH 40/46] fix(segment-view): continue to search through segment contents for enabled --- .../components/segment-view/segment-view.tsx | 33 ++++++++++++------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/core/src/components/segment-view/segment-view.tsx b/core/src/components/segment-view/segment-view.tsx index d7a47b7191b..892c6e1f280 100644 --- a/core/src/components/segment-view/segment-view.tsx +++ b/core/src/components/segment-view/segment-view.tsx @@ -70,20 +70,14 @@ export class SegmentView implements ComponentInterface { // Find the current segment content based on the scroll position const currentIndex = Math.round(scrollLeft / offsetWidth); - let segmentContent = this.getSegmentContents()[currentIndex]; + + // Recursively search for the next enabled content in the scroll direction + const segmentContent = this.getNextEnabledContent(currentIndex, scrollDirection); // Exit if no valid segment content found if (!segmentContent) return; - // If the current content is disabled, find the next enabled content - if (segmentContent.disabled) { - const nextIndex = scrollDirection === 'right' ? currentIndex + 1 : currentIndex - 1; - segmentContent = this.getNextSegmentContent(nextIndex); - } - // Update active content ID and scroll to the segment content - // TODO this is erroring because all of the segment contents are disabled - // in the last example this.activeContentId = segmentContent.id; this.setContent(segmentContent.id); @@ -169,9 +163,26 @@ export class SegmentView implements ComponentInterface { return Array.from(this.el.querySelectorAll('ion-segment-content')); } - private getNextSegmentContent(index: number): HTMLIonSegmentContentElement { + /** + * Recursively find the next enabled segment content based on the scroll direction. + * If no enabled content is found, it will return null. + */ + private getNextEnabledContent(index: number, direction: string): HTMLIonSegmentContentElement | null { const contents = this.getSegmentContents(); - return contents[index]; + + // Stop if we reach the beginning or end of the content array + if (index < 0 || index >= contents.length) return null; + + const segmentContent = contents[index]; + + // If the content is not disabled, return it + if (!segmentContent.disabled) { + return segmentContent; + } + + // Otherwise, keep searching in the same direction + const nextIndex = direction === 'right' ? index + 1 : index - 1; + return this.getNextEnabledContent(nextIndex, direction); } render() { From f74f1540a97e86814605d67c5ed92b968780362d Mon Sep 17 00:00:00 2001 From: Tanner Reits <47483144+tanner-reits@users.noreply.github.com> Date: Fri, 25 Oct 2024 10:41:47 -0400 Subject: [PATCH 41/46] feat(segment, segment-view): remove percentage based indicator effects on scroll (#29968) Issue number: resolves internal --------- ## What is the current behavior? ## What is the new behavior? - - - ## Does this introduce a breaking change? - [ ] Yes - [ ] No ## Other information --- core/api.txt | 8 +- core/src/components.d.ts | 27 +-- .../segment-button/segment-button.tsx | 9 +- .../segment-content/segment-content.ios.scss | 4 - .../segment-content/segment-content.md.scss | 4 - .../segment-content/segment-content.scss | 1 + .../segment-content/segment-content.tsx | 15 +- .../segment-view/segment-view-interface.ts | 4 + .../components/segment-view/segment-view.scss | 4 + .../components/segment-view/segment-view.tsx | 105 ++++------- .../segment-view/test/basic/index.html | 66 +++++-- .../test/basic/segment-view.e2e.ts | 81 +++++++++ .../segment-view/test/disabled/index.html | 27 +-- .../test/disabled/segment-view.e2e.ts | 66 +------ ...w-disabled-ios-ltr-Mobile-Chrome-linux.png | Bin 1132 -> 16516 bytes ...-disabled-ios-ltr-Mobile-Firefox-linux.png | Bin 1939 -> 23115 bytes ...w-disabled-ios-ltr-Mobile-Safari-linux.png | Bin 1121 -> 16069 bytes ...ew-disabled-md-ltr-Mobile-Chrome-linux.png | Bin 1124 -> 15634 bytes ...w-disabled-md-ltr-Mobile-Firefox-linux.png | Bin 1866 -> 23920 bytes ...ew-disabled-md-ltr-Mobile-Safari-linux.png | Bin 1067 -> 15953 bytes core/src/components/segment/segment.tsx | 169 +++++------------- packages/angular/src/directives/proxies.ts | 12 +- .../standalone/src/directives/proxies.ts | 14 +- packages/vue/src/proxies.ts | 4 +- 24 files changed, 258 insertions(+), 362 deletions(-) create mode 100644 core/src/components/segment-view/segment-view-interface.ts diff --git a/core/api.txt b/core/api.txt index 46452ee06f3..269e43875ac 100644 --- a/core/api.txt +++ b/core/api.txt @@ -1543,7 +1543,7 @@ ion-segment,css-prop,--background,md ion-segment-button,shadow ion-segment-button,prop,contentId,string | undefined,undefined,false,true -ion-segment-button,prop,disabled,boolean,false,false,false +ion-segment-button,prop,disabled,boolean,false,false,true ion-segment-button,prop,layout,"icon-bottom" | "icon-end" | "icon-hide" | "icon-start" | "icon-top" | "label-hide" | undefined,'icon-top',false,false ion-segment-button,prop,mode,"ios" | "md",undefined,false,false ion-segment-button,prop,type,"button" | "reset" | "submit",'button',false,false @@ -1609,13 +1609,11 @@ ion-segment-button,part,indicator-background ion-segment-button,part,native ion-segment-content,shadow -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 -ion-segment-view,event,ionSegmentViewScroll,{ scrollDirection: string; scrollDistance: number; scrollDistancePercentage: number; },true -ion-segment-view,event,ionSegmentViewScrollEnd,{ activeContentId: string; },true +ion-segment-view,event,ionSegmentViewScroll,SegmentViewScrollEvent,true +ion-segment-view,event,ionSegmentViewScrollEnd,void,true ion-segment-view,event,ionSegmentViewScrollStart,void,true ion-select,shadow diff --git a/core/src/components.d.ts b/core/src/components.d.ts index 716e5517f4f..03ca88a4cf9 100644 --- a/core/src/components.d.ts +++ b/core/src/components.d.ts @@ -34,6 +34,7 @@ import { NavigationHookCallback } from "./components/route/route-interface"; import { SearchbarChangeEventDetail, SearchbarInputEventDetail } from "./components/searchbar/searchbar-interface"; import { SegmentChangeEventDetail, SegmentValue } from "./components/segment/segment-interface"; import { SegmentButtonLayout } from "./components/segment-button/segment-button-interface"; +import { SegmentViewScrollEvent } from "./components/segment-view/segment-view-interface"; import { SelectChangeEventDetail, SelectCompareFn, SelectInterface } from "./components/select/select-interface"; import { SelectPopoverOption } from "./components/select-popover/select-popover-interface"; import { TabBarChangedEventDetail, TabButtonClickEventDetail, TabButtonLayout } from "./components/tab-bar/tab-bar-interface"; @@ -69,6 +70,7 @@ export { NavigationHookCallback } from "./components/route/route-interface"; export { SearchbarChangeEventDetail, SearchbarInputEventDetail } from "./components/searchbar/searchbar-interface"; export { SegmentChangeEventDetail, SegmentValue } from "./components/segment/segment-interface"; export { SegmentButtonLayout } from "./components/segment-button/segment-button-interface"; +export { SegmentViewScrollEvent } from "./components/segment-view/segment-view-interface"; export { SelectChangeEventDetail, SelectCompareFn, SelectInterface } from "./components/select/select-interface"; export { SelectPopoverOption } from "./components/select-popover/select-popover-interface"; export { TabBarChangedEventDetail, TabButtonClickEventDetail, TabButtonLayout } from "./components/tab-bar/tab-bar-interface"; @@ -2720,10 +2722,6 @@ export namespace Components { "value": SegmentValue; } interface IonSegmentContent { - /** - * If `true`, the segment content will not be displayed. - */ - "disabled": boolean; } interface IonSegmentView { /** @@ -2731,7 +2729,6 @@ export namespace Components { */ "disabled": boolean; /** - * This method is used to programmatically set the displayed segment content in the segment view. Calling this method will update the `value` of the corresponding segment button. * @param id : The id of the segment content to display. * @param smoothScroll : Whether to animate the scroll transition. */ @@ -4445,12 +4442,8 @@ declare global { new (): HTMLIonSegmentContentElement; }; interface HTMLIonSegmentViewElementEventMap { - "ionSegmentViewScroll": { - scrollDirection: string; - scrollDistance: number; - scrollDistancePercentage: number; - }; - "ionSegmentViewScrollEnd": { activeContentId: string }; + "ionSegmentViewScroll": SegmentViewScrollEvent; + "ionSegmentViewScrollEnd": void; "ionSegmentViewScrollStart": void; } interface HTMLIonSegmentViewElement extends Components.IonSegmentView, HTMLStencilElement { @@ -7533,10 +7526,6 @@ declare namespace LocalJSX { "value"?: SegmentValue; } interface IonSegmentContent { - /** - * If `true`, the segment content will not be displayed. - */ - "disabled"?: boolean; } interface IonSegmentView { /** @@ -7546,15 +7535,11 @@ declare namespace LocalJSX { /** * Emitted when the segment view is scrolled. */ - "onIonSegmentViewScroll"?: (event: IonSegmentViewCustomEvent<{ - scrollDirection: string; - scrollDistance: number; - scrollDistancePercentage: number; - }>) => void; + "onIonSegmentViewScroll"?: (event: IonSegmentViewCustomEvent) => void; /** * Emitted when the segment view scroll has ended. */ - "onIonSegmentViewScrollEnd"?: (event: IonSegmentViewCustomEvent<{ activeContentId: string }>) => void; + "onIonSegmentViewScrollEnd"?: (event: IonSegmentViewCustomEvent) => void; "onIonSegmentViewScrollStart"?: (event: IonSegmentViewCustomEvent) => void; } interface IonSelect { diff --git a/core/src/components/segment-button/segment-button.tsx b/core/src/components/segment-button/segment-button.tsx index 004c853a7ea..80cb243c301 100644 --- a/core/src/components/segment-button/segment-button.tsx +++ b/core/src/components/segment-button/segment-button.tsx @@ -44,7 +44,7 @@ export class SegmentButton implements ComponentInterface, ButtonInterface { /** * If `true`, the user cannot interact with the segment button. */ - @Prop({ mutable: true }) disabled = false; + @Prop({ mutable: true, reflect: true }) disabled = false; /** * Set the layout of the text and icon in the segment. @@ -91,8 +91,11 @@ export class SegmentButton implements ComponentInterface, ButtonInterface { return; } - // Set the disabled state of the Segment Content based on the button's disabled state - segmentContent.disabled = this.disabled; + // Prevent buttons from being disabled when associated with segment content + if (this.disabled) { + console.warn(`Segment Button: Segment buttons cannot be disabled when associated with an .`); + this.disabled = false; + } } disconnectedCallback() { diff --git a/core/src/components/segment-content/segment-content.ios.scss b/core/src/components/segment-content/segment-content.ios.scss index 026e16b1f97..aee6789be18 100644 --- a/core/src/components/segment-content/segment-content.ios.scss +++ b/core/src/components/segment-content/segment-content.ios.scss @@ -3,7 +3,3 @@ // iOS Segment Content // -------------------------------------------------- - -:host(.segment-content-disabled) { - opacity: $segment-button-ios-opacity-disabled; -} diff --git a/core/src/components/segment-content/segment-content.md.scss b/core/src/components/segment-content/segment-content.md.scss index 1432941bc19..ea64ce72d8e 100644 --- a/core/src/components/segment-content/segment-content.md.scss +++ b/core/src/components/segment-content/segment-content.md.scss @@ -3,7 +3,3 @@ // Material Design Segment Content // -------------------------------------------------- - -:host(.segment-content-disabled) { - opacity: $segment-button-md-opacity-disabled; -} diff --git a/core/src/components/segment-content/segment-content.scss b/core/src/components/segment-content/segment-content.scss index 00ca64f3041..464402b41ff 100644 --- a/core/src/components/segment-content/segment-content.scss +++ b/core/src/components/segment-content/segment-content.scss @@ -3,6 +3,7 @@ :host { scroll-snap-align: center; + scroll-snap-stop: always; flex-shrink: 0; diff --git a/core/src/components/segment-content/segment-content.tsx b/core/src/components/segment-content/segment-content.tsx index a4db11101ee..3d17217f437 100644 --- a/core/src/components/segment-content/segment-content.tsx +++ b/core/src/components/segment-content/segment-content.tsx @@ -1,5 +1,5 @@ import type { ComponentInterface } from '@stencil/core'; -import { Component, Host, Prop, h } from '@stencil/core'; +import { Component, Host, h } from '@stencil/core'; @Component({ tag: 'ion-segment-content', @@ -10,20 +10,9 @@ import { Component, Host, Prop, h } from '@stencil/core'; shadow: true, }) export class SegmentContent implements ComponentInterface { - /** - * If `true`, the segment content will not be displayed. - */ - @Prop() disabled = false; - render() { - const { disabled } = this; - return ( - + ); diff --git a/core/src/components/segment-view/segment-view-interface.ts b/core/src/components/segment-view/segment-view-interface.ts new file mode 100644 index 00000000000..c6212152ed5 --- /dev/null +++ b/core/src/components/segment-view/segment-view-interface.ts @@ -0,0 +1,4 @@ +export interface SegmentViewScrollEvent { + scrollRatio: number; + isManualScroll: boolean; +} diff --git a/core/src/components/segment-view/segment-view.scss b/core/src/components/segment-view/segment-view.scss index e9eacc5a24f..a41030992f6 100644 --- a/core/src/components/segment-view/segment-view.scss +++ b/core/src/components/segment-view/segment-view.scss @@ -25,3 +25,7 @@ touch-action: none; overflow-x: hidden; } + +:host(.segment-view-scroll-disabled) { + pointer-events: none; +} diff --git a/core/src/components/segment-view/segment-view.tsx b/core/src/components/segment-view/segment-view.tsx index 892c6e1f280..c09b5797162 100644 --- a/core/src/components/segment-view/segment-view.tsx +++ b/core/src/components/segment-view/segment-view.tsx @@ -1,5 +1,7 @@ import type { ComponentInterface, EventEmitter } from '@stencil/core'; -import { Component, Element, Event, Host, Listen, Method, Prop, h } from '@stencil/core'; +import { Component, Element, Event, Host, Listen, Method, Prop, State, h } from '@stencil/core'; + +import type { SegmentViewScrollEvent } from './segment-view-interface'; @Component({ tag: 'ion-segment-view', @@ -10,8 +12,6 @@ import { Component, Element, Event, Host, Listen, Method, Prop, h } from '@stenc shadow: true, }) export class SegmentView implements ComponentInterface { - private initialScrollLeft?: number; - private previousScrollLeft = 0; private scrollEndTimeout: ReturnType | null = null; private isTouching = false; @@ -22,65 +22,37 @@ export class SegmentView implements ComponentInterface { */ @Prop() disabled = false; + /** + * @internal + * + * If `true`, the segment view is scrollable. + * If `false`, pointer events will be disabled. This is to prevent issues with + * quickly scrolling after interacting with a segment button. + */ + @State() isManualScroll?: boolean; + /** * Emitted when the segment view is scrolled. */ - @Event() ionSegmentViewScroll!: EventEmitter<{ - scrollDirection: string; - scrollDistance: number; - scrollDistancePercentage: number; - }>; + @Event() ionSegmentViewScroll!: EventEmitter; /** * Emitted when the segment view scroll has ended. */ - @Event() ionSegmentViewScrollEnd!: EventEmitter<{ activeContentId: string }>; + @Event() ionSegmentViewScrollEnd!: EventEmitter; @Event() ionSegmentViewScrollStart!: EventEmitter; - private activeContentId = ''; - @Listen('scroll') handleScroll(ev: Event) { - const { initialScrollLeft, previousScrollLeft } = this; - const { scrollLeft, offsetWidth } = ev.target as HTMLElement; - - // Set initial scroll position if it's undefined - this.initialScrollLeft = initialScrollLeft ?? scrollLeft; - - // Determine the scroll direction based on the previous scroll position - const scrollDirection = scrollLeft > previousScrollLeft ? 'right' : 'left'; - this.previousScrollLeft = scrollLeft; + const { scrollLeft, scrollWidth, clientWidth } = ev.target as HTMLElement; + const scrollRatio = scrollLeft / (scrollWidth - clientWidth); - // Calculate the distance scrolled based on the initial scroll position - // and then transform it to a percentage of the segment view width - const scrollDistance = scrollLeft - this.initialScrollLeft; - const scrollDistancePercentage = Math.abs(scrollDistance) / offsetWidth; - - // Emit the scroll direction and distance this.ionSegmentViewScroll.emit({ - scrollDirection, - scrollDistance, - scrollDistancePercentage, + scrollRatio, + isManualScroll: this.isManualScroll ?? true, }); - // Check if the scroll is at a snapping point and return if not - const atSnappingPoint = scrollLeft % offsetWidth === 0; - if (!atSnappingPoint) return; - - // Find the current segment content based on the scroll position - const currentIndex = Math.round(scrollLeft / offsetWidth); - - // Recursively search for the next enabled content in the scroll direction - const segmentContent = this.getNextEnabledContent(currentIndex, scrollDirection); - - // Exit if no valid segment content found - if (!segmentContent) return; - - // Update active content ID and scroll to the segment content - this.activeContentId = segmentContent.id; - this.setContent(segmentContent.id); - // Reset the timeout to check for scroll end this.resetScrollEndTimeout(); } @@ -118,7 +90,7 @@ export class SegmentView implements ComponentInterface { } this.scrollEndTimeout = setTimeout(() => { this.checkForScrollEnd(); - }, 150); + }, 50); } /** @@ -127,20 +99,21 @@ export class SegmentView implements ComponentInterface { * reset the scroll position and emit the scroll end event. */ private checkForScrollEnd() { - const activeContent = this.getSegmentContents().find(content => content.id === this.activeContentId); - // Only emit scroll end event if the active content is not disabled and // the user is not touching the segment view - if (activeContent?.disabled === false && !this.isTouching) { - this.ionSegmentViewScrollEnd.emit({ activeContentId: this.activeContentId }); - this.initialScrollLeft = undefined; + if (!this.isTouching) { + this.ionSegmentViewScrollEnd.emit(); + this.isManualScroll = undefined; } } /** + * @internal + * * This method is used to programmatically set the displayed segment content * in the segment view. Calling this method will update the `value` of the * corresponding segment button. + * * @param id: The id of the segment content to display. * @param smoothScroll: Whether to animate the scroll transition. */ @@ -151,6 +124,9 @@ export class SegmentView implements ComponentInterface { if (index === -1) return; + this.isManualScroll = false; + this.resetScrollEndTimeout(); + const contentWidth = this.el.offsetWidth; this.el.scrollTo({ top: 0, @@ -163,35 +139,14 @@ export class SegmentView implements ComponentInterface { return Array.from(this.el.querySelectorAll('ion-segment-content')); } - /** - * Recursively find the next enabled segment content based on the scroll direction. - * If no enabled content is found, it will return null. - */ - private getNextEnabledContent(index: number, direction: string): HTMLIonSegmentContentElement | null { - const contents = this.getSegmentContents(); - - // Stop if we reach the beginning or end of the content array - if (index < 0 || index >= contents.length) return null; - - const segmentContent = contents[index]; - - // If the content is not disabled, return it - if (!segmentContent.disabled) { - return segmentContent; - } - - // Otherwise, keep searching in the same direction - const nextIndex = direction === 'right' ? index + 1 : index - 1; - return this.getNextEnabledContent(nextIndex, direction); - } - render() { - const { disabled } = this; + const { disabled, isManualScroll } = this; return ( diff --git a/core/src/components/segment-view/test/basic/index.html b/core/src/components/segment-view/test/basic/index.html index 0eefd5a5474..02321d28c4e 100644 --- a/core/src/components/segment-view/test/basic/index.html +++ b/core/src/components/segment-view/test/basic/index.html @@ -16,6 +16,8 @@ @@ -60,24 +62,11 @@ Value - - - All - - - Favorites - - - - All - Favorites - - Paid - + Free @@ -90,6 +79,47 @@ Top + + + Orange + + + Banana + + + Pear + + + Peach + + + Grape + + + Mango + + + Apple + + + Strawberry + + + Cherry + + + + Orange + Banana + Pear + Peach + Grape + Mango + Apple + Strawberry + Cherry + + @@ -114,7 +144,7 @@ currentValue = 'value'; } - segmentView.setContent(currentValue); + segment.value = currentValue; } async function clearSegmentValue() { diff --git a/core/src/components/segment-view/test/basic/segment-view.e2e.ts b/core/src/components/segment-view/test/basic/segment-view.e2e.ts index 5d7457743ad..1a7279487fd 100644 --- a/core/src/components/segment-view/test/basic/segment-view.e2e.ts +++ b/core/src/components/segment-view/test/basic/segment-view.e2e.ts @@ -89,4 +89,85 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => { await expect(segmentContent).toBeInViewport(); }); }); + + test('should set correct segment button as checked when changing the value by scrolling the segment content', async ({ + page, + }) => { + await page.setContent( + ` + + + Paid + + + Free + + + Top + + + + + Free + Top + + `, + config + ); + + await page + .locator('ion-segment-view') + .evaluate( + (segmentView: HTMLIonSegmentViewElement) => !segmentView.classList.contains('segment-view-scroll-disabled') + ); + + await page.waitForChanges(); + + await page.locator('ion-segment-content[id="top"]').scrollIntoViewIfNeeded(); + + const segmentButton = page.locator('ion-segment-button[value="top"]'); + await expect(segmentButton).toHaveClass(/segment-button-checked/); + }); + + test('should set correct segment button as checked and show correct content when programmatically setting the segment vale', async ({ + page, + }) => { + await page.setContent( + ` + + + Paid + + + Free + + + Top + + + + + Free + Top + + `, + config + ); + + await page + .locator('ion-segment-view') + .evaluate( + (segmentView: HTMLIonSegmentViewElement) => !segmentView.classList.contains('segment-view-scroll-disabled') + ); + + await page.waitForChanges(); + + await page.locator('ion-segment').evaluate((segment: HTMLIonSegmentElement) => (segment.value = 'top')); + + const segmentButton = page.locator('ion-segment-button[value="top"]'); + await expect(segmentButton).toHaveClass(/segment-button-checked/); + + const segmentContent = page.locator('ion-segment-content[id="top"]'); + await expect(segmentContent).toBeInViewport(); + }); }); diff --git a/core/src/components/segment-view/test/disabled/index.html b/core/src/components/segment-view/test/disabled/index.html index dd30d3c38c0..d19722de6dc 100644 --- a/core/src/components/segment-view/test/disabled/index.html +++ b/core/src/components/segment-view/test/disabled/index.html @@ -64,11 +64,11 @@ Favorites - + Paid - + Free @@ -81,28 +81,7 @@ Top - - - a - - - b - - - c - - - d - - - - a - b - c - d - - - + Bookmarks diff --git a/core/src/components/segment-view/test/disabled/segment-view.e2e.ts b/core/src/components/segment-view/test/disabled/segment-view.e2e.ts index 6582b1e8c31..071e3604d48 100644 --- a/core/src/components/segment-view/test/disabled/segment-view.e2e.ts +++ b/core/src/components/segment-view/test/disabled/segment-view.e2e.ts @@ -7,28 +7,9 @@ import { configs, test } from '@utils/test/playwright'; configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => { test.describe(title('segment-view: disabled'), () => { test('should not have visual regressions', async ({ page }) => { - await page.setContent( - ` - - - - - Free - Top - - `, - config - ); + await page.goto('/src/components/segment-view/test/disabled', config); - const segment = page.locator('ion-segment-view'); - - await expect(segment).toHaveScreenshot(screenshot(`segment-view-disabled`)); + await expect(page).toHaveScreenshot(screenshot(`segment-view-disabled`)); }); }); }); @@ -38,36 +19,7 @@ configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => { */ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => { test.describe(title('segment-view: disabled'), () => { - test('should show the second content when first segment content is disabled', async ({ page }) => { - await page.setContent( - ` - - - Paid - - - Free - - - Top - - - - - Free - Top - - `, - config - ); - - const segmentContent = page.locator('ion-segment-content[id="free"]'); - await expect(segmentContent).toBeInViewport(); - }); - - test('should scroll to the third content and update the segment value when the second segment content is disabled', async ({ - page, - }) => { + test('should keep button enabled even when disabled prop is set', async ({ page }) => { await page.setContent( ` @@ -82,20 +34,16 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => { - - Free + + Free Top `, config ); - const segmentContent = page.locator('ion-segment-content[id="top"]'); - await segmentContent.scrollIntoViewIfNeeded(); - await expect(segmentContent).toBeInViewport(); - - const segment = page.locator('ion-segment'); - expect(await segment.evaluate((el: HTMLIonSegmentElement) => el.value)).toBe('top'); + const segmentButton = page.locator('ion-segment-button[value="free"]'); + await expect(segmentButton).not.toHaveAttribute('disabled'); }); }); }); diff --git a/core/src/components/segment-view/test/disabled/segment-view.e2e.ts-snapshots/segment-view-disabled-ios-ltr-Mobile-Chrome-linux.png b/core/src/components/segment-view/test/disabled/segment-view.e2e.ts-snapshots/segment-view-disabled-ios-ltr-Mobile-Chrome-linux.png index 09790806a7f6bdb3209e102338805d38e6fe6f83..58fa6b0d0ab679e48ed22800a2ef8b91cf5a2767 100644 GIT binary patch literal 16516 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 V3eg!X^1$MQ!PC{xWt~$(69D3Y1Tp{s diff --git a/core/src/components/segment-view/test/disabled/segment-view.e2e.ts-snapshots/segment-view-disabled-ios-ltr-Mobile-Firefox-linux.png b/core/src/components/segment-view/test/disabled/segment-view.e2e.ts-snapshots/segment-view-disabled-ios-ltr-Mobile-Firefox-linux.png index 4dcb813e3ae693df9accc96dfd82efea2c99b330..6ad473ada44c5d8f28189960506f5de47ae145a4 100644 GIT binary patch literal 23115 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

>+8L?RTvbid~qKFvg3R6Pl15Lx07 zelqkE-tw4B61^_=T=R8Ya#wHg43wM+0tvXs6SJ9M)A7OJtJi6yLi!&9WL6o77M>R zRl9)r5^X!|$NcrFwzso%n^4jVGvR>k=SB?VIUc#6>f{s2a}FYxJ*ywGqKBGk6HxAm z(n*=}?Mkx8obOnw_0dB+MUGaH>7KQb+X;=GDr|*OSAE#*9A;BfpMywGyNlzp(7rL- zVUu6%*&`H!`PuOQHJmXwBSgsS7qgZlpYcL&B{)h1{OZ~K=A2(*!G!*6pP2FKF)i{w zW7uMAY8_|xDom95qmJ3?puXX7B;D?2{CM@C>oaM^!MA0<7{XtTu7*Yd*QR-p3<75@9b+AoJ0uNP;e9NwI)WC1bR!Sm&^V2CExZhGXar@!2d4IfnWfBNnJZzO0L zLvLL&i{_B|4!Zif`

w*Ml|{{rU! f$Kguw6|t$Agr%-wts?l7Xgp=4x~~ zh^=Z);?`*DSkU0++YvawXZn#97k@mcU)?X2-)Sge>el1MASk+X+O{ooGp%L6TRxSF zzT_Q|qI!SN2ZQ2sQGdgV>+Yt{PkeAX{`cQUt_=)~Oe|r75&}YX`WylZ4hg!9iVw@1 zL88}0fP$7F!3X~oFGlYOoF@MKeN9Hf0v^8iI=9=(b2`@T-xHB`u0Gi9yNu>y;r44b zWrg2@UdC959^>D;apC9R20&9dj&NO>X-sw&&}&to!^+0Az-ZOyh0Ei+@V@zdm7| zEB^Oi(aX|x+r>ZSIxkNBQ}9{*|5}EdRg0aHUbmfdXkcjMXbg3J6!x%HTKv?T`|78T zTr9tR+y88K=D(=OS-Yc}mxQyqtDidYqp_N4N%i0UT}+j0_L-jL7T`1Q*OH#M)^Yjfws*_S>c4)!wDVK8yEBVmcPRNCt&94?ep7?^WTfHowCWIdgGlRuWwgbzdBp? ze^KRKS)skHGmUD$ectr=qxk7>?(=i+=bN2OKUW+2>$JDiBS-)1KRE>yHn=ugD;gFr z?6Lj$>D0?tYnLqDrG9Z|*{*dQd{a06)n6O9EMBet_l~^0{XuSO^M1v9l&qVV6)v~C zfA9TuHhFuW>CN3T>Bgf6GJfE&ygEN`*H4Z&CypGPDth27_n)BjO>uY5Pm3$xrSpB4 zsrR&74w>^BqI6Z#{{P*$PA2Wtk%!yg{fyid{QPuG?fJLMIs{jPg7GZ(o$qCr?=F9P zb#F^rebSP5>L!gFatpRhsp8K1btXM~oysit?Ag4pJJav&GgryFVDg@`^DO_li7Xrf z9Rd;M9&6tC&(YaE|Mvzxowi%|X8mn_x!Koa&Av%5{N`#Gi`Q%I?%NkGzjW{CqkGci z7gs(B^Kj{j;01%$zoh@2+~o>W9C--A~J3yffDH&xuB(-{;Gnlv0j#fwI$QCh33t|9+j``|HQ6 zB;L-q(yMmeiC!UT7{SKIW?nLfUs%DR;lN?x8wDOVd%{B1K*=|QQBhELog6UT3vqIE zx*Wcw1!OK_ZE@MMqY4-t9x4hdLcHFqK_Me1AawE3D?tSZg^3LlJWO=f3IUl>4jwMu xtKESnxiC2?UEHw6Yt+!uFda=&7&+=6vyA1_$JIjoGk}#XgQu&X%Q~loCIC)E5PARr diff --git a/core/src/components/segment-view/test/disabled/segment-view.e2e.ts-snapshots/segment-view-disabled-ios-ltr-Mobile-Safari-linux.png b/core/src/components/segment-view/test/disabled/segment-view.e2e.ts-snapshots/segment-view-disabled-ios-ltr-Mobile-Safari-linux.png index 9574679268db28e89e6410a3d0a08be782606b77..521946d9f1b56a48a8a40ce60a8c28cd8713d96a 100644 GIT binary patch literal 16069 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⪒n1%koUpY) zIf-*XZ<5b=3P@x}^b$4!H6Et0LB7TnnRC^US(Q`dM0lTnL4ze$5kC$fG~u}|e)Q|j z%fXM84fgR0@kvQ9%f|t`z~H*v$Qssq#Tsl));<&vaD#BdWDbM}j6(T^g%!F*)yM5F z=uG*wTaBk&{QUgF!apCF!}tyF*xTFdHkFDXD<@Pp~`y zEc%LI+*V|nsQ1_fo@cPcsA+6$44PftLlnOHM@0ab1eyfQ?qb0g<_JFMrb&3mLhC4>8-N8inAG~KZzSz`zhf)UNR*S>WVVW2N5`77_OQQCPf6niaS(<&en z^Y8iQkP?ta)A53@&XcE)lQLW;SfOyXPr3l_k~t zZ0_jYp~zB}{RjLC!AZN6CjoR{IWu#oKPCz-&5doi!j+Szo-(4<_$<>utkqXGGCuXG zJDMk=xP^=Mh4O&>-|5`{Du?>01Jr0EoiIu|DfBsbvL{TB1WjSDc7iJ*dORt72Lcb7644` z>rK}EFVprmzo1_J)wI{|o=rC)u~GriJAZxzr905b=8|e?N%f?k&nJ-(u!K8rjD5KIrIBeF z4Rz<$@#w|4X`6_r6xIsK?Km3h$m?+*)bGgmp`Z4q=jRnDQHqp}@72z4PC4zk+o)$f zk+~s;&3Z9*hL4VhR_tH-Z?Ne9xr_V%O4|QBw*&MX!3}7y)dcXdE6RWngW^^}?wnxH zcFFFI*CP1s4uI!aV(-T*U{{@Cq3tm#gZvF?HOc73*~<$9Na|1P zilz)8vd=)S$E!Z03sA8?8~RzBRi(mmu@+MYvtY#I$T8z9ks?4O^$r%X=NXY}Ngc)o zF`6KWv?}5n^)?Z^CnH%R_hk^_xDFf zM{`=1Pi!bDDQOi{K8xWXe`0LRP%gj91^lOV`8dtZzLTRPCMKpDS2?#ksmg9Jts{cF zEl4o_ZhTQNY-MUeV3%_6l+S&rnSxS}T-o3n{B4`~C z9z58~2QEw8>^mWd6#U4Cyu7PxYhq$z(A*8gR_WHp%1qBs_Pe^fVNBedsJ7G6(lRhe z)#r)~4*t+fleVO%8~{^+x2vnGjZKjv3obtX#$YZ%sxJ)cM@L5h@>cgfVeSLKP^-T= z5IwDP=yey_Y65#9k=tm*suHN2TLc6WUI!dg-5H~6E5&m_cYR;n0ntcCS{l$8%JaKe zf?)~w{rfARyqK9S0F%Dpcnl23O}yJtNlDL)j8w2*x-dG|;ZktPF~wwOX9olXfV=~g zfv`czD2xMpTQiq10+&r}P*PGB(^`5tM^<4cDd7ekGPzNLQMj zc{#GlOyEXOPEQBBBF-nBAHQy7JK6HPoOnJkR{`(J}Z@-E8F&DH1X?e1m{_=gb?rNH04H8(q(p}`gw7KY8>e@DO3yB3&&;gDjw zmjny&$?xWRIyinwtJ2xw4jirbKaF+ge_`I~-aGeeE!&RE# z#_LTBFZrk@#_p`T7HU~{(dQ0_;PE26yCcds40Xlt9&AhqSp2x1qc+Z&hO1Gn|0L74 zCMXkicFQwHYs=}3u}+4%H`sQMeL3MY#xurt8&hEUBPJ+j;U-cYSZQR%fv5Dgv71Mj zAB?I+9Y**QKXAWALu+-IED@pIp0vl7BrFYWiLh<3Ls>@2F%j45_c+kRED%SK)YW?4 zK~s$CS|rXUS#08$gHtbZ=;4 zZ^tiH_MI*2S&^0`;ZtTnSAr2)+$H_7{g>hfg@q2YrD$j=v-r&aUsdRTr=k6i_3ZzI zD?ztFEA4vDpX83)@WUYmsg|R4&+f>8VyCz>d*Em~y2#LLQ#WE{0+rZQ_3$Q+7{J6) z&8M2tMylkCUy{(!=xndN0I*C9Ql!#|;F6%FUyZ6ZK^cI)xARPmRJ~)R)0V{+ z)STo3Ih`$fY*yAB`~^rgvbiPhShV8)wPo4k<$aKF>PGc;vjPSqhqq1K;JO-GXiso(iL{qs(aKG9%7wJBY# z;%3%1PqJQ>3`a>p_y(EsXd2e6iRW7iEltSi)~abH!@1Z}(QI zX%w&?=p9e)3ZRttU0uV&B>cAr;xw?~@MW<44@b#}ucyv z{n4RU)YPzv*+>45`D1G0fA<9Z_TNZSJq~hYHw%{kuJbZg=DpBNxh-!=!e!F-Ci^wP z2&XglXgMfAmjn0v;+IpoV>97|p zPI;x<{FR}=m94=xH95I#PI-Q%1$J9FO+{@w_lSL-*vPzR#Ya`C%bD24O#DjUjYX2M zP^+N-1u%L&l!$o!@BbiF0s;cWXiTZ^g08b>a54G#EB?2L*{!We;wfAIJIl_(X-nq3 zlm;)q`E2Ss8^|LL>1#cC+=SatK=$vms&uz?Mh}t71oji>tts{R@ZGw>eivqJ&jsB% zzum(_xBcKv4p34ew8`|v$nS{uJ}JfRSfru0r3%MflQmFej0T?3lrw}#t3cCV;uhD^ z5qptVfq__pNc0tp+JYrX$aJJs`dUk#D-q>+$BhwqR7>=`QmQQ${$WD&9sx!CiO@zO zX0Fvt+W6ffB_}t$5}_)sYiny+Vpp`t_d%hBGpa0K~WPe-5DX8umvY zT)A7kca;#q!&V0M?hY?st`rgU0<)~LQpoH!w6PZb^|_$q!go*>adB~JN$l3N?WPegnSa`NKsDjNv9>xsh zW=6*BNRZpnALFI_0{S~(JOa+&A*_1Bd|+_!CO%b#`}Q0-B3PN38@Ai1olk6h>VX)t znXa#0+yn9TBWUL;vmW9P9|B=l7M|mBuKsxBM)me=5WR9TUkIV&TuaCmv4!O4ynjC* z*Omf{0gNCiq8_{q3*gttk_o2*9`+sOLtBR=s_@@m{=T02pzap;F&Gv+8+?I%@$ru=e!Rc0{t=H(bZmJ3Bpwu)D0Zt9|pzEm#})s-J}g%Rg?J(}N8pk1@j0aR)SBcwbhfWKs$WS%C*w z3_zZ}2f_)kXj>$`-*O*=umjj@fQqGqh(6(NaBg6x7jiU%*8_bKa5m^#H8zI|6t$U2=wKuqLGcuTg(<9yYWCa`$@ljDv)zt@u4!btMc|c4@r_9~n(sC7kUfvkr zB=9lxJtk;vYrCQ~BlhDCM76A~tkOJwC&8rhz_e|BvNpCyT}$g=%rvsKwY57<3B^7?v>yeRzD|?xIhceZXhi_6lXDx2dh9HOQE+qVl>UmArF($v4v38v^kf@8g~I^>r9BK*JzG7<#T;QS)bTKOyg<(vlLprD=(3 z(`FCI+$RYf9-!KSSj-e^k}mE81HrN;lHbPjaLNsox-?*>A>D`(KaX`%{{xXAlyK*0 zKJxVFh(^%<7w8RVO_!%l2a!CWGF;G=fBp>GNfRtZIJLk9@V_w7+VSfpian}hls5ul zeP^*tyIB9YTVMu{Rya<4q`bVG+pzu!D&Nf;H<(#iz_%D zN}3l00o6hmIpRlMy3tI4chuVTmj}{5Lv1{Nv_I-j?Nf( zGw8sp3G*zhs$u>6#KdYS_rM*%7by(gGqRc!5uZK%TAy1SK%wBS8l z!m7U)gV_-_$I8kItji3zl^V}>p0sPi?p&Lw-h#@euBOJNU-K8%%U;t*tgK7m31!zV z-rL`&<$pC+@p2IAZZy12eEj}s)5Rk}L3fA}JlT48xGShBFpuArdFTG?@0+j>7T!;{ zs)QHI4P05-K&ypn0<+!bkEDmr1a9C1c<~|+ygIvkYok{t#qgCOP^fMA^$-aE-JQok z_TRXYLFYktP`=!>87pOg9Asf(0pJ}d2tF=@$1*G&=WABQW@ZB0 z-<~VeH$0F)Gx+%Nl9=PIoGu+={vmjH|# zH#!z~e*E|WbQv7*u3(0x19@bBbr^CCNEVp;b0W9k&fw$)N|0jsV)Qq(imMjT?p@(m z&^-Ho5&}uEr zt`1P?%)vba2?bMngpv9L_v%alm#>wxK@5gujq8T4mDMT;hFA=stNz*DJza=Z!D7IC zbCqQ8`E7Km2k#eYp+LATAFsC>{&X<;0^lEbG5s2#BkfwqeigK?T7V@1<b~fIZ=q z4?jW)UcA+03-Sk8g#A3$imIK34IuXc3oVPiD$^n7&?$9U8v#-jSRr!qw%xOnebDR0 z7?>VB=x=YgyfWoPfo2F^#*$QlMA&AuwC7+?1&b*NYTSHpi$04u_?iKKrI;pKo0<7- zX2uU1Feq)1l?B5cw$!vtd6+Y z*co2hoa9~5ftz3@Yib1T?6$xw8cYaV^3QfVz0np+v723W(bblT&b}&ECnuN~7>Xde zLRWU?&i{|Ztiw&uLE1P4b6wq^fTL)*$tsQE@xb2|84;nZs90E36doM>!qj4k`9Bi2 z=Em?=K7ItRE0js-VqIpRLDxJeC~3sGb?ab%e|T*lddXELfRYRm8A7?)r7DkB+ZiE( z^|`=heF|pP-%Cq?ba+loT)pgRs=W?v-+Z-)c3NcAbOC~W-E<0+&=2bZd(UsM!2vQJ z8`~$NCf{<4ZUwMS9bP^r5Q~eCU!0$x|M^&NqIqiS9ITv%O};*0K>{NaAQW;g{joOo z$PJIe0ZVYph`{m!+``9f2K{o;@%W<>68Z_?DiLr8fzLB?6cqxggL(Gx@l7@7MfEpM z#|-}9N@a^z$aq@e~)7STFYs>G{_2WRK{O9pk81nZw5dS7?Y&R5d5Zq0f$78ffQ4D+Kdl~~l5 z8l`Lh(FLo@4EzSyUA-I}%34iUz{BU2Fw@g+<>S+!kYiQ#1z1m5dp#;!)Kp_`;99J6 zkP}nL(lHoHYdi&0Ex@D?KoaCrGgZ}ocss2k-QkP)!eX~m5MYMMWbK;qUBVJ8M9kow z!OEDcoVxSp&(X*<84IE=P zB1nX-J2YHg%0ISIIxAgB(5_ti?LMt$7)3G3s+RH8b@R)IH3oUHzdO5A#nYu3lkrac z3%hD56X!PN6YNu)jHd;-$_5iX!UQa7P_8_&zRQy#4# zC&lV9RP#nPk$9eO)mAnr?znjl=yH5KS!HAIWgoUOvWWr(><#>sVr1o$M{LK`VAtSi z?=j1&^(SWHklaXmhHPbR=0tW@xKlwUu)~)Xz?wS_CB_apzPZ;p0Tf7|%QTipp4J z6R)%i7FTn2wQx(ecRm$-;^;<=a2pd&nX4PAry?Cx7dMNaeuB5_#M!~bQ;O*#3m^<@ zjdPk!nX+;@+$k<99@6k4QFZ31uPX$qs$WJUk-TdGCswlLY5uyFwTGmkT>AGYaUXCH z#BQ&)I=P~{rv_2QbqWU0jQN*+u2n$x?RF1qX)ah=)OdMz#(!G<9P=qA^Wsh5dnFm4 z$6^dpGf%mrJI~%cuIZT!$27o~UwtXXsFfHG8ovNRZ_H#t=DoI=SdREkFhrsp^Y5{; zyDp2-%2pnAr``gNc|)-<2U~Knt3v>8$g-ZMu6LNk zM=8es(NV~Z_@4$ju3Hom%j6VTc!(I!YuK!+s9GWfhedg-*=i!s_X+OTOWpp@xoeZv zlfA-wSVFHcw3zVT*R)C#&?Y6LqM|M&51XyrQXhkoezJeadLFo zV#&-5K8`Aw17$j8>vWM|VKg13!NnRdR;aZU!jIe+F0W2(8x)yMBf|8_YAjaPR@Ktf zBK8`cN_8*1UW!(tw0Dn&b%kuaB%|tMS#e}I&M?- zkG4znO*O0MtL<7mH1OA8OJt7+w{?U$RWcQ_buloTIRB@Cx}N6jd$rtx&lAEUl@?N} zJji$c&)r7dF$4D_Xn7UC$x~Tb(>%m-JuTv(zx{V*c3KS*}n=yZtPr>odD zXD!70EQp>pBl^79T>nRdcSqMux;6+y&0%L%+d2kXZ1{=yhiy~67KEK2Fy-G|9Ts3aJ)<=6Nb-}*dtRNm z@SD`kh#|oJ=tzcny44{?ktH%XguS!lhv2jO@f`@plhAKlvU1j5Zzl>z2I{R_M?c52 zxoj} zo3rn$#rzG)DE4FmJ*LciSj2mI$Y$LE+KgdslwgGQye3b?yltIqKvYb9flEBuIWZ>6 z%M-`{sa6y_)-{9tTIuep-!y{X9F|oNgDRP7IM-eEd`A|cen-qpscPKQoIN+KKfk6g z#`T;w2BSK9xO11hT#-flo+i!oGWy1Ae1V$q1;@Ag(WEHShmFwDiAf&Hsg8@&1du=l@>mU-?P&|5m}=xkUdX Y8S)o-I{`lYE*(u?T3MVO1B;la zi(^Q|oVR!Gc1wp!G<^I%$8z!+mDy`&zq__pW9_XlZl$jBrVIb0KX|z;T;b}SeXuU# z?Xlb}vpl28XO3IOoslx^eHT{tRn>0)Y=g;ljax*O?g($a@Ge zxX$Uga`Z}gWpU1t8)3(HeNmacym@_Z>0IvjYwF71dtcvs;a<(xEorP9zRl@4bDaPA zV)mq-4i65cN0;ipKl**VUGs|2zfV@HXZ0>FdoPj>R2K7DXsel-{(XUo0#3HlzUYSy*5Bs(?x)XBI_wR>+~dNZqEe(u*V4A;&C zPO6#eU-!$&#l=UYeDde;+kegL>$A?R;3w~-7|yt^T?f^GWd?(%tDnm{ Hr-UW|7@M#G diff --git a/core/src/components/segment-view/test/disabled/segment-view.e2e.ts-snapshots/segment-view-disabled-md-ltr-Mobile-Chrome-linux.png b/core/src/components/segment-view/test/disabled/segment-view.e2e.ts-snapshots/segment-view-disabled-md-ltr-Mobile-Chrome-linux.png index ce5c69da743f06dea2a01d1290d8c2d5e8174e45..e192269dd6fdd971ce009c4be87252da47bbd07f 100644 GIT binary patch literal 15634 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 literal 1124 zcmeAS@N?(olHy`uVBq!ia0y~yVC)34Q#hD_9DcyW%s}T>j*<$lZVTv$M?G) zHugpIjVZmbEVx}FDi(bZFb6vaD{pg1U60*Beyj@n+SV+EU)!Oc<9(ilSyPB&h z8}f6elw5ngJ=E@mf4tXd}+jclF!|F2KlY(IbJv7qwDog4Kk>?=NoYJASNWM$xfvB!7c znYP{cyF54!`nT$;s?SasTT*H`z@iF5Z>h4Jtobvw2zz<1gHrFAR^ zRo`aMl|AriSH|S*W!e1ca)!759@~-j{$Q}Qsq2#`*UvBOH#8HQ_+Yj6Jh215hR=5v z#QUwPnefHzl|ie3(+oic>E%f|m1QM6)hp^*FEj1XOpE88K$G$R=k`HP0`c=CrQ_{Nf5z8Xavt2h zD4F4xGIjG)ZaZnz`6ohcY P*kJH<^>bP0l+XkK-~-|C diff --git a/core/src/components/segment-view/test/disabled/segment-view.e2e.ts-snapshots/segment-view-disabled-md-ltr-Mobile-Firefox-linux.png b/core/src/components/segment-view/test/disabled/segment-view.e2e.ts-snapshots/segment-view-disabled-md-ltr-Mobile-Firefox-linux.png index cd6be072b37ffd523f0c6176f1ed2418c7ae4280..1d84031d3e4e12c4b76450ff85da7b23c902c79a 100644 GIT binary patch literal 23920 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 So&h{box#)9&t;ucLK6VQ2g$Vn literal 1866 zcmeAS@N?(olHy`uVBq!ia0y~yVC)34Q#jawWO2cnPzDBeK~ERQkczmscW&l~hKjI1 zxZCJ=L1{8iAfqeWVvSDzeHk*3=Ib1BS^V&*gplwYWe$m{jsjNG`L>tNch*!8iEvn_ z_IkIz@9f>@EMG_a*56E@e|z3!`}J!?^qE*V1Qb3nG|DMH{NBXC$i#9@SU{-G94P3J z5Zh>`vP^%TR(J69cU8guFJ&b(rOn=H9lp&!ZQecJe%BqF_JpXNuMce7Ca>2#JH962 ztXKNRp82+cZe@Ez?|;6OEPH)6$e;)N1tYjS&)unzy!Z3h+SMy=i_C3ZX;lCB`la28 zOM2ebuK)7oi`_4-?7MHKUQS)`@8f&y{7fZr$Z%@x4$2>72j=8 zyYkGR%2hdcK9@;K-2Ln$wQ*8~S`VL714H9F4jU7dS-0IEr}tft_G>?VVO#UkpLOqU z`KK-e|GND zlGJmK=AUx&^zyrTuTE9WZ2<<0zxsy@6JDJ0pLUWve%i#j?x!CV|339aKK{D>x5=p$ z$0uI8Z66}GOn>@M>F1{-_R3y0-?H=G(p}~%vjS#Xe6M`@^7ZCww)%Vq(MjTPJ>Ec7|}?Y+aVCM~!-dB)$@zrVeH_E&!E!VMP1TVr`mv^#FE|7fiK zD(jJhG}C{+&e`VYrtYlj+4xs}-Bpw0*=E~wW6D$a`GLH@TqxrC%Vp8W-$=`>-QL$X z?cc=Q)i*cBJUf1Vdh5yV)XKtbc?IRSq65zAE_o=s{;s%?@$`L};d05p@5S%-s21`! z)jtXh?-#2M%w0bF)xCqq?j8^Gzju3`#H_1_@2*eNU%2z=6&9!Zy?d`*z8$YtZ++A5 z)UO2-X8iiQI!L}-Yq9Nd^UB}$jeFm7b*3BZyDK_0IE1sxS*uL5{+zmq?a*7_H_x=JO&QemukfGG zt^C=jcrhpIeB9rKIdxATU%G96!|d9fWwR{4&$R7a*LU+IDDkE+^~(yKeK;|oNzJUiZ$O$g6}_A7FE1t@1dQm?WZl>`P%8x$!ANuKrtZ7 z(dv?3xU6o`uid}g|GnR3B#|*cBWl*(uvg}(kJlxCZ*|#yYsc$iq1vyPZ#QSppImWy z|I-(*R?O`IYA$5_|3E))87SXmvb4C&3e27Y7F8594|W7bi3yXF(%BWJUO?uKh6x?B zS1JH~E5)cdG3|i0X;Us&B0@dWNwmWOi_KbnckR12PG(jnLSftIydIk~sACGsvRkH)ukqn-$elF{r5}E)S80jeh diff --git a/core/src/components/segment-view/test/disabled/segment-view.e2e.ts-snapshots/segment-view-disabled-md-ltr-Mobile-Safari-linux.png b/core/src/components/segment-view/test/disabled/segment-view.e2e.ts-snapshots/segment-view-disabled-md-ltr-Mobile-Safari-linux.png index ae81335014ffa8cffdf1cb9bda4f116fee0e62d7..697b8ebfe9ef2f8440a4f523efe386916d0cc145 100644 GIT binary patch literal 15953 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 XDpGOmtxfPyHkRxwB`8wb!1uoa;1YN> literal 1067 zcmeAS@N?(olHy`uVBq!ia0y~yU~B`jQ#hDFB9aXKK#DEN+ueoXKL{?^yL>VO1M>q< z7srr_IdAWH=L@HcG(0qZyiwM=O@mX_vwH%UmXOw}sEr9%L;si5Gwhz07p0}d7^JDH zsKU}DtiI3J{9%w#;{>THsd?!?d-q@b_-yKPC2{lc*9<2X{#nn?P~khN!DUs)i}iP< z_C{F8XmoA5nR`yJRBXLS>eqK?|GL{x?JX;RIeT&Yw|RHuzFvR6@6W^gauOD2`4$I< z%{}*Xy|U7xc`|z}ZtSzaciX1;e`(hI zxAwm(x9q<6YVDrn&u9KToji~K#}b+P%&^k)u189CNDF=~vAMCw_|^LBlM`>gI;#^` zDY{;qKTqteU;fi77ndu|XRkIt=U+6>Bz^j#g6Uj85{|bgTgkb&T+DDi^6r-Y;(w3N z?OeT=+kDl#NAGT}J$dllMcv=Gzki>9FL&vp4qd zYRb0GnyKA&>|FC~_wz<`Wn5eW&(`!zY`pyXsqpqqfu&-7a_e5dJa}&Y-Otx=rR&S+ z$$pP=xS8SZVkgTe~DWM4fj$P<# diff --git a/core/src/components/segment/segment.tsx b/core/src/components/segment/segment.tsx index 6bb0ba50ef2..074c2586b87 100644 --- a/core/src/components/segment/segment.tsx +++ b/core/src/components/segment/segment.tsx @@ -7,6 +7,7 @@ import { createColorClasses, hostContext } from '@utils/theme'; import { getIonMode } from '../../global/ionic-global'; import type { Color, StyleEventDetail } from '../../interface'; +import type { SegmentViewScrollEvent } from '../segment-view/segment-view-interface'; import type { SegmentChangeEventDetail, SegmentValue } from './segment-interface'; @@ -28,8 +29,14 @@ export class Segment implements ComponentInterface { private valueBeforeGesture?: SegmentValue; private segmentViewEl?: HTMLIonSegmentViewElement | null = null; - private scrolledIndicator?: HTMLDivElement | null = null; - private isScrolling = false; + private lastNextIndex?: number; + + /** + * Whether to update the segment view, if exists, when the value changes. + * This behavior is enabled by default, but is set false when scrolling content views + * since we don't want to "double scroll" the segment view. + */ + private triggerScrollOnValueChange?: boolean; @Element() el!: HTMLIonSegmentElement; @@ -83,6 +90,12 @@ export class Segment implements ComponentInterface { @Watch('value') protected valueChanged(value: SegmentValue | undefined, oldValue?: SegmentValue | undefined) { + // Force a value to exist if we're using a segment view + if (this.segmentViewEl && value === undefined) { + this.value = this.getButtons()[0].value; + return; + } + if (oldValue !== undefined && value !== undefined) { const buttons = this.getButtons(); const previous = buttons.find((button) => button.value === oldValue); @@ -91,10 +104,12 @@ export class Segment implements ComponentInterface { if (previous && current) { if (!this.segmentViewEl) { this.checkButton(previous, current); - } else { - this.setCheckedClasses(); + } else if (this.triggerScrollOnValueChange !== false) { + this.updateSegmentView(); } } + } else if (value !== undefined && oldValue === undefined && this.segmentViewEl) { + this.updateSegmentView(); } /** @@ -107,6 +122,8 @@ export class Segment implements ComponentInterface { if (!this.segmentViewEl) { this.scrollActiveButtonIntoView(); } + + this.triggerScrollOnValueChange = undefined; } /** @@ -140,9 +157,13 @@ export class Segment implements ComponentInterface { disabledChanged() { this.gestureChanged(); - const buttons = this.getButtons(); - for (const button of buttons) { - button.disabled = this.disabled; + if (!this.segmentViewEl) { + const buttons = this.getButtons(); + for (const button of buttons) { + button.disabled = this.disabled; + } + } else { + this.segmentViewEl.disabled = this.disabled; } } @@ -322,6 +343,8 @@ export class Segment implements ComponentInterface { // Remove the transform to slide the indicator back to the button clicked currentIndicator.style.setProperty('transform', ''); + + this.scrollActiveButtonIntoView(true); }); this.value = current.value; @@ -352,8 +375,10 @@ export class Segment implements ComponentInterface { } @Listen('ionSegmentViewScroll', { target: 'body' }) - handleSegmentViewScroll(ev: CustomEvent) { - if (!this.isScrolling) { + handleSegmentViewScroll(ev: CustomEvent) { + const { scrollRatio, isManualScroll } = ev.detail; + + if (!isManualScroll) { return; } @@ -366,120 +391,20 @@ export class Segment implements ComponentInterface { const buttons = this.getButtons(); // If no buttons are found or there is no value set then do nothing - if (!buttons.length || this.value === undefined) return; + if (!buttons.length) return; const index = buttons.findIndex((button) => button.value === this.value); const current = buttons[index]; - const indicatorEl = this.getIndicator(current); - this.scrolledIndicator = indicatorEl; - - const { scrollDistancePercentage, scrollDistance } = ev.detail; - - if (indicatorEl && !isNaN(scrollDistancePercentage)) { - indicatorEl.style.transition = 'transform 0.3s ease-out'; - - // Calculate the amount the indicator should move based on the scroll percentage - // and the width of the current button - const scrollAmount = scrollDistancePercentage * current.getBoundingClientRect().width; - const transformValue = scrollDistance < 0 ? -scrollAmount : scrollAmount; - - // Calculate total width of buttons to the left of the current button - const totalButtonWidthBefore = buttons - .slice(0, index) - .reduce((acc, button) => acc + button.getBoundingClientRect().width, 0); - - // Calculate total width of buttons to the right of the current button - const totalButtonWidthAfter = buttons - .slice(index + 1) - .reduce((acc, button) => acc + button.getBoundingClientRect().width, 0); - - // Set minTransform and maxTransform - const minTransform = -totalButtonWidthBefore; - const maxTransform = totalButtonWidthAfter; - - // Clamp the transform value to ensure it doesn't go out of bounds - const clampedTransform = Math.max(minTransform, Math.min(transformValue, maxTransform)); - - // Apply the clamped transform value to the indicator element - const transform = `translate3d(${clampedTransform}px, 0, 0)`; - indicatorEl.style.setProperty('transform', transform); - - // Scroll the buttons if the indicator is out of view - const indicatorX = indicatorEl.getBoundingClientRect().x; - const buttonWidth = current.getBoundingClientRect().width; - if (scrollDistance < 0 && indicatorX < 0) { - this.el.scrollBy({ - top: 0, - left: indicatorX, - behavior: 'instant', - }); - } else if (scrollDistance > 0 && indicatorX + buttonWidth > this.el.offsetWidth) { - this.el.scrollBy({ - top: 0, - left: indicatorX + buttonWidth - this.el.offsetWidth, - behavior: 'instant', - }); - } - } - } - } - @Listen('ionSegmentViewScrollStart', { target: 'body' }) - onScrollStart(ev: CustomEvent) { - const dispatchedFrom = ev.target as HTMLElement; - const segmentViewEl = this.segmentViewEl as EventTarget; - const segmentEl = this.el; + const nextIndex = Math.round(scrollRatio * (buttons.length - 1)); - if (ev.composedPath().includes(segmentViewEl) || dispatchedFrom?.contains(segmentEl)) { - this.isScrolling = true; - } - } - - @Listen('ionSegmentViewScrollEnd', { target: 'body' }) - onScrollEnd(ev: CustomEvent<{ activeContentId: string }>) { - const dispatchedFrom = ev.target as HTMLElement; - const segmentViewEl = this.segmentViewEl as EventTarget; - const segmentEl = this.el; + if (this.lastNextIndex === undefined || this.lastNextIndex !== nextIndex) { + this.lastNextIndex = nextIndex; + this.triggerScrollOnValueChange = false; - if (ev.composedPath().includes(segmentViewEl) || dispatchedFrom?.contains(segmentEl)) { - if (this.scrolledIndicator) { - const computedStyle = window.getComputedStyle(this.scrolledIndicator); - const isTransitioning = computedStyle.transitionDuration !== '0s'; - - if (isTransitioning) { - // Add a transitionend listener if the indicator is transitioning - this.waitForTransitionEnd(this.scrolledIndicator, () => { - this.updateValueAfterTransition(ev.detail.activeContentId); - }); - } else { - // Immediately update the value if there's no transition - this.updateValueAfterTransition(ev.detail.activeContentId); - } - } else { - // Immediately update the value if there's no indicator - this.updateValueAfterTransition(ev.detail.activeContentId); + this.checkButton(current, buttons[nextIndex]); + this.emitValueChange(); } - - this.isScrolling = false; - } - } - - // Wait for the transition to end, then execute the callback - private waitForTransitionEnd(indicator: HTMLElement, callback: () => void) { - const onTransitionEnd = () => { - indicator.removeEventListener('transitionend', onTransitionEnd); - callback(); - }; - indicator.addEventListener('transitionend', onTransitionEnd); - } - - // Update the Segment value after the ionSegmentViewScrollEnd transition has ended - private updateValueAfterTransition(activeContentId: string) { - this.value = activeContentId; - - if (this.scrolledIndicator) { - this.scrolledIndicator.style.transition = ''; - this.scrolledIndicator.style.transform = ''; } } @@ -500,8 +425,7 @@ export class Segment implements ComponentInterface { return; } - const content = document.getElementById(button.contentId); - const segmentView = content?.closest('ion-segment-view'); + const segmentView = this.segmentViewEl; if (segmentView) { segmentView.setContent(button.contentId, smoothScroll); @@ -686,10 +610,15 @@ export class Segment implements ComponentInterface { if (current !== previous) { this.emitValueChange(); - this.updateSegmentView(); } - if (this.scrollable || !this.swipeGesture) { + if (this.segmentViewEl) { + this.updateSegmentView(); + + if (this.scrollable && previous) { + this.checkButton(previous, current); + } + } else if (this.scrollable || !this.swipeGesture) { if (previous) { this.checkButton(previous, current); } else { diff --git a/packages/angular/src/directives/proxies.ts b/packages/angular/src/directives/proxies.ts index ab3d9c061c6..71ab6b42001 100644 --- a/packages/angular/src/directives/proxies.ts +++ b/packages/angular/src/directives/proxies.ts @@ -2009,14 +2009,13 @@ export declare interface IonSegmentButton extends Components.IonSegmentButton {} @ProxyCmp({ - inputs: ['disabled'] }) @Component({ selector: 'ion-segment-content', changeDetection: ChangeDetectionStrategy.OnPush, template: '', // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property - inputs: ['disabled'], + inputs: [], }) export class IonSegmentContent { protected el: HTMLElement; @@ -2031,8 +2030,7 @@ export declare interface IonSegmentContent extends Components.IonSegmentContent @ProxyCmp({ - inputs: ['disabled'], - methods: ['setContent'] + inputs: ['disabled'] }) @Component({ selector: 'ion-segment-view', @@ -2051,15 +2049,17 @@ export class IonSegmentView { } +import type { SegmentViewScrollEvent as IIonSegmentViewSegmentViewScrollEvent } from '@ionic/core'; + export declare interface IonSegmentView extends Components.IonSegmentView { /** * Emitted when the segment view is scrolled. */ - ionSegmentViewScroll: EventEmitter>; + ionSegmentViewScroll: EventEmitter>; /** * Emitted when the segment view scroll has ended. */ - ionSegmentViewScrollEnd: EventEmitter>; + ionSegmentViewScrollEnd: EventEmitter>; ionSegmentViewScrollStart: EventEmitter>; } diff --git a/packages/angular/standalone/src/directives/proxies.ts b/packages/angular/standalone/src/directives/proxies.ts index 65c071dc093..ee340ba66a5 100644 --- a/packages/angular/standalone/src/directives/proxies.ts +++ b/packages/angular/standalone/src/directives/proxies.ts @@ -1844,15 +1844,14 @@ export declare interface IonSegmentButton extends Components.IonSegmentButton {} @ProxyCmp({ - defineCustomElementFn: defineIonSegmentContent, - inputs: ['disabled'] + defineCustomElementFn: defineIonSegmentContent }) @Component({ selector: 'ion-segment-content', changeDetection: ChangeDetectionStrategy.OnPush, template: '', // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property - inputs: ['disabled'], + inputs: [], standalone: true }) export class IonSegmentContent { @@ -1869,8 +1868,7 @@ export declare interface IonSegmentContent extends Components.IonSegmentContent @ProxyCmp({ defineCustomElementFn: defineIonSegmentView, - inputs: ['disabled'], - methods: ['setContent'] + inputs: ['disabled'] }) @Component({ selector: 'ion-segment-view', @@ -1890,15 +1888,17 @@ export class IonSegmentView { } +import type { SegmentViewScrollEvent as IIonSegmentViewSegmentViewScrollEvent } from '@ionic/core/components'; + export declare interface IonSegmentView extends Components.IonSegmentView { /** * Emitted when the segment view is scrolled. */ - ionSegmentViewScroll: EventEmitter>; + ionSegmentViewScroll: EventEmitter>; /** * Emitted when the segment view scroll has ended. */ - ionSegmentViewScrollEnd: EventEmitter>; + ionSegmentViewScrollEnd: EventEmitter>; ionSegmentViewScrollStart: EventEmitter>; } diff --git a/packages/vue/src/proxies.ts b/packages/vue/src/proxies.ts index d72ec0378c5..c83e7b650e9 100644 --- a/packages/vue/src/proxies.ts +++ b/packages/vue/src/proxies.ts @@ -755,9 +755,7 @@ export const IonSegmentButton = /*@__PURE__*/ defineContainer('ion-segment-content', defineIonSegmentContent, [ - 'disabled' -]); +export const IonSegmentContent = /*@__PURE__*/ defineContainer('ion-segment-content', defineIonSegmentContent); export const IonSegmentView = /*@__PURE__*/ defineContainer('ion-segment-view', defineIonSegmentView, [ From ef5bdc71d0ff45e8724be654b2b183fcc140628c Mon Sep 17 00:00:00 2001 From: Tanner Reits Date: Mon, 28 Oct 2024 21:19:45 -0400 Subject: [PATCH 42/46] fix: PR feedback --- core/src/components/segment-view/segment-view.tsx | 2 +- core/src/components/segment-view/test/basic/index.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/components/segment-view/segment-view.tsx b/core/src/components/segment-view/segment-view.tsx index c09b5797162..265d823cca7 100644 --- a/core/src/components/segment-view/segment-view.tsx +++ b/core/src/components/segment-view/segment-view.tsx @@ -90,7 +90,7 @@ export class SegmentView implements ComponentInterface { } this.scrollEndTimeout = setTimeout(() => { this.checkForScrollEnd(); - }, 50); + }, 100); } /** diff --git a/core/src/components/segment-view/test/basic/index.html b/core/src/components/segment-view/test/basic/index.html index 02321d28c4e..69d36d4a6c0 100644 --- a/core/src/components/segment-view/test/basic/index.html +++ b/core/src/components/segment-view/test/basic/index.html @@ -66,7 +66,7 @@ Paid - + Free From 2e484c020691ff88be26221edd61b8a45a427dec Mon Sep 17 00:00:00 2001 From: Tanner Reits Date: Tue, 29 Oct 2024 20:16:58 -0400 Subject: [PATCH 43/46] fix(): more PR feedback --- .../components/segment-view/segment-view.tsx | 23 ++++++++----------- .../test/basic/segment-view.e2e.ts | 2 +- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/core/src/components/segment-view/segment-view.tsx b/core/src/components/segment-view/segment-view.tsx index 265d823cca7..633d9a37d39 100644 --- a/core/src/components/segment-view/segment-view.tsx +++ b/core/src/components/segment-view/segment-view.tsx @@ -36,13 +36,6 @@ export class SegmentView implements ComponentInterface { */ @Event() ionSegmentViewScroll!: EventEmitter; - /** - * Emitted when the segment view scroll has ended. - */ - @Event() ionSegmentViewScrollEnd!: EventEmitter; - - @Event() ionSegmentViewScrollStart!: EventEmitter; - @Listen('scroll') handleScroll(ev: Event) { const { scrollLeft, scrollWidth, clientWidth } = ev.target as HTMLElement; @@ -62,8 +55,6 @@ export class SegmentView implements ComponentInterface { */ @Listen('touchstart') handleScrollStart() { - this.ionSegmentViewScrollStart.emit(); - if (this.scrollEndTimeout) { clearTimeout(this.scrollEndTimeout); this.scrollEndTimeout = null; @@ -88,9 +79,16 @@ export class SegmentView implements ComponentInterface { clearTimeout(this.scrollEndTimeout); this.scrollEndTimeout = null; } - this.scrollEndTimeout = setTimeout(() => { - this.checkForScrollEnd(); - }, 100); + this.scrollEndTimeout = setTimeout( + () => { + this.checkForScrollEnd(); + }, + // Setting this to a lower value may result in inconsistencies in behavior + // across browsers (particularly Firefox). + // Ideally, all of this logic is removed once the scroll end event is + // supported on all browsers (https://caniuse.com/?search=scrollend) + 100 + ); } /** @@ -102,7 +100,6 @@ export class SegmentView implements ComponentInterface { // Only emit scroll end event if the active content is not disabled and // the user is not touching the segment view if (!this.isTouching) { - this.ionSegmentViewScrollEnd.emit(); this.isManualScroll = undefined; } } diff --git a/core/src/components/segment-view/test/basic/segment-view.e2e.ts b/core/src/components/segment-view/test/basic/segment-view.e2e.ts index 1a7279487fd..447a60be309 100644 --- a/core/src/components/segment-view/test/basic/segment-view.e2e.ts +++ b/core/src/components/segment-view/test/basic/segment-view.e2e.ts @@ -83,7 +83,7 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => { config ); - await page.click('ion-segment-button[value="top"]'); + await page.locator('ion-segment-button[value="top"]').click(); const segmentContent = page.locator('ion-segment-content[id="top"]'); await expect(segmentContent).toBeInViewport(); From f5e910aec170e8065820e1656b5a64298b648649 Mon Sep 17 00:00:00 2001 From: Tanner Reits Date: Tue, 29 Oct 2024 20:21:01 -0400 Subject: [PATCH 44/46] fix: regenerate api & proxies --- core/api.txt | 2 -- core/src/components.d.ts | 7 ------- packages/angular/src/directives/proxies.ts | 8 +------- packages/angular/standalone/src/directives/proxies.ts | 8 +------- packages/vue/src/proxies.ts | 4 +--- 5 files changed, 3 insertions(+), 26 deletions(-) diff --git a/core/api.txt b/core/api.txt index 269e43875ac..0d3dbf99b09 100644 --- a/core/api.txt +++ b/core/api.txt @@ -1613,8 +1613,6 @@ ion-segment-content,shadow ion-segment-view,shadow ion-segment-view,prop,disabled,boolean,false,false,false ion-segment-view,event,ionSegmentViewScroll,SegmentViewScrollEvent,true -ion-segment-view,event,ionSegmentViewScrollEnd,void,true -ion-segment-view,event,ionSegmentViewScrollStart,void,true ion-select,shadow ion-select,prop,cancelText,string,'Cancel',false,false diff --git a/core/src/components.d.ts b/core/src/components.d.ts index 03ca88a4cf9..f99d44ed9b5 100644 --- a/core/src/components.d.ts +++ b/core/src/components.d.ts @@ -4443,8 +4443,6 @@ declare global { }; interface HTMLIonSegmentViewElementEventMap { "ionSegmentViewScroll": SegmentViewScrollEvent; - "ionSegmentViewScrollEnd": void; - "ionSegmentViewScrollStart": void; } interface HTMLIonSegmentViewElement extends Components.IonSegmentView, HTMLStencilElement { addEventListener(type: K, listener: (this: HTMLIonSegmentViewElement, ev: IonSegmentViewCustomEvent) => any, options?: boolean | AddEventListenerOptions): void; @@ -7536,11 +7534,6 @@ declare namespace LocalJSX { * Emitted when the segment view is scrolled. */ "onIonSegmentViewScroll"?: (event: IonSegmentViewCustomEvent) => void; - /** - * Emitted when the segment view scroll has ended. - */ - "onIonSegmentViewScrollEnd"?: (event: IonSegmentViewCustomEvent) => void; - "onIonSegmentViewScrollStart"?: (event: IonSegmentViewCustomEvent) => void; } interface IonSelect { /** diff --git a/packages/angular/src/directives/proxies.ts b/packages/angular/src/directives/proxies.ts index 71ab6b42001..39302c377db 100644 --- a/packages/angular/src/directives/proxies.ts +++ b/packages/angular/src/directives/proxies.ts @@ -2044,7 +2044,7 @@ export class IonSegmentView { constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; - proxyOutputs(this, this.el, ['ionSegmentViewScroll', 'ionSegmentViewScrollEnd', 'ionSegmentViewScrollStart']); + proxyOutputs(this, this.el, ['ionSegmentViewScroll']); } } @@ -2056,12 +2056,6 @@ export declare interface IonSegmentView extends Components.IonSegmentView { * Emitted when the segment view is scrolled. */ ionSegmentViewScroll: EventEmitter>; - /** - * Emitted when the segment view scroll has ended. - */ - ionSegmentViewScrollEnd: EventEmitter>; - - ionSegmentViewScrollStart: EventEmitter>; } diff --git a/packages/angular/standalone/src/directives/proxies.ts b/packages/angular/standalone/src/directives/proxies.ts index ee340ba66a5..bbcd9ba9740 100644 --- a/packages/angular/standalone/src/directives/proxies.ts +++ b/packages/angular/standalone/src/directives/proxies.ts @@ -1883,7 +1883,7 @@ export class IonSegmentView { constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) { c.detach(); this.el = r.nativeElement; - proxyOutputs(this, this.el, ['ionSegmentViewScroll', 'ionSegmentViewScrollEnd', 'ionSegmentViewScrollStart']); + proxyOutputs(this, this.el, ['ionSegmentViewScroll']); } } @@ -1895,12 +1895,6 @@ export declare interface IonSegmentView extends Components.IonSegmentView { * Emitted when the segment view is scrolled. */ ionSegmentViewScroll: EventEmitter>; - /** - * Emitted when the segment view scroll has ended. - */ - ionSegmentViewScrollEnd: EventEmitter>; - - ionSegmentViewScrollStart: EventEmitter>; } diff --git a/packages/vue/src/proxies.ts b/packages/vue/src/proxies.ts index c83e7b650e9..324c82e45bb 100644 --- a/packages/vue/src/proxies.ts +++ b/packages/vue/src/proxies.ts @@ -760,9 +760,7 @@ export const IonSegmentContent = /*@__PURE__*/ defineContainer('ion-segment-view', defineIonSegmentView, [ 'disabled', - 'ionSegmentViewScroll', - 'ionSegmentViewScrollEnd', - 'ionSegmentViewScrollStart' + 'ionSegmentViewScroll' ]); From ab5d4e50927fde1ae2482bbfac9ebbf4fb132c23 Mon Sep 17 00:00:00 2001 From: Brandy Carney Date: Wed, 30 Oct 2024 16:11:26 -0400 Subject: [PATCH 45/46] refactor(segment-content): remove mode specific files --- core/src/components/segment-content/segment-content.ios.scss | 5 ----- core/src/components/segment-content/segment-content.md.scss | 5 ----- core/src/components/segment-content/segment-content.tsx | 5 +---- 3 files changed, 1 insertion(+), 14 deletions(-) delete mode 100644 core/src/components/segment-content/segment-content.ios.scss delete mode 100644 core/src/components/segment-content/segment-content.md.scss diff --git a/core/src/components/segment-content/segment-content.ios.scss b/core/src/components/segment-content/segment-content.ios.scss deleted file mode 100644 index aee6789be18..00000000000 --- a/core/src/components/segment-content/segment-content.ios.scss +++ /dev/null @@ -1,5 +0,0 @@ -@import "./segment-content"; -@import "../segment-button/segment-button.ios.vars"; - -// iOS Segment Content -// -------------------------------------------------- diff --git a/core/src/components/segment-content/segment-content.md.scss b/core/src/components/segment-content/segment-content.md.scss deleted file mode 100644 index ea64ce72d8e..00000000000 --- a/core/src/components/segment-content/segment-content.md.scss +++ /dev/null @@ -1,5 +0,0 @@ -@import "./segment-content"; -@import "../segment-button/segment-button.md.vars"; - -// Material Design Segment Content -// -------------------------------------------------- diff --git a/core/src/components/segment-content/segment-content.tsx b/core/src/components/segment-content/segment-content.tsx index 3d17217f437..2b5fefebff2 100644 --- a/core/src/components/segment-content/segment-content.tsx +++ b/core/src/components/segment-content/segment-content.tsx @@ -3,10 +3,7 @@ import { Component, Host, h } from '@stencil/core'; @Component({ tag: 'ion-segment-content', - styleUrls: { - ios: 'segment-content.ios.scss', - md: 'segment-content.md.scss', - }, + styleUrl: 'segment-content.scss', shadow: true, }) export class SegmentContent implements ComponentInterface { From 8429322b3e49c6fce73a902de4e5d32d5a8fbb0e Mon Sep 17 00:00:00 2001 From: Tanner Reits Date: Thu, 31 Oct 2024 10:10:45 -0400 Subject: [PATCH 46/46] fix(segment-button): revert disabled reflection --- core/src/components/segment-button/segment-button.tsx | 2 +- .../components/segment-view/test/disabled/segment-view.e2e.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/components/segment-button/segment-button.tsx b/core/src/components/segment-button/segment-button.tsx index 80cb243c301..d860735f729 100644 --- a/core/src/components/segment-button/segment-button.tsx +++ b/core/src/components/segment-button/segment-button.tsx @@ -44,7 +44,7 @@ export class SegmentButton implements ComponentInterface, ButtonInterface { /** * If `true`, the user cannot interact with the segment button. */ - @Prop({ mutable: true, reflect: true }) disabled = false; + @Prop({ mutable: true }) disabled = false; /** * Set the layout of the text and icon in the segment. diff --git a/core/src/components/segment-view/test/disabled/segment-view.e2e.ts b/core/src/components/segment-view/test/disabled/segment-view.e2e.ts index 071e3604d48..c7dead8943f 100644 --- a/core/src/components/segment-view/test/disabled/segment-view.e2e.ts +++ b/core/src/components/segment-view/test/disabled/segment-view.e2e.ts @@ -43,7 +43,7 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => { ); const segmentButton = page.locator('ion-segment-button[value="free"]'); - await expect(segmentButton).not.toHaveAttribute('disabled'); + await expect(segmentButton).not.toHaveClass(/segment-button-disabled/); }); }); });