Search code examples
htmldrag-and-dropblazormudblazor

MudTable make table rows draggable


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?


Solution

  • 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";
        }
    }