From 722f32a0056f4565ab7efd8c4a3220e413f8c7a4 Mon Sep 17 00:00:00 2001 From: Juwon Jeong Date: Fri, 27 Dec 2024 15:42:21 +0900 Subject: [PATCH] WRR-14337: Fix EditableWrapper to enable hover to scroll in touch mode Enact-DCO-1.0-Signed-off-by: Juwon Jeong (juwon.jeong@lge.com) --- Scroller/EditableWrapper.js | 36 ++++++++++++++---- useScroll/HoverToScroll.js | 58 ++++++++++++++--------------- useScroll/HoverToScroll.module.less | 3 ++ 3 files changed, 58 insertions(+), 39 deletions(-) diff --git a/Scroller/EditableWrapper.js b/Scroller/EditableWrapper.js index 342c31882f..ef74580047 100644 --- a/Scroller/EditableWrapper.js +++ b/Scroller/EditableWrapper.js @@ -542,6 +542,16 @@ const EditableWrapper = (props) => { return Math.floor((clientXFromContent - moveTolerance) / itemWidth); }, [scrollContainerHandle, scrollContentRef]); + const handlePointerDown = useCallback((ev) => { + const {selectedItem} = mutableRef.current; + + if (selectedItem) { + if (ev.target.hasPointerCapture(ev.pointerId)) { + ev.target.releasePointerCapture(ev.pointerId); + } + } + }, []); + const handleMouseMove = useCallback((ev) => { const {clientX} = ev; mutableRef.current.lastMouseClientX = clientX; @@ -735,23 +745,23 @@ const EditableWrapper = (props) => { if (mutableRef.current.isDraggingItem && ev.target?.parentNode?.parentNode.getAttribute('role') !== 'button') { const {clientX} = ev.targetTouches[0]; mutableRef.current.lastMouseClientX = clientX; + scrollContainerRef.current.style.setProperty('--scroller-hover-to-scroll-by-touch', 'auto'); const toIndex = getNextIndexFromPosition(clientX, 0.33); if (toIndex !== mutableRef.current.prevToIndex) { - scrollContainerHandle.current.start({ - targetX: clientX, - targetY: 0 - }); mutableRef.current.lastInputType = 'touch'; moveItems(toIndex); } } - - }, [getNextIndexFromPosition, moveItems, scrollContainerHandle]); + }, [getNextIndexFromPosition, moveItems, scrollContainerRef]); const handleTouchEnd = useCallback((ev) => { - const {selectedItem} = mutableRef.current; + const {itemWidth, lastInputType, lastMouseClientX, selectedItem} = mutableRef.current; + const {rtl} = scrollContainerHandle.current; + const scrollContentNode = scrollContentRef.current; + const scrollContentCenter = scrollContentNode.getBoundingClientRect().width / 2; + const {clientX} = ev.changedTouches[0]; const targetItemIndex = getNextIndexFromPosition(clientX, 0); @@ -766,6 +776,14 @@ const EditableWrapper = (props) => { // Finalize orders and forward `onComplete` event const orders = finalizeOrders(); finalizeEditing(orders); + + if (lastInputType === 'scroll' && mutableRef.current.isDragging) { + const offset = itemWidth * (!rtl ^ !(lastMouseClientX > scrollContentCenter) ? 1 : -1); + scrollContainerHandle.current.start({ + targetX: scrollContentNode.scrollLeft + offset, + targetY: 0 + }); + } } else if (!mutableRef.current.isDragging) { // Cancel mouse event to select a item when it is tapped ev.preventDefault(); @@ -780,7 +798,8 @@ const EditableWrapper = (props) => { } mutableRef.current.isDraggingItem = false; mutableRef.current.isDragging = false; - }, [getNextIndexFromPosition, finalizeEditing, finalizeOrders, findItemNode, selectItemBy, startEditing]); + scrollContainerRef.current.style.setProperty('--scroller-hover-to-scroll-by-touch', 'none'); + }, [getNextIndexFromPosition, finalizeEditing, finalizeOrders, findItemNode, scrollContainerHandle, scrollContainerRef, scrollContentRef, selectItemBy, startEditing]); const handleDragStart = useCallback((ev) => { const {selectedItem} = mutableRef.current; @@ -952,6 +971,7 @@ const EditableWrapper = (props) => { onKeyUpCapture={handleKeyUpCapture} onMouseDown={handleMouseDown} onMouseMove={handleMouseMove} + onPointerDown={handlePointerDown} onDragStart={handleDragStart} onTouchEnd={handleTouchEnd} ref={wrapperRef} diff --git a/useScroll/HoverToScroll.js b/useScroll/HoverToScroll.js index e16847b869..b1b5de5898 100644 --- a/useScroll/HoverToScroll.js +++ b/useScroll/HoverToScroll.js @@ -7,8 +7,6 @@ import classNames from 'classnames'; import PropTypes from 'prop-types'; import {useCallback, useLayoutEffect, useRef, useState} from 'react'; -import {getLastInputType} from '../ThemeDecorator'; - import css from './HoverToScroll.module.less'; const {epsilon} = constants; @@ -117,35 +115,33 @@ const HoverToScrollBase = (props) => { const {axis, clientSize, maxPosition, scrollPosition} = getBoundsPropertyNames(direction); const bounds = scrollContainer.getScrollBounds(); - return function ({pointerType}) { - if (pointerType === 'mouse') { - const distance = - (position === 'before' ? -1 : 1) * // scroll direction - bounds[clientSize] * // scroll page size - hoverToScrollMultiplier[direction]; // a scrolling speed factor - - mutableRef.current.hoveredPosition = position; - mutableRef.current.stopScrollByHover = false; - - const scrollByHover = () => { - if (!mutableRef.current.stopScrollByHover && getLastInputType() === 'mouse') { - scrollContainer.scrollTo({ - position: { - [axis]: clamp( - 0, - bounds[maxPosition], - scrollContainer[scrollPosition] + distance - ) - }, - animate: false - }); - startRaf(scrollByHover); - } else { - stopRaf(); // for other type input during hovering - } - }; - startRaf(scrollByHover); - } + return function () { + const distance = + (position === 'before' ? -1 : 1) * // scroll direction + bounds[clientSize] * // scroll page size + hoverToScrollMultiplier[direction]; // a scrolling speed factor + + mutableRef.current.hoveredPosition = position; + mutableRef.current.stopScrollByHover = false; + + const scrollByHover = () => { + if (!mutableRef.current.stopScrollByHover) { + scrollContainer.scrollTo({ + position: { + [axis]: clamp( + 0, + bounds[maxPosition], + scrollContainer[scrollPosition] + distance + ) + }, + animate: false + }); + startRaf(scrollByHover); + } else { + stopRaf(); // for other type input during hovering + } + }; + startRaf(scrollByHover); }; } else { return nop; diff --git a/useScroll/HoverToScroll.module.less b/useScroll/HoverToScroll.module.less index e3e8affbd4..aadfa0ed09 100644 --- a/useScroll/HoverToScroll.module.less +++ b/useScroll/HoverToScroll.module.less @@ -10,6 +10,9 @@ :global(.spotlight-input-mouse) & { pointer-events: auto; } + :global(.spotlight-input-touch) & { + pointer-events: var(--scroller-hover-to-scroll-by-touch, none) + } &.vertical { width: 100%; height: @sand-scroll-hover-area-size;