mpizenberg / elm-pointer-events / Html.Events.Extra.Drag

HTML5 drag events is a quite complicated specification. Mostly because it is very stateful, and many properties and functions only make sense in one situation and not the rest. For example, the effectAllowed property can only be set successfully in a dragstart event, and setting it in another will be ignored. Another example, the dragend should be attached to the object dragged, while the dragleave should be attached to potential drop target. One more, the dragover event listener is required on a drop target. Otherwise the drop event will be cancelled.

Consequently, I've chosen to present a slightly opinionated API for drag events, mitigating most of potential errors. In case it prevents you from using it, please report your use case in an issue. I hope by also providing the decoders, the library can still help you setup your own event listeners.

There seems to be two main use cases for drag events:

  1. Dropping files from the OS as resources to load.
  2. Drag and dropping DOM elements in page.

The rest of the documentation presents the API with those use cases in mind.

The Event Type


type alias Event =
{ dataTransfer : DataTransfer
, mouseEvent : Html.Events.Extra.Mouse.Event 
}

Type that get returned by a browser drag event. It corresponds to a JavaScript DragEvent.

Since a DragEvent inherits from MouseEvent, all mouse event related properties are provided in the mouseEvent attribute of type Mouse.Event. Please refer to the Mouse module for more information on this value.


type alias DataTransfer =
{ files : List File
, types : List String
, dropEffect : String 
}

Hold the data being dragged during a drag and drop operation. This corresponds to JavaScript DataTransfer.

The files attribute holds the list of files being dragged. Each file is of the type File provided by elm/file.

The types attribute contains a list of strings providing the different formats of objects being dragged.

The dropEffect attribute provides feedback on the selected effect for the current drag and drop operation. It can be one of:

Beware that contrary to JavaScript, you have no way of modifying dropEffect in elm. This is provided purely for information as read only, like any other elm value.

The effectAllowed property is not provided since it has no use in the context of elm.

The items property is not provided by lack of compatibility.

File Dropping

onFileFromOS : FileDropConfig msg -> List (Html.Attribute msg)

Events listeners for a file drop target element.

PS: incompatible with onDropTarget since both functions use the same events listeners. If you need to have a drop target working for both files and DOM elements, you can directly use onDropTarget.


type alias FileDropConfig msg =
{ onOver : Event -> msg
, onDrop : Event -> msg
, onEnter : Maybe (Event -> msg)
, onLeave : Maybe (Event -> msg) 
}

Configuration of a file drop target.

PS: dragenter and dragleave are kind of inconsistent since they bubble up from children items (not consistently depending on borders in addition). You should prefer to let them be Nothing, or to add the CSS property pointer-events: none to all children.

Drag and Drop

I encourage you to read this blog post before you take the decision to use HTML5 drag and drop API instead of your own custom solution.

Managing the Dragged Item

onSourceDrag : DraggedSourceConfig msg -> List (Html.Attribute msg)

Drag events listeners for the source dragged element.


type alias DraggedSourceConfig msg =
{ effectAllowed : EffectAllowed
, onStart : EffectAllowed -> Json.Decode.Value -> msg
, onEnd : Event -> msg
, onDrag : Maybe (Event -> msg) 
}

Configuration of a draggable element. You should provide message taggers for dragstart and dragend events. You can (but it is more compute-intensive) provide a message tagger for drag events.


type alias EffectAllowed =
{ move : Basics.Bool
, copy : Basics.Bool
, link : Basics.Bool 
}

Drop effects allowed for this draggable element. Set to True all effects allowed. This is used in the port of the dragstart event.

startPortData : EffectAllowed -> Json.Decode.Value -> { effectAllowed : String, event : Json.Decode.Value }

Put the effect allowed and the dragstart event in a data format that can be sent through port.

effectAllowedToString : EffectAllowed -> String

Convert EffectAllowed into its String equivalent.

Managing a Drop Target

onDropTarget : DropTargetConfig msg -> List (Html.Attribute msg)

Drag events listeners for the drop target element.

PS: dragenter and dragleave are kind of inconsistent since they bubble up from children items (not consistently depending on borders in addition). You should prefer to let them be Nothing, or to add the CSS property pointer-events: none to all children.


type alias DropTargetConfig msg =
{ dropEffect : DropEffect
, onOver : DropEffect -> Json.Decode.Value -> msg
, onDrop : Event -> msg
, onEnter : Maybe (Event -> msg)
, onLeave : Maybe (Event -> msg) 
}

Configuration of a drop target. You should provide message taggers for dragover and drop events. You can also provide message taggers for dragenter and dragleave events.


type DropEffect
    = NoDropEffect
    | MoveOnDrop
    | CopyOnDrop
    | LinkOnDrop

Drop effect as configured by the drop target. This will change the visual aspect of the mouse icon.

If the drop target sets (via port on dragover) a drop effect incompatible with the effects allowed for the dragged item, the drop will not happen.

overPortData : DropEffect -> Json.Decode.Value -> { dropEffect : String, event : Json.Decode.Value }

Put the drop effect and the dragover event in a data format that can be sent through port.

dropEffectToString : DropEffect -> String

Convert a DropEffect into its string equivalent.

Decoders for Advanced Usage

eventDecoder : Json.Decode.Decoder Event

Drag.Event default decoder. It is provided in case you would like to reuse/extend it.

dataTransferDecoder : Json.Decode.Decoder DataTransfer

DataTransfer decoder. It is provided in case you would like to reuse/extend it.

fileListDecoder : Json.Decode.Decoder a -> Json.Decode.Decoder (List a)

Transform a personalized File decoder into a List File decoder since Json.Decode.list does not work for the list of files.