diff --git a/packages/chonky/src/components/internal/ChonkyPresentationLayer.tsx b/packages/chonky/src/components/internal/ChonkyPresentationLayer.tsx index 814914cf..743981b6 100644 --- a/packages/chonky/src/components/internal/ChonkyPresentationLayer.tsx +++ b/packages/chonky/src/components/internal/ChonkyPresentationLayer.tsx @@ -15,6 +15,7 @@ import { selectFileActionIds, selectIsDnDDisabled, } from '../../redux/selectors'; +import { useDndContextAvailable } from '../../util/dnd-fallback'; import { elementIsInsideButton } from '../../util/helpers'; import { makeGlobalChonkyStyles } from '../../util/styles'; import { useContextMenuTrigger } from '../external/FileContextMenu-hooks'; @@ -60,13 +61,14 @@ export const ChonkyPresentationLayer: React.FC = ( [fileActionIds] ); + const dndContextAvailable = useDndContextAvailable(); const showContextMenu = useContextMenuTrigger(); const classes = useStyles(); return ( - {!dndDisabled && } + {!dndDisabled && dndContextAvailable && } {hotkeyListenerComponents} {children ? children : null} diff --git a/packages/chonky/src/util/dnd-fallback.ts b/packages/chonky/src/util/dnd-fallback.ts new file mode 100644 index 00000000..b459cecb --- /dev/null +++ b/packages/chonky/src/util/dnd-fallback.ts @@ -0,0 +1,26 @@ +import { useCallback, useContext } from 'react'; +import { DndContext, useDrag, useDrop } from 'react-dnd'; + +export const useDndContextAvailable = () => { + const dndContext = useContext(DndContext); + const { dragDropManager } = dndContext; + return !!dragDropManager; +}; + +export const useDragIfAvailable: typeof useDrag = (...args) => { + // This is an ugly hack to make `useDrag` not throw if a `DndContext` is not available. + // See: https://github.com/react-dnd/react-dnd/issues/2212 + const dndContextAvailable = useDndContextAvailable(); + const mockHook = useCallback(() => [{} as any, () => null, () => null], []); + // @ts-ignore + const useHook: typeof useDrag = dndContextAvailable ? useDrag : mockHook; + return useHook(...args); +}; + +export const useDropIfAvailable: typeof useDrop = (...args) => { + const dndContextAvailable = useDndContextAvailable(); + const mockHook = useCallback(() => [{} as any, () => null], []); + // @ts-ignore + const useHook: typeof useDrop = dndContextAvailable ? useDrop : mockHook; + return useHook(...args); +}; diff --git a/packages/chonky/src/util/dnd.ts b/packages/chonky/src/util/dnd.ts index 76ec5e25..ba1abef6 100644 --- a/packages/chonky/src/util/dnd.ts +++ b/packages/chonky/src/util/dnd.ts @@ -1,5 +1,5 @@ import { useCallback, useEffect, useMemo } from 'react'; -import { DragSourceMonitor, DropTargetMonitor, useDrag, useDrop } from 'react-dnd'; +import { DragSourceMonitor, DropTargetMonitor } from 'react-dnd'; import { getEmptyImage } from 'react-dnd-html5-backend'; import { useDispatch, useSelector, useStore } from 'react-redux'; import { ExcludeKeys, Nullable } from 'tsdef'; @@ -21,6 +21,7 @@ import { } from '../types/dnd.types'; import { DndEntryState } from '../types/file-list.types'; import { FileData } from '../types/file.types'; +import { useDragIfAvailable, useDropIfAvailable } from './dnd-fallback'; import { FileHelper } from './file-helper'; import { useInstanceVariable } from './hooks-helpers'; @@ -88,7 +89,7 @@ export const useFileDrag = (file: Nullable) => { (monitor) => ({ isDragging: monitor.isDragging() }), [] ); - const [{ isDragging: dndIsDragging }, drag, preview] = useDrag({ + const [{ isDragging: dndIsDragging }, drag, preview] = useDragIfAvailable({ item, canDrag, begin: onDragStart, @@ -168,7 +169,7 @@ export const useFileDrop = ({ const [ { isOver: dndIsOver, isOverCurrent: dndIsOverCurrent, canDrop: dndCanDrop }, drop, - ] = useDrop({ + ] = useDropIfAvailable({ accept: ChonkyDndFileEntryType, drop: onDrop, canDrop,