From fe9c54d8b3a1c3bd5ed25d55302e154c8d97f6aa Mon Sep 17 00:00:00 2001 From: Sasha Milenkovic Date: Wed, 11 Sep 2024 20:37:26 -0400 Subject: [PATCH] restores some synth scroll functionality, refactors some scroll logic --- src/index.ts | 186 ++++++++++++-------- src/types.ts | 18 +- tests/pages/scroll/native-drag/vertical.vue | 6 +- 3 files changed, 124 insertions(+), 86 deletions(-) diff --git a/src/index.ts b/src/index.ts index 822272a..17c9e0f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -157,7 +157,6 @@ export function dragAndDrop({ handleDragleaveNode, handleDropParent, nativeDrag: config.nativeDrag ?? true, - nativeDragScroll: config.nativeDragScroll ?? true, performSort, performTransfer, root: document, @@ -209,25 +208,35 @@ export function dragAndDrop({ } export function dragStateProps( - data: NodeDragEventData | NodePointerEventData + data: NodeDragEventData | NodePointerEventData, + nativeDrag = true ): DragStateProps { const { x, y } = eventCoordinates(data.e); const rect = data.targetData.node.el.getBoundingClientRect(); const scrollEls: Array<[HTMLElement, AbortController]> = []; - for (const scrollable of getScrollables()) { - const controller = addEvents(scrollable, { - scroll: preventSortOnScroll(), - }); - - scrollEls.push([scrollable, controller]); - } documentController = addEvents(document, { dragover: preventDefault, }); + for (const scrollable of getScrollables()) { + let controller; + + if (nativeDrag) { + controller = addEvents(scrollable, { + scroll: preventSortOnScroll(), + }); + } else { + controller = addEvents(scrollable, { + pointermove: handleScroll, + }); + } + + scrollEls.push([scrollable, controller]); + } + return { affectedNodes: [], ascendingDirection: false, @@ -1007,6 +1016,7 @@ export function end( _eventData: NodeEventData, state: DragState | SynthDragState ) { + state.scrolling = false; if (documentController) { documentController.abort(); @@ -1062,7 +1072,6 @@ export function handleTouchstart( data: NodeEventData, _state: BaseDragState ) { - console.log("handle touchstart"); data.e.preventDefault(); } @@ -1070,22 +1079,13 @@ export function handlePointermove( data: NodePointerEventData, state: SynthDragState | BaseDragState ) { + // TODO: Probably need stopPropagation here + if (isNative || !synthNodePointerDown || !validateDragHandle(data)) return; if (!isSynthDragState(state)) { const synthDragState = initSyntheticDrag(data, state) as SynthDragState; - synthDragState.draggedNode.el.addEventListener( - "lostpointercapture", - (event) => { - console.log("lost pointer capture"); - } - ); - - synthDragState.draggedNode.el.addEventListener("pointercancel", (event) => { - console.log("pointer cancel"); - }); - synthMove(data, synthDragState); if (data.targetData.parent.data.config.onDragstart) @@ -1114,8 +1114,6 @@ function initSyntheticDrag( data: NodePointerEventData, _state: BaseDragState ): SynthDragState { - data.e.stopPropagation(); - const display = data.targetData.node.el.style.display; const rect = data.targetData.node.el.getBoundingClientRect(); @@ -1144,6 +1142,7 @@ function initSyntheticDrag( clonedDraggedEls: [], clonedDraggedNode, draggedNodeDisplay: display, + synthDragScrolling: false, }; addEvents(document, { @@ -1151,11 +1150,11 @@ function initSyntheticDrag( }); const synthDragState = setDragState({ - ...dragStateProps(data), + ...dragStateProps(data, false), ...synthDragStateProps, - }); + }) as SynthDragState; - return synthDragState as SynthDragState; + return synthDragState; } export function handleLongPress( @@ -1202,33 +1201,24 @@ function pointermoveClasses( } function getScrollData( - state?: DragState | SynthDragState -): ScrollData | void { - return; - if (!state || !state.scrollParent) return; - - // If the scrollParent is the document and it isn't a touch event, then - // we can just let the browser handle the scrolling. - if ( - state.scrollParent === document.documentElement && - !("clonedDraggedNode" in state) - ) - return; + e: DragEvent | PointerEvent, + state: DragState | SynthDragState +): ScrollData | undefined { + if (!(e.currentTarget instanceof HTMLElement)) return; - const { x, y, width, height } = state.scrollParent.getBoundingClientRect(); + const { x, y, width, height } = e.currentTarget.getBoundingClientRect(); const { x: xThresh, y: yThresh, scrollOutside, - } = state.lastParent.data.config.scrollBehavior; + } = state.initialParent.data.config.scrollBehavior; return { - state, xThresh, yThresh, scrollOutside, - scrollEls: state.scrollEls, + scrollParent: e.currentTarget, x, y, width, @@ -1236,23 +1226,73 @@ function getScrollData( }; } -function shouldScroll(direction: string): DragState | void { - const data = getScrollData(state); +let interval: number | null = null; + +function setSynthScrollDirection( + direction: "up" | "down" | "left" | "right" | undefined, + el: HTMLElement, + state: SynthDragState +) { + if (state.syntheticScrollDirection === direction) return; + + state.syntheticScrollDirection = direction; + + if (interval !== null) clearInterval(interval); + + interval = setInterval( + (direction: "up" | "down" | "left" | "right") => { + console.log("direction", direction); + switch (direction) { + case "up": + el.scrollBy(0, -1); + + break; + + case "down": + el.scrollBy(0, 1); + + break; + + case "left": + el.scrollBy(-1, 0); + + break; + + case "right": + el.scrollBy(1, 0); + + break; + } + }, + 20, + direction + ); +} + +function shouldScroll( + direction: string, + e: DragEvent | PointerEvent, + state: DragState | SynthDragState +): boolean { + const dataScrollData = getScrollData(e, state); - if (!data) return; + if (!dataScrollData) return false; switch (direction) { case "down": - return shouldScrollDown(data.state, data); + return !!shouldScrollDown(state, dataScrollData); case "up": - return shouldScrollUp(data.state, data); + return !!shouldScrollUp(state, dataScrollData); case "right": - return shouldScrollRight(data.state, data); + return !!shouldScrollRight(state, dataScrollData); case "left": - return shouldScrollLeft(data.state, data); + return !!shouldScrollLeft(state, dataScrollData); + + default: + return false; } } @@ -1295,7 +1335,6 @@ function shouldScrollUp( state: DragState, data: ScrollData ): DragState | void { - return; const diff = data.scrollParent.clientHeight + data.y - state.coordinates.y; if (!data.scrollOutside && diff > data.scrollParent.clientHeight) return; @@ -1303,16 +1342,17 @@ function shouldScrollUp( if ( diff > data.yThresh * data.scrollParent.clientHeight && data.scrollParent.scrollTop !== 0 - ) - return state; + ) { + return data.scrollParent; + } } function shouldScrollDown( state: DragState, data: ScrollData -): DragState | void { - return; +): HTMLElement | void { const diff = data.scrollParent.clientHeight + data.y - state.coordinates.y; + if (!data.scrollOutside && diff < 0) return; if ( @@ -1321,8 +1361,9 @@ function shouldScrollDown( data.scrollParent.scrollTop + data.scrollParent.clientHeight >= data.scrollParent.scrollHeight ) - ) - return state; + ) { + return data.scrollParent; + } } function moveNode(data: NodePointerEventData, state: SynthDragState) { @@ -1363,7 +1404,7 @@ export function synthMove( moveNode(data, state); - handleScroll(); + //handleScroll(data, state); const elFromPoint = getElFromPoint(data); @@ -1390,27 +1431,20 @@ export function synthMove( } } -export function handleScroll() { - for (const direction of Object.keys(scrollConfig)) { - const [x, y] = scrollConfig[direction]; +export function handleScroll(e: DragEvent | PointerEvent) { + if (!isSynthDragState(state)) return; - performScroll(direction, x, y); - } -} + let directionSet = false; -function performScroll(direction: string, x: number, y: number) { - const state = shouldScroll(direction); - - if (!state) return; + for (const direction of Object.keys(scrollConfig)) { + if (shouldScroll(direction, e, state)) { + setSynthScrollDirection(direction, e.currentTarget, state); - //state.scrollParent.scrollBy(x, y); + break; + } + } - setTimeout( - () => { - performScroll(direction, x, y); - }, - "clonedDraggedNode" in state ? 50 : 100 - ); + if (!directionSet) state.syntheticScrollDirection = undefined; } export function handleDragoverNode( @@ -1423,8 +1457,6 @@ export function handleDragoverNode( state.coordinates.x = x; - if (!state.initialParent.data.config.nativeDragScroll) handleScroll(); - dragoverNode(data, state); } @@ -1440,7 +1472,7 @@ export function handleDragoverParent( state.coordinates.x = x; - if (!state.initialParent.data.config.nativeDragScroll) handleScroll(); + //if (!state.initialParent.data.config.nativeDragScroll) handleScroll(state); transfer(data, state); } diff --git a/src/types.ts b/src/types.ts index 4bad90f..93380da 100644 --- a/src/types.ts +++ b/src/types.ts @@ -168,12 +168,6 @@ export interface ParentConfig { * If set to false, the library will not use the native drag and drop API. */ nativeDrag?: boolean; - /** - * Is using native drag events, indicates whether native drag scroll should be - * used (the default behavior when dragging an item to the edge of a scrollable - * container) or if the library should handle scrolling itself - */ - nativeDragScroll?: boolean; /** * Function that is called when a sort operation is to be performed. */ @@ -654,10 +648,19 @@ export interface SynthDragStateProps { * Element */ clonedDraggedNode: HTMLElement; + /** + * Direction of the synthetic drag scroll + */ + syntheticScrollDirection: "up" | "down" | "left" | "right" | undefined; /** * The display of the synthetic node. */ draggedNodeDisplay: string; + /** + * Flag indcating whether a scrollable el is being scrolled via. + * synthetic drag. + */ + synthDragScrolling: boolean; /** * Pointer id of dragged el */ @@ -815,10 +818,9 @@ export interface DragendEventData { } export interface ScrollData { - state: DragState; xThresh: number; yThresh: number; - scrollParent: Array<[HTMLElement, AbortController]>; + scrollParent: HTMLElement; scrollOutside?: boolean; x: number; y: number; diff --git a/tests/pages/scroll/native-drag/vertical.vue b/tests/pages/scroll/native-drag/vertical.vue index 36a8b1f..4b35ace 100644 --- a/tests/pages/scroll/native-drag/vertical.vue +++ b/tests/pages/scroll/native-drag/vertical.vue @@ -80,11 +80,15 @@ const [parent2, values2] = useDragAndDrop(vegetables, { -