lukasbach / react-complex-tree

Unopinionated Accessible Tree Component with Multi-Select and Drag-And-Drop
https://rct.lukasbach.com
MIT License
982 stars 82 forks source link

Ability to set the drag feedback image #135

Closed tonyketcham closed 1 year ago

tonyketcham commented 2 years ago

Is your feature request related to a problem? Please describe.

When I attempt to set a custom drag feedback image via something like...

// Item.tsx

const generateDragGhostStyle = (event: React.DragEvent) => {
  event.dataTransfer.setDragImage(new Image(), 0, 0);
};

...

<button
  {...context.interactiveElementProps}
  onDragStart={generateDragGhostStyle}
  >
  ...
</button>

It completely messes up the drop position detection and scrunches it to a region at the very top of the tree. It seems an offset is calculated internally that my above code snippet overrides.

Describe the solution you'd like

I'd love to be able to provide a renderDragImage prop to the tree environment which corresponds to setDragImage's imgElement argument.

tonyketcham commented 1 year ago

This can be accomplished by extending the InteractionManager on the Controlled/UncontrolledTreeEnvironment:

<ControlledTreeEnvironment
  ...
  defaultInteractionMode={{
    mode: 'custom',
    extends: InteractionMode.ClickArrowToExpand,
    createInteractiveElementProps: (item, treeId, actions, renderFlags) => ({
      onDragStart: (e) => {
        e.dataTransfer.setDragImage(blankImage, 1, 1);
        actions.startDragging();
      },
    }),
  }}
/>

The static blankImage referenced above can be defined at module level outside the component and function scope so that it exists on mount of the tree. This avoids an undesirable default behavior of the browser falling back to the broken image link icon, which can occur if the image does not exist by the time the dragStart event fires.

// creates a blank 1x1 svg image
const blankImage = new Image(1, 1);
blankImage.src = 'data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22/%3E';
tonyketcham commented 1 year ago

You can even get creative using the above pattern to reference off-screen React component elements, as long as they already exist during onDragStart 😉