Skip to content

rozek/svelte-drag-and-drop-actions

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

svelte-drag-and-drop-actions

a lightweight but flexible HTML5 Drag-and-Drop implemented as Svelte actions

some of its Features:

All examples are live and may be changed on the fly!

  • HTML5 drag-and-drop features remain available (see example)
  • works on mobile devices (when combined with svelte-drag-drop-touch)
  • supports custom drag images (from existing DOM elements or from HTML markup)
  • drag images may even be dynamically created (see example)
  • supports simple dragging (i.e., without dropping, see example, don't worry if the example seems to lag - it's just because of the amount of console output)
  • provides mechanisms to reduce the code needed to implement element dragging (see example)
  • can restrict dragging to a given region (see example)
  • supports automatic scrolling (aka "panning") of partially visible elements while s.th. is dragged over them (see example), works fine even on Chrome for desktop (which already provides panning out of the box)
  • can mimic real dragging of an element (and not just of a "ghost image" while the original stands still, see many examples, f.e. this one)
  • can suppress the drag image at all (offering a bunch of new possibilites using the same code base, see many of the examples, f.e. this one)
  • supports dragging elements from a given handle only (instead of the whole element itself, see example)
  • offers visual feedback of dragged elements and drop zones by simple styling (using dynamically assigned CSS classes, see several examples, such as this one)
  • recognizes when a draggable stands still (i.e., it's "held") over a drop zone for a given time (see example)
  • provides lots of live(!) examples for many use cases
  • however, unfortunately, svelte-drag-and-drop-actions may also suffer from any bugs in the browser implementations of native HTML drag-and-drop (thinking of Safari 13.0/13.1, f.e.) if they can not be compensated by the author

HTML5 Drag-and-Drop allows web applications to provide a visual user interface for transfering data between HTML elements or exchanging data with a browser's environment. Using HTML5 Drag-and-Drop for the operation of an application (i.e. the repositioning, resizing or reordering of elements) is difficult and suffers from some browser-specific bugs.

Instead of fully re-implementing the visual operation of web applications with mouse and touch gestures (as done in agnostic-draggable or svelte-dnd-action), svelte-drag-and-drop-actions builds upon already existing HTML5 Drag-and-Drop functionality and simply extends it. The result is a lightweight package with a surprisingly simple programming interface. (And because this module is tree-shakable, using the plain dragging functionality only - i.e., without support for dropping - will reduce the import size even further)

A first Svelte component based on svelte-drag-and-drop-actions is the svelte-sortable-flat-list-view.

But try yourself: there are a number of examples that can be tried out live

NPM users: please consider the Github README for the latest description of this package (as updating the docs would otherwise always require a new NPM package version)

Mobile Developers: since many mobile platforms lack support for native HTML5 drag-and-drop, you should consider importing svelte-drag-drop-touch as a polyfill (a simple import of that package will suffice - there is no extra programming needed)

Firefox Users: because of an old but still unfixed bug in Mozilla Firefox, svelte-drag-and-drop-actions do not work properly in Firefox browsers. Right now, I have no idea how to work around that bug without affecting other browsers and in a way that survives the moment when Mozilla finally fixes that bug...

Just a small note: if you like this module and plan to use it, consider "starring" this repository (you will find the "Star" button on the top right of this page), so that I know which of my repositories to take most care of.

Installation

npm install svelte-drag-and-drop-actions

Usage Example

The following example illustrates plain dragging of a "Draggable" within the bounds of a given "Arena". Read on to understand how it is working.

<script context="module">
  import  DragDropTouch  from 'svelte-drag-drop-touch' // for mobile platforms
  import { asDraggable } from 'svelte-drag-and-drop-actions'
</script>

<script>
  let DraggableX = 20, DraggableY = 20, DraggableWidth = 80, DraggableHeight = 30
  let ArenaWidth = 400, ArenaHeight = 400

  function onDragMove (x,y, dx,dy) { DraggableX = x; DraggableY = y }
  function onDragEnd  (x,y, dx,dy) { DraggableX = x; DraggableY = y }
</script>

<div style="
  display:block; position:relative;
  width:{ArenaWidth}px; height:{ArenaHeight}px; margin:20px;
  border:dotted 1px black; border-radius:4px;
">
  <div style="
    display:block; position:absolute;
    left:{DraggableX}px; top:{DraggableY}px; width:{DraggableWidth}px; height:{DraggableHeight}px;
    background:forestgreen; color:white; line-height:30px; text-align:center; cursor:move;
  " use:asDraggable={{
    minX:0,minY:0, maxX:ArenaWidth-DraggableWidth,maxY:ArenaHeight-DraggableHeight,
    onDragStart:{x:DraggableX,y:DraggableY}, onDragMove, onDragEnd
  }}
  >Drag me!</div>
</div>

Additional, more detailled examples can be found below.

Functional Principle

Svelte is a Framework for building reactive applications, i.e. it assumes, that there is some application state which is used to construct the application's user interface - and whenever any part of this state changes, the corresponding interface elements are updated accordingly.

svelte-drag-and-drop-actions takes this mechanism into account and assumes that position and size of draggable elements are also part of the application's state. For that reason, the module does not perform any dragging, resizing, sorting (or similar) itself but only provides the coordinates and dimensions needed to update state and - in succession - the visual appearance of any affected elements.

Combined with a rather declarative API (designed with the most common use cases in mind), such an approach leads to a lightweight and easily usable module which still offers programmers full control over the actual side-effects of dragging (and dropping)

If this sounds too abstract, just have a look at the examples: many of them illustrate specific use cases and may therefore serve as a basis for your own development.

Nota bene: since it is based on native HTML5 drag-and-drop, svelte-drag-and-drop-actions principally also allows dragging and dropping between multiple windows (or tabs) of the same browser or even between browsers. To what extent this works, depends on the participating browsers, the data types involved (transferring text often works best), and may also depend on the operating system used.

Exported Types

TypeScript programmers may import the following types in order to benefit from static type checking (JavaScript programmers may simply skip this section):

  • type Position = { x:number, y:number }
    a Position instance represents a single point in a linearly scaled cartesic coordinate system. It may be considered as the same coordinate system a browser uses when coordinates are measured in pixels (px) - with one important exception: the origin of this system can be chosen by the programmer. An intelligent choice of such an origin may simplify the callbacks provided for svelte-drag-and-drop-actions and allow to use delivered coordinates, f.e., to set the size of an element directly and without any need for additional calculations.
  • type PositionReference = (
      'parent' | 'body' | string | HTMLElement | SVGElement // | MathMLElement
    )
    a PositionReference specifies, relative to which element the mouse or touch position is measured. This decision becomes important, if the referred element is scrollable and/or its position or size may be changed by external code (which is neither part of this module nor any of the configured callbacks)
  • type DragDummy = (
      string | HTMLElement | SVGElement | 'standard' | 'none' |
      ((DraggableExtras:any, Element:HTMLElement | SVGElement) => HTMLElement | SVGElement)
    )
    a DragDummy specifies which drag image will be shown during dragging (see below for details)
  • type DraggableOptions = {
      Extras?:any, relativeTo?:PositionReference, onlyFrom?:string, neverFrom?:string,
      Dummy?:DragDummy, DummyOffsetX?:number, DummyOffsetY?:number,
      minX?:number, minY?:number, maxX?:number, maxY?:number,
      Pannable?:string|HTMLElement|SVGElement,
      PanSensorWidth?:number, PanSensorHeight?:number, PanSpeed?:number,
      onDragStart?:Position | ((DraggableExtras:any) => Position),
      onDragMove?:(x:number,y:number, dx:number,dy:number, DraggableExtras:any) => void,
      onDragEnd?: (x:number,y:number, dx:number,dy:number, DraggableExtras:any) => void,
    }
    a DraggableOptions instance holds all options for a "Draggable" (see below for details)
     
  • type DropOperation = 'copy'|'move'|'link'
    a DropOperation specifies whether the data associated with a droppable element should be copied, moved or linked to a drop target (native HTML5 drag-and-drop jargon calls this an "effect")
  • type DataOfferSet = { [Type:string]:string }
    DataOfferSet instances are dictionaries listing the various data formats offered by a droppable object and the actually offered data for every format. The keys into a DataOfferSet are often MIME types (or the special value "DownloadURL") but could well be any kind of string (except none) - a detail which is often used to overcome some limitations of native HTML 5 drag-and-drop
  • type DroppableOptions = DraggableOptions & {
      Extras?:any, Operations?:string, DataToOffer?:DataOfferSet,
      onDropZoneEnter?:(x:number,y:number, DropZoneExtras:any, DroppableExtras:any) => void,
      onDropZoneHover?:(x:number,y:number, DropZoneExtras:any, DroppableExtras:any) => void,
      onDropZoneLeave?:(DropZoneExtras:any, DroppableExtras:any) => void,
      onDropped?: (x:number,y:number, Operation:DropOperation,
          TypeTransferred:string, DataTransferred:any, DropZoneExtras:any, DroppableExtras:any) => void
      }
    a DroppableOptions instance holds all extra options for a "Droppable" (please note, that this includes all DraggableOptions shown above, see below for details)
     
  • type TypeAcceptanceSet = { [Type:string]:string }
    TypeAcceptanceSet instances are dictionaries listing the various data formats accepted by a drop zone and the permitted drop operations for every format. The keys into a TypeAcceptanceSet are the same as for the abovementioned DataOfferSets
  • type DropZoneOptions = {
      Extras?:any, TypesToAccept?:TypeAcceptanceSet, HoldDelay?:number,
      Pannable?:string|'this'|HTMLElement|SVGElement,
      PanSensorWidth?:number, PanSensorHeight?:number, PanSpeed?:number,
      onDroppableEnter?: (x:number,y:number, Operation:DropOperation,
          offeredTypeList:string[], DroppableExtras:any, DropZoneExtras:any) => boolean|undefined,
      onDroppableMove?: (x:number,y:number, Operation:DropOperation,
          offeredTypeList:string[], DroppableExtras:any, DropZoneExtras:any) => boolean|undefined,
      onDroppableHold?: (x:number,y:number, DroppableExtras:any, DropZoneExtras:any) => void,
      onDroppableLeave?: (DroppableExtras:any, DropZoneExtras:any) => void,
      onDrop?: (x:number,y:number, Operation:DropOperation,
          DataOffered:any, DroppableExtras:any, DropZoneExtras:any) => string,
    }
    a DropZoneOptions instance holds all options for a drop target (see below for details)

use:asDraggable

use:asDraggable should be used for elements which will only be dragged around (but never dropped onto another element). Many use cases (from draggable windows over draggable nodes of graphical shapes to the resize handles found in many visual designers) only need this kind of behaviour.

The type annotiations shown below are relevant for TypeScript programmers only, JavaScript programmers may simply ignore them.

use:asDraggable={options}

is the classical pattern for Svelte actions. use:asDraggable supports the following options (bundled into an instance of type DraggableOptions):

  • Extras?:any
    Extras are an optional, user-defined value which can be used during drag-and-drop operations within the same application to identify the draggable component. They become useful as soon as multiple draggables share the same callbacks
     
  • relativeTo?:PositionReference
    relativeTo is an optional PositionReference (defaulting to parent) which specifies relative to which element the current mouse or touch position is measured during dragging. It may be set to body for measurements relative to the current document body, to parent for measurements relative to the draggable's containing element, to a CSS selector for measurements relative to the Draggable's closest element matching the given selector (or 'body' otherwise) or to a given HTML or SVG element (which must be part of the document)
  • onlyFrom?:string
    onlyFrom is an optional, comma-separated list of CSS selectors identifying the inner elements of a draggable element, from which a drag operation must be started in order to be allowed. If onlyFrom is missing, no onlyFrom restriction is applied
  • neverFrom?:string
    neverFrom is an optional, comma-separated list of CSS selectors identifying the inner elements of a draggable element, from which a drag operation must never be started in order to be allowed. If neverFrom is missing, no neverFrom restriction is applied
     
  • Dummy?:DragDummy
    Dummy specifies which "drag image" to show at the current mouse or touch position during dragging. Dummy is optional (defaulting to none) and may be set to standard (for the HTML5 standard behaviour which usually shows a semi-transparent copy of the actual draggable), to none (effectively showing nothing), to some HTML code (which is used to construct the element to be shown during dragging), to a function which receives the draggable's configured extras and the element to be dragged as its arguments and returns an HTML element to be used as the drag dummy or to an already existing HTML element (which must be visible). Important: creating a drag image from HTML or from a function is quite tricky - the approach used here is lightweight but may cause some flickering when dragging is started. Such flickering can usually be avoided by setting the CSS "overflow" of the document body to "hidden". Where this is not possible, constructing an image from HTML using html2canvas may be an (albeit no longer lightweight) alternative. If a function is used to construct the dummy, that function should also care itself about removing the newly created HTML element after use (see below for a suitable example)
  • DummyOffsetX?:number
    DummyOffsetX is an optional number (defaulting to 0) specifying the horizontal offset between the current x position of a mouse or finger during dragging and the shown drag image. It is used ay the second argument to DataTransfer.setDragImage without prior change
  • DummyOffsetY?:number
    DummyOffsetY is an optional number (defaulting to 0) specifying the vertical offset between the current y position of a mouse or finger during dragging and the shown drag image. It is used ay the third argument to DataTransfer.setDragImage without prior change
     
  • minX?:number
    minX is an optional number (defaulting to -Infinity) specifying the smallest possible value for the x component of any drag result. Please note: a configured drag image may well be dragged beyond configured limits - for that reason, such limits are most often combined with Dummy:'none' to effectively hide the drag image
  • minY?:number
    minY is an optional number (defaulting to -Infinity) specifying the smallest possible value for the y component of any drag result. Please note: a configured drag image may well be dragged beyond configured limits - for that reason, such limits are most often combined with Dummy:'none' to effectively hide the drag image
  • maxX?:number
    maxX is an optional number (defaulting to Infinity) specifying the largest possible value for the x component of any drag result. Please note: a configured drag image may well be dragged beyond configured limits - for that reason, such limits are most often combined with Dummy:'none' tto effectively hide the drag image
  • maxY?:number
    maxY is an optional number (defaulting to Infinity) specifying the largest possible value for the y component of any drag result. Please note: a configured drag image may well be dragged beyond configured limits - for that reason, such limits are most often combined with Dummy:'none' to effectively hide the drag image
     
  • Pannable?:string|HTMLElement|SVGElement
    some browsers provide automatic scrolling of only partially visible elements while a Draggable is dragged over them - but most do not, which is why svelte-drag-and-drop-actions implements its own "panning". Pannable is an optional parameter specifying the element which should be scrolled automatically. It should be a (not necessarily immediate) container of the Draggable and may be set to the DOM element itself or a CSS selector which identifies the desired container. If omitted, no panning will be performed.
  • PanSensorWidth?:number
    PanSensorWidth is an optional ordinal number (defaulting to 20) which specifies the width (in pixels) of the horizontal pan sensor area: panning starts as soon as the mouse pointer (or finger) gets closer than the given number of pixels to the left or right border of the configured Pannable. If set to 0, no horizontal panning will be performed
  • PanSensorHeight?:number
    PanSensorHeight is an optional ordinal number (defaulting to 20) which specifies the height (in pixels) of the vertical pan sensor area: panning starts as soon as the mouse pointer (or finger) gets closer than the given number of pixels to the upper or lower border of the configured Pannable. If set to 0, no vertical panning will be performed
  • PanSpeed?:number
    PanSpeed is an optional ordinal number (defaulting to 10) which specifies the "speed" of automatic scrolling - values in the range of 10...20 are reasonable choices, but it is always a good idea to make this parameter configurable for the users of your application. If set to 0, no panning will be performed
     
  • onDragStart?:Position | ((DraggableExtras:any) => Position)
    onDragStart is either an optional Position (defaulting to {x:0, y:0}) or a function returning such a Position. When invoked, that function receives any Extras configured for the affected draggable. In either case, this parameter specifies the coordinate values from which to start dragging. Important: these "coordinates" do not necessarily represent a particular point on the screen - in fact, e.g., a resize handle may choose to start with the current width and height of the element to be resized and then directly use the results of any onDragMove or onDragEnd as the new element width and height without additional computation
  • onDragMove?: (x:number,y:number, dx:number,dy:number, DraggableExtras:any) => void
    onDragMove is an optional callback which (if given) is called repeatedly during dragging. When invoked, it receives the current drag result (in x and y, with an initial value chosen by onDragStart), the offset between this and the previous invocation (in dx and dy) and any Extras configured for the affected draggable
  • onDragEnd?: (x:number,y:number, dx:number,dy:number, DraggableExtras:any) => void
    onDragEnd is is an optional callback which (if given) is called once when dragging has finished. When invoked, it receives the final drag result (in x and y, with the origin chosen by onDragStart), the offset between this and the most recent invocation of onDragMove (in dx and dy) and any Extras configured for the affected draggable

While being dragged, the CSS class dragged is assigned to the draggable element (not the drag image!). It will be removed again as soon as dragging has ended.

use:asDraggable should never be combined with use:asDroppable (as the latter already includes the former) - if you want an element to be dropped somewhere, simply use use:asDroppable alone.

use:asDraggable may, however, be combined with use:asDropZone in order to implement draggable drop zones.

use:asDroppable

use:asDroppable is an extension of use:asDraggable and should be used for elements which will not only be dragged around but also dropped onto another element. Because of the underlying HTML5 drag-and-drop, dropping an element onto another one may lead to an exchange of data - but svelte-drag-and-drop-actions extends that functionality (for elements within the same application) and gives the programmer full control over what a "drop" will trigger (you could even delegate the actual action to take after dropping to the droppable, which sometimes turns out to be easier than the "classical" approach where the drop target is responsible)

Warning: some platforms show proper feedback while dragging a droppable over a drop target only for copy as configured drop operation.

The type annotiations shown below are relevant for TypeScript programmers only, JavaScript programmers may simply ignore them.

use:asDroppable={options}

is the classical pattern for Svelte actions. use:asDroppable supports the following options (some of them being equal or, at least, similar to those listed under use:asDraggable):

  • Extras?:any
    Extras are an optional, user-defined value which can be used during drag-and-drop operations within the same application to identify the droppable component or its associated data. They become useful as soon as multiple droppables share the same callbacks
     
  • relativeTo?:PositionReference
    relativeTo is an optional PositionReference (defaulting to parent) which specifies relative to which element the current mouse or touch position is measured during dragging. It may be set to body for measurements relative to the current document body, to parent for measurements relative to the draggable's containing element, to a CSS selector for measurements relative to the Draggable's closest element matching the given selector (or 'body' otherwise) or to a given HTML or SVG element (which must be part of the document)
  • onlyFrom?:string
    onlyFrom is an optional, comma-separated list of CSS selectors identifying the inner elements of a droppable element, from which a drag-and-drop operation must be started in order to be allowed. If onlyFrom is missing, no onlyFrom restriction is applied
  • neverFrom?:string
    neverFrom is an optional, comma-separated list of CSS selectors identifying the inner elements of a droppable element, from which a drag-and-drop operation must never be started in order to be allowed. If neverFrom is missing, no neverFrom restriction is applied
     
  • Dummy?:DragDummy
    Dummy specifies which "drag image" to show at the current mouse or touch position during dragging. Dummy is optional (defaulting to standard) and may be set to none (effectively showing nothing), to standard (for the HTML5 standard behaviour which usually shows a semi-transparent copy of the actual draggable), to some HTML code (which is used to construct the element to be shown during dragging), to a function which receives the draggable's configured extras and the element to be dragged as its arguments and returns an HTML element to be used as the drag dummy or to an already existing HTML element (which must be visible). Important: creating a drag image from HTML is quite tricky - the approach used here is lightweight but may cause some flickering when dragging is started. Such flickering can usually be avoided by setting the CSS "overflow" of the document body to "hidden". Where this is not possible, constructing an image from HTML using html2canvas may be an (albeit no longer lightweight) alternative. If a function is used to construct the dummy, that function should also care itself about removing the newly created HTML element after use (see below for a suitable example)
  • DummyOffsetX?:number
    DummyOffsetX is an optional number (defaulting to 0) specifying the horizontal offset between the current x position of a mouse or finger during dragging and the shown drag image. It is used ay the second argument to DataTransfer.setDragImage without prior change
  • DummyOffsetY?:number
    DummyOffsetY is an optional number (defaulting to 0) specifying the vertical offset between the current y position of a mouse or finger during dragging and the shown drag image. It is used ay the third argument to DataTransfer.setDragImage without prior change
     
  • minX?:number
    minX is an optional number (defaulting to -Infinity) specifying the smallest possible value for the x component of any drag result. Please note: a configured drag image may well be dragged beyond configured limits - for that reason, such limits are most often combined with Dummy:'none' to effectively hide the drag image
  • minY?:number
    minY is an optional number (defaulting to -Infinity) specifying the smallest possible value for the y component of any drag result. Please note: a configured drag image may well be dragged beyond configured limits - for that reason, such limits are most often combined with Dummy:'none' to effectively hide the drag image
  • maxX?:number
    maxX is an optional number (defaulting to Infinity) specifying the largest possible value for the x component of any drag result. Please note: a configured drag image may well be dragged beyond configured limits - for that reason, such limits are most often combined with Dummy:'none' tto effectively hide the drag image
  • maxY?:number
    maxY is an optional number (defaulting to Infinity) specifying the largest possible value for the y component of any drag result. Please note: a configured drag image may well be dragged beyond configured limits - for that reason, such limits are most often combined with Dummy:'none' to effectively hide the drag image
     
  • Pannable?:string|HTMLElement|SVGElement
    some browsers provide automatic scrolling of only partially visible elements while a Draggable is dragged over them - but most do not, which is why svelte-drag-and-drop-actions implements its own "panning". Pannable is an optional parameter specifying the element which should be scrolled automatically. It should be a (not necessarily immediate) container of the Draggable and may be set to the DOM element itself or a CSS selector which identifies the desired container. If omitted, no panning will be performed. Please note: Pannables should be configured for Droppables only if panning should solely be performed while these Droppables are dragged over the given Pannable (which then does not necessarily have to be a DropZone). If you want a DropZone to be automatically scrolled when an arbitrary Droppable is dragged over it, configure panning for the DropZone instead. If you want both, configure both - it's safe to do so
  • PanSensorWidth?:number
    PanSensorWidth is an optional ordinal number (defaulting to 20) which specifies the width (in pixels) of the horizontal pan sensor area: panning starts as soon as the mouse pointer (or finger) gets closer than the given number of pixels to the left or right border of the configured Pannable. If set to 0, no horizontal panning will be performed
  • PanSensorHeight?:number
    PanSensorHeight is an optional ordinal number (defaulting to 20) which specifies the height (in pixels) of the vertical pan sensor area: panning starts as soon as the mouse pointer (or finger) gets closer than the given number of pixels to the upper or lower border of the configured Pannable. If set to 0, no vertical panning will be performed
  • PanSpeed?:number
    PanSpeed is an optional ordinal number (defaulting to 10) which specifies the "speed" of automatic scrolling - values in the range of 10...20 are reasonable choices, but it is always a good idea to make this parameter configurable for the users of your application. If set to 0, no panning will be performed
     
  • Operations?:string
    Operations is either a blank-separated list of DropOperations ('copy', 'move' or 'link'), the keyword all (which includes all three available operations) or the keyword none (which effectively suppresses dropping) and specifies which kind of data transfer the droppable component is going to support
  • DataToOffer?:DataOfferSet
    DataToOffer is a plain JavaScript object whose keys represent the various data formats a droppable component supports and whose corresponding values contain the transferrable data in that format. Often, the given keys denote MIME formats (which simplifies data transfer between different applications) or contain the special value "DownloadURL", but - in principle - any string (except none) may be used
     
  • onDragStart?:Position | ((DroppableExtras:any) => Position)
    onDragStart is either an optional Position (defaulting to {x:0, y:0}) or a function returning such a Position. When invoked, that function receives any Extras configured for the affected droppable. In either case, this parameter specifies the coordinate values from which to start dragging. Important: these "coordinates" do not necessarily represent a particular point on the screen - in fact, e.g., a resize handle may choose to start with the current width and height of the element to be resized and then directly use the results of any onDragMove or onDragEnd as the new element width and height without additional computation
  • onDragMove?: (x:number,y:number, dx:number,dy:number, DroppableExtras:any) => void
    onDragMove is an optional callback which (if given) is called repeatedly during dragging. When invoked, it receives the current drag result (in x and y, with an initial value chosen by onDragStart), the offset between this and the previous invocation (in dx and dy) and any Extras configured for the affected droppable
  • onDragEnd?: (x:number,y:number, dx:number,dy:number, DroppableExtras:any) => void
    onDragEnd is an optional callback which (if given) is called once when dragging has finished. When invoked, it receives the final drag result (in x and y, with the origin chosen by onDragStart), the offset between this and the most recent invocation of onDragMove (in dx and dy) and any Extras configured for the affected droppable
     
  • onDropZoneEnter?: (x:number,y:number, DropZoneExtras:any, DroppableExtras:any) => void
    onDropZoneEnter is an optional callback which (when invoked) indicates that the droppable has entered the bounds of a drop target which accepts (some of) the given data it offers. x and y contain the current coordinates of the mouse or finger relative to the drop zone, DropZoneExtras are any Extras configured for the entered drop zone and DroppableExtras any Extras configured for the affected droppable (which becomes useful as soon as multiple droppables share the same callbacks)
  • onDropZoneHover?: (x:number,y:number, DropZoneExtras:any, DroppableExtras:any) => void
    onDropZoneHover is an optional callback which (when invoked) indicates that the droppable is still moving within the bounds of a drop zone which accepts (some of) the given data it offers. x and y contain the current coordinates of the mouse or finger relative to the drop zone, DropZoneExtras are any Extras configured for the hovered drop zone and DroppableExtras any Extras configured for the affected droppable (which becomes useful as soon as multiple droppables share the same callbacks)
  • onDropZoneLeave?: (DropZoneExtras:any, DroppableExtras:any) => void
    onDropZoneLeave is an optional callback which (when invoked) indicates that the droppable has either just left the bounds of a previously entered drop zone or that drop zone has decided to no longer accept any data offered by the droppable. DropZoneExtras are any Extras configured for the left drop zone and DroppableExtras any Extras configured for the affected droppable (which becomes useful as soon as multiple droppables share the same callbacks)
  • onDropped?: (x:number,y:number, Operation:DropOperation, TypeTransferred:string, DataTransferred:any, DropZoneExtras:any, DroppableExtras:any) => void}
    onDropped is an optional callback which (when invoked) indicates that the droppable has just been dropped onto a drop zone - and it could now be up to the droppable to react accordingly (including the possibility to perform the requested operation itself, instead of the drop zone). x and y contain the coordinates of the mouse or finger relative to the drop zone when the droppable was dropped, Operation contains the performed drop operation (if known - or undefined otherwise), TypeTransferred indicates the type of the actually transferred data (if known - or undefined otherwise), DataTransferred is the actually transferred data itself (if known - or undefined otherwise), DropZoneExtras are any Extras configured for the drop zone and DroppableExtras any Extras configured for the affected droppable (which becomes useful as soon as multiple droppables share the same callbacks)

While being dragged, the CSS class dragged is assigned to the draggable element (not the drag image!). It will be removed again as soon as dragging has ended.

While over a drop zone which accepts (some of) the data offered, the CSS class droppable is assigned to the draggable element (not the drag image!). It will be removed again as soon as dragging has ended or the droppable has been left.

use:asDraggable should never be combined with use:asDroppable (as the latter already includes the former) - if you want an element to be dropped somewhere, simply use use:asDroppable alone.

use:asDroppable may, however, be combined with use:asDropZone in order to implement draggable drop zones which may itself be dropped over other drop zones.

use:asDropZone

use:asDropZone marks an element as a "drop zone" which allows "droppable" elements to be dropped onto it in order to trigger an operation.

The type annotiations shown below are relevant for TypeScript programmers only, JavaScript programmers may simply ignore them.

use:asDropZone={options}

is the classical pattern for Svelte actions. use:asDropZone supports the following options (bundled into an instance of type DropZoneOptions):

  • Extras?:any
    Extras are an optional, user-defined value which can be used during drag-and-drop operations within the same application to identify the drop zone component. They become useful as soon as multiple drop zones share the same callbacks
     
  • TypesToAccept?:TypeAcceptanceSet
    TypesToAccept is a plain JavaScript object whose keys represent the various data formats a drop zone accepts and whose corresponding values contain a blank-separated, perhaps empty, list of supported drop operations for that format. Often, the given keys denote MIME formats (which simplifies data transfer between different applications) or contain the special value "DownloadURL", but - in principle - any string (except none) may be used. Note: since native HTML5 drag-and-drop implementations often fail reporting a correct "dropEffect", the given drop operations can not be properly checked - with the exception, that types with empty operation lists will never be accepted
  • HoldDelay?:number
    when a droppable has entered a drop zone and remains there for at least HoldDelay milliseconds without much movement, the onDroppableHold callback of that drop zone is invoked (if it exists). Such a callback may be used to perform activities such as expanding a collapsed tree list entry, opening an input dialog or similar. The property is optional: when missing, onDroppableHold will never be called
  • Pannable?:string|HTMLElement|SVGElement
    some browsers provide automatic scrolling of only partially visible elements while a Draggable is dragged over them - but most do not, which is why svelte-drag-and-drop-actions implements its own "panning". Pannable is an optional parameter specifying the element which should be scrolled automatically. It may be set to this (if the DropZone itself should be scrolled), to the DropZone's own DOM element, the DOM element of a (not necessarily immediate) container of the DropZone, or a CSS selector which identifies such a container. If omitted, no panning will be performed. Please note: Pannables should be configured for DropZones only if panning should always be performed while an arbitrary Droppable is dragged over them. If you want a Pannable to be automatically scrolled only when its inner Draggables are dragged over it, configure panning for those Draggables instead (the Pannable then does not necessarily have to be a DropZone itself). If you want both, configure both - it's safe to do so
  • PanSensorWidth?:number
    PanSensorWidth is an optional ordinal number (defaulting to 20) which specifies the width (in pixels) of the horizontal pan sensor area: panning starts as soon as the mouse pointer (or finger) gets closer than the given number of pixels to the left or right border of the configured Pannable. If set to 0, no horizontal panning will be performed
  • PanSensorHeight?:number
    PanSensorHeight is an optional ordinal number (defaulting to 20) which specifies the height (in pixels) of the vertical pan sensor area: panning starts as soon as the mouse pointer (or finger) gets closer than the given number of pixels to the upper or lower border of the configured Pannable. If set to 0, no vertical panning will be performed
  • PanSpeed?:number
    PanSpeed is an optional ordinal number (defaulting to 10) which specifies the "speed" of automatic scrolling - values in the range of 10...20 are reasonable choices, but it is always a good idea to make this parameter configurable for the users of your application. If set to 0, no panning will be performed
     
  • onDroppableEnter?: (x:number,y:number, Operation:DropOperation, offeredTypeList:string[], DroppableExtras:any, DropZoneExtras:any) => boolean|undefined
    onDroppableEnter is an optional callback which (when invoked) indicates that a droppable has entered this drop zone and that this droppable's data is at least partially acceptable. x and y contain the current coordinates of the mouse or finger relative to the drop zone, Operation is the requested drop operation (if known - or undefined otherwise), offeredTypeList is a JavaScript array containing the offered data "types", DroppableExtras are any Extras configured for the entering droppable and DropZoneExtras any Extras configured for the affected drop zone (which becomes useful as soon as multiple drop zones share the same callbacks). The callback should return false if the offered data turns out not to be acceptable after all - or anything else (including undefined) otherwise
  • onDroppableMove?: (x:number,y:number, Operation:DropOperation, offeredTypeList:string[], DroppableExtras:any, DropZoneExtras:any) => boolean|undefined
    onDroppableMove is an optional callback which (when invoked) indicates that a droppable is still moving within the bounds of this drop zone and that this droppable's data is at least partially acceptable. x and y contain the current coordinates of the mouse or finger relative to the drop zone, Operation is the requested drop operation (if known - or undefined otherwise), offeredTypeList is a JavaScript array containing the offered data "types", DroppableExtras are any Extras configured for the hovering droppable and DropZoneExtras any Extras configured for the affected drop zone (which becomes useful as soon as multiple drop zones share the same callbacks). The callback should return false if the offered data turns out not to be acceptable after all - or anything else (including undefined) otherwise
  • onDroppableHold?: (x:number,y:number, DroppableExtras:any, DropZoneExtras:any) => void
    onDroppableHold is an optional callback which (when invoked) indicates that a droppable whose data is at least partially acceptable, stood still for at least HoldDelay milliseconds within the bounds of this drop zone. x and y contain the current coordinates of the mouse or finger relative to the drop zone, DroppableExtras are any Extras configured for the held droppable and DropZoneExtras any Extras configured for the affected drop zone (which becomes useful as soon as multiple drop zones share the same callbacks). Warning: be careful with what to do within that callback - if you disturb the flow of events (e.g., by invoking window.alert), the visual feedback for drag-and-drop may get mixed up!
  • onDroppableLeave?: (DroppableExtras:any, DropZoneExtras:any) => void
    onDroppableLeave is an optional callback which (when invoked) indicates that a droppable whose data was at least partially acceptable, moved outside the bounds of this drop zone. DroppableExtras are any Extras configured for the leaving droppable and DropZoneExtras any Extras configured for the affected drop zone (which becomes useful as soon as multiple drop zones share the same callbacks)
  • onDrop?: (x:number,y:number, Operation:DropOperation, DataOffered:any, DroppableExtras:any, DropZoneExtras:any) => string
    onDrop is an optional callback which (when invoked) indicates that a droppable (whose data is at least partially acceptable) was dropped within the bounds of this drop zone - and it may now be up to the drop zone to perform the requested operation. x and y contain the current coordinates of the mouse or finger relative to the drop zone, Operation is the requested drop operation (if known - or undefined otherwise), DataOffered is a JavaScript object, whose keys represent the various data formats offered and whose corresponding values contain the offered data in that format, DroppableExtras are any Extras configured for the droppable and DropZoneExtras any Extras configured for the affected drop zone (which becomes useful as soon as multiple drop zones share the same callbacks). The callback should return the data format actually accepted or, at least, undefined to report that any offered data was accepted - or false if the offered data turned out not to be acceptable after all

While being hovered by a droppable whose data offered is at least partially acceptable, the CSS class hovered is assigned to the drop zone element. It will be removed again as soon as either dragging has ended, the droppable has left the drop zone or the droppable's offered data is no longer acceptable.

use:asDropZone may be combined with either use:asDraggable or use:asDroppable (not both together) in order to implement draggable drop zones which may perhaps itself be dropped over other drop zones.

Examples

Dragging only

Drag-and-Drop

Caveats

Simply extending already existing native HTML5 drag-and-drop functionality has a lot of advantages - but also some disadvantages, as there are:

  • the cursor shown while dragging is under full control of the native drag-and-drop implementation and can not be influenced programmatically (with the minor exception of setting a proper drop operation for a Droppable)
  • a custom drag image must either be an image object or a visible(!) element within the document from which a snapshot is taken
  • the configured drag image can not be changed during dragging as it is constructed when dragging starts and never updated again
  • in some browsers, as soon as the document is scrolled (even a little bit only) a custom drag dummy "slides in" to its designated position when dragging starts rather than appears there from the beginning - this seems to be a browser bug which the author cannot compensate

Build Instructions

You may easily build this package yourself.

Just install NPM according to the instructions for your platform and follow these steps:

  1. either clone this repository using git or download a ZIP archive with its contents to your disk and unpack it there
  2. open a shell and navigate to the root directory of this repository
  3. run npm install in order to install the complete build environment
  4. execute npm run build to create a new build

You may also look into the author's build-configuration-study for a general description of his build environment.

License

MIT License