Documentation
API: primitives (@fragiola/dockable-react)

Dockable.DropZone

A place, inside or outside the layout, where a drag of the layout can be dropped for you to handle with a command.

"A place, inside or outside the layout, where a drag of the layout can be dropped for you to handle: a trash can that closes the tab, an "open to the right" pad, a region of the page. While a drag it takes is over it, the layout shows no outline, and a drop calls onDrop with what is dragged instead of moving it."

<Dockable.DropZone
    model={model}
    accepts={(drag) => drag.kind === "tab" && model.can("tab.close", { tabId: drag.tab.id })}
    onDrop={(drag) => {
        if (drag.kind === "tab") model.run("tab.close", { tabId: drag.tab.id });
    }}
    className="opacity-50 data-drop-active:opacity-100 data-drop-over:bg-red-600"
>
    Trash
</Dockable.DropZone>

Nothing moves by itself: onDrop receives what is dragged, and the zone runs the command it stands for. model.run goes through the model's middleware, like every command of the layout.

What is dragged

accepts and onDrop receive a DragSubject<T>, a union discriminated by kind:

kindfieldswhat is dragged
"tab"tab: TabOf<T>a tab of the layout
"tabset"tabset: TabsetNode<T>a whole tabset of the layout
"new"tab: TabInitOf<T>a new tab that is not in the model yet: a Dockable.DragSource, or a foreign drag accepted by onExternalDrag

T is inferred from model: Model<T>, so drag.tab.data narrows on drag.tab.component.

Props

DropZoneProps<T> extends the common primitive props (its onDrop replaces the element's native drop handler).

proptypedefaultdescription
modelModel<T>requiredthe model whose drags the zone takes
accepts(drag: DragSubject<T>) => booleanevery drag of the modelwhether the zone takes this drag
onDrop(drag: DragSubject<T>, event: DragEventLike) => voidrequiredcalled when the drag is dropped on the zone, with what is dragged ({ kind: "tab", tab }, { kind: "tabset", tabset }, { kind: "new", tab }). Nothing is moved: run the command you want (tab.close)
childrenReactNodenonethe zone's content; the primitive renders no text of its own

State

DropZoneState:

fieldtypedescription
overbooleana drag the zone takes is over it
activebooleana drag the zone would take is in progress

Data attributes

attributevalue
data-layout-pathnone: a drop zone is not part of a layout, so it has no layout path (select it by your own data-testid or role)
data-drop-activea drag the zone would take is in progress (show the zone, arm it)
data-drop-overthat drag is over the zone

Notes

  • A zone can sit anywhere in the page's document: beside the layout, above it, or inside Dockable.Root as an overlay. It does not work across windows (a zone in the main document does not take a drag inside a popout).
  • A zone for a new-tab drag can create the tab itself: model.run("tab.add", { ...drag.tab, to }).
  • useDropZone({ model, accepts, onDrop }) is the lower layer (hooks), and the core's DragDropManager.registerDropZone(model, element, options) (DropZoneOptions<T>: accepts, onDrop, onOverChange) the one under it, on the adapter's side, for other frameworks.