feat(floating-ai): step 7 — implement morph animation (FLIP)

Add FLIP animation so the floating chat visually morphs into a newly-created
TaskRow when the AI creates a task. Uses Framer Motion's shared layoutId
across FloatingChat and TaskRow, with LayoutGroup wrapping the app shell.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Roberto Musso
2026-02-28 13:27:23 +01:00
parent d12681b79f
commit 60b76c6d97
6 changed files with 56 additions and 10 deletions

View File

@@ -1,3 +1,4 @@
import { motion } from 'framer-motion';
import { Calendar, User, Pencil, Trash2 } from 'lucide-react';
import { Checkbox } from '@/components/ui/checkbox';
import { Badge } from '@/components/ui/badge';
@@ -57,6 +58,7 @@ export function TaskRow({
onDelete,
onClick,
hideBreadcrumb,
layoutId,
}: {
task: TaskItem;
onToggle: (id: string, status: string | null) => void;
@@ -64,6 +66,7 @@ export function TaskRow({
onDelete?: (id: string) => void;
onClick?: (task: TaskItem) => void;
hideBreadcrumb?: boolean;
layoutId?: string;
}) {
const isDone = task.status === 'done';
@@ -84,10 +87,14 @@ export function TaskRow({
breadcrumb.length > 0 ||
task.assignee;
const Wrapper = layoutId ? motion.div : 'div';
const wrapperProps = layoutId ? { layoutId, layout: true as const } : {};
return (
<ContextMenu>
<ContextMenuTrigger asChild>
<div
<Wrapper
{...wrapperProps}
className={`flex flex-col gap-1.5 px-4 py-3 rounded-md border select-none transition-colors ${
isDone ? 'bg-green-50 dark:bg-green-950/30 border-green-200 dark:border-green-900' : 'bg-card border-border'
} ${onClick ? 'cursor-pointer hover:bg-accent/50' : 'cursor-default'}`}
@@ -146,7 +153,7 @@ export function TaskRow({
)}
</div>
)}
</div>
</Wrapper>
</ContextMenuTrigger>
<ContextMenuContent>