import type { Position, Spacing, Rect } from 'css-box-model'; import type { DroppableDimension } from '../../types'; import isPartiallyVisibleThroughFrame from './is-partially-visible-through-frame'; import isTotallyVisibleThroughFrame from './is-totally-visible-through-frame'; import isTotallyVisibleThroughFrameOnAxis from './is-totally-visible-through-frame-on-axis'; import { offsetByPosition } from '../spacing'; import { origin } from '../position'; export interface Args { target: Spacing; destination: DroppableDimension; viewport: Rect; withDroppableDisplacement: boolean; shouldCheckDroppable?: boolean; shouldCheckViewport?: boolean; } type IsVisibleThroughFrameFn = ( frame: Spacing, ) => (subject: Spacing) => boolean; interface InternalArgs extends Args { isVisibleThroughFrameFn: IsVisibleThroughFrameFn; } const getDroppableDisplaced = ( target: Spacing, destination: DroppableDimension, ): Spacing => { const displacement: Position = destination.frame ? destination.frame.scroll.diff.displacement : origin; return offsetByPosition(target, displacement); }; const isVisibleInDroppable = ( target: Spacing, destination: DroppableDimension, isVisibleThroughFrameFn: IsVisibleThroughFrameFn, ): boolean => { // destination subject is totally hidden by frame // this should never happen - but just guarding against it if (!destination.subject.active) { return false; } // When considering if the target is visible in the droppable we need // to consider the change in scroll of the droppable. We need to // adjust for the scroll as the clipped viewport takes into account // the scroll of the droppable. return isVisibleThroughFrameFn(destination.subject.active)(target); }; const isVisibleInViewport = ( target: Spacing, viewport: Rect, isVisibleThroughFrameFn: IsVisibleThroughFrameFn, ): boolean => isVisibleThroughFrameFn(viewport)(target); const isVisible = ({ target: toBeDisplaced, destination, viewport, withDroppableDisplacement, isVisibleThroughFrameFn, }: InternalArgs): boolean => { const displacedTarget: Spacing = withDroppableDisplacement ? getDroppableDisplaced(toBeDisplaced, destination) : toBeDisplaced; return ( isVisibleInDroppable( displacedTarget, destination, isVisibleThroughFrameFn, ) && isVisibleInViewport(displacedTarget, viewport, isVisibleThroughFrameFn) ); }; export const isPartiallyVisible = (args: Args): boolean => isVisible({ ...args, isVisibleThroughFrameFn: isPartiallyVisibleThroughFrame, }); export const isTotallyVisible = (args: Args): boolean => isVisible({ ...args, isVisibleThroughFrameFn: isTotallyVisibleThroughFrame, }); export const isTotallyVisibleOnAxis = (args: Args): boolean => isVisible({ ...args, isVisibleThroughFrameFn: isTotallyVisibleThroughFrameOnAxis( args.destination.axis, ), });