feat(timeline): add pulse animation for in-flight drag mutations
Allocation bars that have active optimistic overrides (post-drag, awaiting server confirmation) now pulse subtly via animate-pulse. The pending set is derived from the existing optimisticAllocations map keys, requiring no additional state. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { useRef, useState } from "react";
|
||||
import { useFocusTrap } from "~/hooks/useFocusTrap.js";
|
||||
import { useState } from "react";
|
||||
import { AnimatedModal } from "~/components/ui/AnimatedModal.js";
|
||||
import { trpc } from "~/lib/trpc/client.js";
|
||||
|
||||
const TARGET_TYPES = [
|
||||
@@ -42,9 +42,6 @@ export function BroadcastModal({ onClose, onSuccess }: BroadcastModalProps) {
|
||||
const [serverError, setServerError] = useState<string | null>(null);
|
||||
const [result, setResult] = useState<{ recipientCount: number } | null>(null);
|
||||
|
||||
const panelRef = useRef<HTMLDivElement>(null);
|
||||
useFocusTrap(panelRef, true);
|
||||
|
||||
const utils = trpc.useUtils();
|
||||
|
||||
const createMutation = trpc.notification.createBroadcast.useMutation({
|
||||
@@ -85,17 +82,7 @@ export function BroadcastModal({ onClose, onSuccess }: BroadcastModalProps) {
|
||||
// After successful send, show result
|
||||
if (result) {
|
||||
return (
|
||||
<div
|
||||
className="fixed inset-0 bg-black/50 z-50 flex items-start justify-center overflow-y-auto py-8"
|
||||
onClick={(e) => {
|
||||
if (e.target === e.currentTarget) { onSuccess(); onClose(); }
|
||||
}}
|
||||
>
|
||||
<div
|
||||
ref={panelRef}
|
||||
className="bg-white dark:bg-gray-800 rounded-xl shadow-2xl w-full max-w-lg mx-4"
|
||||
onKeyDown={(e) => { if (e.key === "Escape") { onSuccess(); onClose(); } }}
|
||||
>
|
||||
<AnimatedModal open={true} onClose={() => { onSuccess(); onClose(); }} maxWidth="max-w-lg">
|
||||
<div className="px-6 py-8 text-center">
|
||||
<div className="mx-auto mb-4 flex h-12 w-12 items-center justify-center rounded-full bg-green-100 dark:bg-green-900/30">
|
||||
<svg className="h-6 w-6 text-green-600 dark:text-green-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
@@ -114,23 +101,12 @@ export function BroadcastModal({ onClose, onSuccess }: BroadcastModalProps) {
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</AnimatedModal>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className="fixed inset-0 bg-black/50 z-50 flex items-start justify-center overflow-y-auto py-8"
|
||||
onClick={(e) => {
|
||||
if (e.target === e.currentTarget) onClose();
|
||||
}}
|
||||
>
|
||||
<div
|
||||
ref={panelRef}
|
||||
className="bg-white dark:bg-gray-800 rounded-xl shadow-2xl w-full max-w-lg mx-4"
|
||||
onKeyDown={(e) => { if (e.key === "Escape") onClose(); }}
|
||||
>
|
||||
<AnimatedModal open={true} onClose={onClose} maxWidth="max-w-lg">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<h2 className="text-lg font-semibold text-gray-900 dark:text-gray-100">Send Broadcast</h2>
|
||||
@@ -322,7 +298,6 @@ export function BroadcastModal({ onClose, onSuccess }: BroadcastModalProps) {
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</AnimatedModal>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user