-
Notifications
You must be signed in to change notification settings - Fork 161
Drag&Drop Directives Specification
- Overview
- User Stories
- Functionality
-
API
- 4.1. Directives
- 4.2. Interfaces
- Assumptions and Limitations
-
Test Scenarios
- 6.1. Manual
- 6.2. Automation
Version | User | Date | Notes |
---|---|---|---|
0.1 | Svetoslav Krastev | Jun 20, 2019 | Initial draft |
0.2 | Svetoslav Krastev | Jun 27, 2019 | Updated draft |
- Stefan Ivanov | Date:
- Radoslav Karaivanov | Date:
- Martin Pavlov | Date:
- Konstantin Dinev | Date:
IgxDrag
provides a way to move an element by click and dragging it around. In combination with igxDrop
directive that specifies where element can be put it allows an element to be moved from one place to another.
Elaborate more on the multi-faceted use cases
As a developer, I want to:
- easily define an element can be dragged by using a directive on any element.
- easily define a drop area where a dragged element can be dropped on any element.
- specify if element that can be dragged and moved freely.
- specify if a ghost element should be rendered after dragging begins representing the original element and the original element should keep its original position.
- easily use a custom ghost element that is created from the drag directive after dragging starts.
- provide a way to set where the default/custom ghost should be rendered (what it's parent element should be).
- ? dynamically update the custom ghost based on my provided template
- have elements inside dragged elements that should not trigger dragging and can be interacted with.
- be able to listen to events when the drag has started/ended, when clicked or when the preview element is being created.
- be able to listen to events when element has been dropped onto a drop area.
- provide specific data that the
igxDrag
carries so when a specific element is dropped it can be distinguished. - set new position in the DOM for the
igxDrag
instanced element and have animation that transitions to that new location. - have ability to customize the animations applied to the
igxDrag
element when interacting with it
As an end user, I want to:
- drag an element around freely and it keeping its position on release.
- drag an element around and it rendering a ghost element that is placed under the pointer when dragging around.
- drag an element from one area to another area by both possible ways of dragging.
- use either mouse or touch interaction to drag an element.
- have a clear indication when the dragging has started.
- be able to click the draggable elements.
- have some room of error when clicking on an element so it doesn't start dragging immediately.
- have smooth transition animations if dragged elements keep their original position or have specific new ones that don't align with the exact drop location
The igxDrag
directive can be instantiated on any type of element. It can be used on its own without depending on the igxDrop
. It should provide enough functionality so the user could determine where it has been released and so implements a custom logic.
Specific data can be stored inside the igxDrag
for various purposes like identifying it among other draggable elements and etc. It can be specified by assigning it on the initialization tag [igxDrag]
or by using the data input where it is stored:
<div [igxDrag]="myData">Drag me!</div>
By default the dragging will not start immediately in order to provide some room for error as well as not interrupt if the user wants to click the element instead. The tolerance for it is 5px
in any direction and if it is exceeded then the dragging would start. This can be configured using the dragTolerance
input.
The renderGhost
input is set to true
by default which means that the base element the igxDrag
directive is initialized will keep its position and a ghost will be rendered under the user pointer once the dragging starts. While still holding and moving the ghost created will move along the user pointer instead of the base element.
-
Customizing the ghost
The ghost element by default is a copy of the base element the
igxDrag
is used on. It can be customized by providing a custom template using the*igxDragGhost
directly on the desired element or usingigxDragGhost
onto a<ng-template>
element. It can be done the following way:<div [igxDrag]="'Dolphin'"> Drag me! <div *igxDragGhost>I can fly!</div> </div>
or
<div [igxDrag]="'Dolphin'"> Drag me! <ng-template igxDragGhost> <div>I can fly!</div> </ng-template> </div>
-
Customizing the base
Since when using a ghost element leaves us with the base element being still rendered at its original location we can hide it by setting applying custom visibility style when dragging starts or by completely replacing its content using
ngIf
.Hiding the base element:
<div [igxDrag]="'Dolphin'" [ngStyle]="{ 'visibility': dragged ? 'hidden' : 'visible' }"> Drag me! </div>
Customizing the base content:
<div [igxDrag]="'Dolphin'" [ngStyle]="{ 'visibility': dragged ? 'hidden' : 'visible' }"> <div *ngIf="dragged; else originTemplate">Drag me!</div> <ng-template #originTemplate>Origin!</ng-template> </div>
If renderGhost
input is set to false the dragging logic for the igxDrag
provides dragging ability for the initialized element itself. This means that it can freely move an element around by click and hold and when released it will keep its position where it was dragged.
The user can specify an element that is a child of the igxDrag
by which to drag since by default the whole element is used to perform that action. It can be done using the igxDragHandle
directive and can be applied to multiple elements inside the igxDrag
.
When multiple channels are applied to an igxDrag
and one of them matches one of applied channels to an igxDrop
, then all events and applied behaviors would be executed when that element is dropped.
Example:
<div igxDrag>
<div igxDragHandle>X</div>
Drag me!
</div>
Using the linkChannel
input on both igxDrag
and igxDrop
directives the user can link different elements to interact only between each other. For example if an igxDrag
element needs to be constrained so it can be dropped on specific igxDrop
element and not all available this can easily be achieved by assigning them same channel.
When assigning either single or multiple channels using an array, each channel is compared using the
Strict Equality
comparison.
Example:
<div igxDrag [igxDragLinkChannel]="['Mammals', 'Land']"> Human </div>
<div igxDrag [igxDragLinkChannel]="['Mammals', 'Water']"> Dolphin </div>
<div igxDrag [igxDragLinkChannel]="['Insects', 'Air']"> Butterfly </div>
<div igxDrag [igxDragLinkChannel]="['Insects', 'Land']"> Ant </div>
<div igxDrop [igxDropLinkChannel]="['Mammals']"> Mammals </div>
<div igxDrop [igxDropLinkChannel]="['Insects']"> Insects </div>
<div igxDrop [igxDropLinkChannel]="['Land']"> Land </div>
As displayed above only Human
and Dolphin
can be dropped in the 'Mammals' class but not in the 'Insects' class, where only the Butterfly
and Bee
can be dropped. Same for the 'Land' drop area where only Ant
and Human
can be dropped.
By default when an element is being dragged there are no animations applied.
The user can apply transition animation to the igxDrag
any time, but it is advised to use it when dragging ends or the element is not currently dragged. This can be achieved using the animateToOrigin
and the animateTo
methods.
The animateToOrigin
method as the name suggest animates the currently dragged element or its ghost to the start position where the dragging began. The animateTo
method animates the element to a specific location relative to the page (i.e. pageX
and pageY
) or to the position of a specified element. If the element is not being currently dragged it will animate anyway or create ghost and animate it to the desired position.
Both function have arguments that the user can set to customize the transition animation and set duration, timing function or delay. If specific start location is set it will animate the element starting from there.
When the transition animation ends if a ghost is created it will be removed and the igxDrag
directive will return to its initial state or if no ghost is created it will keep its position. In both cases then the onAnimationEnd
event will be triggered depending on how long the animation lasts. If no animation is applied it will triggered instantly.
If the user want to have other types of animations that involve element transformations he can do that like any other element either using the Angular Animations or straight CSS Animations to either the base igxDrag
element or its ghost. If he wants to apply them to the ghost he would need to define a custom ghost and apply animations to that element.
For achieving a drop functionality with the igxDrag
directive the igxDrop
directive should be used. It can be applied on any kind of element and it specifies an area where the igxDrag
can be dropped.
By default the igxDrop
does not apply any logic to the dragged element when it is dropped onto it. The user could choose between a few different drop strategies if he would like the igxDrop
to perform some action or he could implement his own drop logic using the provided onDragDrop
events.
The igxDrop
comes with 4 drop strategies which are defined in the enum IgxDropStrategy
and has the following values - Append
, Prepend
, Insert
:
-
As the names suggest the first
Append
strategy inserts the dropped element as a last child. -
The
Prepend
strategy inserts the dropped element as first child. -
The
Insert
strategy inserts the dragged element at the dropped position. If there is a child under the element when it was dropped, theigxDrag
instanced element will be inserted at that child's index.
When using a specific drop strategy, its behavior can be canceled in the onDrop
or onDragDrop
events by setting the cancel property to true. The onDrop
event is specific to the igxDrag
and the onDragDrop
event to the igxDrop
. If the user does not have drop strategy applied to the igxDrop
canceling the event would have no side effects.
Example:
HTML
<div igxDrag (igxDragOnDrop)="onDragDropped($event)"></div>
<!-- ... -->
<div igxDrop></div>
TypeScript
public onDragDropped(event) {
event.cancel = true;
}
If the user would like to implement its own drop logic it can easily be done by binding either to onDrop
or the onDragDrop
event and executing their logic when the event is triggered. Both events should provide enough context so the user could implement it on their own.
If the user decides that he want to use transition animations when dropping an element he can do that by using transition animations that can be applied to the igxDrag
by calling the animateToOrigin
or animateTo
methods whenever he wants. Preferably that should be done when dragging of an element ends or when it is dropped onto a igxDrop
instanced element.
Example:
HTML
<div>Products:</div>
<div #productsContainer>
<div *ngFor="let product of availableProducts; let i = index" [igxDrag]="{index: i}"
(igxDragOnRelease)="onDragReleased($event)" (igxDragOnDrop)="onDragDropped($event)"
(igxDragOnAnimationEnd)="onDragAnimationEnd($event)">
{{product}
</div>
<div>
<div>Basket:</div>
<div igxDrop>
<div *ngFor="let product of basketProducts">{{product}}</div>
</div>
TypeScript
public availableProducts = ["milk", "cheese", "banana"];
public basketProducts = [];
public onDragReleased(event) {
event.owner.animateToOrigin();
}
public onDragDropped(event) {
event.owner.animateTo(event.dropDirective.element);
}
public onDragAnimationEnd(event) {
const removeIndex = event.owner.data.index;
const removedElem = availableProducts.splice(removeIndex, 1);
basketProducts.push(removedElem);
}
-
Inputs
Name Description Type Default value data
Sets information to be stored in the directive. any undefined dragTolerance
The amount of pixes the user need to move before the dragging starts and the preview is rendered. number 5 renderGhost
Sets if the when the dragging of the element start a ghost should be rendered under the pointer and the original element kept where it is positioned or not. boolean true linkChannel
Specifies channel or multiple channels to which the element is linked to and can interact with only those igxDrop
elements in those channelsnumber | string | number[] | string[] undefined Outputs
Name Description Cancelable Parameters onMoveStart
Event triggered when any movement starts. true IDragMoveBaseEventArgs
onDrag
Event triggered for every frame where the igxDrag
element has been dragged.true IDragMoveEventArgs
onRelease
Event triggered when the user releases the element area that is not inside an igxDrop
. This is triggered before any animation starts.false IDragMoveBaseEventArgs
onAnimationEnd
Event triggered after any movement of the drag element has ended. This is triggered after all animations have ended and before the ghost is removed. false IDragMoveBaseEventArgs
onEnter
Event triggered once the drag element enters an igxDrop
elementfalse IDragEnterLeaveEventArgs
onLeave
Event triggered once the drag element leaves an igxDrop
elementfalse IDragEnterLeaveEventArgs
onDrop
Event triggered once the drag element has been released over an igxDrop
element.true IDragDropEventArgs
onGhostCreated
Event triggered right before the ghost element is created false IDragGhostBaseEventArgs
onGhostDestroyed
Event triggered right before the ghost element is destroyed false IDragGhostBaseEventArgs
Properties
Name Description Type location
Gets the current location of the element relative to the page. If it is currently being dragged it will get the new location or the location of the ghost IDragLocation
Methods
Name Description Parameters Return Type animateToOrigin
Animates the element from its current location to its initial position. If it was not moved or no start location is specified nothing would happen . duration?:
number,timingFunction?:
string,delay?:
number,startLocation?:
IDragLocation
,void animateTo
Animates the element from its current location to specific location or DOM element. If it was not moved or no start location is specified nothing would happen. target:
IDragLocation
|ElementRef,duration?:
number,timingFunction?:
string,delay?:
number,startLocation?:
IDragLocation
void -
Inputs
Name Description Type Default value data
Sets information to be stored in the directive. any undefined linkChannel
Specifies channel or multiple channels to which the element is linked to and can interact with only those igxDrag
elements in those channelsnumber | string | number[] | string[] undefined Outputs
Name Description Cancelable Type onDragEnter
Event triggered once an IgxDrag
instanced element enters the boundaries of the drop area. Similar to MouseEnter.false IDropBaseEventArgs
onDragOver
Event triggered when an IgxDrag
instanced element moves inside the boundaries of the drop area similar to MouseOver.false IDropBaseEventArgs
onDragLeave
Event triggered once an IgxDrag
instanced element leaves the boundaries of the drop area. Similar to MouseLeave.false IDropBaseEventArgs
onDragDrop
Event triggered once an IgxDrag
instanced element inside the drop area is released.true IDropDragDropEventArgs
Properties
Name Description Type location
Gets the current location of the element relative to the page. IDragLocation
-
Inputs
Name Description Type Default value host
Set the parent element of the ghost to which it will be attached to. HTMLElement, ElementRef null offsetX
Sets how many pixels the ghost should offset from the current pointer location on X axis number Dynamic, based on where the user initiates dragging offsetY
Sets how many pixels the ghost should offset from the current pointer location on Y axis number Dynamic, based on where the user initiates dragging Properties
Name Description Type elementRef
Contains the reference to the ghost element when rendered. ElementRef Outputs
Name Description Cancelable Parameters onCreate
Event triggered after it has been created false IDragGhostBaseEventArgs
onDestroy
Event triggered right before being destroyed false IDragGhostBaseEventArgs
-
Name Description Type pageX
The far left location of the drag element relative to the page horizontally. number pageY
The far top location of the drag element relative to the page vertically. number -
Name Description Type originalEvent
The original event that starting this interaction. PointerEvent/MouseEvent/TouchEvent owner
The owner that triggered this event. In this case the igxDrag
.IgxDragDirective startX
The start pageX position of pointer that initiated the drag. number startY
The start pageY position of pointer that initiated the drag. number pageX
The current pageX position of pointer that initiated the drag. number pageY
The current pageY position of pointer that initiated the drag. number -
Extends
IDragMoveBaseEventArgs
.Name Description Type cancel
Property specifying if the default logic which that event is related should be canceled boolean -
Name Description Type originalEvent
The original event that starting this interaction. PointerEvent/MouseEvent/TouchEvent owner
The owner that triggered this event. In this case the igxDrag
.IgxDragDirective
dropDirective
The start pageX position of pointer that initiated the drag. IgxDropDirective
startX
The start pageX position of pointer that initiated the drag. number startY
The start pageY position of pointer that initiated the drag. number pageX
The current pageX position of pointer that performs the dragging. number pageY
The current pageY position of pointer that performs the dragging. number offsetX
The current offset of the pointer relative to the pageX position of the igxDrop
.number offsetY
The current offset of the pointer relative to the pageY position of the igxDrop
.number -
Extends
IDragEnterLeaveEventArgs
Name Description Type originalEvent
The original event that caused the interaction. PointerEvent/MouseEvent/TouchEvent -
Name Description Type owner
Null or the owner directive that was used to specify custom ghost. IgxDragGhostDirective
dragDirective
The owner igxDrag
directive that created/destroyed the ghost.IgxDragDirective
offsetX
Sets/Gets how many pixels the ghost should offset from the current pointer location on X axis number offsetY
Sets/Gets how many pixels the ghost should offset from the current pointer location on Y axis number -
Name Description Type originalEvent
The original event that caused the interaction. PointerEvent/MouseEvent/TouchEvent owner
The owner element that triggered this event. In this case the igxDrop
.IgxDropDirective
dragDirective
The owner igxDrag
directive of the element being dragged over the drop area.IgxDragDirective
startX
The start pageX position of pointer that initiated the drag. number startY
The start pageY position of pointer that initiated the drag. number pageX
The current pageX position of pointer that performs the dragging. number pageY
The current pageY position of pointer that performs the dragging. number offsetX
The current offset of the pointer relative to the pageX position of the igxDrop
.number offsetY
The current offset of the pointer relative to the pageY position of the igxDrop
.number -
Extends
IDropBaseEventArgs
Name Description Type cancel
Specifies if the default drop logic related to the event should be canceled. boolean
Assumptions | Limitation Notes |
---|---|
Drag/Drop on iOS 11.0 and earlier | Due to missing implementation of vital document functions that the IgxDrag uses in iOS 11.0 and the combination of IgxDrag with IgxDrop would not work. IgxDrag on its own should still work. |