From 989429d65cf57ef8fb69854639f8eac1a12369bc Mon Sep 17 00:00:00 2001 From: Liam DeBeasi Date: Tue, 14 Jun 2022 15:12:06 -0400 Subject: [PATCH] fix(picker-column-internal): switching off an input mode column preserves scroll (#25467) --- .../picker-column-internal.tsx | 47 ++++++++++++++++--- 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/core/src/components/picker-column-internal/picker-column-internal.tsx b/core/src/components/picker-column-internal/picker-column-internal.tsx index 8110b2d0ab..77f473e146 100644 --- a/core/src/components/picker-column-internal/picker-column-internal.tsx +++ b/core/src/components/picker-column-internal/picker-column-internal.tsx @@ -24,7 +24,8 @@ import type { PickerColumnItem } from './picker-column-internal-interfaces'; }) export class PickerColumnInternal implements ComponentInterface { private destroyScrollListener?: () => void; - private hapticsStarted = false; + private isScrolling = false; + private scrollEndCallback?: () => void; private isColumnVisible = false; @State() isActive = false; @@ -187,11 +188,30 @@ export class PickerColumnInternal implements ComponentInterface { const isColumnActive = inputModeColumn === undefined || inputModeColumn === this.el; if (!useInputMode || !isColumnActive) { - this.isActive = false; + this.setInputModeActive(false); return; } - this.isActive = true; + this.setInputModeActive(true); + }; + + /** + * Setting isActive will cause a re-render. + * As a result, we do not want to cause the + * re-render mid scroll as this will cause + * the picker column to jump back to + * whatever value was selected at the + * start of the scroll interaction. + */ + private setInputModeActive = (state: boolean) => { + if (this.isScrolling) { + this.scrollEndCallback = () => { + this.isActive = state; + }; + return; + } + + this.isActive = state; }; /** @@ -213,9 +233,9 @@ export class PickerColumnInternal implements ComponentInterface { timeout = undefined; } - if (!this.hapticsStarted) { + if (!this.isScrolling) { hapticSelectionStart(); - this.hapticsStarted = true; + this.isScrolling = true; } /** @@ -243,6 +263,21 @@ export class PickerColumnInternal implements ComponentInterface { activeElement.classList.add(PICKER_COL_ACTIVE); timeout = setTimeout(() => { + this.isScrolling = false; + hapticSelectionEnd(); + + /** + * Certain tasks (such as those that + * cause re-renders) should only be done + * once scrolling has finished, otherwise + * flickering may occur. + */ + const { scrollEndCallback } = this; + if (scrollEndCallback) { + scrollEndCallback(); + this.scrollEndCallback = undefined; + } + const dataIndex = activeElement.getAttribute('data-index'); /** @@ -259,8 +294,6 @@ export class PickerColumnInternal implements ComponentInterface { if (selectedItem.value !== this.value) { this.setValue(selectedItem.value); - hapticSelectionEnd(); - this.hapticsStarted = false; } }, 250); });