feat: Phase 3 - filter bar, keyboard navigation, notifications, comments
- FilterBar component with text search, label chips, due date and priority dropdowns - "/" keyboard shortcut and toolbar button to toggle filter bar - Keyboard card navigation with J/K/H/L keys, Enter to open, Escape to clear - Focus ring on keyboard-selected cards with auto-scroll - Desktop notifications for due/overdue cards via tauri-plugin-notification - CommentsSection component with add/delete and relative timestamps - Filtered card count display in column headers
This commit is contained in:
@@ -25,11 +25,13 @@ const WIDTH_MAP = {
|
||||
|
||||
interface KanbanColumnProps {
|
||||
column: Column;
|
||||
filteredCardIds?: string[];
|
||||
focusedCardId?: string | null;
|
||||
onCardClick?: (cardId: string) => void;
|
||||
isNew?: boolean;
|
||||
}
|
||||
|
||||
export function KanbanColumn({ column, onCardClick, isNew }: KanbanColumnProps) {
|
||||
export function KanbanColumn({ column, filteredCardIds, focusedCardId, onCardClick, isNew }: KanbanColumnProps) {
|
||||
const [showAddCard, setShowAddCard] = useState(false);
|
||||
const board = useBoardStore((s) => s.board);
|
||||
const toggleColumnCollapse = useBoardStore((s) => s.toggleColumnCollapse);
|
||||
@@ -62,6 +64,8 @@ export function KanbanColumn({ column, onCardClick, isNew }: KanbanColumnProps)
|
||||
? `3px solid ${board.color}30`
|
||||
: undefined;
|
||||
|
||||
const displayCardIds = filteredCardIds ?? column.cardIds;
|
||||
const isFiltering = filteredCardIds != null;
|
||||
const cardCount = column.cardIds.length;
|
||||
|
||||
const wipTint = column.wipLimit != null
|
||||
@@ -117,7 +121,7 @@ export function KanbanColumn({ column, onCardClick, isNew }: KanbanColumnProps)
|
||||
>
|
||||
{/* The column header is the drag handle for column reordering */}
|
||||
<div {...listeners}>
|
||||
<ColumnHeader column={column} cardCount={column.cardIds.length} />
|
||||
<ColumnHeader column={column} cardCount={cardCount} filteredCount={isFiltering ? displayCardIds.length : undefined} />
|
||||
</div>
|
||||
|
||||
{/* Card list - wrapped in SortableContext for within-column sorting */}
|
||||
@@ -138,7 +142,7 @@ export function KanbanColumn({ column, onCardClick, isNew }: KanbanColumnProps)
|
||||
initial="hidden"
|
||||
animate="visible"
|
||||
>
|
||||
{column.cardIds.map((cardId) => {
|
||||
{displayCardIds.map((cardId) => {
|
||||
const card = board?.cards[cardId];
|
||||
if (!card) return null;
|
||||
return (
|
||||
@@ -148,13 +152,14 @@ export function KanbanColumn({ column, onCardClick, isNew }: KanbanColumnProps)
|
||||
boardLabels={board?.labels ?? []}
|
||||
columnId={column.id}
|
||||
onCardClick={onCardClick}
|
||||
isFocused={focusedCardId === cardId}
|
||||
/>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
{column.cardIds.length === 0 && (
|
||||
{displayCardIds.length === 0 && (
|
||||
<li className="flex min-h-[60px] items-center justify-center rounded-md border border-dashed border-pylon-text-secondary/20 text-xs text-pylon-text-secondary/50">
|
||||
Drop or add a card
|
||||
{isFiltering ? "No matching cards" : "Drop or add a card"}
|
||||
</li>
|
||||
)}
|
||||
</motion.ul>
|
||||
|
||||
Reference in New Issue
Block a user