-
Notifications
You must be signed in to change notification settings - Fork 156
Drag&Drop Directives Specification
- Overview
- User Stories
- Functionality
-
API
- 4.1. Directives
- 4.2. Classes
- 4.3. 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 |
| 0.3 | Svetoslav Krastev | Aug 12, 2019 | Updated API |
- 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
igxDragcarries so when a specific element is dropped it can be distinguished. - set new position in the DOM for the
igxDraginstanced element and have animation that transitions to that new location. - have ability to customize the animations applied to the
igxDragelement 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.
- Interactable children elements
When the user wants to have interactable children of the main element that has igxDrag instanced, he can set the igxDragIgnore directive to them in order for them to be ignored by the igxDrag and not perform any dragging action. This will leave these element to be fully interactable and will receive all mouse events.
<div [igxDrag]="myData">
<span>Drag me!</span>
<igx-icon igxDragIgnore fontSet="material" (click)="remove()">bin</igx-icon>
</div>The ghost 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
igxDragis used on. It can be customized by providing a template reference to theghostTemplateinput directly. The template itself can be position anyway, since the only thing provided is reference to it. It can be done the following way:<div [igxDrag]="'Dolphin'" [ghostTemplate]="customGhost"> Drag me! </div> <ng-template #customGhost> <div>I can fly!</div> </ng-template>
-
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 dragChannel and dropChannel input on respectively 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 Equalitycomparison.
Example:
<div igxDrag [dragChannel]="['Mammals', 'Land']"> Human </div>
<div igxDrag [dragChannel]="['Mammals', 'Water']"> Dolphin </div>
<div igxDrag [dragChannel]="['Insects', 'Air']"> Butterfly </div>
<div igxDrag [dragChannel]="['Insects', 'Land']"> Ant </div>
<div igxDrop [dropChannel]="['Mammals']"> Mammals </div>
<div igxDrop [dropChannel]="['Insects']"> Insects </div>
<div igxDrop [dropChannel]="['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 transitionToOrigin and the transitionTo methods.
The transitionToOrigin method as the name suggest animates the currently dragged element or its ghost to the start position where the dragging began. The transitionTo 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 transitioned 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 separate drop strategies and each is defined a separate class that has specific functionality:
-
The
Defaultstrategy does not perform any action when an element is dropped onto an IgxDrop element and is implemented as a class namedIgxDefaultDropStrategy. -
As the names suggest the first
Appendstrategy inserts the dropped element as a last child and is implemented as a class namedIgxAppendDropStrategy. -
The
Prependstrategy inserts the dropped element as first child and is implemented as a class namedIgxPrependDropStrategy. -
The
Insertstrategy inserts the dragged element at the dropped position. If there is a child under the element when it was dropped, theigxDraginstanced element will be inserted at that child's index. It is implemented as a class namedIgxInsertDropStrategy
The way a strategy can be applied is by setting the dropStrategy input to one of the listed classes above. The value provided has to be e type and not an instance, since the igxDrop has to create the instance itself.
Example:
TypeScript:
public insertStrategy = IgxInsertDropStrategy;HTML:
<div igxDrop [dropStrategy]="insertStrategy"></div>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></div>
<!-- ... -->
<div igxDrop (dropped)="onDropped($event)"></div>TypeScript
public onDropped(event) {
event.cancel = true;
}If the user would like to implement its own drop logic it can easily be done by binding to dropped and executing their logic when the event is triggered or extending the default drop strategy.
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 transitionToOrigin or transitionTo 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}"
(dragEnd)="onDragEnd($event)"
(transitioned)="onDragAnimationEnd($event)">
{{product}
</div>
<div>
<div>Basket:</div>
<div igxDrop (dropped)="onDragDropped($event)">
<div *ngFor="let product of basketProducts">{{product}}</div>
</div>TypeScript
public availableProducts = ["milk", "cheese", "banana"];
public basketProducts = [];
public onDragEnd(event) {
event.owner.transitionToOrigin();
}
public onDragDropped(event) {
event.drag.transitionTo(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 dataSets information to be stored in the directive. any undefined dragToleranceThe amount of pixes the user need to move before the dragging starts and the preview is rendered. number 5 dragDirectionSpecifies if the element should be draggable only in one direction. DragDirection DragDirection.BOTH dragChannelSpecifies channel or multiple channels to which the element is linked to and can interact with only those igxDropelements in those channelsnumber | string | number[] | string[] undefined renderGhostSets 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 ghostTemplateA custom template for the ghost element that completely replaces the default one. TempalteRef undefined ghostHostSets the element to which the drag ghost element will be appended to. By default it's set to null and the ghost element is appended to the body. ElementRef undefined ghostOffsetXSets the offset position of the ghost element relative to the mouse horizontally. number undefined ghostOffsetYSets the offset position of the ghost element relative to the mouse vertically. number undefined Outputs
Name Description Cancelable Parameters dragStartEvent triggered when any movement starts. true IDragBaseEventArgsdragMoveEvent triggered for every frame where the igxDragelement has been dragged.true IDragMoveEventArgsdragEndEvent triggered when the user releases the element area that is not inside an igxDrop. This is triggered before any animation starts.false IDragBaseEventArgsclickEven triggered when the user performs a click and not dragging. This is the native event. false MouseEvent transitionedEvent 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 IDragBaseEventArgsghostCreateEvent triggered right before the ghost element is created false IDragGhostBaseEventArgsghostDestroyEvent triggered right before the ghost element is destroyed false IDragGhostBaseEventArgsProperties
Name Description Type locationGets the current location of the element relative to the page. If ghost is enabled it will get the location of the ghost, if the user is not currently dragging it will return the location of the base element. IgxDragLocationoriginLocationGets the origin location of the element before dragging started. If ghost is enabled it will get the location of the base. IgxDragLocationMethods
Name Description Parameters Return Type setLocationSets new location for the igxDrag directive. When ghost is enable and it is not rendered it will be ignored. newLocation?:IgxDragLocationvoid transitionToOriginAnimates the element from its current location to its initial position. If it was not moved or no start location is specified nothing would happen . customTransitionArgs?: IDragCustomTransitionArgs,startLocation?:IgxDragLocation,void transitionToAnimates 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:IgxDragLocation|ElementRef, customTransitionArgs?:IDragCustomTransitionArgs,startLocation?:IgxDragLocationvoid -
Inputs
Name Description Type Default value dataSets information to be stored in the directive. any undefined dropChannelSpecifies channel or multiple channels to which the element is linked to and can interact with only those igxDragelements in those channelsnumber | string | number[] | string[] undefined dropStrategySets a drop strategy that should be applied once an element is dropped into the current igxDropelement.class reference IgxDefaultDropStrategy Outputs
Name Description Cancelable Type enterEvent triggered once an IgxDraginstanced element enters the boundaries of the drop area. Similar to MouseEnter.false IDropBaseEventArgsoverEvent triggered when an IgxDraginstanced element moves inside the boundaries of the drop area similar to MouseOver.false IDropBaseEventArgsleaveEvent triggered once an IgxDraginstanced element leaves the boundaries of the drop area. Similar to MouseLeave.false IDropBaseEventArgsdroppedEvent triggered once an IgxDraginstanced element inside the drop area is released.true IDropDragDropEventArgs
-
Name Description Type pageXThe far left location of the drag element relative to the page horizontally. number pageYThe far top location of the drag element relative to the page vertically. number
-
Name Description Type durationSpecifies how many seconds or milliseconds the animation takes to complete. number timingFunctionSpecifies the speed curve for the animation effect. string delaySpecifies a delay (in seconds) for the animation to start. number -
Name Description Type originalEventThe original event that starting this interaction. PointerEvent/MouseEvent/TouchEvent ownerThe owner that triggered this event. In this case the igxDrag.IgxDragDirective startXThe start pageX position of pointer that initiated the drag. number startYThe start pageY position of pointer that initiated the drag. number pageXThe current pageX position of pointer that initiated the drag. number pageYThe current pageY position of pointer that initiated the drag. number -
Extends
IDragBaseEventArgs.Name Description Type cancelProperty specifying if the default logic which that event is related should be canceled boolean -
Extends
IDragStartEventArgs.Name Description Type cancelProperty specifying if the default logic which that event is related should be canceled boolean newPageXThe new pageX position of the pointer that the igxDrag will use. It can be overridden. number newPageYThe new pageX position of the pointer that the igxDrag will use. It can be overridden. number -
Name Description Type ownerNull or the owner directive that was used to specify custom ghost. IgxDragGhostDirectivedragDirectiveThe owner igxDragdirective that created/destroyed the ghost.IgxDragDirective -
Name Description Type originalEventThe original event that caused the interaction. PointerEvent/MouseEvent/TouchEvent ownerThe owner element that triggered this event. In this case the igxDrop.IgxDropDirectivedragDirectiveThe owner igxDragdirective of the element being dragged over the drop area.IgxDragDirectivestartXThe start pageX position of pointer that initiated the drag. number startYThe start pageY position of pointer that initiated the drag. number pageXThe current pageX position of pointer that performs the dragging. number pageYThe current pageY position of pointer that performs the dragging. number offsetXThe current offset of the pointer relative to the pageX position of the igxDrop.number offsetYThe current offset of the pointer relative to the pageY position of the igxDrop.number -
Extends
IDropBaseEventArgsName Description Type cancelSpecifies 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. |
- Should correctly initialize drag and drop directives
- Should create drag ghost element and trigger
onGhostCreate/onGhostDestroy. - Should not create drag ghost element when the dragged amount is less than
dragTolerance. - Should trigger dragStart/dragMove/dragEnd events in that order.
- Should trigger igxDrag
onDragEnter/onDragLeaveevents when it enters and leaves igxDrop area. -
- Should trigger igxDrag
onDropevent when it is dropped onto and igxDrop area.
- Should trigger igxDrag
- Should position ghost at the same position relative to the mouse when drag started.
- Should position ghost relative to the mouse using offsetX and offsetY correctly.
- Should position ghost at the same position relative to the mouse when drag started when host is defined.
- Should allow customizing of ghost element by passing template reference and position it correctly.
- Should position custom ghost relative to the mouse using
offsetXandoffsetYcorrectly. - Should correctly move igxDrag element when ghost is disabled and trigger dragStart/dragMove/dragEnd events.
- Should prevent dragging if it does not exceed dragTolerance and ghost is disabled.
- Should correctly apply dragTolerance of 0 when it is set to 0 and ghost is disabled.
- Should correctly set location using setLocation() method when ghost is disabled.
- Should trigger onEnter/onDrop/onLeave events when element is dropped inside igxDrop element
-
- Should trigger onEnter/onDrop/onLeave events when element is dropped inside and is linked with it.
-
- Should not trigger onEnter/onDrop/onLeave events when element is dropped inside and is not linked with it.
-
- Should not perform any action by default when an element is dropped inside.
-
- Should put dropped element as a first child when
Prependdrop strategy is used.
- Should put dropped element as a first child when
-
- Should put dropped element as a last child when
Appenddrop strategy is used.
- Should put dropped element as a last child when
-
- Should put dropped element as a second child when
Insertdrop strategy is used and element is dropped over the second child already in the igxDrop area.
- Should put dropped element as a second child when
-
- Should cancel drop strategy when the
onDropevent is canceled.
- Should cancel drop strategy when the