mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-18 19:21:34 +08:00
fix(datetime): time wheel input mode is dismissed on user scroll (#26567)
This commit is contained in:
1
core/src/components.d.ts
vendored
1
core/src/components.d.ts
vendored
@ -1918,6 +1918,7 @@ export namespace Components {
|
||||
"value"?: string | number;
|
||||
}
|
||||
interface IonPickerInternal {
|
||||
"exitInputMode": () => Promise<void>;
|
||||
/**
|
||||
* The mode determines which platform styles to use.
|
||||
*/
|
||||
|
@ -27,6 +27,8 @@ export class PickerColumnInternal implements ComponentInterface {
|
||||
private isScrolling = false;
|
||||
private scrollEndCallback?: () => void;
|
||||
private isColumnVisible = false;
|
||||
private parentEl?: HTMLIonPickerInternalElement | null;
|
||||
private canExitInputMode = true;
|
||||
|
||||
@State() isActive = false;
|
||||
|
||||
@ -109,7 +111,7 @@ export class PickerColumnInternal implements ComponentInterface {
|
||||
};
|
||||
new IntersectionObserver(visibleCallback, { threshold: 0.001 }).observe(this.el);
|
||||
|
||||
const parentEl = this.el.closest('ion-picker-internal') as HTMLIonPickerInternalElement | null;
|
||||
const parentEl = (this.parentEl = this.el.closest('ion-picker-internal') as HTMLIonPickerInternalElement | null);
|
||||
if (parentEl !== null) {
|
||||
parentEl.addEventListener('ionInputModeChange', (ev: any) => this.inputModeChange(ev));
|
||||
}
|
||||
@ -140,7 +142,7 @@ export class PickerColumnInternal implements ComponentInterface {
|
||||
const activeEl = this.activeItem;
|
||||
|
||||
if (activeEl) {
|
||||
this.centerPickerItemInView(activeEl, false);
|
||||
this.centerPickerItemInView(activeEl, false, false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -161,13 +163,21 @@ export class PickerColumnInternal implements ComponentInterface {
|
||||
}
|
||||
}
|
||||
|
||||
private centerPickerItemInView = (target: HTMLElement, smooth = true) => {
|
||||
private centerPickerItemInView = (target: HTMLElement, smooth = true, canExitInputMode = true) => {
|
||||
const { el, isColumnVisible } = this;
|
||||
if (isColumnVisible) {
|
||||
// (Vertical offset from parent) - (three empty picker rows) + (half the height of the target to ensure the scroll triggers)
|
||||
const top = target.offsetTop - 3 * target.clientHeight + target.clientHeight / 2;
|
||||
|
||||
if (el.scrollTop !== top) {
|
||||
/**
|
||||
* Setting this flag prevents input
|
||||
* mode from exiting in the picker column's
|
||||
* scroll callback. This is useful when the user manually
|
||||
* taps an item or types on the keyboard as both
|
||||
* of these can cause a scroll to occur.
|
||||
*/
|
||||
this.canExitInputMode = canExitInputMode;
|
||||
el.scroll({
|
||||
top,
|
||||
left: 0,
|
||||
@ -270,6 +280,21 @@ export class PickerColumnInternal implements ComponentInterface {
|
||||
*/
|
||||
if (activeElement !== activeEl) {
|
||||
hapticSelectionChanged();
|
||||
|
||||
if (this.canExitInputMode) {
|
||||
/**
|
||||
* The native iOS wheel picker
|
||||
* only dismisses the keyboard
|
||||
* once the selected item has changed
|
||||
* as a result of a swipe
|
||||
* from the user. If `canExitInputMode` is
|
||||
* `false` then this means that the
|
||||
* scroll is happening as a result of
|
||||
* the `value` property programmatically changing
|
||||
* either by an application or by the user via the keyboard.
|
||||
*/
|
||||
this.exitInputMode();
|
||||
}
|
||||
}
|
||||
|
||||
activeEl = activeElement;
|
||||
@ -291,6 +316,14 @@ export class PickerColumnInternal implements ComponentInterface {
|
||||
this.scrollEndCallback = undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset this flag as the
|
||||
* next scroll interaction could
|
||||
* be a scroll from the user. In this
|
||||
* case, we should exit input mode.
|
||||
*/
|
||||
this.canExitInputMode = true;
|
||||
|
||||
const dataIndex = activeElement.getAttribute('data-index');
|
||||
|
||||
/**
|
||||
@ -325,6 +358,31 @@ export class PickerColumnInternal implements ComponentInterface {
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Tells the parent picker to
|
||||
* exit text entry mode. This is only called
|
||||
* when the selected item changes during scroll, so
|
||||
* we know that the user likely wants to scroll
|
||||
* instead of type.
|
||||
*/
|
||||
private exitInputMode = () => {
|
||||
const { parentEl } = this;
|
||||
|
||||
if (parentEl == null) return;
|
||||
|
||||
parentEl.exitInputMode();
|
||||
|
||||
/**
|
||||
* setInputModeActive only takes
|
||||
* effect once scrolling stops to avoid
|
||||
* a component re-render while scrolling.
|
||||
* However, we want the visual active
|
||||
* indicator to go away immediately, so
|
||||
* we call classList.remove here.
|
||||
*/
|
||||
this.el.classList.remove('picker-column-active');
|
||||
};
|
||||
|
||||
get activeItem() {
|
||||
return getElementRoot(this.el).querySelector(
|
||||
`.picker-item[data-value="${this.value}"]:not([disabled])`
|
||||
@ -368,7 +426,7 @@ export class PickerColumnInternal implements ComponentInterface {
|
||||
data-value={item.value}
|
||||
data-index={index}
|
||||
onClick={(ev: Event) => {
|
||||
this.centerPickerItemInView(ev.target as HTMLElement);
|
||||
this.centerPickerItemInView(ev.target as HTMLElement, true);
|
||||
}}
|
||||
disabled={item.disabled}
|
||||
>
|
||||
|
@ -1,5 +1,5 @@
|
||||
import type { ComponentInterface, EventEmitter } from '@stencil/core';
|
||||
import { Component, Element, Event, Listen, Host, h } from '@stencil/core';
|
||||
import { Component, Element, Event, Listen, Method, Host, h } from '@stencil/core';
|
||||
|
||||
import { getElementRoot } from '../../utils/helpers';
|
||||
|
||||
@ -273,7 +273,14 @@ export class PickerInternal implements ComponentInterface {
|
||||
this.emitInputModeChange();
|
||||
};
|
||||
|
||||
private exitInputMode = () => {
|
||||
/**
|
||||
* @internal
|
||||
* Exits text entry mode for the picker
|
||||
* This method blurs the hidden input
|
||||
* and cause the keyboard to dismiss.
|
||||
*/
|
||||
@Method()
|
||||
async exitInputMode() {
|
||||
const { inputEl, useInputMode } = this;
|
||||
if (!useInputMode || !inputEl) {
|
||||
return;
|
||||
@ -290,7 +297,7 @@ export class PickerInternal implements ComponentInterface {
|
||||
}
|
||||
|
||||
this.emitInputModeChange();
|
||||
};
|
||||
}
|
||||
|
||||
private onKeyPress = (ev: KeyboardEvent) => {
|
||||
const { inputEl } = this;
|
||||
|
Reference in New Issue
Block a user