prevwong / craft.js

🚀 A React Framework for building extensible drag and drop page editors
https://craft.js.org
MIT License
7.32k stars 705 forks source link

Simplified custom event handling #659

Open jjoderis opened 2 weeks ago

jjoderis commented 2 weeks ago

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

In my project i want to customize the logic that runs when an element is dragged or created. I try to remove the indicator that is currently updated when dragging an element and instead automatically place/move the element to the newly calculated position.

At the same time i want to enforce a layout where elements are contained in a column, mutiple columns can be grouped in a row and multiple rows can be grouped in a container which then are again wrapped in a column. For this i need to check if the dragover event currently targets a container or a row when dragging a column containing an element and either create a new row when a container is targeted or just drop the element at the correct position in the targeted row.

From what i have gathered from the documentation the way to do this currently would be using custom event handlers. Experimenting with that i have found that it is possible to achieve what i want to do but it is somewhat involved since it's not always clear when for example different handlers that register eventListeners are run. At the same time it seems to be hard (or impossible) to for example keep the default logic for dragstart events while replacing the logic for dragend events.

Describe the solution you'd like

I think it would be nice to have a system where you are able to easily register handlers for specific events that are triggered while interacting with the editor. Maybe it could look something like this:

<Editor
      customEventHandling: {
          onCreate: (nodeTree, targetParentNodeId, defaultHandler, ...) => {
              // e.g: create a new row if the target is a container and place it at the correct position in said container
              defaultHandler(nodeTree, newRowId or targetParentNodeId, ...)
          },
          onMove: (draggedNode, targetParentNodeId, defaultHandler, ...) => {
              // e.g: create a new row if the target is a container and place it at the correct position in said container
              defaultHandler(draggedNode, newRowId or targetParentNodeId, ...)
              // remove the previous parent row if it is empty after the element is moved
          },
          onDelete: (targetNodeId, defaultHandler, ...) => {
              // get the id of the parent row
             defaultHandler(targetNodeId, ...)
             // remove the parent row if it is empty after the element is removed
          },
          onIndicatorUpdate(newIndicator, defaultHandler, ...) =>  {
              // move the dragged element or create an element at the correct position with information from the indicator
              // don't run the default logic that would display the indicator in the editor
          }
          onDrop(..., defaultHandler, ...) {
              // do nothing since everything already happened while dragging
          } 
          ...
      }
    >
      ...
    </Editor>

Additional context

I have created an incomplete codesandbox example that i have already posted in a different issue where someone had a use case similar to mine.

If this is considered to be a valid/useful change i would be open to try and open a pull request for it but i wanted to make sure that this idea actually aligns with the desired API for this framework.