fix(segment): scroll to active segment-button on first load (#28276)

Issue number: resolves #28096

---------

<!-- Please do not submit updates to dependencies unless it fixes an
issue. -->

<!-- Please try to limit your pull request to one type (bugfix, feature,
etc). Submit multiple pull requests if needed. -->

## What is the current behavior?
<!-- Please describe the current behavior that you are modifying. -->

When a segment button is clicked, the segment will auto-scroll to
position the newly active button fully in view. However, this behavior
does not occur on first load. This means that when a segment is
initialized with a `value` corresponding to an off-screen button, the
button will remain off-screen.

## What is the new behavior?
<!-- Please describe the behavior or changes that are being added by
this PR. -->

The same auto-scroll behavior from button click now also occurs on
component load.

## Does this introduce a breaking change?

- [ ] Yes
- [x] No

<!-- If this introduces a breaking change, please describe the impact
and migration path for existing applications below. -->


## Other information

<!-- Any other information that is important to this PR such as
screenshots of how the component looks before and after the change. -->
This commit is contained in:
Amanda Johnston
2023-10-04 12:11:32 -05:00
committed by GitHub
parent dc75392e9d
commit 1167a9325f
2 changed files with 84 additions and 26 deletions

View File

@ -1,6 +1,7 @@
import type { ComponentInterface, EventEmitter } from '@stencil/core';
import { Component, Element, Event, Host, Listen, Prop, State, Watch, h, writeTask } from '@stencil/core';
import type { Gesture, GestureDetail } from '@utils/gesture';
import { raf } from '@utils/helpers';
import { isRTL } from '@utils/rtl';
import { createColorClasses, hostContext } from '@utils/theme';
@ -83,31 +84,7 @@ export class Segment implements ComponentInterface {
* Used by `ion-segment-button` to determine if the button should be checked.
*/
this.ionSelect.emit({ value });
if (this.scrollable) {
const buttons = this.getButtons();
const activeButton = buttons.find((button) => button.value === value);
if (activeButton !== undefined) {
/**
* Scrollable segment buttons should be
* centered within the view including
* buttons that are partially offscreen.
*/
activeButton.scrollIntoView({
behavior: 'smooth',
inline: 'center',
/**
* Segment should scroll on the
* horizontal axis. `block: 'nearest'`
* ensures that the vertical axis
* does not scroll if the segment
* as a whole is already in view.
*/
block: 'nearest',
});
}
}
this.scrollActiveButtonIntoView();
}
/**
@ -163,6 +140,14 @@ export class Segment implements ComponentInterface {
async componentDidLoad() {
this.setCheckedClasses();
/**
* We need to wait for the buttons to all be rendered
* before we can scroll.
*/
raf(() => {
this.scrollActiveButtonIntoView();
});
this.gesture = (await import('../../utils/gesture')).createGesture({
el: this.el,
gestureName: 'segment',
@ -320,6 +305,35 @@ export class Segment implements ComponentInterface {
}
}
private scrollActiveButtonIntoView() {
const { scrollable, value } = this;
if (scrollable) {
const buttons = this.getButtons();
const activeButton = buttons.find((button) => button.value === value);
if (activeButton !== undefined) {
/**
* Scrollable segment buttons should be
* centered within the view including
* buttons that are partially offscreen.
*/
activeButton.scrollIntoView({
behavior: 'smooth',
inline: 'center',
/**
* Segment should scroll on the
* horizontal axis. `block: 'nearest'`
* ensures that the vertical axis
* does not scroll if the segment
* as a whole is already in view.
*/
block: 'nearest',
});
}
}
}
private setNextIndex(detail: GestureDetail, isEnd = false) {
const rtl = isRTL(this.el);
const activated = this.activated;