@hello-pangea/dnd
Version:
Beautiful and accessible drag and drop for lists with React
101 lines (86 loc) • 2.93 kB
text/typescript
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,
),
});