feat: improve theme selector and UI components
- Updated `ThemeSelector` to use a new `ThemePreview` component for better theme visualization. - Refactored button implementation in `ThemeSelector` to utilize the new `Button` component, enhancing consistency. - Added a UI showcase section in `GeneralSettingsPageClient` to display available UI components with different themes. - Enhanced `Badge`, `Button`, and `Input` components with new variants and improved styling for better usability and visual appeal. - Updated CSS variables in `globals.css` for improved contrast and accessibility across themes.
This commit is contained in:
@@ -34,7 +34,7 @@
|
||||
--border: #64748b; /* slate-500 - encore plus clair */
|
||||
--input: #334155; /* slate-700 - plus clair */
|
||||
--primary: #06b6d4; /* cyan-500 */
|
||||
--primary-foreground: #f1f5f9; /* slate-100 */
|
||||
--primary-foreground: #ffffff; /* white - better contrast with cyan */
|
||||
--muted: #64748b; /* slate-500 */
|
||||
--muted-foreground: #94a3b8; /* slate-400 */
|
||||
--accent: #f59e0b; /* amber-500 */
|
||||
@@ -58,7 +58,7 @@
|
||||
--border: #6272a4; /* dracula comment */
|
||||
--input: #44475a; /* dracula current line */
|
||||
--primary: #ff79c6; /* dracula pink */
|
||||
--primary-foreground: #282a36; /* dracula background */
|
||||
--primary-foreground: #ffffff; /* white for contrast */
|
||||
--muted: #6272a4; /* dracula comment */
|
||||
--muted-foreground: #50fa7b; /* dracula green */
|
||||
--accent: #ffb86c; /* dracula orange */
|
||||
@@ -82,7 +82,7 @@
|
||||
--border: #49483e; /* monokai line */
|
||||
--input: #3e3d32; /* monokai selection */
|
||||
--primary: #f92672; /* monokai pink */
|
||||
--primary-foreground: #272822; /* monokai background */
|
||||
--primary-foreground: #ffffff; /* white for contrast */
|
||||
--muted: #75715e; /* monokai comment */
|
||||
--muted-foreground: #a6e22e; /* monokai green */
|
||||
--accent: #fd971f; /* monokai orange */
|
||||
@@ -106,7 +106,7 @@
|
||||
--border: #4c566a; /* nord3 */
|
||||
--input: #3b4252; /* nord1 */
|
||||
--primary: #88c0d0; /* nord7 */
|
||||
--primary-foreground: #2e3440; /* nord0 */
|
||||
--primary-foreground: #ffffff; /* white for contrast */
|
||||
--muted: #4c566a; /* nord3 */
|
||||
--muted-foreground: #81a1c1; /* nord9 */
|
||||
--accent: #d08770; /* nord12 */
|
||||
@@ -130,7 +130,7 @@
|
||||
--border: #665c54; /* gruvbox bg3 */
|
||||
--input: #3c3836; /* gruvbox bg1 */
|
||||
--primary: #fe8019; /* gruvbox orange */
|
||||
--primary-foreground: #282828; /* gruvbox bg0 */
|
||||
--primary-foreground: #ffffff; /* white for contrast */
|
||||
--muted: #665c54; /* gruvbox bg3 */
|
||||
--muted-foreground: #a89984; /* gruvbox gray */
|
||||
--accent: #fabd2f; /* gruvbox yellow */
|
||||
@@ -154,7 +154,7 @@
|
||||
--border: #565f89; /* tokyo-night comment */
|
||||
--input: #24283b; /* tokyo-night bg_highlight */
|
||||
--primary: #7aa2f7; /* tokyo-night blue */
|
||||
--primary-foreground: #1a1b26; /* tokyo-night bg */
|
||||
--primary-foreground: #ffffff; /* white for contrast */
|
||||
--muted: #565f89; /* tokyo-night comment */
|
||||
--muted-foreground: #9aa5ce; /* tokyo-night fg_dark */
|
||||
--accent: #ff9e64; /* tokyo-night orange */
|
||||
@@ -178,7 +178,7 @@
|
||||
--border: #6c7086; /* catppuccin overlay0 */
|
||||
--input: #313244; /* catppuccin surface0 */
|
||||
--primary: #cba6f7; /* catppuccin mauve */
|
||||
--primary-foreground: #1e1e2e; /* catppuccin base */
|
||||
--primary-foreground: #ffffff; /* white for contrast */
|
||||
--muted: #6c7086; /* catppuccin overlay0 */
|
||||
--muted-foreground: #a6adc8; /* catppuccin subtext0 */
|
||||
--accent: #fab387; /* catppuccin peach */
|
||||
@@ -202,7 +202,7 @@
|
||||
--border: #6e6a86; /* rose-pine muted */
|
||||
--input: #26233a; /* rose-pine surface */
|
||||
--primary: #c4a7e7; /* rose-pine iris */
|
||||
--primary-foreground: #191724; /* rose-pine base */
|
||||
--primary-foreground: #ffffff; /* white for contrast */
|
||||
--muted: #6e6a86; /* rose-pine muted */
|
||||
--muted-foreground: #908caa; /* rose-pine subtle */
|
||||
--accent: #f6c177; /* rose-pine gold */
|
||||
@@ -226,7 +226,7 @@
|
||||
--border: #5c6370; /* one-dark bg3 */
|
||||
--input: #3e4451; /* one-dark bg1 */
|
||||
--primary: #61afef; /* one-dark blue */
|
||||
--primary-foreground: #282c34; /* one-dark bg */
|
||||
--primary-foreground: #ffffff; /* white for contrast */
|
||||
--muted: #5c6370; /* one-dark bg3 */
|
||||
--muted-foreground: #828997; /* one-dark gray */
|
||||
--accent: #e06c75; /* one-dark red */
|
||||
@@ -250,7 +250,7 @@
|
||||
--border: #3c3c3c; /* material outline */
|
||||
--input: #1e1e1e; /* material surface */
|
||||
--primary: #bb86fc; /* material primary */
|
||||
--primary-foreground: #121212; /* material bg */
|
||||
--primary-foreground: #ffffff; /* white for contrast */
|
||||
--muted: #3c3c3c; /* material outline */
|
||||
--muted-foreground: #b3b3b3; /* material on-surface-variant */
|
||||
--accent: #ffab40; /* material secondary */
|
||||
@@ -274,7 +274,7 @@
|
||||
--border: #586e75; /* solarized base01 */
|
||||
--input: #073642; /* solarized base02 */
|
||||
--primary: #268bd2; /* solarized blue */
|
||||
--primary-foreground: #002b36; /* solarized base03 */
|
||||
--primary-foreground: #ffffff; /* white for contrast */
|
||||
--muted: #586e75; /* solarized base01 */
|
||||
--muted-foreground: #657b83; /* solarized base00 */
|
||||
--accent: #b58900; /* solarized yellow */
|
||||
|
||||
5
src/app/ui-showcase/page.tsx
Normal file
5
src/app/ui-showcase/page.tsx
Normal file
@@ -0,0 +1,5 @@
|
||||
import { UIShowcaseClient } from '@/components/ui-showcase/UIShowcaseClient';
|
||||
|
||||
export default function UIShowcasePage() {
|
||||
return <UIShowcaseClient />;
|
||||
}
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
import { useTheme } from '@/contexts/ThemeContext';
|
||||
import { Theme } from '@/lib/types';
|
||||
import { Button } from '@/components/ui/Button';
|
||||
|
||||
|
||||
const themes: { id: Theme; name: string; description: string }[] = [
|
||||
{ id: 'dark', name: 'Dark', description: 'Thème sombre par défaut' },
|
||||
@@ -17,6 +19,50 @@ const themes: { id: Theme; name: string; description: string }[] = [
|
||||
{ id: 'solarized', name: 'Solarized', description: 'Thème Solarized scientifique' },
|
||||
];
|
||||
|
||||
// Composant pour l'aperçu du thème
|
||||
function ThemePreview({ themeId, isSelected }: { themeId: Theme; isSelected: boolean }) {
|
||||
return (
|
||||
<div
|
||||
className={`w-16 h-12 rounded-lg border-2 overflow-hidden ${themeId}`}
|
||||
style={{
|
||||
borderColor: isSelected ? 'var(--primary)' : 'var(--border)',
|
||||
backgroundColor: 'var(--background)'
|
||||
}}
|
||||
>
|
||||
{/* Barre de titre */}
|
||||
<div
|
||||
className="h-3 w-full"
|
||||
style={{ backgroundColor: 'var(--card)' }}
|
||||
/>
|
||||
|
||||
{/* Contenu avec couleurs du thème */}
|
||||
<div className="p-1 h-9 flex flex-col gap-0.5">
|
||||
{/* Ligne de texte */}
|
||||
<div
|
||||
className="h-1 rounded-sm"
|
||||
style={{ backgroundColor: 'var(--foreground)' }}
|
||||
/>
|
||||
|
||||
{/* Couleurs d'accent */}
|
||||
<div className="flex gap-0.5">
|
||||
<div
|
||||
className="h-1 flex-1 rounded-sm"
|
||||
style={{ backgroundColor: 'var(--primary)' }}
|
||||
/>
|
||||
<div
|
||||
className="h-1 flex-1 rounded-sm"
|
||||
style={{ backgroundColor: 'var(--accent)' }}
|
||||
/>
|
||||
<div
|
||||
className="h-1 flex-1 rounded-sm"
|
||||
style={{ backgroundColor: 'var(--success)' }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function ThemeSelector() {
|
||||
const { theme, setTheme } = useTheme();
|
||||
|
||||
@@ -36,134 +82,19 @@ export function ThemeSelector() {
|
||||
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3">
|
||||
{themes.map((themeOption) => (
|
||||
<button
|
||||
<Button
|
||||
key={themeOption.id}
|
||||
onClick={() => setTheme(themeOption.id)}
|
||||
className={`
|
||||
p-4 rounded-lg border text-left transition-all duration-200 group
|
||||
${theme === themeOption.id
|
||||
? 'border-[var(--primary)] bg-[color-mix(in_srgb,var(--primary)_15%,transparent)] shadow-lg shadow-[var(--primary)]/20'
|
||||
: 'border-[var(--border)] hover:border-[var(--primary)] hover:bg-[color-mix(in_srgb,var(--primary)_8%,transparent)] hover:shadow-md'
|
||||
}
|
||||
`}
|
||||
variant={theme === themeOption.id ? 'selected' : 'secondary'}
|
||||
className="p-4 h-auto text-left justify-start"
|
||||
>
|
||||
<div className="flex items-start gap-3">
|
||||
{/* Aperçu du thème */}
|
||||
<div className="flex-shrink-0">
|
||||
<div
|
||||
className="w-16 h-12 rounded-lg border-2 overflow-hidden"
|
||||
style={{
|
||||
borderColor: theme === themeOption.id ? 'var(--primary)' : 'var(--border)',
|
||||
backgroundColor: themeOption.id === 'light' ? '#f1f5f9' :
|
||||
themeOption.id === 'dark' ? '#1e293b' :
|
||||
themeOption.id === 'dracula' ? '#282a36' :
|
||||
themeOption.id === 'monokai' ? '#272822' :
|
||||
themeOption.id === 'nord' ? '#2e3440' :
|
||||
themeOption.id === 'gruvbox' ? '#282828' :
|
||||
themeOption.id === 'tokyo_night' ? '#1a1b26' :
|
||||
themeOption.id === 'catppuccin' ? '#1e1e2e' :
|
||||
themeOption.id === 'rose_pine' ? '#191724' :
|
||||
themeOption.id === 'one_dark' ? '#282c34' :
|
||||
themeOption.id === 'material' ? '#121212' :
|
||||
'#002b36'
|
||||
}}
|
||||
>
|
||||
{/* Barre de titre */}
|
||||
<div
|
||||
className="h-3 w-full"
|
||||
style={{
|
||||
backgroundColor: themeOption.id === 'light' ? '#ffffff' :
|
||||
themeOption.id === 'dark' ? '#334155' :
|
||||
themeOption.id === 'dracula' ? '#44475a' :
|
||||
themeOption.id === 'monokai' ? '#3e3d32' :
|
||||
themeOption.id === 'nord' ? '#3b4252' :
|
||||
themeOption.id === 'gruvbox' ? '#3c3836' :
|
||||
themeOption.id === 'tokyo_night' ? '#24283b' :
|
||||
themeOption.id === 'catppuccin' ? '#313244' :
|
||||
themeOption.id === 'rose_pine' ? '#26233a' :
|
||||
themeOption.id === 'one_dark' ? '#3e4451' :
|
||||
themeOption.id === 'material' ? '#1e1e1e' :
|
||||
'#073642'
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Contenu avec couleurs du thème */}
|
||||
<div className="p-1 h-9 flex flex-col gap-0.5">
|
||||
{/* Ligne de texte */}
|
||||
<div
|
||||
className="h-1 rounded-sm"
|
||||
style={{
|
||||
backgroundColor: themeOption.id === 'light' ? '#0f172a' :
|
||||
themeOption.id === 'dark' ? '#f1f5f9' :
|
||||
themeOption.id === 'dracula' ? '#f8f8f2' :
|
||||
themeOption.id === 'monokai' ? '#f8f8f2' :
|
||||
themeOption.id === 'nord' ? '#d8dee9' :
|
||||
themeOption.id === 'gruvbox' ? '#ebdbb2' :
|
||||
themeOption.id === 'tokyo_night' ? '#a9b1d6' :
|
||||
themeOption.id === 'catppuccin' ? '#cdd6f4' :
|
||||
themeOption.id === 'rose_pine' ? '#e0def4' :
|
||||
themeOption.id === 'one_dark' ? '#abb2bf' :
|
||||
themeOption.id === 'material' ? '#ffffff' :
|
||||
'#93a1a1'
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Couleurs d'accent */}
|
||||
<div className="flex gap-0.5">
|
||||
<div
|
||||
className="h-1 flex-1 rounded-sm"
|
||||
style={{
|
||||
backgroundColor: themeOption.id === 'light' ? '#0891b2' :
|
||||
themeOption.id === 'dark' ? '#06b6d4' :
|
||||
themeOption.id === 'dracula' ? '#ff79c6' :
|
||||
themeOption.id === 'monokai' ? '#f92672' :
|
||||
themeOption.id === 'nord' ? '#88c0d0' :
|
||||
themeOption.id === 'gruvbox' ? '#fe8019' :
|
||||
themeOption.id === 'tokyo_night' ? '#7aa2f7' :
|
||||
themeOption.id === 'catppuccin' ? '#cba6f7' :
|
||||
themeOption.id === 'rose_pine' ? '#c4a7e7' :
|
||||
themeOption.id === 'one_dark' ? '#61afef' :
|
||||
themeOption.id === 'material' ? '#bb86fc' :
|
||||
'#268bd2'
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
className="h-1 flex-1 rounded-sm"
|
||||
style={{
|
||||
backgroundColor: themeOption.id === 'light' ? '#d97706' :
|
||||
themeOption.id === 'dark' ? '#f59e0b' :
|
||||
themeOption.id === 'dracula' ? '#ffb86c' :
|
||||
themeOption.id === 'monokai' ? '#fd971f' :
|
||||
themeOption.id === 'nord' ? '#d08770' :
|
||||
themeOption.id === 'gruvbox' ? '#fabd2f' :
|
||||
themeOption.id === 'tokyo_night' ? '#ff9e64' :
|
||||
themeOption.id === 'catppuccin' ? '#fab387' :
|
||||
themeOption.id === 'rose_pine' ? '#f6c177' :
|
||||
themeOption.id === 'one_dark' ? '#e06c75' :
|
||||
themeOption.id === 'material' ? '#ffab40' :
|
||||
'#b58900'
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
className="h-1 flex-1 rounded-sm"
|
||||
style={{
|
||||
backgroundColor: themeOption.id === 'light' ? '#059669' :
|
||||
themeOption.id === 'dark' ? '#10b981' :
|
||||
themeOption.id === 'dracula' ? '#50fa7b' :
|
||||
themeOption.id === 'monokai' ? '#a6e22e' :
|
||||
themeOption.id === 'nord' ? '#a3be8c' :
|
||||
themeOption.id === 'gruvbox' ? '#b8bb26' :
|
||||
themeOption.id === 'tokyo_night' ? '#9ece6a' :
|
||||
themeOption.id === 'catppuccin' ? '#a6e3a1' :
|
||||
themeOption.id === 'rose_pine' ? '#9ccfd8' :
|
||||
themeOption.id === 'one_dark' ? '#98c379' :
|
||||
themeOption.id === 'material' ? '#4caf50' :
|
||||
'#859900'
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ThemePreview
|
||||
themeId={themeOption.id}
|
||||
isSelected={theme === themeOption.id}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex-1 min-w-0">
|
||||
@@ -180,7 +111,7 @@ export function ThemeSelector() {
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -53,6 +53,28 @@ export function GeneralSettingsPageClient({ initialTags }: GeneralSettingsPageCl
|
||||
<ThemeSelector />
|
||||
</div>
|
||||
|
||||
{/* UI Showcase */}
|
||||
<Card>
|
||||
<CardContent className="p-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h3 className="text-lg font-medium text-[var(--foreground)] mb-2">
|
||||
🎨 UI Components Showcase
|
||||
</h3>
|
||||
<p className="text-sm text-[var(--muted-foreground)]">
|
||||
Visualisez tous les composants UI disponibles avec différents thèmes
|
||||
</p>
|
||||
</div>
|
||||
<Link
|
||||
href="/ui-showcase"
|
||||
className="inline-flex items-center px-4 py-2 bg-[var(--primary)] text-[var(--primary-foreground)] rounded-md hover:bg-[color-mix(in_srgb,var(--primary)_90%,transparent)] transition-colors font-medium"
|
||||
>
|
||||
Voir la démo
|
||||
</Link>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Gestion des tags */}
|
||||
<TagsManagement
|
||||
tags={tags}
|
||||
|
||||
327
src/components/ui-showcase/UIShowcaseClient.tsx
Normal file
327
src/components/ui-showcase/UIShowcaseClient.tsx
Normal file
@@ -0,0 +1,327 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import { Button } from '@/components/ui/Button';
|
||||
import { Badge } from '@/components/ui/Badge';
|
||||
import { Alert, AlertTitle, AlertDescription } from '@/components/ui/Alert';
|
||||
import { Input } from '@/components/ui/Input';
|
||||
import { StyledCard } from '@/components/ui/StyledCard';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/Card';
|
||||
import { ThemeSelector } from '@/components/ThemeSelector';
|
||||
|
||||
export function UIShowcaseClient() {
|
||||
const [inputValue, setInputValue] = useState('');
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-[var(--background)] p-8">
|
||||
<div className="max-w-6xl mx-auto space-y-16">
|
||||
{/* Header */}
|
||||
<div className="text-center space-y-6">
|
||||
<h1 className="text-4xl font-mono font-bold text-[var(--foreground)]">
|
||||
🎨 UI Components Showcase
|
||||
</h1>
|
||||
<p className="text-lg text-[var(--muted-foreground)]">
|
||||
Démonstration de tous les composants UI disponibles
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Theme Selector */}
|
||||
<section className="space-y-8">
|
||||
<div className="text-center">
|
||||
<h2 className="text-2xl font-mono font-semibold text-[var(--foreground)] mb-6">
|
||||
🎨 Sélecteur de Thèmes
|
||||
</h2>
|
||||
<p className="text-[var(--muted-foreground)] mb-8">
|
||||
Changez de thème pour voir comment tous les composants s'adaptent
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="max-w-4xl mx-auto">
|
||||
<div className="bg-[var(--card)]/30 border border-[var(--border)]/50 rounded-lg p-6 backdrop-blur-sm">
|
||||
<ThemeSelector />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Buttons Section */}
|
||||
<section className="space-y-8">
|
||||
<h2 className="text-2xl font-mono font-semibold text-[var(--foreground)] border-b border-[var(--border)] pb-3">
|
||||
Buttons
|
||||
</h2>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||
<div className="space-y-6">
|
||||
<h3 className="text-lg font-medium text-[var(--foreground)]">Variants</h3>
|
||||
<div className="space-y-4">
|
||||
<Button variant="primary">Primary Button</Button>
|
||||
<Button variant="secondary">Secondary Button</Button>
|
||||
<Button variant="ghost">Ghost Button</Button>
|
||||
<Button variant="destructive">Destructive Button</Button>
|
||||
<Button variant="success">Success Button</Button>
|
||||
<Button variant="selected">Selected Button</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-6">
|
||||
<h3 className="text-lg font-medium text-[var(--foreground)]">Sizes</h3>
|
||||
<div className="space-y-4">
|
||||
<Button size="sm">Small Button</Button>
|
||||
<Button size="md">Medium Button</Button>
|
||||
<Button size="lg">Large Button</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-6">
|
||||
<h3 className="text-lg font-medium text-[var(--foreground)]">States</h3>
|
||||
<div className="space-y-4">
|
||||
<Button>Normal State</Button>
|
||||
<Button disabled>Disabled State</Button>
|
||||
<Button className="opacity-50">Loading State</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Badges Section */}
|
||||
<section className="space-y-8">
|
||||
<h2 className="text-2xl font-mono font-semibold text-[var(--foreground)] border-b border-[var(--border)] pb-3">
|
||||
Badges
|
||||
</h2>
|
||||
|
||||
<div className="space-y-6">
|
||||
<div className="flex flex-wrap gap-4">
|
||||
<Badge variant="default">Default Badge</Badge>
|
||||
<Badge variant="primary">Primary Badge</Badge>
|
||||
<Badge variant="success">Success Badge</Badge>
|
||||
<Badge variant="destructive">Destructive Badge</Badge>
|
||||
<Badge variant="accent">Accent Badge</Badge>
|
||||
<Badge variant="purple">Purple Badge</Badge>
|
||||
<Badge variant="yellow">Yellow Badge</Badge>
|
||||
<Badge variant="green">Green Badge</Badge>
|
||||
<Badge variant="blue">Blue Badge</Badge>
|
||||
<Badge variant="gray">Gray Badge</Badge>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Alerts Section */}
|
||||
<section className="space-y-8">
|
||||
<h2 className="text-2xl font-mono font-semibold text-[var(--foreground)] border-b border-[var(--border)] pb-3">
|
||||
Alerts
|
||||
</h2>
|
||||
|
||||
<div className="space-y-6">
|
||||
<Alert variant="default">
|
||||
<AlertTitle>Information</AlertTitle>
|
||||
<AlertDescription>
|
||||
Ceci est une alerte par défaut avec des informations importantes.
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
|
||||
<Alert variant="success">
|
||||
<AlertTitle>Succès</AlertTitle>
|
||||
<AlertDescription>
|
||||
Opération terminée avec succès ! Toutes les données ont été sauvegardées.
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
|
||||
<Alert variant="destructive">
|
||||
<AlertTitle>Erreur</AlertTitle>
|
||||
<AlertDescription>
|
||||
Une erreur s'est produite lors du traitement de votre demande.
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
|
||||
<Alert variant="warning">
|
||||
<AlertTitle>Attention</AlertTitle>
|
||||
<AlertDescription>
|
||||
Veuillez vérifier vos informations avant de continuer.
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
|
||||
<Alert variant="info">
|
||||
<AlertTitle>Conseil</AlertTitle>
|
||||
<AlertDescription>
|
||||
Astuce : Vous pouvez utiliser les raccourcis clavier pour naviguer plus rapidement.
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Inputs Section */}
|
||||
<section className="space-y-8">
|
||||
<h2 className="text-2xl font-mono font-semibold text-[var(--foreground)] border-b border-[var(--border)] pb-3">
|
||||
Inputs
|
||||
</h2>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
|
||||
<div className="space-y-6">
|
||||
<h3 className="text-lg font-medium text-[var(--foreground)]">Types</h3>
|
||||
<div className="space-y-4">
|
||||
<Input placeholder="Texte normal" />
|
||||
<Input type="email" placeholder="Adresse email" />
|
||||
<Input type="password" placeholder="Mot de passe" />
|
||||
<Input type="number" placeholder="Nombre" />
|
||||
<Input type="search" placeholder="Rechercher..." />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-6">
|
||||
<h3 className="text-lg font-medium text-[var(--foreground)]">États</h3>
|
||||
<div className="space-y-4">
|
||||
<Input placeholder="État normal" />
|
||||
<Input placeholder="État focus" autoFocus />
|
||||
<Input placeholder="État désactivé" disabled />
|
||||
<Input variant="error" placeholder="État erreur" />
|
||||
<Input value={inputValue} onChange={(e) => setInputValue(e.target.value)} placeholder="État avec valeur" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Cards Section */}
|
||||
<section className="space-y-6">
|
||||
<h2 className="text-2xl font-mono font-semibold text-[var(--foreground)] border-b border-[var(--border)] pb-2">
|
||||
Cards
|
||||
</h2>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{/* Standard Cards */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Card Standard</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p className="text-[var(--muted-foreground)]">
|
||||
Ceci est une carte standard avec header et contenu.
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card variant="elevated">
|
||||
<CardHeader>
|
||||
<CardTitle>Card Élevée</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p className="text-[var(--muted-foreground)]">
|
||||
Cette carte a une ombre plus prononcée.
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card variant="bordered">
|
||||
<CardHeader>
|
||||
<CardTitle>Card Bordée</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p className="text-[var(--muted-foreground)]">
|
||||
Cette carte a une bordure colorée.
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Styled Cards */}
|
||||
<StyledCard color="primary" className="p-4">
|
||||
<h3 className="font-medium mb-2">Styled Card Primary</h3>
|
||||
<p className="text-sm opacity-80">
|
||||
Carte avec couleur primaire et fond subtil.
|
||||
</p>
|
||||
</StyledCard>
|
||||
|
||||
<StyledCard color="success" className="p-4">
|
||||
<h3 className="font-medium mb-2">Styled Card Success</h3>
|
||||
<p className="text-sm opacity-80">
|
||||
Carte avec couleur de succès.
|
||||
</p>
|
||||
</StyledCard>
|
||||
|
||||
<StyledCard color="destructive" className="p-4">
|
||||
<h3 className="font-medium mb-2">Styled Card Destructive</h3>
|
||||
<p className="text-sm opacity-80">
|
||||
Carte avec couleur destructive.
|
||||
</p>
|
||||
</StyledCard>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Interactive Demo */}
|
||||
<section className="space-y-6">
|
||||
<h2 className="text-2xl font-mono font-semibold text-[var(--foreground)] border-b border-[var(--border)] pb-2">
|
||||
Démonstration Interactive
|
||||
</h2>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Formulaire d'exemple</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-[var(--foreground)] mb-2">
|
||||
Nom d'utilisateur
|
||||
</label>
|
||||
<Input placeholder="Entrez votre nom" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-[var(--foreground)] mb-2">
|
||||
Email
|
||||
</label>
|
||||
<Input type="email" placeholder="votre@email.com" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-[var(--foreground)] mb-2">
|
||||
Statut
|
||||
</label>
|
||||
<div className="flex gap-2">
|
||||
<Badge variant="success">Actif</Badge>
|
||||
<Badge variant="gray">En attente</Badge>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-3 pt-4">
|
||||
<Button variant="primary">Enregistrer</Button>
|
||||
<Button variant="secondary">Annuler</Button>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Notifications</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<Alert variant="success">
|
||||
<AlertTitle>Bienvenue !</AlertTitle>
|
||||
<AlertDescription>
|
||||
Votre compte a été créé avec succès.
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
|
||||
<Alert variant="warning">
|
||||
<AlertTitle>Mise à jour disponible</AlertTitle>
|
||||
<AlertDescription>
|
||||
Une nouvelle version de l'application est disponible.
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
|
||||
<div className="flex gap-2">
|
||||
<Button size="sm">Voir les détails</Button>
|
||||
<Button size="sm" variant="ghost">Ignorer</Button>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Footer */}
|
||||
<div className="text-center pt-8 border-t border-[var(--border)]">
|
||||
<p className="text-[var(--muted-foreground)]">
|
||||
Cette page est accessible via <code className="bg-[var(--card)] px-2 py-1 rounded text-sm">/ui-showcase</code>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
58
src/components/ui/Alert.tsx
Normal file
58
src/components/ui/Alert.tsx
Normal file
@@ -0,0 +1,58 @@
|
||||
import { HTMLAttributes, forwardRef } from 'react';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
interface AlertProps extends HTMLAttributes<HTMLDivElement> {
|
||||
variant?: 'default' | 'success' | 'destructive' | 'warning' | 'info';
|
||||
}
|
||||
|
||||
const Alert = forwardRef<HTMLDivElement, AlertProps>(
|
||||
({ className, variant = 'default', ...props }, ref) => {
|
||||
const variants = {
|
||||
default: 'bg-[var(--card)] text-[var(--foreground)] border border-[var(--border)]',
|
||||
success: 'bg-[color-mix(in_srgb,var(--success)_10%,transparent)] text-[var(--success)] border border-[color-mix(in_srgb,var(--success)_20%,var(--border))]',
|
||||
destructive: 'bg-[color-mix(in_srgb,var(--destructive)_10%,transparent)] text-[var(--destructive)] border border-[color-mix(in_srgb,var(--destructive)_20%,var(--border))]',
|
||||
warning: 'bg-[color-mix(in_srgb,var(--accent)_10%,transparent)] text-[var(--accent)] border border-[color-mix(in_srgb,var(--accent)_20%,var(--border))]',
|
||||
info: 'bg-[color-mix(in_srgb,var(--primary)_10%,transparent)] text-[var(--primary)] border border-[color-mix(in_srgb,var(--primary)_20%,var(--border))]'
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'relative w-full rounded-lg border p-4',
|
||||
variants[variant],
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
Alert.displayName = 'Alert';
|
||||
|
||||
const AlertTitle = forwardRef<HTMLParagraphElement, HTMLAttributes<HTMLHeadingElement>>(
|
||||
({ className, ...props }, ref) => (
|
||||
<h5
|
||||
ref={ref}
|
||||
className={cn('mb-1 font-medium leading-none tracking-tight', className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
);
|
||||
|
||||
AlertTitle.displayName = 'AlertTitle';
|
||||
|
||||
const AlertDescription = forwardRef<HTMLParagraphElement, HTMLAttributes<HTMLParagraphElement>>(
|
||||
({ className, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn('text-sm [&_p]:leading-relaxed', className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
);
|
||||
|
||||
AlertDescription.displayName = 'AlertDescription';
|
||||
|
||||
export { Alert, AlertTitle, AlertDescription };
|
||||
@@ -1,38 +1,33 @@
|
||||
import { HTMLAttributes, forwardRef } from 'react';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
interface BadgeProps extends HTMLAttributes<HTMLSpanElement> {
|
||||
variant?: 'default' | 'primary' | 'success' | 'warning' | 'danger' | 'outline';
|
||||
size?: 'sm' | 'md';
|
||||
interface BadgeProps extends HTMLAttributes<HTMLDivElement> {
|
||||
variant?: 'default' | 'primary' | 'success' | 'destructive' | 'accent' | 'purple' | 'yellow' | 'green' | 'blue' | 'gray';
|
||||
}
|
||||
|
||||
const Badge = forwardRef<HTMLSpanElement, BadgeProps>(
|
||||
({ className, variant = 'default', size = 'md', ...props }, ref) => {
|
||||
const baseStyles = 'inline-flex items-center font-mono font-medium transition-all duration-200';
|
||||
|
||||
const Badge = forwardRef<HTMLDivElement, BadgeProps>(
|
||||
({ className, variant = 'default', ...props }, ref) => {
|
||||
const variants = {
|
||||
default: 'bg-[var(--card)] text-[var(--muted-foreground)] border border-[var(--border)]',
|
||||
primary: 'bg-[var(--primary)]/20 text-[var(--primary)] border border-[var(--primary)]/30',
|
||||
success: 'bg-[var(--success)]/20 text-[var(--success)] border border-[var(--success)]/30',
|
||||
warning: 'bg-[var(--accent)]/20 text-[var(--accent)] border border-[var(--accent)]/30',
|
||||
danger: 'bg-[var(--destructive)]/20 text-[var(--destructive)] border border-[var(--destructive)]/30',
|
||||
outline: 'bg-transparent text-[var(--muted-foreground)] border border-[var(--border)] hover:bg-[var(--card-hover)] hover:text-[var(--foreground)]'
|
||||
default: 'bg-[var(--card)] text-[var(--foreground)] border border-[var(--border)]',
|
||||
primary: 'bg-[color-mix(in_srgb,var(--primary)_10%,transparent)] text-[var(--primary)] border border-[color-mix(in_srgb,var(--primary)_25%,var(--border))]',
|
||||
success: 'bg-[color-mix(in_srgb,var(--success)_10%,transparent)] text-[var(--success)] border border-[color-mix(in_srgb,var(--success)_25%,var(--border))]',
|
||||
destructive: 'bg-[color-mix(in_srgb,var(--destructive)_10%,transparent)] text-[var(--destructive)] border border-[color-mix(in_srgb,var(--destructive)_25%,var(--border))]',
|
||||
accent: 'bg-[color-mix(in_srgb,var(--accent)_10%,transparent)] text-[var(--accent)] border border-[color-mix(in_srgb,var(--accent)_25%,var(--border))]',
|
||||
purple: 'bg-[color-mix(in_srgb,var(--purple)_10%,transparent)] text-[var(--purple)] border border-[color-mix(in_srgb,var(--purple)_25%,var(--border))]',
|
||||
yellow: 'bg-[color-mix(in_srgb,var(--yellow)_10%,transparent)] text-[var(--yellow)] border border-[color-mix(in_srgb,var(--yellow)_25%,var(--border))]',
|
||||
green: 'bg-[color-mix(in_srgb,var(--green)_10%,transparent)] text-[var(--green)] border border-[color-mix(in_srgb,var(--green)_25%,var(--border))]',
|
||||
blue: 'bg-[color-mix(in_srgb,var(--blue)_10%,transparent)] text-[var(--blue)] border border-[color-mix(in_srgb,var(--blue)_25%,var(--border))]',
|
||||
gray: 'bg-[color-mix(in_srgb,var(--gray)_10%,transparent)] text-[var(--gray)] border border-[color-mix(in_srgb,var(--gray)_25%,var(--border))]'
|
||||
};
|
||||
|
||||
const sizes = {
|
||||
sm: 'px-1.5 py-0.5 text-xs rounded',
|
||||
md: 'px-2 py-1 text-xs rounded-md'
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<span
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn(
|
||||
baseStyles,
|
||||
'inline-flex items-center rounded-md px-2.5 py-0.5 text-xs font-medium transition-colors',
|
||||
variants[variant],
|
||||
sizes[size],
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
@@ -41,4 +36,4 @@ const Badge = forwardRef<HTMLSpanElement, BadgeProps>(
|
||||
|
||||
Badge.displayName = 'Badge';
|
||||
|
||||
export { Badge };
|
||||
export { Badge };
|
||||
@@ -2,36 +2,36 @@ import { ButtonHTMLAttributes, forwardRef } from 'react';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
variant?: 'primary' | 'secondary' | 'danger' | 'ghost';
|
||||
variant?: 'primary' | 'secondary' | 'ghost' | 'destructive' | 'success' | 'selected';
|
||||
size?: 'sm' | 'md' | 'lg';
|
||||
}
|
||||
|
||||
const Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
({ className, variant = 'primary', size = 'md', ...props }, ref) => {
|
||||
const baseStyles = 'inline-flex items-center justify-center font-mono font-medium transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-[var(--background)] disabled:opacity-50 disabled:cursor-not-allowed';
|
||||
|
||||
const variants = {
|
||||
primary: 'bg-[var(--primary)] hover:bg-[var(--primary)]/80 text-[var(--primary-foreground)] border border-[var(--primary)]/30 shadow-[var(--primary)]/20 shadow-lg hover:shadow-[var(--primary)]/30 focus:ring-[var(--primary)]',
|
||||
secondary: 'bg-[var(--card)] hover:bg-[var(--card-hover)] text-[var(--foreground)] border border-[var(--border)] shadow-[var(--muted)]/20 shadow-lg hover:shadow-[var(--muted)]/30 focus:ring-[var(--muted)]',
|
||||
danger: 'bg-[var(--destructive)] hover:bg-[var(--destructive)]/80 text-white border border-[var(--destructive)]/30 shadow-[var(--destructive)]/20 shadow-lg hover:shadow-[var(--destructive)]/30 focus:ring-[var(--destructive)]',
|
||||
ghost: 'bg-transparent hover:bg-[var(--card)]/50 text-[var(--muted-foreground)] hover:text-[var(--foreground)] border border-[var(--border)]/50 hover:border-[var(--border)] focus:ring-[var(--muted)]'
|
||||
primary: 'bg-[var(--primary)] text-[var(--primary-foreground)] hover:bg-[color-mix(in_srgb,var(--primary)_90%,transparent)]',
|
||||
secondary: 'bg-[var(--card)] text-[var(--foreground)] border border-[var(--border)] hover:bg-[var(--card-hover)]',
|
||||
ghost: 'text-[var(--foreground)] hover:bg-[var(--card-hover)]',
|
||||
destructive: 'bg-[var(--destructive)] text-white hover:bg-[color-mix(in_srgb,var(--destructive)_90%,transparent)]',
|
||||
success: 'bg-[var(--success)] text-white hover:bg-[color-mix(in_srgb,var(--success)_90%,transparent)]',
|
||||
selected: 'bg-[color-mix(in_srgb,var(--primary)_15%,transparent)] text-[var(--foreground)] border border-[var(--primary)] hover:bg-[color-mix(in_srgb,var(--primary)_20%,transparent)]'
|
||||
};
|
||||
|
||||
|
||||
const sizes = {
|
||||
sm: 'px-3 py-1.5 text-xs rounded-md',
|
||||
md: 'px-4 py-2 text-sm rounded-lg',
|
||||
lg: 'px-6 py-3 text-base rounded-lg'
|
||||
sm: 'px-3 py-1.5 text-sm',
|
||||
md: 'px-4 py-2 text-sm',
|
||||
lg: 'px-6 py-3 text-base'
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<button
|
||||
ref={ref}
|
||||
className={cn(
|
||||
baseStyles,
|
||||
'inline-flex items-center justify-center rounded-md font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--primary)] focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
|
||||
variants[variant],
|
||||
sizes[size],
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
@@ -40,4 +40,4 @@ const Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
|
||||
Button.displayName = 'Button';
|
||||
|
||||
export { Button };
|
||||
export { Button };
|
||||
@@ -2,43 +2,31 @@ import { InputHTMLAttributes, forwardRef } from 'react';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
interface InputProps extends InputHTMLAttributes<HTMLInputElement> {
|
||||
label?: string;
|
||||
error?: string;
|
||||
variant?: 'default' | 'error';
|
||||
}
|
||||
|
||||
const Input = forwardRef<HTMLInputElement, InputProps>(
|
||||
({ className, label, error, ...props }, ref) => {
|
||||
({ className, variant = 'default', type, ...props }, ref) => {
|
||||
const variants = {
|
||||
default: 'border border-[var(--border)]/50 bg-[var(--input)] text-[var(--foreground)] placeholder:text-[var(--muted-foreground)] focus:border-[var(--primary)]/70 focus:ring-1 focus:ring-[var(--primary)]/20',
|
||||
error: 'border border-[var(--destructive)]/50 bg-[var(--input)] text-[var(--foreground)] placeholder:text-[var(--muted-foreground)] focus:border-[var(--destructive)]/70 focus:ring-1 focus:ring-[var(--destructive)]/20'
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
{label && (
|
||||
<label className="block text-sm font-mono font-medium text-[var(--muted-foreground)] uppercase tracking-wider">
|
||||
{label}
|
||||
</label>
|
||||
<input
|
||||
type={type}
|
||||
className={cn(
|
||||
'flex h-10 w-full rounded-md px-3 py-2 text-sm file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50 transition-colors',
|
||||
variants[variant],
|
||||
className
|
||||
)}
|
||||
<input
|
||||
className={cn(
|
||||
'w-full px-3 py-2 bg-[var(--input)] border border-[var(--border)]/50 rounded-lg',
|
||||
'text-[var(--foreground)] font-mono text-sm placeholder-[var(--muted-foreground)]',
|
||||
'focus:outline-none focus:ring-2 focus:ring-[var(--primary)]/50 focus:border-[var(--primary)]/50',
|
||||
'hover:border-[var(--border)] transition-all duration-200',
|
||||
'backdrop-blur-sm',
|
||||
error && 'border-[var(--destructive)]/50 focus:ring-[var(--destructive)]/50 focus:border-[var(--destructive)]/50',
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
{error && (
|
||||
<p className="text-xs font-mono text-[var(--destructive)] flex items-center gap-1">
|
||||
<span className="text-[var(--destructive)]">⚠</span>
|
||||
{error}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
Input.displayName = 'Input';
|
||||
|
||||
export { Input };
|
||||
export { Input };
|
||||
49
src/components/ui/StyledCard.tsx
Normal file
49
src/components/ui/StyledCard.tsx
Normal file
@@ -0,0 +1,49 @@
|
||||
import { HTMLAttributes, forwardRef } from 'react';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
interface StyledCardProps extends HTMLAttributes<HTMLDivElement> {
|
||||
variant?: 'default' | 'outline' | 'elevated' | 'bordered' | 'column';
|
||||
color?: 'default' | 'primary' | 'success' | 'destructive' | 'accent' | 'purple' | 'yellow' | 'green' | 'blue' | 'gray';
|
||||
}
|
||||
|
||||
const StyledCard = forwardRef<HTMLDivElement, StyledCardProps>(
|
||||
({ className, variant = 'default', color = 'default', ...props }, ref) => {
|
||||
const variants = {
|
||||
default: 'bg-[var(--card)]/50 border border-[var(--border)]/50',
|
||||
outline: 'bg-transparent border border-[var(--border)]',
|
||||
elevated: 'bg-[var(--card)]/80 border border-[var(--border)]/50 shadow-lg shadow-[var(--card)]/20',
|
||||
bordered: 'bg-[var(--card)]/50 border border-[var(--primary)]/30 shadow-[var(--primary)]/10 shadow-lg',
|
||||
column: 'bg-[var(--card-column)] border border-[var(--border)]/50 shadow-lg shadow-[var(--card)]/20'
|
||||
};
|
||||
|
||||
const colors = {
|
||||
default: '',
|
||||
primary: 'bg-[color-mix(in_srgb,var(--primary)_8%,transparent)] border-[color-mix(in_srgb,var(--primary)_25%,var(--border))] text-[var(--primary)]',
|
||||
success: 'bg-[color-mix(in_srgb,var(--success)_8%,transparent)] border-[color-mix(in_srgb,var(--success)_25%,var(--border))] text-[var(--success)]',
|
||||
destructive: 'bg-[color-mix(in_srgb,var(--destructive)_8%,transparent)] border-[color-mix(in_srgb,var(--destructive)_25%,var(--border))] text-[var(--destructive)]',
|
||||
accent: 'bg-[color-mix(in_srgb,var(--accent)_8%,transparent)] border-[color-mix(in_srgb,var(--accent)_25%,var(--border))] text-[var(--accent)]',
|
||||
purple: 'bg-[color-mix(in_srgb,var(--purple)_8%,transparent)] border-[color-mix(in_srgb,var(--purple)_25%,var(--border))] text-[var(--purple)]',
|
||||
yellow: 'bg-[color-mix(in_srgb,var(--yellow)_8%,transparent)] border-[color-mix(in_srgb,var(--yellow)_25%,var(--border))] text-[var(--yellow)]',
|
||||
green: 'bg-[color-mix(in_srgb,var(--green)_8%,transparent)] border-[color-mix(in_srgb,var(--green)_25%,var(--border))] text-[var(--green)]',
|
||||
blue: 'bg-[color-mix(in_srgb,var(--blue)_8%,transparent)] border-[color-mix(in_srgb,var(--blue)_25%,var(--border))] text-[var(--blue)]',
|
||||
gray: 'bg-[color-mix(in_srgb,var(--gray)_8%,transparent)] border-[color-mix(in_srgb,var(--gray)_25%,var(--border))] text-[var(--gray)]'
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'rounded-lg backdrop-blur-sm transition-all duration-200',
|
||||
variants[variant],
|
||||
colors[color],
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
StyledCard.displayName = 'StyledCard';
|
||||
|
||||
export { StyledCard };
|
||||
11
src/components/ui/index.ts
Normal file
11
src/components/ui/index.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
// Composants UI de base
|
||||
export { Button } from './Button';
|
||||
export { Badge } from './Badge';
|
||||
export { Alert, AlertTitle, AlertDescription } from './Alert';
|
||||
export { Input } from './Input';
|
||||
export { StyledCard } from './StyledCard';
|
||||
|
||||
// Composants existants
|
||||
export { Card, CardHeader, CardTitle, CardContent, CardFooter } from './Card';
|
||||
export { Header } from './Header';
|
||||
export { FontSizeToggle } from './FontSizeToggle';
|
||||
Reference in New Issue
Block a user