Blazor - Criando um quadro Kanban - II


   Neste artigo vamos continuar apresentando conceitos importantes relacionados com o tratamento de eventos no Blazor e a seguir vamos criar uma aplicação simples que simula um quadro Kanban.

Continuando o artigo anterior vamos criar o projeto Blazor WebAssembly KanbanBoard.

Criando o projeto WebAssembly

Abra o VS 2022 Community e acione a opção Create New Project selecionando o template Blazor WebAssembly App infomando o nome KanbanBoard;

A seguir defina as configurações do projeto conforme mostra a figura abaixo:

Clique no botão Create para criar o projeto.

Podemos remover do projeto os componentes FetchData.razor e Counter.razor e suas dependências deixando apenas o componente Index.razor na pasta Pages.

Vamos criar no projeto a pasta Models onde vamos definir a classe TaskItem que representa o nosso domínio e a enumeração TaskPriority que define 3 níveis de prioridade.

1- TaskItem

public class TaskItem
{
    public string? TaskName { get; set; }
    public TaskPriority Priority { get; set; }
}

2- TaskPriority

public enum TaskPriority
{
    High,
    Mid ,
    Low
}

Agora vamos alterar o código do componente MainLayout.razor conforme abaixo:

<main class="container">
    @Body
</main>

Este código adiciona um contêiner ao redor do corpo da página.  Neste projeto, usaremos o sistema de grade do Bootstrap para definir o layout do nosso conteúdo. Ele se baseia em uma série de contêineres, linhas e colunas.  Adicionaremos as linhas e colunas mais tarde.

Criando o componente DropZone

Vamos criar na pasta Shared do projeto o componente razor DropZone que vamos usar para criar os quadros kanban no projeto. Neste componente vamos poder arrastar e soltar as tarefas entre os 3 quadros kanban que iremos criar.

@using KanbanBoard.Models
<div class="col">
    <h2 style="">@Priority.ToString() Prioridade</h2>
    <div class="dropzone"
         ondragover="event.preventDefault();"
         @ondrop="OnDropHandler">
        @foreach (var item in TaskItems
            .Where(q => q.Priority == Priority))
            {
             <div class="draggable" 
                 draggable="true" 
                 @ondragstart="@(() => OnDragStartHandler(item))"> @item.TaskName
                 <span class="badge text-bg-secondary">
                    @item.Priority
                 </span>
                 </div>
            }
    </div>
</div>

@code
{
    [Parameter]
    public List<TaskItem> TaskItems { get; set; } = new();

    [Parameter]
    public TaskPriority Priority { get; set; }

    [Parameter]
    public EventCallback<TaskPriority> OnDrop { get; set; }

    [Parameter]
    public EventCallback<TaskItem> OnStartDrag { get; set; }
    private void OnDropHandler()
    {
        OnDrop.InvokeAsync(Priority);
    }

    private void OnDragStartHandler(TaskItem task)
    {
        OnStartDrag.InvokeAsync(task);
    }
}

Neste código vamos usar os recursos do drag and drop ou seja arrastar e soltar:

Estamos definindo o componente Dropzone por sua prioridade e permitindo que os elementos sejam jogados nele, evitando o valor padrão do evento ondragover.

O método OnDropHandler é chamado quando um elemento é solto no Dropzone, e, por fim, ele percorre todos os itens da classe TaskItems com a Prioridade correspondente.

O parâmetro TaskItems é usado para acompanhar as tarefas que foram descartadas no Dropzone e o parâmetro Priority é usado para indicar a prioridade das tarefas que estão no Dropzone.

O evento OnDrop indica o evento que é invocado quando uma tarefa for solta no Dropzone e o evento OnStartDrag indica o evento que é invocado quando uma tarefa é arrastada do Dropzone.

Vamos agora definir um estilo associado a este componente criando o arquivo Dropzone.razor.css :

.draggable {
    margin-bottom: 10px;
    padding: 10px 25px;
    border: 1px solid #424d5c;
    background: #ff6a00;
    color: #ffffff;
    border-radius: 5px;
    cursor: grab;
}

.draggable:active {
        cursor: grabbing;
    }
.dropzone {
    padding: .75rem;
    border: 2px solid black;
    min-height: 20rem;
}

Criando o componente NewTask

Vamos criar na pasta Shared do projeto o componente razor NewTask que permite incluir novas tarefas no quadro Kanban.

<div class="row pt-3" >
    <div class="input-group mb-3">
        <label class="input-group-text"
          for="inputTask">
            Tarefa
        </label>
        <input @ref="taskInput"
               type="text"
               id="inputTask"
               class="form-control"
               @bind-value="@taskName"
               @attributes="InputParameters" />
        <button type="button"
                class="btn btn-outline-secondary"
                @onclick="OnClickHandlerAsync">
            Incluir tarefa
        </button>
    </div>
</div>
@code{
    private string? taskName;

    private ElementReference taskInput;

    [Parameter(CaptureUnmatchedValues = true)]
    public Dictionary<string, object>? 

    InputParameters{ get; set; }

    [Parameter]
    public EventCallback<string> OnSubmit { get; set; }
  private async Task OnClickHandlerAsync()
  {
      if (!string.IsNullOrWhiteSpace(taskName))
      {
         await OnSubmit.InvokeAsync(taskName);
         taskName = null;
         await taskInput.FocusAsync();
      }
  }
}

No código deste componente temos :

A inclusão de uma Label, uma caixa de texto e um button. Onde a caixa de texto inclui um atributo @ref que usaremos mais tarde para definir o foco para a caixa de texto.

A seguir invocamos o método OnSubmit, definimos o campo taskName como nulo e colocamos o foco para o objeto taskInput usando o método FocusAsync.

Usando os componentes criados

Vamos agora usar os componentes DropZone e NewTask que foram criados alterando o código do componente Index.razor na pasta Pages.

@page "/"
<PageTitle>Quadro Kanban</PageTitle>
<NewTask OnSubmit="AddTask"
         @attributes="InputAttributes" />
<div class="row">
    <Dropzone Priority="TaskPriority.High"
              TaskItems="TaskItems"
              OnDrop="OnDrop"
              OnStartDrag="OnStartDrag" />
    <Dropzone Priority="TaskPriority.Mid"
              TaskItems="TaskItems"
              OnDrop="OnDrop"
              OnStartDrag="OnStartDrag" />
    <Dropzone Priority="TaskPriority.Low"
              TaskItems="TaskItems"
              OnDrop="OnDrop"
              OnStartDrag="OnStartDrag" />
</div>

@code {

    public TaskItem? CurrentItem;

    List<TaskItem> TaskItems = new();

    protected override void OnInitialized()
    {
        TaskItems.Add(new TaskItem
        {
            TaskName = "Revisar código",
            Priority = TaskPriority.High
        });
       TaskItems.Add(new TaskItem
       {
           TaskName = "Ler livro",
           Priority = TaskPriority.Mid
        });
        TaskItems.Add(new TaskItem
        {
            TaskName = "Exercício",
            Priority = TaskPriority.Low
        });    
    }
    private void OnStartDrag(TaskItem item)
    {
        CurrentItem = item;
    }
    private void OnDrop(TaskPriority priority)
    {
        CurrentItem!.Priority = priority;
    }

    public Dictionary<string, object> InputAttributes = new ()
    {
        { "maxlength", "25" },
        { "placeholder", "Nova tarefa" },
        { "title", "Aqui você inclui novas tarefas." }
    };

    private void AddTask(string taskName)
    {
        var taskItem = new TaskItem()
            {
                TaskName = taskName,
                Priority = TaskPriority.High
            };
        TaskItems.Add(taskItem);
    }
}

Vamos entender o código acima:

Estamos usando o componente NewTask para apresentara a caixa de entrada onde o usuário vai informar o nome da tarefa usando os recursos do attribute splatting.

A seguir estamos adicionando três componentes DropZone um para cada prioridade.

No bloco de código estamos inicializando 3 tarefas , cada uma com prioridade diferente.

No evento OnstartDrag definimos o valor de CurrentItem para o item que está sendo arrastado no momento. Usaremos esse valor quando o item for subseqüentemente descartado. O componente Dropzone invoca este método quando o evento @ondragstart é acionado.

No evento OnDrop definios a propriedade Priority do CurrentItem para a prioridade associada ao Dropzone no qual ele é solto. O componente Dropzone invoca este método quando o evento @ondrop for acionado.

Usamos a classe Dictionary para definir as propriedades de Inputattributes.

No método AddTask definimos a prioridade do novo item como High e adicionamos a tarefa ao objeto TaskItems.

Executando o projeto iremos obter o seguinte resultado:

Pegue o código do projeto aqui :  KanbanBoard.zip ...

"Regozijai-vos sempre.
Orai sem cessar.
Em tudo dai graças, porque esta é a vontade de Deus em Cristo Jesus para convosco."
1 Tessalonicenses 5:16-18

Referências:


José Carlos Macoratti