Retour au catalogue
Integration Calendar
Calendrier mensuel interactif avec evenements, selection de jour et navigation.
integrationsmedium Both Responsive a11y
minimalcorporatesaaseventuniversalcentered
Theme
"use client";
import { useState } from "react";
import { motion } from "framer-motion";
import { ChevronLeft, ChevronRight } from "lucide-react";
interface CalendarEvent {
day: number;
title: string;
color?: string;
}
interface IntegrationCalendarProps {
title?: string;
month?: string;
year?: number;
events?: CalendarEvent[];
daysInMonth?: number;
firstDayOffset?: number;
}
export default function IntegrationCalendar({
title = "Calendrier",
month = "Mars",
year = 2026,
events = [],
daysInMonth = 31,
firstDayOffset = 0,
}: IntegrationCalendarProps) {
const [selectedDay, setSelectedDay] = useState<number | null>(null);
const days = ["Lun", "Mar", "Mer", "Jeu", "Ven", "Sam", "Dim"];
const blanks = Array.from({ length: firstDayOffset });
const allDays = Array.from({ length: daysInMonth }, (_, i) => i + 1);
const getEvents = (day: number) => events.filter((e) => e.day === day);
return (
<div className="py-12 px-6" style={{ background: "var(--color-background)" }}>
<motion.div
initial={{ opacity: 0, y: 16 }}
animate={{ opacity: 1, y: 0 }}
className="mx-auto max-w-md rounded-xl overflow-hidden"
style={{ background: "var(--color-background-card)", border: "1px solid var(--color-border)" }}
>
<div className="flex items-center justify-between px-5 py-4" style={{ borderBottom: "1px solid var(--color-border)" }}>
<button className="p-1"><ChevronLeft size={18} style={{ color: "var(--color-foreground-muted)" }} /></button>
<h3 className="text-sm font-semibold" style={{ color: "var(--color-foreground)" }}>{month} {year}</h3>
<button className="p-1"><ChevronRight size={18} style={{ color: "var(--color-foreground-muted)" }} /></button>
</div>
<div className="px-4 py-3">
<div className="grid grid-cols-7 gap-1 mb-2">
{days.map((d) => (
<span key={d} className="text-center text-[10px] font-semibold py-1" style={{ color: "var(--color-foreground-light)" }}>{d}</span>
))}
</div>
<div className="grid grid-cols-7 gap-1">
{blanks.map((_, i) => <div key={`b-${i}`} />)}
{allDays.map((day) => {
const dayEvents = getEvents(day);
const isSelected = selectedDay === day;
const today = day === 6;
return (
<button
key={day}
onClick={() => setSelectedDay(day)}
className="relative aspect-square flex flex-col items-center justify-center rounded-lg text-xs font-medium transition-colors"
style={{
background: isSelected ? "var(--color-accent)" : today ? "var(--color-background-alt)" : "transparent",
color: isSelected ? "var(--color-background)" : "var(--color-foreground)",
}}
>
{day}
{dayEvents.length > 0 && (
<div className="absolute bottom-1 flex gap-0.5">
{dayEvents.slice(0, 3).map((_, ei) => (
<div key={ei} className="w-1 h-1 rounded-full" style={{ background: isSelected ? "var(--color-background)" : "var(--color-accent)" }} />
))}
</div>
)}
</button>
);
})}
</div>
</div>
{selectedDay && getEvents(selectedDay).length > 0 && (
<motion.div
initial={{ height: 0 }}
animate={{ height: "auto" }}
className="px-4 pb-4 overflow-hidden"
>
<div className="pt-3" style={{ borderTop: "1px solid var(--color-border)" }}>
{getEvents(selectedDay).map((e, i) => (
<div key={i} className="flex items-center gap-2 py-1.5">
<div className="w-2 h-2 rounded-full" style={{ background: "var(--color-accent)" }} />
<span className="text-xs" style={{ color: "var(--color-foreground)" }}>{e.title}</span>
</div>
))}
</div>
</motion.div>
)}
</motion.div>
</div>
);
}
Autres variantes integrations
API Integration Cards
complex · both
minimalcorporate
Integration Chat Widget
medium · both
playfulminimal
Integration Flow Diagram
complex · both
elegantcorporate
Integration Grid Logos
simple · both
minimalcorporate
Integration Map Embed
medium · both
minimalcorporate
Integration Marketplace
complex · both
minimalelegant