Angular 19 component library for kanban boards: - kb-board: main board with toolbar, swimlanes, and pipeline view - kb-column: columns with WIP limits, collapse, and drag reorder - kb-card: draggable cards with labels, assignees, and priorities - kb-swimlane: horizontal grouping with collapsible rows - kb-toolbar: search, sort, filter, and view toggle - kb-quick-add: inline card creation - kb-wip-indicator: WIP limit status display - kb-card-def / kb-column-header-def: custom template directives Includes signal-based services (board, drag, filter), SCSS design tokens with dark mode, and full TypeScript type definitions. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
88 lines
3.1 KiB
HTML
88 lines
3.1 KiB
HTML
<div class="kb-toolbar">
|
|
@if (searchable()) {
|
|
<div class="kb-toolbar__search">
|
|
<span class="kb-toolbar__search-icon">
|
|
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><circle cx="7" cy="7" r="4.5"/><path d="M10.5 10.5L14 14"/></svg>
|
|
</span>
|
|
<input
|
|
class="kb-toolbar__search-input"
|
|
type="text"
|
|
placeholder="Search cards..."
|
|
[value]="searchTerm()"
|
|
(input)="onSearchInput($event)"
|
|
/>
|
|
</div>
|
|
}
|
|
|
|
<div class="kb-toolbar__actions">
|
|
<!-- Sort dropdown -->
|
|
<div class="kb-toolbar__sort">
|
|
<button
|
|
class="kb-toolbar__btn"
|
|
type="button"
|
|
(click)="toggleSortMenu()"
|
|
[title]="currentSort() ? 'Sort: ' + currentSort()!.field + ' ' + currentSort()!.direction : 'Sort cards'"
|
|
>
|
|
<span class="kb-toolbar__btn-icon">
|
|
<svg viewBox="0 0 16 16" fill="currentColor"><path d="M3 4h10v1.5H3V4zm0 3.25h7v1.5H3v-1.5zm0 3.25h4v1.5H3v-1.5z"/></svg>
|
|
Sort
|
|
</span>
|
|
</button>
|
|
@if (showSortMenu()) {
|
|
<div class="kb-toolbar__sort-menu">
|
|
@for (opt of sortOptions; track opt.field) {
|
|
<button
|
|
class="kb-toolbar__sort-option"
|
|
[class.kb-toolbar__sort-option--active]="currentSort()?.field === opt.field"
|
|
type="button"
|
|
(click)="onSort(opt.field)"
|
|
>
|
|
{{ opt.label }}
|
|
@if (currentSort()?.field === opt.field) {
|
|
<span class="kb-toolbar__sort-dir">
|
|
@if (currentSort()!.direction === 'asc') {
|
|
<svg viewBox="0 0 16 16" fill="currentColor"><path d="M8 3.5l4.5 5H3.5L8 3.5z"/></svg>
|
|
} @else {
|
|
<svg viewBox="0 0 16 16" fill="currentColor"><path d="M8 12.5L3.5 7.5h9L8 12.5z"/></svg>
|
|
}
|
|
</span>
|
|
}
|
|
</button>
|
|
}
|
|
</div>
|
|
}
|
|
</div>
|
|
|
|
@if (filterCount() > 0) {
|
|
<span class="kb-toolbar__filter-badge">{{ filterCount() }}</span>
|
|
}
|
|
|
|
@if (showViewToggle()) {
|
|
<button
|
|
class="kb-toolbar__btn"
|
|
[class.kb-toolbar__btn--active]="viewMode() === 'board'"
|
|
type="button"
|
|
title="Board view"
|
|
(click)="viewMode() !== 'board' && toggleView()"
|
|
>
|
|
<span class="kb-toolbar__btn-icon">
|
|
<svg viewBox="0 0 16 16" fill="currentColor"><path d="M2 3h3v10H2V3zm4.5 0h3v10h-3V3zm4.5 0h3v10h-3V3z"/></svg>
|
|
Board
|
|
</span>
|
|
</button>
|
|
<button
|
|
class="kb-toolbar__btn"
|
|
[class.kb-toolbar__btn--active]="viewMode() === 'pipeline'"
|
|
type="button"
|
|
title="Pipeline view"
|
|
(click)="viewMode() !== 'pipeline' && toggleView()"
|
|
>
|
|
<span class="kb-toolbar__btn-icon">
|
|
<svg viewBox="0 0 16 16" fill="currentColor"><path d="M1 7.25h3.5v1.5H1v-1.5zm5 0h3.5v1.5H6v-1.5zm5 0h3.5v1.5H11v-1.5zM4.5 7.5l1.5 .5-1.5.5v-1zm5 0l1.5.5-1.5.5v-1z"/></svg>
|
|
Pipeline
|
|
</span>
|
|
</button>
|
|
}
|
|
</div>
|
|
</div>
|