fix(toggle): swipe gesture applies to knob (#27255)

Issue number: resolves #27254

---------

<!-- Please refer to our contributing documentation for any questions on
submitting a pull request, or let us know here if you need any help:
https://ionicframework.com/docs/building/contributing -->

<!-- Some docs updates need to be made in the `ionic-docs` repo, in a
separate PR. See
https://github.com/ionic-team/ionic-framework/blob/main/.github/CONTRIBUTING.md#modifying-documentation
for details. -->

<!-- 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. -->

The swipe gesture is currently applied to the entire `ion-toggle`
element. This was fine for the legacy syntax, but with the modern syntax
it means users can swipe on the label text which is not correct.

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

- The toggle now creates the gesture on the `.toggle-icon` element which
is the container for the knob for both modern and legacy syntaxes.
- Moved `touch-action: none` to the host of the legacy toggle. This was
preventing scrolling from happening on the modern toggle. I double
checked with native iOS and you can scroll when a pointer moves over a
toggle.

The structure of this fix was designed to match what `ion-range` does:
a8749929e0/core/src/components/range/range.tsx (L282-L296)

| Modern | Legacy |
| - | - |
| <video
src="https://user-images.githubusercontent.com/2721089/233431240-11f0c94f-d86b-4975-afd5-e534262a6f16.mov"></video>
| <video
src="https://user-images.githubusercontent.com/2721089/233431275-6c6f7fef-6cc0-4adc-8915-6fd5c3795ade.mov"></video>
|



## 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:
Liam DeBeasi
2023-04-26 10:50:00 -04:00
committed by GitHub
parent 8ee16f2a6b
commit 65245826e3
2 changed files with 39 additions and 19 deletions

View File

@ -33,7 +33,6 @@
outline: none;
cursor: pointer;
touch-action: none;
user-select: none;
z-index: $z-index-item-input;
}
@ -50,6 +49,8 @@
:host(.legacy-toggle) {
contain: content;
touch-action: none;
}
:host(.ion-focused) input {
@ -240,8 +241,6 @@ input {
background: var(--track-background);
pointer-events: none;
overflow: inherit;
}

View File

@ -40,6 +40,8 @@ export class Toggle implements ComponentInterface {
private lastDrag = 0;
private legacyFormController!: LegacyFormController;
private inheritedAttributes: Attributes = {};
private toggleTrack?: HTMLElement;
private didLoad = false;
// This flag ensures we log the deprecation warning at most once.
private hasLoggedDeprecationWarning = false;
@ -157,23 +159,42 @@ export class Toggle implements ComponentInterface {
}
async connectedCallback() {
const { el } = this;
this.legacyFormController = createLegacyFormController(el);
this.gesture = (await import('../../utils/gesture')).createGesture({
el,
gestureName: 'toggle',
gesturePriority: 100,
threshold: 5,
passive: false,
onStart: () => this.onStart(),
onMove: (ev) => this.onMove(ev),
onEnd: (ev) => this.onEnd(ev),
});
this.disabledChanged();
this.legacyFormController = createLegacyFormController(this.el);
/**
* If we have not yet rendered
* ion-toggle, then toggleTrack is not defined.
* But if we are moving ion-toggle via appendChild,
* then toggleTrack will be defined.
*/
if (this.didLoad) {
this.setupGesture();
}
}
componentDidLoad() {
this.setupGesture();
this.didLoad = true;
}
private setupGesture = async () => {
const { toggleTrack } = this;
if (toggleTrack) {
this.gesture = (await import('../../utils/gesture')).createGesture({
el: toggleTrack,
gestureName: 'toggle',
gesturePriority: 100,
threshold: 5,
passive: false,
onStart: () => this.onStart(),
onMove: (ev) => this.onMove(ev),
onEnd: (ev) => this.onEnd(ev),
});
this.disabledChanged();
}
};
disconnectedCallback() {
if (this.gesture) {
this.gesture.destroy();
@ -273,7 +294,7 @@ export class Toggle implements ComponentInterface {
const { enableOnOffLabels, checked } = this;
return (
<div class="toggle-icon" part="track">
<div class="toggle-icon" part="track" ref={(el) => (this.toggleTrack = el)}>
{/* The iOS on/off labels are rendered outside of .toggle-icon-wrapper,
since the wrapper is translated when the handle is interacted with and
this would move the on/off labels outside of the view box */}