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:
@@ -1,4 +1,4 @@
|
||||
import { useState, useRef } from "react";
|
||||
import { useState, useRef, useEffect } from "react";
|
||||
import { createPortal } from "react-dom";
|
||||
import { format } from "date-fns";
|
||||
import { motion, useReducedMotion, AnimatePresence } from "framer-motion";
|
||||
@@ -65,9 +65,10 @@ interface CardThumbnailProps {
|
||||
boardLabels: Label[];
|
||||
columnId: string;
|
||||
onCardClick?: (cardId: string) => void;
|
||||
isFocused?: boolean;
|
||||
}
|
||||
|
||||
export function CardThumbnail({ card, boardLabels, columnId, onCardClick }: CardThumbnailProps) {
|
||||
export function CardThumbnail({ card, boardLabels, columnId, onCardClick, isFocused }: CardThumbnailProps) {
|
||||
const prefersReducedMotion = useReducedMotion();
|
||||
|
||||
const {
|
||||
@@ -82,6 +83,14 @@ export function CardThumbnail({ card, boardLabels, columnId, onCardClick }: Card
|
||||
data: { type: "card", columnId },
|
||||
});
|
||||
|
||||
const cardRef = useRef<HTMLButtonElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (isFocused && cardRef.current) {
|
||||
cardRef.current.scrollIntoView({ block: "nearest", behavior: "smooth" });
|
||||
}
|
||||
}, [isFocused]);
|
||||
|
||||
const hasDueDate = card.dueDate != null;
|
||||
const dueDateStatus = getDueDateStatus(card.dueDate);
|
||||
|
||||
@@ -111,7 +120,10 @@ export function CardThumbnail({ card, boardLabels, columnId, onCardClick }: Card
|
||||
<ContextMenu>
|
||||
<ContextMenuTrigger asChild>
|
||||
<motion.button
|
||||
ref={setNodeRef}
|
||||
ref={(node) => {
|
||||
setNodeRef(node);
|
||||
(cardRef as React.MutableRefObject<HTMLButtonElement | null>).current = node;
|
||||
}}
|
||||
style={{
|
||||
transform: CSS.Transform.toString(transform),
|
||||
transition,
|
||||
@@ -119,7 +131,9 @@ export function CardThumbnail({ card, boardLabels, columnId, onCardClick }: Card
|
||||
opacity: getAgingOpacity(card.updatedAt),
|
||||
}}
|
||||
onClick={handleClick}
|
||||
className="w-full rounded-lg bg-pylon-surface shadow-sm text-left"
|
||||
className={`w-full rounded-lg bg-pylon-surface shadow-sm text-left ${
|
||||
isFocused ? "ring-2 ring-pylon-accent ring-offset-2 ring-offset-pylon-column" : ""
|
||||
}`}
|
||||
layoutId={`card-${card.id}`}
|
||||
variants={fadeSlideUp}
|
||||
initial={prefersReducedMotion ? false : "hidden"}
|
||||
|
||||
Reference in New Issue
Block a user