From b907a5be97d6ccf2e55eb38b092a10b42e188167 Mon Sep 17 00:00:00 2001 From: Seungcheon Baek Date: Sun, 20 Feb 2022 20:01:58 +0900 Subject: [PATCH 1/7] WRN-16706: POC for item reordering in VirtualList Enact-DCO-1.0-Signed-off-by: Seungcheon Baek (sc.baek@lge.com) --- packages/ui/VirtualList/VirtualList.js | 6 ++++++ packages/ui/VirtualList/VirtualListBasic.js | 11 +++++++++++ packages/ui/useScroll/useScroll.js | 2 ++ 3 files changed, 19 insertions(+) diff --git a/packages/ui/VirtualList/VirtualList.js b/packages/ui/VirtualList/VirtualList.js index 2e81a79980..cd8df4e1a5 100644 --- a/packages/ui/VirtualList/VirtualList.js +++ b/packages/ui/VirtualList/VirtualList.js @@ -71,6 +71,8 @@ const VirtualList = (props) => { VirtualList.displayName = 'ui:VirtualList'; VirtualList.propTypes = /** @lends ui/VirtualList.VirtualList.prototype */ { + cbMoveItem: PropTypes.func, + /** * A callback function that receives a reference to the `scrollTo` feature. * @@ -277,6 +279,7 @@ VirtualList.propTypes = /** @lends ui/VirtualList.VirtualList.prototype */ { }; VirtualList.defaultProps = { + cbMoveItem: nop, cbScrollTo: nop, direction: 'vertical', horizontalScrollbar: 'auto', @@ -341,6 +344,8 @@ const VirtualGridList = (props) => { VirtualGridList.displayName = 'ui:VirtualGridList'; VirtualGridList.propTypes = /** @lends ui/VirtualList.VirtualGridList.prototype */ { + cbMoveItem: PropTypes.func, + /** * A callback function that receives a reference to the `scrollTo` feature. * @@ -547,6 +552,7 @@ VirtualGridList.propTypes = /** @lends ui/VirtualList.VirtualGridList.prototype }; VirtualGridList.defaultProps = { + cbMoveItem: nop, cbScrollTo: nop, direction: 'vertical', horizontalScrollbar: 'auto', diff --git a/packages/ui/VirtualList/VirtualListBasic.js b/packages/ui/VirtualList/VirtualListBasic.js index c3f36b05c2..781f4dd786 100644 --- a/packages/ui/VirtualList/VirtualListBasic.js +++ b/packages/ui/VirtualList/VirtualListBasic.js @@ -93,6 +93,8 @@ class VirtualListBasic extends Component { gridListItemSizeShape ]).isRequired, + cbMoveItem: PropTypes.func, + /** * Callback method of scrollTo. * Normally, useScroll should set this value. @@ -297,6 +299,7 @@ class VirtualListBasic extends Component { }; static defaultProps = { + cbMoveItem: nop, cbScrollTo: nop, dataSize: 0, direction: 'vertical', @@ -329,6 +332,9 @@ class VirtualListBasic extends Component { updateTo: 0, ...nextState }; + + props.cbMoveItem(this.moveItem); + window.moveItem = this.moveItem; // FIXME: for testing only } static getDerivedStateFromProps (props, state) { @@ -1218,6 +1224,10 @@ class VirtualListBasic extends Component { return false; }; + moveItem = (from, to) => { + console.log('moveItem', from, to); + } + // render render () { @@ -1233,6 +1243,7 @@ class VirtualListBasic extends Component { ), contentClasses = scrollModeNative ? null : css.content; + delete rest.cbMoveItem; delete rest.cbScrollTo; delete rest.childProps; delete rest.clientSize; diff --git a/packages/ui/useScroll/useScroll.js b/packages/ui/useScroll/useScroll.js index 9a1b6d662d..8cda4c21a5 100644 --- a/packages/ui/useScroll/useScroll.js +++ b/packages/ui/useScroll/useScroll.js @@ -91,6 +91,7 @@ const useScrollBase = (props) => { 'data-webos-voice-focused': voiceFocused, 'data-webos-voice-group-label': voiceGroupLabel, assignProperties, + cbMoveItem, dataSize, direction, horizontalScrollbar, @@ -1541,6 +1542,7 @@ const useScrollBase = (props) => { assignProperties('scrollContentProps', { ...scrollContentProps, ...voiceProps, + cbMoveItem, cbScrollTo: scrollTo, className: [css.scrollFill], direction, From 1175ee00099f8a2714d5f2542ca2d2a70ea0a910 Mon Sep 17 00:00:00 2001 From: Seungcheon Baek Date: Mon, 21 Feb 2022 16:59:36 +0900 Subject: [PATCH 2/7] Try to move item by pseudo dragging Enact-DCO-1.0-Signed-off-by: Seungcheon Baek (sc.baek@lge.com) --- packages/ui/VirtualList/VirtualList.js | 18 ++-- packages/ui/VirtualList/VirtualListBasic.js | 106 ++++++++++++++++---- packages/ui/useScroll/useScroll.js | 6 +- 3 files changed, 100 insertions(+), 30 deletions(-) diff --git a/packages/ui/VirtualList/VirtualList.js b/packages/ui/VirtualList/VirtualList.js index cd8df4e1a5..13cfa35ef3 100644 --- a/packages/ui/VirtualList/VirtualList.js +++ b/packages/ui/VirtualList/VirtualList.js @@ -71,8 +71,6 @@ const VirtualList = (props) => { VirtualList.displayName = 'ui:VirtualList'; VirtualList.propTypes = /** @lends ui/VirtualList.VirtualList.prototype */ { - cbMoveItem: PropTypes.func, - /** * A callback function that receives a reference to the `scrollTo` feature. * @@ -118,6 +116,8 @@ VirtualList.propTypes = /** @lends ui/VirtualList.VirtualList.prototype */ { */ direction: PropTypes.oneOf(['horizontal', 'vertical']), + editMode: PropTypes.bool, + /** * Specifies how to show horizontal scrollbar. * @@ -228,6 +228,8 @@ VirtualList.propTypes = /** @lends ui/VirtualList.VirtualList.prototype */ { */ onScrollStop: PropTypes.func, + onUpdateItemsOrder: PropTypes.func, + /** * Specifies overscroll effects shows on which type of inputs. * @@ -279,15 +281,16 @@ VirtualList.propTypes = /** @lends ui/VirtualList.VirtualList.prototype */ { }; VirtualList.defaultProps = { - cbMoveItem: nop, cbScrollTo: nop, direction: 'vertical', + editMode: false, horizontalScrollbar: 'auto', noScrollByDrag: false, noScrollByWheel: false, onScroll: nop, onScrollStart: nop, onScrollStop: nop, + onUpdateItemsOrder: nop, overscrollEffectOn: { drag: false, pageKey: false, @@ -344,8 +347,6 @@ const VirtualGridList = (props) => { VirtualGridList.displayName = 'ui:VirtualGridList'; VirtualGridList.propTypes = /** @lends ui/VirtualList.VirtualGridList.prototype */ { - cbMoveItem: PropTypes.func, - /** * A callback function that receives a reference to the `scrollTo` feature. * @@ -391,6 +392,8 @@ VirtualGridList.propTypes = /** @lends ui/VirtualList.VirtualGridList.prototype */ direction: PropTypes.oneOf(['horizontal', 'vertical']), + editMode: PropTypes.bool, + /** * Specifies how to show horizontal scrollbar. * @@ -501,6 +504,8 @@ VirtualGridList.propTypes = /** @lends ui/VirtualList.VirtualGridList.prototype */ onScrollStop: PropTypes.func, + onUpdateItemsOrder: PropTypes.func, + /** * Specifies overscroll effects shows on which type of inputs. * @@ -552,15 +557,16 @@ VirtualGridList.propTypes = /** @lends ui/VirtualList.VirtualGridList.prototype }; VirtualGridList.defaultProps = { - cbMoveItem: nop, cbScrollTo: nop, direction: 'vertical', + editMode: false, horizontalScrollbar: 'auto', noScrollByDrag: false, noScrollByWheel: false, onScroll: nop, onScrollStart: nop, onScrollStop: nop, + onUpdateItemsOrder: nop, overscrollEffectOn: { drag: false, pageKey: false, diff --git a/packages/ui/VirtualList/VirtualListBasic.js b/packages/ui/VirtualList/VirtualListBasic.js index 781f4dd786..7ef17acd62 100644 --- a/packages/ui/VirtualList/VirtualListBasic.js +++ b/packages/ui/VirtualList/VirtualListBasic.js @@ -3,6 +3,7 @@ import EnactPropTypes from '@enact/core/internal/prop-types'; import {forward} from '@enact/core/handle'; import {platform} from '@enact/core/platform'; import {clamp} from '@enact/core/util'; +import {utilDOM} from '@enact/ui/useScroll/utilDOM'; import PropTypes from 'prop-types'; import equals from 'ramda/src/equals'; import {createRef, Component} from 'react'; @@ -93,8 +94,6 @@ class VirtualListBasic extends Component { gridListItemSizeShape ]).isRequired, - cbMoveItem: PropTypes.func, - /** * Callback method of scrollTo. * Normally, useScroll should set this value. @@ -171,6 +170,8 @@ class VirtualListBasic extends Component { */ direction: PropTypes.oneOf(['horizontal', 'vertical']), + editMode: PropTypes.bool, + /** * Called to get the scroll affordance from themed component. * @@ -203,6 +204,8 @@ class VirtualListBasic extends Component { */ itemSizes: PropTypes.arrayOf(PropTypes.number), + onUpdateItemsOrder: PropTypes.func, + /** * Called when the range of items has updated. * @@ -299,10 +302,10 @@ class VirtualListBasic extends Component { }; static defaultProps = { - cbMoveItem: nop, cbScrollTo: nop, dataSize: 0, direction: 'vertical', + editMode: false, getAffordance: () => (0), overhang: 3, pageScroll: false, @@ -330,10 +333,11 @@ class VirtualListBasic extends Component { prevFirstIndex: 0, updateFrom: 0, updateTo: 0, + editMode: props.editMode, ...nextState }; - props.cbMoveItem(this.moveItem); + this.emitUpdateItemsOrder = props.onUpdateItemsOrder; window.moveItem = this.moveItem; // FIXME: for testing only } @@ -370,6 +374,13 @@ class VirtualListBasic extends Component { } else { this.setContainerSize(); } + + if (this.props.scrollContentRef.current) { + const node = this.props.scrollContentRef.current; + node.addEventListener('mousedown', this.itemMovingBegin); + node.addEventListener('mousemove', this.itemMoving); + node.addEventListener('mouseup', this.itemMovingEnd); + } } componentDidUpdate (prevProps, prevState) { @@ -511,6 +522,9 @@ class VirtualListBasic extends Component { itemPositions = []; indexToScrollIntoView = -1; + // Edit mode + editingIndex = null; + updateScrollPosition = ({x, y}, rtl = this.props.rtl) => { if (this.props.scrollMode === 'native') { this.scrollToPosition(x, y, rtl); @@ -530,7 +544,7 @@ class VirtualListBasic extends Component { getCenterItemIndexFromScrollPosition = (scrollPosition) => Math.floor((scrollPosition + (this.primary.clientSize / 2)) / this.primary.gridSize) * this.dimensionToExtent + Math.floor(this.dimensionToExtent / 2); - getGridPosition (index) { + getGridPosition (index) { // TBD const {dataSize, itemSizes} = this.props, {dimensionToExtent, itemPositions, primary, secondary} = this, @@ -561,7 +575,7 @@ class VirtualListBasic extends Component { } // For individually sized item - getItemBottomPosition = (index) => { + getItemBottomPosition = (index) => { // TBD const itemPosition = this.itemPositions[index], itemSize = this.props.itemSizes[index]; @@ -574,11 +588,11 @@ class VirtualListBasic extends Component { }; // For individually sized item - getItemTopPositionFromPreviousItemBottomPosition = (index, spacing) => { + getItemTopPositionFromPreviousItemBottomPosition = (index, spacing) => { // TBD return index === 0 ? 0 : this.getItemBottomPosition(index - 1) + spacing; }; - getItemPosition = (index, stickTo = 'start', optionalOffset = 0) => { + getItemPosition = (index, stickTo = 'start', optionalOffset = 0) => { // TBD const {isPrimaryDirectionVertical, primary, scrollBounds} = this; const maxPos = isPrimaryDirectionVertical ? scrollBounds.maxTop : scrollBounds.maxLeft; const position = this.getGridPosition(index); @@ -688,7 +702,7 @@ class VirtualListBasic extends Component { } } - getStatesAndUpdateBounds = (props, firstIndex = 0) => { + getStatesAndUpdateBounds = (props, firstIndex = 0) => { // TBD const {dataSize, overhang, updateStatesAndBounds} = props, {dimensionToExtent, primary, moreInfo, scrollPosition} = this, @@ -727,7 +741,7 @@ class VirtualListBasic extends Component { }; }; - calculateFirstIndex (props, wasFirstIndexMax, dataSizeDiff, firstIndex) { + calculateFirstIndex (props, wasFirstIndexMax, dataSizeDiff, firstIndex) { // TBD const {overhang} = props, {dimensionToExtent, isPrimaryDirectionVertical, maxFirstIndex, primary, scrollBounds, scrollPosition, threshold} = this, @@ -981,7 +995,7 @@ class VirtualListBasic extends Component { } // For individually sized item - calculateAndCacheItemPosition (index) { + calculateAndCacheItemPosition (index) { // TBD const {itemSizes} = this.props; if (!this.itemPositions[index] && itemSizes[index]) { @@ -994,7 +1008,7 @@ class VirtualListBasic extends Component { } // For individually sized item - applyItemPositionToDOMElement (index) { + applyItemPositionToDOMElement (index) { // TBD const {direction, rtl} = this.props, {numOfItems} = this.state, @@ -1096,39 +1110,40 @@ class VirtualListBasic extends Component { return style; } - applyStyleToNewNode = (index, ...rest) => { + applyStyleToNewNode = (index, ...rest) => { // TBD + const dataIndex = index; const {childProps, itemRefs, itemRenderer, getComponentProps} = this.props, key = index % this.state.numOfItems, - componentProps = getComponentProps && getComponentProps(index) || {}, + componentProps = getComponentProps && getComponentProps(dataIndex) || {}, itemContainerRef = (ref) => { if (ref === null) { itemRefs.current[key] = ref; } else { const itemNode = ref.children[0]; - itemRefs.current[key] = (parseInt(itemNode.dataset.index) === index) ? + itemRefs.current[key] = (parseInt(itemNode.dataset.index) === dataIndex) ? itemNode : - ref.querySelector(`[data-index="${index}"]`); + ref.querySelector(`[data-index="${dataIndex}"]`); // TBD this.itemContainerRefs[key] = ref; } }; - this.cc[key] = ( + this.cc[key] = ( // TBD
- {itemRenderer({...childProps, ...componentProps, index})} + {itemRenderer({...childProps, ...componentProps, index: dataIndex})}
); }; - applyStyleToHideNode = (index) => { + applyStyleToHideNode = (index) => { // TBD const {itemRefs} = this.props, key = index % this.state.numOfItems, itemContainerRef = () => (itemRefs.current[key] = null); - this.cc[key] =
; + this.cc[key] =
; // TBD }; positionItems () { @@ -1224,8 +1239,54 @@ class VirtualListBasic extends Component { return false; }; + getCCNodeFromPosition = (x, y) => { + if (typeof window !== 'undefined') { + const contentNode = this.contentRef.current; + let node = document.elementFromPoint(x, y); + if (utilDOM.containsDangerously(contentNode, node)) { + while (node.parentNode !== contentNode) { + if (node === document) { + return null; + } + node = node.parentNode; + } + + return node; + } + } + + return null; + } + + getIndexFromCCNode = (node) => (node ? parseInt(node.querySelector(`[data-index]`).dataset.index) : null) + + itemMovingBegin = ({clientX, clientY}) => { + console.log('mousedown'); + const node = this.getCCNodeFromPosition(clientX, clientY); + this.editingIndex = this.getIndexFromCCNode(node); + } + + itemMoving = ({clientX, clientY}) => { + console.log('mousemove'); + if (this.editingIndex !== null) { + const node = this.getCCNodeFromPosition(clientX, clientY); + const index = this.getIndexFromCCNode(node); + if (index !== null) { + this.moveItem(this.editingIndex, index); + this.editingIndex = index; + } + } + } + + itemMovingEnd = () => { + console.log('mouseup'); + this.editingIndex = null; + } + moveItem = (from, to) => { - console.log('moveItem', from, to); + const newItemsOrder = [...Array(this.props.dataSize).keys()]; + newItemsOrder.splice(to, 0, newItemsOrder.splice(from, 1)[0]); + this.emitUpdateItemsOrder(newItemsOrder); } // render @@ -1243,12 +1304,12 @@ class VirtualListBasic extends Component { ), contentClasses = scrollModeNative ? null : css.content; - delete rest.cbMoveItem; delete rest.cbScrollTo; delete rest.childProps; delete rest.clientSize; delete rest.dataSize; delete rest.direction; + delete rest.editMode; delete rest.getAffordance; delete rest.getComponentProps; delete rest.isHorizontalScrollbarVisible; @@ -1259,6 +1320,7 @@ class VirtualListBasic extends Component { delete rest.itemSizes; delete rest.onUpdate; delete rest.onUpdateItems; + delete rest.onUpdateItemsOrder; delete rest.overhang; delete rest.pageScroll; delete rest.rtl; diff --git a/packages/ui/useScroll/useScroll.js b/packages/ui/useScroll/useScroll.js index 8cda4c21a5..75d802b4f1 100644 --- a/packages/ui/useScroll/useScroll.js +++ b/packages/ui/useScroll/useScroll.js @@ -91,9 +91,9 @@ const useScrollBase = (props) => { 'data-webos-voice-focused': voiceFocused, 'data-webos-voice-group-label': voiceGroupLabel, assignProperties, - cbMoveItem, dataSize, direction, + editMode, horizontalScrollbar, horizontalScrollbarHandle, itemRenderer, @@ -101,6 +101,7 @@ const useScrollBase = (props) => { itemSizes, noScrollByDrag, noScrollByWheel, + onUpdateItemsOrder, overhang, overscrollEffectOn, pageScroll, @@ -1542,10 +1543,10 @@ const useScrollBase = (props) => { assignProperties('scrollContentProps', { ...scrollContentProps, ...voiceProps, - cbMoveItem, cbScrollTo: scrollTo, className: [css.scrollFill], direction, + editMode, get isHorizontalScrollbarVisible () { return isHorizontalScrollbarVisible; }, @@ -1553,6 +1554,7 @@ const useScrollBase = (props) => { return isVerticalScrollbarVisible; }, onScroll: scrollMode === 'translate' ? handleScroll : null, + onUpdateItemsOrder, role, rtl, scrollContainerContainsDangerously, From 5cfafc9b499e1dcd8059eab6e38f1aa95667f1b2 Mon Sep 17 00:00:00 2001 From: Seungcheon Baek Date: Thu, 24 Feb 2022 16:40:25 +0900 Subject: [PATCH 3/7] Relocate items via dragging Enact-DCO-1.0-Signed-off-by: Seungcheon Baek (sc.baek@lge.com) --- packages/ui/VirtualList/VirtualListBasic.js | 406 +++++++++++++++++--- 1 file changed, 347 insertions(+), 59 deletions(-) diff --git a/packages/ui/VirtualList/VirtualListBasic.js b/packages/ui/VirtualList/VirtualListBasic.js index 7ef17acd62..4d4732ebe4 100644 --- a/packages/ui/VirtualList/VirtualListBasic.js +++ b/packages/ui/VirtualList/VirtualListBasic.js @@ -333,12 +333,12 @@ class VirtualListBasic extends Component { prevFirstIndex: 0, updateFrom: 0, updateTo: 0, - editMode: props.editMode, ...nextState }; this.emitUpdateItemsOrder = props.onUpdateItemsOrder; window.moveItem = this.moveItem; // FIXME: for testing only + window.setEditMode = this.setEditMode; // FIXME: for testing only } static getDerivedStateFromProps (props, state) { @@ -375,12 +375,7 @@ class VirtualListBasic extends Component { this.setContainerSize(); } - if (this.props.scrollContentRef.current) { - const node = this.props.scrollContentRef.current; - node.addEventListener('mousedown', this.itemMovingBegin); - node.addEventListener('mousemove', this.itemMoving); - node.addEventListener('mouseup', this.itemMovingEnd); - } + this.setEditMode(this.props.editMode); } componentDidUpdate (prevProps, prevState) { @@ -484,6 +479,10 @@ class VirtualListBasic extends Component { this.props.cbScrollTo({position: (this.isPrimaryDirectionVertical) ? {y: maxPos} : {x: maxPos}, animate: false}); this.scrollToPositionTarget = -1; } + + if (prevProps.editMode !== this.props.editMode) { + this.setEditMode(this.props.editMode); + } } scrollBounds = { @@ -523,7 +522,15 @@ class VirtualListBasic extends Component { indexToScrollIntoView = -1; // Edit mode - editingIndex = null; + editModeInfo = { + editingIndex: null, + editingNode: null, + editingDataOrder: null, + itemSize: {width: null, height: null}, + lastPointer: {clientX: null, clientY: null}, + lastVisualIndex: null, + scrollContentBounds: {clientWidth: null, x: null, y: null} + } updateScrollPosition = ({x, y}, rtl = this.props.rtl) => { if (this.props.scrollMode === 'native') { @@ -700,6 +707,8 @@ class VirtualListBasic extends Component { this.updateScrollPosition(this.getXY(this.scrollPosition, 0)); node.style.scrollBehavior = 'smooth'; } + + this.calculateMetricsForEditMode(node); } getStatesAndUpdateBounds = (props, firstIndex = 0) => { // TBD @@ -908,7 +917,7 @@ class VirtualListBasic extends Component { didScroll (x, y) { const - {dataSize, spacing, itemSizes} = this.props, + {dataSize, spacing, itemSizes, editMode} = this.props, {firstIndex} = this.state, {isPrimaryDirectionVertical, threshold, dimensionToExtent, maxFirstIndex, scrollBounds, itemPositions} = this, {clientSize, gridSize} = this.primary, @@ -992,6 +1001,10 @@ class VirtualListBasic extends Component { if (this.shouldUpdateBounds || firstIndex !== newFirstIndex) { this.setState({firstIndex: newFirstIndex}); } + + if (editMode && this.editModeInfo.editingNode) { + this.updateMovingItem(); + } } // For individually sized item @@ -1110,11 +1123,12 @@ class VirtualListBasic extends Component { return style; } - applyStyleToNewNode = (index, ...rest) => { // TBD - const dataIndex = index; + applyStyleToNewNode = (visualIndex, ...rest) => { // TBD + //const dataIndex = this.props.editMode ? this.editModeInfo.editingDataOrder[visualIndex] : visualIndex; + const dataIndex = visualIndex; const {childProps, itemRefs, itemRenderer, getComponentProps} = this.props, - key = index % this.state.numOfItems, + key = dataIndex % this.state.numOfItems, componentProps = getComponentProps && getComponentProps(dataIndex) || {}, itemContainerRef = (ref) => { if (ref === null) { @@ -1137,15 +1151,86 @@ class VirtualListBasic extends Component { ); }; - applyStyleToHideNode = (index) => { // TBD + applyStyleToHideNode = (visualIndex) => { // TBD const {itemRefs} = this.props, - key = index % this.state.numOfItems, + key = visualIndex % this.state.numOfItems, itemContainerRef = () => (itemRefs.current[key] = null); this.cc[key] =
; // TBD }; + getPrevPosition (position, indexInExtent) { + const {dimensionToExtent, itemPositions, primary, secondary} = this; + + if (indexInExtent === 0) { + if (this.props.itemSizes) { + if (itemPositions[i - 1] || itemPositions[i - 1] === 0) { + position.primaryPosition = itemPositions[i - 1].position; + } else if (itemSizes[i]) { + position.primaryPosition -= itemSizes[i] + this.props.spacing; + } else { + position.primaryPosition -= primary.gridSize; + } + } else { + position.primaryPosition -= primary.gridSize; + } + position.secondaryPosition = 0; + return dimensionToExtent - 1; + } else { + position.secondaryPosition += secondary.gridSize; + return indexInExtent - 1; + } + } + + getNextPosition (position, indexInExtent) { + const {dimensionToExtent, itemPositions, primary, secondary} = this; + + if (indexInExtent + 1 >= dimensionToExtent) { + if (this.props.itemSizes) { + if (itemPositions[i + 1] || itemPositions[i + 1] === 0) { + position.primaryPosition = itemPositions[i + 1].position; + } else if (itemSizes[i]) { + position.primaryPosition += itemSizes[i] + this.props.spacing; + } else { + position.primaryPosition += primary.gridSize; + } + } else { + position.primaryPosition += primary.gridSize; + } + position.secondaryPosition = 0; + return 0; + } else { + position.secondaryPosition += secondary.gridSize; + return indexInExtent + 1; + } + } + + setCCNodeStyle (node, {action, position}) { + if (node) { + const style = node.style; + switch (action) { + case 'hide': + style.opacity = 0; + style.transition = null; + break; + case 'animate': + style.opacity = null; + style.transition = 'transform 1000ms'; + break; + case 'reset': + default: + style.opacity = null; + style.transition = null; + break; + } + if (position) { + const {x, y} = this.getXY(position.primaryPosition, position.secondaryPosition); + style.transform = `translate3d(${this.props.rtl ? -x : x}px, ${y}px, 0)`; + } + } + } + positionItems () { const {dataSize, itemSizes} = this.props, @@ -1163,40 +1248,62 @@ class VirtualListBasic extends Component { updateTo = dataSize; } - let - width, height, - {primaryPosition, secondaryPosition} = this.getGridPosition(updateFrom); - - width = (isPrimaryDirectionVertical ? secondary.itemSize : primary.itemSize) + 'px'; - height = (isPrimaryDirectionVertical ? primary.itemSize : secondary.itemSize) + 'px'; - + let width = (isPrimaryDirectionVertical ? secondary.itemSize : primary.itemSize) + 'px'; + let height = (isPrimaryDirectionVertical ? primary.itemSize : secondary.itemSize) + 'px'; + let position = this.getGridPosition(updateFrom); + let indexInExtent = updateFrom % dimensionToExtent; + if (updateFrom > this.editModeInfo.editingIndex) { + indexInExtent = this.getPrevPosition(position, indexInExtent); + } // positioning items - for (let i = updateFrom, j = updateFrom % dimensionToExtent; i < updateTo; i++) { - this.applyStyleToNewNode(i, width, height, primaryPosition, secondaryPosition); + for (let index = updateFrom; index < updateTo; index++) { + const itemRef = this.props.itemRefs.current[index % this.state.numOfItems]; + const CCNode = itemRef ? itemRef.parentNode : null; + if (this.props.editMode && this.editModeInfo.editingIndex !== null) { + const {editingIndex, lastVisualIndex} = this.editModeInfo; + + if (index === editingIndex) { + this.setCCNodeStyle(CCNode, {action: 'hide'}); + continue; + } - if (++j === dimensionToExtent) { - secondaryPosition = 0; + if (lastVisualIndex >= editingIndex && index === lastVisualIndex + 1 || + lastVisualIndex < editingIndex && index === lastVisualIndex) { + indexInExtent = this.getNextPosition(position, indexInExtent); + } + + this.setCCNodeStyle(CCNode, {action: 'animate', position}); + indexInExtent = this.getNextPosition(position, indexInExtent); + } else { + this.setCCNodeStyle(CCNode, {action: 'reset', position}); + this.applyStyleToNewNode(index, width, height, position.primaryPosition, position.secondaryPosition); + indexInExtent = this.getNextPosition(position, indexInExtent); + } + /* + if (++indexInExtent === dimensionToExtent) { + position.secondaryPosition = 0; if (this.props.itemSizes) { - if (itemPositions[i + 1] || itemPositions[i + 1] === 0) { - primaryPosition = itemPositions[i + 1].position; - } else if (itemSizes[i]) { - primaryPosition += itemSizes[i] + this.props.spacing; + if (itemPositions[index + 1] || itemPositions[index + 1] === 0) { + position.primaryPosition = itemPositions[index + 1].position; + } else if (itemSizes[index]) { + position.primaryPosition += itemSizes[index] + this.props.spacing; } else { - primaryPosition += primary.gridSize; + position.primaryPosition += primary.gridSize; } } else { - primaryPosition += primary.gridSize; + position.primaryPosition += primary.gridSize; } - j = 0; + indexInExtent = 0; } else { - secondaryPosition += secondary.gridSize; + position.secondaryPosition += secondary.gridSize; } + */ } - for (let i = updateTo; i < hideTo; i++) { - this.applyStyleToHideNode(i); + for (let index = updateTo; index < hideTo; index++) { + this.applyStyleToHideNode(index); } } @@ -1239,10 +1346,140 @@ class VirtualListBasic extends Component { return false; }; - getCCNodeFromPosition = (x, y) => { + setEditMode = (on) => { + if (on) { + if (this.props.scrollContentRef.current) { + const node = this.props.scrollContentRef.current; + node.addEventListener('mousedown', this.itemMovingBegin); + node.addEventListener('mousemove', this.itemMoving); + node.addEventListener('mouseup', this.itemMovingEnd); + } + this.initializeEditingDataOrder(); + } else { + if (this.props.scrollContentRef.current) { + const node = this.props.scrollContentRef.current; + node.removeEventListener('mousedown', this.itemMovingBegin); + node.removeEventListener('mousemove', this.itemMoving); + node.removeEventListener('mouseup', this.itemMovingEnd); + } + this.editModeInfo.editingDataOrder = null; + } + }; + + moveItem = (dataIndex, toVisualIndex) => { + if (this.props.editMode) { + const order = this.editModeInfo.editingDataOrder; + const fromVisualIndex = order.indexOf(dataIndex); + if (fromVisualIndex !== toVisualIndex) { + order.splice( + toVisualIndex, + 0, + order.splice( + fromVisualIndex, + 1 + )[0] + ); + this.editModeInfo.lastVisualIndex = toVisualIndex; + + this.forceUpdate(); + } + } else { + // In this case, visual index is the same as data index + if (dataIndex !== toVisualIndex) { + const newItemsOrder = [...Array(this.props.dataSize).keys()]; + newItemsOrder.splice(toVisualIndex, 0, newItemsOrder.splice(dataIndex, 1)[0]); + this.emitUpdateItemsOrder(newItemsOrder); + } + } + }; + + calculateMetricsForEditMode = (node) => { + if (node) { + const {primary, secondary, isPrimaryDirectionVertical} = this; + const {itemSize, scrollContentBounds} = this.editModeInfo; + const {clientWidth} = node; + const {x, y} = node.getBoundingClientRect(); + let [xAxis, yAxis] = [primary, secondary]; + if (isPrimaryDirectionVertical) { + [xAxis, yAxis] = [yAxis, xAxis]; + } + + scrollContentBounds.clientWidth = clientWidth; + scrollContentBounds.x = x; + scrollContentBounds.y = y; + itemSize.width = xAxis.itemSize; + itemSize.height = yAxis.itemSize; + } + }; + + updateMovingPosition = (clientX, clientY) => { + this.editModeInfo.lastPointer.clientX = clientX; + this.editModeInfo.lastPointer.clientY = clientY; + }; + + initializeEditingDataOrder = () => { + this.editModeInfo.editingDataOrder = [...Array(this.props.dataSize).keys()]; + }; + + getXYPositionFromClientXY = (clientX, clientY) => { + const {scrollContentBounds: {clientWidth, x, y}} = this.editModeInfo; + const relativeX = this.props.rtl ? clientWidth - (clientX - x) : clientX - x; + const relativeY = clientY - y; + if (this.isPrimaryDirectionVertical) { + return { + x: relativeX, + y: relativeY + this.scrollPosition + }; + } else { + return { + x: relativeX + this.scrollPosition, + y: relativeY + }; + } + }; + + getPositionFromClientXY = () => { + const {clientX, clientY} = this.editModeInfo.lastPointer; + const {x, y} = this.getXYPositionFromClientXY(clientX, clientY); + if (this.isPrimaryDirectionVertical) { + return { + primaryPosition: y, + secondaryPosition: x + }; + } else { + return { + primaryPosition: x, + secondaryPosition: y + }; + } + }; + + getVisualIndexFromPosition = () => { + const {dataSize, itemSizes} = this.props; + const {dimensionToExtent, itemPositions, primary, secondary} = this; + const {primaryPosition, secondaryPosition} = this.getPositionFromClientXY(); + + if (itemSizes && typeof itemSizes[index] !== 'undefined') { + // TBD: refer getGridPosition() to fill this + return null; + } else { + const primaryIndex = Math.floor(primaryPosition / primary.gridSize); + const secondaryIndex = Math.floor(secondaryPosition / secondary.gridSize); + + // return null if the coordinate points to space between items + if ((primaryIndex * primary.gridSize + primary.itemSize < primaryPosition) || + (secondaryIndex * secondary.gridSize + secondary.itemSize < secondaryPosition)) { + return null; + } + + return primaryIndex * dimensionToExtent + secondaryIndex; + } + }; + + getDataIndexFromPosition = (clientX, clientY) => { if (typeof window !== 'undefined') { const contentNode = this.contentRef.current; - let node = document.elementFromPoint(x, y); + let node = document.elementFromPoint(clientX, clientY); if (utilDOM.containsDangerously(contentNode, node)) { while (node.parentNode !== contentNode) { if (node === document) { @@ -1251,43 +1488,94 @@ class VirtualListBasic extends Component { node = node.parentNode; } - return node; + if (node) { + return parseInt(node.querySelector(`[data-index]`).dataset.index); + } } } return null; - } + }; + + // getDataIndexFromItemNode = (node) => (node ? parseInt(node.querySelector(`[data-index]`).dataset.index) : null); + + calculateMovingItemPosition = () => { + const {itemSize: {width, height}, lastPointer: {clientX, clientY}} = this.editModeInfo; + const {x, y} = this.getXYPositionFromClientXY( + clientX - (this.props.rtl ? -1 : 1) * width / 2, + clientY - height / 2 + ); + return {x, y, width, height}; + }; - getIndexFromCCNode = (node) => (node ? parseInt(node.querySelector(`[data-index]`).dataset.index) : null) + addMovingItem = () => { + /* TBD: using this.itemContainerRefs[key] ? */ + const {childProps, itemRenderer, getComponentProps} = this.props; + const {x, y, width, height} = this.calculateMovingItemPosition(); + const componentProps = getComponentProps && getComponentProps(this.editModeInfo.editingIndex) || {}; + const itemContainerRef = (ref) => { + this.editModeInfo.editingNode = ref; + } + const style = { + width: width + 'px', + height: height + 'px', + /* FIXME: RTL / this calculation only works for Chrome */ + transform: `translate3d(${this.props.rtl ? -x : x}px, ${y}px, 0)`, + zIndex: 10 + }; + + this.cc[this.state.numOfItems] = ( +
+ {itemRenderer({...childProps, ...componentProps, index: this.editModeInfo.editingIndex})} +
+ ); + + this.forceUpdate(); + }; + + updateMovingItem = () => { + const {x, y} = this.calculateMovingItemPosition(); + this.editModeInfo.editingNode.style.transform = `translate3d(${this.props.rtl ? -x : x}px, ${y}px, 0)`; + }; + + removeMovingItem = () => { + if (this.cc.length > this.state.numOfItems) { + this.cc.pop(); + } + + // do not call this.forceUpdate() here since this.emitUpdateItemsOrder() will be called in itemMovingEnd. + }; itemMovingBegin = ({clientX, clientY}) => { - console.log('mousedown'); - const node = this.getCCNodeFromPosition(clientX, clientY); - this.editingIndex = this.getIndexFromCCNode(node); - } + const {x, y} = this.props.scrollContentRef.current.getBoundingClientRect(); + + this.initializeEditingDataOrder(); + this.editModeInfo.editingIndex = this.getDataIndexFromPosition(clientX, clientY); + this.editModeInfo.lastVisualIndex = this.editModeInfo.editingIndex; + this.updateMovingPosition(clientX, clientY); + + this.addMovingItem(); + }; itemMoving = ({clientX, clientY}) => { - console.log('mousemove'); - if (this.editingIndex !== null) { - const node = this.getCCNodeFromPosition(clientX, clientY); - const index = this.getIndexFromCCNode(node); + if (this.editModeInfo.editingIndex !== null) { + this.updateMovingPosition(clientX, clientY); + + const index = this.getVisualIndexFromPosition(); if (index !== null) { - this.moveItem(this.editingIndex, index); - this.editingIndex = index; + this.moveItem(this.editModeInfo.editingIndex, index); } + + this.updateMovingItem(); } } itemMovingEnd = () => { - console.log('mouseup'); - this.editingIndex = null; - } - - moveItem = (from, to) => { - const newItemsOrder = [...Array(this.props.dataSize).keys()]; - newItemsOrder.splice(to, 0, newItemsOrder.splice(from, 1)[0]); - this.emitUpdateItemsOrder(newItemsOrder); - } + this.editModeInfo.editingIndex = null; + this.removeMovingItem(); + this.emitUpdateItemsOrder(this.editModeInfo.editingDataOrder); + this.editModeInfo.editingDataOrder = null; + }; // render From b8b87638087493f49a386c0b06ab4d2e2cb96b2c Mon Sep 17 00:00:00 2001 From: Seungcheon Baek Date: Fri, 4 Mar 2022 13:58:56 +0900 Subject: [PATCH 4/7] Fix abnormal animation and data update error Enact-DCO-1.0-Signed-off-by: Seungcheon Baek (sc.baek@lge.com) --- packages/ui/VirtualList/VirtualListBasic.js | 62 +++++++++++++++------ 1 file changed, 44 insertions(+), 18 deletions(-) diff --git a/packages/ui/VirtualList/VirtualListBasic.js b/packages/ui/VirtualList/VirtualListBasic.js index 4d4732ebe4..230d9231f1 100644 --- a/packages/ui/VirtualList/VirtualListBasic.js +++ b/packages/ui/VirtualList/VirtualListBasic.js @@ -526,10 +526,12 @@ class VirtualListBasic extends Component { editingIndex: null, editingNode: null, editingDataOrder: null, + guessPosition: false, itemSize: {width: null, height: null}, lastPointer: {clientX: null, clientY: null}, lastVisualIndex: null, - scrollContentBounds: {clientWidth: null, x: null, y: null} + scrollContentBounds: {clientWidth: null, x: null, y: null}, + transitionTime: '100ms' } updateScrollPosition = ({x, y}, rtl = this.props.rtl) => { @@ -1160,15 +1162,15 @@ class VirtualListBasic extends Component { this.cc[key] =
; // TBD }; - getPrevPosition (position, indexInExtent) { + getPrevPosition (index, position, indexInExtent) { const {dimensionToExtent, itemPositions, primary, secondary} = this; if (indexInExtent === 0) { if (this.props.itemSizes) { - if (itemPositions[i - 1] || itemPositions[i - 1] === 0) { - position.primaryPosition = itemPositions[i - 1].position; - } else if (itemSizes[i]) { - position.primaryPosition -= itemSizes[i] + this.props.spacing; + if (itemPositions[index - 1] || itemPositions[index - 1] === 0) { + position.primaryPosition = itemPositions[index - 1].position; + } else if (itemSizes[index]) { + position.primaryPosition -= itemSizes[index] + this.props.spacing; } else { position.primaryPosition -= primary.gridSize; } @@ -1183,15 +1185,15 @@ class VirtualListBasic extends Component { } } - getNextPosition (position, indexInExtent) { + getNextPosition (index, position, indexInExtent) { const {dimensionToExtent, itemPositions, primary, secondary} = this; if (indexInExtent + 1 >= dimensionToExtent) { if (this.props.itemSizes) { - if (itemPositions[i + 1] || itemPositions[i + 1] === 0) { - position.primaryPosition = itemPositions[i + 1].position; - } else if (itemSizes[i]) { - position.primaryPosition += itemSizes[i] + this.props.spacing; + if (itemPositions[index + 1] || itemPositions[index + 1] === 0) { + position.primaryPosition = itemPositions[index + 1].position; + } else if (itemSizes[index]) { + position.primaryPosition += itemSizes[index] + this.props.spacing; } else { position.primaryPosition += primary.gridSize; } @@ -1216,7 +1218,7 @@ class VirtualListBasic extends Component { break; case 'animate': style.opacity = null; - style.transition = 'transform 1000ms'; + style.transition = `transform ${this.editModeInfo.transitionTime}`; break; case 'reset': default: @@ -1252,9 +1254,14 @@ class VirtualListBasic extends Component { let height = (isPrimaryDirectionVertical ? primary.itemSize : secondary.itemSize) + 'px'; let position = this.getGridPosition(updateFrom); let indexInExtent = updateFrom % dimensionToExtent; - if (updateFrom > this.editModeInfo.editingIndex) { - indexInExtent = this.getPrevPosition(position, indexInExtent); + + if (this.editModeInfo.guessPosition) { + this.itemMoving(this.editModeInfo.lastPointer); + } + if (this.editModeInfo.editingIndex !== null && updateFrom > this.editModeInfo.editingIndex) { + indexInExtent = this.getPrevPosition(updateFrom, position, indexInExtent); } + // positioning items for (let index = updateFrom; index < updateTo; index++) { const itemRef = this.props.itemRefs.current[index % this.state.numOfItems]; @@ -1269,15 +1276,20 @@ class VirtualListBasic extends Component { if (lastVisualIndex >= editingIndex && index === lastVisualIndex + 1 || lastVisualIndex < editingIndex && index === lastVisualIndex) { - indexInExtent = this.getNextPosition(position, indexInExtent); + indexInExtent = this.getNextPosition(index, position, indexInExtent); } - this.setCCNodeStyle(CCNode, {action: 'animate', position}); - indexInExtent = this.getNextPosition(position, indexInExtent); + if (index !== parseInt(CCNode.querySelector(`[data-index]`).dataset.index)) { + this.setCCNodeStyle(CCNode, {action: 'reset'}); + this.applyStyleToNewNode(index, width, height, position.primaryPosition, position.secondaryPosition); + } else { + this.setCCNodeStyle(CCNode, {action: 'animate', position}); + } + indexInExtent = this.getNextPosition(index, position, indexInExtent); } else { this.setCCNodeStyle(CCNode, {action: 'reset', position}); this.applyStyleToNewNode(index, width, height, position.primaryPosition, position.secondaryPosition); - indexInExtent = this.getNextPosition(position, indexInExtent); + indexInExtent = this.getNextPosition(index, position, indexInExtent); } /* if (++indexInExtent === dimensionToExtent) { @@ -1353,6 +1365,8 @@ class VirtualListBasic extends Component { node.addEventListener('mousedown', this.itemMovingBegin); node.addEventListener('mousemove', this.itemMoving); node.addEventListener('mouseup', this.itemMovingEnd); + node.addEventListener('mouseenter', this.itemMovingEnter); + node.addEventListener('mouseleave', this.itemMovingLeave); } this.initializeEditingDataOrder(); } else { @@ -1361,6 +1375,8 @@ class VirtualListBasic extends Component { node.removeEventListener('mousedown', this.itemMovingBegin); node.removeEventListener('mousemove', this.itemMoving); node.removeEventListener('mouseup', this.itemMovingEnd); + node.removeEventListener('mouseenter', this.itemMovingEnter); + node.removeEventListener('mouseleave', this.itemMovingLeave); } this.editModeInfo.editingDataOrder = null; } @@ -1577,6 +1593,16 @@ class VirtualListBasic extends Component { this.editModeInfo.editingDataOrder = null; }; + itemMovingEnter = () => { + this.editModeInfo.guessPosition = false; + }; + + itemMovingLeave = () => { + if (this.editModeInfo.editingIndex !== null) { + this.editModeInfo.guessPosition = true; + } + }; + // render render () { From 9ab46f8c74cdcda9deb96dfe97eaeeabccdbebcc Mon Sep 17 00:00:00 2001 From: Seungcheon Baek Date: Fri, 4 Mar 2022 17:37:39 +0900 Subject: [PATCH 5/7] Refine code Enact-DCO-1.0-Signed-off-by: Seungcheon Baek (sc.baek@lge.com) --- packages/ui/VirtualList/VirtualListBasic.js | 134 ++++++++------------ 1 file changed, 54 insertions(+), 80 deletions(-) diff --git a/packages/ui/VirtualList/VirtualListBasic.js b/packages/ui/VirtualList/VirtualListBasic.js index 230d9231f1..99c8e2c606 100644 --- a/packages/ui/VirtualList/VirtualListBasic.js +++ b/packages/ui/VirtualList/VirtualListBasic.js @@ -204,8 +204,6 @@ class VirtualListBasic extends Component { */ itemSizes: PropTypes.arrayOf(PropTypes.number), - onUpdateItemsOrder: PropTypes.func, - /** * Called when the range of items has updated. * @@ -216,6 +214,8 @@ class VirtualListBasic extends Component { */ onUpdateItems: PropTypes.func, + onUpdateItemsOrder: PropTypes.func, + /** * Number of spare DOM node. * `3` is good for the default value experimentally and @@ -532,7 +532,7 @@ class VirtualListBasic extends Component { lastVisualIndex: null, scrollContentBounds: {clientWidth: null, x: null, y: null}, transitionTime: '100ms' - } + }; updateScrollPosition = ({x, y}, rtl = this.props.rtl) => { if (this.props.scrollMode === 'native') { @@ -553,7 +553,7 @@ class VirtualListBasic extends Component { getCenterItemIndexFromScrollPosition = (scrollPosition) => Math.floor((scrollPosition + (this.primary.clientSize / 2)) / this.primary.gridSize) * this.dimensionToExtent + Math.floor(this.dimensionToExtent / 2); - getGridPosition (index) { // TBD + getGridPosition (index) { const {dataSize, itemSizes} = this.props, {dimensionToExtent, itemPositions, primary, secondary} = this, @@ -584,7 +584,7 @@ class VirtualListBasic extends Component { } // For individually sized item - getItemBottomPosition = (index) => { // TBD + getItemBottomPosition = (index) => { const itemPosition = this.itemPositions[index], itemSize = this.props.itemSizes[index]; @@ -597,11 +597,11 @@ class VirtualListBasic extends Component { }; // For individually sized item - getItemTopPositionFromPreviousItemBottomPosition = (index, spacing) => { // TBD + getItemTopPositionFromPreviousItemBottomPosition = (index, spacing) => { return index === 0 ? 0 : this.getItemBottomPosition(index - 1) + spacing; }; - getItemPosition = (index, stickTo = 'start', optionalOffset = 0) => { // TBD + getItemPosition = (index, stickTo = 'start', optionalOffset = 0) => { const {isPrimaryDirectionVertical, primary, scrollBounds} = this; const maxPos = isPrimaryDirectionVertical ? scrollBounds.maxTop : scrollBounds.maxLeft; const position = this.getGridPosition(index); @@ -713,7 +713,7 @@ class VirtualListBasic extends Component { this.calculateMetricsForEditMode(node); } - getStatesAndUpdateBounds = (props, firstIndex = 0) => { // TBD + getStatesAndUpdateBounds = (props, firstIndex = 0) => { const {dataSize, overhang, updateStatesAndBounds} = props, {dimensionToExtent, primary, moreInfo, scrollPosition} = this, @@ -752,7 +752,7 @@ class VirtualListBasic extends Component { }; }; - calculateFirstIndex (props, wasFirstIndexMax, dataSizeDiff, firstIndex) { // TBD + calculateFirstIndex (props, wasFirstIndexMax, dataSizeDiff, firstIndex) { const {overhang} = props, {dimensionToExtent, isPrimaryDirectionVertical, maxFirstIndex, primary, scrollBounds, scrollPosition, threshold} = this, @@ -1010,7 +1010,7 @@ class VirtualListBasic extends Component { } // For individually sized item - calculateAndCacheItemPosition (index) { // TBD + calculateAndCacheItemPosition (index) { const {itemSizes} = this.props; if (!this.itemPositions[index] && itemSizes[index]) { @@ -1023,7 +1023,7 @@ class VirtualListBasic extends Component { } // For individually sized item - applyItemPositionToDOMElement (index) { // TBD + applyItemPositionToDOMElement (index) { const {direction, rtl} = this.props, {numOfItems} = this.state, @@ -1125,48 +1125,47 @@ class VirtualListBasic extends Component { return style; } - applyStyleToNewNode = (visualIndex, ...rest) => { // TBD - //const dataIndex = this.props.editMode ? this.editModeInfo.editingDataOrder[visualIndex] : visualIndex; - const dataIndex = visualIndex; + applyStyleToNewNode = (index, ...rest) => { const {childProps, itemRefs, itemRenderer, getComponentProps} = this.props, - key = dataIndex % this.state.numOfItems, - componentProps = getComponentProps && getComponentProps(dataIndex) || {}, + key = index % this.state.numOfItems, + componentProps = getComponentProps && getComponentProps(index) || {}, itemContainerRef = (ref) => { if (ref === null) { itemRefs.current[key] = ref; } else { const itemNode = ref.children[0]; - itemRefs.current[key] = (parseInt(itemNode.dataset.index) === dataIndex) ? + itemRefs.current[key] = (parseInt(itemNode.dataset.index) === index) ? itemNode : - ref.querySelector(`[data-index="${dataIndex}"]`); // TBD + ref.querySelector(`[data-index="${index}"]`); this.itemContainerRefs[key] = ref; } }; - this.cc[key] = ( // TBD + this.cc[key] = (
- {itemRenderer({...childProps, ...componentProps, index: dataIndex})} + {itemRenderer({...childProps, ...componentProps, index})}
); }; - applyStyleToHideNode = (visualIndex) => { // TBD + applyStyleToHideNode = (index) => { const {itemRefs} = this.props, - key = visualIndex % this.state.numOfItems, + key = index % this.state.numOfItems, itemContainerRef = () => (itemRefs.current[key] = null); - this.cc[key] =
; // TBD + this.cc[key] =
; }; getPrevPosition (index, position, indexInExtent) { + const {itemSizes} = this.props; const {dimensionToExtent, itemPositions, primary, secondary} = this; if (indexInExtent === 0) { - if (this.props.itemSizes) { + if (itemSizes) { if (itemPositions[index - 1] || itemPositions[index - 1] === 0) { position.primaryPosition = itemPositions[index - 1].position; } else if (itemSizes[index]) { @@ -1186,10 +1185,11 @@ class VirtualListBasic extends Component { } getNextPosition (index, position, indexInExtent) { + const {itemSizes} = this.props; const {dimensionToExtent, itemPositions, primary, secondary} = this; if (indexInExtent + 1 >= dimensionToExtent) { - if (this.props.itemSizes) { + if (itemSizes) { if (itemPositions[index + 1] || itemPositions[index + 1] === 0) { position.primaryPosition = itemPositions[index + 1].position; } else if (itemSizes[index]) { @@ -1208,7 +1208,7 @@ class VirtualListBasic extends Component { } } - setCCNodeStyle (node, {action, position}) { + setItemContainerStyle (node, {action, position}) { if (node) { const style = node.style; switch (action) { @@ -1235,9 +1235,10 @@ class VirtualListBasic extends Component { positionItems () { const - {dataSize, itemSizes} = this.props, + {dataSize} = this.props, {firstIndex, numOfItems} = this.state, - {cc, isPrimaryDirectionVertical, dimensionToExtent, primary, secondary, itemPositions} = this; + {cc, isPrimaryDirectionVertical, dimensionToExtent, primary, secondary} = this; + const {editingIndex, guessPosition} = this.editModeInfo; let hideTo = 0, updateFrom = cc.length ? this.state.updateFrom : firstIndex, @@ -1255,22 +1256,23 @@ class VirtualListBasic extends Component { let position = this.getGridPosition(updateFrom); let indexInExtent = updateFrom % dimensionToExtent; - if (this.editModeInfo.guessPosition) { - this.itemMoving(this.editModeInfo.lastPointer); - } - if (this.editModeInfo.editingIndex !== null && updateFrom > this.editModeInfo.editingIndex) { - indexInExtent = this.getPrevPosition(updateFrom, position, indexInExtent); + if (editingIndex !== null) { + if (guessPosition) { + this.itemMoving(this.editModeInfo.lastPointer); + } + if (updateFrom > editingIndex) { + indexInExtent = this.getPrevPosition(updateFrom, position, indexInExtent); + } } // positioning items for (let index = updateFrom; index < updateTo; index++) { - const itemRef = this.props.itemRefs.current[index % this.state.numOfItems]; - const CCNode = itemRef ? itemRef.parentNode : null; - if (this.props.editMode && this.editModeInfo.editingIndex !== null) { - const {editingIndex, lastVisualIndex} = this.editModeInfo; + const itemContainer = this.itemContainerRefs[index % this.state.numOfItems]; + if (this.props.editMode && editingIndex !== null) { + const {lastVisualIndex} = this.editModeInfo; if (index === editingIndex) { - this.setCCNodeStyle(CCNode, {action: 'hide'}); + this.setItemContainerStyle(itemContainer, {action: 'hide'}); continue; } @@ -1279,39 +1281,18 @@ class VirtualListBasic extends Component { indexInExtent = this.getNextPosition(index, position, indexInExtent); } - if (index !== parseInt(CCNode.querySelector(`[data-index]`).dataset.index)) { - this.setCCNodeStyle(CCNode, {action: 'reset'}); + if (index !== parseInt(itemContainer.querySelector('[data-index]').dataset.index)) { + this.setItemContainerStyle(itemContainer, {action: 'reset'}); this.applyStyleToNewNode(index, width, height, position.primaryPosition, position.secondaryPosition); } else { - this.setCCNodeStyle(CCNode, {action: 'animate', position}); + this.setItemContainerStyle(itemContainer, {action: 'animate', position}); } indexInExtent = this.getNextPosition(index, position, indexInExtent); - } else { - this.setCCNodeStyle(CCNode, {action: 'reset', position}); + } else { // normal case + this.setItemContainerStyle(itemContainer, {action: 'reset', position}); this.applyStyleToNewNode(index, width, height, position.primaryPosition, position.secondaryPosition); indexInExtent = this.getNextPosition(index, position, indexInExtent); } - /* - if (++indexInExtent === dimensionToExtent) { - position.secondaryPosition = 0; - - if (this.props.itemSizes) { - if (itemPositions[index + 1] || itemPositions[index + 1] === 0) { - position.primaryPosition = itemPositions[index + 1].position; - } else if (itemSizes[index]) { - position.primaryPosition += itemSizes[index] + this.props.spacing; - } else { - position.primaryPosition += primary.gridSize; - } - } else { - position.primaryPosition += primary.gridSize; - } - - indexInExtent = 0; - } else { - position.secondaryPosition += secondary.gridSize; - } - */ } for (let index = updateTo; index < hideTo; index++) { @@ -1399,13 +1380,10 @@ class VirtualListBasic extends Component { this.forceUpdate(); } - } else { - // In this case, visual index is the same as data index - if (dataIndex !== toVisualIndex) { - const newItemsOrder = [...Array(this.props.dataSize).keys()]; - newItemsOrder.splice(toVisualIndex, 0, newItemsOrder.splice(dataIndex, 1)[0]); - this.emitUpdateItemsOrder(newItemsOrder); - } + } else if (dataIndex !== toVisualIndex) { // In this case, visual index is the same as data index + const newItemsOrder = [...Array(this.props.dataSize).keys()]; + newItemsOrder.splice(toVisualIndex, 0, newItemsOrder.splice(dataIndex, 1)[0]); + this.emitUpdateItemsOrder(newItemsOrder); } }; @@ -1471,8 +1449,8 @@ class VirtualListBasic extends Component { }; getVisualIndexFromPosition = () => { - const {dataSize, itemSizes} = this.props; - const {dimensionToExtent, itemPositions, primary, secondary} = this; + const {itemSizes} = this.props; + const {dimensionToExtent, primary, secondary} = this; const {primaryPosition, secondaryPosition} = this.getPositionFromClientXY(); if (itemSizes && typeof itemSizes[index] !== 'undefined') { @@ -1505,7 +1483,7 @@ class VirtualListBasic extends Component { } if (node) { - return parseInt(node.querySelector(`[data-index]`).dataset.index); + return parseInt(node.querySelector('[data-index]').dataset.index); } } } @@ -1513,8 +1491,6 @@ class VirtualListBasic extends Component { return null; }; - // getDataIndexFromItemNode = (node) => (node ? parseInt(node.querySelector(`[data-index]`).dataset.index) : null); - calculateMovingItemPosition = () => { const {itemSize: {width, height}, lastPointer: {clientX, clientY}} = this.editModeInfo; const {x, y} = this.getXYPositionFromClientXY( @@ -1531,7 +1507,7 @@ class VirtualListBasic extends Component { const componentProps = getComponentProps && getComponentProps(this.editModeInfo.editingIndex) || {}; const itemContainerRef = (ref) => { this.editModeInfo.editingNode = ref; - } + }; const style = { width: width + 'px', height: height + 'px', @@ -1563,8 +1539,6 @@ class VirtualListBasic extends Component { }; itemMovingBegin = ({clientX, clientY}) => { - const {x, y} = this.props.scrollContentRef.current.getBoundingClientRect(); - this.initializeEditingDataOrder(); this.editModeInfo.editingIndex = this.getDataIndexFromPosition(clientX, clientY); this.editModeInfo.lastVisualIndex = this.editModeInfo.editingIndex; @@ -1584,7 +1558,7 @@ class VirtualListBasic extends Component { this.updateMovingItem(); } - } + }; itemMovingEnd = () => { this.editModeInfo.editingIndex = null; From c00a8c21af5adb16f8f12494cd1eb09f1c4c2887 Mon Sep 17 00:00:00 2001 From: Seungcheon Baek Date: Fri, 4 Mar 2022 18:43:34 +0900 Subject: [PATCH 6/7] Fix run-time errors Enact-DCO-1.0-Signed-off-by: Seungcheon Baek (sc.baek@lge.com) --- packages/ui/VirtualList/VirtualListBasic.js | 55 ++++++++++++++++----- 1 file changed, 43 insertions(+), 12 deletions(-) diff --git a/packages/ui/VirtualList/VirtualListBasic.js b/packages/ui/VirtualList/VirtualListBasic.js index 99c8e2c606..d4cb88b6f6 100644 --- a/packages/ui/VirtualList/VirtualListBasic.js +++ b/packages/ui/VirtualList/VirtualListBasic.js @@ -432,6 +432,8 @@ class VirtualListBasic extends Component { this.indexToScrollIntoView = -1; } + + this.resetEditModeInfo(); } if ( @@ -457,6 +459,8 @@ class VirtualListBasic extends Component { }); deferScrollTo = true; + + this.resetEditModeInfo(); } else if (this.hasDataSizeChanged) { const newState = this.getStatesAndUpdateBounds(this.props, this.state.firstIndex); // eslint-disable-next-line react/no-did-update-set-state @@ -1241,8 +1245,8 @@ class VirtualListBasic extends Component { const {editingIndex, guessPosition} = this.editModeInfo; let hideTo = 0, - updateFrom = cc.length ? this.state.updateFrom : firstIndex, - updateTo = cc.length ? this.state.updateTo : firstIndex + numOfItems; + updateFrom = cc.length && !guessPosition ? this.state.updateFrom : firstIndex, + updateTo = cc.length && !guessPosition ? this.state.updateTo : firstIndex + numOfItems; if (updateFrom >= updateTo) { return; @@ -1258,7 +1262,7 @@ class VirtualListBasic extends Component { if (editingIndex !== null) { if (guessPosition) { - this.itemMoving(this.editModeInfo.lastPointer); + this.itemMoving(this.editModeInfo.lastPointer, true); } if (updateFrom > editingIndex) { indexInExtent = this.getPrevPosition(updateFrom, position, indexInExtent); @@ -1268,6 +1272,7 @@ class VirtualListBasic extends Component { // positioning items for (let index = updateFrom; index < updateTo; index++) { const itemContainer = this.itemContainerRefs[index % this.state.numOfItems]; + if (this.props.editMode && editingIndex !== null) { const {lastVisualIndex} = this.editModeInfo; @@ -1281,7 +1286,7 @@ class VirtualListBasic extends Component { indexInExtent = this.getNextPosition(index, position, indexInExtent); } - if (index !== parseInt(itemContainer.querySelector('[data-index]').dataset.index)) { + if (index !== this.getDataIndexFromNode(itemContainer)) { this.setItemContainerStyle(itemContainer, {action: 'reset'}); this.applyStyleToNewNode(index, width, height, position.primaryPosition, position.secondaryPosition); } else { @@ -1363,7 +1368,7 @@ class VirtualListBasic extends Component { } }; - moveItem = (dataIndex, toVisualIndex) => { + moveItem = (dataIndex, toVisualIndex, skipRendering = false) => { if (this.props.editMode) { const order = this.editModeInfo.editingDataOrder; const fromVisualIndex = order.indexOf(dataIndex); @@ -1378,7 +1383,9 @@ class VirtualListBasic extends Component { ); this.editModeInfo.lastVisualIndex = toVisualIndex; - this.forceUpdate(); + if (!skipRendering) { + this.forceUpdate(); + } } } else if (dataIndex !== toVisualIndex) { // In this case, visual index is the same as data index const newItemsOrder = [...Array(this.props.dataSize).keys()]; @@ -1470,6 +1477,17 @@ class VirtualListBasic extends Component { } }; + getDataIndexFromNode = (node) => { + const targetNode = node && node.querySelector('[data-index]'); + if (targetNode) { + const index = parseInt(targetNode.dataset.index); + if (!isNaN(index)) { + return index; + } + } + return null; + }; + getDataIndexFromPosition = (clientX, clientY) => { if (typeof window !== 'undefined') { const contentNode = this.contentRef.current; @@ -1483,7 +1501,7 @@ class VirtualListBasic extends Component { } if (node) { - return parseInt(node.querySelector('[data-index]').dataset.index); + return this.getDataIndexFromNode(node); } } } @@ -1504,7 +1522,8 @@ class VirtualListBasic extends Component { /* TBD: using this.itemContainerRefs[key] ? */ const {childProps, itemRenderer, getComponentProps} = this.props; const {x, y, width, height} = this.calculateMovingItemPosition(); - const componentProps = getComponentProps && getComponentProps(this.editModeInfo.editingIndex) || {}; + const index = this.editModeInfo.editingIndex; + const componentProps = getComponentProps && getComponentProps(index) || {}; const itemContainerRef = (ref) => { this.editModeInfo.editingNode = ref; }; @@ -1518,7 +1537,7 @@ class VirtualListBasic extends Component { this.cc[this.state.numOfItems] = (
- {itemRenderer({...childProps, ...componentProps, index: this.editModeInfo.editingIndex})} + {itemRenderer({...childProps, ...componentProps, index, ['data-index']: index})}
); @@ -1534,6 +1553,7 @@ class VirtualListBasic extends Component { if (this.cc.length > this.state.numOfItems) { this.cc.pop(); } + this.editModeInfo.editingNode = null; // do not call this.forceUpdate() here since this.emitUpdateItemsOrder() will be called in itemMovingEnd. }; @@ -1547,16 +1567,18 @@ class VirtualListBasic extends Component { this.addMovingItem(); }; - itemMoving = ({clientX, clientY}) => { + itemMoving = ({clientX, clientY}, skipRendering = false) => { if (this.editModeInfo.editingIndex !== null) { this.updateMovingPosition(clientX, clientY); const index = this.getVisualIndexFromPosition(); if (index !== null) { - this.moveItem(this.editModeInfo.editingIndex, index); + this.moveItem(this.editModeInfo.editingIndex, index, skipRendering); } - this.updateMovingItem(); + if (this.editModeInfo.editingNode) { + this.updateMovingItem(); + } } }; @@ -1577,6 +1599,15 @@ class VirtualListBasic extends Component { } }; + resetEditModeInfo = () => { + this.editModeInfo.editingIndex = null; + this.editModeInfo.editingNode = null; + this.editModeInfo.editingDataOrder = null; + this.editModeInfo.guessPosition = false; + this.editModeInfo.lastVisualIndex = null; + this.removeMovingItem(); + }; + // render render () { From 790c5862974e848855cca843c1b5a15e8861776e Mon Sep 17 00:00:00 2001 From: Seungcheon Baek Date: Thu, 10 Mar 2022 18:55:11 +0900 Subject: [PATCH 7/7] Add feature to move item's position by key input Enact-DCO-1.0-Signed-off-by: Seungcheon Baek (sc.baek@lge.com) --- packages/ui/VirtualList/VirtualListBasic.js | 160 ++++++++++++++------ 1 file changed, 114 insertions(+), 46 deletions(-) diff --git a/packages/ui/VirtualList/VirtualListBasic.js b/packages/ui/VirtualList/VirtualListBasic.js index d4cb88b6f6..cc71b31297 100644 --- a/packages/ui/VirtualList/VirtualListBasic.js +++ b/packages/ui/VirtualList/VirtualListBasic.js @@ -3,11 +3,12 @@ import EnactPropTypes from '@enact/core/internal/prop-types'; import {forward} from '@enact/core/handle'; import {platform} from '@enact/core/platform'; import {clamp} from '@enact/core/util'; -import {utilDOM} from '@enact/ui/useScroll/utilDOM'; import PropTypes from 'prop-types'; import equals from 'ramda/src/equals'; import {createRef, Component} from 'react'; +import {utilDOM} from '../useScroll/utilDOM'; + import css from './VirtualList.module.less'; const nop = () => {}; @@ -338,6 +339,7 @@ class VirtualListBasic extends Component { this.emitUpdateItemsOrder = props.onUpdateItemsOrder; window.moveItem = this.moveItem; // FIXME: for testing only + window.scrollIntoViewByIndex = this.scrollIntoViewByIndex; window.setEditMode = this.setEditMode; // FIXME: for testing only } @@ -535,7 +537,8 @@ class VirtualListBasic extends Component { lastPointer: {clientX: null, clientY: null}, lastVisualIndex: null, scrollContentBounds: {clientWidth: null, x: null, y: null}, - transitionTime: '100ms' + transitionTime: '100ms', + positioningType: null }; updateScrollPosition = ({x, y}, rtl = this.props.rtl) => { @@ -557,7 +560,7 @@ class VirtualListBasic extends Component { getCenterItemIndexFromScrollPosition = (scrollPosition) => Math.floor((scrollPosition + (this.primary.clientSize / 2)) / this.primary.gridSize) * this.dimensionToExtent + Math.floor(this.dimensionToExtent / 2); - getGridPosition (index) { + getGridPosition = (index) => { const {dataSize, itemSizes} = this.props, {dimensionToExtent, itemPositions, primary, secondary} = this, @@ -585,7 +588,7 @@ class VirtualListBasic extends Component { } return {primaryPosition, secondaryPosition}; - } + }; // For individually sized item getItemBottomPosition = (index) => { @@ -1344,19 +1347,22 @@ class VirtualListBasic extends Component { return false; }; + /* + * Edit mode + */ + + /* Edit mode common */ + setEditMode = (on) => { - if (on) { - if (this.props.scrollContentRef.current) { + if (this.props.scrollContentRef.current) { + if (on) { const node = this.props.scrollContentRef.current; node.addEventListener('mousedown', this.itemMovingBegin); node.addEventListener('mousemove', this.itemMoving); node.addEventListener('mouseup', this.itemMovingEnd); node.addEventListener('mouseenter', this.itemMovingEnter); node.addEventListener('mouseleave', this.itemMovingLeave); - } - this.initializeEditingDataOrder(); - } else { - if (this.props.scrollContentRef.current) { + } else { const node = this.props.scrollContentRef.current; node.removeEventListener('mousedown', this.itemMovingBegin); node.removeEventListener('mousemove', this.itemMoving); @@ -1364,12 +1370,11 @@ class VirtualListBasic extends Component { node.removeEventListener('mouseenter', this.itemMovingEnter); node.removeEventListener('mouseleave', this.itemMovingLeave); } - this.editModeInfo.editingDataOrder = null; } }; - moveItem = (dataIndex, toVisualIndex, skipRendering = false) => { - if (this.props.editMode) { + moveItem = (dataIndex, toVisualIndex, {skipRendering = false, scrollIntoView = false}) => { + if (this.props.editMode && this.editModeInfo.editingIndex !== null) { const order = this.editModeInfo.editingDataOrder; const fromVisualIndex = order.indexOf(dataIndex); if (fromVisualIndex !== toVisualIndex) { @@ -1385,6 +1390,9 @@ class VirtualListBasic extends Component { if (!skipRendering) { this.forceUpdate(); + if (scrollIntoView) { + this.scrollIntoViewByIndex(toVisualIndex); + } } } } else if (dataIndex !== toVisualIndex) { // In this case, visual index is the same as data index @@ -1413,19 +1421,83 @@ class VirtualListBasic extends Component { } }; + initializeEditingDataOrder = () => { + this.editModeInfo.editingDataOrder = [...Array(this.props.dataSize).keys()]; + }; + + editingBegin = (editingIndex, positioningType, callbackRef = nop) => { + this.initializeEditingDataOrder(); + this.editModeInfo.editingIndex = editingIndex; + this.editModeInfo.lastVisualIndex = editingIndex; + this.editModeInfo.positioningType = positioningType; + + this.addMovingItem(callbackRef); + }; + + editingEnd = () => { + this.removeMovingItem(); + + this.editModeInfo.positioningType = null; + this.editModeInfo.editingIndex = null; + this.emitUpdateItemsOrder(this.editModeInfo.editingDataOrder); + this.editModeInfo.editingDataOrder = null; + }; + + resetEditModeInfo = () => { + this.editModeInfo.editingIndex = null; + this.editModeInfo.editingNode = null; + this.editModeInfo.editingDataOrder = null; + this.editModeInfo.guessPosition = false; + this.editModeInfo.lastVisualIndex = null; + this.removeMovingItem(); + }; + + scrollIntoViewByIndex = (index) => { + const {scrollPosition, primary} = this; + const {primaryPosition} = this.getGridPosition(index); + if (primaryPosition < scrollPosition) { + this.props.cbScrollTo({ + index, + stickTo: 'start', + animate: true + }); + } else if (primaryPosition + primary.itemSize > scrollPosition + primary.clientSize) { + // TBD: affordance is not calculated for now + this.props.cbScrollTo({ + index, + stickTo: 'end', + animate: true + }); + } + }; + + /* Edit mode for pointer mode */ + updateMovingPosition = (clientX, clientY) => { this.editModeInfo.lastPointer.clientX = clientX; this.editModeInfo.lastPointer.clientY = clientY; }; - initializeEditingDataOrder = () => { - this.editModeInfo.editingDataOrder = [...Array(this.props.dataSize).keys()]; + getClientXYFromXYPosition = (x, y) => { + const {scrollContentBounds: {clientWidth, x: boundX, y: boundY}} = this.editModeInfo; + let relativeX = x + let relativeY = y; + if (this.isPrimaryDirectionVertical) { + relativeY -= this.scrollPosition; + } else { + relativeX -= this.scrollPosition; + } + + return { + clientX: this.props.rtl? (clientWidth - relativeX) + boundX : relativeX + boundX, + clientY: relativeY + boundY + }; }; getXYPositionFromClientXY = (clientX, clientY) => { - const {scrollContentBounds: {clientWidth, x, y}} = this.editModeInfo; - const relativeX = this.props.rtl ? clientWidth - (clientX - x) : clientX - x; - const relativeY = clientY - y; + const {scrollContentBounds: {clientWidth, x: boundX, y: boundY}} = this.editModeInfo; + const relativeX = this.props.rtl ? clientWidth - (clientX - boundX) : clientX - boundX; + const relativeY = clientY - boundY; if (this.isPrimaryDirectionVertical) { return { x: relativeX, @@ -1510,15 +1582,21 @@ class VirtualListBasic extends Component { }; calculateMovingItemPosition = () => { - const {itemSize: {width, height}, lastPointer: {clientX, clientY}} = this.editModeInfo; - const {x, y} = this.getXYPositionFromClientXY( - clientX - (this.props.rtl ? -1 : 1) * width / 2, - clientY - height / 2 - ); - return {x, y, width, height}; + const {positioningType, itemSize: {width, height}, lastPointer: {clientX, clientY}} = this.editModeInfo; + if (positioningType === 'pointer') { + const {x, y} = this.getXYPositionFromClientXY( + clientX - (this.props.rtl ? -1 : 1) * width / 2, + clientY - height / 2 + ); + return {x, y, width, height}; + } else if (positioningType === 'index') { + const {lastVisualIndex} = this.editModeInfo; + const {left, top} = this.gridPositionToItemPosition(this.getGridPosition(lastVisualIndex)); + return {x: left, y: top, width, height}; + } }; - addMovingItem = () => { + addMovingItem = (callbackRef) => { /* TBD: using this.itemContainerRefs[key] ? */ const {childProps, itemRenderer, getComponentProps} = this.props; const {x, y, width, height} = this.calculateMovingItemPosition(); @@ -1526,6 +1604,9 @@ class VirtualListBasic extends Component { const componentProps = getComponentProps && getComponentProps(index) || {}; const itemContainerRef = (ref) => { this.editModeInfo.editingNode = ref; + if (ref) { + callbackRef(ref); + } }; const style = { width: width + 'px', @@ -1537,7 +1618,7 @@ class VirtualListBasic extends Component { this.cc[this.state.numOfItems] = (
- {itemRenderer({...childProps, ...componentProps, index, ['data-index']: index})} + {itemRenderer({...childProps, ...componentProps, index, ['data-index']: index, editMode: true})}
); @@ -1559,12 +1640,9 @@ class VirtualListBasic extends Component { }; itemMovingBegin = ({clientX, clientY}) => { - this.initializeEditingDataOrder(); - this.editModeInfo.editingIndex = this.getDataIndexFromPosition(clientX, clientY); - this.editModeInfo.lastVisualIndex = this.editModeInfo.editingIndex; + const editingIndex = this.getDataIndexFromPosition(clientX, clientY); + this.editingBegin(editingIndex, 'pointer'); this.updateMovingPosition(clientX, clientY); - - this.addMovingItem(); }; itemMoving = ({clientX, clientY}, skipRendering = false) => { @@ -1573,7 +1651,7 @@ class VirtualListBasic extends Component { const index = this.getVisualIndexFromPosition(); if (index !== null) { - this.moveItem(this.editModeInfo.editingIndex, index, skipRendering); + this.moveItem(this.editModeInfo.editingIndex, index, {skipRendering}); } if (this.editModeInfo.editingNode) { @@ -1583,10 +1661,7 @@ class VirtualListBasic extends Component { }; itemMovingEnd = () => { - this.editModeInfo.editingIndex = null; - this.removeMovingItem(); - this.emitUpdateItemsOrder(this.editModeInfo.editingDataOrder); - this.editModeInfo.editingDataOrder = null; + this.editingEnd(); }; itemMovingEnter = () => { @@ -1599,16 +1674,9 @@ class VirtualListBasic extends Component { } }; - resetEditModeInfo = () => { - this.editModeInfo.editingIndex = null; - this.editModeInfo.editingNode = null; - this.editModeInfo.editingDataOrder = null; - this.editModeInfo.guessPosition = false; - this.editModeInfo.lastVisualIndex = null; - this.removeMovingItem(); - }; - - // render + /* + * render + */ render () { const