// @flow import ReactDOM from 'react-dom'; import { useMemo, useCallback } from 'use-memo-one'; import React, { useRef, useContext, type Node } from 'react'; import { invariant } from '../../invariant'; import type { DraggableId } from '../../types'; import type { Props, Provided } from './droppable-types'; import useDroppablePublisher from '../use-droppable-publisher'; import Placeholder from '../placeholder'; import AppContext, { type AppContextValue } from '../context/app-context'; import DroppableContext, { type DroppableContextValue, } from '../context/droppable-context'; // import useAnimateInOut from '../use-animate-in-out/use-animate-in-out'; import getMaxWindowScroll from '../window/get-max-window-scroll'; import useValidation from './use-validation'; import type { StateSnapshot as DraggableStateSnapshot, Provided as DraggableProvided, } from '../draggable/draggable-types'; import AnimateInOut, { type AnimateProvided, } from '../animate-in-out/animate-in-out'; import { PrivateDraggable } from '../draggable/draggable-api'; export default function Droppable(props: Props) { const appContext: ?AppContextValue = useContext(AppContext); invariant(appContext, 'Could not find app context'); const { contextId, isMovementAllowed } = appContext; const droppableRef = useRef(null); const placeholderRef = useRef(null); const { // own props children, droppableId, type, mode, direction, ignoreContainerClipping, isDropDisabled, isCombineEnabled, // map props snapshot, useClone, // dispatch props updateViewportMaxScroll, // clone (ownProps) getContainerForClone, } = props; const getDroppableRef = useCallback( (): ?HTMLElement => droppableRef.current, [], ); const setDroppableRef = useCallback((value: ?HTMLElement) => { droppableRef.current = value; }, []); const getPlaceholderRef = useCallback( (): ?HTMLElement => placeholderRef.current, [], ); const setPlaceholderRef = useCallback((value: ?HTMLElement) => { placeholderRef.current = value; }, []); useValidation({ props, getDroppableRef, getPlaceholderRef, }); const onPlaceholderTransitionEnd = useCallback(() => { // A placeholder change can impact the window's max scroll if (isMovementAllowed()) { updateViewportMaxScroll({ maxScroll: getMaxWindowScroll() }); } }, [isMovementAllowed, updateViewportMaxScroll]); useDroppablePublisher({ droppableId, type, mode, direction, isDropDisabled, isCombineEnabled, ignoreContainerClipping, getDroppableRef, }); const placeholder: Node = ( {({ onClose, data, animate }: AnimateProvided) => ( )} ); const provided: Provided = useMemo( (): Provided => ({ innerRef: setDroppableRef, placeholder, droppableProps: { 'data-rbd-droppable-id': droppableId, 'data-rbd-droppable-context-id': contextId, }, }), [contextId, droppableId, placeholder, setDroppableRef], ); const isUsingCloneFor: ?DraggableId = useClone ? useClone.dragging.draggableId : null; const droppableContext: ?DroppableContextValue = useMemo( () => ({ droppableId, type, isUsingCloneFor, }), [droppableId, isUsingCloneFor, type], ); function getClone(): ?Node { if (!useClone) { return null; } const { dragging, render } = useClone; const node: Node = ( {( draggableProvided: DraggableProvided, draggableSnapshot: DraggableStateSnapshot, ) => render(draggableProvided, draggableSnapshot, dragging)} ); return ReactDOM.createPortal(node, getContainerForClone()); } return ( {children(provided, snapshot)} {getClone()} ); }