import ReactDOM from 'react-dom'; import { useMemo, useCallback } from 'use-memo-one'; import React, { useRef, useContext, FunctionComponent } from 'react'; import type { ReactNode } from 'react'; import { invariant } from '../../invariant'; import type { DraggableId } from '../../types'; import type { Props, DroppableProvided } from './droppable-types'; import useDroppablePublisher from '../use-droppable-publisher'; import Placeholder from '../placeholder'; import AppContext from '../context/app-context'; import type { AppContextValue } from '../context/app-context'; import DroppableContext from '../context/droppable-context'; import 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 { DraggableStateSnapshot, DraggableProvided, } from '../draggable/draggable-types'; import AnimateInOut from '../animate-in-out/animate-in-out'; import type { AnimateProvided } from '../animate-in-out/animate-in-out'; import { PrivateDraggable } from '../draggable/draggable-api'; const Droppable: FunctionComponent = (props) => { const appContext: AppContextValue | null = 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 | null => droppableRef.current, [], ); const setDroppableRef = useCallback((value: HTMLElement | null = null) => { droppableRef.current = value; }, []); const getPlaceholderRef = useCallback( (): HTMLElement | null => placeholderRef.current, [], ); const setPlaceholderRef = useCallback((value: HTMLElement | null = null) => { 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: ReactNode = useMemo( () => ( {({ onClose, data, animate }: AnimateProvided) => ( )} ), [ contextId, onPlaceholderTransitionEnd, props.placeholder, props.shouldAnimatePlaceholder, setPlaceholderRef, ], ); const provided: DroppableProvided = useMemo( (): DroppableProvided => ({ innerRef: setDroppableRef, placeholder, droppableProps: { 'data-rfd-droppable-id': droppableId, 'data-rfd-droppable-context-id': contextId, }, }), [contextId, droppableId, placeholder, setDroppableRef], ); const isUsingCloneFor: DraggableId | null = useClone ? useClone.dragging.draggableId : null; const droppableContext: DroppableContextValue | null = useMemo( () => ({ droppableId, type, isUsingCloneFor, }), [droppableId, isUsingCloneFor, type], ); function getClone(): ReactNode | null { if (!useClone) { return null; } const { dragging, render } = useClone; const node: ReactNode = ( {( draggableProvided: DraggableProvided, draggableSnapshot: DraggableStateSnapshot, ) => render(draggableProvided, draggableSnapshot, dragging)} ); return ReactDOM.createPortal(node, getContainerForClone()); } return ( {children(provided, snapshot)} {getClone()} ); }; export default Droppable;