atlassian / pragmatic-drag-and-drop

Fast drag and drop for any experience on any tech stack
https://atlassian.design/components/pragmatic-drag-and-drop
Other
7.45k stars 133 forks source link

Out of view components are cropped when dragged #25

Closed hazirmagron closed 1 month ago

hazirmagron commented 1 month ago

When dragging a component that is out of view (see left cards in image below) the dragged card is cropped and only displays what was shown in the view before. My board roughly follows the example board.

Screenshot 2024-04-06 at 15 20 13

The board in the examples does not have this issue, so I am missing something. Since my board doesn't follow the example strictly (I don't use Atlassian dependencies and persist the board in a DB) it is difficult to troubleshoot though. I've looked through the docs but can't find what is responsible to track the component UI when it is outside of the view.

alexreardon commented 1 month ago

Hi @hazirmagron,

You have two options:

  1. Use a custom native drag preview (recommended)
  2. scroll the element into view in onGenerateDragPreview (use scrollJustEnoughIntoView())
alexreardon commented 1 month ago

Let me know how you go!

hazirmagron commented 1 month ago

Thanks for the response @alexreardon.

I am using the preserveOffsetOnSource - replicating the example board but the preview position is {x: 0, y: 0}, essentially always selecting the preview card's top left corner.

My onGenerateDragPreview function below which is identical to the example board. The element variable is const element = ref.current

onGenerateDragPreview: ({ location, source, nativeSetDragImage }) => {
      const rect = source.element.getBoundingClientRect()
      setCustomNativeDragPreview({
        nativeSetDragImage,
        getOffset: preserveOffsetOnSource({
          element,
          input: location.current.input,
        }),
        render({ container }) {
          setState({ type: "preview", container, rect })
          return () => setState(draggingState)
        },
    })
}

My render function.

<>
      <CardPrimitive closestEdge={closestEdge} task={task} state={state} ref={ref} />
      {state.type === "preview" &&
        createPortal(
          <div
            style={{
              /**
               * Ensuring the preview has the same dimensions as the original.
               *
               * Using `border-box` sizing here is not necessary in this
               * specific example, but it is safer to include generally.
               */
              boxSizing: "border-box",
              width: state.rect.width,
              height: state.rect.height,
            }}
          >
            <CardPrimitive closestEdge={null} task={task} state={state} />
          </div>,
          state.container
      )}
</>

The DOCS provides the source element instead of reference, which also didn't work for me:

getOffset: preserveOffsetOnSource({
        element: source.element,
        input: location.current.input,
}),

I ultimately solved it through the below function, but a little feedback on whether I've misunderstood the preserveOffsetOnSource would be great:

getOffset: () => {
              const { clientX, clientY } = location.current.input
              const { x, y } = rect

              return {
                x: clientX - x,
                y: clientY - y,
              }
}