Skip to content
IzzyBlazor 0.1.0-alpha.20

Enter all results ↑ ↓ move Esc close

Reordering

Since 0.1.0-alpha.20
Users reorder items by dragging or from the keyboard, and the list posts or binds the new order.

Static server-side rendering: no circuit, no WebAssembly.

Every move can be made three ways: by dragging, from a menu of moves, or with Alt+arrow keys. The menu also works with JavaScript off, where each move posts the form. To move items between lists, such as the columns of a board, put the lists in a DropZoneGroup.

Reordering, and saving the order

A reorderable list gives each item a drag handle with a menu of moves. Set Reorderable. On static pages, give the list a Name and pass the posted ListOrder back as Order; Order.Arrange puts your items in the posted order. On interactive pages, bind @bind-Items to keep the new order.

Preview

  • Draft
  • Review
  • Approve
  • Publish

Code

SaveOrder.razor
@* On a static page the list posts with its form: Order holds the posted keys and any menu move,
   and the list shows them. Save arranges the steps when the form's own button posted. *@
<Stack Gap="3" MaxWidth="24.Rem" Grow>
    <Form Model="this" FormName="list-view-steps" OnValidSubmit="Save">
        <ListView @bind-Items="_steps" ItemKey="s => s.Id" ItemText="s => s.Title" Reorderable Name="order" Order="Order" Bordered />
        <FormActions><Button Submit>Save order</Button></FormActions>
    </Form>
    @if (_saved is not null)
    {
        <TextBlock Size="TextSize.Sm" Color="Color.Muted">@_saved</TextBlock>
    }
</Stack>

@code {
    [SupplyParameterFromForm(FormName = "list-view-steps", Name = "order")]
    private ListOrder? Order { get; set; }

    private IReadOnlyList<Step> _steps = [new("1", "Draft"), new("2", "Review"), new("3", "Approve"), new("4", "Publish")];
    private string? _saved;

    private void Save()
    {
        // A menu move posts too; only the Save button saves.
        if (Order?.Move is not null)
        {
            return;
        }

        if (Order is not null)
        {
            _steps = Order.Arrange(_steps, s => s.Id);
        }

        _saved = $"Saved: {string.Join(", ", _steps.Select(s => s.Title))}.";
    }
}

Static: Drag a handle, or use its menu or Alt+arrow keys. Items move on the page and the move is announced. Save order posts the new order.

Dragging the whole item

To let a drag start anywhere on an item, such as a card, set DragWholeItem. With a mouse, the drag starts after a small move; on touch, after a long press, so a swipe still scrolls the page. Links, buttons and fields inside the item still work. If the list has an ItemMenu, there are no handles: each ⋯ menu ends with a Move submenu, and Alt+arrow keys work on the ⋯ button.

Preview

  • Sales

    Revenue this month against last.

    Open
  • Orders

    Open, shipped and returned.

    Open
  • Traffic

    Visitors by source.

    Open
  • Stock

    Items running low.

    Open
  • Support

    Tickets waiting for a reply.

    Open
  • Team

    Who's in today.

    Open

Code

DragCards.razor
@* DragWholeItem: each card drags from anywhere on it, with a mouse after a small move and on touch
   after a long press, so a swipe still scrolls. With an ItemMenu there are no handles: each card's
   ⋯ menu ends with Move, a submenu of Move up, Move down, Move to top and Move to bottom. Open stands in for
   a link to the tile's report: here it names the tile under the list. *@
@inject NavigationManager Navigation
<Stack Gap="3" Grow>
    <Form Model="this" FormName="list-view-tiles" OnValidSubmit="Save">
        <ListView @bind-Items="_tiles" ItemKey="t => t.Id" ItemLabel="t => t.Title" Reorderable DragWholeItem Name="tiles" Order="Order"
                  Columns="3" Bordered Context="tile">
            <ChildContent>
                <Stack Gap="1">
                    <Heading Level="3" Size="TextSize.Md">@tile.Title</Heading>
                    <TextBlock Size="TextSize.Sm" Color="Color.Muted">@tile.Description</TextBlock>
                </Stack>
            </ChildContent>
            <ItemMenu>
                <MenuItem Href="@Navigation.GetUriWithQueryParameter("tile", tile.Id)" Icon="Lucide.ExternalLink">Open</MenuItem>
            </ItemMenu>
        </ListView>
        <FormActions><Button Submit>Save layout</Button></FormActions>
    </Form>
    @if (Opened is not null && _tiles.FirstOrDefault(t => t.Id == Opened) is { } open)
    {
        <TextBlock Size="TextSize.Sm" Color="Color.Muted">Opened: @open.Title.</TextBlock>
    }
    @if (_saved is not null)
    {
        <TextBlock Size="TextSize.Sm" Color="Color.Muted">@_saved</TextBlock>
    }
</Stack>

@code {
    [SupplyParameterFromQuery(Name = "tile")] private string? Opened { get; set; }

    [SupplyParameterFromForm(FormName = "list-view-tiles", Name = "tiles")]
    private ListOrder? Order { get; set; }

    private IReadOnlyList<Tile> _tiles =
    [
        new("sales", "Sales", "Revenue this month against last."),
        new("orders", "Orders", "Open, shipped and returned."),
        new("traffic", "Traffic", "Visitors by source."),
        new("stock", "Stock", "Items running low."),
        new("support", "Support", "Tickets waiting for a reply."),
        new("team", "Team", "Who's in today."),
    ];
    private string? _saved;

    private void Save()
    {
        // A menu move posts too; only the Save button saves.
        if (Order?.Move is not null)
        {
            return;
        }

        if (Order is not null)
        {
            _tiles = Order.Arrange(_tiles, t => t.Id);
        }

        _saved = $"Saved: {string.Join(", ", _tiles.Select(t => t.Title))}.";
    }
}

Static: Drag a card, or press and hold one on a phone; the other cards move aside to show where it will land. Move in its ⋯ menu and Alt+arrow keys on the ⋯ button move it too. Save layout posts the new order.

Connection lost

Rejoining the server…