feat(deps): sprinkle @formkit/auto-animate on rail lists

Adds smooth fade+slide animations when list items enter/leave on the
three highest-visibility realtime surfaces:

- alert-rail.tsx — socket-driven alerts appearing / dismissed.
- my-reminders-rail.tsx — reminders completed / arriving via realtime.
- notes-list.tsx — notes added / edited / deleted.

One-line `useAutoAnimate()` hook per site, no CSS, ~2kb gzip. Replaces
the jarring "row just appears/disappears" pattern with a per-item
transition.

Skipped on pipeline-board (kanban) — combining auto-animate with
@dnd-kit's SortableContext causes double-animation glitches because
both libraries fight to animate the same layout shift.

Verified: tsc clean, vitest 1293/1293 pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-12 18:38:28 +02:00
parent a65aadc530
commit 9455ff9981
5 changed files with 28 additions and 3 deletions

View File

@@ -5,6 +5,7 @@ import { useParams } from 'next/navigation';
import { useQuery } from '@tanstack/react-query';
import { formatDistanceToNowStrict, isAfter, isBefore } from 'date-fns';
import { AlarmClock, ChevronRight } from 'lucide-react';
import { useAutoAnimate } from '@formkit/auto-animate/react';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Badge } from '@/components/ui/badge';
@@ -58,6 +59,10 @@ export function MyRemindersRail() {
.slice(0, 6);
const overdueCount = items.filter((r) => isBefore(new Date(r.dueAt), now)).length;
// Smooth animation when reminders complete / get dismissed / arrive
// via socket realtime.
const [animateRef] = useAutoAnimate<HTMLUListElement>();
function hrefFor(r: ReminderRow): string {
if (r.interestId) return `/${portSlug}/interests/${r.interestId}`;
if (r.clientId) return `/${portSlug}/clients/${r.clientId}`;
@@ -105,7 +110,7 @@ export function MyRemindersRail() {
All caught up - no reminders.
</p>
) : (
<ul className="space-y-1">
<ul ref={animateRef} className="space-y-1">
{sorted.map((r) => {
const due = new Date(r.dueAt);
const isOverdue = isBefore(due, now);