fix: separate subtask expand chevron from detail view icon (#63)

Split TaskItem into two independent states:
- `expanded` (chevron): toggles subtask visibility, only shown when
  subtasks exist
- `detailOpen` (search icon + title click): opens detail panel with
  notes, priority, edit/delete actions

The two actions are fully independent — expanding subtasks does not
open the detail view and vice versa.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
le king fu 2026-04-08 21:15:59 -04:00
parent 21020406b2
commit 78471543c3

View file

@ -9,6 +9,7 @@ import {
Calendar,
Repeat,
Check,
Search,
} from "lucide-react";
import { useTranslation } from "react-i18next";
import type { Task } from "@/lib/types";
@ -40,6 +41,7 @@ export function TaskItem({ task, subtasks = [], depth = 0 }: TaskItemProps) {
const { t } = useTranslation();
const router = useRouter();
const [expanded, setExpanded] = useState(false);
const [detailOpen, setDetailOpen] = useState(false);
const [editing, setEditing] = useState(false);
const [title, setTitle] = useState(task.title);
const [notes, setNotes] = useState(task.notes || "");
@ -123,17 +125,21 @@ export function TaskItem({ task, subtasks = [], depth = 0 }: TaskItemProps) {
>
{/* Main row */}
<div className="flex items-center gap-2 px-3 py-2">
{/* Expand toggle */}
<button
onClick={() => setExpanded(!expanded)}
className="p-0.5 text-foreground/40 hover:text-foreground shrink-0"
>
{expanded ? (
<ChevronDown size={14} />
) : (
<ChevronRight size={14} />
)}
</button>
{/* Expand subtasks toggle — only shown when subtasks exist */}
{subtasks.length > 0 ? (
<button
onClick={() => setExpanded(!expanded)}
className="p-0.5 text-foreground/40 hover:text-foreground shrink-0"
>
{expanded ? (
<ChevronDown size={14} />
) : (
<ChevronRight size={14} />
)}
</button>
) : (
<span className="w-[18px] shrink-0" />
)}
{/* Checkbox */}
<button
@ -147,12 +153,12 @@ export function TaskItem({ task, subtasks = [], depth = 0 }: TaskItemProps) {
{task.completed && <Check size={12} />}
</button>
{/* Title */}
{/* Title — click opens detail */}
<span
className={`flex-1 text-sm cursor-pointer ${
task.completed ? "line-through text-foreground/50" : ""
}`}
onClick={() => setExpanded(!expanded)}
onClick={() => setDetailOpen(!detailOpen)}
>
{task.title}
</span>
@ -174,10 +180,20 @@ export function TaskItem({ task, subtasks = [], depth = 0 }: TaskItemProps) {
{subtasks.filter((s) => s.completed).length}/{subtasks.length}
</span>
)}
{/* Detail view toggle */}
<button
onClick={() => setDetailOpen(!detailOpen)}
className={`p-0.5 shrink-0 transition-colors ${
detailOpen ? "text-bleu" : "text-foreground/30 hover:text-foreground/60"
}`}
>
<Search size={14} />
</button>
</div>
{/* Expanded view */}
{expanded && (
{/* Detail view */}
{detailOpen && (
<div className="px-3 pb-3 pt-1 border-t border-border-light dark:border-border-dark">
{editing ? (
<div className="space-y-2">
@ -284,7 +300,7 @@ export function TaskItem({ task, subtasks = [], depth = 0 }: TaskItemProps) {
</div>
{/* Subtask form */}
{showSubtaskForm && expanded && (
{showSubtaskForm && detailOpen && (
<div style={{ marginLeft: 24 }} className="mb-1.5">
<TaskForm
listId={task.listId}
@ -294,8 +310,8 @@ export function TaskItem({ task, subtasks = [], depth = 0 }: TaskItemProps) {
</div>
)}
{/* Subtasks */}
{subtasks.map((sub) => (
{/* Subtasks — toggled by chevron */}
{expanded && subtasks.map((sub) => (
<TaskItem key={sub.id} task={sub} depth={depth + 1} />
))}
</div>