Vitral 0.2
Dados

Taskboard

Um quadro kanban de cards em colunas e raias, arrastados com mouse, toque ou teclado, com limites de trabalho em andamento.

Importação

main.ts
import { Taskboard } from '@vitral/vue';

Básico

Uma lista plana de tarefas, cada uma dizendo sua coluna. In progress comporta no máximo duas.

Backlog1 cartões
To do2 cartões
In progress2 de 2 cartões
Done1 cartões
  • Draft the release notesDocs
  • Audit colour contrastDesignHigh
  • Virtual scrolling in SelectWeb
  • Keyboard grid for DataGridWebHigh
  • Theme editor prototypeDesign
  • Publish 0.1WebHighLocked
Move a card to see the card-move event.
<script setup lang="ts">import { Button, Tag, Taskboard, type TaskboardCardMoveEvent, type TaskboardColumn } from '@vitral/vue';import { ref } from 'vue'; interface Task {    id: number;    title: string;    status: string;    team: string;    priority: 'low' | 'high';    locked?: boolean;} const columns = ref<TaskboardColumn[]>([    { key: 'backlog', title: 'Backlog', color: 'var(--vt-chart-8)' },    { key: 'todo', title: 'To do', color: 'var(--vt-chart-4)' },    { key: 'doing', title: 'In progress', wipLimit: 2, color: 'var(--vt-chart-5)' },    { key: 'done', title: 'Done', color: 'var(--vt-chart-3)' }]); const tasks = ref<Task[]>([    { id: 1, title: 'Draft the release notes', status: 'backlog', team: 'Docs', priority: 'low' },    { id: 2, title: 'Audit colour contrast', status: 'todo', team: 'Design', priority: 'high' },    { id: 3, title: 'Virtual scrolling in Select', status: 'todo', team: 'Web', priority: 'low' },    { id: 4, title: 'Keyboard grid for DataGrid', status: 'doing', team: 'Web', priority: 'high' },    { id: 5, title: 'Theme editor prototype', status: 'doing', team: 'Design', priority: 'low' },    { id: 6, title: 'Publish 0.1', status: 'done', team: 'Web', priority: 'high', locked: true }]); let next = 7;function add(column: string | number, team = 'Web') {    tasks.value = [...tasks.value, { id: next, title: `New task ${next++}`, status: String(column), team, priority: 'low' }];} const log = ref<string[]>([]);function onMove(event: TaskboardCardMoveEvent) {    const task = event.item as Task;    log.value = [`${task.title}: ${event.from.column} → ${event.to.column} (#${event.to.index + 1}, ${event.via})`, ...log.value].slice(0, 3);}</script> <template>    <Taskboard v-model:columns="columns" v-model:items="tasks" column-field="status" style="width: 100%" @card-move="onMove">        <template #card="{ item, locked }">            <div style="display: flex; flex-direction: column; gap: 0.5rem">                <span>{{ (item as Task).title }}</span>                <span style="display: flex; gap: 0.375rem; align-items: center">                    <Tag :value="(item as Task).team" severity="secondary" />                    <Tag v-if="(item as Task).priority === 'high'" value="High" severity="danger" />                    <small v-if="locked" style="color: var(--vt-text-muted-color)">Locked</small>                </span>            </div>        </template>        <template #add-card="{ column }">            <Button label="Add task" icon="plus" variant="text" size="small" severity="secondary" fluid @click="add(column.key)" />        </template>    </Taskboard>    <small style="color: var(--vt-text-muted-color)">{{ log.length ? log.join(' · ') : 'Move a card to see the card-move event.' }}</small></template>

Raias

As mesmas tarefas divididas por equipe. Page Up e Page Down levam um card pego de uma raia para outra; uma raia se recolhe pelo cabeçalho.

Backlog1 cartões
To do2 cartões
In progress2 de 2 cartões
Done1 cartões
Docs1 tasks
  • Draft the release notes
    Nenhum cartão
      Nenhum cartão
        Nenhum cartão
        Design2 tasks
          Nenhum cartão
          • Audit colour contrast
          • Theme editor prototype
            Nenhum cartão
            Web3 tasks
              Nenhum cartão
              • Virtual scrolling in Select
              • Keyboard grid for DataGrid
              • Publish 0.1
              <script setup lang="ts">import { Taskboard, type TaskboardColumn } from '@vitral/vue';import { ref } from 'vue'; interface Task {    id: number;    title: string;    status: string;    team: string;    priority: 'low' | 'high';    locked?: boolean;} const columns = ref<TaskboardColumn[]>([    { key: 'backlog', title: 'Backlog', color: 'var(--vt-chart-8)' },    { key: 'todo', title: 'To do', color: 'var(--vt-chart-4)' },    { key: 'doing', title: 'In progress', wipLimit: 2, color: 'var(--vt-chart-5)' },    { key: 'done', title: 'Done', color: 'var(--vt-chart-3)' }]); const tasks = ref<Task[]>([    { id: 1, title: 'Draft the release notes', status: 'backlog', team: 'Docs', priority: 'low' },    { id: 2, title: 'Audit colour contrast', status: 'todo', team: 'Design', priority: 'high' },    { id: 3, title: 'Virtual scrolling in Select', status: 'todo', team: 'Web', priority: 'low' },    { id: 4, title: 'Keyboard grid for DataGrid', status: 'doing', team: 'Web', priority: 'high' },    { id: 5, title: 'Theme editor prototype', status: 'doing', team: 'Design', priority: 'low' },    { id: 6, title: 'Publish 0.1', status: 'done', team: 'Web', priority: 'high', locked: true }]);</script> <template>    <Taskboard v-model:columns="columns" v-model:items="tasks" column-field="status" lane-field="team" :reorder-columns="false" style="width: 100%" scroll-height="14rem">        <template #lane-header="{ lane, count }">            <strong>{{ lane.title }}</strong>            <small style="color: var(--vt-text-muted-color)">{{ count }} tasks</small>        </template>    </Taskboard></template>

              Colunas aninhadas

              Colunas que carregam os próprios cards (v-model:columns). Later começa recolhida, mas ainda aceita cards soltos; Frozen está travada, e seu card está desabilitado.

              Ideas2 cartões
              Next1 cartões
              Later2 cartões
              Frozen1 cartões
              • Dark mode for charts
              • Right-to-left layout
              • Nuxt module
                • IE support
                <script setup lang="ts">import { Taskboard, type TaskboardColumn } from '@vitral/vue';import { ref } from 'vue'; const nested = ref<TaskboardColumn[]>([    { key: 'ideas', title: 'Ideas', items: [{ id: 'a', title: 'Dark mode for charts' }, { id: 'b', title: 'Right-to-left layout' }] },    { key: 'next', title: 'Next', items: [{ id: 'c', title: 'Nuxt module' }] },    { key: 'later', title: 'Later', collapsed: true, items: [{ id: 'd', title: 'Angular adapter' }, { id: 'e', title: 'React adapter' }] },    { key: 'frozen', title: 'Frozen', locked: true, items: [{ id: 'f', title: 'IE support', disabled: true }] }]);</script> <template>    <Taskboard v-model:columns="nested" style="width: 100%">        <template #column-footer="{ count }">            <small style="color: var(--vt-text-muted-color)">{{ count }} in this column</small>        </template>    </Taskboard></template>

                canDrop

                Um hook dá a palavra final: aqui, só tarefas de alta prioridade podem ir para Done, e de qualquer lugar menos de In progress.

                Backlog1 cartões
                To do2 cartões
                In progress2 de 2 cartões
                Done1 cartões
                • Draft the release notes
                • Audit colour contrast
                • Virtual scrolling in Select
                • Keyboard grid for DataGrid
                • Theme editor prototype
                • Publish 0.1
                <script setup lang="ts">import { Taskboard, type TaskboardColumn } from '@vitral/vue';import { ref } from 'vue'; interface Task {    id: number;    title: string;    status: string;    team: string;    priority: 'low' | 'high';    locked?: boolean;} const columns = ref<TaskboardColumn[]>([    { key: 'backlog', title: 'Backlog', color: 'var(--vt-chart-8)' },    { key: 'todo', title: 'To do', color: 'var(--vt-chart-4)' },    { key: 'doing', title: 'In progress', wipLimit: 2, color: 'var(--vt-chart-5)' },    { key: 'done', title: 'Done', color: 'var(--vt-chart-3)' }]); const tasks = ref<Task[]>([    { id: 1, title: 'Draft the release notes', status: 'backlog', team: 'Docs', priority: 'low' },    { id: 2, title: 'Audit colour contrast', status: 'todo', team: 'Design', priority: 'high' },    { id: 3, title: 'Virtual scrolling in Select', status: 'todo', team: 'Web', priority: 'low' },    { id: 4, title: 'Keyboard grid for DataGrid', status: 'doing', team: 'Web', priority: 'high' },    { id: 5, title: 'Theme editor prototype', status: 'doing', team: 'Design', priority: 'low' },    { id: 6, title: 'Publish 0.1', status: 'done', team: 'Web', priority: 'high', locked: true }]); // Only high-priority work may skip straight to Done.const canDrop = ({ item, from, to }: { item: unknown; from: { column: string | number }; to: { column: string | number } }) =>    !(to.column === 'done' && from.column !== 'doing' && (item as Task).priority !== 'high');</script> <template>    <Taskboard v-model:columns="columns" v-model:items="tasks" column-field="status" :can-drop="canDrop" style="width: 100%" /></template>

                API

                Lido de packages/vue/src/components/Taskboard/types.ts, então diz o que o componente aceita de fato.

                Props

                NomeTipoDescrição
                columnsTaskboardColumn[]As colunas, em ordem. Sem `items`, cada uma leva seus próprios cards (`v-model:columns`).
                itemsunknown[]Os cards como uma lista plana (`v-model:items`); cada um indica sua coluna em `columnField`.
                columnFieldstringO campo de um item plano com a chave da sua coluna. O padrão é `'column'`.
                laneFieldstringO campo que põe um card numa raia; defina-o para dividir o quadro em raias.
                lanesTaskboardLane[]As raias, em ordem. Sem elas, as raias são os valores distintos de `laneField`, na ordem em que aparecem.
                dataKeystringO campo que identifica um card. O padrão é `'id'`.
                cardLabelstringO campo com o título de um card, mostrado pelo card padrão e usado nos anúncios. O padrão é `'title'`.
                disabledFieldstringO campo que marca um card como desabilitado: nem clicável nem movível. O padrão é `'disabled'`.
                lockedFieldstringO campo que marca um card como travado: clicável, mas não movível. O padrão é `'locked'`.
                canDrop(context: TaskboardDropContext) => booleanA última palavra sobre uma soltura. Devolva false para recusá-la.
                dragdropbooleanOs cards podem ser arrastados (ponteiro, toque ou teclado). O padrão é true.
                reorderColumnsbooleanAs colunas podem ser reordenadas pela alça. O padrão é true.
                collapsiblebooleanAs colunas (e raias) ganham um botão que as recolhe. O padrão é true.
                autoScrollbooleanRola o quadro e as colunas enquanto um card é arrastado perto das bordas. O padrão é true.
                touchDelaynumberMilissegundos que um dedo repousa num card antes de arrastá-lo, para que um deslize ainda role. O padrão é 250.
                scrollHeightstringA altura da lista de cards de uma coluna; além dela, rola.
                disabledboolean—

                Mais pt, dt e unstyled de BaseProps; veja pass-through e modo sem estilo.

                Emits

                EventoPayloadDescrição
                update:columnsvalue: TaskboardColumn[]As colunas depois que um card ou uma coluna se moveu num quadro aninhado, ou depois que uma coluna se moveu num quadro plano.
                update:itemsvalue: unknown[]A lista plana depois que um card se moveu.
                card-moveevent: TaskboardCardMoveEvent—
                column-moveevent: TaskboardColumnMoveEvent—
                card-clickevent: { item: unknown; column: TaskboardColumn; originalEvent: Event }—
                drop-refusedevent: TaskboardDropRefusedEvent—

                Slots

                NomeProps do slotDescrição
                default—As partes, escritas como filhos: `<Taskboard.Card>`, `<Taskboard.ColumnHeader>`…
                card(props: TaskboardCardSlotProps)O conteúdo de um card.
                column-header(props: TaskboardColumnSlotProps)A área de título do cabeçalho de uma coluna.
                column-footer(props: TaskboardColumnSlotProps)Abaixo de uma coluna.
                add-card(props: { column: TaskboardColumn; lane?: TaskboardLane })No fim de cada célula: normalmente um botão "adicionar card".
                lane-header(props: { lane: TaskboardLane; count: number; collapsed: boolean; toggle: () => void })A área de título do cabeçalho de uma raia.
                empty(props: { column: TaskboardColumn; lane?: TaskboardLane })Uma célula vazia.