Skip to content

Commit 9cfff41

Browse files
author
Tanner Reits
committed
feat(segment): simplify things, prevent disabled
1 parent 475de8b commit 9cfff41

File tree

11 files changed

+80
-241
lines changed

11 files changed

+80
-241
lines changed

core/src/components.d.ts

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2676,6 +2676,10 @@ export namespace Components {
26762676
* If `true`, the segment buttons will overflow and the user can swipe to see them. In addition, this will disable the gesture to drag the indicator between the buttons in order to swipe to see hidden buttons.
26772677
*/
26782678
"scrollable": boolean;
2679+
/**
2680+
* The `id` of the segment view that this segment component should be linked to.
2681+
*/
2682+
"segmentViewId"?: string;
26792683
/**
26802684
* If `true`, navigating to an `ion-segment-button` with the keyboard will focus and select the element. If `false`, keyboard navigation will only focus the `ion-segment-button` element.
26812685
*/
@@ -2717,10 +2721,6 @@ export namespace Components {
27172721
"value": SegmentValue;
27182722
}
27192723
interface IonSegmentContent {
2720-
/**
2721-
* If `true`, the segment content will not be displayed.
2722-
*/
2723-
"disabled": boolean;
27242724
}
27252725
interface IonSegmentView {
27262726
/**
@@ -4443,11 +4443,10 @@ declare global {
44434443
};
44444444
interface HTMLIonSegmentViewElementEventMap {
44454445
"ionSegmentViewScroll": {
4446-
scrollDirection: string;
4447-
scrollDistance: number;
4448-
scrollDistancePercentage: number;
4446+
scrollRatio: number;
4447+
isManualScroll: boolean;
44494448
};
4450-
"ionSegmentViewScrollEnd": { activeContentId: string };
4449+
"ionSegmentViewScrollEnd": void;
44514450
"ionSegmentViewScrollStart": void;
44524451
}
44534452
interface HTMLIonSegmentViewElement extends Components.IonSegmentView, HTMLStencilElement {
@@ -7490,6 +7489,10 @@ declare namespace LocalJSX {
74907489
* If `true`, the segment buttons will overflow and the user can swipe to see them. In addition, this will disable the gesture to drag the indicator between the buttons in order to swipe to see hidden buttons.
74917490
*/
74927491
"scrollable"?: boolean;
7492+
/**
7493+
* The `id` of the segment view that this segment component should be linked to.
7494+
*/
7495+
"segmentViewId"?: string;
74937496
/**
74947497
* If `true`, navigating to an `ion-segment-button` with the keyboard will focus and select the element. If `false`, keyboard navigation will only focus the `ion-segment-button` element.
74957498
*/
@@ -7530,10 +7533,6 @@ declare namespace LocalJSX {
75307533
"value"?: SegmentValue;
75317534
}
75327535
interface IonSegmentContent {
7533-
/**
7534-
* If `true`, the segment content will not be displayed.
7535-
*/
7536-
"disabled"?: boolean;
75377536
}
75387537
interface IonSegmentView {
75397538
/**
@@ -7544,14 +7543,13 @@ declare namespace LocalJSX {
75447543
* Emitted when the segment view is scrolled.
75457544
*/
75467545
"onIonSegmentViewScroll"?: (event: IonSegmentViewCustomEvent<{
7547-
scrollDirection: string;
7548-
scrollDistance: number;
7549-
scrollDistancePercentage: number;
7546+
scrollRatio: number;
7547+
isManualScroll: boolean;
75507548
}>) => void;
75517549
/**
75527550
* Emitted when the segment view scroll has ended.
75537551
*/
7554-
"onIonSegmentViewScrollEnd"?: (event: IonSegmentViewCustomEvent<{ activeContentId: string }>) => void;
7552+
"onIonSegmentViewScrollEnd"?: (event: IonSegmentViewCustomEvent<void>) => void;
75557553
"onIonSegmentViewScrollStart"?: (event: IonSegmentViewCustomEvent<void>) => void;
75567554
}
75577555
interface IonSelect {

core/src/components/segment-button/segment-button.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,11 @@ export class SegmentButton implements ComponentInterface, ButtonInterface {
9191
return;
9292
}
9393

94-
// Set the disabled state of the Segment Content based on the button's disabled state
95-
segmentContent.disabled = this.disabled;
94+
// Prevent buttons from being disabled when associated with segment content
95+
if (this.disabled) {
96+
console.warn(`Segment Button: Segment buttons cannot be disabled when associated with an <ion-segment-content>.`);
97+
this.disabled = false;
98+
}
9699
}
97100

98101
disconnectedCallback() {

core/src/components/segment-content/segment-content.ios.scss

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,3 @@
33

44
// iOS Segment Content
55
// --------------------------------------------------
6-
7-
:host(.segment-content-disabled) {
8-
opacity: $segment-button-ios-opacity-disabled;
9-
}

core/src/components/segment-content/segment-content.md.scss

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,3 @@
33

44
// Material Design Segment Content
55
// --------------------------------------------------
6-
7-
:host(.segment-content-disabled) {
8-
opacity: $segment-button-md-opacity-disabled;
9-
}

core/src/components/segment-content/segment-content.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
:host {
55
scroll-snap-align: center;
6+
scroll-snap-stop: always;
67

78
flex-shrink: 0;
89

core/src/components/segment-content/segment-content.tsx

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { ComponentInterface } from '@stencil/core';
2-
import { Component, Host, Prop, h } from '@stencil/core';
2+
import { Component, Host, h } from '@stencil/core';
33

44
@Component({
55
tag: 'ion-segment-content',
@@ -10,20 +10,9 @@ import { Component, Host, Prop, h } from '@stencil/core';
1010
shadow: true,
1111
})
1212
export class SegmentContent implements ComponentInterface {
13-
/**
14-
* If `true`, the segment content will not be displayed.
15-
*/
16-
@Prop() disabled = false;
17-
1813
render() {
19-
const { disabled } = this;
20-
2114
return (
22-
<Host
23-
class={{
24-
'segment-content-disabled': disabled,
25-
}}
26-
>
15+
<Host>
2716
<slot></slot>
2817
</Host>
2918
);

core/src/components/segment-view/segment-view.tsx

Lines changed: 13 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,9 @@ import { Component, Element, Event, Host, Listen, Method, Prop, h } from '@stenc
1010
shadow: true,
1111
})
1212
export class SegmentView implements ComponentInterface {
13-
private initialScrollLeft?: number;
14-
private previousScrollLeft = 0;
1513
private scrollEndTimeout: ReturnType<typeof setTimeout> | null = null;
1614
private isTouching = false;
15+
private isManualScroll = true;
1716

1817
@Element() el!: HTMLElement;
1918

@@ -26,61 +25,27 @@ export class SegmentView implements ComponentInterface {
2625
* Emitted when the segment view is scrolled.
2726
*/
2827
@Event() ionSegmentViewScroll!: EventEmitter<{
29-
scrollDirection: string;
30-
scrollDistance: number;
31-
scrollDistancePercentage: number;
28+
scrollRatio: number;
29+
isManualScroll: boolean;
3230
}>;
3331

3432
/**
3533
* Emitted when the segment view scroll has ended.
3634
*/
37-
@Event() ionSegmentViewScrollEnd!: EventEmitter<{ activeContentId: string }>;
35+
@Event() ionSegmentViewScrollEnd!: EventEmitter<void>;
3836

3937
@Event() ionSegmentViewScrollStart!: EventEmitter<void>;
4038

41-
private activeContentId = '';
42-
4339
@Listen('scroll')
4440
handleScroll(ev: Event) {
45-
const { initialScrollLeft, previousScrollLeft } = this;
46-
const { scrollLeft, offsetWidth } = ev.target as HTMLElement;
47-
48-
// Set initial scroll position if it's undefined
49-
this.initialScrollLeft = initialScrollLeft ?? scrollLeft;
50-
51-
// Determine the scroll direction based on the previous scroll position
52-
const scrollDirection = scrollLeft > previousScrollLeft ? 'right' : 'left';
53-
this.previousScrollLeft = scrollLeft;
41+
const { scrollLeft, scrollWidth, clientWidth } = ev.target as HTMLElement;
42+
const scrollRatio = scrollLeft / (scrollWidth - clientWidth);
5443

55-
// Calculate the distance scrolled based on the initial scroll position
56-
// and then transform it to a percentage of the segment view width
57-
const scrollDistance = scrollLeft - this.initialScrollLeft;
58-
const scrollDistancePercentage = Math.abs(scrollDistance) / offsetWidth;
59-
60-
// Emit the scroll direction and distance
6144
this.ionSegmentViewScroll.emit({
62-
scrollDirection,
63-
scrollDistance,
64-
scrollDistancePercentage,
45+
scrollRatio,
46+
isManualScroll: this.isManualScroll,
6547
});
6648

67-
// Check if the scroll is at a snapping point and return if not
68-
const atSnappingPoint = scrollLeft % offsetWidth === 0;
69-
if (!atSnappingPoint) return;
70-
71-
// Find the current segment content based on the scroll position
72-
const currentIndex = Math.round(scrollLeft / offsetWidth);
73-
74-
// Recursively search for the next enabled content in the scroll direction
75-
const segmentContent = this.getNextEnabledContent(currentIndex, scrollDirection);
76-
77-
// Exit if no valid segment content found
78-
if (!segmentContent) return;
79-
80-
// Update active content ID and scroll to the segment content
81-
this.activeContentId = segmentContent.id;
82-
this.setContent(segmentContent.id);
83-
8449
// Reset the timeout to check for scroll end
8550
this.resetScrollEndTimeout();
8651
}
@@ -127,13 +92,11 @@ export class SegmentView implements ComponentInterface {
12792
* reset the scroll position and emit the scroll end event.
12893
*/
12994
private checkForScrollEnd() {
130-
const activeContent = this.getSegmentContents().find(content => content.id === this.activeContentId);
131-
13295
// Only emit scroll end event if the active content is not disabled and
13396
// the user is not touching the segment view
134-
if (activeContent?.disabled === false && !this.isTouching) {
135-
this.ionSegmentViewScrollEnd.emit({ activeContentId: this.activeContentId });
136-
this.initialScrollLeft = undefined;
97+
if (!this.isTouching) {
98+
this.ionSegmentViewScrollEnd.emit();
99+
this.isManualScroll = true;
137100
}
138101
}
139102

@@ -151,6 +114,8 @@ export class SegmentView implements ComponentInterface {
151114

152115
if (index === -1) return;
153116

117+
this.isManualScroll = false;
118+
154119
const contentWidth = this.el.offsetWidth;
155120
this.el.scrollTo({
156121
top: 0,
@@ -163,28 +128,6 @@ export class SegmentView implements ComponentInterface {
163128
return Array.from(this.el.querySelectorAll('ion-segment-content'));
164129
}
165130

166-
/**
167-
* Recursively find the next enabled segment content based on the scroll direction.
168-
* If no enabled content is found, it will return null.
169-
*/
170-
private getNextEnabledContent(index: number, direction: string): HTMLIonSegmentContentElement | null {
171-
const contents = this.getSegmentContents();
172-
173-
// Stop if we reach the beginning or end of the content array
174-
if (index < 0 || index >= contents.length) return null;
175-
176-
const segmentContent = contents[index];
177-
178-
// If the content is not disabled, return it
179-
if (!segmentContent.disabled) {
180-
return segmentContent;
181-
}
182-
183-
// Otherwise, keep searching in the same direction
184-
const nextIndex = direction === 'right' ? index + 1 : index - 1;
185-
return this.getNextEnabledContent(nextIndex, direction);
186-
}
187-
188131
render() {
189132
const { disabled } = this;
190133

0 commit comments

Comments
 (0)