Skip to content

Commit

Permalink
fix: fixes issues brought up in #105
Browse files Browse the repository at this point in the history
reconfigures validateDragHandle props, reapplies erroneously removed long press functionality, if clicking within a draggable item and the target itself is of type input then do not prevent default
  • Loading branch information
sashamilenkovic committed Oct 10, 2024
1 parent aa3a8f9 commit bec695f
Show file tree
Hide file tree
Showing 6 changed files with 221 additions and 44 deletions.
133 changes: 93 additions & 40 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,6 @@ let dropped = false;
*/
let documentController: AbortController | undefined;

let isNative = false;

let animationFrameId: number | null = null;

export const nodes: NodesData<any> = new WeakMap<Node, NodeData<unknown>>();
Expand Down Expand Up @@ -99,9 +97,12 @@ const baseDragState = {
originalZIndex: undefined,
pointerSelection: false,
preventEnter: false,
longPress: false,
longPressTimeout: 0,
remapJustFinished: false,
selectednodes: [],
selectedParent: undefined,
preventSynthDrag: false,
};

/**
Expand All @@ -121,11 +122,14 @@ export function resetState() {
preventEnter: false,
remapJustFinished: false,
selectednodes: [],
preventSynthDrag: false,
selectedParent: undefined,
pointerSelection: false,
synthScrollDirection: undefined,
draggedNodeDisplay: undefined,
synthDragScrolling: false,
longPress: false,
longPressTimeout: 0,
};

state = { ...baseDragState } as BaseDragState<unknown>;
Expand Down Expand Up @@ -155,7 +159,7 @@ export function setDragState<T>(
/**
*
*/
function handlePointerdownRoot(_e: PointerEvent) {
function handleRootPointerdown(_e: PointerEvent) {
if (state.activeState) setActive(state.activeState.parent, undefined, state);

if (state.selectedState)
Expand All @@ -164,7 +168,7 @@ function handlePointerdownRoot(_e: PointerEvent) {
state.selectedState = state.activeState = undefined;
}

function handlePointerupRoot(_e: PointerEvent) {
function handleRootPointerup(_e: PointerEvent) {
if (!isSynthDragState(state)) return;

const config = state.currentParent.data.config;
Expand Down Expand Up @@ -219,8 +223,8 @@ export function dragAndDrop<T>({
if (!documentController)
documentController = addEvents(document, {
dragover: handleRootDragover,
pointerdown: handlePointerdownRoot,
pointerup: handlePointerupRoot,
pointerdown: handleRootPointerdown,
pointerup: handleRootPointerup,
keydown: handleRootKeydown,
drop: handleRootDrop,
});
Expand Down Expand Up @@ -873,10 +877,6 @@ export function setupNode<T>(data: SetupNodeData<T>) {
pointerup: nodeEventData(config.handleNodePointerup),
pointermove: nodeEventData(config.handleNodePointermove),
handleNodePointerover: config.handleNodePointerover,
mousedown: () => {
if (!config.nativeDrag) isNative = false;
else isNative = true;
},
});

data.node.el.setAttribute("role", "option");
Expand Down Expand Up @@ -1106,6 +1106,7 @@ export function remapNodes<T>(parent: HTMLElement, force?: boolean) {
state.draggedNode.data = nodeData;

state.draggedNode.el = node;

const draggedNode = state.draggedNodes.find(
(x) => x.data.value === nodeData.value
);
Expand Down Expand Up @@ -1202,14 +1203,22 @@ export function handleDragstart<T>(
data: NodeDragEventData<T>,
state: BaseDragState<T>
) {
if (!validateDragstart(data) || !validateDragHandle(data)) {
const config = data.targetData.parent.data.config;

if (
!validateDragstart(data) ||
!validateDragHandle({
x: data.e.clientX,
y: data.e.clientY,
node: data.targetData.node,
config,
})
) {
data.e.preventDefault();

return;
}

const config = data.targetData.parent.data.config;

const nodes = config.draggedNodes(data);

config.dragstartClasses(data.targetData.node, nodes, config);
Expand All @@ -1236,12 +1245,22 @@ export function handleNodePointerdown<T>(
data: NodePointerEventData<T>,
state: BaseDragState<T>
) {
if (!validateDragHandle(data)) return;
if (
!validateDragHandle({
x: data.e.clientX,
y: data.e.clientY,
node: data.targetData.node,
config: data.targetData.parent.data.config,
})
)
return;

data.e.stopPropagation();

synthNodePointerDown = true;

handleLongPress(data, state, data.targetData.node);

const parentData = data.targetData.parent.data;

let selectedNodes = [data.targetData.node];
Expand Down Expand Up @@ -1325,7 +1344,7 @@ export function handleNodePointerdown<T>(
if (idx === -1) {
if (state.selectedState.parent.el !== data.targetData.parent.el) {
deselect(state.selectedState.nodes, data.targetData.parent, state);
} else if (parentData.config.multiDrag && (touchDevice || !isNative)) {
} else if (parentData.config.multiDrag && touchDevice) {
selectedNodes.push(...state.selectedState.nodes);
} else {
deselect(state.selectedState.nodes, data.targetData.parent, state);
Expand Down Expand Up @@ -1393,7 +1412,6 @@ export function initDrag<T>(
draggedNodes: Array<NodeRecord<T>>
): DragState<T> {
const dragState = setDragState(dragStateProps(data, draggedNodes));

data.e.stopPropagation();

if (data.e.dataTransfer) {
Expand Down Expand Up @@ -1465,25 +1483,24 @@ export function initDrag<T>(
return dragState;
}

export function validateDragHandle<T>(
data: NodeDragEventData<T> | NodePointerEventData<T>
): boolean {
const config = data.targetData.parent.data.config;

export function validateDragHandle<T>({
x,
y,
node,
config,
}: {
x: number;
y: number;
node: NodeRecord<T>;
config: ParentConfig<T>;
}): boolean {
if (!config.dragHandle) return true;

const dragHandles = data.targetData.node.el.querySelectorAll(
config.dragHandle
);
const dragHandles = node.el.querySelectorAll(config.dragHandle);

if (!dragHandles) return false;

const coordinates = data.e;

const elFromPoint = config.root.elementFromPoint(
coordinates.x,
coordinates.y
);
const elFromPoint = config.root.elementFromPoint(x, y);

if (!elFromPoint) return false;

Expand Down Expand Up @@ -1735,17 +1752,33 @@ export function handleEnd<T>(state: DragState<T> | SynthDragState<T>) {
state.emit("dragEnded", state);
}

const clickableTags = ["BUTTON", "A", "INPUT", "SELECT", "TEXTAREA", "LABEL"];

export function handleNodeTouchstart<T>(
data: NodeEventData<T>,
_state: BaseDragState<T>
) {
if (
!(data.e instanceof TouchEvent) ||
!(data.e.target instanceof HTMLElement)
)
return;

if (clickableTags.includes(data.e.target.tagName)) {
state.preventSynthDrag = true;

return;
}

if (data.e.cancelable) data.e.preventDefault();
}

export function handleNodePointerup<T>(
data: NodePointerEventData<T>,
state: DragState<T> | SynthDragState<T> | BaseDragState<T>
) {
state.preventSynthDrag = false;

if (!state.pointerSelection && state.selectedState)
deselect(state.selectedState.nodes, data.targetData.parent, state);

Expand All @@ -1755,6 +1788,14 @@ export function handleNodePointerup<T>(

synthNodePointerDown = false;

if ("longPressTimeout" in state && state.longPressTimeout)
clearTimeout(state.longPressTimeout);

removeClass(
data.targetData.parent.data.enabledNodes.map((x) => x.el),
config.longPressClass
);

if (!isDragState(state)) return;

config.handleEnd(state as DragState<T> | SynthDragState<T>);
Expand All @@ -1764,7 +1805,21 @@ export function handleNodePointermove<T>(
data: NodePointerEventData<T>,
state: SynthDragState<T> | BaseDragState<T>
) {
if (isNative || !synthNodePointerDown || !validateDragHandle(data)) return;
const touchDevice = isBrowser && window && "ontouchstart" in window;

if (!touchDevice || state.preventSynthDrag) return;

if (
!synthNodePointerDown ||
(!isSynthDragState(state) &&
!validateDragHandle({
x: data.e.clientX,
y: data.e.clientY,
node: data.targetData.node,
config: data.targetData.parent.data.config,
}))
)
return;

if (!isSynthDragState(state)) {
const config = data.targetData.parent.data.config;
Expand Down Expand Up @@ -1891,25 +1946,23 @@ function initSynthDrag<T>(

export function handleLongPress<T>(
data: NodePointerEventData<T>,
dragState: DragState<T>
state: BaseDragState<T>,
node: NodeRecord<T>
) {
const config = data.targetData.parent.data.config;

if (!config.longPress) return;

dragState.longPressTimeout = setTimeout(() => {
if (!dragState) return;
state.longPressTimeout = setTimeout(() => {
if (!state) return;

dragState.longPress = true;
state.longPress = true;

if (config.longPressClass && data.e.cancelable)
addNodeClass(
dragState.draggedNodes.map((x) => x.el),
config.longPressClass
);
addNodeClass([node.el], config.longPressClass);

data.e.preventDefault();
}, config.longPressTimeout || 200);
}, config.longPressDuration || 200);
}

function pointermoveClasses<T>(
Expand Down
6 changes: 5 additions & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ export interface ParentConfig<T> {
/**
* The time in milliseconds to wait before a long touch is performed.
*/
longPressTimeout?: any;
longPressDuration?: number;
/**
* The name of the parent (used for accepts function for increased specificity).
*/
Expand Down Expand Up @@ -793,6 +793,10 @@ export type BaseDragState<T> = {
emit: (event: string, data: unknown) => void;
on: (event: string, callback: CallableFunction) => void;
newActiveDescendant?: NodeRecord<T>;
preventSynthDrag: boolean;
longPress: boolean;
longPressTimeout: number;

/**
* The original z-index of the dragged node.
*/
Expand Down
2 changes: 1 addition & 1 deletion tests/.nuxt/nuxt.d.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Generated by nuxi
/// <reference types="@nuxt/devtools" />
/// <reference types="@nuxtjs/tailwindcss" />
/// <reference types="@nuxt/telemetry" />
/// <reference types="@nuxt/devtools" />
/// <reference types="nuxt" />
/// <reference path="types/app-defaults.d.ts" />
/// <reference path="types/plugins.d.ts" />
Expand Down
2 changes: 1 addition & 1 deletion tests/nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ export default defineNuxtConfig({
css: ["@/assets/css/main.css"],
compatibilityDate: "2024-09-22",
modules: ["@nuxtjs/tailwindcss"],
});
});
Loading

0 comments on commit bec695f

Please sign in to comment.