UNPKG

@syncfusion/react-base

Version:

A common package of core React base, methods and class definitions

150 lines (149 loc) 5.42 kB
import { useId, useLayoutEffect } from 'react'; import { Browser } from './browser'; import { addClass, isVisible, matches } from './dom'; import { compareElementParent } from './util'; import { EventHandler } from './event-handler'; import { useDragDropContext } from './dragdrop'; /** * Creates a droppable instance with the specified element and properties. * * @private * @param {RefObject<HTMLElement>} [element] - Reference to the HTML element to make droppable. * @param {IDroppable} [props] - Configuration properties for the droppable instance. * @returns {IDroppable} The configured droppable instance. */ export function useDroppable(element, props) { const droppableId = useId(); const droppableContext = useDragDropContext(); const { registerDroppable, unregisterDroppable } = droppableContext || {}; const propsRef = { accept: '', scope: 'default', dragData: {}, drop: null, over: null, out: null, ...props }; /** Represents whether the mouse is over the droppable area */ let mouseOverRef = false; /** Indicates if drag stop has been called */ let dragStopCalledRef = true; /** * Method to add drop events. * * @returns {void} */ function addEvent() { EventHandler.add(element.current, Browser.isSafari() ? 'touchend' : Browser.touchEndEvent, propsRef.intDrop); } /** * Handles interactions when a dragged item is over the droppable area. * * @param {MouseEvent | TouchEvent} event - Mouse or touch event arguments. * @param {Element} [element] - The target element over which the drag is happening. * @returns {void} */ propsRef.intOver = (event, element) => { if (!mouseOverRef) { const drag = propsRef.dragData[propsRef.scope]; if (propsRef.over) { propsRef.over({ event, target: element, dragData: drag }); } mouseOverRef = true; } }; /** * Method for handling interactions when dragged item is out of the droppable area. * * @param {MouseEvent | TouchEvent} event - Mouse or touch event arguments. * @param {Element} [element] - The target element from which the drag is moving out. * @returns {void} */ propsRef.intOut = (event, element) => { if (mouseOverRef) { if (propsRef.out) { propsRef.out({ event, target: element }); } mouseOverRef = false; } }; /** * Method to handle drop event. * * @param {MouseEvent | TouchEvent} evt - Mouse or touch event arguments. * @param {HTMLElement} [element] - The target element where the drop is happening. * @returns {void} */ propsRef.intDrop = (evt, element) => { if (!dragStopCalledRef) { return; } else { dragStopCalledRef = false; } let accept = true; const drag = propsRef.dragData[propsRef.scope]; const isDrag = drag ? (drag.helper && isVisible(drag.helper)) : false; let area; if (isDrag) { area = isDropArea(evt, drag.helper, element); if (propsRef.accept) { accept = matches(drag.helper, propsRef.accept); } } if (isDrag && propsRef.drop && area.canDrop && accept) { propsRef.drop({ event: evt, target: area.target, droppedElement: drag.helper, dragData: drag }); } mouseOverRef = false; }; /** * Method to check if the drop area is valid. * * @param {MouseEvent | TouchEvent} evt - Mouse or touch event arguments. * @param {HTMLElement} helper - The helper element involved in the drag operation. * @param {HTMLElement} [element] - The element to check for drop validity. * @returns {DropData} - The result indicating if the area is a valid drop target and the target itself. */ function isDropArea(evt, helper, element) { const area = { canDrop: true, target: element || evt.target }; const isTouch = evt.type === 'touchend'; if (isTouch || area.target === helper) { helper.style.display = 'none'; const coord = isTouch ? (evt.changedTouches[0]) : evt; const ele = document.elementFromPoint(coord.clientX, coord.clientY); area.canDrop = false; area.canDrop = compareElementParent(ele, element); if (area.canDrop) { area.target = ele; } helper.style.display = ''; } return area; } /** * Method to clean up and remove event handlers on the component destruction. * * @returns {void} */ function removeEvent() { EventHandler.remove(element.current, Browser.isSafari() ? 'touchend' : Browser.touchEndEvent, propsRef.intDrop); } useLayoutEffect(() => { addClass([element.current], ['sf-lib', 'sf-droppable']); addEvent(); if (registerDroppable) { registerDroppable(droppableId, { ...propsRef, element: element }); } return () => { if (unregisterDroppable) { unregisterDroppable(droppableId); } removeEvent(); }; }, []); return propsRef; }