feat(ui): add MicroSteps expand/collapse to WipTaskList; exclude subtasks from WIP count

This commit is contained in:
2026-04-19 10:51:11 +01:00
parent 442fa6656e
commit 674fc2c4b8

View File

@@ -1,13 +1,16 @@
<script> <script>
import { tasks, addTask, completeTask, uncompleteTask, deleteTask } from '$lib/stores/page.svelte.js'; import { tasks, addTask, completeTask, uncompleteTask, deleteTask } from '$lib/stores/page.svelte.js';
import { ChevronDown } from 'lucide-svelte'; import MicroSteps from '$lib/components/MicroSteps.svelte';
import { ChevronDown, ChevronRight } from 'lucide-svelte';
let { wipLimit = 3 } = $props(); let { wipLimit = 3 } = $props();
let newTaskText = $state(''); let newTaskText = $state('');
let showOverflow = $state(false); let showOverflow = $state(false);
let expandedTaskIds = $state(new Set());
let activeTasks = $derived(tasks.filter(t => !t.done)); // Exclude subtasks from WIP count — only top-level tasks count
let activeTasks = $derived(tasks.filter(t => !t.done && !t.parentTaskId));
let visibleTasks = $derived(activeTasks.slice(0, wipLimit)); let visibleTasks = $derived(activeTasks.slice(0, wipLimit));
let overflowTasks = $derived(activeTasks.slice(wipLimit)); let overflowTasks = $derived(activeTasks.slice(wipLimit));
let hasOverflow = $derived(overflowTasks.length > 0); let hasOverflow = $derived(overflowTasks.length > 0);
@@ -22,6 +25,16 @@
function handleKeydown(e) { function handleKeydown(e) {
if (e.key === 'Enter') handleAdd(); if (e.key === 'Enter') handleAdd();
} }
function toggleExpand(taskId) {
const next = new Set(expandedTaskIds);
if (next.has(taskId)) {
next.delete(taskId);
} else {
next.add(taskId);
}
expandedTaskIds = next;
}
</script> </script>
<div class="flex flex-col gap-3"> <div class="flex flex-col gap-3">
@@ -44,23 +57,42 @@
{:else} {:else}
<div class="flex flex-col gap-1"> <div class="flex flex-col gap-1">
{#each visibleTasks as task (task.id)} {#each visibleTasks as task (task.id)}
<div class="flex items-center gap-2 px-3 py-2 rounded-lg hover:bg-hover group" <div>
style="transition-duration: var(--duration-ui)"> <!-- Task row -->
<button <div class="flex items-center gap-2 px-3 py-2 rounded-lg hover:bg-hover group"
class="w-4 h-4 rounded border border-border-subtle flex items-center justify-center flex-shrink-0 style="transition-duration: var(--duration-ui)">
hover:border-accent" <button
onclick={() => completeTask(task.id)} class="w-4 h-4 rounded border border-border-subtle flex items-center justify-center flex-shrink-0
aria-label="Complete task" hover:border-accent"
> onclick={() => completeTask(task.id)}
</button> aria-label="Complete task"
<span class="text-[13px] text-text flex-1 min-w-0 truncate">{task.text}</span> ></button>
<button <span class="text-[13px] text-text flex-1 min-w-0 truncate">{task.text}</span>
class="text-text-tertiary hover:text-danger opacity-0 group-hover:opacity-100 text-[11px]" <!-- Expand/collapse micro-steps toggle -->
onclick={() => deleteTask(task.id)} <button
aria-label="Delete task" class="opacity-0 group-hover:opacity-100 text-text-tertiary hover:text-accent"
> onclick={() => toggleExpand(task.id)}
&times; aria-label={expandedTaskIds.has(task.id) ? 'Collapse steps' : 'Expand steps'}
</button> style="transition: opacity var(--duration-ui)"
>
{#if expandedTaskIds.has(task.id)}
<ChevronDown size={12} aria-hidden="true" />
{:else}
<ChevronRight size={12} aria-hidden="true" />
{/if}
</button>
<button
class="text-text-tertiary hover:text-danger opacity-0 group-hover:opacity-100 text-[11px]"
onclick={() => deleteTask(task.id)}
aria-label="Delete task"
>
&times;
</button>
</div>
<!-- Micro-steps panel (expanded) -->
{#if expandedTaskIds.has(task.id)}
<MicroSteps parentTaskId={task.id} />
{/if}
</div> </div>
{/each} {/each}
</div> </div>