UNPKG

@wordpress/compose

Version:
8 lines (7 loc) 10.1 kB
{ "version": 3, "sources": ["../../../src/hooks/use-drop-zone/index.js"], "sourcesContent": ["/**\n * Internal dependencies\n */\nimport useRefEffect from '../use-ref-effect';\nimport useEvent from '../use-event';\n\n/**\n * A hook to facilitate drag and drop handling.\n *\n * @param {Object} props Named parameters.\n * @param {?HTMLElement} [props.dropZoneElement] Optional element to be used as the drop zone.\n * @param {boolean} [props.isDisabled] Whether or not to disable the drop zone.\n * @param {(e: DragEvent) => void} [props.onDragStart] Called when dragging has started.\n * @param {(e: DragEvent) => void} [props.onDragEnter] Called when the zone is entered.\n * @param {(e: DragEvent) => void} [props.onDragOver] Called when the zone is moved within.\n * @param {(e: DragEvent) => void} [props.onDragLeave] Called when the zone is left.\n * @param {(e: MouseEvent) => void} [props.onDragEnd] Called when dragging has ended.\n * @param {(e: DragEvent) => void} [props.onDrop] Called when dropping in the zone.\n *\n * @return {React.RefCallback<HTMLElement>} Ref callback to be passed to the drop zone element.\n */\nexport default function useDropZone( {\n\tdropZoneElement,\n\tisDisabled,\n\tonDrop: _onDrop,\n\tonDragStart: _onDragStart,\n\tonDragEnter: _onDragEnter,\n\tonDragLeave: _onDragLeave,\n\tonDragEnd: _onDragEnd,\n\tonDragOver: _onDragOver,\n} ) {\n\tconst onDropEvent = useEvent( _onDrop );\n\tconst onDragStartEvent = useEvent( _onDragStart );\n\tconst onDragEnterEvent = useEvent( _onDragEnter );\n\tconst onDragLeaveEvent = useEvent( _onDragLeave );\n\tconst onDragEndEvent = useEvent( _onDragEnd );\n\tconst onDragOverEvent = useEvent( _onDragOver );\n\n\treturn useRefEffect(\n\t\t( elem ) => {\n\t\t\tif ( isDisabled ) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// If a custom dropZoneRef is passed, use that instead of the element.\n\t\t\t// This allows the dropzone to cover an expanded area, rather than\n\t\t\t// be restricted to the area of the ref returned by this hook.\n\t\t\tconst element = dropZoneElement ?? elem;\n\n\t\t\tlet isDragging = false;\n\n\t\t\tconst { ownerDocument } = element;\n\n\t\t\t/**\n\t\t\t * Checks if an element is in the drop zone.\n\t\t\t *\n\t\t\t * @param {EventTarget|null} targetToCheck\n\t\t\t *\n\t\t\t * @return {boolean} True if in drop zone, false if not.\n\t\t\t */\n\t\t\tfunction isElementInZone( targetToCheck ) {\n\t\t\t\tconst { defaultView } = ownerDocument;\n\n\t\t\t\tif (\n\t\t\t\t\t! targetToCheck ||\n\t\t\t\t\t! defaultView ||\n\t\t\t\t\t! ( targetToCheck instanceof defaultView.HTMLElement ) ||\n\t\t\t\t\t! element.contains( targetToCheck )\n\t\t\t\t) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\n\t\t\t\t/** @type {HTMLElement|null} */\n\t\t\t\tlet elementToCheck = targetToCheck;\n\n\t\t\t\tdo {\n\t\t\t\t\tif ( elementToCheck.dataset.isDropZone ) {\n\t\t\t\t\t\treturn elementToCheck === element;\n\t\t\t\t\t}\n\t\t\t\t} while ( ( elementToCheck = elementToCheck.parentElement ) );\n\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tfunction maybeDragStart( /** @type {DragEvent} */ event ) {\n\t\t\t\tif ( isDragging ) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tisDragging = true;\n\n\t\t\t\t// Note that `dragend` doesn't fire consistently for file and\n\t\t\t\t// HTML drag events where the drag origin is outside the browser\n\t\t\t\t// window. In Firefox it may also not fire if the originating\n\t\t\t\t// node is removed.\n\t\t\t\townerDocument.addEventListener( 'dragend', maybeDragEnd );\n\t\t\t\townerDocument.addEventListener( 'mousemove', maybeDragEnd );\n\n\t\t\t\tif ( _onDragStart ) {\n\t\t\t\t\tonDragStartEvent( event );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfunction onDragEnter( /** @type {DragEvent} */ event ) {\n\t\t\t\tevent.preventDefault();\n\n\t\t\t\t// The `dragenter` event will also fire when entering child\n\t\t\t\t// elements, but we only want to call `onDragEnter` when\n\t\t\t\t// entering the drop zone, which means the `relatedTarget`\n\t\t\t\t// (element that has been left) should be outside the drop zone.\n\t\t\t\tif (\n\t\t\t\t\telement.contains(\n\t\t\t\t\t\t/** @type {Node} */ ( event.relatedTarget )\n\t\t\t\t\t)\n\t\t\t\t) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif ( _onDragEnter ) {\n\t\t\t\t\tonDragEnterEvent( event );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfunction onDragOver( /** @type {DragEvent} */ event ) {\n\t\t\t\t// Only call onDragOver for the innermost hovered drop zones.\n\t\t\t\tif ( ! event.defaultPrevented && _onDragOver ) {\n\t\t\t\t\tonDragOverEvent( event );\n\t\t\t\t}\n\n\t\t\t\t// Prevent the browser default while also signalling to parent\n\t\t\t\t// drop zones that `onDragOver` is already handled.\n\t\t\t\tevent.preventDefault();\n\t\t\t}\n\n\t\t\tfunction onDragLeave( /** @type {DragEvent} */ event ) {\n\t\t\t\t// The `dragleave` event will also fire when leaving child\n\t\t\t\t// elements, but we only want to call `onDragLeave` when\n\t\t\t\t// leaving the drop zone, which means the `relatedTarget`\n\t\t\t\t// (element that has been entered) should be outside the drop\n\t\t\t\t// zone.\n\t\t\t\t// Note: This is not entirely reliable in Safari due to this bug\n\t\t\t\t// https://bugs.webkit.org/show_bug.cgi?id=66547\n\n\t\t\t\tif ( isElementInZone( event.relatedTarget ) ) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif ( _onDragLeave ) {\n\t\t\t\t\tonDragLeaveEvent( event );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfunction onDrop( /** @type {DragEvent} */ event ) {\n\t\t\t\t// Don't handle drop if an inner drop zone already handled it.\n\t\t\t\tif ( event.defaultPrevented ) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// Prevent the browser default while also signalling to parent\n\t\t\t\t// drop zones that `onDrop` is already handled.\n\t\t\t\tevent.preventDefault();\n\n\t\t\t\t// This seemingly useless line has been shown to resolve a\n\t\t\t\t// Safari issue where files dragged directly from the dock are\n\t\t\t\t// not recognized.\n\t\t\t\t// eslint-disable-next-line no-unused-expressions\n\t\t\t\tevent.dataTransfer && event.dataTransfer.files.length;\n\n\t\t\t\tif ( _onDrop ) {\n\t\t\t\t\tonDropEvent( event );\n\t\t\t\t}\n\n\t\t\t\tmaybeDragEnd( event );\n\t\t\t}\n\n\t\t\tfunction maybeDragEnd( /** @type {MouseEvent} */ event ) {\n\t\t\t\tif ( ! isDragging ) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tisDragging = false;\n\n\t\t\t\townerDocument.removeEventListener( 'dragend', maybeDragEnd );\n\t\t\t\townerDocument.removeEventListener( 'mousemove', maybeDragEnd );\n\n\t\t\t\tif ( _onDragEnd ) {\n\t\t\t\t\tonDragEndEvent( event );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\telement.setAttribute( 'data-is-drop-zone', 'true' );\n\t\t\telement.addEventListener( 'drop', onDrop );\n\t\t\telement.addEventListener( 'dragenter', onDragEnter );\n\t\t\telement.addEventListener( 'dragover', onDragOver );\n\t\t\telement.addEventListener( 'dragleave', onDragLeave );\n\t\t\t// The `dragstart` event doesn't fire if the drag started outside\n\t\t\t// the document.\n\t\t\townerDocument.addEventListener( 'dragenter', maybeDragStart );\n\n\t\t\treturn () => {\n\t\t\t\telement.removeAttribute( 'data-is-drop-zone' );\n\t\t\t\telement.removeEventListener( 'drop', onDrop );\n\t\t\t\telement.removeEventListener( 'dragenter', onDragEnter );\n\t\t\t\telement.removeEventListener( 'dragover', onDragOver );\n\t\t\t\telement.removeEventListener( 'dragleave', onDragLeave );\n\t\t\t\townerDocument.removeEventListener( 'dragend', maybeDragEnd );\n\t\t\t\townerDocument.removeEventListener( 'mousemove', maybeDragEnd );\n\t\t\t\townerDocument.removeEventListener(\n\t\t\t\t\t'dragenter',\n\t\t\t\t\tmaybeDragStart\n\t\t\t\t);\n\t\t\t};\n\t\t},\n\t\t[ isDisabled, dropZoneElement ] // Refresh when the passed in dropZoneElement changes.\n\t);\n}\n"], "mappings": "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", "names": ["useEvent", "useRefEffect"] }