I want to create draggable table rows with MudTable. To be able to set the draggable attribute on a tr element, I used the ChildRowContent and left the RowTemplate empty, since RowTemplate will generate the tr element automatically having no ability to set attributes like draggable.
<ChildRowContent>
<MudTr draggable="true"
ondragover="event.preventDefault();"
ondragstart="event.dataTransfer.setData('', event.target.id);"
@ondrop="HandleDrop"
@ondragenter="HandleDragEnter"
@ondragleave="HandleDragLeave">
<MudTd>Col</MudTd>
</MudTr>
</ChildRowContent>
But unfortunately when I drag a table row It seems to drag the whole table. Is there an easier way to achieve this with MudTable?
It seems that the problem was caused by a MudMenu element inside a column, which was rendering a with a "map-ripple" class.
By setting <MudMenu DisableRipple="true">
this issue can be resolved. Now when I drag a row it will only drag the row and not other parts of the UI.
Edit: Here is the full example code:
<div>
<MudTable T="GridModel"
Items="@Items"
Dense
Hover>
<ColGroup>
<col class="action-column" />
<col class="action-column" />
</ColGroup>
<HeaderContent>
<MudTh>
</MudTh>
<MudTh>
#
</MudTh>
</HeaderContent>
<RowTemplate>
</RowTemplate>
<ChildRowContent>
<MudTr ondragover="event.preventDefault();"
@ondragenter="() => DragEnter(context.Index, false)"
@ondrop="async () => await DropAsync(context.Index)"
Class="@($"{(IsEntering(context.Index) && draggedItem?.Index != context.Index - 1 ? "enter" : "hide")} {(enterAfterDropZone == false ? "dropzone" : "")}")">
<MudTd colspan="2">
</MudTd>
</MudTr>
<MudTr
draggable="@IsDraggable()"
@ondragstart="() => DragStart(context)"
@ondragenter="() => DragEnter(context.Index, false)"
@ondrop="() => DropAsync(context.Index)"
@ondragend="() => DragEnd()"
Class="grabable">
<MudTd>
<MudMenu Icon="@Icons.Filled.MoreVert" Size="Size.Small" DisableRipple="true">
</MudMenu>
</MudTd>
<MudTd>
@(context.Index + 1)
</MudTd>
</MudTr>
<MudTr ondragover="event.preventDefault();"
@ondragenter="() => DragEnter(context.Index, true)"
@ondrop="() => DropAsync(draggedItem?.Index < context.Index ? context.Index : context.Index + 1)"
Class="@($"{(IsEntering(context.Index) && draggedItem?.Index != context.Index + 1 ? "enter" : "hide")} {(enterAfterDropZone == true ? "dropzone" : "")}")">
<MudTd colspan="2">
</MudTd>
</MudTr>
</ChildRowContent>
</MudTable>
</div>
public partial class Component
{
[Parameter]
[EditorRequired]
public IEnumerable<GridModel> Items { get; set; } = null!;
[Parameter]
public bool Disabled { get; set; } = false;
private GridModel? draggedItem;
private int? enterIndex;
private bool? enterAfterDropZone;
private void DragStart(GridModel model)
{
draggedItem = model;
}
private void DragEnter(int index, bool isAfterDropZone)
{
if(draggedItem?.Index == index)
{
enterIndex = null;
enterAfterDropZone = null;
}
else
{
enterIndex = index;
enterAfterDropZone = isAfterDropZone;
}
}
private async Task DropAsync(int index)
{
DragEnd();
}
private void DragEnd()
{
draggedItem = null;
enterIndex = null;
enterAfterDropZone = null;
}
private bool IsEntering(int index)
{
return enterIndex == index;
}
private string IsDraggable()
{
return "true";
}
}