Skip to content

Commit

Permalink
restores some synth scroll functionality, refactors some scroll logic
Browse files Browse the repository at this point in the history
  • Loading branch information
sashamilenkovic committed Sep 12, 2024
1 parent 5c5c507 commit fe9c54d
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 86 deletions.
186 changes: 109 additions & 77 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,6 @@ export function dragAndDrop<T>({
handleDragleaveNode,
handleDropParent,
nativeDrag: config.nativeDrag ?? true,
nativeDragScroll: config.nativeDragScroll ?? true,
performSort,
performTransfer,
root: document,
Expand Down Expand Up @@ -209,25 +208,35 @@ export function dragAndDrop<T>({
}

export function dragStateProps<T>(
data: NodeDragEventData<T> | NodePointerEventData<T>
data: NodeDragEventData<T> | NodePointerEventData<T>,
nativeDrag = true
): DragStateProps<T> {
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,
Expand Down Expand Up @@ -1007,6 +1016,7 @@ export function end<T>(
_eventData: NodeEventData<T>,
state: DragState<T> | SynthDragState<T>
) {
state.scrolling = false;
if (documentController) {
documentController.abort();

Expand Down Expand Up @@ -1062,30 +1072,20 @@ export function handleTouchstart<T>(
data: NodeEventData<T>,
_state: BaseDragState
) {
console.log("handle touchstart");
data.e.preventDefault();
}

export function handlePointermove<T>(
data: NodePointerEventData<T>,
state: SynthDragState<T> | BaseDragState
) {
// TODO: Probably need stopPropagation here

if (isNative || !synthNodePointerDown || !validateDragHandle(data)) return;

if (!isSynthDragState(state)) {
const synthDragState = initSyntheticDrag(data, state) as SynthDragState<T>;

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)
Expand Down Expand Up @@ -1114,8 +1114,6 @@ function initSyntheticDrag<T>(
data: NodePointerEventData<T>,
_state: BaseDragState
): SynthDragState<T> {
data.e.stopPropagation();

const display = data.targetData.node.el.style.display;

const rect = data.targetData.node.el.getBoundingClientRect();
Expand Down Expand Up @@ -1144,18 +1142,19 @@ function initSyntheticDrag<T>(
clonedDraggedEls: [],
clonedDraggedNode,
draggedNodeDisplay: display,
synthDragScrolling: false,
};

addEvents(document, {
contextmenu: noDefault,
});

const synthDragState = setDragState({
...dragStateProps(data),
...dragStateProps(data, false),
...synthDragStateProps,
});
}) as SynthDragState<T>;

return synthDragState as SynthDragState<T>;
return synthDragState;
}

export function handleLongPress<T>(
Expand Down Expand Up @@ -1202,57 +1201,98 @@ function pointermoveClasses<T>(
}

function getScrollData<T>(
state?: DragState<T> | SynthDragState<T>
): ScrollData<T> | 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<T> | SynthDragState<T>
): ScrollData<T> | 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,
height,
};
}

function shouldScroll<T>(direction: string): DragState<T> | void {
const data = getScrollData(state);
let interval: number | null = null;

function setSynthScrollDirection<T>(
direction: "up" | "down" | "left" | "right" | undefined,
el: HTMLElement,
state: SynthDragState<T>
) {
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<T>(
direction: string,
e: DragEvent | PointerEvent,
state: DragState<T> | SynthDragState<T>
): 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;
}
}

Expand Down Expand Up @@ -1295,24 +1335,24 @@ function shouldScrollUp<T>(
state: DragState<T>,
data: ScrollData<T>
): DragState<T> | void {
return;
const diff = data.scrollParent.clientHeight + data.y - state.coordinates.y;

if (!data.scrollOutside && diff > data.scrollParent.clientHeight) return;

if (
diff > data.yThresh * data.scrollParent.clientHeight &&
data.scrollParent.scrollTop !== 0
)
return state;
) {
return data.scrollParent;
}
}

function shouldScrollDown<T>(
state: DragState<T>,
data: ScrollData<T>
): DragState<T> | void {
return;
): HTMLElement | void {
const diff = data.scrollParent.clientHeight + data.y - state.coordinates.y;

if (!data.scrollOutside && diff < 0) return;

if (
Expand All @@ -1321,8 +1361,9 @@ function shouldScrollDown<T>(
data.scrollParent.scrollTop + data.scrollParent.clientHeight >=
data.scrollParent.scrollHeight
)
)
return state;
) {
return data.scrollParent;
}
}

function moveNode<T>(data: NodePointerEventData<T>, state: SynthDragState<T>) {
Expand Down Expand Up @@ -1363,7 +1404,7 @@ export function synthMove<T>(

moveNode(data, state);

handleScroll();
//handleScroll(data, state);

const elFromPoint = getElFromPoint(data);

Expand All @@ -1390,27 +1431,20 @@ export function synthMove<T>(
}
}

export function handleScroll() {
for (const direction of Object.keys(scrollConfig)) {
const [x, y] = scrollConfig[direction];
export function handleScroll<T>(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<T>(
Expand All @@ -1423,8 +1457,6 @@ export function handleDragoverNode<T>(

state.coordinates.x = x;

if (!state.initialParent.data.config.nativeDragScroll) handleScroll();

dragoverNode(data, state);
}

Expand All @@ -1440,7 +1472,7 @@ export function handleDragoverParent<T>(

state.coordinates.x = x;

if (!state.initialParent.data.config.nativeDragScroll) handleScroll();
//if (!state.initialParent.data.config.nativeDragScroll) handleScroll(state);

transfer(data, state);
}
Expand Down
18 changes: 10 additions & 8 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,12 +168,6 @@ export interface ParentConfig<T> {
* 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.
*/
Expand Down Expand Up @@ -654,10 +648,19 @@ export interface SynthDragStateProps<T> {
* 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
*/
Expand Down Expand Up @@ -815,10 +818,9 @@ export interface DragendEventData<T> {
}

export interface ScrollData<T> {
state: DragState<T>;
xThresh: number;
yThresh: number;
scrollParent: Array<[HTMLElement, AbortController]>;
scrollParent: HTMLElement;
scrollOutside?: boolean;
x: number;
y: number;
Expand Down
Loading

0 comments on commit fe9c54d

Please sign in to comment.