mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-19 03:32:21 +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;
|
"value"?: string | number;
|
||||||
}
|
}
|
||||||
interface IonPickerInternal {
|
interface IonPickerInternal {
|
||||||
|
"exitInputMode": () => Promise<void>;
|
||||||
/**
|
/**
|
||||||
* The mode determines which platform styles to use.
|
* The mode determines which platform styles to use.
|
||||||
*/
|
*/
|
||||||
|
@ -27,6 +27,8 @@ export class PickerColumnInternal implements ComponentInterface {
|
|||||||
private isScrolling = false;
|
private isScrolling = false;
|
||||||
private scrollEndCallback?: () => void;
|
private scrollEndCallback?: () => void;
|
||||||
private isColumnVisible = false;
|
private isColumnVisible = false;
|
||||||
|
private parentEl?: HTMLIonPickerInternalElement | null;
|
||||||
|
private canExitInputMode = true;
|
||||||
|
|
||||||
@State() isActive = false;
|
@State() isActive = false;
|
||||||
|
|
||||||
@ -109,7 +111,7 @@ export class PickerColumnInternal implements ComponentInterface {
|
|||||||
};
|
};
|
||||||
new IntersectionObserver(visibleCallback, { threshold: 0.001 }).observe(this.el);
|
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) {
|
if (parentEl !== null) {
|
||||||
parentEl.addEventListener('ionInputModeChange', (ev: any) => this.inputModeChange(ev));
|
parentEl.addEventListener('ionInputModeChange', (ev: any) => this.inputModeChange(ev));
|
||||||
}
|
}
|
||||||
@ -140,7 +142,7 @@ export class PickerColumnInternal implements ComponentInterface {
|
|||||||
const activeEl = this.activeItem;
|
const activeEl = this.activeItem;
|
||||||
|
|
||||||
if (activeEl) {
|
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;
|
const { el, isColumnVisible } = this;
|
||||||
if (isColumnVisible) {
|
if (isColumnVisible) {
|
||||||
// (Vertical offset from parent) - (three empty picker rows) + (half the height of the target to ensure the scroll triggers)
|
// (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;
|
const top = target.offsetTop - 3 * target.clientHeight + target.clientHeight / 2;
|
||||||
|
|
||||||
if (el.scrollTop !== top) {
|
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({
|
el.scroll({
|
||||||
top,
|
top,
|
||||||
left: 0,
|
left: 0,
|
||||||
@ -270,6 +280,21 @@ export class PickerColumnInternal implements ComponentInterface {
|
|||||||
*/
|
*/
|
||||||
if (activeElement !== activeEl) {
|
if (activeElement !== activeEl) {
|
||||||
hapticSelectionChanged();
|
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;
|
activeEl = activeElement;
|
||||||
@ -291,6 +316,14 @@ export class PickerColumnInternal implements ComponentInterface {
|
|||||||
this.scrollEndCallback = undefined;
|
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');
|
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() {
|
get activeItem() {
|
||||||
return getElementRoot(this.el).querySelector(
|
return getElementRoot(this.el).querySelector(
|
||||||
`.picker-item[data-value="${this.value}"]:not([disabled])`
|
`.picker-item[data-value="${this.value}"]:not([disabled])`
|
||||||
@ -368,7 +426,7 @@ export class PickerColumnInternal implements ComponentInterface {
|
|||||||
data-value={item.value}
|
data-value={item.value}
|
||||||
data-index={index}
|
data-index={index}
|
||||||
onClick={(ev: Event) => {
|
onClick={(ev: Event) => {
|
||||||
this.centerPickerItemInView(ev.target as HTMLElement);
|
this.centerPickerItemInView(ev.target as HTMLElement, true);
|
||||||
}}
|
}}
|
||||||
disabled={item.disabled}
|
disabled={item.disabled}
|
||||||
>
|
>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import type { ComponentInterface, EventEmitter } from '@stencil/core';
|
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';
|
import { getElementRoot } from '../../utils/helpers';
|
||||||
|
|
||||||
@ -273,7 +273,14 @@ export class PickerInternal implements ComponentInterface {
|
|||||||
this.emitInputModeChange();
|
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;
|
const { inputEl, useInputMode } = this;
|
||||||
if (!useInputMode || !inputEl) {
|
if (!useInputMode || !inputEl) {
|
||||||
return;
|
return;
|
||||||
@ -290,7 +297,7 @@ export class PickerInternal implements ComponentInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.emitInputModeChange();
|
this.emitInputModeChange();
|
||||||
};
|
}
|
||||||
|
|
||||||
private onKeyPress = (ev: KeyboardEvent) => {
|
private onKeyPress = (ev: KeyboardEvent) => {
|
||||||
const { inputEl } = this;
|
const { inputEl } = this;
|
||||||
|
Reference in New Issue
Block a user