@ -10,6 +10,13 @@ namespace Connected.Components;
public class DragAndDropIndexChangedEventArgs : EventArgs
public class DragAndDropIndexChangedEventArgs : EventArgs
{
{
#region Variables
public string ZoneIdentifier { get ; }
public int Index { get ; }
public string OldZoneIdentifier { get ; }
# endregion
#region Events
public DragAndDropIndexChangedEventArgs ( string zoneIdentifier , string oldZoneIdentifier , int index )
public DragAndDropIndexChangedEventArgs ( string zoneIdentifier , string oldZoneIdentifier , int index )
{
{
ZoneIdentifier = zoneIdentifier ;
ZoneIdentifier = zoneIdentifier ;
@ -17,41 +24,17 @@ public class DragAndDropIndexChangedEventArgs : EventArgs
OldZoneIdentifier = oldZoneIdentifier ;
OldZoneIdentifier = oldZoneIdentifier ;
}
}
public string ZoneIdentifier { get ; }
# endregion
public int Index { get ; }
public string OldZoneIdentifier { get ; }
}
}
/// <summary>
/// Used to encapsulate data for a drag and drop transaction
/// </summary>
/// <typeparam name="T"></typeparam>
public class DragAndDropItemTransaction < T >
public class DragAndDropItemTransaction < T >
{
{
#region Variables
private Func < Task > _commitCallback ;
private Func < Task > _commitCallback ;
private Func < Task > _cancelCallback ;
private Func < Task > _cancelCallback ;
# endregion
/// <summary>
#region Events
/// The Item that is dragged during the transaction
/// </summary>
public T Item { get ; init ; }
/// <summary>
/// The index of the item in the current drop zone
/// </summary>
public int Index { get ; private set ; }
/// <summary>
/// The index of the item when the transaction started
/// </summary>
public int SourceIndex { get ; private set ; }
/// <summary>
/// Identifier for drop zone where the transaction started
/// </summary>
public string SourceZoneIdentifier { get ; init ; }
public string CurrentZone { get ; private set ; }
/// <summary>
/// <summary>
/// create a new instance of a drag and drop transaction encapsulating the item and source
/// create a new instance of a drag and drop transaction encapsulating the item and source
@ -101,248 +84,293 @@ public class DragAndDropItemTransaction<T>
Index = - 1 ;
Index = - 1 ;
return true ;
return true ;
}
}
}
/// <summary>
# endregion
/// Record encaplusalting data regaring a completed transaction
/// </summary>
/// <typeparam name="T">Type of dragged item</typeparam>
/// <param name="Item">The dragged item during the transaction</param>
/// <param name="DropzoneIdentifier">Identifier of the zone where the transaction started</param>
/// <param name="IndexInZone">The index of the item within in the dropzone</param>
public record ItemDropInfo < T > ( T Item , string DropzoneIdentifier , int IndexInZone ) ;
public class DragAndDropTransactionFinishedEventArgs < T > : EventArgs
{
public DragAndDropTransactionFinishedEventArgs ( DragAndDropItemTransaction < T > transaction ) :
this ( string . Empty , false , transaction )
{
}
public DragAndDropTransactionFinishedEventArgs ( string destinationDropzoneIdentifier , bool success , DragAndDropItemTransaction < T > transaction )
#region Content
{
Item = transaction . Item ;
Success = success ;
OriginatedDropzoneIdentifier = transaction . SourceZoneIdentifier ;
DestinationDropzoneIdentifier = destinationDropzoneIdentifier ;
OriginIndex = transaction . SourceIndex ;
DestinationIndex = transaction . Index ;
}
public T Item { get ; }
public bool Success { get ; }
public string OriginatedDropzoneIdentifier { get ; }
public string DestinationDropzoneIdentifier { get ; }
public int OriginIndex { get ; }
public int DestinationIndex { get ; }
}
/// <summary>
/// The container of a drag and drop zones
/// </summary>
/// <typeparam name="T">Datetype of items</typeparam>
public partial class DropContainer < T > : UIComponent
{
private DragAndDropItemTransaction < T > _transaction ;
protected string Classname = >
new CssBuilder ( "drop-container" )
. AddClass ( AdditionalClassList )
. Build ( ) ;
/// <summary>
/// Child content of component. This should include the drop zones
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.Appearance)]
public RenderFragment ChildContent { get ; set ; }
/// <summary>
/// <summary>
/// The items that can be drag and dropped within the container
/// The Item that is dragged during the transaction
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.Items)]
public IEnumerable < T > Items { get ; set ; }
/// <summary>
/// The render fragment (template) that should be used to render the items within a drop zone
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.Items)]
public RenderFragment < T > ItemRenderer { get ; set ; }
/// <summary>
/// The method is used to determinate if an item can be dropped within a drop zone
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.Items)]
public Func < T , string , bool > ItemsSelector { get ; set ; }
/// <summary>
/// Callback that indicates that an item has been dropped on a drop zone. Should be used to update the "status" of the data item
/// </summary>
/// </summary>
[Parameter]
public T Item { get ; init ; }
[Category(CategoryTypes.DropZone.Items)]
public EventCallback < ItemDropInfo < T > > ItemDropped { get ; set ; }
/// <summary>
/// <summary>
/// The method is used to determinate if an item can be dropped within a drop zone
/// The index of the item in the current drop zone
/// </summary>
/// </summary>
[Parameter]
public int Index { get ; private set ; }
[Category(CategoryTypes.DropZone.DropRules)]
public Func < T , string , bool > CanDrop { get ; set ; }
/// <summary>
/// <summary>
/// The CSS class(es), that is applied to drop zones that are a valid target for drag and drop transaction
/// The index of the item when the transaction started
/// </summary>
/// </summary>
[Parameter]
public int SourceIndex { get ; private set ; }
[Category(CategoryTypes.DropZone.DropRules)]
public string CanDropClass { get ; set ; }
/// <summary>
/// <summary>
/// The CSS class(es), that is applied to drop zones that are NOT valid target for drag and drop transaction
/// Identifier for drop zone where the transaction started
/// </summary>
/// </summary>
[Parameter]
public string SourceZoneIdentifier { get ; init ; }
[Category(CategoryTypes.DropZone.DropRules)]
public string NoDropClass { get ; set ; }
/// <summary>
public string CurrentZone { get ; private set ; }
/// If true, drop classes CanDropClass <see cref="CanDropClass"/> or NoDropClass <see cref="NoDropClass"/> or applied as soon, as a transaction has started
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.DropRules)]
public bool ApplyDropClassesOnDragStarted { get ; set ; } = false ;
/// <summary>
# endregion
/// The method is used to determinate if an item should be disabled for dragging. Defaults to allow all items
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.Disabled)]
public Func < T , bool > ItemIsDisabled { get ; set ; }
/// <summary>
#region Styling
/// If a drop item is disabled (determinate by <see cref="ItemIsDisabled"/>). This class is applied to the element
# endregion
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.Disabled)]
public string DisabledClass { get ; set ; } = "disabled" ;
/// <summary>
#region Behavior
/// An additional class that is applied to the drop zone where a drag operation started
# endregion
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.DraggingClass)]
public string DraggingClass { get ; set ; }
/// <summary>
#region Lifecycle
/// An additional class that is applied to an drop item, when it is dragged
# endregion
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.DraggingClass)]
public string ItemDraggingClass { get ; set ; }
public event EventHandler < DragAndDropItemTransaction < T > > TransactionStarted ;
public event EventHandler < DragAndDropIndexChangedEventArgs > TransactionIndexChanged ;
public event EventHandler < DragAndDropTransactionFinishedEventArgs < T > > TransactionEnded ;
}
public event EventHandler RefreshRequested ;
public void StartTransaction ( T item , string identifier , int index , Func < Task > commitCallback , Func < Task > cancelCallback )
/// <summary>
{
/// Used to encapsulate data for a drag and drop transaction
_transaction = new DragAndDropItemTransaction < T > ( item , identifier , index , commitCallback , cancelCallback ) ;
/// </summary>
TransactionStarted ? . Invoke ( this , _transaction ) ;
/// <typeparam name="T"></typeparam>
}
public T GetTransactionItem ( ) = > _transaction . Item ;
public bool TransactionInProgress ( ) = > _transaction ! = null ;
/// <summary>
public string GetTransactionOrignZoneIdentiifer ( ) = > _transaction ? . SourceZoneIdentifier ? ? string . Empty ;
/// Record encaplusalting data regaring a completed transaction
public string GetTransactionCurrentZoneIdentiifer ( ) = > _transaction ? . CurrentZone ? ? string . Empty ;
/// </summary>
public bool IsTransactionOriginatedFromInside ( string identifier ) = > _transaction . SourceZoneIdentifier = = identifier ;
/// <typeparam name="T">Type of dragged item</typeparam>
/// <param name="Item">The dragged item during the transaction</param>
/// <param name="DropzoneIdentifier">Identifier of the zone where the transaction started</param>
/// <param name="IndexInZone">The index of the item within in the dropzone</param>
public record ItemDropInfo < T > ( T Item , string DropzoneIdentifier , int IndexInZone ) ;
public int GetTransactionIndex ( ) = > _transaction ? . Index ? ? - 1 ;
public class DragAndDropTransactionFinishedEventArgs < T > : EventArgs
public bool IsItemMovedDownwards ( ) = > _transaction . Index > _transaction . SourceIndex ;
public bool HasTransactionIndexChanged ( )
{
{
if ( _transaction = = null )
public DragAndDropTransactionFinishedEventArgs ( DragAndDropItemTransaction < T > transaction ) :
this ( string . Empty , false , transaction )
{
{
return false ;
}
}
if ( _transaction . CurrentZone ! = _transaction . SourceZoneIdentifier )
public DragAndDropTransactionFinishedEventArgs ( string destinationDropzoneIdentifier , bool success , DragAndDropItemTransaction < T > transaction )
{
{
return true ;
Item = transaction . Item ;
Success = success ;
OriginatedDropzoneIdentifier = transaction . SourceZoneIdentifier ;
DestinationDropzoneIdentifier = destinationDropzoneIdentifier ;
OriginIndex = transaction . SourceIndex ;
DestinationIndex = transaction . Index ;
}
}
return _transaction . Index ! = _transaction . SourceIndex ;
public T Item { get ; }
public bool Success { get ; }
public string OriginatedDropzoneIdentifier { get ; }
public string DestinationDropzoneIdentifier { get ; }
public int OriginIndex { get ; }
public int DestinationIndex { get ; }
}
}
public bool IsOrign ( int index , string identifier )
/// <summary>
/// The container of a drag and drop zones
/// </summary>
/// <typeparam name="T">Datetype of items</typeparam>
public partial class DropContainer < T > : UIComponent
{
{
if ( _transaction = = null )
private DragAndDropItemTransaction < T > _transaction ;
protected string Classname = >
new CssBuilder ( "drop-container" )
. AddClass ( AdditionalClassList )
. Build ( ) ;
/// <summary>
/// Child content of component. This should include the drop zones
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.Appearance)]
public RenderFragment ChildContent { get ; set ; }
/// <summary>
/// The items that can be drag and dropped within the container
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.Items)]
public IEnumerable < T > Items { get ; set ; }
/// <summary>
/// The render fragment (template) that should be used to render the items within a drop zone
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.Items)]
public RenderFragment < T > ItemRenderer { get ; set ; }
/// <summary>
/// The method is used to determinate if an item can be dropped within a drop zone
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.Items)]
public Func < T , string , bool > ItemsSelector { get ; set ; }
/// <summary>
/// Callback that indicates that an item has been dropped on a drop zone. Should be used to update the "status" of the data item
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.Items)]
public EventCallback < ItemDropInfo < T > > ItemDropped { get ; set ; }
/// <summary>
/// The method is used to determinate if an item can be dropped within a drop zone
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.DropRules)]
public Func < T , string , bool > CanDrop { get ; set ; }
/// <summary>
/// The CSS class(es), that is applied to drop zones that are a valid target for drag and drop transaction
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.DropRules)]
public string CanDropClass { get ; set ; }
/// <summary>
/// The CSS class(es), that is applied to drop zones that are NOT valid target for drag and drop transaction
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.DropRules)]
public string NoDropClass { get ; set ; }
/// <summary>
/// If true, drop classes CanDropClass <see cref="CanDropClass"/> or NoDropClass <see cref="NoDropClass"/> or applied as soon, as a transaction has started
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.DropRules)]
public bool ApplyDropClassesOnDragStarted { get ; set ; } = false ;
/// <summary>
/// The method is used to determinate if an item should be disabled for dragging. Defaults to allow all items
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.Disabled)]
public Func < T , bool > ItemIsDisabled { get ; set ; }
/// <summary>
/// If a drop item is disabled (determinate by <see cref="ItemIsDisabled"/>). This class is applied to the element
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.Disabled)]
public string DisabledClass { get ; set ; } = "disabled" ;
/// <summary>
/// An additional class that is applied to the drop zone where a drag operation started
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.DraggingClass)]
public string DraggingClass { get ; set ; }
/// <summary>
/// An additional class that is applied to an drop item, when it is dragged
/// </summary>
[Parameter]
[Category(CategoryTypes.DropZone.DraggingClass)]
public string ItemDraggingClass { get ; set ; }
public event EventHandler < DragAndDropItemTransaction < T > > TransactionStarted ;
public event EventHandler < DragAndDropIndexChangedEventArgs > TransactionIndexChanged ;
public event EventHandler < DragAndDropTransactionFinishedEventArgs < T > > TransactionEnded ;
public event EventHandler RefreshRequested ;
public void StartTransaction ( T item , string identifier , int index , Func < Task > commitCallback , Func < Task > cancelCallback )
{
{
return false ;
_transaction = new DragAndDropItemTransaction < T > ( item , identifier , index , commitCallback , cancelCallback ) ;
TransactionStarted ? . Invoke ( this , _transaction ) ;
}
}
if ( identifier ! = _transaction . SourceZoneIdentifier )
public T GetTransactionItem ( ) = > _transaction . Item ;
public bool TransactionInProgress ( ) = > _transaction ! = null ;
public string GetTransactionOrignZoneIdentiifer ( ) = > _transaction ? . SourceZoneIdentifier ? ? string . Empty ;
public string GetTransactionCurrentZoneIdentiifer ( ) = > _transaction ? . CurrentZone ? ? string . Empty ;
public bool IsTransactionOriginatedFromInside ( string identifier ) = > _transaction . SourceZoneIdentifier = = identifier ;
public int GetTransactionIndex ( ) = > _transaction ? . Index ? ? - 1 ;
public bool IsItemMovedDownwards ( ) = > _transaction . Index > _transaction . SourceIndex ;
public bool HasTransactionIndexChanged ( )
{
{
return false ;
if ( _transaction = = null )
}
{
return false ;
}
return _transaction . SourceIndex = = index | | _transaction . SourceIndex - 1 = = index ;
if ( _transaction . CurrentZone ! = _transaction . SourceZoneIdentifier )
}
{
return true ;
}
public async Task CommitTransaction ( string dropzoneIdentifier , bool reorderIsAllowed )
return _transaction . Index ! = _transaction . SourceIndex ;
{
}
await _transaction . Commit ( ) ;
var index = - 1 ;
public bool IsOrign ( int index , string identifier )
if ( reorderIsAllowed = = true )
{
{
index = GetTransactionIndex ( ) + 1 ;
if ( _transaction = = null )
if ( _transaction . SourceZoneIdentifier = = _transaction . CurrentZone & & IsItemMovedDownwards ( ) = = true )
{
return false ;
}
if ( identifier ! = _transaction . SourceZoneIdentifier )
{
{
index - = 1 ;
return false ;
}
}
return _transaction . SourceIndex = = index | | _transaction . SourceIndex - 1 = = index ;
}
}
await ItemDropped . InvokeAsync ( new ItemDropInfo < T > ( _transaction . Item , dropzoneIdentifier , index ) ) ;
public async Task CommitTransaction ( string dropzoneIdentifier , bool reorderIsAllowed )
var transactionFinishedEventArgs = new DragAndDropTransactionFinishedEventArgs < T > ( dropzoneIdentifier , true , _transaction ) ;
{
_transaction = null ;
await _transaction . Commit ( ) ;
TransactionEnded ? . Invoke ( this , transactionFinishedEventArgs ) ;
var index = - 1 ;
}
if ( reorderIsAllowed = = true )
{
index = GetTransactionIndex ( ) + 1 ;
if ( _transaction . SourceZoneIdentifier = = _transaction . CurrentZone & & IsItemMovedDownwards ( ) = = true )
{
index - = 1 ;
}
}
public async Task CancelTransaction ( )
await ItemDropped . InvokeAsync ( new ItemDropInfo < T > ( _transaction . Item , dropzoneIdentifier , index ) ) ;
{
var transactionFinishedEventArgs = new DragAndDropTransactionFinishedEventArgs < T > ( dropzoneIdentifier , true , _transaction ) ;
await _transaction . Cancel ( ) ;
_transaction = null ;
var transactionFinishedEventArgs = new DragAndDropTransactionFinishedEventArgs < T > ( _transaction ) ;
TransactionEnded ? . Invoke ( this , transactionFinishedEventArgs ) ;
_transaction = null ;
}
TransactionEnded ? . Invoke ( this , transactionFinishedEventArgs ) ;
}
public void UpdateTransactionIndex ( int index )
public async Task CancelTransaction ( )
{
{
var changed = _transaction . UpdateIndex ( index ) ;
await _transaction . Cancel ( ) ;
if ( changed = = false ) { return ; }
var transactionFinishedEventArgs = new DragAndDropTransactionFinishedEventArgs < T > ( _transaction ) ;
_transaction = null ;
TransactionEnded ? . Invoke ( this , transactionFinishedEventArgs ) ;
}
TransactionIndexChanged ? . Invoke ( this , new DragAndDropIndexChangedEventArgs ( _transaction . CurrentZone , _transaction . CurrentZone , _transaction . Index ) ) ;
public void UpdateTransactionIndex ( int index )
}
{
var changed = _transaction . UpdateIndex ( index ) ;
if ( changed = = false ) { return ; }
internal void UpdateTransactionZone ( string identifier )
TransactionIndexChanged ? . Invoke ( this , new DragAndDropIndexChangedEventArgs ( _transaction . CurrentZone , _transaction . CurrentZone , _transaction . Index ) ) ;
{
}
var oldValue = _transaction . CurrentZone ;
var changed = _transaction . UpdateZone ( identifier ) ;
if ( changed = = false ) { return ; }
TransactionIndexChanged ? . Invoke ( this , new DragAndDropIndexChangedEventArgs ( _transaction . CurrentZone , oldValue , _transaction . Index ) ) ;
internal void UpdateTransactionZone ( string identifier )
}
{
var oldValue = _transaction . CurrentZone ;
var changed = _transaction . UpdateZone ( identifier ) ;
if ( changed = = false ) { return ; }
TransactionIndexChanged ? . Invoke ( this , new DragAndDropIndexChangedEventArgs ( _transaction . CurrentZone , oldValue , _transaction . Index ) ) ;
}
/// <summary>
/// Refreshes the dropzone and all items within. This is neded in case of adding items to the collection or changed values of items
/// <summary>
/// </summary>
/// Refreshes the dropzone and all items within. This is neded in case of adding items to the collection or changed values of items
public void Refresh ( ) = > RefreshRequested ? . Invoke ( this , EventArgs . Empty ) ;
/// </summary>
public void Refresh ( ) = > RefreshRequested ? . Invoke ( this , EventArgs . Empty ) ;
}
}