Retour au catalogue
Integration Chat Widget
Widget de chat flottant avec bulles de conversation, input et bouton FAB.
integrationsmedium Both Responsive a11y
playfulminimalsaasecommerceuniversalstacked
Theme
"use client";
import { useState } from "react";
import { motion, AnimatePresence } from "framer-motion";
import { MessageCircle, X, Send } from "lucide-react";
interface ChatMessage {
sender: "user" | "bot";
text: string;
time: string;
}
interface IntegrationChatWidgetProps {
title?: string;
subtitle?: string;
messages?: ChatMessage[];
placeholder?: string;
}
const ease: [number, number, number, number] = [0.16, 1, 0.3, 1];
export default function IntegrationChatWidget({
title = "Support",
subtitle = "Nous repondons en quelques minutes",
messages: initialMessages = [],
placeholder = "Ecrivez votre message...",
}: IntegrationChatWidgetProps) {
const [isOpen, setIsOpen] = useState(true);
const [messages, setMessages] = useState(initialMessages);
const [input, setInput] = useState("");
const send = () => {
if (!input.trim()) return;
setMessages([...messages, { sender: "user", text: input, time: "A l'instant" }]);
setInput("");
};
return (
<div className="relative min-h-[500px] flex items-end justify-end p-6" style={{ background: "var(--color-background)" }}>
<AnimatePresence>
{isOpen && (
<motion.div
initial={{ opacity: 0, y: 20, scale: 0.95 }}
animate={{ opacity: 1, y: 0, scale: 1 }}
exit={{ opacity: 0, y: 20, scale: 0.95 }}
transition={{ duration: 0.25, ease }}
className="w-80 rounded-2xl overflow-hidden shadow-xl flex flex-col"
style={{ background: "var(--color-background-card)", border: "1px solid var(--color-border)", maxHeight: 440 }}
>
<div className="px-4 py-3 flex items-center justify-between" style={{ background: "var(--color-accent)" }}>
<div>
<h4 className="text-sm font-semibold" style={{ color: "var(--color-background)" }}>{title}</h4>
<p className="text-[10px]" style={{ color: "var(--color-background)", opacity: 0.8 }}>{subtitle}</p>
</div>
<button onClick={() => setIsOpen(false)}>
<X size={16} style={{ color: "var(--color-background)" }} />
</button>
</div>
<div className="flex-1 overflow-y-auto px-4 py-3 flex flex-col gap-3 min-h-[200px]">
{messages.map((msg, i) => (
<motion.div
key={i}
initial={{ opacity: 0, y: 8 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: i * 0.05 }}
className={`flex ${msg.sender === "user" ? "justify-end" : "justify-start"}`}
>
<div
className="max-w-[80%] px-3 py-2 rounded-xl text-sm"
style={{
background: msg.sender === "user" ? "var(--color-accent)" : "var(--color-background-alt)",
color: msg.sender === "user" ? "var(--color-background)" : "var(--color-foreground)",
}}
>
{msg.text}
<span className="block text-[9px] mt-1" style={{ opacity: 0.6 }}>{msg.time}</span>
</div>
</motion.div>
))}
</div>
<div className="px-3 py-3" style={{ borderTop: "1px solid var(--color-border)" }}>
<div className="flex items-center gap-2">
<input
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyDown={(e) => e.key === "Enter" && send()}
placeholder={placeholder}
className="flex-1 bg-transparent text-sm outline-none"
style={{ color: "var(--color-foreground)" }}
/>
<motion.button whileTap={{ scale: 0.9 }} onClick={send} className="p-2 rounded-lg" style={{ background: "var(--color-accent)" }}>
<Send size={14} style={{ color: "var(--color-background)" }} />
</motion.button>
</div>
</div>
</motion.div>
)}
</AnimatePresence>
{!isOpen && (
<motion.button
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
onClick={() => setIsOpen(true)}
className="w-14 h-14 rounded-full flex items-center justify-center shadow-lg"
style={{ background: "var(--color-accent)" }}
>
<MessageCircle size={24} style={{ color: "var(--color-background)" }} />
</motion.button>
)}
</div>
);
}
Autres variantes integrations
API Integration Cards
complex · both
minimalcorporate
Integration Calendar
medium · both
minimalcorporate
Integration Flow Diagram
complex · both
elegantcorporate
Integration Grid Logos
simple · both
minimalcorporate
Integration Map Embed
medium · both
minimalcorporate
Integration Marketplace
complex · both
minimalelegant