// Adapted from https://github.com/alexreardon/pdnd-react-tailwind/blob/main/src/drop-indicator.tsx import type { Edge } from '@atlaskit/pragmatic-drag-and-drop-hitbox/types'; import type { SystemStyleObject } from '@invoke-ai/ui-library'; import { Box } from '@invoke-ai/ui-library'; import type { DndListTargetState } from 'features/dnd/types'; /** * Design decisions for the drop indicator's main line */ const line = { thickness: 2, backgroundColor: 'base.500', }; type DropIndicatorProps = { /** * The `edge` to draw a drop indicator on. * * `edge` is required as for the best possible performance * outcome you should only render this component when it needs to do something * * @example {closestEdge && } */ edge: Edge; /** * `gap` allows you to position the drop indicator further away from the drop target. * `gap` should be the distance between your drop targets * a drop indicator will be rendered halfway between the drop targets * (the drop indicator will be offset by half of the `gap`) * * `gap` should be a valid CSS length. * @example "8px" * @example "var(--gap)" */ gap?: string; }; const lineStyles: SystemStyleObject = { display: 'block', position: 'absolute', zIndex: 1, borderRadius: 'full', // Blocking pointer events to prevent the line from triggering drag events // Dragging over the line should count as dragging over the element behind it pointerEvents: 'none', background: line.backgroundColor, }; type Orientation = 'horizontal' | 'vertical'; const orientationStyles: Record = { horizontal: { height: `${line.thickness}px`, left: 2, right: 2, }, vertical: { width: `${line.thickness}px`, top: 2, bottom: 2, }, }; const edgeToOrientationMap: Record = { top: 'horizontal', bottom: 'horizontal', left: 'vertical', right: 'vertical', }; const edgeStyles: Record = { top: { top: 'var(--local-line-offset)', }, right: { right: 'var(--local-line-offset)', }, bottom: { bottom: 'var(--local-line-offset)', }, left: { left: 'var(--local-line-offset)', }, }; /** * __Drop indicator__ * * A drop indicator is used to communicate the intended resting place of the draggable item. The orientation of the drop indicator should always match the direction of the content flow. */ function DndDropIndicatorInternal({ edge, gap = '0px' }: DropIndicatorProps) { /** * To clearly communicate the resting place of a draggable item during a drag operation, * the drop indicator should be positioned half way between draggable items. */ const lineOffset = `calc(-0.5 * (${gap} + ${line.thickness}px))`; const orientation = edgeToOrientationMap[edge]; return ( ); } export const DndListDropIndicator = ({ dndState }: { dndState: DndListTargetState }) => { if (dndState.type !== 'is-dragging-over') { return null; } if (!dndState.closestEdge) { return null; } return ( ); };