kaneo (minimalist kanban) fork to experiment adding a tangled integration github.com/usekaneo/kaneo
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 123 lines 4.3 kB view raw
1import { motion } from "framer-motion"; 2import { useTranslation } from "react-i18next"; 3import TaskCardContextMenuContent from "@/components/kanban-board/task-card-context-menu/task-card-context-menu-content"; 4import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; 5import { Checkbox } from "@/components/ui/checkbox"; 6import { ContextMenu, ContextMenuTrigger } from "@/components/ui/context-menu"; 7import { getColumnIcon } from "@/lib/column"; 8import type Task from "@/types/task"; 9import SubtaskAssigneePopover from "./subtask-assignee-popover"; 10import SubtaskStatusPopover from "./subtask-status-popover"; 11 12type SubtaskRowProps = { 13 task: Task; 14 tasks: Task[]; 15 projectId: string; 16 workspaceId: string; 17 isSelected: boolean; 18 isFocused: boolean; 19 selectionRadius: string; 20 assignee: { 21 user?: { image?: string | null; name?: string | null } | null; 22 } | null; 23 onToggleSelection: () => void; 24 onNavigate: () => void; 25 onDeleteClick: () => void; 26}; 27 28export default function SubtaskRow({ 29 task, 30 tasks, 31 projectId, 32 workspaceId, 33 isSelected, 34 isFocused, 35 selectionRadius, 36 assignee, 37 onToggleSelection, 38 onNavigate, 39 onDeleteClick, 40}: SubtaskRowProps) { 41 const { t } = useTranslation(); 42 43 return ( 44 <motion.div 45 layout 46 initial={{ opacity: 0, height: 0 }} 47 animate={{ opacity: 1, height: "auto" }} 48 exit={{ opacity: 0, height: 0 }} 49 transition={{ duration: 0.2, ease: "easeOut" }} 50 > 51 <ContextMenu> 52 <ContextMenuTrigger asChild> 53 <div 54 className={`group flex items-center gap-2 py-1 px-2 ${selectionRadius} transition-colors cursor-default ${isSelected ? "bg-primary/10 hover:bg-primary/15" : "hover:bg-accent/50"} ${isFocused ? "ring-1 ring-inset ring-ring/50" : ""}`} 55 > 56 <Checkbox 57 checked={isSelected} 58 onCheckedChange={onToggleSelection} 59 /> 60 61 <SubtaskStatusPopover tasks={tasks} projectId={projectId}> 62 <button 63 type="button" 64 className="shrink-0 flex items-center justify-center rounded p-0.5 transition-colors outline-none [&_svg]:text-muted-foreground hover:[&_svg]:text-foreground" 65 > 66 {getColumnIcon(task.status, false)} 67 </button> 68 </SubtaskStatusPopover> 69 70 <button 71 type="button" 72 className="flex-1 min-w-0 text-left outline-none" 73 onClick={onNavigate} 74 > 75 <span 76 className={`text-sm truncate block ${task.status === "done" ? "line-through text-muted-foreground" : "text-foreground/90"}`} 77 > 78 {task.title} 79 </span> 80 </button> 81 82 <SubtaskAssigneePopover tasks={tasks} workspaceId={workspaceId}> 83 <button 84 type="button" 85 className="shrink-0 flex items-center justify-center rounded p-0.5 transition-colors outline-none" 86 > 87 {task.userId && assignee ? ( 88 <Avatar className="h-5 w-5"> 89 <AvatarImage 90 src={assignee?.user?.image ?? ""} 91 alt={assignee?.user?.name || ""} 92 /> 93 <AvatarFallback className="text-[9px] font-medium border border-border/30"> 94 {assignee?.user?.name?.charAt(0).toUpperCase()} 95 </AvatarFallback> 96 </Avatar> 97 ) : ( 98 <div 99 className="flex h-5 w-5 items-center justify-center rounded-full border border-dashed border-border/70" 100 title={t("tasks:popover.assignee.unassigned")} 101 > 102 <span className="text-[9px] font-medium text-muted-foreground"> 103 ? 104 </span> 105 </div> 106 )} 107 </button> 108 </SubtaskAssigneePopover> 109 </div> 110 </ContextMenuTrigger> 111 112 <TaskCardContextMenuContent 113 task={task} 114 taskCardContext={{ 115 projectId, 116 worskpaceId: workspaceId, 117 }} 118 onDeleteClick={onDeleteClick} 119 /> 120 </ContextMenu> 121 </motion.div> 122 ); 123}