mirror of
https://github.com/coleam00/Archon.git
synced 2025-12-24 02:39:17 -05:00
First implementation of the style guide.
This commit is contained in:
967
PRPs/interactive-style-guide.md
Normal file
967
PRPs/interactive-style-guide.md
Normal file
@@ -0,0 +1,967 @@
|
||||
# PRP: Interactive Style Guide Implementation
|
||||
|
||||
## Overview
|
||||
Implement a comprehensive interactive style guide page for the Archon UI system that provides configurators for glass morphism components, standardized modal types, layout templates, and generates copy-ready code with AI-friendly documentation.
|
||||
|
||||
## Context for AI Implementation
|
||||
This PRP provides complete implementation guidance for creating an interactive style guide at `/style-guide` route. The style guide will showcase existing Archon glass morphism components with interactive configurators, allowing developers to visually configure components and copy production-ready code.
|
||||
|
||||
**CRITICAL LAYOUT CONSTRAINT**: The Archon app has a fixed left navigation sidebar and potential right chat sidebar. The style guide must work within these constraints:
|
||||
- Main app navigation stays on the left (always visible)
|
||||
- Style guide has its own internal navigation (sections/components)
|
||||
- Layout templates must include collapsible sidebars that work within the main content area
|
||||
- The "Sidebar Layout" template should demonstrate a collapsible project sidebar (like ProjectsView)
|
||||
|
||||
**CRITICAL COMPONENT CONSISTENCY**: The style guide must use existing Radix UI primitives and extend them, not create new competing components. All components should import from `/features/ui/primitives/`.
|
||||
|
||||
## Existing Patterns to Follow
|
||||
|
||||
### Glass Morphism Styles (Reference)
|
||||
- **Location**: `/archon-ui-main/src/features/ui/primitives/styles.ts`
|
||||
- Glass backgrounds, borders, shadows with glow effects
|
||||
- Priority colors system already defined
|
||||
- Use existing `glassmorphism` and `compoundStyles` objects
|
||||
|
||||
### Button Component (Reference)
|
||||
- **Location**: `/archon-ui-main/src/features/ui/primitives/button.tsx`
|
||||
- Variants: default, destructive, outline, ghost, link, cyan, knowledge
|
||||
- Sizes: xs, sm, default, lg, icon
|
||||
- Loading state support built-in
|
||||
|
||||
### Modal Patterns (Reference)
|
||||
- **Location**: `/archon-ui-main/src/features/ui/components/DeleteConfirmModal.tsx`
|
||||
- Uses AlertDialog from primitives
|
||||
- Size prop support (compact, default, large)
|
||||
- Proper glass morphism styling
|
||||
|
||||
### Routing Pattern (Reference)
|
||||
- **Location**: `/archon-ui-main/src/App.tsx`
|
||||
- Add new route: `<Route path="/style-guide" element={<StyleGuidePage />} />`
|
||||
- Follow existing page import pattern
|
||||
|
||||
## Implementation Tasks
|
||||
|
||||
### Task 0: Create Missing Radix Primitives (PRIORITY)
|
||||
Before implementing the style guide, we need to create missing Radix primitives that the configurators will use:
|
||||
|
||||
#### Create RadioGroup Primitive
|
||||
```typescript
|
||||
// /src/features/ui/primitives/radio-group.tsx
|
||||
import * as RadioGroupPrimitive from "@radix-ui/react-radio-group";
|
||||
import { Circle } from "lucide-react";
|
||||
import React from "react";
|
||||
import { cn, glassmorphism } from "./styles";
|
||||
|
||||
export const RadioGroup = RadioGroupPrimitive.Root;
|
||||
|
||||
export const RadioGroupItem = React.forwardRef<
|
||||
React.ElementRef<typeof RadioGroupPrimitive.Item>,
|
||||
React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Item>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<RadioGroupPrimitive.Item
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"aspect-square h-4 w-4 rounded-full",
|
||||
"backdrop-blur-md bg-gradient-to-b from-white/80 to-white/60",
|
||||
"dark:from-white/10 dark:to-black/30",
|
||||
glassmorphism.border.default,
|
||||
glassmorphism.interactive.base,
|
||||
"focus:outline-none focus:ring-2 focus:ring-cyan-500",
|
||||
"disabled:cursor-not-allowed disabled:opacity-50",
|
||||
"data-[state=checked]:border-cyan-500",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<RadioGroupPrimitive.Indicator className="flex items-center justify-center">
|
||||
<Circle className="h-2.5 w-2.5 fill-cyan-500 text-cyan-500" />
|
||||
</RadioGroupPrimitive.Indicator>
|
||||
</RadioGroupPrimitive.Item>
|
||||
));
|
||||
RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName;
|
||||
```
|
||||
|
||||
#### Create Label Primitive
|
||||
```typescript
|
||||
// /src/features/ui/primitives/label.tsx
|
||||
import * as LabelPrimitive from "@radix-ui/react-label";
|
||||
import React from "react";
|
||||
import { cn } from "./styles";
|
||||
|
||||
export const Label = React.forwardRef<
|
||||
React.ElementRef<typeof LabelPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<LabelPrimitive.Root
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"text-sm font-medium leading-none",
|
||||
"text-gray-700 dark:text-gray-200",
|
||||
"peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
Label.displayName = LabelPrimitive.Root.displayName;
|
||||
```
|
||||
|
||||
#### Create Card Primitive
|
||||
```typescript
|
||||
// /src/features/ui/primitives/card.tsx
|
||||
import React from "react";
|
||||
import { cn, glassmorphism } from "./styles";
|
||||
|
||||
interface CardProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
glowColor?: keyof typeof glassmorphism.shadow.glow | 'none';
|
||||
edgePosition?: keyof typeof glassmorphism.edgeGlow | 'none';
|
||||
size?: keyof typeof glassmorphism.sizes.card;
|
||||
}
|
||||
|
||||
export const Card = React.forwardRef<HTMLDivElement, CardProps>(
|
||||
({ className, glowColor = 'none', edgePosition = 'none', size = 'md', children, ...props }, ref) => {
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn(
|
||||
glassmorphism.background.card,
|
||||
glassmorphism.border.default,
|
||||
glassmorphism.shadow.md,
|
||||
glowColor !== 'none' && glassmorphism.shadow.glow[glowColor],
|
||||
edgePosition !== 'none' && glassmorphism.edgeGlow[edgePosition],
|
||||
glassmorphism.sizes.card[size],
|
||||
"rounded-lg transition-all duration-300",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
Card.displayName = "Card";
|
||||
```
|
||||
|
||||
#### Enhance styles.ts
|
||||
```typescript
|
||||
// Add to /src/features/ui/primitives/styles.ts
|
||||
|
||||
export const glassmorphism = {
|
||||
// ... existing code ...
|
||||
|
||||
// Add edge glow positions for card configurator
|
||||
edgeGlow: {
|
||||
top: "border-t-2 border-t-cyan-400 relative before:content-[''] before:absolute before:top-0 before:left-0 before:right-0 before:h-[2px] before:bg-gradient-to-r before:from-transparent before:via-cyan-400 before:to-transparent",
|
||||
left: "border-l-2 border-l-cyan-400 relative before:content-[''] before:absolute before:top-0 before:left-0 before:bottom-0 before:w-[2px] before:bg-gradient-to-b before:from-transparent before:via-cyan-400 before:to-transparent",
|
||||
right: "border-r-2 border-r-cyan-400 relative before:content-[''] before:absolute before:top-0 before:right-0 before:bottom-0 before:w-[2px] before:bg-gradient-to-b before:from-transparent before:via-cyan-400 before:to-transparent",
|
||||
bottom: "border-b-2 border-b-cyan-400 relative before:content-[''] before:absolute before:bottom-0 before:left-0 before:right-0 before:h-[2px] before:bg-gradient-to-r before:from-transparent before:via-cyan-400 before:to-transparent",
|
||||
all: "border-2 border-cyan-400 relative before:content-[''] before:absolute before:inset-0 before:rounded-lg before:p-[2px] before:bg-gradient-to-r before:from-cyan-400 before:via-cyan-500 before:to-cyan-400 before:-z-10",
|
||||
none: ""
|
||||
},
|
||||
|
||||
// Add configurable sizes for cards
|
||||
sizes: {
|
||||
card: {
|
||||
sm: "p-4 max-w-sm",
|
||||
md: "p-6 max-w-md",
|
||||
lg: "p-8 max-w-lg",
|
||||
xl: "p-10 max-w-xl"
|
||||
}
|
||||
},
|
||||
|
||||
// Ensure all glow colors are available
|
||||
shadow: {
|
||||
// ... existing shadow code ...
|
||||
glow: {
|
||||
purple: "shadow-[0_0_10px_2px_rgba(168,85,247,0.4)] dark:shadow-[0_0_20px_5px_rgba(168,85,247,0.7)]",
|
||||
blue: "shadow-[0_0_10px_2px_rgba(59,130,246,0.4)] dark:shadow-[0_0_20px_5px_rgba(59,130,246,0.7)]",
|
||||
green: "shadow-[0_0_10px_2px_rgba(16,185,129,0.4)] dark:shadow-[0_0_20px_5px_rgba(16,185,129,0.7)]",
|
||||
red: "shadow-[0_0_10px_2px_rgba(239,68,68,0.4)] dark:shadow-[0_0_20px_5px_rgba(239,68,68,0.7)]",
|
||||
orange: "shadow-[0_0_10px_2px_rgba(251,146,60,0.4)] dark:shadow-[0_0_20px_5px_rgba(251,146,60,0.7)]",
|
||||
cyan: "shadow-[0_0_10px_2px_rgba(34,211,238,0.4)] dark:shadow-[0_0_20px_5px_rgba(34,211,238,0.7)]",
|
||||
pink: "shadow-[0_0_10px_2px_rgba(236,72,153,0.4)] dark:shadow-[0_0_20px_5px_rgba(236,72,153,0.7)]"
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Task 1: Create Page Structure and Routing
|
||||
```typescript
|
||||
// 1. Create /src/pages/StyleGuidePage.tsx
|
||||
// 2. Add route in App.tsx
|
||||
// 3. Add navigation link in MainLayout navigation
|
||||
```
|
||||
|
||||
### Task 2: Create Base Components Structure
|
||||
```
|
||||
/src/components/style-guide/
|
||||
├── shared/
|
||||
│ ├── NavigationSidebar.tsx # 4 sections with expandable items
|
||||
│ ├── LivePreview.tsx # Preview container with zoom controls
|
||||
│ ├── CodeDisplay.tsx # Syntax highlighted code with copy
|
||||
│ └── ConfigPanel.tsx # Configuration controls container
|
||||
```
|
||||
|
||||
### Task 3: Implement Component Configurators
|
||||
```
|
||||
/src/components/style-guide/configurators/
|
||||
├── GlassCardConfigurator.tsx # Glow colors, edge positions, sizes
|
||||
├── ButtonConfigurator.tsx # All variants, sizes, states
|
||||
├── ModalConfigurator.tsx # 6 standard types showcase
|
||||
├── TableConfigurator.tsx # Table with glass effects
|
||||
├── FormConfigurator.tsx # Input states and validation
|
||||
└── ToggleConfigurator.tsx # PowerButton variations
|
||||
```
|
||||
|
||||
### Task 4: Create Modal Standards
|
||||
```typescript
|
||||
// /src/components/style-guide/standards/modalStandards.ts
|
||||
export const MODAL_TYPES = {
|
||||
confirmation: { size: "sm", glowColor: "red", purpose: "Destructive actions" },
|
||||
formCreate: { size: "md", glowColor: "green", purpose: "Creating resources" },
|
||||
formEdit: { size: "md", glowColor: "blue", purpose: "Editing resources" },
|
||||
display: { size: "lg", glowColor: "purple", purpose: "Detailed information" },
|
||||
codeViewer: { size: "xl", glowColor: "cyan", purpose: "Code display" },
|
||||
settings: { size: "lg", glowColor: "blue", purpose: "App settings" }
|
||||
};
|
||||
```
|
||||
|
||||
### Task 5: Implement Layout Templates
|
||||
```
|
||||
/src/components/style-guide/layout-templates/
|
||||
├── DashboardTemplate.tsx # Grid of cards/widgets
|
||||
├── SidebarTemplate.tsx # Collapsible sidebar + main (like ProjectsView)
|
||||
├── KanbanTemplate.tsx # Column-based organization
|
||||
├── TableViewTemplate.tsx # Data table with actions
|
||||
└── CenteredFormTemplate.tsx # Single focus area
|
||||
```
|
||||
|
||||
**SidebarTemplate Special Requirements:**
|
||||
- Must show a collapsible sidebar within the main content area
|
||||
- Sidebar toggles between top position (cards) and left position (list)
|
||||
- Similar to ProjectsView with project cards that can be toggled to sidebar
|
||||
- Include toggle button to switch between layouts
|
||||
- Sidebar width: 280px when on left, full width when on top
|
||||
|
||||
### Task 6: Code Generation System
|
||||
```typescript
|
||||
// Each configurator must:
|
||||
// 1. Generate real-time code based on configuration
|
||||
// 2. Include all imports
|
||||
// 3. Add AI context comments with decision trees
|
||||
// 4. Provide copy-to-clipboard functionality
|
||||
```
|
||||
|
||||
## Component Implementation Details
|
||||
|
||||
### ConfigPanel Component (Uses Card Primitive)
|
||||
```typescript
|
||||
// /src/components/style-guide/shared/ConfigPanel.tsx
|
||||
import { Card } from '@/features/ui/primitives/card';
|
||||
import { cn } from '@/features/ui/primitives/styles';
|
||||
|
||||
interface ConfigPanelProps {
|
||||
title?: string;
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const ConfigPanel = ({ title, children, className }: ConfigPanelProps) => (
|
||||
<Card className={cn("space-y-4", className)}>
|
||||
{title && <h3 className="font-semibold text-lg mb-2">{title}</h3>}
|
||||
{children}
|
||||
</Card>
|
||||
);
|
||||
```
|
||||
|
||||
### GlassCardConfigurator (Using All Primitives)
|
||||
```typescript
|
||||
// Configuration Options:
|
||||
- glowColor: ["purple", "blue", "green", "orange", "red", "none"]
|
||||
- edgePosition: ["top", "left", "right", "bottom", "all", "none"]
|
||||
- size: ["sm", "md", "lg", "xl"]
|
||||
|
||||
// Generated Code must include:
|
||||
/**
|
||||
* 🤖 AI CONTEXT: GlassCard Component
|
||||
* PURPOSE: Container with glass morphism effect
|
||||
* WHEN TO USE: Primary content containers, cards, panels
|
||||
* WHEN NOT TO USE: Backgrounds, layout wrappers
|
||||
*
|
||||
* DECISION TREE:
|
||||
* - If primary content → glowColor="purple"
|
||||
* - If success message → glowColor="green"
|
||||
* - If error/danger → glowColor="red"
|
||||
*/
|
||||
```
|
||||
|
||||
### NavigationSidebar Structure
|
||||
```typescript
|
||||
const NAVIGATION = {
|
||||
foundations: {
|
||||
label: "Foundations",
|
||||
items: ["Colors", "Typography", "Spacing", "Effects"]
|
||||
},
|
||||
components: {
|
||||
label: "Components",
|
||||
items: ["Cards", "Buttons", "Forms", "Tables", "Modals", "Toggles"]
|
||||
},
|
||||
patterns: {
|
||||
label: "Patterns",
|
||||
items: ["Layouts", "Feedback", "Navigation", "Data Display"]
|
||||
},
|
||||
examples: {
|
||||
label: "Examples",
|
||||
items: ["Compositions", "Pages", "Workflows"]
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### LivePreview Component
|
||||
```typescript
|
||||
// Implementation: /src/components/style-guide/shared/LivePreview.tsx
|
||||
import { useState } from 'react';
|
||||
import { ZoomIn, ZoomOut, RotateCcw } from 'lucide-react';
|
||||
import { Button } from '@/features/ui/primitives/button';
|
||||
import { cn } from '@/features/ui/primitives/styles';
|
||||
|
||||
interface LivePreviewProps {
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
minHeight?: string;
|
||||
}
|
||||
|
||||
export const LivePreview = ({ children, className, minHeight = "400px" }: LivePreviewProps) => {
|
||||
const [zoom, setZoom] = useState(100);
|
||||
|
||||
return (
|
||||
<div className="relative rounded-lg overflow-hidden">
|
||||
{/* Zoom Controls */}
|
||||
<div className="absolute top-2 right-2 z-10 flex gap-1">
|
||||
<Button
|
||||
size="icon"
|
||||
variant="outline"
|
||||
onClick={() => setZoom(prev => Math.max(50, prev - 25))}
|
||||
>
|
||||
<ZoomOut className="w-4 h-4" />
|
||||
</Button>
|
||||
<Button
|
||||
size="icon"
|
||||
variant="outline"
|
||||
onClick={() => setZoom(100)}
|
||||
>
|
||||
<RotateCcw className="w-4 h-4" />
|
||||
</Button>
|
||||
<Button
|
||||
size="icon"
|
||||
variant="outline"
|
||||
onClick={() => setZoom(prev => Math.min(150, prev + 25))}
|
||||
>
|
||||
<ZoomIn className="w-4 h-4" />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Grid Background */}
|
||||
<div
|
||||
className={cn(
|
||||
"bg-gray-50 dark:bg-gray-900/50",
|
||||
"bg-[linear-gradient(to_right,#8882_1px,transparent_1px),linear-gradient(to_bottom,#8882_1px,transparent_1px)]",
|
||||
"bg-[size:20px_20px]",
|
||||
"p-8 flex items-center justify-center",
|
||||
className
|
||||
)}
|
||||
style={{ minHeight }}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
transform: `scale(${zoom / 100})`,
|
||||
transformOrigin: 'center'
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### CodeDisplay Component
|
||||
```typescript
|
||||
// Implementation: /src/components/style-guide/shared/CodeDisplay.tsx
|
||||
import { useState } from 'react';
|
||||
import { Check, Copy } from 'lucide-react';
|
||||
import { Button } from '@/features/ui/primitives/button';
|
||||
import { cn } from '@/features/ui/primitives/styles';
|
||||
|
||||
interface CodeDisplayProps {
|
||||
code: string;
|
||||
language?: 'typescript' | 'jsx' | 'css';
|
||||
showLineNumbers?: boolean;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const CodeDisplay = ({
|
||||
code,
|
||||
language = 'typescript',
|
||||
showLineNumbers = false,
|
||||
className
|
||||
}: CodeDisplayProps) => {
|
||||
const [copied, setCopied] = useState(false);
|
||||
|
||||
const handleCopy = async () => {
|
||||
await navigator.clipboard.writeText(code);
|
||||
setCopied(true);
|
||||
setTimeout(() => setCopied(false), 2000);
|
||||
};
|
||||
|
||||
const lines = code.split('\n');
|
||||
|
||||
// Basic syntax highlighting with regex
|
||||
const highlightCode = (line: string) => {
|
||||
// Escape HTML
|
||||
line = line.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
||||
|
||||
// Keywords
|
||||
line = line.replace(
|
||||
/\b(import|export|from|const|let|var|function|return|if|else|interface|type|class|extends|implements)\b/g,
|
||||
'<span class="text-purple-400">$1</span>'
|
||||
);
|
||||
|
||||
// Strings
|
||||
line = line.replace(
|
||||
/(["'`])([^"'`]*)\1/g,
|
||||
'<span class="text-green-400">$1$2$1</span>'
|
||||
);
|
||||
|
||||
// Comments
|
||||
line = line.replace(
|
||||
/(\/\/.*$|\/\*.*\*\/)/g,
|
||||
'<span class="text-gray-500">$1</span>'
|
||||
);
|
||||
|
||||
// JSX tags
|
||||
line = line.replace(
|
||||
/<([A-Z][A-Za-z0-9]*)(\s|>|\/>)/g,
|
||||
'<<span class="text-cyan-400">$1</span>$2'
|
||||
);
|
||||
|
||||
// Props
|
||||
line = line.replace(
|
||||
/(\w+)=/g,
|
||||
'<span class="text-orange-400">$1</span>='
|
||||
);
|
||||
|
||||
return line;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={cn(
|
||||
"relative rounded-lg overflow-hidden",
|
||||
"bg-gray-900 border border-gray-800",
|
||||
className
|
||||
)}>
|
||||
{/* Copy Button */}
|
||||
<Button
|
||||
size="icon"
|
||||
variant="ghost"
|
||||
onClick={handleCopy}
|
||||
className="absolute top-2 right-2 z-10 text-gray-400 hover:text-white"
|
||||
>
|
||||
{copied ? (
|
||||
<Check className="w-4 h-4 text-green-400" />
|
||||
) : (
|
||||
<Copy className="w-4 h-4" />
|
||||
)}
|
||||
</Button>
|
||||
|
||||
{/* Code Content */}
|
||||
<pre className="p-4 overflow-x-auto">
|
||||
<code className="text-sm font-mono">
|
||||
{lines.map((line, index) => (
|
||||
<div key={index} className="flex">
|
||||
{showLineNumbers && (
|
||||
<span className="text-gray-500 mr-4 select-none w-8 text-right">
|
||||
{index + 1}
|
||||
</span>
|
||||
)}
|
||||
<span
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: highlightCode(line) || ' '
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</code>
|
||||
</pre>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### Code Generation System (Updated with Proper Imports)
|
||||
```typescript
|
||||
// Detailed implementation for code generation
|
||||
// Example: /src/components/style-guide/configurators/GlassCardConfigurator.tsx
|
||||
|
||||
import { useState } from 'react';
|
||||
import { Card } from '@/features/ui/primitives/card';
|
||||
import { Button } from '@/features/ui/primitives/button';
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/features/ui/primitives/select';
|
||||
import { RadioGroup, RadioGroupItem } from '@/features/ui/primitives/radio-group';
|
||||
import { Label } from '@/features/ui/primitives/label';
|
||||
import { LivePreview } from '../shared/LivePreview';
|
||||
import { CodeDisplay } from '../shared/CodeDisplay';
|
||||
import { ConfigPanel } from '../shared/ConfigPanel';
|
||||
import { glassmorphism } from '@/features/ui/primitives/styles';
|
||||
|
||||
interface GlassCardConfig {
|
||||
glowColor: 'purple' | 'blue' | 'green' | 'orange' | 'red' | 'none';
|
||||
edgePosition: 'top' | 'left' | 'right' | 'bottom' | 'all' | 'none';
|
||||
size: 'sm' | 'md' | 'lg' | 'xl';
|
||||
content: string;
|
||||
}
|
||||
|
||||
export const GlassCardConfigurator = () => {
|
||||
const [config, setConfig] = useState<GlassCardConfig>({
|
||||
glowColor: 'purple',
|
||||
edgePosition: 'top',
|
||||
size: 'md',
|
||||
content: 'Your content here'
|
||||
});
|
||||
|
||||
const generateCode = (config: GlassCardConfig) => {
|
||||
const imports = `import { Card } from '@/features/ui/primitives/card';`;
|
||||
|
||||
const aiContext = `/**
|
||||
* 🤖 AI CONTEXT: GlassCard Component
|
||||
*
|
||||
* PURPOSE: Container with glass morphism effect for primary content
|
||||
* WHEN TO USE: Feature cards, content sections, important information displays
|
||||
* WHEN NOT TO USE: Backgrounds, full-page layouts, navigation elements
|
||||
*
|
||||
* DECISION TREE:
|
||||
* - If primary/featured content → glowColor="purple"
|
||||
* - If success state/creation → glowColor="green"
|
||||
* - If error/danger/deletion → glowColor="red"
|
||||
* - If informational/edit → glowColor="blue"
|
||||
* - If warning/caution → glowColor="orange"
|
||||
*
|
||||
* SEMANTIC COLORS:
|
||||
* - purple: Primary actions, featured content, main CTAs
|
||||
* - green: Success states, creation, positive feedback
|
||||
* - red: Destructive actions, errors, critical alerts
|
||||
* - blue: Information, editing, secondary actions
|
||||
* - orange: Warnings, important notices, caution states
|
||||
*
|
||||
* EDGE POSITION GUIDELINES:
|
||||
* - top: Default, draws eye downward into content
|
||||
* - left: For sidebar items or navigation cards
|
||||
* - right: For action panels or secondary info
|
||||
* - bottom: For footer-like content or summaries
|
||||
* - all: For highly important or selected states
|
||||
* - none: For subtle, non-emphasized containers
|
||||
*/`;
|
||||
|
||||
const component = `export const MyCard = ({ children }) => {
|
||||
return (
|
||||
<Card
|
||||
glowColor="${config.glowColor}"
|
||||
edgePosition="${config.edgePosition}"
|
||||
size="${config.size}"
|
||||
>
|
||||
{children}
|
||||
</Card>
|
||||
);
|
||||
};`;
|
||||
|
||||
return `${imports}\n\n${aiContext}\n\n${component}`;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||
{/* Left side: Configuration */}
|
||||
<div className="space-y-6">
|
||||
<ConfigPanel title="Glass Card Configuration">
|
||||
<div className="space-y-4">
|
||||
{/* Glow Color */}
|
||||
<div>
|
||||
<Label>Glow Color</Label>
|
||||
<Select
|
||||
value={config.glowColor}
|
||||
onValueChange={(value) => setConfig({...config, glowColor: value as any})}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="purple">Purple (Primary)</SelectItem>
|
||||
<SelectItem value="blue">Blue (Info)</SelectItem>
|
||||
<SelectItem value="green">Green (Success)</SelectItem>
|
||||
<SelectItem value="orange">Orange (Warning)</SelectItem>
|
||||
<SelectItem value="red">Red (Danger)</SelectItem>
|
||||
<SelectItem value="none">None</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
{/* Edge Position */}
|
||||
<div>
|
||||
<Label>Edge Glow Position</Label>
|
||||
<RadioGroup
|
||||
value={config.edgePosition}
|
||||
onValueChange={(value) => setConfig({...config, edgePosition: value as any})}
|
||||
>
|
||||
<div className="grid grid-cols-3 gap-2">
|
||||
{['top', 'left', 'right', 'bottom', 'all', 'none'].map(position => (
|
||||
<div key={position} className="flex items-center space-x-2">
|
||||
<RadioGroupItem value={position} id={position} />
|
||||
<Label htmlFor={position} className="capitalize">
|
||||
{position}
|
||||
</Label>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</RadioGroup>
|
||||
</div>
|
||||
|
||||
{/* Size */}
|
||||
<div>
|
||||
<Label>Size</Label>
|
||||
<RadioGroup
|
||||
value={config.size}
|
||||
onValueChange={(value) => setConfig({...config, size: value as any})}
|
||||
>
|
||||
<div className="flex gap-4">
|
||||
{['sm', 'md', 'lg', 'xl'].map(size => (
|
||||
<div key={size} className="flex items-center space-x-2">
|
||||
<RadioGroupItem value={size} id={`size-${size}`} />
|
||||
<Label htmlFor={`size-${size}`} className="uppercase">
|
||||
{size}
|
||||
</Label>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</RadioGroup>
|
||||
</div>
|
||||
</div>
|
||||
</ConfigPanel>
|
||||
|
||||
{/* Live Preview */}
|
||||
<LivePreview>
|
||||
<Card
|
||||
glowColor={config.glowColor}
|
||||
edgePosition={config.edgePosition}
|
||||
size={config.size}
|
||||
>
|
||||
<h3 className="text-lg font-semibold mb-2">Glass Card Example</h3>
|
||||
<p className="text-gray-600 dark:text-gray-300">
|
||||
{config.content}
|
||||
</p>
|
||||
</Card>
|
||||
</LivePreview>
|
||||
</div>
|
||||
|
||||
{/* Right side: Generated Code */}
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold mb-4">Generated Code</h3>
|
||||
<CodeDisplay
|
||||
code={generateCode(config)}
|
||||
language="typescript"
|
||||
showLineNumbers
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### SidebarTemplate Implementation (Using Primitives)
|
||||
```typescript
|
||||
// /src/components/style-guide/layout-templates/SidebarTemplate.tsx
|
||||
import { useState } from 'react';
|
||||
import { ChevronLeft, ChevronRight, LayoutGrid, List } from 'lucide-react';
|
||||
import { Button } from '@/features/ui/primitives/button';
|
||||
import { Card } from '@/features/ui/primitives/card';
|
||||
import { cn } from '@/features/ui/primitives/styles';
|
||||
import { CodeDisplay } from '../shared/CodeDisplay';
|
||||
|
||||
export const SidebarTemplate = () => {
|
||||
const [sidebarPosition, setSidebarPosition] = useState<'top' | 'left'>('top');
|
||||
const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
|
||||
|
||||
const items = [
|
||||
{ id: '1', title: 'Project Alpha', status: 'active' },
|
||||
{ id: '2', title: 'Project Beta', status: 'pending' },
|
||||
{ id: '3', title: 'Project Gamma', status: 'completed' },
|
||||
];
|
||||
|
||||
const generateCode = () => {
|
||||
return `import { useState } from 'react';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import { Button } from '@/features/ui/primitives/button';
|
||||
import { Card } from '@/features/ui/primitives/card';
|
||||
|
||||
export const SidebarLayout = () => {
|
||||
const [position, setPosition] = useState<'top' | 'left'>('top');
|
||||
const [collapsed, setCollapsed] = useState(false);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-full">
|
||||
{/* Toggle Button */}
|
||||
<div className="flex justify-end p-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => setPosition(p => p === 'top' ? 'left' : 'top')}
|
||||
>
|
||||
{position === 'top' ? <List /> : <LayoutGrid />}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className={cn(
|
||||
"flex",
|
||||
position === 'top' ? "flex-col" : "flex-row",
|
||||
"flex-1 gap-4 p-4"
|
||||
)}>
|
||||
{/* Sidebar Content */}
|
||||
<AnimatePresence mode="wait">
|
||||
<motion.div
|
||||
key={position}
|
||||
initial={{ opacity: 0, scale: 0.95 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
exit={{ opacity: 0, scale: 0.95 }}
|
||||
className={cn(
|
||||
position === 'left' && !collapsed && "w-[280px]",
|
||||
position === 'left' && collapsed && "w-[60px]",
|
||||
position === 'top' && "w-full",
|
||||
"transition-all duration-300"
|
||||
)}
|
||||
>
|
||||
{/* Sidebar items here */}
|
||||
</motion.div>
|
||||
</AnimatePresence>
|
||||
|
||||
{/* Main Content Area */}
|
||||
<div className="flex-1">
|
||||
{/* Main content here */}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};`;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<h3 className="text-lg font-semibold">Sidebar Layout Template</h3>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => setSidebarPosition(p => p === 'top' ? 'left' : 'top')}
|
||||
>
|
||||
Toggle Position: {sidebarPosition === 'top' ? <LayoutGrid className="w-4 h-4 ml-2" /> : <List className="w-4 h-4 ml-2" />}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Live Demo */}
|
||||
<div className="border rounded-lg p-4 min-h-[400px] bg-gray-50 dark:bg-gray-900/50">
|
||||
<div className={cn(
|
||||
"flex h-full",
|
||||
sidebarPosition === 'top' ? "flex-col" : "flex-row",
|
||||
"gap-4"
|
||||
)}>
|
||||
{/* Sidebar */}
|
||||
<div className={cn(
|
||||
sidebarPosition === 'left' && !sidebarCollapsed && "w-[280px]",
|
||||
sidebarPosition === 'left' && sidebarCollapsed && "w-[60px]",
|
||||
sidebarPosition === 'top' && "w-full",
|
||||
"transition-all duration-300"
|
||||
)}>
|
||||
{sidebarPosition === 'top' ? (
|
||||
<div className="grid grid-cols-3 gap-4">
|
||||
{items.map(item => (
|
||||
<Card key={item.id} glowColor="purple" className="hover:shadow-lg transition-shadow cursor-pointer">
|
||||
<h4 className="font-medium">{item.title}</h4>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">{item.status}</p>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-2">
|
||||
{sidebarPosition === 'left' && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="w-full justify-start"
|
||||
onClick={() => setSidebarCollapsed(!sidebarCollapsed)}
|
||||
>
|
||||
{sidebarCollapsed ? <ChevronRight /> : <ChevronLeft />}
|
||||
</Button>
|
||||
)}
|
||||
{items.map(item => (
|
||||
<Card
|
||||
key={item.id}
|
||||
className={cn(
|
||||
"hover:shadow-lg transition-shadow cursor-pointer",
|
||||
sidebarCollapsed && "p-2"
|
||||
)}
|
||||
>
|
||||
{!sidebarCollapsed ? (
|
||||
<>
|
||||
<h4 className="font-medium text-sm">{item.title}</h4>
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400">{item.status}</p>
|
||||
</>
|
||||
) : (
|
||||
<div className="w-8 h-8 rounded bg-purple-500/20 flex items-center justify-center">
|
||||
{item.title[0]}
|
||||
</div>
|
||||
)}
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Main Content */}
|
||||
<div className="flex-1 border-2 border-dashed border-gray-300 dark:border-gray-700 rounded-lg p-8">
|
||||
<div className="text-center text-gray-500 dark:text-gray-400">
|
||||
<p className="text-lg font-medium mb-2">Main Content Area</p>
|
||||
<p className="text-sm">This is where your main content would go</p>
|
||||
<p className="text-xs mt-4">Sidebar is currently: {sidebarPosition === 'top' ? 'Top' : `Left (${sidebarCollapsed ? 'Collapsed' : 'Expanded'})`}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<CodeDisplay code={generateCode()} language="typescript" showLineNumbers />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
## Confidence Score: 10/10
|
||||
|
||||
The PRP provides comprehensive implementation guidance with:
|
||||
- **Radix primitive creation tasks** for missing components
|
||||
- **Style enhancements** to support configurator needs
|
||||
- **Consistent imports** from `/features/ui/primitives/`
|
||||
- **Complete code examples** using actual primitives
|
||||
- **Clear implementation order** with primitive creation first
|
||||
|
||||
The score is 10/10 because:
|
||||
- **Component consistency**: All configurators use existing primitives
|
||||
- **No duplicate components**: Reuses existing infrastructure
|
||||
- **Enhanced flexibility**: styles.ts gets necessary additions
|
||||
- **Complete primitive set**: Creates missing RadioGroup, Label, and Card
|
||||
- **Production ready**: Uses same components developers will use
|
||||
|
||||
This PRP ensures the style guide showcases actual Archon components, not separate demo versions.
|
||||
|
||||
## Validation Gates
|
||||
|
||||
```bash
|
||||
# Frontend checks
|
||||
cd archon-ui-main
|
||||
npm run lint # No linting errors
|
||||
npx tsc --noEmit # No TypeScript errors
|
||||
npm run test # All tests pass
|
||||
|
||||
# Verify primitives work
|
||||
# Check that all new primitives render correctly
|
||||
# Verify Card primitive replaces old Card.tsx
|
||||
# Test RadioGroup and Label components
|
||||
# Confirm styles.ts enhancements work
|
||||
|
||||
# Verify routes work
|
||||
# Navigate to http://localhost:3737/style-guide
|
||||
# Verify all configurators render
|
||||
# Test code generation and copy functionality
|
||||
# Check responsive behavior
|
||||
```
|
||||
|
||||
## Dependencies
|
||||
- Existing Archon UI primitives and components
|
||||
- Tailwind CSS (already configured)
|
||||
- Lucide React icons (already installed)
|
||||
- React Router (already installed)
|
||||
- Radix UI packages (already installed):
|
||||
- @radix-ui/react-radio-group
|
||||
- @radix-ui/react-label
|
||||
- For syntax highlighting: Use simple regex-based solution (no external libs)
|
||||
|
||||
## File Creation Order
|
||||
1. **Create missing primitives first** (RadioGroup, Label, Card)
|
||||
2. **Enhance styles.ts** with edgeGlow and sizes
|
||||
3. Create page and add route
|
||||
4. Build shared components (NavigationSidebar, LivePreview, CodeDisplay, ConfigPanel)
|
||||
5. Implement GlassCardConfigurator first (as template)
|
||||
6. Add ButtonConfigurator
|
||||
7. Create modal standards and ModalConfigurator
|
||||
8. Add remaining configurators
|
||||
9. Implement layout templates
|
||||
10. Add AI context comments throughout
|
||||
|
||||
## Key Implementation Notes
|
||||
|
||||
### State Management
|
||||
- Use React hooks (useState) for configuration state
|
||||
- Each configurator manages its own state
|
||||
- URL params for sharing configurations (optional enhancement)
|
||||
|
||||
### Import Consistency
|
||||
- ALWAYS import from `/features/ui/primitives/` for UI components
|
||||
- Never create duplicate components
|
||||
- Use the Card primitive for all card needs
|
||||
- Import glassmorphism styles from styles.ts
|
||||
|
||||
### Performance Considerations
|
||||
- Lazy load configurators that aren't visible
|
||||
- Debounce configuration changes (200ms)
|
||||
- Use React.memo for expensive preview renders
|
||||
- Limit blur effects to 3 layers maximum
|
||||
|
||||
## Success Criteria
|
||||
- [ ] All primitives created and working
|
||||
- [ ] Old Card.tsx replaced with new primitive
|
||||
- [ ] All routes work and page loads without errors
|
||||
- [ ] Navigation sidebar with 4 main sections
|
||||
- [ ] GlassCard configurator with live preview using Card primitive
|
||||
- [ ] Button configurator with all variants
|
||||
- [ ] 6 modal types clearly documented
|
||||
- [ ] 5 layout templates available
|
||||
- [ ] Code generation with AI comments
|
||||
- [ ] Copy-to-clipboard works
|
||||
- [ ] Responsive design functions properly
|
||||
- [ ] No TypeScript or linting errors
|
||||
- [ ] All imports from primitives folder
|
||||
|
||||
## Common Pitfalls to Avoid
|
||||
- Don't create new component variants - use existing ones
|
||||
- Don't duplicate components - use primitives
|
||||
- Don't add external libraries for syntax highlighting initially
|
||||
- Don't forget AI context comments in generated code
|
||||
- Don't nest glass effects more than 3 levels
|
||||
- Don't hardcode values - use configuration objects
|
||||
- Don't import from old `/components/ui/` folder
|
||||
|
||||
## Testing Approach
|
||||
1. Manual testing of all configurators
|
||||
2. Verify code generation accuracy
|
||||
3. Test copy-to-clipboard functionality
|
||||
4. Check responsive breakpoints
|
||||
5. Validate AI comments are included
|
||||
6. Test with actual AI assistant to verify comments help
|
||||
7. Verify all imports use primitives
|
||||
|
||||
## References
|
||||
- Button component: `/archon-ui-main/src/features/ui/primitives/button.tsx`
|
||||
- Glass morphism styles: `/archon-ui-main/src/features/ui/primitives/styles.ts`
|
||||
- Select primitive: `/archon-ui-main/src/features/ui/primitives/select.tsx`
|
||||
- Modal example: `/archon-ui-main/src/features/ui/components/DeleteConfirmModal.tsx`
|
||||
- Routing: `/archon-ui-main/src/App.tsx`
|
||||
- Layout: `/archon-ui-main/src/components/layout/MainLayout.tsx`
|
||||
- Projects View (sidebar example): `/archon-ui-main/src/features/projects/views/ProjectsView.tsx`
|
||||
- Old Card to replace: `/archon-ui-main/src/components/ui/Card.tsx`
|
||||
87
archon-ui-main/package-lock.json
generated
87
archon-ui-main/package-lock.json
generated
@@ -12,8 +12,11 @@
|
||||
"@radix-ui/react-alert-dialog": "^1.1.15",
|
||||
"@radix-ui/react-dialog": "^1.1.15",
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
||||
"@radix-ui/react-label": "^2.1.7",
|
||||
"@radix-ui/react-popover": "^1.1.15",
|
||||
"@radix-ui/react-radio-group": "^1.3.8",
|
||||
"@radix-ui/react-select": "^2.2.6",
|
||||
"@radix-ui/react-switch": "^1.2.6",
|
||||
"@radix-ui/react-tabs": "^1.1.13",
|
||||
"@radix-ui/react-toast": "^1.2.15",
|
||||
"@radix-ui/react-tooltip": "^1.2.8",
|
||||
@@ -2745,6 +2748,29 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-label": {
|
||||
"version": "2.1.7",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.7.tgz",
|
||||
"integrity": "sha512-YT1GqPSL8kJn20djelMX7/cTRp/Y9w5IZHvfxQTVHrOqa2yMl7i/UfMqKRU5V7mEyKTrUVgJXhNQPVCG8PBLoQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-primitive": "2.1.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-menu": {
|
||||
"version": "2.1.16",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.16.tgz",
|
||||
@@ -2925,6 +2951,38 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-radio-group": {
|
||||
"version": "1.3.8",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-radio-group/-/react-radio-group-1.3.8.tgz",
|
||||
"integrity": "sha512-VBKYIYImA5zsxACdisNQ3BjCBfmbGH3kQlnFVqlWU4tXwjy7cGX8ta80BcrO+WJXIn5iBylEH3K6ZTlee//lgQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/primitive": "1.1.3",
|
||||
"@radix-ui/react-compose-refs": "1.1.2",
|
||||
"@radix-ui/react-context": "1.1.2",
|
||||
"@radix-ui/react-direction": "1.1.1",
|
||||
"@radix-ui/react-presence": "1.1.5",
|
||||
"@radix-ui/react-primitive": "2.1.3",
|
||||
"@radix-ui/react-roving-focus": "1.1.11",
|
||||
"@radix-ui/react-use-controllable-state": "1.2.2",
|
||||
"@radix-ui/react-use-previous": "1.1.1",
|
||||
"@radix-ui/react-use-size": "1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-roving-focus": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.11.tgz",
|
||||
@@ -3040,6 +3098,35 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-switch": {
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.2.6.tgz",
|
||||
"integrity": "sha512-bByzr1+ep1zk4VubeEVViV592vu2lHE2BZY5OnzehZqOOgogN80+mNtCqPkhn2gklJqOpxWgPoYTSnhBCqpOXQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/primitive": "1.1.3",
|
||||
"@radix-ui/react-compose-refs": "1.1.2",
|
||||
"@radix-ui/react-context": "1.1.2",
|
||||
"@radix-ui/react-primitive": "2.1.3",
|
||||
"@radix-ui/react-use-controllable-state": "1.2.2",
|
||||
"@radix-ui/react-use-previous": "1.1.1",
|
||||
"@radix-ui/react-use-size": "1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-tabs": {
|
||||
"version": "1.1.13",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.13.tgz",
|
||||
|
||||
@@ -32,8 +32,11 @@
|
||||
"@radix-ui/react-alert-dialog": "^1.1.15",
|
||||
"@radix-ui/react-dialog": "^1.1.15",
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.16",
|
||||
"@radix-ui/react-label": "^2.1.7",
|
||||
"@radix-ui/react-popover": "^1.1.15",
|
||||
"@radix-ui/react-radio-group": "^1.3.8",
|
||||
"@radix-ui/react-select": "^2.2.6",
|
||||
"@radix-ui/react-switch": "^1.2.6",
|
||||
"@radix-ui/react-tabs": "^1.1.13",
|
||||
"@radix-ui/react-toast": "^1.2.15",
|
||||
"@radix-ui/react-tooltip": "^1.2.8",
|
||||
|
||||
@@ -13,6 +13,7 @@ import { ToastProvider } from './features/ui/components/ToastProvider';
|
||||
import { SettingsProvider, useSettings } from './contexts/SettingsContext';
|
||||
import { TooltipProvider } from './features/ui/primitives/tooltip';
|
||||
import { ProjectPage } from './pages/ProjectPage';
|
||||
import StyleGuidePage from './pages/StyleGuidePage';
|
||||
import { DisconnectScreenOverlay } from './components/DisconnectScreenOverlay';
|
||||
import { ErrorBoundaryWithBugReport } from './components/bug-report/ErrorBoundaryWithBugReport';
|
||||
import { MigrationBanner } from './components/ui/MigrationBanner';
|
||||
@@ -29,6 +30,7 @@ const AppRoutes = () => {
|
||||
<Route path="/onboarding" element={<OnboardingPage />} />
|
||||
<Route path="/settings" element={<SettingsPage />} />
|
||||
<Route path="/mcp" element={<MCPPage />} />
|
||||
<Route path="/style-guide" element={<StyleGuidePage />} />
|
||||
{projectsEnabled ? (
|
||||
<>
|
||||
<Route path="/projects" element={<ProjectPage />} />
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { BookOpen, Settings } from "lucide-react";
|
||||
import { BookOpen, Palette, Settings } from "lucide-react";
|
||||
import type React from "react";
|
||||
import { Link, useLocation } from "react-router-dom";
|
||||
// TEMPORARY: Use old SettingsContext until settings are migrated
|
||||
@@ -54,6 +54,12 @@ export function Navigation({ className }: NavigationProps) {
|
||||
label: "MCP Server",
|
||||
enabled: true,
|
||||
},
|
||||
{
|
||||
path: "/style-guide",
|
||||
icon: <Palette className="h-5 w-5" />,
|
||||
label: "Style Guide",
|
||||
enabled: true,
|
||||
},
|
||||
{
|
||||
path: "/settings",
|
||||
icon: <Settings className="h-5 w-5" />,
|
||||
|
||||
150
archon-ui-main/src/components/style-guide/StyleGuideView.tsx
Normal file
150
archon-ui-main/src/components/style-guide/StyleGuideView.tsx
Normal file
@@ -0,0 +1,150 @@
|
||||
import { useState } from 'react';
|
||||
import { Palette, Component, Layout, Code } from 'lucide-react';
|
||||
import { PillNavigation } from './shared/PillNavigation';
|
||||
import { ThemeToggle } from '../ui/ThemeToggle';
|
||||
import { GlassCardConfigurator } from './configurators/GlassCardConfigurator';
|
||||
import { ButtonConfigurator } from './configurators/ButtonConfigurator';
|
||||
import { ModalConfigurator } from './configurators/ModalConfigurator';
|
||||
import { FormConfigurator } from './configurators/FormConfigurator';
|
||||
import { TableConfigurator } from './configurators/TableConfigurator';
|
||||
import { ToggleConfigurator } from './configurators/ToggleConfigurator';
|
||||
import { ColorsFoundation } from './foundations/ColorsFoundation';
|
||||
import { TypographyFoundation } from './foundations/TypographyFoundation';
|
||||
import { SpacingFoundation } from './foundations/SpacingFoundation';
|
||||
import { EffectsFoundation } from './foundations/EffectsFoundation';
|
||||
import { LayoutsPattern } from './patterns/LayoutsPattern';
|
||||
import { FeedbackPattern } from './patterns/FeedbackPattern';
|
||||
import { NavigationPattern } from './patterns/NavigationPattern';
|
||||
import { DataDisplayPattern } from './patterns/DataDisplayPattern';
|
||||
import { CompositionsExample } from './examples/CompositionsExample';
|
||||
import { PagesExample } from './examples/PagesExample';
|
||||
import { WorkflowsExample } from './examples/WorkflowsExample';
|
||||
|
||||
const FOUNDATION_TABS = [
|
||||
{ id: 'Colors', label: 'Colors', component: ColorsFoundation },
|
||||
{ id: 'Typography', label: 'Typography', component: TypographyFoundation },
|
||||
{ id: 'Spacing', label: 'Spacing', component: SpacingFoundation },
|
||||
{ id: 'Effects', label: 'Effects', component: EffectsFoundation },
|
||||
];
|
||||
|
||||
const COMPONENT_TABS = [
|
||||
{ id: 'Cards', label: 'Cards', component: GlassCardConfigurator },
|
||||
{ id: 'Buttons', label: 'Buttons', component: ButtonConfigurator },
|
||||
{ id: 'Forms', label: 'Forms', component: FormConfigurator },
|
||||
{ id: 'Tables', label: 'Tables', component: TableConfigurator },
|
||||
{ id: 'Modals', label: 'Modals', component: ModalConfigurator },
|
||||
{ id: 'Toggles', label: 'Toggles', component: ToggleConfigurator },
|
||||
];
|
||||
|
||||
const PATTERN_TABS = [
|
||||
{ id: 'Layouts', label: 'Layouts', component: LayoutsPattern },
|
||||
{ id: 'Feedback', label: 'Feedback', component: FeedbackPattern },
|
||||
{ id: 'Navigation', label: 'Navigation', component: NavigationPattern },
|
||||
{ id: 'Data Display', label: 'Data Display', component: DataDisplayPattern },
|
||||
];
|
||||
|
||||
const EXAMPLE_TABS = [
|
||||
{ id: 'Compositions', label: 'Compositions', component: CompositionsExample },
|
||||
{ id: 'Pages', label: 'Pages', component: PagesExample },
|
||||
{ id: 'Workflows', label: 'Workflows', component: WorkflowsExample },
|
||||
];
|
||||
|
||||
export const StyleGuideView = () => {
|
||||
const [selectedSection, setSelectedSection] = useState('foundations');
|
||||
const [selectedComponent, setSelectedComponent] = useState<string | null>('Colors');
|
||||
|
||||
const handleSectionChange = (section: string) => {
|
||||
setSelectedSection(section);
|
||||
// Reset to first item of new section
|
||||
if (section === 'foundations') setSelectedComponent('Colors');
|
||||
else if (section === 'components') setSelectedComponent('Cards');
|
||||
else if (section === 'patterns') setSelectedComponent('Layouts');
|
||||
else if (section === 'examples') setSelectedComponent('Compositions');
|
||||
else setSelectedComponent(null);
|
||||
};
|
||||
|
||||
const renderContent = () => {
|
||||
if (selectedSection === 'foundations' && selectedComponent) {
|
||||
const tab = FOUNDATION_TABS.find(t => t.id === selectedComponent);
|
||||
if (tab) {
|
||||
const FoundationComponent = tab.component;
|
||||
return <FoundationComponent />;
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedSection === 'components' && selectedComponent) {
|
||||
const tab = COMPONENT_TABS.find(t => t.id === selectedComponent);
|
||||
if (tab) {
|
||||
const ComponentConfigurator = tab.component;
|
||||
return <ComponentConfigurator />;
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedSection === 'patterns' && selectedComponent) {
|
||||
const tab = PATTERN_TABS.find(t => t.id === selectedComponent);
|
||||
if (tab) {
|
||||
const PatternComponent = tab.component;
|
||||
return <PatternComponent />;
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedSection === 'examples' && selectedComponent) {
|
||||
const tab = EXAMPLE_TABS.find(t => t.id === selectedComponent);
|
||||
if (tab) {
|
||||
const ExampleComponent = tab.component;
|
||||
return <ExampleComponent />;
|
||||
}
|
||||
}
|
||||
|
||||
// Default content for other sections or no selection
|
||||
const sectionContent = {
|
||||
foundations: { icon: <Palette className="w-16 h-16 mx-auto mb-4 opacity-50" />, text: "Select a foundation element from the dropdown above" },
|
||||
components: { icon: <Component className="w-16 h-16 mx-auto mb-4 opacity-50" />, text: "Select a component from the dropdown above" },
|
||||
patterns: { icon: <Layout className="w-16 h-16 mx-auto mb-4 opacity-50" />, text: "Select a pattern from the dropdown above" },
|
||||
examples: { icon: <Code className="w-16 h-16 mx-auto mb-4 opacity-50" />, text: "Select an example from the dropdown above" }
|
||||
};
|
||||
|
||||
const content = sectionContent[selectedSection as keyof typeof sectionContent];
|
||||
|
||||
return (
|
||||
<div className="text-center py-16 text-gray-500 dark:text-gray-400">
|
||||
{content.icon}
|
||||
<p>{content.text}</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-12">
|
||||
{/* Header */}
|
||||
<div className="relative">
|
||||
{/* Theme Toggle in top right */}
|
||||
<div className="absolute top-0 right-0">
|
||||
<ThemeToggle accentColor="blue" />
|
||||
</div>
|
||||
|
||||
<div className="text-center space-y-4">
|
||||
<h1 className="text-4xl font-bold text-gray-900 dark:text-white mb-4">Interactive Style Guide</h1>
|
||||
<p className="text-gray-600 dark:text-gray-400 text-lg max-w-2xl mx-auto">
|
||||
Configure and preview Archon's glassmorphism components with live code generation.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Pill Navigation */}
|
||||
<div className="flex justify-center">
|
||||
<PillNavigation
|
||||
selectedSection={selectedSection}
|
||||
selectedItem={selectedComponent}
|
||||
onSectionChange={handleSectionChange}
|
||||
onItemChange={setSelectedComponent}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div>
|
||||
{renderContent()}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,214 @@
|
||||
import { useState } from 'react';
|
||||
import { Button } from '@/features/ui/primitives/button';
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/features/ui/primitives/select';
|
||||
import { Switch } from '@/features/ui/primitives/switch';
|
||||
import { Input } from '@/features/ui/primitives/input';
|
||||
import { LivePreview } from '../shared/LivePreview';
|
||||
import { CodeDisplay } from '../shared/CodeDisplay';
|
||||
import { ConfigPanel } from '../shared/ConfigPanel';
|
||||
import { ConfigRow } from '../shared/ConfigRow';
|
||||
import { Loader2, Download, Eye, Code } from 'lucide-react';
|
||||
import type { ButtonVariant, ButtonSize } from '../types';
|
||||
|
||||
interface ButtonConfig {
|
||||
variant: ButtonVariant;
|
||||
size: ButtonSize;
|
||||
loading: boolean;
|
||||
disabled: boolean;
|
||||
withIcon: boolean;
|
||||
text: string;
|
||||
}
|
||||
|
||||
export const ButtonConfigurator = () => {
|
||||
const [config, setConfig] = useState<ButtonConfig>({
|
||||
variant: 'default',
|
||||
size: 'default',
|
||||
loading: false,
|
||||
disabled: false,
|
||||
withIcon: false,
|
||||
text: 'Click me'
|
||||
});
|
||||
const [activeTab, setActiveTab] = useState<'preview' | 'code'>('preview');
|
||||
|
||||
const generateCode = (config: ButtonConfig) => {
|
||||
const imports = [`import { Button } from '@/features/ui/primitives/button';`];
|
||||
|
||||
if (config.loading) {
|
||||
imports.push(`import { Loader2 } from 'lucide-react';`);
|
||||
}
|
||||
if (config.withIcon) {
|
||||
imports.push(`import { Download } from 'lucide-react';`);
|
||||
}
|
||||
|
||||
const aiContext = `/**
|
||||
* 🤖 AI CONTEXT: Button Component
|
||||
*
|
||||
* VARIANT DECISION TREE:
|
||||
* - default: Primary actions, main CTAs, submit buttons
|
||||
* - destructive: Delete, remove, cancel dangerous operations
|
||||
* - outline: Secondary actions, alternative options
|
||||
* - ghost: Tertiary actions, minimal emphasis
|
||||
* - link: Navigation, text-only actions
|
||||
* - cyan: Special emphasis, Tron-themed primary
|
||||
* - knowledge: Knowledge base specific actions
|
||||
*
|
||||
* SIZE GUIDELINES:
|
||||
* - xs: Inline actions, table cells
|
||||
* - sm: Dense UI, secondary actions
|
||||
* - default: Most use cases
|
||||
* - lg: Primary CTAs, hero sections
|
||||
* - icon: Icon-only buttons
|
||||
*/`;
|
||||
|
||||
const props: string[] = [];
|
||||
if (config.variant !== 'default') props.push(`variant="${config.variant}"`);
|
||||
if (config.size !== 'default') props.push(`size="${config.size}"`);
|
||||
if (config.loading) props.push(`loading={true}`);
|
||||
if (config.disabled) props.push(`disabled={true}`);
|
||||
|
||||
const content = config.loading
|
||||
? `<>\n <Loader2 className="mr-2 h-4 w-4 animate-spin" />\n ${config.text}\n </>`
|
||||
: config.withIcon
|
||||
? `<>\n <Download className="mr-2 h-4 w-4" />\n ${config.text}\n </>`
|
||||
: config.text;
|
||||
|
||||
const component = `export const MyButton = () => {
|
||||
return (
|
||||
<Button${props.length > 0 ? '\n ' + props.join('\n ') : ''}>
|
||||
${content}
|
||||
</Button>
|
||||
);
|
||||
};`;
|
||||
|
||||
return `${imports.join('\n')}\n\n${aiContext}\n\n${component}`;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-4 gap-6 h-full">
|
||||
{/* LEFT: Configuration Panel (1/4 width) */}
|
||||
<div className="col-span-1">
|
||||
<ConfigPanel title="Button Configuration">
|
||||
<div className="space-y-3">
|
||||
<ConfigRow label="Variant">
|
||||
<Select
|
||||
value={config.variant}
|
||||
onValueChange={(value) => setConfig({...config, variant: value as ButtonVariant})}
|
||||
>
|
||||
<SelectTrigger className="w-24 text-xs">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="default">Default</SelectItem>
|
||||
<SelectItem value="destructive">Destructive</SelectItem>
|
||||
<SelectItem value="outline">Outline</SelectItem>
|
||||
<SelectItem value="ghost">Ghost</SelectItem>
|
||||
<SelectItem value="link">Link</SelectItem>
|
||||
<SelectItem value="cyan">Cyan</SelectItem>
|
||||
<SelectItem value="knowledge">Knowledge</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</ConfigRow>
|
||||
|
||||
<ConfigRow label="Size">
|
||||
<Select
|
||||
value={config.size}
|
||||
onValueChange={(value) => setConfig({...config, size: value as ButtonSize})}
|
||||
>
|
||||
<SelectTrigger className="w-24 text-xs">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="xs">XS</SelectItem>
|
||||
<SelectItem value="sm">SM</SelectItem>
|
||||
<SelectItem value="default">Default</SelectItem>
|
||||
<SelectItem value="lg">LG</SelectItem>
|
||||
<SelectItem value="icon">Icon</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</ConfigRow>
|
||||
|
||||
<ConfigRow label="Loading">
|
||||
<Switch
|
||||
checked={config.loading}
|
||||
onCheckedChange={(loading) => setConfig({...config, loading})}
|
||||
/>
|
||||
</ConfigRow>
|
||||
|
||||
<ConfigRow label="Disabled">
|
||||
<Switch
|
||||
checked={config.disabled}
|
||||
onCheckedChange={(disabled) => setConfig({...config, disabled})}
|
||||
/>
|
||||
</ConfigRow>
|
||||
|
||||
<ConfigRow label="With Icon">
|
||||
<Switch
|
||||
checked={config.withIcon}
|
||||
onCheckedChange={(withIcon) => setConfig({...config, withIcon})}
|
||||
/>
|
||||
</ConfigRow>
|
||||
|
||||
{config.size !== 'icon' && (
|
||||
<ConfigRow label="Text">
|
||||
<Input
|
||||
value={config.text}
|
||||
onChange={(e) => setConfig({...config, text: e.target.value})}
|
||||
placeholder="Button text..."
|
||||
className="w-24 text-xs"
|
||||
/>
|
||||
</ConfigRow>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Preview/Code Tabs INSIDE configurator */}
|
||||
<div className="mt-6 pt-4 border-t border-gray-200 dark:border-gray-700">
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
size="sm"
|
||||
variant={activeTab === 'preview' ? 'default' : 'outline'}
|
||||
onClick={() => setActiveTab('preview')}
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
<Eye className="w-4 h-4" />
|
||||
Preview
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant={activeTab === 'code' ? 'default' : 'outline'}
|
||||
onClick={() => setActiveTab('code')}
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
<Code className="w-4 h-4" />
|
||||
Code
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</ConfigPanel>
|
||||
</div>
|
||||
|
||||
{/* RIGHT: Preview or Code Content (3/4 width) */}
|
||||
<div className="col-span-3">
|
||||
{activeTab === 'preview' ? (
|
||||
<LivePreview>
|
||||
<Button
|
||||
variant={config.variant}
|
||||
size={config.size}
|
||||
loading={config.loading}
|
||||
disabled={config.disabled}
|
||||
>
|
||||
{config.loading && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
|
||||
{!config.loading && config.withIcon && <Download className="mr-2 h-4 w-4" />}
|
||||
{config.size !== 'icon' && config.text}
|
||||
{config.size === 'icon' && !config.loading && <Download className="h-4 w-4" />}
|
||||
</Button>
|
||||
</LivePreview>
|
||||
) : (
|
||||
<CodeDisplay
|
||||
code={generateCode(config)}
|
||||
showLineNumbers
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,219 @@
|
||||
import { useState } from 'react';
|
||||
import { Input } from '@/features/ui/primitives/input';
|
||||
import { Button } from '@/features/ui/primitives/button';
|
||||
import { Label } from '@/features/ui/primitives/label';
|
||||
import { Switch } from '@/features/ui/primitives/switch';
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/features/ui/primitives/select';
|
||||
import { LivePreview } from '../shared/LivePreview';
|
||||
import { CodeDisplay } from '../shared/CodeDisplay';
|
||||
import { ConfigPanel } from '../shared/ConfigPanel';
|
||||
import { Eye, Code } from 'lucide-react';
|
||||
import type { InputType } from '../types';
|
||||
|
||||
interface FormConfig {
|
||||
error: boolean;
|
||||
disabled: boolean;
|
||||
placeholder: string;
|
||||
inputType: InputType;
|
||||
}
|
||||
|
||||
export const FormConfigurator = () => {
|
||||
const [config, setConfig] = useState<FormConfig>({
|
||||
error: false,
|
||||
disabled: false,
|
||||
placeholder: 'Enter your text...',
|
||||
inputType: 'text'
|
||||
});
|
||||
const [activeTab, setActiveTab] = useState<'preview' | 'code'>('preview');
|
||||
|
||||
const generateCode = (config: FormConfig) => {
|
||||
const imports = `import { Input } from '@/features/ui/primitives/input';
|
||||
import { Label } from '@/features/ui/primitives/label';
|
||||
import { Button } from '@/features/ui/primitives/button';`;
|
||||
|
||||
const aiContext = `/**
|
||||
* 🤖 AI CONTEXT: Form Input Component
|
||||
*
|
||||
* PURPOSE: Text input with glass morphism styling and validation states
|
||||
* WHEN TO USE: Forms, search bars, data entry fields
|
||||
* WHEN NOT TO USE: Large text content (use textarea), complex selections (use select)
|
||||
*
|
||||
* INPUT TYPE GUIDELINES:
|
||||
* - text: General text input, names, titles
|
||||
* - email: Email addresses (includes validation)
|
||||
* - password: Passwords (masks input)
|
||||
* - number: Numeric values (shows number pad on mobile)
|
||||
*
|
||||
* ERROR STATE:
|
||||
* - Use when validation fails
|
||||
* - Shows red border and focus ring
|
||||
* - Pair with error message below input
|
||||
*
|
||||
* DISABLED STATE:
|
||||
* - Use when input is temporarily unavailable
|
||||
* - Shows reduced opacity and prevents interaction
|
||||
* - Consider using readonly for permanent restrictions
|
||||
*/`;
|
||||
|
||||
const props: string[] = [];
|
||||
if (config.inputType !== 'text') props.push(`type="${config.inputType}"`);
|
||||
if (config.error) props.push(`error={true}`);
|
||||
if (config.disabled) props.push(`disabled={true}`);
|
||||
if (config.placeholder) props.push(`placeholder="${config.placeholder}"`);
|
||||
|
||||
const component = `export const MyForm = () => {
|
||||
const [value, setValue] = useState('');
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<Label htmlFor="example-input">
|
||||
${config.inputType === 'email' ? 'Email Address' :
|
||||
config.inputType === 'password' ? 'Password' :
|
||||
config.inputType === 'number' ? 'Number' :
|
||||
'Text Input'}
|
||||
</Label>
|
||||
<Input
|
||||
id="example-input"
|
||||
value={value}
|
||||
onChange={(e) => setValue(e.target.value)}${props.length > 0 ? '\n ' + props.join('\n ') : ''}
|
||||
/>
|
||||
${config.error ? `<p className="text-sm text-red-600 dark:text-red-400 mt-1">
|
||||
This field is required
|
||||
</p>` : ''}
|
||||
</div>
|
||||
|
||||
<Button type="submit">
|
||||
Submit
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
};`;
|
||||
|
||||
return `${imports}\n\n${aiContext}\n\n${component}`;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-4 gap-6 h-full">
|
||||
{/* LEFT: Configuration Panel (1/4 width) */}
|
||||
<div className="col-span-1">
|
||||
<ConfigPanel title="Form Input Configuration">
|
||||
<div className="space-y-4">
|
||||
{/* Input Type */}
|
||||
<div>
|
||||
<Label>Input Type</Label>
|
||||
<Select
|
||||
value={config.inputType}
|
||||
onValueChange={(value) => setConfig({...config, inputType: value as InputType})}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="text">Text</SelectItem>
|
||||
<SelectItem value="email">Email</SelectItem>
|
||||
<SelectItem value="password">Password</SelectItem>
|
||||
<SelectItem value="number">Number</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
{/* States */}
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<Label htmlFor="error-state" className="text-sm">Error State</Label>
|
||||
<Switch
|
||||
id="error-state"
|
||||
checked={config.error}
|
||||
onCheckedChange={(error) => setConfig({...config, error})}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<Label htmlFor="disabled-state" className="text-sm">Disabled</Label>
|
||||
<Switch
|
||||
id="disabled-state"
|
||||
checked={config.disabled}
|
||||
onCheckedChange={(disabled) => setConfig({...config, disabled})}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Placeholder */}
|
||||
<div>
|
||||
<Label htmlFor="placeholder-text" className="text-sm">Placeholder Text</Label>
|
||||
<Input
|
||||
id="placeholder-text"
|
||||
value={config.placeholder}
|
||||
onChange={(e) => setConfig({...config, placeholder: e.target.value})}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Preview/Code Tabs INSIDE configurator */}
|
||||
<div className="mt-4 pt-4 border-t border-gray-200 dark:border-gray-700">
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
size="sm"
|
||||
variant={activeTab === 'preview' ? 'default' : 'outline'}
|
||||
onClick={() => setActiveTab('preview')}
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
<Eye className="w-4 h-4" />
|
||||
Preview
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant={activeTab === 'code' ? 'default' : 'outline'}
|
||||
onClick={() => setActiveTab('code')}
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
<Code className="w-4 h-4" />
|
||||
Code
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</ConfigPanel>
|
||||
</div>
|
||||
|
||||
{/* RIGHT: Preview or Code Content (3/4 width) */}
|
||||
<div className="col-span-3">
|
||||
{activeTab === 'preview' ? (
|
||||
<LivePreview>
|
||||
<div className="space-y-4 w-full max-w-sm">
|
||||
<div>
|
||||
<Label htmlFor="preview-input">
|
||||
{config.inputType === 'email' ? 'Email Address' :
|
||||
config.inputType === 'password' ? 'Password' :
|
||||
config.inputType === 'number' ? 'Number' :
|
||||
'Text Input'}
|
||||
</Label>
|
||||
<Input
|
||||
id="preview-input"
|
||||
type={config.inputType}
|
||||
placeholder={config.placeholder}
|
||||
error={config.error}
|
||||
disabled={config.disabled}
|
||||
/>
|
||||
{config.error && (
|
||||
<p className="text-sm text-red-600 dark:text-red-400 mt-1">
|
||||
This field is required
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<Button type="submit" disabled={config.disabled}>
|
||||
Submit
|
||||
</Button>
|
||||
</div>
|
||||
</LivePreview>
|
||||
) : (
|
||||
<CodeDisplay
|
||||
code={generateCode(config)}
|
||||
showLineNumbers
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,327 @@
|
||||
import { useState } from 'react';
|
||||
import { Card } from '@/features/ui/primitives/card';
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/features/ui/primitives/select';
|
||||
import { Input } from '@/features/ui/primitives/input';
|
||||
import { Button } from '@/features/ui/primitives/button';
|
||||
import { LivePreview } from '../shared/LivePreview';
|
||||
import { CodeDisplay } from '../shared/CodeDisplay';
|
||||
import { ConfigPanel } from '../shared/ConfigPanel';
|
||||
import { ConfigRow } from '../shared/ConfigRow';
|
||||
import { Eye, Code } from 'lucide-react';
|
||||
import type { GlowColor, EdgePosition, EdgeColor, CardSize, Transparency, BlurLevel, GlassTint } from '../types';
|
||||
|
||||
interface GlassCardConfig {
|
||||
blur: BlurLevel;
|
||||
transparency: Transparency;
|
||||
glassTint: GlassTint;
|
||||
glowColor: GlowColor;
|
||||
edgePosition: EdgePosition;
|
||||
edgeColor: EdgeColor;
|
||||
size: CardSize;
|
||||
content: string;
|
||||
}
|
||||
|
||||
export const GlassCardConfigurator = () => {
|
||||
const [config, setConfig] = useState<GlassCardConfig>({
|
||||
blur: 'xl', // Standard glass (3px) - subtle effect
|
||||
transparency: 'light', // 3% opacity for true glass
|
||||
glassTint: 'none',
|
||||
glowColor: 'none',
|
||||
edgePosition: 'none',
|
||||
edgeColor: 'cyan',
|
||||
size: 'lg',
|
||||
content: 'Your content here'
|
||||
});
|
||||
const [activeTab, setActiveTab] = useState<'preview' | 'code'>('preview');
|
||||
|
||||
const generateCode = (config: GlassCardConfig) => {
|
||||
const imports = `import { Card } from '@/features/ui/primitives/card';`;
|
||||
|
||||
const aiContext = `/**
|
||||
* 🤖 AI CONTEXT: Glass Card Component
|
||||
*
|
||||
* GLASS PROPERTIES for true glassmorphism:
|
||||
*
|
||||
* 1. TRANSPARENCY - How much you can see through the glass
|
||||
* - clear: Almost invisible (0.01 opacity)
|
||||
* - light: Subtle frosting (0.03 opacity) ← BEST FOR TRUE GLASS
|
||||
* - medium: Standard glass (0.05 opacity)
|
||||
* - frosted: Heavy frosting (0.08 opacity)
|
||||
* - solid: Maximum opacity (0.12 opacity)
|
||||
*
|
||||
* 2. GLASS TINT - Adds color to the glass itself
|
||||
* - none: Pure transparent
|
||||
* - purple/blue/cyan/green/orange/pink/red: Colored glass
|
||||
*
|
||||
* 3. GLOW EFFECTS - Choose ONE:
|
||||
* a) glowColor - Full perimeter neon backlight
|
||||
* - none: No glow
|
||||
* - purple/blue/cyan/green/orange/pink/red: Neon glow
|
||||
*
|
||||
* b) edgePosition + edgeColor - Single edge accent (like original cards!)
|
||||
* - edgePosition: top/left/right/bottom
|
||||
* - edgeColor: purple/blue/cyan/green/orange/pink/red
|
||||
*
|
||||
* USAGE PATTERNS:
|
||||
* - Default glass: transparency="light" blur="xl"
|
||||
* - Knowledge cards: edgePosition="top" edgeColor="purple"
|
||||
* - Task cards: edgePosition="left" edgeColor based on priority
|
||||
* - Featured content: glowColor="purple" (full perimeter)
|
||||
* - Subtle containers: transparency="clear" glassTint="none"
|
||||
*
|
||||
* DECISION TREE:
|
||||
* - Navigation/headers → edgePosition="top"
|
||||
* - Sidebars/lists → edgePosition="left"
|
||||
* - Actions/CTAs → edgePosition="right"
|
||||
* - Status/footers → edgePosition="bottom"
|
||||
* - Featured/selected → glowColor (full perimeter)
|
||||
*/`;
|
||||
|
||||
const props: string[] = [];
|
||||
if (config.blur !== 'xl') props.push(`blur="${config.blur}"`);
|
||||
if (config.transparency !== 'medium') props.push(`transparency="${config.transparency}"`);
|
||||
if (config.glassTint !== 'none') props.push(`glassTint="${config.glassTint}"`);
|
||||
if (config.glowColor !== 'none') props.push(`glowColor="${config.glowColor}"`);
|
||||
if (config.edgePosition !== 'none') props.push(`edgePosition="${config.edgePosition}"`);
|
||||
if (config.edgePosition !== 'none' && config.edgeColor !== 'cyan') props.push(`edgeColor="${config.edgeColor}"`);
|
||||
if (config.size !== 'md') props.push(`size="${config.size}"`);
|
||||
|
||||
const component = `export const MyCard = ({ children }) => {
|
||||
return (
|
||||
<Card${props.length > 0 ? '\n ' + props.join('\n ') : ''}>
|
||||
{children}
|
||||
</Card>
|
||||
);
|
||||
};`;
|
||||
|
||||
return `${imports}\n\n${aiContext}\n\n${component}`;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-4 gap-6 h-full">
|
||||
{/* LEFT: Configuration Panel (1/4 width) */}
|
||||
<div className="col-span-1">
|
||||
<ConfigPanel title="Glass Card Configuration">
|
||||
<div className="space-y-3">
|
||||
<ConfigRow label="Blur">
|
||||
<Select
|
||||
value={config.blur}
|
||||
onValueChange={(value) => setConfig({...config, blur: value as BlurLevel})}
|
||||
>
|
||||
<SelectTrigger className="w-24 text-xs">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="none">None (0px)</SelectItem>
|
||||
<SelectItem value="sm">Minimal (0.5px)</SelectItem>
|
||||
<SelectItem value="md">Subtle (1px)</SelectItem>
|
||||
<SelectItem value="lg">Light (2px)</SelectItem>
|
||||
<SelectItem value="xl">Standard (3px)</SelectItem>
|
||||
<SelectItem value="2xl">Noticeable (5px)</SelectItem>
|
||||
<SelectItem value="3xl">Maximum (8px)</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</ConfigRow>
|
||||
|
||||
<ConfigRow label="Transparency">
|
||||
<Select
|
||||
value={config.transparency}
|
||||
onValueChange={(value) => setConfig({...config, transparency: value as Transparency})}
|
||||
>
|
||||
<SelectTrigger className="w-24 text-xs">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="clear">Clear</SelectItem>
|
||||
<SelectItem value="light">Light</SelectItem>
|
||||
<SelectItem value="medium">Medium</SelectItem>
|
||||
<SelectItem value="frosted">Frosted</SelectItem>
|
||||
<SelectItem value="solid">Solid</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</ConfigRow>
|
||||
|
||||
<ConfigRow label="Glass Tint">
|
||||
<Select
|
||||
value={config.glassTint}
|
||||
onValueChange={(value) => setConfig({...config, glassTint: value as GlassTint})}
|
||||
>
|
||||
<SelectTrigger className="w-24 text-xs">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="none">None</SelectItem>
|
||||
<SelectItem value="purple">🟣 Purple</SelectItem>
|
||||
<SelectItem value="blue">🔵 Blue</SelectItem>
|
||||
<SelectItem value="cyan">🔷 Cyan</SelectItem>
|
||||
<SelectItem value="green">🟢 Green</SelectItem>
|
||||
<SelectItem value="orange">🟠 Orange</SelectItem>
|
||||
<SelectItem value="pink">🩷 Pink</SelectItem>
|
||||
<SelectItem value="red">🔴 Red</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</ConfigRow>
|
||||
|
||||
<ConfigRow label="Glow Color">
|
||||
<Select
|
||||
value={config.glowColor}
|
||||
onValueChange={(value) => setConfig({...config, glowColor: value as GlowColor})}
|
||||
disabled={config.edgePosition !== 'none'}
|
||||
>
|
||||
<SelectTrigger className="w-24 text-xs">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="none">None</SelectItem>
|
||||
<SelectItem value="purple">🟣 Purple</SelectItem>
|
||||
<SelectItem value="blue">🔵 Blue</SelectItem>
|
||||
<SelectItem value="cyan">🔷 Cyan</SelectItem>
|
||||
<SelectItem value="green">🟢 Green</SelectItem>
|
||||
<SelectItem value="orange">🟠 Orange</SelectItem>
|
||||
<SelectItem value="pink">🩷 Pink</SelectItem>
|
||||
<SelectItem value="red">🔴 Red</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</ConfigRow>
|
||||
|
||||
<ConfigRow label="Edge Position">
|
||||
<Select
|
||||
value={config.edgePosition}
|
||||
onValueChange={(value) => {
|
||||
setConfig({
|
||||
...config,
|
||||
edgePosition: value as EdgePosition,
|
||||
glowColor: value !== 'none' ? 'none' : config.glowColor
|
||||
});
|
||||
}}
|
||||
>
|
||||
<SelectTrigger className="w-24 text-xs">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="none">None</SelectItem>
|
||||
<SelectItem value="top">↑ Top</SelectItem>
|
||||
<SelectItem value="left">← Left</SelectItem>
|
||||
<SelectItem value="right">→ Right</SelectItem>
|
||||
<SelectItem value="bottom">↓ Bottom</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</ConfigRow>
|
||||
|
||||
<ConfigRow label="Edge Color">
|
||||
<Select
|
||||
value={config.edgeColor}
|
||||
onValueChange={(value) => setConfig({...config, edgeColor: value as EdgeColor})}
|
||||
disabled={config.edgePosition === 'none'}
|
||||
>
|
||||
<SelectTrigger className="w-24 text-xs">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="purple">🟣 Purple</SelectItem>
|
||||
<SelectItem value="blue">🔵 Blue</SelectItem>
|
||||
<SelectItem value="cyan">🔷 Cyan</SelectItem>
|
||||
<SelectItem value="green">🟢 Green</SelectItem>
|
||||
<SelectItem value="orange">🟠 Orange</SelectItem>
|
||||
<SelectItem value="pink">🩷 Pink</SelectItem>
|
||||
<SelectItem value="red">🔴 Red</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</ConfigRow>
|
||||
|
||||
<ConfigRow label="Size">
|
||||
<Select
|
||||
value={config.size}
|
||||
onValueChange={(value) => setConfig({...config, size: value as CardSize})}
|
||||
>
|
||||
<SelectTrigger className="w-24 text-xs">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="sm">Small</SelectItem>
|
||||
<SelectItem value="md">Medium</SelectItem>
|
||||
<SelectItem value="lg">Large</SelectItem>
|
||||
<SelectItem value="xl">XLarge</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</ConfigRow>
|
||||
|
||||
<ConfigRow label="Content">
|
||||
<Input
|
||||
value={config.content}
|
||||
onChange={(e) => setConfig({...config, content: e.target.value})}
|
||||
placeholder="Card content..."
|
||||
className="w-24 text-xs"
|
||||
/>
|
||||
</ConfigRow>
|
||||
</div>
|
||||
|
||||
{/* Preview/Code Tabs INSIDE configurator */}
|
||||
<div className="mt-6 pt-4 border-t border-gray-200 dark:border-gray-700">
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
size="sm"
|
||||
variant={activeTab === 'preview' ? 'default' : 'outline'}
|
||||
onClick={() => setActiveTab('preview')}
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
<Eye className="w-4 h-4" />
|
||||
Preview
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant={activeTab === 'code' ? 'default' : 'outline'}
|
||||
onClick={() => setActiveTab('code')}
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
<Code className="w-4 h-4" />
|
||||
Code
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</ConfigPanel>
|
||||
</div>
|
||||
|
||||
{/* RIGHT: Preview or Code Content (3/4 width) */}
|
||||
<div className="col-span-3">
|
||||
{activeTab === 'preview' ? (
|
||||
<LivePreview>
|
||||
<Card
|
||||
blur={config.blur}
|
||||
transparency={config.transparency}
|
||||
glassTint={config.glassTint}
|
||||
glowColor={config.glowColor}
|
||||
edgePosition={config.edgePosition}
|
||||
edgeColor={config.edgeColor}
|
||||
size={config.size}
|
||||
>
|
||||
<h4 className="text-xl font-semibold text-gray-900 dark:text-white mb-4">Glass Card Example</h4>
|
||||
<p className="text-gray-700 dark:text-gray-300 mb-4">
|
||||
{config.content}
|
||||
</p>
|
||||
<div className="text-xs text-gray-600 dark:text-gray-400 p-3 bg-white/20 dark:bg-black/20 rounded border border-gray-300 dark:border-gray-600">
|
||||
<div><strong>Blur:</strong> {config.blur}</div>
|
||||
<div><strong>Transparency:</strong> {config.transparency}</div>
|
||||
<div><strong>Glass Tint:</strong> {config.glassTint}</div>
|
||||
{config.edgePosition === 'none' ? (
|
||||
<div><strong>Glow Color:</strong> {config.glowColor}</div>
|
||||
) : (
|
||||
<>
|
||||
<div><strong>Edge Position:</strong> {config.edgePosition}</div>
|
||||
<div><strong>Edge Color:</strong> {config.edgeColor}</div>
|
||||
</>
|
||||
)}
|
||||
<div><strong>Size:</strong> {config.size}</div>
|
||||
</div>
|
||||
</Card>
|
||||
</LivePreview>
|
||||
) : (
|
||||
<CodeDisplay
|
||||
code={generateCode(config)}
|
||||
showLineNumbers
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,235 @@
|
||||
import { useState } from 'react';
|
||||
import { Button } from '@/features/ui/primitives/button';
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/features/ui/primitives/select';
|
||||
import { CodeDisplay } from '../shared/CodeDisplay';
|
||||
import { ConfigPanel } from '../shared/ConfigPanel';
|
||||
import { ConfigRow } from '../shared/ConfigRow';
|
||||
import { MODAL_TYPES } from '../standards/modalStandards';
|
||||
import { Eye, Code, X } from 'lucide-react';
|
||||
import type { ModalSize, GlowColor } from '../types';
|
||||
|
||||
export const ModalConfigurator = () => {
|
||||
const [selectedType, setSelectedType] = useState<keyof typeof MODAL_TYPES>('confirmation');
|
||||
const [activeTab, setActiveTab] = useState<'preview' | 'code'>('preview');
|
||||
const [customSize, setCustomSize] = useState<ModalSize>('sm');
|
||||
const [customGlowColor, setCustomGlowColor] = useState<Exclude<GlowColor, 'none' | 'pink'>>('red');
|
||||
|
||||
const generateCode = (type: keyof typeof MODAL_TYPES) => {
|
||||
|
||||
const imports = `import { AlertDialog, AlertDialogContent, AlertDialogHeader, AlertDialogTitle, AlertDialogDescription, AlertDialogFooter, AlertDialogAction, AlertDialogCancel, AlertDialogTrigger } from '@/features/ui/primitives/alert-dialog';
|
||||
import { Button } from '@/features/ui/primitives/button';`;
|
||||
|
||||
const aiContext = `/**
|
||||
* 🤖 AI CONTEXT: ${type.charAt(0).toUpperCase() + type.slice(1)} Modal
|
||||
*
|
||||
* PURPOSE: ${MODAL_TYPES[type].purpose}
|
||||
* SIZE: ${customSize.toUpperCase()} - ${customSize === 'sm' ? 'Compact for simple confirmations' :
|
||||
customSize === 'md' ? 'Standard for forms and content' :
|
||||
customSize === 'lg' ? 'Large for detailed displays' :
|
||||
'Extra large for code/data views'}
|
||||
* GLOW: ${customGlowColor.toUpperCase()} - ${customGlowColor === 'red' ? 'Danger/destructive actions' :
|
||||
customGlowColor === 'green' ? 'Success/creation actions' :
|
||||
customGlowColor === 'blue' ? 'Information/editing' :
|
||||
customGlowColor === 'purple' ? 'Primary/featured content' :
|
||||
'Special emphasis/code display'}
|
||||
*
|
||||
* WHEN TO USE:
|
||||
* ${type === 'confirmation' ? '- Delete operations\n * - Irreversible actions\n * - Warning confirmations' :
|
||||
type === 'formCreate' ? '- Creating new resources\n * - Data entry forms\n * - Positive actions' :
|
||||
type === 'formEdit' ? '- Editing existing data\n * - Update operations\n * - Modification forms' :
|
||||
type === 'display' ? '- Showing detailed information\n * - Read-only content\n * - Feature showcases' :
|
||||
type === 'codeViewer' ? '- Code snippets display\n * - JSON/data viewing\n * - Technical content' :
|
||||
'- Application settings\n * - Configuration panels\n * - User preferences'}
|
||||
*
|
||||
* IMPLEMENTATION: Use AlertDialog for modal behavior, wrap trigger in AlertDialogTrigger
|
||||
*/`;
|
||||
|
||||
const component = `export const ${type.charAt(0).toUpperCase() + type.slice(1)}Modal = ({ trigger, onConfirm }) => {
|
||||
return (
|
||||
<AlertDialog>
|
||||
<AlertDialogTrigger asChild>
|
||||
{trigger}
|
||||
</AlertDialogTrigger>
|
||||
<AlertDialogContent className="sm:max-w-${customSize === 'sm' ? 'sm' : customSize === 'md' ? 'md' : customSize === 'lg' ? 'lg' : 'xl'}">
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>${type === 'confirmation' ? 'Confirm Action' :
|
||||
type === 'formCreate' ? 'Create New Item' :
|
||||
type === 'formEdit' ? 'Edit Item' :
|
||||
type === 'display' ? 'Details' :
|
||||
type === 'codeViewer' ? 'Code Viewer' :
|
||||
'Settings'}</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
${MODAL_TYPES[type].purpose}
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
|
||||
{/* Modal content goes here */}
|
||||
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
||||
<AlertDialogAction onClick={onConfirm}>
|
||||
${type === 'confirmation' ? 'Confirm' :
|
||||
type === 'formCreate' ? 'Create' :
|
||||
type === 'formEdit' ? 'Save Changes' :
|
||||
'Close'}
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
);
|
||||
};`;
|
||||
|
||||
return `${imports}\n\n${aiContext}\n\n${component}`;
|
||||
};
|
||||
|
||||
const currentConfig = MODAL_TYPES[selectedType];
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-4 gap-6 h-full">
|
||||
{/* LEFT: Configuration Panel (1/4 width) */}
|
||||
<div className="col-span-1">
|
||||
<ConfigPanel title="Modal Standards">
|
||||
<div className="space-y-4">
|
||||
<ConfigRow label="Modal Type">
|
||||
<Select
|
||||
value={selectedType}
|
||||
onValueChange={(value) => setSelectedType(value as keyof typeof MODAL_TYPES)}
|
||||
>
|
||||
<SelectTrigger className="w-32">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="confirmation">Confirmation</SelectItem>
|
||||
<SelectItem value="formCreate">Form Create</SelectItem>
|
||||
<SelectItem value="formEdit">Form Edit</SelectItem>
|
||||
<SelectItem value="display">Display</SelectItem>
|
||||
<SelectItem value="codeViewer">Code Viewer</SelectItem>
|
||||
<SelectItem value="settings">Settings</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</ConfigRow>
|
||||
|
||||
<ConfigRow label="Size">
|
||||
<Select
|
||||
value={customSize}
|
||||
onValueChange={(value) => setCustomSize(value as ModalSize)}
|
||||
>
|
||||
<SelectTrigger className="w-24 text-xs">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="sm">SM</SelectItem>
|
||||
<SelectItem value="md">MD</SelectItem>
|
||||
<SelectItem value="lg">LG</SelectItem>
|
||||
<SelectItem value="xl">XL</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</ConfigRow>
|
||||
|
||||
<ConfigRow label="Glow Color">
|
||||
<Select
|
||||
value={customGlowColor}
|
||||
onValueChange={(value) => setCustomGlowColor(value as Exclude<GlowColor, 'none' | 'pink'>)}
|
||||
>
|
||||
<SelectTrigger className="w-24 text-xs">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="purple">Purple</SelectItem>
|
||||
<SelectItem value="blue">Blue</SelectItem>
|
||||
<SelectItem value="green">Green</SelectItem>
|
||||
<SelectItem value="red">Red</SelectItem>
|
||||
<SelectItem value="orange">Orange</SelectItem>
|
||||
<SelectItem value="cyan">Cyan</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</ConfigRow>
|
||||
|
||||
<ConfigRow label="Purpose">
|
||||
<div className="text-xs text-gray-600 dark:text-gray-400 w-32 text-right">
|
||||
{currentConfig.purpose}
|
||||
</div>
|
||||
</ConfigRow>
|
||||
</div>
|
||||
|
||||
{/* Preview/Code Tabs INSIDE configurator */}
|
||||
<div className="mt-6 pt-4 border-t border-gray-200 dark:border-gray-700">
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
size="sm"
|
||||
variant={activeTab === 'preview' ? 'default' : 'outline'}
|
||||
onClick={() => setActiveTab('preview')}
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
<Eye className="w-4 h-4" />
|
||||
Preview
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant={activeTab === 'code' ? 'default' : 'outline'}
|
||||
onClick={() => setActiveTab('code')}
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
<Code className="w-4 h-4" />
|
||||
Code
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</ConfigPanel>
|
||||
</div>
|
||||
|
||||
{/* RIGHT: Preview or Code Content (3/4 width) */}
|
||||
<div className="col-span-3">
|
||||
{activeTab === 'preview' ? (
|
||||
<div className="flex items-center justify-center min-h-[400px]">
|
||||
{/* Inline Modal Preview - No Button Trigger */}
|
||||
<div className={`relative max-w-${customSize === 'sm' ? 'sm' : customSize === 'md' ? 'md' : customSize === 'lg' ? 'lg' : 'xl'} w-full mx-auto`}>
|
||||
<div className="backdrop-blur-md bg-white/95 dark:bg-gray-900/95 border border-gray-200 dark:border-gray-700 rounded-lg shadow-2xl p-6">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<h3 className="text-lg font-semibold">
|
||||
{selectedType === 'confirmation' ? 'Confirm Action' :
|
||||
selectedType === 'formCreate' ? 'Create New Item' :
|
||||
selectedType === 'formEdit' ? 'Edit Item' :
|
||||
selectedType === 'display' ? 'Details' :
|
||||
selectedType === 'codeViewer' ? 'Code Viewer' :
|
||||
'Settings'}
|
||||
</h3>
|
||||
<Button size="icon" variant="ghost">
|
||||
<X className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="mb-4">
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400">
|
||||
{currentConfig.purpose} - This is an inline preview of the {selectedType} modal type.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="py-4 mb-6">
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400">
|
||||
Modal content would go here. This {customSize} sized modal uses {customGlowColor} color theming.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-end gap-3">
|
||||
<Button variant="outline">Cancel</Button>
|
||||
<Button variant={selectedType === 'confirmation' ? 'destructive' : 'default'}>
|
||||
{selectedType === 'confirmation' ? 'Confirm' :
|
||||
selectedType === 'formCreate' ? 'Create' :
|
||||
selectedType === 'formEdit' ? 'Save Changes' :
|
||||
'Close'}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<CodeDisplay
|
||||
code={generateCode(selectedType)}
|
||||
showLineNumbers
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,196 @@
|
||||
import { useState } from 'react';
|
||||
import { Button } from '@/features/ui/primitives/button';
|
||||
import { Switch } from '@/features/ui/primitives/switch';
|
||||
import { Label } from '@/features/ui/primitives/label';
|
||||
import { LivePreview } from '../shared/LivePreview';
|
||||
import { CodeDisplay } from '../shared/CodeDisplay';
|
||||
import { ConfigPanel } from '../shared/ConfigPanel';
|
||||
import { Edit, Trash2, Eye, Code } from 'lucide-react';
|
||||
|
||||
interface TableConfig {
|
||||
glassMorphism: boolean;
|
||||
actions: boolean;
|
||||
headers: boolean;
|
||||
striped: boolean;
|
||||
}
|
||||
|
||||
const sampleData = [
|
||||
{ id: 1, name: 'John Doe', email: 'john@example.com', status: 'Active' },
|
||||
{ id: 2, name: 'Jane Smith', email: 'jane@example.com', status: 'Inactive' },
|
||||
{ id: 3, name: 'Bob Johnson', email: 'bob@example.com', status: 'Active' },
|
||||
];
|
||||
|
||||
export const TableConfigurator = () => {
|
||||
const [config, setConfig] = useState<TableConfig>({
|
||||
glassMorphism: true,
|
||||
actions: true,
|
||||
headers: true,
|
||||
striped: true
|
||||
});
|
||||
const [activeTab, setActiveTab] = useState<'preview' | 'code'>('preview');
|
||||
|
||||
const generateCode = (_config: TableConfig) => {
|
||||
const imports = `import { Button } from '@/features/ui/primitives/button';
|
||||
import { Edit, Trash2, Eye } from 'lucide-react';`;
|
||||
|
||||
const aiContext = `/**
|
||||
* 🤖 AI CONTEXT: Glass Morphism Table
|
||||
*
|
||||
* PURPOSE: Data display with glass morphism styling and interactive elements
|
||||
* WHEN TO USE: Data grids, lists with actions, dashboard tables
|
||||
* WHEN NOT TO USE: Simple lists (use basic ul/li), massive datasets (use virtualization)
|
||||
*/`;
|
||||
|
||||
return `${imports}\n\n${aiContext}\n\n// Table component code here...`;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-4 gap-6 h-full">
|
||||
{/* LEFT: Configuration Panel (1/4 width) */}
|
||||
<div className="col-span-1">
|
||||
<ConfigPanel title="Table Configuration">
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<Label htmlFor="glass-morphism" className="text-sm">Glass Morphism</Label>
|
||||
<Switch
|
||||
id="glass-morphism"
|
||||
checked={config.glassMorphism}
|
||||
onCheckedChange={(glassMorphism) => setConfig({...config, glassMorphism})}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<Label htmlFor="show-actions" className="text-sm">Show Actions</Label>
|
||||
<Switch
|
||||
id="show-actions"
|
||||
checked={config.actions}
|
||||
onCheckedChange={(actions) => setConfig({...config, actions})}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<Label htmlFor="show-headers" className="text-sm">Show Headers</Label>
|
||||
<Switch
|
||||
id="show-headers"
|
||||
checked={config.headers}
|
||||
onCheckedChange={(headers) => setConfig({...config, headers})}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<Label htmlFor="striped-rows" className="text-sm">Striped Rows</Label>
|
||||
<Switch
|
||||
id="striped-rows"
|
||||
checked={config.striped}
|
||||
onCheckedChange={(striped) => setConfig({...config, striped})}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Preview/Code Tabs INSIDE configurator */}
|
||||
<div className="mt-4 pt-4 border-t border-gray-200 dark:border-gray-700">
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
size="sm"
|
||||
variant={activeTab === 'preview' ? 'default' : 'outline'}
|
||||
onClick={() => setActiveTab('preview')}
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
<Eye className="w-4 h-4" />
|
||||
Preview
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant={activeTab === 'code' ? 'default' : 'outline'}
|
||||
onClick={() => setActiveTab('code')}
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
<Code className="w-4 h-4" />
|
||||
Code
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</ConfigPanel>
|
||||
</div>
|
||||
|
||||
{/* RIGHT: Preview or Code Content (3/4 width) */}
|
||||
<div className="col-span-3">
|
||||
{activeTab === 'preview' ? (
|
||||
<LivePreview>
|
||||
<div className={`w-full ${config.glassMorphism
|
||||
? 'backdrop-blur-md bg-white/80 dark:bg-black/30 border border-gray-200 dark:border-gray-700 rounded-lg overflow-hidden shadow-lg'
|
||||
: 'bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg overflow-hidden'
|
||||
}`}>
|
||||
<table className="w-full">
|
||||
{config.headers && (
|
||||
<thead className="bg-gray-50/80 dark:bg-gray-800/80 border-b border-gray-200 dark:border-gray-700">
|
||||
<tr>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
|
||||
Name
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
|
||||
Email
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
|
||||
Status
|
||||
</th>
|
||||
{config.actions && (
|
||||
<th className="px-6 py-3 text-right text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">
|
||||
Actions
|
||||
</th>
|
||||
)}
|
||||
</tr>
|
||||
</thead>
|
||||
)}
|
||||
<tbody className="divide-y divide-gray-200 dark:divide-gray-700">
|
||||
{sampleData.map((item, index) => (
|
||||
<tr
|
||||
key={item.id}
|
||||
className={`${config.striped && index % 2 === 1 ? 'bg-gray-50/50 dark:bg-gray-800/50' : ''} hover:bg-gray-50/70 dark:hover:bg-gray-800/70 transition-colors`}
|
||||
>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900 dark:text-white">
|
||||
{item.name}
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500 dark:text-gray-400">
|
||||
{item.email}
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap">
|
||||
<span className={`inline-flex px-2 py-1 text-xs font-semibold rounded-full ${
|
||||
item.status === 'Active'
|
||||
? 'bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-400'
|
||||
: 'bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-400'
|
||||
}`}>
|
||||
{item.status}
|
||||
</span>
|
||||
</td>
|
||||
{config.actions && (
|
||||
<td className="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
|
||||
<div className="flex justify-end gap-2">
|
||||
<Button size="icon" variant="ghost">
|
||||
<Eye className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button size="icon" variant="ghost">
|
||||
<Edit className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button size="icon" variant="ghost">
|
||||
<Trash2 className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</td>
|
||||
)}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</LivePreview>
|
||||
) : (
|
||||
<CodeDisplay
|
||||
code={generateCode(config)}
|
||||
showLineNumbers
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,174 @@
|
||||
import { useState } from 'react';
|
||||
import { Switch } from '@/features/ui/primitives/switch';
|
||||
import { Button } from '@/features/ui/primitives/button';
|
||||
import { Label } from '@/features/ui/primitives/label';
|
||||
import { RadioGroup, RadioGroupItem } from '@/features/ui/primitives/radio-group';
|
||||
import { LivePreview } from '../shared/LivePreview';
|
||||
import { CodeDisplay } from '../shared/CodeDisplay';
|
||||
import { ConfigPanel } from '../shared/ConfigPanel';
|
||||
import { Eye, Code } from 'lucide-react';
|
||||
import type { LabelPosition } from '../types';
|
||||
|
||||
interface ToggleConfig {
|
||||
disabled: boolean;
|
||||
size: 'sm' | 'default' | 'lg';
|
||||
labelPosition: LabelPosition;
|
||||
labelText: string;
|
||||
}
|
||||
|
||||
export const ToggleConfigurator = () => {
|
||||
const [config, setConfig] = useState<ToggleConfig>({
|
||||
disabled: false,
|
||||
size: 'default',
|
||||
labelPosition: 'right',
|
||||
labelText: 'Enable notifications'
|
||||
});
|
||||
const [activeTab, setActiveTab] = useState<'preview' | 'code'>('preview');
|
||||
const [toggleState, setToggleState] = useState(false);
|
||||
|
||||
const generateCode = (_config: ToggleConfig) => {
|
||||
const imports = `import { Switch } from '@/features/ui/primitives/switch';
|
||||
import { Label } from '@/features/ui/primitives/label';`;
|
||||
|
||||
const aiContext = `/**
|
||||
* 🤖 AI CONTEXT: Switch Toggle Component
|
||||
*
|
||||
* PURPOSE: Binary on/off controls with accessible labeling
|
||||
* WHEN TO USE: Settings, preferences, feature toggles, boolean states
|
||||
* WHEN NOT TO USE: Multiple options (use RadioGroup), momentary actions (use Button)
|
||||
*/`;
|
||||
|
||||
return `${imports}\n\n${aiContext}\n\n// Toggle component code here...`;
|
||||
};
|
||||
|
||||
const layoutClasses = {
|
||||
left: 'flex-row-reverse',
|
||||
right: 'flex-row',
|
||||
top: 'flex-col',
|
||||
bottom: 'flex-col-reverse'
|
||||
};
|
||||
|
||||
const gapClasses = {
|
||||
left: 'gap-3',
|
||||
right: 'gap-3',
|
||||
top: 'gap-2',
|
||||
bottom: 'gap-2'
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-4 gap-6 h-full">
|
||||
{/* LEFT: Configuration Panel (1/4 width) */}
|
||||
<div className="col-span-1">
|
||||
<ConfigPanel title="Switch Toggle Configuration">
|
||||
<div className="space-y-4">
|
||||
{/* Label Position */}
|
||||
<div>
|
||||
<Label className="text-sm">Label Position</Label>
|
||||
<RadioGroup
|
||||
value={config.labelPosition}
|
||||
onValueChange={(value) => setConfig({...config, labelPosition: value as LabelPosition})}
|
||||
>
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
{['left', 'right', 'top', 'bottom'].map(position => (
|
||||
<div key={position} className="flex items-center space-x-2">
|
||||
<RadioGroupItem value={position} id={`position-${position}`} />
|
||||
<Label htmlFor={`position-${position}`} className="capitalize text-xs">
|
||||
{position}
|
||||
</Label>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</RadioGroup>
|
||||
</div>
|
||||
|
||||
{/* States */}
|
||||
<div className="flex items-center justify-between">
|
||||
<Label htmlFor="disabled-toggle" className="text-sm">Disabled State</Label>
|
||||
<Switch
|
||||
id="disabled-toggle"
|
||||
checked={config.disabled}
|
||||
onCheckedChange={(disabled) => setConfig({...config, disabled})}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Label Text */}
|
||||
<div>
|
||||
<Label htmlFor="label-text" className="text-sm">Label Text</Label>
|
||||
<input
|
||||
id="label-text"
|
||||
type="text"
|
||||
value={config.labelText}
|
||||
onChange={(e) => setConfig({...config, labelText: e.target.value})}
|
||||
className="w-full mt-1 px-3 py-2 border rounded-md dark:bg-gray-800 text-sm"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Preview/Code Tabs INSIDE configurator */}
|
||||
<div className="mt-4 pt-4 border-t border-gray-200 dark:border-gray-700">
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
size="sm"
|
||||
variant={activeTab === 'preview' ? 'default' : 'outline'}
|
||||
onClick={() => setActiveTab('preview')}
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
<Eye className="w-4 h-4" />
|
||||
Preview
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant={activeTab === 'code' ? 'default' : 'outline'}
|
||||
onClick={() => setActiveTab('code')}
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
<Code className="w-4 h-4" />
|
||||
Code
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</ConfigPanel>
|
||||
</div>
|
||||
|
||||
{/* RIGHT: Preview or Code Content (3/4 width) */}
|
||||
<div className="col-span-3">
|
||||
{activeTab === 'preview' ? (
|
||||
<LivePreview>
|
||||
<div className={`flex ${layoutClasses[config.labelPosition]} ${gapClasses[config.labelPosition]} items-center`}>
|
||||
{(config.labelPosition === 'top' || config.labelPosition === 'left') ? (
|
||||
<>
|
||||
<Label htmlFor="preview-toggle">
|
||||
{config.labelText}
|
||||
</Label>
|
||||
<Switch
|
||||
id="preview-toggle"
|
||||
checked={toggleState}
|
||||
onCheckedChange={setToggleState}
|
||||
disabled={config.disabled}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Switch
|
||||
id="preview-toggle"
|
||||
checked={toggleState}
|
||||
onCheckedChange={setToggleState}
|
||||
disabled={config.disabled}
|
||||
/>
|
||||
<Label htmlFor="preview-toggle">
|
||||
{config.labelText}
|
||||
</Label>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</LivePreview>
|
||||
) : (
|
||||
<CodeDisplay
|
||||
code={generateCode(config)}
|
||||
showLineNumbers
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,638 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Card } from '@/features/ui/primitives/card';
|
||||
import type { GlowColor } from '../types';
|
||||
import { Button } from '@/features/ui/primitives/button';
|
||||
import { CodeDisplay } from '../shared/CodeDisplay';
|
||||
import { LivePreview } from '../shared/LivePreview';
|
||||
import { cn, glassmorphism } from '@/features/ui/primitives/styles';
|
||||
import {
|
||||
Home,
|
||||
Settings,
|
||||
User,
|
||||
FileText,
|
||||
BarChart,
|
||||
Plus,
|
||||
Search,
|
||||
Bell,
|
||||
MoreVertical,
|
||||
Calendar,
|
||||
} from 'lucide-react';
|
||||
|
||||
interface ExampleComposition {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
usage: string;
|
||||
component: React.ComponentType;
|
||||
}
|
||||
|
||||
// Complete Dashboard Composition
|
||||
const DashboardComposition = () => {
|
||||
return (
|
||||
<div className="h-96 bg-gray-50 dark:bg-gray-900 overflow-hidden rounded-lg">
|
||||
{/* Top Navigation */}
|
||||
<Card className="p-4 rounded-none border-b">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-8 h-8 bg-gradient-to-br from-cyan-500 to-purple-500 rounded-lg" />
|
||||
<span className="font-bold text-lg">Archon</span>
|
||||
</div>
|
||||
<nav className="hidden md:flex items-center gap-1">
|
||||
{['Dashboard', 'Projects', 'Team', 'Analytics'].map((item, index) => (
|
||||
<button
|
||||
key={item}
|
||||
className={cn(
|
||||
"px-3 py-1 rounded-lg text-sm font-medium transition-all duration-200",
|
||||
index === 0
|
||||
? cn(glassmorphism.background.cyan, "text-cyan-700 dark:text-cyan-300")
|
||||
: "text-gray-600 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800"
|
||||
)}
|
||||
>
|
||||
{item}
|
||||
</button>
|
||||
))}
|
||||
</nav>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button variant="ghost" size="sm"><Search className="w-4 h-4" /></Button>
|
||||
<Button variant="ghost" size="sm"><Bell className="w-4 h-4" /></Button>
|
||||
<Button variant="ghost" size="sm"><User className="w-4 h-4" /></Button>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<div className="flex h-full">
|
||||
{/* Sidebar */}
|
||||
<Card className="w-48 h-full rounded-none border-r">
|
||||
<div className="p-3">
|
||||
<nav className="space-y-1">
|
||||
{[
|
||||
{ icon: <Home className="w-4 h-4" />, label: 'Dashboard', active: true },
|
||||
{ icon: <FileText className="w-4 h-4" />, label: 'Projects' },
|
||||
{ icon: <BarChart className="w-4 h-4" />, label: 'Analytics' },
|
||||
{ icon: <Settings className="w-4 h-4" />, label: 'Settings' }
|
||||
].map((item) => (
|
||||
<button
|
||||
key={item.label}
|
||||
className={cn(
|
||||
"w-full flex items-center gap-2 px-2 py-1.5 rounded text-sm transition-all",
|
||||
item.active
|
||||
? cn(glassmorphism.background.cyan, "text-cyan-700 dark:text-cyan-300")
|
||||
: "hover:bg-gray-100 dark:hover:bg-gray-800"
|
||||
)}
|
||||
>
|
||||
{item.icon}
|
||||
{item.label}
|
||||
</button>
|
||||
))}
|
||||
</nav>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* Main Content */}
|
||||
<div className="flex-1 p-4 space-y-4">
|
||||
{/* Stats Grid */}
|
||||
<div className="grid grid-cols-3 gap-3">
|
||||
{[
|
||||
{ label: 'Projects', value: '24', color: 'purple' },
|
||||
{ label: 'Tasks', value: '156', color: 'blue' },
|
||||
{ label: 'Team', value: '8', color: 'green' }
|
||||
].map((stat) => (
|
||||
<Card key={stat.label} glowColor={stat.color as GlowColor} className="p-3 text-center">
|
||||
<p className="text-lg font-bold">{stat.value}</p>
|
||||
<p className="text-xs text-gray-600 dark:text-gray-400">{stat.label}</p>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Content Grid */}
|
||||
<div className="grid grid-cols-3 gap-3 flex-1">
|
||||
<Card className="col-span-2 p-3">
|
||||
<h3 className="font-medium text-sm mb-2">Recent Activity</h3>
|
||||
<div className="space-y-1">
|
||||
{['Project updated', 'Task completed', 'New member'].map((activity, i) => (
|
||||
<div key={i} className="text-xs py-1 border-b border-gray-200 dark:border-gray-700 last:border-b-0">
|
||||
{activity}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</Card>
|
||||
<Card className="p-3">
|
||||
<h3 className="font-medium text-sm mb-2">Quick Actions</h3>
|
||||
<div className="space-y-1">
|
||||
<Button variant="outline" size="sm" className="w-full text-xs">Create</Button>
|
||||
<Button variant="outline" size="sm" className="w-full text-xs">Invite</Button>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// Project Card Collection
|
||||
const ProjectCardsComposition = () => {
|
||||
const projects = [
|
||||
{ title: 'Alpha', status: 'Active', progress: 75, color: 'purple' },
|
||||
{ title: 'Beta', status: 'Review', progress: 90, color: 'blue' },
|
||||
{ title: 'Gamma', status: 'Planning', progress: 25, color: 'orange' }
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="p-6 bg-gray-50 dark:bg-gray-900 rounded-lg">
|
||||
<div className="flex items-center justify-between mb-6">
|
||||
<h2 className="text-xl font-bold">Projects</h2>
|
||||
<Button size="sm">
|
||||
<Plus className="w-4 h-4 mr-2" />
|
||||
New Project
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
{projects.map((project) => (
|
||||
<Card
|
||||
key={project.title}
|
||||
glowColor={project.color as GlowColor}
|
||||
className="p-4 hover:shadow-lg transition-shadow"
|
||||
>
|
||||
<div className="flex items-start justify-between mb-3">
|
||||
<h3 className="font-semibold">Project {project.title}</h3>
|
||||
<Button variant="ghost" size="sm">
|
||||
<MoreVertical className="w-4 h-4" />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400 mb-3">
|
||||
Development project with team collaboration features.
|
||||
</p>
|
||||
|
||||
<div className="mb-3">
|
||||
<div className="flex justify-between text-sm mb-1">
|
||||
<span>Progress</span>
|
||||
<span>{project.progress}%</span>
|
||||
</div>
|
||||
<div className="w-full bg-gray-200 dark:bg-gray-700 rounded-full h-2">
|
||||
<div
|
||||
className="bg-cyan-500 h-2 rounded-full"
|
||||
style={{ width: `${project.progress}%` }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between text-xs text-gray-500 dark:text-gray-400">
|
||||
<span className="flex items-center gap-1">
|
||||
<Calendar className="w-3 h-3" />
|
||||
Due: 2 weeks
|
||||
</span>
|
||||
<span className="flex items-center gap-1">
|
||||
<User className="w-3 h-3" />
|
||||
5 members
|
||||
</span>
|
||||
</div>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// Modal with Form
|
||||
const ModalFormComposition = () => {
|
||||
const [showModal, setShowModal] = useState(false);
|
||||
|
||||
return (
|
||||
<div className="relative">
|
||||
<div className="p-6 text-center">
|
||||
<Button onClick={() => setShowModal(true)}>
|
||||
Show Modal Example
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{showModal && (
|
||||
<div className="fixed inset-0 bg-black/50 backdrop-blur-sm flex items-center justify-center z-50">
|
||||
<Card size="lg" glowColor="purple" className="w-full max-w-md">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<h2 className="text-xl font-bold">Create New Task</h2>
|
||||
<Button variant="ghost" size="sm" onClick={() => setShowModal(false)}>
|
||||
×
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<form className="space-y-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-1">Task Title</label>
|
||||
<input
|
||||
type="text"
|
||||
className={cn(
|
||||
"w-full px-3 py-2 rounded-md text-sm",
|
||||
glassmorphism.background.subtle,
|
||||
glassmorphism.border.default,
|
||||
glassmorphism.border.focus
|
||||
)}
|
||||
placeholder="Enter task title"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-1">Priority</label>
|
||||
<select
|
||||
className={cn(
|
||||
"w-full px-3 py-2 rounded-md text-sm",
|
||||
glassmorphism.background.subtle,
|
||||
glassmorphism.border.default,
|
||||
glassmorphism.border.focus
|
||||
)}
|
||||
>
|
||||
<option>High</option>
|
||||
<option>Medium</option>
|
||||
<option>Low</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-1">Description</label>
|
||||
<textarea
|
||||
className={cn(
|
||||
"w-full px-3 py-2 rounded-md text-sm h-20 resize-none",
|
||||
glassmorphism.background.subtle,
|
||||
glassmorphism.border.default,
|
||||
glassmorphism.border.focus
|
||||
)}
|
||||
placeholder="Describe the task"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-3 pt-4">
|
||||
<Button
|
||||
variant="outline"
|
||||
className="flex-1"
|
||||
onClick={() => setShowModal(false)}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
className="flex-1"
|
||||
onClick={() => setShowModal(false)}
|
||||
>
|
||||
Create Task
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Card>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const EXAMPLE_COMPOSITIONS: ExampleComposition[] = [
|
||||
{
|
||||
id: 'dashboard',
|
||||
name: 'Complete Dashboard',
|
||||
description: 'Full dashboard layout with navigation, sidebar, stats, and content areas',
|
||||
usage: 'Admin panels, application dashboards, overview pages',
|
||||
component: DashboardComposition
|
||||
},
|
||||
{
|
||||
id: 'cards',
|
||||
name: 'Project Cards Grid',
|
||||
description: 'Collection of project cards with consistent styling and interactions',
|
||||
usage: 'Project portfolios, product grids, content galleries',
|
||||
component: ProjectCardsComposition
|
||||
},
|
||||
{
|
||||
id: 'modal',
|
||||
name: 'Modal with Form',
|
||||
description: 'Interactive modal dialog with form inputs and glassmorphism styling',
|
||||
usage: 'Create/edit dialogs, settings panels, confirmation prompts',
|
||||
component: ModalFormComposition
|
||||
}
|
||||
];
|
||||
|
||||
const generateCode = (compositionId: string) => {
|
||||
const codeExamples = {
|
||||
dashboard: `/**
|
||||
* 🤖 AI CONTEXT: Complete Dashboard Composition
|
||||
*
|
||||
* PURPOSE: Full-featured dashboard with navigation and content
|
||||
* WHEN TO USE: Admin interfaces, application dashboards, data overview
|
||||
* WHEN NOT TO USE: Simple pages, mobile-first designs, content-focused sites
|
||||
*
|
||||
* COMPOSITION ELEMENTS:
|
||||
* - Top navigation with branding and user actions
|
||||
* - Collapsible sidebar with navigation items
|
||||
* - Stats cards grid for key metrics
|
||||
* - Content areas with recent activity and quick actions
|
||||
*
|
||||
* RESPONSIVE STRATEGY:
|
||||
* - Desktop: Full layout with sidebar
|
||||
* - Tablet: Collapsible sidebar
|
||||
* - Mobile: Overlay navigation drawer
|
||||
*/
|
||||
|
||||
import { Card } from '@/features/ui/primitives/card';
|
||||
import { Button } from '@/features/ui/primitives/button';
|
||||
import { glassmorphism } from '@/features/ui/primitives/styles';
|
||||
|
||||
export const DashboardLayout = () => {
|
||||
return (
|
||||
<div className="h-screen bg-gray-50 dark:bg-gray-900">
|
||||
{/* Top Navigation */}
|
||||
<Card className="p-4 rounded-none border-b">
|
||||
<div className="flex items-center justify-between">
|
||||
{/* Brand */}
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-8 h-8 bg-gradient-to-br from-cyan-500 to-purple-500 rounded-lg" />
|
||||
<span className="font-bold text-lg">Your App</span>
|
||||
</div>
|
||||
|
||||
{/* Navigation */}
|
||||
<nav className="hidden md:flex items-center gap-1">
|
||||
{/* Navigation items */}
|
||||
</nav>
|
||||
|
||||
{/* User Actions */}
|
||||
<div className="flex items-center gap-2">
|
||||
{/* User action buttons */}
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<div className="flex h-full">
|
||||
{/* Sidebar */}
|
||||
<Card className="w-64 h-full rounded-none border-r">
|
||||
<nav className="p-4 space-y-1">
|
||||
{/* Navigation items */}
|
||||
</nav>
|
||||
</Card>
|
||||
|
||||
{/* Main Content */}
|
||||
<div className="flex-1 p-6 space-y-6">
|
||||
{/* Stats Grid */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
|
||||
{/* Stat cards */}
|
||||
</div>
|
||||
|
||||
{/* Content Grid */}
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
||||
{/* Content sections */}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};`,
|
||||
|
||||
cards: `/**
|
||||
* 🤖 AI CONTEXT: Project Cards Grid Composition
|
||||
*
|
||||
* PURPOSE: Consistent card layout for project/product display
|
||||
* WHEN TO USE: Portfolios, galleries, collection views
|
||||
* WHEN NOT TO USE: List data, text-heavy content, simple navigation
|
||||
*
|
||||
* CARD ELEMENTS:
|
||||
* - Header with title and actions
|
||||
* - Progress indicators for status
|
||||
* - Metadata with icons and labels
|
||||
* - Consistent hover states and interactions
|
||||
*
|
||||
* GRID BEHAVIOR:
|
||||
* - Responsive: 1->2->3 columns
|
||||
* - Equal heights maintained
|
||||
* - Proper spacing and alignment
|
||||
*/
|
||||
|
||||
import { Card } from '@/features/ui/primitives/card';
|
||||
import { Button } from '@/features/ui/primitives/button';
|
||||
|
||||
export const ProjectCardsGrid = ({ projects }: { projects: any[] }) => {
|
||||
return (
|
||||
<div className="p-6">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between mb-6">
|
||||
<h2 className="text-2xl font-bold">Projects</h2>
|
||||
<Button>
|
||||
<Plus className="w-4 h-4 mr-2" />
|
||||
New Project
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Cards Grid */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{projects.map((project) => (
|
||||
<Card
|
||||
key={project.id}
|
||||
glowColor={project.status}
|
||||
className="p-6 hover:shadow-lg transition-all duration-300"
|
||||
>
|
||||
{/* Card header */}
|
||||
<div className="flex items-start justify-between mb-4">
|
||||
<h3 className="font-semibold text-lg">{project.title}</h3>
|
||||
<Button variant="ghost" size="sm">
|
||||
<MoreVertical className="w-4 h-4" />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Card content */}
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400 mb-4">
|
||||
{project.description}
|
||||
</p>
|
||||
|
||||
{/* Progress */}
|
||||
<div className="mb-4">
|
||||
<div className="flex justify-between text-sm mb-2">
|
||||
<span>Progress</span>
|
||||
<span>{project.progress}%</span>
|
||||
</div>
|
||||
<div className="w-full bg-gray-200 dark:bg-gray-700 rounded-full h-2">
|
||||
<div
|
||||
className="bg-cyan-500 h-2 rounded-full transition-all"
|
||||
style={{ width: \`\${project.progress}%\` }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Metadata */}
|
||||
<div className="flex items-center justify-between text-xs text-gray-500">
|
||||
<span>Due: {project.dueDate}</span>
|
||||
<span>{project.teamSize} members</span>
|
||||
</div>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};`,
|
||||
|
||||
modal: `/**
|
||||
* 🤖 AI CONTEXT: Modal with Form Composition
|
||||
*
|
||||
* PURPOSE: Interactive dialog for data input and actions
|
||||
* WHEN TO USE: Create/edit workflows, settings, confirmations
|
||||
* WHEN NOT TO USE: Large forms, multi-step processes, navigation
|
||||
*
|
||||
* MODAL STRUCTURE:
|
||||
* - Backdrop with blur effect
|
||||
* - Card container with glassmorphism
|
||||
* - Form with consistent input styling
|
||||
* - Action buttons with clear hierarchy
|
||||
*
|
||||
* ACCESSIBILITY:
|
||||
* - Focus trap within modal
|
||||
* - Escape key to close
|
||||
* - Proper ARIA labels
|
||||
* - Keyboard navigation
|
||||
*/
|
||||
|
||||
import { useState } from 'react';
|
||||
import { Card } from '@/features/ui/primitives/card';
|
||||
import { Button } from '@/features/ui/primitives/button';
|
||||
import { glassmorphism, cn } from '@/features/ui/primitives/styles';
|
||||
|
||||
export const ModalForm = ({ isOpen, onClose, onSubmit }: {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
onSubmit: (data: any) => void;
|
||||
}) => {
|
||||
if (!isOpen) return null;
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 bg-black/50 backdrop-blur-sm flex items-center justify-center z-50">
|
||||
<Card size="lg" glowColor="purple" className="w-full max-w-md">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between mb-6">
|
||||
<h2 className="text-xl font-bold">Create New Item</h2>
|
||||
<Button variant="ghost" size="sm" onClick={onClose}>
|
||||
×
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Form */}
|
||||
<form className="space-y-4" onSubmit={onSubmit}>
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-2">
|
||||
Title
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
className={cn(
|
||||
"w-full px-3 py-2 rounded-md text-sm",
|
||||
glassmorphism.background.subtle,
|
||||
glassmorphism.border.default,
|
||||
glassmorphism.border.focus,
|
||||
"transition-all duration-200"
|
||||
)}
|
||||
placeholder="Enter title"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-2">
|
||||
Description
|
||||
</label>
|
||||
<textarea
|
||||
className={cn(
|
||||
"w-full px-3 py-2 rounded-md text-sm h-20 resize-none",
|
||||
glassmorphism.background.subtle,
|
||||
glassmorphism.border.default,
|
||||
glassmorphism.border.focus,
|
||||
"transition-all duration-200"
|
||||
)}
|
||||
placeholder="Enter description"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Actions */}
|
||||
<div className="flex gap-3 pt-4">
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
className="flex-1"
|
||||
onClick={onClose}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button type="submit" className="flex-1">
|
||||
Create
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
};`
|
||||
};
|
||||
|
||||
return codeExamples[compositionId as keyof typeof codeExamples] || '';
|
||||
};
|
||||
|
||||
export const CompositionsExample = () => {
|
||||
const [selectedComposition, setSelectedComposition] = useState<string>('dashboard');
|
||||
|
||||
const currentComposition = EXAMPLE_COMPOSITIONS.find(c => c.id === selectedComposition);
|
||||
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold mb-4">Complete Compositions</h2>
|
||||
<p className="text-gray-600 dark:text-gray-400 mb-6">
|
||||
Real-world examples combining multiple components into complete UI compositions.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Composition Selector */}
|
||||
<Card className="p-6 max-w-none">
|
||||
<h3 className="text-lg font-semibold mb-4">Select Composition</h3>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
{EXAMPLE_COMPOSITIONS.map((composition) => (
|
||||
<Card
|
||||
key={composition.id}
|
||||
className={cn(
|
||||
"p-4 cursor-pointer transition-all duration-200",
|
||||
selectedComposition === composition.id
|
||||
? "border-cyan-500 shadow-[0_0_10px_rgba(34,211,238,0.3)]"
|
||||
: "hover:shadow-lg"
|
||||
)}
|
||||
onClick={() => setSelectedComposition(composition.id)}
|
||||
>
|
||||
<h4 className="font-medium mb-2">{composition.name}</h4>
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400 mb-2">
|
||||
{composition.description}
|
||||
</p>
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400">
|
||||
<strong>Use for:</strong> {composition.usage}
|
||||
</p>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* Live Preview */}
|
||||
{currentComposition && (
|
||||
<Card className="p-6 max-w-none">
|
||||
<h3 className="text-lg font-semibold mb-4">
|
||||
{currentComposition.name} - Live Preview
|
||||
</h3>
|
||||
<LivePreview minHeight="400px">
|
||||
<div className="w-full max-w-6xl">
|
||||
<currentComposition.component />
|
||||
</div>
|
||||
</LivePreview>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{/* Generated Code */}
|
||||
{currentComposition && (
|
||||
<Card className="p-6 max-w-none">
|
||||
<h3 className="text-lg font-semibold mb-4">Implementation Code</h3>
|
||||
<CodeDisplay
|
||||
code={generateCode(selectedComposition)}
|
||||
|
||||
showLineNumbers
|
||||
/>
|
||||
</Card>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,94 @@
|
||||
import { Card } from '@/features/ui/primitives/card';
|
||||
import { CodeDisplay } from '../shared/CodeDisplay';
|
||||
|
||||
export const PagesExample = () => {
|
||||
const generateCode = () => {
|
||||
return `/**
|
||||
* 🤖 AI CONTEXT: Complete Page Examples
|
||||
*
|
||||
* PURPOSE: Full page layouts using Archon's design system
|
||||
* WHEN TO USE: Reference for building new pages with consistent styling
|
||||
* WHEN NOT TO USE: Component-level implementation
|
||||
*
|
||||
* PAGE TYPES:
|
||||
* - Landing pages with hero sections
|
||||
* - Dashboard pages with data visualization
|
||||
* - Settings pages with form layouts
|
||||
* - Profile pages with user information
|
||||
* - Error pages with helpful messaging
|
||||
*
|
||||
* COMPOSITION PRINCIPLES:
|
||||
* - Consistent spacing and typography
|
||||
* - Proper use of glassmorphism effects
|
||||
* - Responsive grid layouts
|
||||
* - Accessible navigation patterns
|
||||
*/
|
||||
|
||||
// Example: Landing Page
|
||||
export const LandingPage = () => {
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-br from-gray-900 to-gray-800">
|
||||
{/* Hero Section */}
|
||||
<section className="py-20 text-center">
|
||||
<h1 className="text-4xl md:text-6xl font-bold mb-6">
|
||||
Welcome to Archon
|
||||
</h1>
|
||||
<p className="text-xl text-gray-400 mb-8 max-w-2xl mx-auto">
|
||||
Build beautiful applications with our glassmorphism design system
|
||||
</p>
|
||||
<Button size="lg" glowColor="purple">
|
||||
Get Started
|
||||
</Button>
|
||||
</section>
|
||||
|
||||
{/* Features Section */}
|
||||
<section className="py-20">
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-8 max-w-6xl mx-auto">
|
||||
{features.map((feature) => (
|
||||
<Card key={feature.id} glowColor="cyan" className="p-8 text-center">
|
||||
<feature.icon className="w-12 h-12 mx-auto mb-4" />
|
||||
<h3 className="text-xl font-bold mb-4">{feature.title}</h3>
|
||||
<p className="text-gray-400">{feature.description}</p>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
};`;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold mb-4">Page Examples</h2>
|
||||
<p className="text-gray-600 dark:text-gray-400 mb-6">
|
||||
Complete page layouts demonstrating how to compose components into full user interfaces.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<Card className="p-6 max-w-none">
|
||||
<h3 className="text-lg font-semibold mb-4">Coming Soon</h3>
|
||||
<p className="text-gray-600 dark:text-gray-400 mb-4">
|
||||
This section will include complete page examples such as:
|
||||
</p>
|
||||
<ul className="list-disc list-inside text-sm text-gray-600 dark:text-gray-400 space-y-1 mb-6">
|
||||
<li>Landing pages with hero sections and feature grids</li>
|
||||
<li>Dashboard pages with navigation and data visualization</li>
|
||||
<li>Settings pages with form layouts and preferences</li>
|
||||
<li>Profile pages with user information and actions</li>
|
||||
<li>Error pages with helpful messaging and navigation</li>
|
||||
</ul>
|
||||
</Card>
|
||||
|
||||
<Card className="p-6 max-w-none">
|
||||
<h3 className="text-lg font-semibold mb-4">Example Code Structure</h3>
|
||||
<CodeDisplay
|
||||
code={generateCode()}
|
||||
|
||||
showLineNumbers
|
||||
/>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,137 @@
|
||||
import { Card } from '@/features/ui/primitives/card';
|
||||
import { CodeDisplay } from '../shared/CodeDisplay';
|
||||
|
||||
export const WorkflowsExample = () => {
|
||||
const generateCode = () => {
|
||||
return `/**
|
||||
* 🤖 AI CONTEXT: User Workflow Examples
|
||||
*
|
||||
* PURPOSE: Multi-step user flows using Archon components
|
||||
* WHEN TO USE: Complex user journeys requiring multiple screens
|
||||
* WHEN NOT TO USE: Simple single-page interactions
|
||||
*
|
||||
* WORKFLOW TYPES:
|
||||
* - Onboarding flows with progressive disclosure
|
||||
* - Multi-step forms with validation
|
||||
* - Wizard-style configuration processes
|
||||
* - Data import/export workflows
|
||||
* - User authentication flows
|
||||
*
|
||||
* FLOW PRINCIPLES:
|
||||
* - Clear progress indication
|
||||
* - Consistent navigation patterns
|
||||
* - Proper error handling and recovery
|
||||
* - Accessible keyboard navigation
|
||||
* - Mobile-responsive design
|
||||
*/
|
||||
|
||||
import { useState } from 'react';
|
||||
import { Card } from '@/features/ui/primitives/card';
|
||||
import { Button } from '@/features/ui/primitives/button';
|
||||
|
||||
// Example: Multi-step Onboarding
|
||||
export const OnboardingWorkflow = () => {
|
||||
const [currentStep, setCurrentStep] = useState(0);
|
||||
const [formData, setFormData] = useState({});
|
||||
|
||||
const steps = [
|
||||
{ title: "Welcome", component: WelcomeStep },
|
||||
{ title: "Profile", component: ProfileStep },
|
||||
{ title: "Preferences", component: PreferencesStep },
|
||||
{ title: "Complete", component: CompleteStep }
|
||||
];
|
||||
|
||||
const nextStep = () => setCurrentStep(prev => Math.min(prev + 1, steps.length - 1));
|
||||
const prevStep = () => setCurrentStep(prev => Math.max(prev - 1, 0));
|
||||
|
||||
return (
|
||||
<div className="max-w-2xl mx-auto">
|
||||
{/* Progress Indicator */}
|
||||
<div className="mb-8">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
{steps.map((step, index) => (
|
||||
<div
|
||||
key={step.title}
|
||||
className={cn(
|
||||
"flex items-center justify-center w-8 h-8 rounded-full text-sm font-medium",
|
||||
index <= currentStep
|
||||
? "bg-cyan-500 text-white"
|
||||
: "bg-gray-200 dark:bg-gray-700 text-gray-600 dark:text-gray-400"
|
||||
)}
|
||||
>
|
||||
{index + 1}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="w-full bg-gray-200 dark:bg-gray-700 rounded-full h-2">
|
||||
<div
|
||||
className="bg-cyan-500 h-2 rounded-full transition-all"
|
||||
style={{ width: \`\${((currentStep + 1) / steps.length) * 100}%\` }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Step Content */}
|
||||
<Card glowColor="purple" className="p-8 mb-6">
|
||||
<h2 className="text-2xl font-bold mb-6">{steps[currentStep].title}</h2>
|
||||
<steps[currentStep].component
|
||||
formData={formData}
|
||||
setFormData={setFormData}
|
||||
/>
|
||||
</Card>
|
||||
|
||||
{/* Navigation */}
|
||||
<div className="flex justify-between">
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={prevStep}
|
||||
disabled={currentStep === 0}
|
||||
>
|
||||
Previous
|
||||
</Button>
|
||||
<Button
|
||||
onClick={nextStep}
|
||||
disabled={currentStep === steps.length - 1}
|
||||
>
|
||||
{currentStep === steps.length - 1 ? 'Complete' : 'Next'}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};`;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold mb-4">Workflow Examples</h2>
|
||||
<p className="text-gray-600 dark:text-gray-400 mb-6">
|
||||
Multi-step user flows and complex interactions using Archon's design system.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<Card className="p-6 max-w-none">
|
||||
<h3 className="text-lg font-semibold mb-4">Coming Soon</h3>
|
||||
<p className="text-gray-600 dark:text-gray-400 mb-4">
|
||||
This section will include interactive workflow examples such as:
|
||||
</p>
|
||||
<ul className="list-disc list-inside text-sm text-gray-600 dark:text-gray-400 space-y-1 mb-6">
|
||||
<li>Onboarding flows with step-by-step guidance</li>
|
||||
<li>Multi-step form wizards with validation</li>
|
||||
<li>Data import/export workflows with progress tracking</li>
|
||||
<li>User authentication flows with error handling</li>
|
||||
<li>Configuration wizards with branching logic</li>
|
||||
</ul>
|
||||
</Card>
|
||||
|
||||
<Card className="p-6 max-w-none">
|
||||
<h3 className="text-lg font-semibold mb-4">Example Workflow Pattern</h3>
|
||||
<CodeDisplay
|
||||
code={generateCode()}
|
||||
|
||||
showLineNumbers
|
||||
/>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,258 @@
|
||||
import { Card } from '@/features/ui/primitives/card';
|
||||
import { CodeDisplay } from '../shared/CodeDisplay';
|
||||
|
||||
// Colored glass cards (tinted glass, no glow)
|
||||
const GLASS_TINT_COLORS = [
|
||||
{
|
||||
name: 'Clear Glass',
|
||||
glassTint: 'none' as const,
|
||||
transparency: 'light' as const,
|
||||
description: 'Pure transparent glass',
|
||||
usage: 'Default containers, neutral elements'
|
||||
},
|
||||
{
|
||||
name: 'Purple Glass',
|
||||
glassTint: 'purple' as const,
|
||||
transparency: 'medium' as const,
|
||||
description: 'Purple-tinted transparent glass',
|
||||
usage: 'Primary content areas'
|
||||
},
|
||||
{
|
||||
name: 'Blue Glass',
|
||||
glassTint: 'blue' as const,
|
||||
transparency: 'medium' as const,
|
||||
description: 'Blue-tinted transparent glass',
|
||||
usage: 'Informational panels'
|
||||
},
|
||||
{
|
||||
name: 'Cyan Glass',
|
||||
glassTint: 'cyan' as const,
|
||||
transparency: 'medium' as const,
|
||||
description: 'Cyan-tinted transparent glass',
|
||||
usage: 'Active/selected states'
|
||||
},
|
||||
{
|
||||
name: 'Green Glass',
|
||||
glassTint: 'green' as const,
|
||||
transparency: 'medium' as const,
|
||||
description: 'Green-tinted transparent glass',
|
||||
usage: 'Success messages'
|
||||
},
|
||||
{
|
||||
name: 'Orange Glass',
|
||||
glassTint: 'orange' as const,
|
||||
transparency: 'medium' as const,
|
||||
description: 'Orange-tinted transparent glass',
|
||||
usage: 'Warning notifications'
|
||||
}
|
||||
];
|
||||
|
||||
// Neon glow cards (backlight effect)
|
||||
const NEON_GLOW_COLORS = [
|
||||
{
|
||||
name: 'Purple Glow',
|
||||
glowColor: 'purple' as const,
|
||||
transparency: 'light' as const,
|
||||
description: 'Glass with purple neon backlight',
|
||||
usage: 'Featured content, primary CTAs'
|
||||
},
|
||||
{
|
||||
name: 'Blue Glow',
|
||||
glowColor: 'blue' as const,
|
||||
transparency: 'light' as const,
|
||||
description: 'Glass with blue neon backlight',
|
||||
usage: 'Information highlights'
|
||||
},
|
||||
{
|
||||
name: 'Cyan Glow',
|
||||
glowColor: 'cyan' as const,
|
||||
transparency: 'light' as const,
|
||||
description: 'Glass with cyan neon backlight',
|
||||
usage: 'Tron-style emphasis'
|
||||
},
|
||||
{
|
||||
name: 'Green Glow',
|
||||
glowColor: 'green' as const,
|
||||
transparency: 'light' as const,
|
||||
description: 'Glass with green neon backlight',
|
||||
usage: 'Success states'
|
||||
},
|
||||
{
|
||||
name: 'Red Glow',
|
||||
glowColor: 'red' as const,
|
||||
transparency: 'light' as const,
|
||||
description: 'Glass with red neon backlight',
|
||||
usage: 'Errors and critical alerts'
|
||||
},
|
||||
{
|
||||
name: 'Pink Glow',
|
||||
glowColor: 'pink' as const,
|
||||
transparency: 'light' as const,
|
||||
description: 'Glass with pink neon backlight',
|
||||
usage: 'Special features'
|
||||
}
|
||||
];
|
||||
|
||||
// Transparency levels
|
||||
const TRANSPARENCY_LEVELS = [
|
||||
{ level: 'clear' as const, name: 'Clear', description: 'Almost invisible' },
|
||||
{ level: 'light' as const, name: 'Light', description: 'Subtle frosting' },
|
||||
{ level: 'medium' as const, name: 'Medium', description: 'Standard glass' },
|
||||
{ level: 'frosted' as const, name: 'Frosted', description: 'Heavy frosting' },
|
||||
{ level: 'solid' as const, name: 'Solid', description: 'Nearly opaque' }
|
||||
];
|
||||
|
||||
const generateCode = () => {
|
||||
return `import { Card } from '@/features/ui/primitives/card';
|
||||
|
||||
/**
|
||||
* 🤖 AI CONTEXT: Glass Card System
|
||||
*
|
||||
* Three independent properties for maximum flexibility:
|
||||
*
|
||||
* 1. TRANSPARENCY - How much you can see through the glass
|
||||
* - clear: Almost invisible (0.01 opacity)
|
||||
* - light: Subtle frosting (0.05 opacity)
|
||||
* - medium: Standard glass (0.08 opacity)
|
||||
* - frosted: Heavy frosting (0.12 opacity)
|
||||
* - solid: Nearly opaque (0.20 opacity)
|
||||
*
|
||||
* 2. GLASS TINT - Adds color to the glass itself
|
||||
* - none: Pure transparent
|
||||
* - purple/blue/cyan/green/orange/pink/red: Colored glass
|
||||
*
|
||||
* 3. GLOW COLOR - Neon backlight effect
|
||||
* - none: No glow
|
||||
* - purple/blue/cyan/green/orange/pink/red: Neon glow
|
||||
*
|
||||
* USAGE PATTERNS:
|
||||
* - Default containers: transparency="light" glassTint="none"
|
||||
* - Colored sections: transparency="medium" glassTint="blue"
|
||||
* - Featured content: transparency="light" glowColor="purple"
|
||||
* - Layered UI: Use different transparency levels for depth
|
||||
*/
|
||||
|
||||
// Basic transparent glass
|
||||
<Card transparency="light">
|
||||
Pure glass effect
|
||||
</Card>
|
||||
|
||||
// Colored glass (tinted)
|
||||
<Card transparency="medium" glassTint="blue">
|
||||
Blue-tinted glass
|
||||
</Card>
|
||||
|
||||
// Glass with neon glow
|
||||
<Card transparency="light" glowColor="purple">
|
||||
Glass with purple backlight
|
||||
</Card>
|
||||
|
||||
// Combined: Colored glass with glow
|
||||
<Card transparency="medium" glassTint="cyan" glowColor="cyan">
|
||||
Cyan glass with matching glow
|
||||
</Card>
|
||||
|
||||
// Heavy frosted glass
|
||||
<Card transparency="frosted" glassTint="none">
|
||||
Heavily frosted glass
|
||||
</Card>`;
|
||||
};
|
||||
|
||||
export const ColorsFoundation = () => {
|
||||
return (
|
||||
<div className="space-y-12">
|
||||
{/* Header */}
|
||||
<div className="text-center">
|
||||
<h2 className="text-3xl font-bold text-gray-900 dark:text-white mb-4">Glass Card System</h2>
|
||||
<p className="text-gray-600 dark:text-gray-400 text-lg max-w-3xl mx-auto">
|
||||
True glassmorphism with independent control over transparency, tint, and neon glow effects.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Transparency Levels */}
|
||||
<div className="space-y-6">
|
||||
<h3 className="text-xl font-semibold text-gray-900 dark:text-white text-center">Glass Transparency Levels</h3>
|
||||
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-5 gap-4">
|
||||
{TRANSPARENCY_LEVELS.map((item) => (
|
||||
<Card
|
||||
key={item.level}
|
||||
transparency={item.level}
|
||||
className="hover:scale-105 transition-transform duration-300"
|
||||
>
|
||||
<h4 className="text-lg font-semibold text-gray-900 dark:text-white mb-1">{item.name}</h4>
|
||||
<p className="text-gray-600 dark:text-gray-400 text-xs">{item.description}</p>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Colored Glass (Tints) */}
|
||||
<div className="space-y-6">
|
||||
<h3 className="text-xl font-semibold text-gray-900 dark:text-white text-center">Colored Glass Tints</h3>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{GLASS_TINT_COLORS.map((color) => (
|
||||
<Card
|
||||
key={color.name}
|
||||
glassTint={color.glassTint}
|
||||
transparency={color.transparency}
|
||||
className="hover:scale-105 transition-transform duration-300"
|
||||
>
|
||||
<h4 className="text-lg font-semibold text-gray-900 dark:text-white mb-2">{color.name}</h4>
|
||||
<p className="text-gray-700 dark:text-gray-300 text-sm mb-2">{color.description}</p>
|
||||
<p className="text-gray-600 dark:text-gray-400 text-xs">{color.usage}</p>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Neon Glow Effects */}
|
||||
<div className="space-y-6">
|
||||
<h3 className="text-xl font-semibold text-gray-900 dark:text-white text-center">Neon Backlight Glow</h3>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
{NEON_GLOW_COLORS.map((color) => (
|
||||
<Card
|
||||
key={color.name}
|
||||
glowColor={color.glowColor}
|
||||
transparency={color.transparency}
|
||||
className="hover:scale-105 transition-transform duration-300"
|
||||
>
|
||||
<h4 className="text-lg font-semibold text-gray-900 dark:text-white mb-2">{color.name}</h4>
|
||||
<p className="text-gray-700 dark:text-gray-300 text-sm mb-2">{color.description}</p>
|
||||
<p className="text-gray-600 dark:text-gray-400 text-xs">{color.usage}</p>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Combined Examples */}
|
||||
<div className="space-y-6">
|
||||
<h3 className="text-xl font-semibold text-gray-900 dark:text-white text-center">Combined Effects</h3>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
<Card transparency="frosted" glassTint="purple" glowColor="purple">
|
||||
<h4 className="text-lg font-semibold text-gray-900 dark:text-white mb-2">Frosted Purple</h4>
|
||||
<p className="text-gray-700 dark:text-gray-300 text-sm">Heavy frosting with purple tint and glow</p>
|
||||
</Card>
|
||||
<Card transparency="clear" glassTint="none" glowColor="cyan">
|
||||
<h4 className="text-lg font-semibold text-gray-900 dark:text-white mb-2">Clear with Cyan Edge</h4>
|
||||
<p className="text-gray-700 dark:text-gray-300 text-sm">Nearly invisible with cyan neon edge</p>
|
||||
</Card>
|
||||
<Card transparency="medium" glassTint="blue" glowColor="none">
|
||||
<h4 className="text-lg font-semibold text-gray-900 dark:text-white mb-2">Blue Tinted Glass</h4>
|
||||
<p className="text-gray-700 dark:text-gray-300 text-sm">Medium opacity blue glass, no glow</p>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Usage Code */}
|
||||
<div className="max-w-4xl mx-auto">
|
||||
<Card className="p-6" transparency="light">
|
||||
<h3 className="text-xl font-semibold text-white mb-6 text-center">Usage Code</h3>
|
||||
<CodeDisplay
|
||||
code={generateCode()}
|
||||
showLineNumbers
|
||||
/>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,380 @@
|
||||
import { Card } from '@/features/ui/primitives/card';
|
||||
import { CodeDisplay } from '../shared/CodeDisplay';
|
||||
import { cn, glassmorphism } from '@/features/ui/primitives/styles';
|
||||
|
||||
interface Effect {
|
||||
name: string;
|
||||
className: string;
|
||||
description: string;
|
||||
usage: string;
|
||||
}
|
||||
|
||||
const BLUR_EFFECTS: Effect[] = [
|
||||
{
|
||||
name: 'Backdrop Blur Small',
|
||||
className: 'backdrop-blur-sm',
|
||||
description: 'Subtle blur for lightweight overlays',
|
||||
usage: 'Light modals, hover effects, subtle overlays'
|
||||
},
|
||||
{
|
||||
name: 'Backdrop Blur Medium',
|
||||
className: 'backdrop-blur-md',
|
||||
description: 'Standard blur for glassmorphism',
|
||||
usage: 'Cards, panels, primary glassmorphism effects'
|
||||
},
|
||||
{
|
||||
name: 'Backdrop Blur Large',
|
||||
className: 'backdrop-blur-lg',
|
||||
description: 'Strong blur for prominent overlays',
|
||||
usage: 'Modals, sidebars, major overlay elements'
|
||||
},
|
||||
{
|
||||
name: 'Backdrop Blur Extra Large',
|
||||
className: 'backdrop-blur-xl',
|
||||
description: 'Maximum blur for full overlays',
|
||||
usage: 'Background overlays, page transitions'
|
||||
}
|
||||
];
|
||||
|
||||
const SHADOW_EFFECTS: Effect[] = [
|
||||
{
|
||||
name: 'Small Shadow',
|
||||
className: glassmorphism.shadow.sm,
|
||||
description: 'Subtle elevation for small elements',
|
||||
usage: 'Buttons, small cards, minor elevation'
|
||||
},
|
||||
{
|
||||
name: 'Medium Shadow',
|
||||
className: glassmorphism.shadow.md,
|
||||
description: 'Standard elevation for cards',
|
||||
usage: 'Cards, panels, standard components'
|
||||
},
|
||||
{
|
||||
name: 'Large Shadow',
|
||||
className: glassmorphism.shadow.lg,
|
||||
description: 'Strong elevation for prominent elements',
|
||||
usage: 'Modals, floating panels, major elements'
|
||||
},
|
||||
{
|
||||
name: 'Elevated Shadow',
|
||||
className: glassmorphism.shadow.elevated,
|
||||
description: 'Maximum elevation with custom shadow',
|
||||
usage: 'Tooltips, dropdowns, floating elements'
|
||||
}
|
||||
];
|
||||
|
||||
const GLOW_EFFECTS: Effect[] = [
|
||||
{
|
||||
name: 'Purple Glow',
|
||||
className: glassmorphism.shadow.glow.purple,
|
||||
description: 'Primary accent glow effect',
|
||||
usage: 'Primary actions, featured content'
|
||||
},
|
||||
{
|
||||
name: 'Blue Glow',
|
||||
className: glassmorphism.shadow.glow.blue,
|
||||
description: 'Information accent glow',
|
||||
usage: 'Information states, secondary actions'
|
||||
},
|
||||
{
|
||||
name: 'Green Glow',
|
||||
className: glassmorphism.shadow.glow.green,
|
||||
description: 'Success accent glow',
|
||||
usage: 'Success states, positive feedback'
|
||||
},
|
||||
{
|
||||
name: 'Red Glow',
|
||||
className: glassmorphism.shadow.glow.red,
|
||||
description: 'Danger accent glow',
|
||||
usage: 'Error states, destructive actions'
|
||||
},
|
||||
{
|
||||
name: 'Orange Glow',
|
||||
className: glassmorphism.shadow.glow.orange,
|
||||
description: 'Warning accent glow',
|
||||
usage: 'Warning states, caution indicators'
|
||||
},
|
||||
{
|
||||
name: 'Cyan Glow',
|
||||
className: glassmorphism.shadow.glow.cyan,
|
||||
description: 'Active accent glow',
|
||||
usage: 'Active states, focus indicators'
|
||||
}
|
||||
];
|
||||
|
||||
const EDGE_EFFECTS: Effect[] = [
|
||||
{
|
||||
name: 'Top Edge Glow',
|
||||
className: glassmorphism.edgePositions.top,
|
||||
description: 'Glowing border at top edge',
|
||||
usage: 'Header cards, top navigation elements'
|
||||
},
|
||||
{
|
||||
name: 'Left Edge Glow',
|
||||
className: glassmorphism.edgePositions.left,
|
||||
description: 'Glowing border at left edge',
|
||||
usage: 'Sidebar items, left navigation'
|
||||
},
|
||||
{
|
||||
name: 'Right Edge Glow',
|
||||
className: glassmorphism.edgePositions.right,
|
||||
description: 'Glowing border at right edge',
|
||||
usage: 'Action panels, right sidebars'
|
||||
},
|
||||
{
|
||||
name: 'Bottom Edge Glow',
|
||||
className: glassmorphism.edgePositions.bottom,
|
||||
description: 'Glowing border at bottom edge',
|
||||
usage: 'Footer elements, bottom panels'
|
||||
}
|
||||
];
|
||||
|
||||
const INTERACTIVE_EFFECTS: Effect[] = [
|
||||
{
|
||||
name: 'Hover Effect',
|
||||
className: glassmorphism.interactive.hover,
|
||||
description: 'Subtle background change on hover',
|
||||
usage: 'Interactive elements, buttons, clickable items'
|
||||
},
|
||||
{
|
||||
name: 'Active Effect',
|
||||
className: glassmorphism.interactive.active,
|
||||
description: 'Background change when pressed',
|
||||
usage: 'Button active states, pressed elements'
|
||||
},
|
||||
{
|
||||
name: 'Selected Effect',
|
||||
className: glassmorphism.interactive.selected,
|
||||
description: 'Background and text change when selected',
|
||||
usage: 'Selected items, active navigation'
|
||||
},
|
||||
{
|
||||
name: 'Focus Effect',
|
||||
className: glassmorphism.border.focus,
|
||||
description: 'Border and shadow change on focus',
|
||||
usage: 'Form inputs, keyboard navigation'
|
||||
}
|
||||
];
|
||||
|
||||
const generateCode = () => {
|
||||
return `import { glassmorphism } from '@/features/ui/primitives/styles';
|
||||
|
||||
/**
|
||||
* 🤖 AI CONTEXT: Glassmorphism Effects System
|
||||
*
|
||||
* PURPOSE: Consistent visual effects for the Tron-inspired design
|
||||
* WHEN TO USE: Layer effects to create depth and visual hierarchy
|
||||
* WHEN NOT TO USE: Don't over-layer effects (max 3 layers)
|
||||
*
|
||||
* EFFECT HIERARCHY:
|
||||
* 1. Backdrop Blur: Creates the "glass" foundation
|
||||
* 2. Background: Adds subtle color and opacity
|
||||
* 3. Border: Defines edges and focus states
|
||||
* 4. Shadow/Glow: Adds depth and accent colors
|
||||
* 5. Interactive: Responds to user actions
|
||||
*
|
||||
* LAYERING GUIDELINES:
|
||||
* - Always start with backdrop-blur for glassmorphism
|
||||
* - Add background for subtle color tinting
|
||||
* - Use borders for definition and focus
|
||||
* - Apply shadows for elevation
|
||||
* - Add glow effects for accent and attention
|
||||
* - Include interactive states for usability
|
||||
*
|
||||
* PERFORMANCE CONSIDERATIONS:
|
||||
* - Backdrop blur is expensive, use sparingly
|
||||
* - Limit glow effects to 2-3 per view
|
||||
* - Use transform3d for smooth animations
|
||||
* - Prefer opacity changes over color changes
|
||||
*/
|
||||
|
||||
// Basic Glassmorphism Card
|
||||
<div className={cn(
|
||||
"backdrop-blur-md", // Glass effect
|
||||
glassmorphism.background.card, // Subtle background
|
||||
glassmorphism.border.default, // Border definition
|
||||
glassmorphism.shadow.md, // Standard elevation
|
||||
"rounded-lg transition-all duration-300" // Shape and animation
|
||||
)}>
|
||||
Standard glassmorphism card
|
||||
</div>
|
||||
|
||||
// Accented Card with Glow
|
||||
<div className={cn(
|
||||
"backdrop-blur-md",
|
||||
glassmorphism.background.card,
|
||||
glassmorphism.border.cyan,
|
||||
glassmorphism.shadow.glow.purple, // Accent glow
|
||||
glassmorphism.edgePositions.top, // Edge accent
|
||||
"rounded-lg"
|
||||
)}>
|
||||
Accented card with purple glow
|
||||
</div>
|
||||
|
||||
// Interactive Button
|
||||
<button className={cn(
|
||||
"backdrop-blur-sm",
|
||||
glassmorphism.background.subtle,
|
||||
glassmorphism.border.default,
|
||||
glassmorphism.interactive.hover, // Hover state
|
||||
glassmorphism.interactive.active, // Active state
|
||||
glassmorphism.border.focus, // Focus state
|
||||
"px-4 py-2 rounded-md transition-all"
|
||||
)}>
|
||||
Interactive glassmorphism button
|
||||
</button>
|
||||
|
||||
// Floating Panel
|
||||
<div className={cn(
|
||||
"backdrop-blur-lg", // Strong blur
|
||||
glassmorphism.background.strong, // High opacity
|
||||
glassmorphism.border.default,
|
||||
glassmorphism.shadow.elevated, // Maximum elevation
|
||||
"rounded-xl p-6"
|
||||
)}>
|
||||
Floating panel with strong effects
|
||||
</div>`;
|
||||
};
|
||||
|
||||
const EffectDemo = ({ effect, showBackground = false }: { effect: Effect; showBackground?: boolean }) => (
|
||||
<div className="space-y-2">
|
||||
<div className="relative h-24 rounded-lg overflow-hidden">
|
||||
{showBackground && (
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-purple-500 via-cyan-500 to-blue-500 opacity-30" />
|
||||
)}
|
||||
<div
|
||||
className={cn(
|
||||
'absolute inset-4 rounded-lg flex items-center justify-center text-sm font-medium',
|
||||
glassmorphism.background.card,
|
||||
glassmorphism.border.default,
|
||||
effect.className
|
||||
)}
|
||||
>
|
||||
{effect.name}
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-xs">
|
||||
<p className="font-medium text-gray-900 dark:text-gray-100">{effect.description}</p>
|
||||
<p className="text-gray-600 dark:text-gray-400 mt-1">{effect.usage}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
export const EffectsFoundation = () => {
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold mb-4">Effects System</h2>
|
||||
<p className="text-gray-600 dark:text-gray-400 mb-6">
|
||||
Glassmorphism effects including blur, shadows, glows, and interactive states for the Tron-inspired design.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Blur Effects */}
|
||||
<Card className="p-6 max-w-none">
|
||||
<h3 className="text-lg font-semibold mb-4">Backdrop Blur Effects</h3>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
{BLUR_EFFECTS.map((effect) => (
|
||||
<EffectDemo key={effect.name} effect={effect} showBackground />
|
||||
))}
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* Shadow Effects */}
|
||||
<Card className="p-6 max-w-none">
|
||||
<h3 className="text-lg font-semibold mb-4">Shadow Effects</h3>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
{SHADOW_EFFECTS.map((effect) => (
|
||||
<EffectDemo key={effect.name} effect={effect} />
|
||||
))}
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* Glow Effects */}
|
||||
<Card className="p-6 max-w-none">
|
||||
<h3 className="text-lg font-semibold mb-4">Glow Effects</h3>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
{GLOW_EFFECTS.map((effect) => (
|
||||
<EffectDemo key={effect.name} effect={effect} />
|
||||
))}
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* Edge Effects */}
|
||||
<Card className="p-6 max-w-none">
|
||||
<h3 className="text-lg font-semibold mb-4">Edge Glow Effects</h3>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
{EDGE_EFFECTS.map((effect) => (
|
||||
<EffectDemo key={effect.name} effect={effect} />
|
||||
))}
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* Interactive Effects */}
|
||||
<Card className="p-6 max-w-none">
|
||||
<h3 className="text-lg font-semibold mb-4">Interactive Effects</h3>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
{INTERACTIVE_EFFECTS.map((effect) => (
|
||||
<div key={effect.name} className="space-y-2">
|
||||
<div
|
||||
className={cn(
|
||||
'h-24 rounded-lg flex items-center justify-center text-sm font-medium cursor-pointer',
|
||||
glassmorphism.background.card,
|
||||
glassmorphism.border.default,
|
||||
glassmorphism.shadow.md,
|
||||
effect.className,
|
||||
'transition-all duration-200'
|
||||
)}
|
||||
>
|
||||
{effect.name}
|
||||
</div>
|
||||
<div className="text-xs">
|
||||
<p className="font-medium text-gray-900 dark:text-gray-100">{effect.description}</p>
|
||||
<p className="text-gray-600 dark:text-gray-400 mt-1">{effect.usage}</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* Layered Example */}
|
||||
<Card className="p-6 max-w-none">
|
||||
<h3 className="text-lg font-semibold mb-4">Layered Effects Example</h3>
|
||||
<div className="relative h-48 rounded-lg overflow-hidden">
|
||||
{/* Background gradient */}
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-purple-500 via-cyan-500 to-blue-500 opacity-30" />
|
||||
|
||||
{/* Multiple layered elements */}
|
||||
<div className={cn(
|
||||
'absolute top-8 left-8 right-8 h-32 rounded-lg',
|
||||
'backdrop-blur-md',
|
||||
glassmorphism.background.card,
|
||||
glassmorphism.border.cyan,
|
||||
glassmorphism.shadow.glow.purple,
|
||||
glassmorphism.edgePositions.top,
|
||||
'flex items-center justify-center font-medium'
|
||||
)}>
|
||||
Complete Glassmorphism Effect
|
||||
<div className="absolute top-2 right-2 text-xs text-gray-500 dark:text-gray-400">
|
||||
Blur + Background + Border + Glow + Edge
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400 mt-4">
|
||||
This example combines multiple effects: backdrop blur, glassmorphism background,
|
||||
colored border, purple glow shadow, and top edge glow for a complete Tron-inspired effect.
|
||||
</p>
|
||||
</Card>
|
||||
|
||||
{/* Usage Code */}
|
||||
<Card className="p-6 max-w-none">
|
||||
<h3 className="text-lg font-semibold mb-4">Usage Code</h3>
|
||||
<CodeDisplay
|
||||
code={generateCode()}
|
||||
|
||||
showLineNumbers
|
||||
/>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,322 @@
|
||||
import { Card } from '@/features/ui/primitives/card';
|
||||
import { CodeDisplay } from '../shared/CodeDisplay';
|
||||
import { cn } from '@/features/ui/primitives/styles';
|
||||
|
||||
interface SpacingScale {
|
||||
name: string;
|
||||
value: string;
|
||||
pixels: string;
|
||||
usage: string;
|
||||
tailwindClass: string;
|
||||
}
|
||||
|
||||
const SPACING_SCALE: SpacingScale[] = [
|
||||
{
|
||||
name: 'xs',
|
||||
value: '0.125rem',
|
||||
pixels: '2px',
|
||||
usage: 'Minimal spacing, fine adjustments',
|
||||
tailwindClass: '0.5'
|
||||
},
|
||||
{
|
||||
name: 'sm',
|
||||
value: '0.25rem',
|
||||
pixels: '4px',
|
||||
usage: 'Small gaps, tight layouts',
|
||||
tailwindClass: '1'
|
||||
},
|
||||
{
|
||||
name: 'md',
|
||||
value: '0.5rem',
|
||||
pixels: '8px',
|
||||
usage: 'Default small spacing',
|
||||
tailwindClass: '2'
|
||||
},
|
||||
{
|
||||
name: 'lg',
|
||||
value: '0.75rem',
|
||||
pixels: '12px',
|
||||
usage: 'Medium spacing between elements',
|
||||
tailwindClass: '3'
|
||||
},
|
||||
{
|
||||
name: 'xl',
|
||||
value: '1rem',
|
||||
pixels: '16px',
|
||||
usage: 'Standard spacing unit',
|
||||
tailwindClass: '4'
|
||||
},
|
||||
{
|
||||
name: '2xl',
|
||||
value: '1.25rem',
|
||||
pixels: '20px',
|
||||
usage: 'Comfortable spacing',
|
||||
tailwindClass: '5'
|
||||
},
|
||||
{
|
||||
name: '3xl',
|
||||
value: '1.5rem',
|
||||
pixels: '24px',
|
||||
usage: 'Large spacing between sections',
|
||||
tailwindClass: '6'
|
||||
},
|
||||
{
|
||||
name: '4xl',
|
||||
value: '2rem',
|
||||
pixels: '32px',
|
||||
usage: 'Extra large spacing',
|
||||
tailwindClass: '8'
|
||||
},
|
||||
{
|
||||
name: '5xl',
|
||||
value: '2.5rem',
|
||||
pixels: '40px',
|
||||
usage: 'Section dividers',
|
||||
tailwindClass: '10'
|
||||
},
|
||||
{
|
||||
name: '6xl',
|
||||
value: '3rem',
|
||||
pixels: '48px',
|
||||
usage: 'Major section spacing',
|
||||
tailwindClass: '12'
|
||||
}
|
||||
];
|
||||
|
||||
const LAYOUT_SPACING = [
|
||||
{
|
||||
name: 'Component Padding',
|
||||
small: 'p-4 (1rem)',
|
||||
medium: 'p-6 (1.5rem)',
|
||||
large: 'p-8 (2rem)',
|
||||
usage: 'Internal spacing within cards and containers'
|
||||
},
|
||||
{
|
||||
name: 'Element Gaps',
|
||||
small: 'gap-2 (0.5rem)',
|
||||
medium: 'gap-4 (1rem)',
|
||||
large: 'gap-6 (1.5rem)',
|
||||
usage: 'Spacing between elements in flex/grid layouts'
|
||||
},
|
||||
{
|
||||
name: 'Section Margins',
|
||||
small: 'mb-4 (1rem)',
|
||||
medium: 'mb-6 (1.5rem)',
|
||||
large: 'mb-8 (2rem)',
|
||||
usage: 'Vertical spacing between page sections'
|
||||
},
|
||||
{
|
||||
name: 'Content Margins',
|
||||
small: 'mx-4 (1rem)',
|
||||
medium: 'mx-6 (1.5rem)',
|
||||
large: 'mx-8 (2rem)',
|
||||
usage: 'Horizontal margins for content containers'
|
||||
}
|
||||
];
|
||||
|
||||
const generateCode = () => {
|
||||
return `/**
|
||||
* 🤖 AI CONTEXT: Spacing System
|
||||
*
|
||||
* PURPOSE: Consistent spacing throughout the application
|
||||
* WHEN TO USE: All layouts should use the standardized spacing scale
|
||||
* WHEN NOT TO USE: Never hardcode spacing values, always use Tailwind classes
|
||||
*
|
||||
* SPACING SCALE:
|
||||
* - 0.5 (2px): Minimal adjustments, fine-tuning
|
||||
* - 1 (4px): Small gaps in tight layouts
|
||||
* - 2 (8px): Default small spacing
|
||||
* - 3 (12px): Medium element spacing
|
||||
* - 4 (16px): Standard spacing unit (base)
|
||||
* - 5 (20px): Comfortable spacing
|
||||
* - 6 (24px): Large spacing between sections
|
||||
* - 8 (32px): Extra large spacing
|
||||
* - 10 (40px): Section dividers
|
||||
* - 12 (48px): Major section spacing
|
||||
*
|
||||
* LAYOUT PATTERNS:
|
||||
* - Card padding: p-4 (small), p-6 (medium), p-8 (large)
|
||||
* - Element gaps: gap-2 (tight), gap-4 (standard), gap-6 (loose)
|
||||
* - Section margins: mb-4 (small), mb-6 (medium), mb-8 (large)
|
||||
* - Page margins: mx-4 (mobile), mx-6 (tablet), mx-8 (desktop)
|
||||
*
|
||||
* RESPONSIVE SPACING:
|
||||
* - Use responsive variants: p-4 md:p-6 lg:p-8
|
||||
* - Scale up spacing on larger screens for better proportions
|
||||
* - Maintain readability and visual hierarchy across devices
|
||||
*/
|
||||
|
||||
// Padding Examples
|
||||
<div className="p-4">Small padding (16px)</div>
|
||||
<div className="p-6">Medium padding (24px)</div>
|
||||
<div className="p-8">Large padding (32px)</div>
|
||||
|
||||
// Margin Examples
|
||||
<div className="mb-4">Small bottom margin (16px)</div>
|
||||
<div className="mb-6">Medium bottom margin (24px)</div>
|
||||
<div className="mb-8">Large bottom margin (32px)</div>
|
||||
|
||||
// Gap Examples (Flexbox/Grid)
|
||||
<div className="flex gap-2">Tight gap (8px)</div>
|
||||
<div className="flex gap-4">Standard gap (16px)</div>
|
||||
<div className="flex gap-6">Loose gap (24px)</div>
|
||||
|
||||
// Responsive Spacing
|
||||
<div className="p-4 md:p-6 lg:p-8">Responsive padding</div>
|
||||
<div className="space-y-4 md:space-y-6">Responsive vertical spacing</div>`;
|
||||
};
|
||||
|
||||
export const SpacingFoundation = () => {
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold mb-4">Spacing System</h2>
|
||||
<p className="text-gray-600 dark:text-gray-400 mb-6">
|
||||
Consistent spacing scale for layouts, components, and content organization.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Spacing Scale */}
|
||||
<Card className="p-6 max-w-none">
|
||||
<h3 className="text-lg font-semibold mb-4">Spacing Scale</h3>
|
||||
<div className="space-y-4">
|
||||
{SPACING_SCALE.map((spacing) => (
|
||||
<div key={spacing.name} className="flex items-center justify-between border-b border-gray-200 dark:border-gray-700 pb-4 last:border-b-0">
|
||||
<div className="flex items-center space-x-6">
|
||||
<div className="w-20 text-sm font-medium">
|
||||
{spacing.name}
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<div
|
||||
className="bg-cyan-500"
|
||||
style={{
|
||||
width: spacing.pixels,
|
||||
height: '20px'
|
||||
}}
|
||||
/>
|
||||
<span className="text-xs text-gray-500 dark:text-gray-400 w-12">
|
||||
{spacing.pixels}
|
||||
</span>
|
||||
</div>
|
||||
<div className="text-sm text-gray-600 dark:text-gray-400 min-w-[100px]">
|
||||
{spacing.value}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center space-x-4">
|
||||
<div className="text-xs font-mono bg-gray-100 dark:bg-gray-800 px-2 py-1 rounded">
|
||||
{spacing.tailwindClass}
|
||||
</div>
|
||||
<div className="text-xs text-gray-500 dark:text-gray-400 max-w-[200px]">
|
||||
{spacing.usage}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* Layout Spacing Patterns */}
|
||||
<Card className="p-6 max-w-none">
|
||||
<h3 className="text-lg font-semibold mb-4">Layout Patterns</h3>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
{LAYOUT_SPACING.map((pattern) => (
|
||||
<div key={pattern.name} className="space-y-3">
|
||||
<h4 className="font-medium text-gray-900 dark:text-gray-100">
|
||||
{pattern.name}
|
||||
</h4>
|
||||
<div className="space-y-2 text-sm">
|
||||
<div className="flex justify-between">
|
||||
<span className="text-gray-600 dark:text-gray-400">Small:</span>
|
||||
<code className="bg-gray-100 dark:bg-gray-800 px-2 py-1 rounded text-xs">
|
||||
{pattern.small}
|
||||
</code>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-gray-600 dark:text-gray-400">Medium:</span>
|
||||
<code className="bg-gray-100 dark:bg-gray-800 px-2 py-1 rounded text-xs">
|
||||
{pattern.medium}
|
||||
</code>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-gray-600 dark:text-gray-400">Large:</span>
|
||||
<code className="bg-gray-100 dark:bg-gray-800 px-2 py-1 rounded text-xs">
|
||||
{pattern.large}
|
||||
</code>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400">
|
||||
{pattern.usage}
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* Visual Examples */}
|
||||
<Card className="p-6 max-w-none">
|
||||
<h3 className="text-lg font-semibold mb-4">Visual Examples</h3>
|
||||
|
||||
{/* Padding Examples */}
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<h4 className="text-sm font-medium mb-3 text-gray-700 dark:text-gray-300">Padding Examples</h4>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
{['p-4', 'p-6', 'p-8'].map((padding) => (
|
||||
<div key={padding} className="space-y-2">
|
||||
<div className="text-xs font-mono bg-gray-100 dark:bg-gray-800 px-2 py-1 rounded inline-block">
|
||||
{padding}
|
||||
</div>
|
||||
<div className="border-2 border-dashed border-gray-300 dark:border-gray-600">
|
||||
<div className={cn(padding, 'bg-cyan-100 dark:bg-cyan-900/30')}>
|
||||
<div className="bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded text-center text-xs py-2">
|
||||
Content
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Gap Examples */}
|
||||
<div>
|
||||
<h4 className="text-sm font-medium mb-3 text-gray-700 dark:text-gray-300">Gap Examples</h4>
|
||||
<div className="space-y-4">
|
||||
{[
|
||||
{ gap: 'gap-2', label: 'gap-2 (8px)' },
|
||||
{ gap: 'gap-4', label: 'gap-4 (16px)' },
|
||||
{ gap: 'gap-6', label: 'gap-6 (24px)' }
|
||||
].map((example) => (
|
||||
<div key={example.gap} className="space-y-2">
|
||||
<div className="text-xs font-mono bg-gray-100 dark:bg-gray-800 px-2 py-1 rounded inline-block">
|
||||
{example.label}
|
||||
</div>
|
||||
<div className={cn('flex', example.gap)}>
|
||||
{[1, 2, 3].map((i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="w-16 h-12 bg-cyan-100 dark:bg-cyan-900/30 border border-gray-200 dark:border-gray-700 rounded flex items-center justify-center text-xs"
|
||||
>
|
||||
{i}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* Usage Code */}
|
||||
<Card className="p-6 max-w-none">
|
||||
<h3 className="text-lg font-semibold mb-4">Usage Code</h3>
|
||||
<CodeDisplay
|
||||
code={generateCode()}
|
||||
|
||||
showLineNumbers
|
||||
/>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,346 @@
|
||||
import { Card } from '@/features/ui/primitives/card';
|
||||
import { CodeDisplay } from '../shared/CodeDisplay';
|
||||
import { cn } from '@/features/ui/primitives/styles';
|
||||
|
||||
interface TypographyStyle {
|
||||
name: string;
|
||||
className: string;
|
||||
size: string;
|
||||
weight: string;
|
||||
usage: string;
|
||||
example: string;
|
||||
}
|
||||
|
||||
const HEADING_STYLES: TypographyStyle[] = [
|
||||
{
|
||||
name: 'Display Large',
|
||||
className: 'text-4xl md:text-5xl lg:text-6xl font-bold',
|
||||
size: '3.75rem / 4rem / 4.5rem',
|
||||
weight: '700',
|
||||
usage: 'Hero headings, landing page titles',
|
||||
example: 'Hero Title'
|
||||
},
|
||||
{
|
||||
name: 'Display Medium',
|
||||
className: 'text-3xl md:text-4xl font-bold',
|
||||
size: '1.875rem / 2.25rem',
|
||||
weight: '700',
|
||||
usage: 'Page titles, section headers',
|
||||
example: 'Page Title'
|
||||
},
|
||||
{
|
||||
name: 'Heading 1',
|
||||
className: 'text-2xl font-bold',
|
||||
size: '1.5rem',
|
||||
weight: '700',
|
||||
usage: 'Main section titles',
|
||||
example: 'Section Title'
|
||||
},
|
||||
{
|
||||
name: 'Heading 2',
|
||||
className: 'text-xl font-semibold',
|
||||
size: '1.25rem',
|
||||
weight: '600',
|
||||
usage: 'Subsection titles',
|
||||
example: 'Subsection Title'
|
||||
},
|
||||
{
|
||||
name: 'Heading 3',
|
||||
className: 'text-lg font-semibold',
|
||||
size: '1.125rem',
|
||||
weight: '600',
|
||||
usage: 'Card titles, component headers',
|
||||
example: 'Component Header'
|
||||
},
|
||||
{
|
||||
name: 'Heading 4',
|
||||
className: 'text-base font-medium',
|
||||
size: '1rem',
|
||||
weight: '500',
|
||||
usage: 'Small headings, labels',
|
||||
example: 'Label Text'
|
||||
}
|
||||
];
|
||||
|
||||
const BODY_STYLES: TypographyStyle[] = [
|
||||
{
|
||||
name: 'Body Large',
|
||||
className: 'text-lg font-normal',
|
||||
size: '1.125rem',
|
||||
weight: '400',
|
||||
usage: 'Large body text, introductions',
|
||||
example: 'This is large body text used for introductions and important content that needs to stand out.'
|
||||
},
|
||||
{
|
||||
name: 'Body Medium',
|
||||
className: 'text-base font-normal',
|
||||
size: '1rem',
|
||||
weight: '400',
|
||||
usage: 'Standard body text, paragraphs',
|
||||
example: 'This is standard body text used for regular content, paragraphs, and general information.'
|
||||
},
|
||||
{
|
||||
name: 'Body Small',
|
||||
className: 'text-sm font-normal',
|
||||
size: '0.875rem',
|
||||
weight: '400',
|
||||
usage: 'Secondary text, descriptions',
|
||||
example: 'This is small body text used for secondary information and descriptions.'
|
||||
},
|
||||
{
|
||||
name: 'Caption',
|
||||
className: 'text-xs font-normal',
|
||||
size: '0.75rem',
|
||||
weight: '400',
|
||||
usage: 'Captions, metadata, helper text',
|
||||
example: 'This is caption text used for metadata and helper information.'
|
||||
}
|
||||
];
|
||||
|
||||
const UTILITY_STYLES: TypographyStyle[] = [
|
||||
{
|
||||
name: 'Button Text',
|
||||
className: 'text-sm font-medium',
|
||||
size: '0.875rem',
|
||||
weight: '500',
|
||||
usage: 'Button labels, action text',
|
||||
example: 'Button Label'
|
||||
},
|
||||
{
|
||||
name: 'Code',
|
||||
className: 'text-sm font-mono bg-gray-100 dark:bg-gray-800 px-1 py-0.5 rounded',
|
||||
size: '0.875rem',
|
||||
weight: '400',
|
||||
usage: 'Inline code, technical text',
|
||||
example: 'const variable = value;'
|
||||
},
|
||||
{
|
||||
name: 'Link',
|
||||
className: 'text-cyan-600 dark:text-cyan-400 hover:text-cyan-500 dark:hover:text-cyan-300 underline',
|
||||
size: 'inherit',
|
||||
weight: 'inherit',
|
||||
usage: 'Links, interactive text',
|
||||
example: 'This is a link'
|
||||
},
|
||||
{
|
||||
name: 'Muted',
|
||||
className: 'text-gray-500 dark:text-gray-400',
|
||||
size: 'inherit',
|
||||
weight: 'inherit',
|
||||
usage: 'Secondary text, disabled states',
|
||||
example: 'This is muted text'
|
||||
}
|
||||
];
|
||||
|
||||
const COLOR_VARIATIONS = [
|
||||
{
|
||||
name: 'Primary Text',
|
||||
className: 'text-gray-900 dark:text-gray-100',
|
||||
usage: 'Main content, headings'
|
||||
},
|
||||
{
|
||||
name: 'Secondary Text',
|
||||
className: 'text-gray-700 dark:text-gray-300',
|
||||
usage: 'Body text, secondary content'
|
||||
},
|
||||
{
|
||||
name: 'Muted Text',
|
||||
className: 'text-gray-500 dark:text-gray-400',
|
||||
usage: 'Helper text, captions, disabled'
|
||||
},
|
||||
{
|
||||
name: 'Accent Text',
|
||||
className: 'text-cyan-600 dark:text-cyan-400',
|
||||
usage: 'Links, highlights, active states'
|
||||
},
|
||||
{
|
||||
name: 'Success Text',
|
||||
className: 'text-green-600 dark:text-green-400',
|
||||
usage: 'Success messages, positive states'
|
||||
},
|
||||
{
|
||||
name: 'Warning Text',
|
||||
className: 'text-orange-600 dark:text-orange-400',
|
||||
usage: 'Warning messages, caution states'
|
||||
},
|
||||
{
|
||||
name: 'Error Text',
|
||||
className: 'text-red-600 dark:text-red-400',
|
||||
usage: 'Error messages, destructive actions'
|
||||
}
|
||||
];
|
||||
|
||||
const generateCode = () => {
|
||||
return `/**
|
||||
* 🤖 AI CONTEXT: Typography System
|
||||
*
|
||||
* PURPOSE: Consistent text styles across the application
|
||||
* WHEN TO USE: All text content should use these utility classes
|
||||
* WHEN NOT TO USE: Never hardcode font sizes, always use system classes
|
||||
*
|
||||
* HIERARCHY GUIDELINES:
|
||||
* - Display Large: Hero sections, landing pages
|
||||
* - Display Medium: Page titles, main headers
|
||||
* - Heading 1-4: Section titles, descending importance
|
||||
* - Body Large: Important content, introductions
|
||||
* - Body Medium: Standard content, paragraphs
|
||||
* - Body Small: Secondary content, descriptions
|
||||
* - Caption: Metadata, helper text, timestamps
|
||||
*
|
||||
* COLOR SEMANTIC MEANINGS:
|
||||
* - Primary: Main content, most important text
|
||||
* - Secondary: Body content, readable but less prominent
|
||||
* - Muted: Helper text, less important information
|
||||
* - Accent: Interactive elements, links, highlights
|
||||
* - Success/Warning/Error: Status-based messaging
|
||||
*
|
||||
* RESPONSIVE BEHAVIOR:
|
||||
* - Display styles scale down on smaller screens
|
||||
* - Body text maintains consistent size across devices
|
||||
* - Use responsive classes (md:, lg:) for display text
|
||||
*/
|
||||
|
||||
// Heading Examples
|
||||
<h1 className="text-2xl font-bold">Main Section Title</h1>
|
||||
<h2 className="text-xl font-semibold">Subsection Title</h2>
|
||||
<h3 className="text-lg font-semibold">Component Header</h3>
|
||||
|
||||
// Body Text Examples
|
||||
<p className="text-lg font-normal">Large body text for introductions</p>
|
||||
<p className="text-base font-normal">Standard body text for content</p>
|
||||
<p className="text-sm font-normal">Small text for descriptions</p>
|
||||
|
||||
// Utility Examples
|
||||
<button className="text-sm font-medium">Button Label</button>
|
||||
<code className="text-sm font-mono bg-gray-100 dark:bg-gray-800 px-1 py-0.5 rounded">
|
||||
code snippet
|
||||
</code>
|
||||
<a className="text-cyan-600 dark:text-cyan-400 hover:text-cyan-500 underline">
|
||||
Link text
|
||||
</a>
|
||||
|
||||
// Color Variations
|
||||
<span className="text-gray-900 dark:text-gray-100">Primary text</span>
|
||||
<span className="text-gray-500 dark:text-gray-400">Muted text</span>
|
||||
<span className="text-cyan-600 dark:text-cyan-400">Accent text</span>`;
|
||||
};
|
||||
|
||||
export const TypographyFoundation = () => {
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold text-gray-900 dark:text-white mb-4">Typography System</h2>
|
||||
<p className="text-gray-600 dark:text-gray-400 mb-6">
|
||||
Consistent typography scale and styles for clear content hierarchy and optimal readability.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Heading Styles */}
|
||||
<Card className="p-6 max-w-none">
|
||||
<h3 className="text-lg font-semibold text-gray-900 dark:text-white mb-4">Headings</h3>
|
||||
<div className="space-y-6">
|
||||
{HEADING_STYLES.map((style) => (
|
||||
<div key={style.name} className="border-b border-gray-200 dark:border-gray-700 pb-4 last:border-b-0">
|
||||
<div className={cn(style.className, 'mb-2')}>
|
||||
{style.example}
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 text-xs text-gray-600 dark:text-gray-400">
|
||||
<div>
|
||||
<span className="font-medium">Style:</span> {style.name}
|
||||
</div>
|
||||
<div>
|
||||
<span className="font-medium">Size:</span> {style.size}
|
||||
</div>
|
||||
<div>
|
||||
<span className="font-medium">Usage:</span> {style.usage}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* Body Styles */}
|
||||
<Card className="p-6 max-w-none">
|
||||
<h3 className="text-lg font-semibold text-gray-900 dark:text-white mb-4">Body Text</h3>
|
||||
<div className="space-y-6">
|
||||
{BODY_STYLES.map((style) => (
|
||||
<div key={style.name} className="border-b border-gray-200 dark:border-gray-700 pb-4 last:border-b-0">
|
||||
<div className={cn(style.className, 'mb-2')}>
|
||||
{style.example}
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 text-xs text-gray-600 dark:text-gray-400">
|
||||
<div>
|
||||
<span className="font-medium">Style:</span> {style.name}
|
||||
</div>
|
||||
<div>
|
||||
<span className="font-medium">Size:</span> {style.size}
|
||||
</div>
|
||||
<div>
|
||||
<span className="font-medium">Usage:</span> {style.usage}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* Utility Styles */}
|
||||
<Card className="p-6 max-w-none">
|
||||
<h3 className="text-lg font-semibold text-gray-900 dark:text-white mb-4">Utility Styles</h3>
|
||||
<div className="space-y-6">
|
||||
{UTILITY_STYLES.map((style) => (
|
||||
<div key={style.name} className="border-b border-gray-200 dark:border-gray-700 pb-4 last:border-b-0">
|
||||
<div className={cn(style.className, 'mb-2')}>
|
||||
{style.example}
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 text-xs text-gray-600 dark:text-gray-400">
|
||||
<div>
|
||||
<span className="font-medium">Style:</span> {style.name}
|
||||
</div>
|
||||
<div>
|
||||
<span className="font-medium">Weight:</span> {style.weight}
|
||||
</div>
|
||||
<div>
|
||||
<span className="font-medium">Usage:</span> {style.usage}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* Color Variations */}
|
||||
<Card className="p-6 max-w-none">
|
||||
<h3 className="text-lg font-semibold mb-4">Text Colors</h3>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
{COLOR_VARIATIONS.map((color) => (
|
||||
<div key={color.name} className="flex items-center justify-between p-4 border border-gray-200 dark:border-gray-700 rounded-lg">
|
||||
<div>
|
||||
<div className={cn(color.className, 'text-base font-medium mb-1')}>
|
||||
{color.name}
|
||||
</div>
|
||||
<div className="text-xs text-gray-500 dark:text-gray-400">
|
||||
{color.usage}
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-xs font-mono text-gray-400 dark:text-gray-500">
|
||||
{color.className.split(' ')[0]}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* Usage Code */}
|
||||
<Card className="p-6 max-w-none">
|
||||
<h3 className="text-lg font-semibold mb-4">Usage Code</h3>
|
||||
<CodeDisplay
|
||||
code={generateCode()}
|
||||
|
||||
showLineNumbers
|
||||
/>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,861 @@
|
||||
import { useState } from 'react';
|
||||
import { Card } from '@/features/ui/primitives/card';
|
||||
import { Button } from '@/features/ui/primitives/button';
|
||||
import { CodeDisplay } from '../shared/CodeDisplay';
|
||||
import { cn, glassmorphism } from '@/features/ui/primitives/styles';
|
||||
import {
|
||||
CheckCircle,
|
||||
XCircle,
|
||||
AlertTriangle,
|
||||
Info,
|
||||
X,
|
||||
Loader2,
|
||||
AlertCircle,
|
||||
HelpCircle
|
||||
} from 'lucide-react';
|
||||
|
||||
interface FeedbackPattern {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
usage: string;
|
||||
component: React.ComponentType;
|
||||
}
|
||||
|
||||
// Alert Component
|
||||
const AlertsDemo = () => {
|
||||
const alerts = [
|
||||
{
|
||||
type: 'success',
|
||||
icon: <CheckCircle className="w-5 h-5" />,
|
||||
title: 'Success',
|
||||
message: 'Your project has been created successfully.',
|
||||
bgColor: 'bg-green-50 dark:bg-green-900/20',
|
||||
borderColor: 'border-green-200 dark:border-green-800',
|
||||
textColor: 'text-green-800 dark:text-green-200',
|
||||
iconColor: 'text-green-600 dark:text-green-400'
|
||||
},
|
||||
{
|
||||
type: 'error',
|
||||
icon: <XCircle className="w-5 h-5" />,
|
||||
title: 'Error',
|
||||
message: 'There was an error processing your request. Please try again.',
|
||||
bgColor: 'bg-red-50 dark:bg-red-900/20',
|
||||
borderColor: 'border-red-200 dark:border-red-800',
|
||||
textColor: 'text-red-800 dark:text-red-200',
|
||||
iconColor: 'text-red-600 dark:text-red-400'
|
||||
},
|
||||
{
|
||||
type: 'warning',
|
||||
icon: <AlertTriangle className="w-5 h-5" />,
|
||||
title: 'Warning',
|
||||
message: 'This action cannot be undone. Please proceed with caution.',
|
||||
bgColor: 'bg-orange-50 dark:bg-orange-900/20',
|
||||
borderColor: 'border-orange-200 dark:border-orange-800',
|
||||
textColor: 'text-orange-800 dark:text-orange-200',
|
||||
iconColor: 'text-orange-600 dark:text-orange-400'
|
||||
},
|
||||
{
|
||||
type: 'info',
|
||||
icon: <Info className="w-5 h-5" />,
|
||||
title: 'Information',
|
||||
message: 'New features have been added to your dashboard.',
|
||||
bgColor: 'bg-blue-50 dark:bg-blue-900/20',
|
||||
borderColor: 'border-blue-200 dark:border-blue-800',
|
||||
textColor: 'text-blue-800 dark:text-blue-200',
|
||||
iconColor: 'text-blue-600 dark:text-blue-400'
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
{alerts.map((alert) => (
|
||||
<div
|
||||
key={alert.type}
|
||||
className={cn(
|
||||
'p-4 rounded-lg border backdrop-blur-sm',
|
||||
alert.bgColor,
|
||||
alert.borderColor
|
||||
)}
|
||||
>
|
||||
<div className="flex items-start gap-3">
|
||||
<div className={alert.iconColor}>
|
||||
{alert.icon}
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<h4 className={cn('font-medium text-sm', alert.textColor)}>
|
||||
{alert.title}
|
||||
</h4>
|
||||
<p className={cn('text-sm mt-1', alert.textColor)}>
|
||||
{alert.message}
|
||||
</p>
|
||||
</div>
|
||||
<Button variant="ghost" size="sm" className={cn('p-1', alert.textColor)}>
|
||||
<X className="w-4 h-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// Loading States Demo
|
||||
const LoadingStatesDemo = () => {
|
||||
const loadingStates = [
|
||||
{
|
||||
name: 'Spinner',
|
||||
component: (
|
||||
<div className="flex items-center justify-center p-8">
|
||||
<Loader2 className="w-8 h-8 animate-spin text-cyan-500" />
|
||||
</div>
|
||||
)
|
||||
},
|
||||
{
|
||||
name: 'Spinner with Text',
|
||||
component: (
|
||||
<div className="flex items-center justify-center gap-3 p-8">
|
||||
<Loader2 className="w-5 h-5 animate-spin text-cyan-500" />
|
||||
<span className="text-sm text-gray-600 dark:text-gray-400">Loading...</span>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
{
|
||||
name: 'Progress Bar',
|
||||
component: (
|
||||
<div className="p-8 space-y-3">
|
||||
<div className="flex justify-between text-sm">
|
||||
<span>Uploading files...</span>
|
||||
<span>67%</span>
|
||||
</div>
|
||||
<div className="w-full bg-gray-200 dark:bg-gray-700 rounded-full h-2">
|
||||
<div className="bg-cyan-500 h-2 rounded-full transition-all duration-300" style={{ width: '67%' }} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
{
|
||||
name: 'Skeleton Loading',
|
||||
component: (
|
||||
<div className="p-8 space-y-4">
|
||||
<div className="animate-pulse">
|
||||
<div className="h-4 bg-gray-300 dark:bg-gray-700 rounded w-3/4 mb-3" />
|
||||
<div className="h-4 bg-gray-300 dark:bg-gray-700 rounded w-1/2 mb-3" />
|
||||
<div className="h-4 bg-gray-300 dark:bg-gray-700 rounded w-5/6" />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
{
|
||||
name: 'Pulsing Card',
|
||||
component: (
|
||||
<div className="p-8">
|
||||
<Card className="animate-pulse opacity-60">
|
||||
<div className="h-20 bg-gray-300 dark:bg-gray-700 rounded" />
|
||||
</Card>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
{loadingStates.map((state) => (
|
||||
<Card key={state.name} className="text-center">
|
||||
<h4 className="font-medium text-sm mb-2 p-4 pb-0">{state.name}</h4>
|
||||
{state.component}
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// Toast Notifications Demo
|
||||
const ToastDemo = () => {
|
||||
const [toasts, setToasts] = useState<Array<{
|
||||
id: number;
|
||||
type: string;
|
||||
title: string;
|
||||
message: string;
|
||||
}>>([]);
|
||||
|
||||
const addToast = (type: string) => {
|
||||
const toastData = {
|
||||
success: { title: 'Success!', message: 'Action completed successfully.' },
|
||||
error: { title: 'Error!', message: 'Something went wrong.' },
|
||||
warning: { title: 'Warning!', message: 'Please review your action.' },
|
||||
info: { title: 'Info', message: 'Here\'s some useful information.' }
|
||||
};
|
||||
|
||||
const newToast = {
|
||||
id: Date.now(),
|
||||
type,
|
||||
...toastData[type as keyof typeof toastData]
|
||||
};
|
||||
|
||||
setToasts(prev => [...prev, newToast]);
|
||||
|
||||
// Auto-remove after 3 seconds
|
||||
setTimeout(() => {
|
||||
setToasts(prev => prev.filter(t => t.id !== newToast.id));
|
||||
}, 3000);
|
||||
};
|
||||
|
||||
const removeToast = (id: number) => {
|
||||
setToasts(prev => prev.filter(t => t.id !== id));
|
||||
};
|
||||
|
||||
const getToastStyles = (type: string) => {
|
||||
const styles = {
|
||||
success: {
|
||||
bg: glassmorphism.background.card,
|
||||
border: 'border-green-500',
|
||||
glow: glassmorphism.shadow.glow.green,
|
||||
icon: <CheckCircle className="w-5 h-5 text-green-500" />
|
||||
},
|
||||
error: {
|
||||
bg: glassmorphism.background.card,
|
||||
border: 'border-red-500',
|
||||
glow: glassmorphism.shadow.glow.red,
|
||||
icon: <XCircle className="w-5 h-5 text-red-500" />
|
||||
},
|
||||
warning: {
|
||||
bg: glassmorphism.background.card,
|
||||
border: 'border-orange-500',
|
||||
glow: glassmorphism.shadow.glow.orange,
|
||||
icon: <AlertTriangle className="w-5 h-5 text-orange-500" />
|
||||
},
|
||||
info: {
|
||||
bg: glassmorphism.background.card,
|
||||
border: 'border-blue-500',
|
||||
glow: glassmorphism.shadow.glow.blue,
|
||||
icon: <Info className="w-5 h-5 text-blue-500" />
|
||||
}
|
||||
};
|
||||
return styles[type as keyof typeof styles];
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
{/* Controls */}
|
||||
<div className="flex gap-2 mb-6">
|
||||
<Button onClick={() => addToast('success')} size="sm" variant="outline">
|
||||
Success Toast
|
||||
</Button>
|
||||
<Button onClick={() => addToast('error')} size="sm" variant="outline">
|
||||
Error Toast
|
||||
</Button>
|
||||
<Button onClick={() => addToast('warning')} size="sm" variant="outline">
|
||||
Warning Toast
|
||||
</Button>
|
||||
<Button onClick={() => addToast('info')} size="sm" variant="outline">
|
||||
Info Toast
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Toast Container */}
|
||||
<div className="fixed top-4 right-4 z-50 space-y-2 max-w-sm">
|
||||
{toasts.map((toast) => {
|
||||
const styles = getToastStyles(toast.type);
|
||||
return (
|
||||
<div
|
||||
key={toast.id}
|
||||
className={cn(
|
||||
'p-4 rounded-lg border backdrop-blur-md animate-in slide-in-from-right',
|
||||
styles.bg,
|
||||
styles.border,
|
||||
styles.glow
|
||||
)}
|
||||
>
|
||||
<div className="flex items-start gap-3">
|
||||
{styles.icon}
|
||||
<div className="flex-1 min-w-0">
|
||||
<h4 className="font-medium text-sm">{toast.title}</h4>
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400 mt-1">
|
||||
{toast.message}
|
||||
</p>
|
||||
</div>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="p-1"
|
||||
onClick={() => removeToast(toast.id)}
|
||||
>
|
||||
<X className="w-4 h-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
{/* Static Example */}
|
||||
<Card className="p-4">
|
||||
<h4 className="font-medium mb-3">Static Toast Example</h4>
|
||||
<div
|
||||
className={cn(
|
||||
'p-4 rounded-lg border backdrop-blur-md max-w-sm',
|
||||
glassmorphism.background.card,
|
||||
'border-green-500',
|
||||
glassmorphism.shadow.glow.green
|
||||
)}
|
||||
>
|
||||
<div className="flex items-start gap-3">
|
||||
<CheckCircle className="w-5 h-5 text-green-500" />
|
||||
<div className="flex-1 min-w-0">
|
||||
<h4 className="font-medium text-sm">Project Created</h4>
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400 mt-1">
|
||||
Your new project has been created successfully.
|
||||
</p>
|
||||
</div>
|
||||
<Button variant="ghost" size="sm" className="p-1">
|
||||
<X className="w-4 h-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// Empty States Demo
|
||||
const EmptyStatesDemo = () => {
|
||||
const emptyStates = [
|
||||
{
|
||||
name: 'No Data',
|
||||
icon: <HelpCircle className="w-12 h-12 mx-auto mb-4 text-gray-400" />,
|
||||
title: 'No projects found',
|
||||
description: 'Get started by creating your first project.',
|
||||
action: <Button size="sm">Create Project</Button>
|
||||
},
|
||||
{
|
||||
name: 'No Search Results',
|
||||
icon: <AlertCircle className="w-12 h-12 mx-auto mb-4 text-gray-400" />,
|
||||
title: 'No results found',
|
||||
description: 'Try adjusting your search criteria or filters.',
|
||||
action: <Button size="sm" variant="outline">Clear Filters</Button>
|
||||
},
|
||||
{
|
||||
name: 'Coming Soon',
|
||||
icon: <Info className="w-12 h-12 mx-auto mb-4 text-cyan-500" />,
|
||||
title: 'Feature coming soon',
|
||||
description: 'This feature is currently under development.',
|
||||
action: null
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
{emptyStates.map((state) => (
|
||||
<Card key={state.name} className="text-center p-8">
|
||||
<h4 className="font-medium text-xs mb-4 text-gray-500 uppercase">
|
||||
{state.name}
|
||||
</h4>
|
||||
{state.icon}
|
||||
<h3 className="font-semibold text-lg mb-2">{state.title}</h3>
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400 mb-4">
|
||||
{state.description}
|
||||
</p>
|
||||
{state.action}
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const FEEDBACK_PATTERNS: FeedbackPattern[] = [
|
||||
{
|
||||
id: 'alerts',
|
||||
name: 'Alerts & Messages',
|
||||
description: 'Contextual feedback messages with different severity levels',
|
||||
usage: 'Form validation, system notifications, user feedback',
|
||||
component: AlertsDemo
|
||||
},
|
||||
{
|
||||
id: 'loading',
|
||||
name: 'Loading States',
|
||||
description: 'Visual indicators for loading and processing states',
|
||||
usage: 'Data fetching, file uploads, long-running operations',
|
||||
component: LoadingStatesDemo
|
||||
},
|
||||
{
|
||||
id: 'toasts',
|
||||
name: 'Toast Notifications',
|
||||
description: 'Temporary notifications that appear and disappear automatically',
|
||||
usage: 'Action confirmations, real-time updates, background processes',
|
||||
component: ToastDemo
|
||||
},
|
||||
{
|
||||
id: 'empty',
|
||||
name: 'Empty States',
|
||||
description: 'Helpful messaging when no content is available',
|
||||
usage: 'Empty lists, no search results, feature placeholders',
|
||||
component: EmptyStatesDemo
|
||||
}
|
||||
];
|
||||
|
||||
const generateCode = (patternId: string) => {
|
||||
const codeExamples = {
|
||||
alerts: `import { Card } from '@/features/ui/primitives/card';
|
||||
import { CheckCircle, XCircle, AlertTriangle, Info, X } from 'lucide-react';
|
||||
|
||||
/**
|
||||
* 🤖 AI CONTEXT: Alert Pattern
|
||||
*
|
||||
* PURPOSE: Contextual feedback with appropriate visual treatment
|
||||
* WHEN TO USE: Form validation, system notifications, important messages
|
||||
* WHEN NOT TO USE: Temporary messages (use toasts), blocking dialogs (use modals)
|
||||
*
|
||||
* SEVERITY LEVELS:
|
||||
* - Success: Green - Positive outcomes, completions
|
||||
* - Error: Red - Critical errors, validation failures
|
||||
* - Warning: Orange - Cautions, potential issues
|
||||
* - Info: Blue - Neutral information, tips
|
||||
*
|
||||
* ACCESSIBILITY:
|
||||
* - Use semantic colors consistently
|
||||
* - Include icons for visual clarity
|
||||
* - Provide clear, actionable text
|
||||
* - Support dismissal with keyboard
|
||||
*/
|
||||
|
||||
interface AlertProps {
|
||||
type: 'success' | 'error' | 'warning' | 'info';
|
||||
title: string;
|
||||
message: string;
|
||||
onDismiss?: () => void;
|
||||
}
|
||||
|
||||
export const Alert = ({ type, title, message, onDismiss }: AlertProps) => {
|
||||
const config = {
|
||||
success: {
|
||||
icon: <CheckCircle className="w-5 h-5" />,
|
||||
bgColor: 'bg-green-50 dark:bg-green-900/20',
|
||||
borderColor: 'border-green-200 dark:border-green-800',
|
||||
textColor: 'text-green-800 dark:text-green-200',
|
||||
iconColor: 'text-green-600 dark:text-green-400'
|
||||
},
|
||||
error: {
|
||||
icon: <XCircle className="w-5 h-5" />,
|
||||
bgColor: 'bg-red-50 dark:bg-red-900/20',
|
||||
borderColor: 'border-red-200 dark:border-red-800',
|
||||
textColor: 'text-red-800 dark:text-red-200',
|
||||
iconColor: 'text-red-600 dark:text-red-400'
|
||||
},
|
||||
warning: {
|
||||
icon: <AlertTriangle className="w-5 h-5" />,
|
||||
bgColor: 'bg-orange-50 dark:bg-orange-900/20',
|
||||
borderColor: 'border-orange-200 dark:border-orange-800',
|
||||
textColor: 'text-orange-800 dark:text-orange-200',
|
||||
iconColor: 'text-orange-600 dark:text-orange-400'
|
||||
},
|
||||
info: {
|
||||
icon: <Info className="w-5 h-5" />,
|
||||
bgColor: 'bg-blue-50 dark:bg-blue-900/20',
|
||||
borderColor: 'border-blue-200 dark:border-blue-800',
|
||||
textColor: 'text-blue-800 dark:text-blue-200',
|
||||
iconColor: 'text-blue-600 dark:text-blue-400'
|
||||
}
|
||||
};
|
||||
|
||||
const styles = config[type];
|
||||
|
||||
return (
|
||||
<div className={cn(
|
||||
'p-4 rounded-lg border backdrop-blur-sm',
|
||||
styles.bgColor,
|
||||
styles.borderColor
|
||||
)}>
|
||||
<div className="flex items-start gap-3">
|
||||
<div className={styles.iconColor}>
|
||||
{styles.icon}
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<h4 className={cn('font-medium text-sm', styles.textColor)}>
|
||||
{title}
|
||||
</h4>
|
||||
<p className={cn('text-sm mt-1', styles.textColor)}>
|
||||
{message}
|
||||
</p>
|
||||
</div>
|
||||
{onDismiss && (
|
||||
<button
|
||||
onClick={onDismiss}
|
||||
className={cn('p-1 hover:opacity-70', styles.textColor)}
|
||||
>
|
||||
<X className="w-4 h-4" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};`,
|
||||
|
||||
loading: `import { Loader2 } from 'lucide-react';
|
||||
import { Card } from '@/features/ui/primitives/card';
|
||||
|
||||
/**
|
||||
* 🤖 AI CONTEXT: Loading States Pattern
|
||||
*
|
||||
* PURPOSE: Visual feedback during async operations
|
||||
* WHEN TO USE: Data fetching, file uploads, processing operations
|
||||
* WHEN NOT TO USE: Instant operations, already completed actions
|
||||
*
|
||||
* LOADING TYPES:
|
||||
* - Spinner: Quick operations (< 5 seconds)
|
||||
* - Progress: Trackable operations with known duration
|
||||
* - Skeleton: Content loading with known structure
|
||||
* - Pulse: Unknown duration, maintaining layout
|
||||
*
|
||||
* UX GUIDELINES:
|
||||
* - Show immediately for operations > 500ms
|
||||
* - Provide progress feedback when possible
|
||||
* - Maintain layout during loading states
|
||||
* - Include descriptive text for clarity
|
||||
*/
|
||||
|
||||
// Spinner Loading
|
||||
export const SpinnerLoader = ({ text }: { text?: string }) => (
|
||||
<div className="flex items-center justify-center gap-3 p-8">
|
||||
<Loader2 className="w-5 h-5 animate-spin text-cyan-500" />
|
||||
{text && (
|
||||
<span className="text-sm text-gray-600 dark:text-gray-400">
|
||||
{text}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
// Progress Loading
|
||||
export const ProgressLoader = ({
|
||||
progress,
|
||||
text
|
||||
}: {
|
||||
progress: number;
|
||||
text?: string;
|
||||
}) => (
|
||||
<div className="p-4 space-y-3">
|
||||
{text && (
|
||||
<div className="flex justify-between text-sm">
|
||||
<span>{text}</span>
|
||||
<span>{progress}%</span>
|
||||
</div>
|
||||
)}
|
||||
<div className="w-full bg-gray-200 dark:bg-gray-700 rounded-full h-2">
|
||||
<div
|
||||
className="bg-cyan-500 h-2 rounded-full transition-all duration-300"
|
||||
style={{ width: \`\${progress}%\` }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
// Skeleton Loading
|
||||
export const SkeletonLoader = () => (
|
||||
<div className="animate-pulse space-y-4">
|
||||
<div className="h-4 bg-gray-300 dark:bg-gray-700 rounded w-3/4" />
|
||||
<div className="h-4 bg-gray-300 dark:bg-gray-700 rounded w-1/2" />
|
||||
<div className="h-4 bg-gray-300 dark:bg-gray-700 rounded w-5/6" />
|
||||
</div>
|
||||
);
|
||||
|
||||
// Card Loading
|
||||
export const CardLoader = () => (
|
||||
<Card className="animate-pulse opacity-60">
|
||||
<div className="h-20 bg-gray-300 dark:bg-gray-700 rounded" />
|
||||
</Card>
|
||||
);`,
|
||||
|
||||
toasts: `import { useState, useEffect } from 'react';
|
||||
import { Card } from '@/features/ui/primitives/card';
|
||||
import { glassmorphism } from '@/features/ui/primitives/styles';
|
||||
import { CheckCircle, XCircle, AlertTriangle, Info, X } from 'lucide-react';
|
||||
|
||||
/**
|
||||
* 🤖 AI CONTEXT: Toast Notifications Pattern
|
||||
*
|
||||
* PURPOSE: Temporary, non-blocking feedback messages
|
||||
* WHEN TO USE: Action confirmations, background updates, quick feedback
|
||||
* WHEN NOT TO USE: Critical errors (use alerts), complex messages (use modals)
|
||||
*
|
||||
* TOAST CHARACTERISTICS:
|
||||
* - Auto-dismiss after 3-5 seconds
|
||||
* - Non-blocking, can be dismissed manually
|
||||
* - Stack vertically in corner of screen
|
||||
* - Use glassmorphism for modern feel
|
||||
*
|
||||
* POSITIONING:
|
||||
* - Top-right: Most common, non-intrusive
|
||||
* - Top-center: Important announcements
|
||||
* - Bottom-right: Mobile-friendly alternative
|
||||
*/
|
||||
|
||||
interface Toast {
|
||||
id: string;
|
||||
type: 'success' | 'error' | 'warning' | 'info';
|
||||
title: string;
|
||||
message: string;
|
||||
duration?: number;
|
||||
}
|
||||
|
||||
export const useToast = () => {
|
||||
const [toasts, setToasts] = useState<Toast[]>([]);
|
||||
|
||||
const addToast = (toast: Omit<Toast, 'id'>) => {
|
||||
const id = Date.now().toString();
|
||||
const newToast = { ...toast, id };
|
||||
|
||||
setToasts(prev => [...prev, newToast]);
|
||||
|
||||
// Auto-remove
|
||||
setTimeout(() => {
|
||||
removeToast(id);
|
||||
}, toast.duration || 4000);
|
||||
|
||||
return id;
|
||||
};
|
||||
|
||||
const removeToast = (id: string) => {
|
||||
setToasts(prev => prev.filter(t => t.id !== id));
|
||||
};
|
||||
|
||||
return { toasts, addToast, removeToast };
|
||||
};
|
||||
|
||||
export const ToastContainer = ({
|
||||
toasts,
|
||||
onRemove
|
||||
}: {
|
||||
toasts: Toast[];
|
||||
onRemove: (id: string) => void;
|
||||
}) => {
|
||||
const getToastStyles = (type: string) => {
|
||||
const styles = {
|
||||
success: {
|
||||
border: 'border-green-500',
|
||||
glow: glassmorphism.shadow.glow.green,
|
||||
icon: <CheckCircle className="w-5 h-5 text-green-500" />
|
||||
},
|
||||
error: {
|
||||
border: 'border-red-500',
|
||||
glow: glassmorphism.shadow.glow.red,
|
||||
icon: <XCircle className="w-5 h-5 text-red-500" />
|
||||
},
|
||||
warning: {
|
||||
border: 'border-orange-500',
|
||||
glow: glassmorphism.shadow.glow.orange,
|
||||
icon: <AlertTriangle className="w-5 h-5 text-orange-500" />
|
||||
},
|
||||
info: {
|
||||
border: 'border-blue-500',
|
||||
glow: glassmorphism.shadow.glow.blue,
|
||||
icon: <Info className="w-5 h-5 text-blue-500" />
|
||||
}
|
||||
};
|
||||
return styles[type as keyof typeof styles];
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="fixed top-4 right-4 z-50 space-y-2 max-w-sm">
|
||||
{toasts.map((toast) => {
|
||||
const styles = getToastStyles(toast.type);
|
||||
return (
|
||||
<div
|
||||
key={toast.id}
|
||||
className={cn(
|
||||
'p-4 rounded-lg border backdrop-blur-md',
|
||||
'animate-in slide-in-from-right duration-300',
|
||||
glassmorphism.background.card,
|
||||
styles.border,
|
||||
styles.glow
|
||||
)}
|
||||
>
|
||||
<div className="flex items-start gap-3">
|
||||
{styles.icon}
|
||||
<div className="flex-1 min-w-0">
|
||||
<h4 className="font-medium text-sm">{toast.title}</h4>
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400 mt-1">
|
||||
{toast.message}
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => onRemove(toast.id)}
|
||||
className="p-1 hover:opacity-70"
|
||||
>
|
||||
<X className="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};`,
|
||||
|
||||
empty: `import { Card } from '@/features/ui/primitives/card';
|
||||
import { Button } from '@/features/ui/primitives/button';
|
||||
import { HelpCircle, AlertCircle, Info } from 'lucide-react';
|
||||
|
||||
/**
|
||||
* 🤖 AI CONTEXT: Empty States Pattern
|
||||
*
|
||||
* PURPOSE: Helpful guidance when no content is available
|
||||
* WHEN TO USE: Empty lists, no search results, disabled features
|
||||
* WHEN NOT TO USE: Loading states, error conditions, temporary states
|
||||
*
|
||||
* EMPTY STATE TYPES:
|
||||
* - No Data: First-time use, empty collections
|
||||
* - No Results: Search/filter yielded nothing
|
||||
* - Feature Disabled: Functionality not available
|
||||
* - Coming Soon: Planned but not implemented
|
||||
*
|
||||
* CONTENT GUIDELINES:
|
||||
* - Clear, descriptive title
|
||||
* - Helpful explanation or next steps
|
||||
* - Primary action when applicable
|
||||
* - Appropriate icon for context
|
||||
*/
|
||||
|
||||
interface EmptyStateProps {
|
||||
icon: React.ReactNode;
|
||||
title: string;
|
||||
description: string;
|
||||
action?: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const EmptyState = ({
|
||||
icon,
|
||||
title,
|
||||
description,
|
||||
action,
|
||||
className
|
||||
}: EmptyStateProps) => (
|
||||
<Card className={cn('text-center p-12', className)}>
|
||||
<div className="mx-auto mb-4">
|
||||
{icon}
|
||||
</div>
|
||||
<h3 className="font-semibold text-lg mb-2">{title}</h3>
|
||||
<p className="text-gray-600 dark:text-gray-400 mb-6 max-w-md mx-auto">
|
||||
{description}
|
||||
</p>
|
||||
{action}
|
||||
</Card>
|
||||
);
|
||||
|
||||
// Predefined empty states
|
||||
export const NoDataState = ({
|
||||
onAction
|
||||
}: {
|
||||
onAction?: () => void;
|
||||
}) => (
|
||||
<EmptyState
|
||||
icon={<HelpCircle className="w-16 h-16 text-gray-400" />}
|
||||
title="No projects found"
|
||||
description="Get started by creating your first project. You can organize your work and collaborate with your team."
|
||||
action={
|
||||
onAction && (
|
||||
<Button onClick={onAction}>
|
||||
Create Your First Project
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
||||
export const NoSearchResultsState = ({
|
||||
onClearFilters
|
||||
}: {
|
||||
onClearFilters?: () => void;
|
||||
}) => (
|
||||
<EmptyState
|
||||
icon={<AlertCircle className="w-16 h-16 text-gray-400" />}
|
||||
title="No results found"
|
||||
description="We couldn't find any projects matching your search criteria. Try adjusting your filters or search terms."
|
||||
action={
|
||||
onClearFilters && (
|
||||
<Button variant="outline" onClick={onClearFilters}>
|
||||
Clear All Filters
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
||||
export const ComingSoonState = ({ feature }: { feature: string }) => (
|
||||
<EmptyState
|
||||
icon={<Info className="w-16 h-16 text-cyan-500" />}
|
||||
title={\`\${feature} coming soon\`}
|
||||
description="This feature is currently under development. We'll notify you when it becomes available."
|
||||
/>
|
||||
);`
|
||||
};
|
||||
|
||||
return codeExamples[patternId as keyof typeof codeExamples] || '';
|
||||
};
|
||||
|
||||
export const FeedbackPattern = () => {
|
||||
const [selectedPattern, setSelectedPattern] = useState<string>('alerts');
|
||||
|
||||
const currentPattern = FEEDBACK_PATTERNS.find(p => p.id === selectedPattern);
|
||||
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold mb-4">Feedback Patterns</h2>
|
||||
<p className="text-gray-600 dark:text-gray-400 mb-6">
|
||||
User feedback patterns including alerts, loading states, notifications, and empty states.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Pattern Selector */}
|
||||
<Card className="p-6 max-w-none">
|
||||
<h3 className="text-lg font-semibold mb-4">Select Feedback Pattern</h3>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
{FEEDBACK_PATTERNS.map((pattern) => (
|
||||
<Card
|
||||
key={pattern.id}
|
||||
className={cn(
|
||||
"p-4 cursor-pointer transition-all duration-200",
|
||||
selectedPattern === pattern.id
|
||||
? "border-cyan-500 shadow-[0_0_10px_rgba(34,211,238,0.3)]"
|
||||
: "hover:shadow-lg"
|
||||
)}
|
||||
onClick={() => setSelectedPattern(pattern.id)}
|
||||
>
|
||||
<h4 className="font-medium mb-2">{pattern.name}</h4>
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400 mb-2">
|
||||
{pattern.description}
|
||||
</p>
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400">
|
||||
<strong>Use for:</strong> {pattern.usage}
|
||||
</p>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* Live Preview */}
|
||||
{currentPattern && (
|
||||
<Card className="p-6 max-w-none">
|
||||
<h3 className="text-lg font-semibold mb-4">
|
||||
{currentPattern.name} - Examples
|
||||
</h3>
|
||||
<currentPattern.component />
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{/* Generated Code */}
|
||||
{currentPattern && (
|
||||
<Card className="p-6 max-w-none">
|
||||
<h3 className="text-lg font-semibold mb-4">Generated Code</h3>
|
||||
<CodeDisplay
|
||||
code={generateCode(selectedPattern)}
|
||||
|
||||
showLineNumbers
|
||||
/>
|
||||
</Card>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,743 @@
|
||||
import { useState } from 'react';
|
||||
import { Card } from '@/features/ui/primitives/card';
|
||||
import type { GlowColor } from '../types';
|
||||
import { Button } from '@/features/ui/primitives/button';
|
||||
import { CodeDisplay } from '../shared/CodeDisplay';
|
||||
import { LivePreview } from '../shared/LivePreview';
|
||||
import { cn, glassmorphism } from '@/features/ui/primitives/styles';
|
||||
import {
|
||||
LayoutGrid,
|
||||
Sidebar,
|
||||
PlusCircle,
|
||||
ChevronLeft,
|
||||
ChevronRight,
|
||||
MoreVertical
|
||||
} from 'lucide-react';
|
||||
|
||||
interface LayoutTemplate {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
usage: string;
|
||||
component: React.ComponentType;
|
||||
}
|
||||
|
||||
// Dashboard Template Component
|
||||
const DashboardTemplate = () => {
|
||||
const cards = [
|
||||
{ title: 'Total Projects', value: '12', change: '+2 this week', color: 'purple' },
|
||||
{ title: 'Active Tasks', value: '38', change: '+5 today', color: 'blue' },
|
||||
{ title: 'Completed', value: '245', change: '+12 this week', color: 'green' },
|
||||
{ title: 'Team Members', value: '8', change: 'No change', color: 'orange' }
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="p-4 space-y-4">
|
||||
{/* Header */}
|
||||
<div className="flex justify-between items-center">
|
||||
<h2 className="text-xl font-semibold">Dashboard</h2>
|
||||
<Button size="sm" variant="outline">
|
||||
<PlusCircle className="w-4 h-4 mr-2" />
|
||||
New Project
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Stats Cards */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
{cards.map((card) => (
|
||||
<Card
|
||||
key={card.title}
|
||||
glowColor={card.color as GlowColor}
|
||||
className="p-4 hover:shadow-lg transition-shadow"
|
||||
>
|
||||
<h3 className="text-sm font-medium text-gray-600 dark:text-gray-400">
|
||||
{card.title}
|
||||
</h3>
|
||||
<p className="text-2xl font-bold mt-1">{card.value}</p>
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400 mt-1">
|
||||
{card.change}
|
||||
</p>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Content Grid */}
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-4">
|
||||
<Card className="lg:col-span-2 p-4">
|
||||
<h3 className="font-semibold mb-3">Recent Activity</h3>
|
||||
<div className="space-y-2">
|
||||
{['Project Alpha updated', 'New task assigned', 'Beta milestone completed'].map((activity, i) => (
|
||||
<div key={i} className="text-sm py-2 border-b border-gray-200 dark:border-gray-700 last:border-b-0">
|
||||
{activity}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</Card>
|
||||
<Card className="p-4">
|
||||
<h3 className="font-semibold mb-3">Quick Actions</h3>
|
||||
<div className="space-y-2">
|
||||
<Button variant="outline" size="sm" className="w-full justify-start">
|
||||
Create Task
|
||||
</Button>
|
||||
<Button variant="outline" size="sm" className="w-full justify-start">
|
||||
Add Member
|
||||
</Button>
|
||||
<Button variant="outline" size="sm" className="w-full justify-start">
|
||||
View Reports
|
||||
</Button>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// Sidebar Template Component
|
||||
const SidebarTemplate = () => {
|
||||
const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
|
||||
const [sidebarPosition, setSidebarPosition] = useState<'top' | 'left'>('left');
|
||||
|
||||
const items = [
|
||||
{ id: '1', title: 'Project Alpha', status: 'active', color: 'purple' },
|
||||
{ id: '2', title: 'Project Beta', status: 'pending', color: 'blue' },
|
||||
{ id: '3', title: 'Project Gamma', status: 'completed', color: 'green' },
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="h-64 flex flex-col">
|
||||
{/* Toggle Controls */}
|
||||
<div className="flex justify-between items-center p-2 border-b border-gray-200 dark:border-gray-700">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => setSidebarPosition(p => p === 'top' ? 'left' : 'top')}
|
||||
>
|
||||
{sidebarPosition === 'top' ? <Sidebar className="w-4 h-4 mr-2" /> : <LayoutGrid className="w-4 h-4 mr-2" />}
|
||||
{sidebarPosition === 'top' ? 'Switch to Sidebar' : 'Switch to Top'}
|
||||
</Button>
|
||||
{sidebarPosition === 'left' && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => setSidebarCollapsed(!sidebarCollapsed)}
|
||||
>
|
||||
{sidebarCollapsed ? <ChevronRight className="w-4 h-4" /> : <ChevronLeft className="w-4 h-4" />}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className={cn(
|
||||
"flex flex-1",
|
||||
sidebarPosition === 'top' ? "flex-col" : "flex-row",
|
||||
"gap-4 p-4"
|
||||
)}>
|
||||
{/* Sidebar */}
|
||||
<div className={cn(
|
||||
sidebarPosition === 'left' && !sidebarCollapsed && "w-64",
|
||||
sidebarPosition === 'left' && sidebarCollapsed && "w-16",
|
||||
sidebarPosition === 'top' && "w-full",
|
||||
"transition-all duration-300"
|
||||
)}>
|
||||
{sidebarPosition === 'top' ? (
|
||||
<div className="grid grid-cols-3 gap-4">
|
||||
{items.map(item => (
|
||||
<Card
|
||||
key={item.id}
|
||||
glowColor={item.color as GlowColor}
|
||||
className="p-3 hover:shadow-lg transition-shadow cursor-pointer"
|
||||
>
|
||||
<h4 className="font-medium text-sm">{item.title}</h4>
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400">{item.status}</p>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-2">
|
||||
{items.map(item => (
|
||||
<Card
|
||||
key={item.id}
|
||||
className={cn(
|
||||
"p-3 hover:shadow-lg transition-shadow cursor-pointer",
|
||||
sidebarCollapsed && "p-2"
|
||||
)}
|
||||
>
|
||||
{!sidebarCollapsed ? (
|
||||
<>
|
||||
<h4 className="font-medium text-sm">{item.title}</h4>
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400">{item.status}</p>
|
||||
</>
|
||||
) : (
|
||||
<div className="w-8 h-8 rounded bg-purple-500/20 flex items-center justify-center text-xs font-medium">
|
||||
{item.title[0]}
|
||||
</div>
|
||||
)}
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Main Content */}
|
||||
<div className="flex-1">
|
||||
<Card className="h-full p-6 border-2 border-dashed border-gray-300 dark:border-gray-600">
|
||||
<div className="text-center text-gray-500 dark:text-gray-400">
|
||||
<h3 className="font-medium mb-2">Main Content Area</h3>
|
||||
<p className="text-sm">Your application content goes here</p>
|
||||
<p className="text-xs mt-2">
|
||||
Sidebar: {sidebarPosition === 'top' ? 'Top' : `Left (${sidebarCollapsed ? 'Collapsed' : 'Expanded'})`}
|
||||
</p>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// Table View Template Component
|
||||
const TableViewTemplate = () => {
|
||||
const data = [
|
||||
{ id: 1, name: 'Project Alpha', status: 'Active', priority: 'High', progress: 75 },
|
||||
{ id: 2, name: 'Project Beta', status: 'Pending', priority: 'Medium', progress: 25 },
|
||||
{ id: 3, name: 'Project Gamma', status: 'Completed', priority: 'Low', progress: 100 },
|
||||
];
|
||||
|
||||
const getStatusColor = (status: string) => {
|
||||
switch (status.toLowerCase()) {
|
||||
case 'active': return 'text-green-600 dark:text-green-400';
|
||||
case 'pending': return 'text-orange-600 dark:text-orange-400';
|
||||
case 'completed': return 'text-blue-600 dark:text-blue-400';
|
||||
default: return 'text-gray-600 dark:text-gray-400';
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="p-4 space-y-4">
|
||||
{/* Header */}
|
||||
<div className="flex justify-between items-center">
|
||||
<h2 className="text-xl font-semibold">Projects Table</h2>
|
||||
<div className="flex gap-2">
|
||||
<Button variant="outline" size="sm">Filter</Button>
|
||||
<Button size="sm">
|
||||
<PlusCircle className="w-4 h-4 mr-2" />
|
||||
Add Project
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Table */}
|
||||
<Card className="overflow-hidden">
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full">
|
||||
<thead className="bg-gray-50 dark:bg-gray-800/50">
|
||||
<tr>
|
||||
<th className="px-4 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase">
|
||||
Name
|
||||
</th>
|
||||
<th className="px-4 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase">
|
||||
Status
|
||||
</th>
|
||||
<th className="px-4 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase">
|
||||
Priority
|
||||
</th>
|
||||
<th className="px-4 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase">
|
||||
Progress
|
||||
</th>
|
||||
<th className="px-4 py-3 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase">
|
||||
Actions
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-gray-200 dark:divide-gray-700">
|
||||
{data.map((row) => (
|
||||
<tr key={row.id} className="hover:bg-gray-50 dark:hover:bg-gray-800/50">
|
||||
<td className="px-4 py-3 text-sm font-medium">{row.name}</td>
|
||||
<td className="px-4 py-3">
|
||||
<span className={cn('text-sm font-medium', getStatusColor(row.status))}>
|
||||
{row.status}
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-4 py-3 text-sm">{row.priority}</td>
|
||||
<td className="px-4 py-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="flex-1 bg-gray-200 dark:bg-gray-700 rounded-full h-2">
|
||||
<div
|
||||
className="bg-cyan-500 h-2 rounded-full transition-all"
|
||||
style={{ width: `${row.progress}%` }}
|
||||
/>
|
||||
</div>
|
||||
<span className="text-xs text-gray-500 dark:text-gray-400 w-8">
|
||||
{row.progress}%
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-4 py-3">
|
||||
<Button variant="ghost" size="sm">
|
||||
<MoreVertical className="w-4 h-4" />
|
||||
</Button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// Centered Form Template Component
|
||||
const CenteredFormTemplate = () => {
|
||||
return (
|
||||
<div className="min-h-64 flex items-center justify-center p-8">
|
||||
<Card size="lg" glowColor="purple" className="w-full max-w-md">
|
||||
<div className="text-center mb-6">
|
||||
<h2 className="text-2xl font-bold mb-2">Create Project</h2>
|
||||
<p className="text-gray-600 dark:text-gray-400">
|
||||
Enter the details for your new project
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<form className="space-y-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-2">Project Name</label>
|
||||
<input
|
||||
type="text"
|
||||
className={cn(
|
||||
"w-full px-3 py-2 rounded-md text-sm",
|
||||
glassmorphism.background.subtle,
|
||||
glassmorphism.border.default,
|
||||
glassmorphism.border.focus,
|
||||
"transition-all duration-200"
|
||||
)}
|
||||
placeholder="Enter project name"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-2">Description</label>
|
||||
<textarea
|
||||
className={cn(
|
||||
"w-full px-3 py-2 rounded-md text-sm h-20 resize-none",
|
||||
glassmorphism.background.subtle,
|
||||
glassmorphism.border.default,
|
||||
glassmorphism.border.focus,
|
||||
"transition-all duration-200"
|
||||
)}
|
||||
placeholder="Describe your project"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-2">Priority</label>
|
||||
<select
|
||||
className={cn(
|
||||
"w-full px-3 py-2 rounded-md text-sm",
|
||||
glassmorphism.background.subtle,
|
||||
glassmorphism.border.default,
|
||||
glassmorphism.border.focus,
|
||||
"transition-all duration-200"
|
||||
)}
|
||||
>
|
||||
<option>High</option>
|
||||
<option>Medium</option>
|
||||
<option>Low</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-3 pt-4">
|
||||
<Button variant="outline" className="flex-1">
|
||||
Cancel
|
||||
</Button>
|
||||
<Button className="flex-1">
|
||||
Create Project
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const LAYOUT_TEMPLATES: LayoutTemplate[] = [
|
||||
{
|
||||
id: 'dashboard',
|
||||
name: 'Dashboard Layout',
|
||||
description: 'Grid-based dashboard with stats cards and content sections',
|
||||
usage: 'Admin panels, analytics dashboards, overview pages',
|
||||
component: DashboardTemplate
|
||||
},
|
||||
{
|
||||
id: 'sidebar',
|
||||
name: 'Sidebar Layout',
|
||||
description: 'Collapsible sidebar with main content area (like ProjectsView)',
|
||||
usage: 'Applications with navigation, project management, file browsers',
|
||||
component: SidebarTemplate
|
||||
},
|
||||
{
|
||||
id: 'table',
|
||||
name: 'Table View Layout',
|
||||
description: 'Data table with actions and filters',
|
||||
usage: 'Data management, lists, admin interfaces',
|
||||
component: TableViewTemplate
|
||||
},
|
||||
{
|
||||
id: 'form',
|
||||
name: 'Centered Form Layout',
|
||||
description: 'Single focus form with centered alignment',
|
||||
usage: 'Login forms, creation dialogs, focused input workflows',
|
||||
component: CenteredFormTemplate
|
||||
}
|
||||
];
|
||||
|
||||
const generateCode = (templateId: string) => {
|
||||
const template = LAYOUT_TEMPLATES.find(t => t.id === templateId);
|
||||
if (!template) return '';
|
||||
|
||||
const codeExamples = {
|
||||
dashboard: `import { Card } from '@/features/ui/primitives/card';
|
||||
import { Button } from '@/features/ui/primitives/button';
|
||||
|
||||
/**
|
||||
* 🤖 AI CONTEXT: Dashboard Layout Pattern
|
||||
*
|
||||
* PURPOSE: Grid-based layout for displaying metrics and overview data
|
||||
* WHEN TO USE: Admin dashboards, analytics views, KPI displays
|
||||
* WHEN NOT TO USE: Forms, detailed content pages, navigation-heavy interfaces
|
||||
*
|
||||
* STRUCTURE:
|
||||
* - Header with title and primary action
|
||||
* - Stats cards in responsive grid (1->2->4 columns)
|
||||
* - Content sections in asymmetric grid (2:1 ratio typical)
|
||||
*
|
||||
* RESPONSIVE BEHAVIOR:
|
||||
* - Mobile: Single column stack
|
||||
* - Tablet: 2-column stats, stacked content
|
||||
* - Desktop: 4-column stats, side-by-side content
|
||||
*/
|
||||
|
||||
export const DashboardLayout = () => {
|
||||
return (
|
||||
<div className="p-4 space-y-4">
|
||||
{/* Header Section */}
|
||||
<div className="flex justify-between items-center">
|
||||
<h1 className="text-2xl font-bold">Dashboard</h1>
|
||||
<Button>Primary Action</Button>
|
||||
</div>
|
||||
|
||||
{/* Stats Grid */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
{statsData.map((stat) => (
|
||||
<Card key={stat.id} glowColor={stat.color} className="p-4">
|
||||
<h3 className="text-sm font-medium text-gray-600 dark:text-gray-400">
|
||||
{stat.title}
|
||||
</h3>
|
||||
<p className="text-2xl font-bold mt-1">{stat.value}</p>
|
||||
<p className="text-xs text-gray-500 mt-1">{stat.change}</p>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Content Grid */}
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-4">
|
||||
<Card className="lg:col-span-2 p-4">
|
||||
{/* Main content area */}
|
||||
</Card>
|
||||
<Card className="p-4">
|
||||
{/* Sidebar content */}
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};`,
|
||||
|
||||
sidebar: `import { useState } from 'react';
|
||||
import { Card } from '@/features/ui/primitives/card';
|
||||
import { Button } from '@/features/ui/primitives/button';
|
||||
import { ChevronLeft, ChevronRight } from 'lucide-react';
|
||||
|
||||
/**
|
||||
* 🤖 AI CONTEXT: Sidebar Layout Pattern
|
||||
*
|
||||
* PURPOSE: Navigation-focused layout with collapsible sidebar
|
||||
* WHEN TO USE: File browsers, project management, settings pages
|
||||
* WHEN NOT TO USE: Simple content pages, forms, dashboards
|
||||
*
|
||||
* FEATURES:
|
||||
* - Collapsible sidebar (280px -> 60px)
|
||||
* - Responsive behavior (sidebar -> top nav on mobile)
|
||||
* - Smooth transitions and state preservation
|
||||
* - Keyboard navigation support
|
||||
*
|
||||
* ACCESSIBILITY:
|
||||
* - ARIA labels for collapse state
|
||||
* - Keyboard shortcuts (Ctrl+\\)
|
||||
* - Focus management on state change
|
||||
*/
|
||||
|
||||
export const SidebarLayout = () => {
|
||||
const [collapsed, setCollapsed] = useState(false);
|
||||
|
||||
return (
|
||||
<div className="flex h-full">
|
||||
{/* Sidebar */}
|
||||
<div className={cn(
|
||||
"transition-all duration-300",
|
||||
collapsed ? "w-16" : "w-64"
|
||||
)}>
|
||||
<Card className="h-full p-4 rounded-none border-r">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => setCollapsed(!collapsed)}
|
||||
className="mb-4"
|
||||
>
|
||||
{collapsed ? <ChevronRight /> : <ChevronLeft />}
|
||||
</Button>
|
||||
|
||||
{/* Sidebar content */}
|
||||
<nav className="space-y-2">
|
||||
{navigationItems.map((item) => (
|
||||
<Card
|
||||
key={item.id}
|
||||
className={cn(
|
||||
"p-3 cursor-pointer transition-all",
|
||||
collapsed && "p-2"
|
||||
)}
|
||||
>
|
||||
{collapsed ? (
|
||||
<div className="w-8 h-8 flex items-center justify-center">
|
||||
{item.icon}
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div className="flex items-center gap-3">
|
||||
{item.icon}
|
||||
<span>{item.label}</span>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</Card>
|
||||
))}
|
||||
</nav>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* Main Content */}
|
||||
<div className="flex-1 p-6">
|
||||
{/* Your main content */}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};`,
|
||||
|
||||
table: `import { Card } from '@/features/ui/primitives/card';
|
||||
import { Button } from '@/features/ui/primitives/button';
|
||||
|
||||
/**
|
||||
* 🤖 AI CONTEXT: Table View Layout Pattern
|
||||
*
|
||||
* PURPOSE: Data-heavy interface with filtering and actions
|
||||
* WHEN TO USE: Admin interfaces, data management, list views
|
||||
* WHEN NOT TO USE: Dashboards, forms, content-focused pages
|
||||
*
|
||||
* FEATURES:
|
||||
* - Responsive table with horizontal scroll
|
||||
* - Action buttons in header and rows
|
||||
* - Sortable columns and filtering
|
||||
* - Pagination support
|
||||
*
|
||||
* RESPONSIVE BEHAVIOR:
|
||||
* - Desktop: Full table display
|
||||
* - Tablet: Horizontal scroll for wide tables
|
||||
* - Mobile: Card-based list view (transform table to cards)
|
||||
*/
|
||||
|
||||
export const TableViewLayout = () => {
|
||||
return (
|
||||
<div className="p-4 space-y-4">
|
||||
{/* Header with Actions */}
|
||||
<div className="flex justify-between items-center">
|
||||
<h1 className="text-2xl font-bold">Data Table</h1>
|
||||
<div className="flex gap-2">
|
||||
<Button variant="outline">Filter</Button>
|
||||
<Button>Add Item</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Table Container */}
|
||||
<Card className="overflow-hidden">
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full">
|
||||
<thead className="bg-gray-50 dark:bg-gray-800/50">
|
||||
<tr>
|
||||
{columns.map((column) => (
|
||||
<th
|
||||
key={column.key}
|
||||
className="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase"
|
||||
>
|
||||
{column.label}
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-gray-200 dark:divide-gray-700">
|
||||
{data.map((row) => (
|
||||
<tr key={row.id} className="hover:bg-gray-50 dark:hover:bg-gray-800/50">
|
||||
{columns.map((column) => (
|
||||
<td key={column.key} className="px-4 py-3 text-sm">
|
||||
{row[column.key]}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
};`,
|
||||
|
||||
form: `import { Card } from '@/features/ui/primitives/card';
|
||||
import { Button } from '@/features/ui/primitives/button';
|
||||
import { glassmorphism, cn } from '@/features/ui/primitives/styles';
|
||||
|
||||
/**
|
||||
* 🤖 AI CONTEXT: Centered Form Layout Pattern
|
||||
*
|
||||
* PURPOSE: Single-focus form interface with minimal distractions
|
||||
* WHEN TO USE: Login/signup, creation modals, settings forms
|
||||
* WHEN NOT TO USE: Complex multi-step forms, data entry workflows
|
||||
*
|
||||
* FEATURES:
|
||||
* - Centered card with appropriate max-width
|
||||
* - Glassmorphism form controls
|
||||
* - Clear visual hierarchy
|
||||
* - Consistent spacing and typography
|
||||
*
|
||||
* FORM VALIDATION:
|
||||
* - Real-time validation feedback
|
||||
* - Error states with glassmorphism styling
|
||||
* - Success states with appropriate colors
|
||||
* - Accessibility-compliant error messages
|
||||
*/
|
||||
|
||||
export const CenteredFormLayout = () => {
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center p-8">
|
||||
<Card size="lg" glowColor="purple" className="w-full max-w-md">
|
||||
<div className="text-center mb-6">
|
||||
<h2 className="text-2xl font-bold mb-2">Form Title</h2>
|
||||
<p className="text-gray-600 dark:text-gray-400">
|
||||
Brief description of the form purpose
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<form className="space-y-4">
|
||||
{formFields.map((field) => (
|
||||
<div key={field.name}>
|
||||
<label className="block text-sm font-medium mb-2">
|
||||
{field.label}
|
||||
</label>
|
||||
<input
|
||||
type={field.type}
|
||||
className={cn(
|
||||
"w-full px-3 py-2 rounded-md text-sm",
|
||||
glassmorphism.background.subtle,
|
||||
glassmorphism.border.default,
|
||||
glassmorphism.border.focus,
|
||||
"transition-all duration-200"
|
||||
)}
|
||||
placeholder={field.placeholder}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
|
||||
<div className="flex gap-3 pt-4">
|
||||
<Button variant="outline" className="flex-1">
|
||||
Cancel
|
||||
</Button>
|
||||
<Button className="flex-1">
|
||||
Submit
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
};`
|
||||
};
|
||||
|
||||
return codeExamples[templateId as keyof typeof codeExamples] || '';
|
||||
};
|
||||
|
||||
export const LayoutsPattern = () => {
|
||||
const [selectedTemplate, setSelectedTemplate] = useState<string>('dashboard');
|
||||
|
||||
const currentTemplate = LAYOUT_TEMPLATES.find(t => t.id === selectedTemplate);
|
||||
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold mb-4">Layout Patterns</h2>
|
||||
<p className="text-gray-600 dark:text-gray-400 mb-6">
|
||||
Reusable layout templates for common application structures and workflows.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Template Selector */}
|
||||
<Card className="p-6 max-w-none">
|
||||
<h3 className="text-lg font-semibold mb-4">Select Layout Template</h3>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
{LAYOUT_TEMPLATES.map((template) => (
|
||||
<Card
|
||||
key={template.id}
|
||||
className={cn(
|
||||
"p-4 cursor-pointer transition-all duration-200",
|
||||
selectedTemplate === template.id
|
||||
? "border-cyan-500 shadow-[0_0_10px_rgba(34,211,238,0.3)]"
|
||||
: "hover:shadow-lg"
|
||||
)}
|
||||
onClick={() => setSelectedTemplate(template.id)}
|
||||
>
|
||||
<h4 className="font-medium mb-2">{template.name}</h4>
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400 mb-2">
|
||||
{template.description}
|
||||
</p>
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400">
|
||||
<strong>Use for:</strong> {template.usage}
|
||||
</p>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* Live Preview */}
|
||||
{currentTemplate && (
|
||||
<Card className="p-6 max-w-none">
|
||||
<h3 className="text-lg font-semibold mb-4">
|
||||
{currentTemplate.name} - Live Preview
|
||||
</h3>
|
||||
<LivePreview minHeight="320px">
|
||||
<div className="w-full max-w-4xl">
|
||||
<currentTemplate.component />
|
||||
</div>
|
||||
</LivePreview>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{/* Generated Code */}
|
||||
{currentTemplate && (
|
||||
<Card className="p-6 max-w-none">
|
||||
<h3 className="text-lg font-semibold mb-4">Generated Code</h3>
|
||||
<CodeDisplay
|
||||
code={generateCode(selectedTemplate)}
|
||||
|
||||
showLineNumbers
|
||||
/>
|
||||
</Card>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,866 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Card } from '@/features/ui/primitives/card';
|
||||
import { Button } from '@/features/ui/primitives/button';
|
||||
import { CodeDisplay } from '../shared/CodeDisplay';
|
||||
import { cn, glassmorphism } from '@/features/ui/primitives/styles';
|
||||
import {
|
||||
Home,
|
||||
Settings,
|
||||
User,
|
||||
FileText,
|
||||
BarChart,
|
||||
Menu,
|
||||
ChevronDown,
|
||||
ChevronRight,
|
||||
Search,
|
||||
Bell
|
||||
} from 'lucide-react';
|
||||
|
||||
interface NavigationPattern {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
usage: string;
|
||||
component: React.ComponentType;
|
||||
}
|
||||
|
||||
// Pill Navigation Demo
|
||||
const PillNavigationDemo = () => {
|
||||
const [activeTab, setActiveTab] = useState('dashboard');
|
||||
|
||||
const tabs = [
|
||||
{ id: 'dashboard', label: 'Dashboard', icon: <Home className="w-4 h-4" /> },
|
||||
{ id: 'projects', label: 'Projects', icon: <FileText className="w-4 h-4" /> },
|
||||
{ id: 'analytics', label: 'Analytics', icon: <BarChart className="w-4 h-4" /> },
|
||||
{ id: 'settings', label: 'Settings', icon: <Settings className="w-4 h-4" /> }
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
{/* Standard Pill Navigation */}
|
||||
<div>
|
||||
<h4 className="font-medium mb-3">Standard Pills</h4>
|
||||
<div className="backdrop-blur-sm bg-white/40 dark:bg-white/5 border border-white/30 dark:border-white/15 rounded-full p-1 shadow-lg inline-flex">
|
||||
{tabs.map((tab) => (
|
||||
<button
|
||||
key={tab.id}
|
||||
onClick={() => setActiveTab(tab.id)}
|
||||
className={cn(
|
||||
"flex items-center gap-2 px-4 py-2 rounded-full transition-all duration-200 text-sm font-medium",
|
||||
activeTab === tab.id
|
||||
? "text-white border border-cyan-400 shadow-[0_0_8px_rgba(34,211,238,0.3)] dark:shadow-[0_0_10px_rgba(34,211,238,0.5)]"
|
||||
: "text-gray-700 dark:text-gray-300 hover:bg-white/20 dark:hover:bg-white/5"
|
||||
)}
|
||||
>
|
||||
{tab.icon}
|
||||
{tab.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Icon-only Pills */}
|
||||
<div>
|
||||
<h4 className="font-medium mb-3">Icon Pills</h4>
|
||||
<div className="backdrop-blur-sm bg-white/40 dark:bg-white/5 border border-white/30 dark:border-white/15 rounded-full p-1 shadow-lg inline-flex">
|
||||
{tabs.map((tab) => (
|
||||
<button
|
||||
key={tab.id}
|
||||
onClick={() => setActiveTab(tab.id)}
|
||||
className={cn(
|
||||
"p-3 rounded-full transition-all duration-200",
|
||||
activeTab === tab.id
|
||||
? "text-white border border-cyan-400 shadow-[0_0_8px_rgba(34,211,238,0.3)]"
|
||||
: "text-gray-700 dark:text-gray-300 hover:bg-white/20 dark:hover:bg-white/5"
|
||||
)}
|
||||
title={tab.label}
|
||||
>
|
||||
{tab.icon}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Vertical Pills */}
|
||||
<div>
|
||||
<h4 className="font-medium mb-3">Vertical Pills</h4>
|
||||
<div className="backdrop-blur-sm bg-white/40 dark:bg-white/5 border border-white/30 dark:border-white/15 rounded-2xl p-1 shadow-lg inline-flex flex-col">
|
||||
{tabs.map((tab) => (
|
||||
<button
|
||||
key={tab.id}
|
||||
onClick={() => setActiveTab(tab.id)}
|
||||
className={cn(
|
||||
"flex items-center gap-2 px-4 py-2 rounded-xl transition-all duration-200 text-sm font-medium min-w-[120px]",
|
||||
activeTab === tab.id
|
||||
? "text-white border border-cyan-400 shadow-[0_0_8px_rgba(34,211,238,0.3)]"
|
||||
: "text-gray-700 dark:text-gray-300 hover:bg-white/20 dark:hover:bg-white/5"
|
||||
)}
|
||||
>
|
||||
{tab.icon}
|
||||
{tab.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// Sidebar Navigation Demo
|
||||
const SidebarNavigationDemo = () => {
|
||||
const [collapsed, setCollapsed] = useState(false);
|
||||
const [activeItem, setActiveItem] = useState('dashboard');
|
||||
|
||||
const navItems = [
|
||||
{
|
||||
id: 'dashboard',
|
||||
label: 'Dashboard',
|
||||
icon: <Home className="w-5 h-5" />,
|
||||
hasSubmenu: false
|
||||
},
|
||||
{
|
||||
id: 'projects',
|
||||
label: 'Projects',
|
||||
icon: <FileText className="w-5 h-5" />,
|
||||
hasSubmenu: true,
|
||||
submenu: ['All Projects', 'Active', 'Archived']
|
||||
},
|
||||
{
|
||||
id: 'analytics',
|
||||
label: 'Analytics',
|
||||
icon: <BarChart className="w-5 h-5" />,
|
||||
hasSubmenu: false
|
||||
},
|
||||
{
|
||||
id: 'team',
|
||||
label: 'Team',
|
||||
icon: <User className="w-5 h-5" />,
|
||||
hasSubmenu: true,
|
||||
submenu: ['Members', 'Roles', 'Permissions']
|
||||
},
|
||||
{
|
||||
id: 'settings',
|
||||
label: 'Settings',
|
||||
icon: <Settings className="w-5 h-5" />,
|
||||
hasSubmenu: false
|
||||
}
|
||||
];
|
||||
|
||||
const [expandedItems, setExpandedItems] = useState<string[]>([]);
|
||||
|
||||
const toggleExpanded = (itemId: string) => {
|
||||
setExpandedItems(prev =>
|
||||
prev.includes(itemId)
|
||||
? prev.filter(id => id !== itemId)
|
||||
: [...prev, itemId]
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="h-96 flex">
|
||||
{/* Sidebar */}
|
||||
<Card className={cn(
|
||||
"h-full transition-all duration-300 rounded-r-none border-r-0",
|
||||
collapsed ? "w-16" : "w-64"
|
||||
)}>
|
||||
<div className="p-4">
|
||||
{/* Toggle Button */}
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => setCollapsed(!collapsed)}
|
||||
className="mb-4 w-full justify-start"
|
||||
>
|
||||
<Menu className="w-4 h-4" />
|
||||
{!collapsed && <span className="ml-2">Menu</span>}
|
||||
</Button>
|
||||
|
||||
{/* Navigation Items */}
|
||||
<nav className="space-y-1">
|
||||
{navItems.map((item) => (
|
||||
<div key={item.id}>
|
||||
<button
|
||||
onClick={() => {
|
||||
setActiveItem(item.id);
|
||||
if (item.hasSubmenu && !collapsed) {
|
||||
toggleExpanded(item.id);
|
||||
}
|
||||
}}
|
||||
className={cn(
|
||||
"w-full flex items-center gap-3 px-3 py-2 rounded-lg transition-all duration-200 text-left",
|
||||
activeItem === item.id
|
||||
? cn(glassmorphism.background.cyan, "text-cyan-700 dark:text-cyan-300")
|
||||
: "hover:bg-gray-100 dark:hover:bg-gray-800"
|
||||
)}
|
||||
>
|
||||
{item.icon}
|
||||
{!collapsed && (
|
||||
<>
|
||||
<span className="flex-1 text-sm font-medium">
|
||||
{item.label}
|
||||
</span>
|
||||
{item.hasSubmenu && (
|
||||
<ChevronDown
|
||||
className={cn(
|
||||
"w-4 h-4 transition-transform",
|
||||
expandedItems.includes(item.id) && "rotate-180"
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
|
||||
{/* Submenu */}
|
||||
{item.hasSubmenu && !collapsed && expandedItems.includes(item.id) && (
|
||||
<div className="ml-8 mt-1 space-y-1">
|
||||
{item.submenu?.map((subItem) => (
|
||||
<button
|
||||
key={subItem}
|
||||
className="w-full text-left px-3 py-1 text-sm text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100 rounded"
|
||||
>
|
||||
{subItem}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</nav>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* Main Content */}
|
||||
<Card className="flex-1 rounded-l-none border-l-0 p-6">
|
||||
<div className="text-center text-gray-500 dark:text-gray-400">
|
||||
<h3 className="font-medium mb-2">Main Content</h3>
|
||||
<p className="text-sm">Active page: {activeItem}</p>
|
||||
<p className="text-xs mt-2">Sidebar: {collapsed ? 'Collapsed' : 'Expanded'}</p>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// Top Navigation Demo
|
||||
const TopNavigationDemo = () => {
|
||||
const [activeNav, setActiveNav] = useState('dashboard');
|
||||
|
||||
const navItems = [
|
||||
{ id: 'dashboard', label: 'Dashboard' },
|
||||
{ id: 'projects', label: 'Projects' },
|
||||
{ id: 'team', label: 'Team' },
|
||||
{ id: 'analytics', label: 'Analytics' }
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
{/* Top Navigation Bar */}
|
||||
<Card className="p-4">
|
||||
<div className="flex items-center justify-between">
|
||||
{/* Logo/Brand */}
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-8 h-8 bg-gradient-to-br from-cyan-500 to-purple-500 rounded-lg" />
|
||||
<span className="font-bold text-lg">Archon</span>
|
||||
</div>
|
||||
|
||||
{/* Navigation Links */}
|
||||
<nav className="flex items-center gap-1">
|
||||
{navItems.map((item) => (
|
||||
<button
|
||||
key={item.id}
|
||||
onClick={() => setActiveNav(item.id)}
|
||||
className={cn(
|
||||
"px-4 py-2 rounded-lg text-sm font-medium transition-all duration-200",
|
||||
activeNav === item.id
|
||||
? cn(glassmorphism.background.cyan, "text-cyan-700 dark:text-cyan-300")
|
||||
: "text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100 hover:bg-gray-100 dark:hover:bg-gray-800"
|
||||
)}
|
||||
>
|
||||
{item.label}
|
||||
</button>
|
||||
))}
|
||||
</nav>
|
||||
|
||||
{/* User Actions */}
|
||||
<div className="flex items-center gap-2">
|
||||
<Button variant="ghost" size="sm">
|
||||
<Search className="w-4 h-4" />
|
||||
</Button>
|
||||
<Button variant="ghost" size="sm">
|
||||
<Bell className="w-4 h-4" />
|
||||
</Button>
|
||||
<Button variant="ghost" size="sm">
|
||||
<User className="w-4 h-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* Content Area */}
|
||||
<Card className="h-48 p-6">
|
||||
<div className="text-center text-gray-500 dark:text-gray-400">
|
||||
<h3 className="font-medium mb-2">Page Content</h3>
|
||||
<p className="text-sm">Currently viewing: {activeNav}</p>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// Breadcrumb Navigation Demo
|
||||
const BreadcrumbDemo = () => {
|
||||
const breadcrumbs = [
|
||||
{ label: 'Home', href: '/' },
|
||||
{ label: 'Projects', href: '/projects' },
|
||||
{ label: 'Project Alpha', href: '/projects/alpha' },
|
||||
{ label: 'Settings', href: '/projects/alpha/settings' }
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
{/* Standard Breadcrumbs */}
|
||||
<div>
|
||||
<h4 className="font-medium mb-3">Standard Breadcrumbs</h4>
|
||||
<nav className="flex items-center space-x-2 text-sm">
|
||||
{breadcrumbs.map((crumb, index) => (
|
||||
<React.Fragment key={crumb.href}>
|
||||
<button
|
||||
className={cn(
|
||||
"hover:text-cyan-600 dark:hover:text-cyan-400 transition-colors",
|
||||
index === breadcrumbs.length - 1
|
||||
? "text-gray-900 dark:text-gray-100 font-medium"
|
||||
: "text-gray-500 dark:text-gray-400"
|
||||
)}
|
||||
>
|
||||
{crumb.label}
|
||||
</button>
|
||||
{index < breadcrumbs.length - 1 && (
|
||||
<ChevronRight className="w-4 h-4 text-gray-400" />
|
||||
)}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
{/* Pill Breadcrumbs */}
|
||||
<div>
|
||||
<h4 className="font-medium mb-3">Pill Breadcrumbs</h4>
|
||||
<nav className="flex items-center space-x-1">
|
||||
{breadcrumbs.map((crumb, index) => (
|
||||
<React.Fragment key={crumb.href}>
|
||||
<button
|
||||
className={cn(
|
||||
"px-3 py-1 rounded-full text-sm transition-all duration-200",
|
||||
index === breadcrumbs.length - 1
|
||||
? cn(glassmorphism.background.cyan, "text-cyan-700 dark:text-cyan-300 font-medium")
|
||||
: "text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800"
|
||||
)}
|
||||
>
|
||||
{crumb.label}
|
||||
</button>
|
||||
{index < breadcrumbs.length - 1 && (
|
||||
<ChevronRight className="w-4 h-4 text-gray-400 mx-1" />
|
||||
)}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
{/* Compact Breadcrumbs */}
|
||||
<div>
|
||||
<h4 className="font-medium mb-3">Compact (Mobile)</h4>
|
||||
<nav className="flex items-center space-x-2 text-sm">
|
||||
<button className="text-gray-500 dark:text-gray-400 hover:text-cyan-600 dark:hover:text-cyan-400">
|
||||
...
|
||||
</button>
|
||||
<ChevronRight className="w-4 h-4 text-gray-400" />
|
||||
<button className="text-gray-500 dark:text-gray-400 hover:text-cyan-600 dark:hover:text-cyan-400">
|
||||
{breadcrumbs[breadcrumbs.length - 2]?.label}
|
||||
</button>
|
||||
<ChevronRight className="w-4 h-4 text-gray-400" />
|
||||
<span className="text-gray-900 dark:text-gray-100 font-medium">
|
||||
{breadcrumbs[breadcrumbs.length - 1]?.label}
|
||||
</span>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const NAVIGATION_PATTERNS: NavigationPattern[] = [
|
||||
{
|
||||
id: 'pills',
|
||||
name: 'Pill Navigation',
|
||||
description: 'Modern glassmorphism pill-style navigation tabs',
|
||||
usage: 'Primary navigation, tab switching, section selection',
|
||||
component: PillNavigationDemo
|
||||
},
|
||||
{
|
||||
id: 'sidebar',
|
||||
name: 'Sidebar Navigation',
|
||||
description: 'Collapsible sidebar with hierarchical navigation',
|
||||
usage: 'Application navigation, admin panels, file browsers',
|
||||
component: SidebarNavigationDemo
|
||||
},
|
||||
{
|
||||
id: 'topnav',
|
||||
name: 'Top Navigation',
|
||||
description: 'Horizontal navigation bar with branding and actions',
|
||||
usage: 'Website headers, application top bars, main navigation',
|
||||
component: TopNavigationDemo
|
||||
},
|
||||
{
|
||||
id: 'breadcrumbs',
|
||||
name: 'Breadcrumb Navigation',
|
||||
description: 'Hierarchical path navigation showing current location',
|
||||
usage: 'Deep navigation, file paths, multi-level applications',
|
||||
component: BreadcrumbDemo
|
||||
}
|
||||
];
|
||||
|
||||
const generateCode = (patternId: string) => {
|
||||
const codeExamples = {
|
||||
pills: `import { useState } from 'react';
|
||||
import { cn } from '@/features/ui/primitives/styles';
|
||||
|
||||
/**
|
||||
* 🤖 AI CONTEXT: Pill Navigation Pattern
|
||||
*
|
||||
* PURPOSE: Modern tab-style navigation with glassmorphism design
|
||||
* WHEN TO USE: Primary navigation, tab switching, section selection
|
||||
* WHEN NOT TO USE: Deep hierarchies, complex menus, long lists
|
||||
*
|
||||
* VARIANTS:
|
||||
* - Standard: Icon + text for desktop
|
||||
* - Icon-only: Compact for mobile/narrow spaces
|
||||
* - Vertical: Sidebar-style pill navigation
|
||||
*
|
||||
* ACCESSIBILITY:
|
||||
* - Use semantic nav element
|
||||
* - Include ARIA states (aria-current)
|
||||
* - Support keyboard navigation
|
||||
* - Provide tooltips for icon-only variants
|
||||
*/
|
||||
|
||||
interface Tab {
|
||||
id: string;
|
||||
label: string;
|
||||
icon?: React.ReactNode;
|
||||
}
|
||||
|
||||
export const PillNavigation = ({
|
||||
tabs,
|
||||
activeTab,
|
||||
onTabChange,
|
||||
variant = 'standard'
|
||||
}: {
|
||||
tabs: Tab[];
|
||||
activeTab: string;
|
||||
onTabChange: (tabId: string) => void;
|
||||
variant?: 'standard' | 'icon-only' | 'vertical';
|
||||
}) => {
|
||||
return (
|
||||
<nav
|
||||
className={cn(
|
||||
"backdrop-blur-sm bg-white/40 dark:bg-white/5",
|
||||
"border border-white/30 dark:border-white/15 shadow-lg",
|
||||
variant === 'vertical' ? "rounded-2xl p-1 inline-flex flex-col" : "rounded-full p-1 inline-flex"
|
||||
)}
|
||||
>
|
||||
{tabs.map((tab) => (
|
||||
<button
|
||||
key={tab.id}
|
||||
onClick={() => onTabChange(tab.id)}
|
||||
className={cn(
|
||||
"transition-all duration-200 text-sm font-medium",
|
||||
variant === 'vertical' ? "rounded-xl min-w-[120px]" : "rounded-full",
|
||||
variant === 'icon-only' ? "p-3" : "px-4 py-2",
|
||||
"flex items-center gap-2",
|
||||
activeTab === tab.id
|
||||
? "text-white border border-cyan-400 shadow-[0_0_8px_rgba(34,211,238,0.3)] dark:shadow-[0_0_10px_rgba(34,211,238,0.5)]"
|
||||
: "text-gray-700 dark:text-gray-300 hover:bg-white/20 dark:hover:bg-white/5"
|
||||
)}
|
||||
aria-current={activeTab === tab.id ? 'page' : undefined}
|
||||
title={variant === 'icon-only' ? tab.label : undefined}
|
||||
>
|
||||
{tab.icon}
|
||||
{variant !== 'icon-only' && tab.label}
|
||||
</button>
|
||||
))}
|
||||
</nav>
|
||||
);
|
||||
};`,
|
||||
|
||||
sidebar: `import { useState } from 'react';
|
||||
import { Card } from '@/features/ui/primitives/card';
|
||||
import { Button } from '@/features/ui/primitives/button';
|
||||
import { ChevronDown, ChevronLeft, ChevronRight } from 'lucide-react';
|
||||
import { cn, glassmorphism } from '@/features/ui/primitives/styles';
|
||||
|
||||
/**
|
||||
* 🤖 AI CONTEXT: Sidebar Navigation Pattern
|
||||
*
|
||||
* PURPOSE: Hierarchical navigation with collapsible sidebar
|
||||
* WHEN TO USE: Complex applications, admin panels, file systems
|
||||
* WHEN NOT TO USE: Simple sites, mobile-first designs, shallow navigation
|
||||
*
|
||||
* FEATURES:
|
||||
* - Collapsible width (280px -> 60px)
|
||||
* - Submenu support with expansion
|
||||
* - Active state highlighting
|
||||
* - Smooth transitions
|
||||
*
|
||||
* RESPONSIVE BEHAVIOR:
|
||||
* - Desktop: Side-by-side layout
|
||||
* - Tablet: Overlay sidebar
|
||||
* - Mobile: Full-screen drawer
|
||||
*/
|
||||
|
||||
interface NavItem {
|
||||
id: string;
|
||||
label: string;
|
||||
icon: React.ReactNode;
|
||||
hasSubmenu?: boolean;
|
||||
submenu?: string[];
|
||||
}
|
||||
|
||||
export const SidebarNavigation = ({
|
||||
items,
|
||||
activeItem,
|
||||
onItemClick,
|
||||
collapsed = false,
|
||||
onToggleCollapse
|
||||
}: {
|
||||
items: NavItem[];
|
||||
activeItem: string;
|
||||
onItemClick: (itemId: string) => void;
|
||||
collapsed?: boolean;
|
||||
onToggleCollapse: () => void;
|
||||
}) => {
|
||||
const [expandedItems, setExpandedItems] = useState<string[]>([]);
|
||||
|
||||
const toggleExpanded = (itemId: string) => {
|
||||
setExpandedItems(prev =>
|
||||
prev.includes(itemId)
|
||||
? prev.filter(id => id !== itemId)
|
||||
: [...prev, itemId]
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Card className={cn(
|
||||
"h-full transition-all duration-300",
|
||||
collapsed ? "w-16" : "w-64"
|
||||
)}>
|
||||
<div className="p-4">
|
||||
{/* Toggle Button */}
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={onToggleCollapse}
|
||||
className="mb-4 w-full justify-start"
|
||||
>
|
||||
{collapsed ? <ChevronRight /> : <ChevronLeft />}
|
||||
{!collapsed && <span className="ml-2">Collapse</span>}
|
||||
</Button>
|
||||
|
||||
{/* Navigation Items */}
|
||||
<nav className="space-y-1">
|
||||
{items.map((item) => (
|
||||
<div key={item.id}>
|
||||
<button
|
||||
onClick={() => {
|
||||
onItemClick(item.id);
|
||||
if (item.hasSubmenu && !collapsed) {
|
||||
toggleExpanded(item.id);
|
||||
}
|
||||
}}
|
||||
className={cn(
|
||||
"w-full flex items-center gap-3 px-3 py-2 rounded-lg",
|
||||
"transition-all duration-200 text-left",
|
||||
activeItem === item.id
|
||||
? cn(glassmorphism.background.cyan, "text-cyan-700 dark:text-cyan-300")
|
||||
: "hover:bg-gray-100 dark:hover:bg-gray-800"
|
||||
)}
|
||||
>
|
||||
{item.icon}
|
||||
{!collapsed && (
|
||||
<>
|
||||
<span className="flex-1 text-sm font-medium">
|
||||
{item.label}
|
||||
</span>
|
||||
{item.hasSubmenu && (
|
||||
<ChevronDown
|
||||
className={cn(
|
||||
"w-4 h-4 transition-transform",
|
||||
expandedItems.includes(item.id) && "rotate-180"
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
|
||||
{/* Submenu */}
|
||||
{item.hasSubmenu && !collapsed && expandedItems.includes(item.id) && (
|
||||
<div className="ml-8 mt-1 space-y-1">
|
||||
{item.submenu?.map((subItem) => (
|
||||
<button
|
||||
key={subItem}
|
||||
className="w-full text-left px-3 py-1 text-sm text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100 rounded"
|
||||
>
|
||||
{subItem}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</nav>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
};`,
|
||||
|
||||
topnav: `import { Card } from '@/features/ui/primitives/card';
|
||||
import { Button } from '@/features/ui/primitives/button';
|
||||
import { Search, Bell, User } from 'lucide-react';
|
||||
import { cn, glassmorphism } from '@/features/ui/primitives/styles';
|
||||
|
||||
/**
|
||||
* 🤖 AI CONTEXT: Top Navigation Pattern
|
||||
*
|
||||
* PURPOSE: Primary horizontal navigation with branding and actions
|
||||
* WHEN TO USE: Website headers, app top bars, main navigation
|
||||
* WHEN NOT TO USE: Deep navigation, complex hierarchies, space-constrained designs
|
||||
*
|
||||
* STRUCTURE:
|
||||
* - Left: Brand/logo
|
||||
* - Center: Primary navigation links
|
||||
* - Right: User actions and utilities
|
||||
*
|
||||
* RESPONSIVE BEHAVIOR:
|
||||
* - Desktop: Full horizontal layout
|
||||
* - Tablet: Collapsed navigation menu
|
||||
* - Mobile: Hamburger menu with drawer
|
||||
*/
|
||||
|
||||
interface NavItem {
|
||||
id: string;
|
||||
label: string;
|
||||
href?: string;
|
||||
}
|
||||
|
||||
export const TopNavigation = ({
|
||||
brand,
|
||||
navItems,
|
||||
activeItem,
|
||||
onNavClick,
|
||||
userActions
|
||||
}: {
|
||||
brand: React.ReactNode;
|
||||
navItems: NavItem[];
|
||||
activeItem: string;
|
||||
onNavClick: (itemId: string) => void;
|
||||
userActions?: React.ReactNode;
|
||||
}) => {
|
||||
return (
|
||||
<Card className="p-4">
|
||||
<div className="flex items-center justify-between">
|
||||
{/* Brand/Logo */}
|
||||
<div className="flex items-center gap-3">
|
||||
{brand}
|
||||
</div>
|
||||
|
||||
{/* Navigation Links */}
|
||||
<nav className="hidden md:flex items-center gap-1">
|
||||
{navItems.map((item) => (
|
||||
<button
|
||||
key={item.id}
|
||||
onClick={() => onNavClick(item.id)}
|
||||
className={cn(
|
||||
"px-4 py-2 rounded-lg text-sm font-medium transition-all duration-200",
|
||||
activeItem === item.id
|
||||
? cn(glassmorphism.background.cyan, "text-cyan-700 dark:text-cyan-300")
|
||||
: "text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100 hover:bg-gray-100 dark:hover:bg-gray-800"
|
||||
)}
|
||||
>
|
||||
{item.label}
|
||||
</button>
|
||||
))}
|
||||
</nav>
|
||||
|
||||
{/* User Actions */}
|
||||
<div className="flex items-center gap-2">
|
||||
{userActions || (
|
||||
<>
|
||||
<Button variant="ghost" size="sm">
|
||||
<Search className="w-4 h-4" />
|
||||
</Button>
|
||||
<Button variant="ghost" size="sm">
|
||||
<Bell className="w-4 h-4" />
|
||||
</Button>
|
||||
<Button variant="ghost" size="sm">
|
||||
<User className="w-4 h-4" />
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
};`,
|
||||
|
||||
breadcrumbs: `import { ChevronRight } from 'lucide-react';
|
||||
import { cn, glassmorphism } from '@/features/ui/primitives/styles';
|
||||
|
||||
/**
|
||||
* 🤖 AI CONTEXT: Breadcrumb Navigation Pattern
|
||||
*
|
||||
* PURPOSE: Hierarchical path navigation showing current location
|
||||
* WHEN TO USE: Deep navigation, file systems, multi-level apps
|
||||
* WHEN NOT TO USE: Shallow navigation, single-page apps, unclear hierarchies
|
||||
*
|
||||
* VARIANTS:
|
||||
* - Standard: Text links with separators
|
||||
* - Pill: Rounded pill-style breadcrumbs
|
||||
* - Compact: Ellipsis for mobile/narrow spaces
|
||||
*
|
||||
* ACCESSIBILITY:
|
||||
* - Use semantic nav element
|
||||
* - Include aria-label for navigation
|
||||
* - Mark current page with aria-current
|
||||
* - Ensure sufficient color contrast
|
||||
*/
|
||||
|
||||
interface Breadcrumb {
|
||||
label: string;
|
||||
href: string;
|
||||
}
|
||||
|
||||
export const BreadcrumbNavigation = ({
|
||||
breadcrumbs,
|
||||
variant = 'standard',
|
||||
maxItems = 4
|
||||
}: {
|
||||
breadcrumbs: Breadcrumb[];
|
||||
variant?: 'standard' | 'pill' | 'compact';
|
||||
maxItems?: number;
|
||||
}) => {
|
||||
const displayCrumbs = variant === 'compact' && breadcrumbs.length > maxItems
|
||||
? [
|
||||
...breadcrumbs.slice(0, 1),
|
||||
{ label: '...', href: '#' },
|
||||
...breadcrumbs.slice(-2)
|
||||
]
|
||||
: breadcrumbs;
|
||||
|
||||
return (
|
||||
<nav aria-label="Breadcrumb" className="flex items-center space-x-1">
|
||||
{displayCrumbs.map((crumb, index) => (
|
||||
<React.Fragment key={crumb.href}>
|
||||
{variant === 'pill' ? (
|
||||
<button
|
||||
className={cn(
|
||||
"px-3 py-1 rounded-full text-sm transition-all duration-200",
|
||||
index === displayCrumbs.length - 1
|
||||
? cn(glassmorphism.background.cyan, "text-cyan-700 dark:text-cyan-300 font-medium")
|
||||
: "text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800"
|
||||
)}
|
||||
aria-current={index === displayCrumbs.length - 1 ? 'page' : undefined}
|
||||
>
|
||||
{crumb.label}
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
className={cn(
|
||||
"hover:text-cyan-600 dark:hover:text-cyan-400 transition-colors text-sm",
|
||||
index === displayCrumbs.length - 1
|
||||
? "text-gray-900 dark:text-gray-100 font-medium"
|
||||
: "text-gray-500 dark:text-gray-400"
|
||||
)}
|
||||
aria-current={index === displayCrumbs.length - 1 ? 'page' : undefined}
|
||||
>
|
||||
{crumb.label}
|
||||
</button>
|
||||
)}
|
||||
|
||||
{index < displayCrumbs.length - 1 && (
|
||||
<ChevronRight className="w-4 h-4 text-gray-400 mx-1" />
|
||||
)}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</nav>
|
||||
);
|
||||
};`
|
||||
};
|
||||
|
||||
return codeExamples[patternId as keyof typeof codeExamples] || '';
|
||||
};
|
||||
|
||||
export const NavigationPattern = () => {
|
||||
const [selectedPattern, setSelectedPattern] = useState<string>('pills');
|
||||
|
||||
const currentPattern = NAVIGATION_PATTERNS.find(p => p.id === selectedPattern);
|
||||
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold mb-4">Navigation Patterns</h2>
|
||||
<p className="text-gray-600 dark:text-gray-400 mb-6">
|
||||
Navigation components including pills, sidebars, top navigation, and breadcrumbs.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Pattern Selector */}
|
||||
<Card className="p-6 max-w-none">
|
||||
<h3 className="text-lg font-semibold mb-4">Select Navigation Pattern</h3>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
{NAVIGATION_PATTERNS.map((pattern) => (
|
||||
<Card
|
||||
key={pattern.id}
|
||||
className={cn(
|
||||
"p-4 cursor-pointer transition-all duration-200",
|
||||
selectedPattern === pattern.id
|
||||
? "border-cyan-500 shadow-[0_0_10px_rgba(34,211,238,0.3)]"
|
||||
: "hover:shadow-lg"
|
||||
)}
|
||||
onClick={() => setSelectedPattern(pattern.id)}
|
||||
>
|
||||
<h4 className="font-medium mb-2">{pattern.name}</h4>
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400 mb-2">
|
||||
{pattern.description}
|
||||
</p>
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400">
|
||||
<strong>Use for:</strong> {pattern.usage}
|
||||
</p>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{/* Live Preview */}
|
||||
{currentPattern && (
|
||||
<Card className="p-6 max-w-none">
|
||||
<h3 className="text-lg font-semibold mb-4">
|
||||
{currentPattern.name} - Examples
|
||||
</h3>
|
||||
<currentPattern.component />
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{/* Generated Code */}
|
||||
{currentPattern && (
|
||||
<Card className="p-6 max-w-none">
|
||||
<h3 className="text-lg font-semibold mb-4">Generated Code</h3>
|
||||
<CodeDisplay
|
||||
code={generateCode(selectedPattern)}
|
||||
|
||||
showLineNumbers
|
||||
/>
|
||||
</Card>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
106
archon-ui-main/src/components/style-guide/shared/CodeDisplay.tsx
Normal file
106
archon-ui-main/src/components/style-guide/shared/CodeDisplay.tsx
Normal file
@@ -0,0 +1,106 @@
|
||||
import { useState } from 'react';
|
||||
import { Check, Copy } from 'lucide-react';
|
||||
import { Button } from '@/features/ui/primitives/button';
|
||||
import { cn } from '@/features/ui/primitives/styles';
|
||||
|
||||
interface CodeDisplayProps {
|
||||
code: string;
|
||||
showLineNumbers?: boolean;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const CodeDisplay = ({
|
||||
code,
|
||||
showLineNumbers = false,
|
||||
className
|
||||
}: CodeDisplayProps) => {
|
||||
const [copied, setCopied] = useState(false);
|
||||
|
||||
const handleCopy = async () => {
|
||||
await navigator.clipboard.writeText(code);
|
||||
setCopied(true);
|
||||
setTimeout(() => setCopied(false), 2000);
|
||||
};
|
||||
|
||||
const lines = code.split('\n');
|
||||
|
||||
// Basic syntax highlighting with regex
|
||||
const highlightCode = (line: string) => {
|
||||
// Escape HTML
|
||||
line = line.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
||||
|
||||
// Keywords
|
||||
line = line.replace(
|
||||
/\b(import|export|from|const|let|var|function|return|if|else|interface|type|class|extends|implements)\b/g,
|
||||
'<span class="text-purple-400">$1</span>'
|
||||
);
|
||||
|
||||
// Strings
|
||||
line = line.replace(
|
||||
/(["'`])([^"'`]*)\1/g,
|
||||
'<span class="text-green-400">$1$2$1</span>'
|
||||
);
|
||||
|
||||
// Comments
|
||||
line = line.replace(
|
||||
/(\/\/.*$|\/\*.*\*\/)/g,
|
||||
'<span class="text-gray-500">$1</span>'
|
||||
);
|
||||
|
||||
// JSX tags
|
||||
line = line.replace(
|
||||
/<([A-Z][A-Za-z0-9]*)(\s|>|\/>)/g,
|
||||
'<<span class="text-cyan-400">$1</span>$2'
|
||||
);
|
||||
|
||||
// Props
|
||||
line = line.replace(
|
||||
/(\w+)=/g,
|
||||
'<span class="text-orange-400">$1</span>='
|
||||
);
|
||||
|
||||
return line;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={cn(
|
||||
"relative rounded-lg overflow-hidden",
|
||||
"bg-gray-900 border border-gray-800",
|
||||
className
|
||||
)}>
|
||||
{/* Copy Button */}
|
||||
<Button
|
||||
size="icon"
|
||||
variant="ghost"
|
||||
onClick={handleCopy}
|
||||
className="absolute top-2 right-2 z-10 text-gray-400 hover:text-white"
|
||||
>
|
||||
{copied ? (
|
||||
<Check className="w-4 h-4 text-green-400" />
|
||||
) : (
|
||||
<Copy className="w-4 h-4" />
|
||||
)}
|
||||
</Button>
|
||||
|
||||
{/* Code Content */}
|
||||
<pre className="p-4 overflow-x-auto">
|
||||
<code className="text-sm font-mono">
|
||||
{lines.map((line, index) => (
|
||||
<div key={index} className="flex">
|
||||
{showLineNumbers && (
|
||||
<span className="text-gray-500 mr-4 select-none w-8 text-right">
|
||||
{index + 1}
|
||||
</span>
|
||||
)}
|
||||
<span
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: highlightCode(line) || ' '
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</code>
|
||||
</pre>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,15 @@
|
||||
import { Card } from '@/features/ui/primitives/card';
|
||||
import { cn } from '@/features/ui/primitives/styles';
|
||||
|
||||
interface ConfigPanelProps {
|
||||
title?: string;
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const ConfigPanel = ({ title, children, className }: ConfigPanelProps) => (
|
||||
<Card className={cn("space-y-4", className)}>
|
||||
{title && <h3 className="font-semibold text-lg mb-2 text-gray-900 dark:text-white">{title}</h3>}
|
||||
{children}
|
||||
</Card>
|
||||
);
|
||||
@@ -0,0 +1,17 @@
|
||||
import { Label } from '@/features/ui/primitives/label';
|
||||
import { cn } from '@/features/ui/primitives/styles';
|
||||
|
||||
interface ConfigRowProps {
|
||||
label: string;
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const ConfigRow = ({ label, children, className }: ConfigRowProps) => (
|
||||
<div className={cn("flex items-center justify-between", className)}>
|
||||
<Label className="text-sm font-medium text-gray-700 dark:text-gray-300">{label}</Label>
|
||||
<div className="flex-shrink-0">
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -0,0 +1,64 @@
|
||||
import { useState } from 'react';
|
||||
import { ZoomIn, ZoomOut, RotateCcw } from 'lucide-react';
|
||||
import { Button } from '@/features/ui/primitives/button';
|
||||
import { cn } from '@/features/ui/primitives/styles';
|
||||
|
||||
interface LivePreviewProps {
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
minHeight?: string;
|
||||
}
|
||||
|
||||
export const LivePreview = ({ children, className, minHeight = "400px" }: LivePreviewProps) => {
|
||||
const [zoom, setZoom] = useState(100);
|
||||
|
||||
return (
|
||||
<div className="relative rounded-lg overflow-hidden">
|
||||
{/* Zoom Controls */}
|
||||
<div className="absolute top-2 right-2 z-10 flex gap-1">
|
||||
<Button
|
||||
size="icon"
|
||||
variant="outline"
|
||||
onClick={() => setZoom(prev => Math.max(50, prev - 25))}
|
||||
>
|
||||
<ZoomOut className="w-4 h-4" />
|
||||
</Button>
|
||||
<Button
|
||||
size="icon"
|
||||
variant="outline"
|
||||
onClick={() => setZoom(100)}
|
||||
>
|
||||
<RotateCcw className="w-4 h-4" />
|
||||
</Button>
|
||||
<Button
|
||||
size="icon"
|
||||
variant="outline"
|
||||
onClick={() => setZoom(prev => Math.min(150, prev + 25))}
|
||||
>
|
||||
<ZoomIn className="w-4 h-4" />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Grid Background */}
|
||||
<div
|
||||
className={cn(
|
||||
"bg-gray-50 dark:bg-gray-900/50",
|
||||
"bg-[linear-gradient(to_right,#8882_1px,transparent_1px),linear-gradient(to_bottom,#8882_1px,transparent_1px)]",
|
||||
"bg-[size:20px_20px]",
|
||||
"p-8 flex items-center justify-center",
|
||||
className
|
||||
)}
|
||||
style={{ minHeight }}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
transform: `scale(${zoom / 100})`,
|
||||
transformOrigin: 'center'
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,91 @@
|
||||
import { useState } from 'react';
|
||||
import { ChevronRight, ChevronDown } from 'lucide-react';
|
||||
import { cn } from '@/features/ui/primitives/styles';
|
||||
|
||||
interface NavigationSection {
|
||||
label: string;
|
||||
items: string[];
|
||||
}
|
||||
|
||||
interface NavigationSidebarProps {
|
||||
sections: NavigationSection[];
|
||||
selectedItem: string;
|
||||
onItemSelect: (item: string) => void;
|
||||
}
|
||||
|
||||
// Default navigation structure for style guide
|
||||
// This can be used when creating a NavigationSidebar instanc
|
||||
|
||||
export const NavigationSidebar: React.FC<NavigationSidebarProps> = ({
|
||||
sections,
|
||||
selectedItem,
|
||||
onItemSelect
|
||||
}) => {
|
||||
const [expandedSections, setExpandedSections] = useState<string[]>(
|
||||
sections.map(s => s.label)
|
||||
);
|
||||
|
||||
const toggleSection = (label: string) => {
|
||||
setExpandedSections(prev =>
|
||||
prev.includes(label)
|
||||
? prev.filter(l => l !== label)
|
||||
: [...prev, label]
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<aside className="w-64 p-4">
|
||||
<div className="space-y-2">
|
||||
{sections.map((section) => {
|
||||
const isExpanded = expandedSections.includes(section.label);
|
||||
|
||||
return (
|
||||
<div key={section.label}>
|
||||
<button
|
||||
onClick={() => toggleSection(section.label)}
|
||||
className={cn(
|
||||
"flex items-center justify-between w-full px-3 py-2 text-left",
|
||||
"rounded-md hover:bg-white/10 dark:hover:bg-white/5",
|
||||
"text-sm font-medium text-gray-700 dark:text-gray-300",
|
||||
"transition-colors"
|
||||
)}
|
||||
>
|
||||
{section.label}
|
||||
{isExpanded ? (
|
||||
<ChevronDown className="w-4 h-4" />
|
||||
) : (
|
||||
<ChevronRight className="w-4 h-4" />
|
||||
)}
|
||||
</button>
|
||||
|
||||
{isExpanded && (
|
||||
<div className="ml-2 mt-1 space-y-1">
|
||||
{section.items.map((item) => {
|
||||
const isSelected = selectedItem === item;
|
||||
|
||||
return (
|
||||
<button
|
||||
key={item}
|
||||
onClick={() => onItemSelect(item)}
|
||||
className={cn(
|
||||
"block w-full px-3 py-1.5 text-left rounded-md",
|
||||
"text-sm text-gray-600 dark:text-gray-400",
|
||||
"transition-colors",
|
||||
isSelected
|
||||
? "bg-cyan-500/20 text-cyan-700 dark:text-cyan-300"
|
||||
: "hover:bg-white/10 dark:hover:bg-white/5"
|
||||
)}
|
||||
>
|
||||
{item}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</aside>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,162 @@
|
||||
import React, { useState } from 'react';
|
||||
import { cn, glassmorphism, glassCard } from '@/features/ui/primitives/styles';
|
||||
import { Palette, Component, Layout, Code, ChevronDown } from 'lucide-react';
|
||||
|
||||
interface NavigationItem {
|
||||
id: string;
|
||||
label: string;
|
||||
icon: React.ReactNode;
|
||||
items?: string[];
|
||||
}
|
||||
|
||||
interface PillNavigationProps {
|
||||
selectedSection: string;
|
||||
selectedItem: string | null;
|
||||
onSectionChange: (section: string) => void;
|
||||
onItemChange: (item: string | null) => void;
|
||||
}
|
||||
|
||||
const NAVIGATION_ITEMS: NavigationItem[] = [
|
||||
{
|
||||
id: 'foundations',
|
||||
label: 'Foundations',
|
||||
icon: <Palette className="w-4 h-4" />,
|
||||
items: ['Colors', 'Typography', 'Spacing', 'Effects']
|
||||
},
|
||||
{
|
||||
id: 'components',
|
||||
label: 'Components',
|
||||
icon: <Component className="w-4 h-4" />,
|
||||
items: ['Cards', 'Buttons', 'Forms', 'Tables', 'Modals', 'Toggles']
|
||||
},
|
||||
{
|
||||
id: 'patterns',
|
||||
label: 'Patterns',
|
||||
icon: <Layout className="w-4 h-4" />,
|
||||
items: ['Layouts', 'Feedback', 'Navigation', 'Data Display']
|
||||
},
|
||||
{
|
||||
id: 'examples',
|
||||
label: 'Examples',
|
||||
icon: <Code className="w-4 h-4" />,
|
||||
items: ['Compositions', 'Pages', 'Workflows']
|
||||
}
|
||||
];
|
||||
|
||||
export const PillNavigation: React.FC<PillNavigationProps> = ({
|
||||
selectedSection,
|
||||
selectedItem,
|
||||
onSectionChange,
|
||||
onItemChange
|
||||
}) => {
|
||||
const [openDropdown, setOpenDropdown] = useState<string | null>(null);
|
||||
|
||||
const handleSectionClick = (sectionId: string) => {
|
||||
if (selectedSection === sectionId && openDropdown === sectionId) {
|
||||
// Close dropdown if same section clicked
|
||||
setOpenDropdown(null);
|
||||
} else {
|
||||
// Open new section
|
||||
onSectionChange(sectionId);
|
||||
setOpenDropdown(sectionId);
|
||||
}
|
||||
};
|
||||
|
||||
const handleItemClick = (item: string) => {
|
||||
onItemChange(item);
|
||||
setOpenDropdown(null); // Close dropdown after selection
|
||||
};
|
||||
|
||||
const selectedSectionData = NAVIGATION_ITEMS.find(item => item.id === selectedSection);
|
||||
|
||||
return (
|
||||
<div className="relative">
|
||||
{/* Top-level Glass Pill Navigation - Using proper glass styles */}
|
||||
<div className="flex items-center justify-center">
|
||||
<div className={cn(
|
||||
glassCard.blur.xl, // Standard glass blur
|
||||
glassCard.transparency.light, // Light transparency for true glass
|
||||
glassmorphism.border.default, // Proper glass border
|
||||
"rounded-full p-1 shadow-lg"
|
||||
)}>
|
||||
<div className="flex gap-1">
|
||||
{NAVIGATION_ITEMS.map((item) => {
|
||||
const isSelected = selectedSection === item.id;
|
||||
const hasDropdown = item.items && item.items.length > 0;
|
||||
|
||||
return (
|
||||
<button
|
||||
key={item.id}
|
||||
onClick={() => handleSectionClick(item.id)}
|
||||
className={cn(
|
||||
"flex items-center gap-2 px-6 py-3 rounded-full transition-all duration-200",
|
||||
"text-sm font-medium",
|
||||
isSelected
|
||||
? "bg-cyan-500/20 dark:bg-cyan-400/20 text-cyan-700 dark:text-cyan-300 border border-cyan-400/50 shadow-[0_0_10px_rgba(34,211,238,0.5)]"
|
||||
: "text-gray-700 dark:text-gray-300 hover:bg-white/10 dark:hover:bg-white/5"
|
||||
)}
|
||||
>
|
||||
{item.icon}
|
||||
{item.label}
|
||||
{hasDropdown && (
|
||||
<ChevronDown
|
||||
className={cn(
|
||||
"w-4 h-4 transition-transform duration-200",
|
||||
openDropdown === item.id && "rotate-180"
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Sub-menu Drawer - Using proper glass styles */}
|
||||
{openDropdown && selectedSectionData?.items && (
|
||||
<div className="absolute top-full left-1/2 -translate-x-1/2 mt-2 z-50">
|
||||
<div className={cn(
|
||||
glassCard.blur["2xl"], // Slightly stronger blur for dropdown
|
||||
"shadow-2xl"
|
||||
)}>
|
||||
{/* Square top edge connecting to pill */}
|
||||
<div className={cn(
|
||||
glassCard.transparency.medium,
|
||||
glassmorphism.border.default,
|
||||
"h-4 border-t border-l border-r"
|
||||
)} />
|
||||
|
||||
{/* Dropdown content */}
|
||||
<div className={cn(
|
||||
glassCard.transparency.medium,
|
||||
glassmorphism.border.default,
|
||||
"rounded-b-lg border border-t-0 p-2"
|
||||
)}>
|
||||
<div className="grid grid-cols-3 gap-1 min-w-[400px]">
|
||||
{selectedSectionData.items.map((subItem) => {
|
||||
const isSelected = selectedItem === subItem;
|
||||
|
||||
return (
|
||||
<button
|
||||
key={subItem}
|
||||
onClick={() => handleItemClick(subItem)}
|
||||
className={cn(
|
||||
"px-4 py-3 rounded-lg text-sm font-medium transition-all duration-200",
|
||||
isSelected
|
||||
? "bg-cyan-500/30 dark:bg-cyan-400/30 text-cyan-700 dark:text-cyan-300 border border-cyan-400/50 shadow-[0_0_10px_rgba(34,211,238,0.5)]"
|
||||
: "text-gray-700 dark:text-gray-300 hover:bg-white/20 dark:hover:bg-white/10"
|
||||
)}
|
||||
>
|
||||
{subItem}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
export const MODAL_TYPES = {
|
||||
confirmation: {
|
||||
size: "sm" as const,
|
||||
glowColor: "red" as const,
|
||||
purpose: "Destructive actions"
|
||||
},
|
||||
formCreate: {
|
||||
size: "md" as const,
|
||||
glowColor: "green" as const,
|
||||
purpose: "Creating resources"
|
||||
},
|
||||
formEdit: {
|
||||
size: "md" as const,
|
||||
glowColor: "blue" as const,
|
||||
purpose: "Editing resources"
|
||||
},
|
||||
display: {
|
||||
size: "lg" as const,
|
||||
glowColor: "purple" as const,
|
||||
purpose: "Detailed information"
|
||||
},
|
||||
codeViewer: {
|
||||
size: "xl" as const,
|
||||
glowColor: "cyan" as const,
|
||||
purpose: "Code display"
|
||||
},
|
||||
settings: {
|
||||
size: "lg" as const,
|
||||
glowColor: "blue" as const,
|
||||
purpose: "App settings"
|
||||
}
|
||||
};
|
||||
24
archon-ui-main/src/components/style-guide/types/index.ts
Normal file
24
archon-ui-main/src/components/style-guide/types/index.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
// Type definitions for style guide components
|
||||
|
||||
// Card types
|
||||
export type GlowColor = 'none' | 'purple' | 'blue' | 'cyan' | 'green' | 'orange' | 'pink' | 'red';
|
||||
export type EdgePosition = 'none' | 'top' | 'left' | 'right' | 'bottom';
|
||||
export type EdgeColor = 'purple' | 'blue' | 'cyan' | 'green' | 'orange' | 'pink' | 'red';
|
||||
export type CardSize = 'sm' | 'md' | 'lg' | 'xl';
|
||||
export type Transparency = 'clear' | 'light' | 'medium' | 'frosted' | 'solid';
|
||||
export type BlurLevel = 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl';
|
||||
export type GlassTint = 'none' | 'purple' | 'blue' | 'cyan' | 'green' | 'orange' | 'pink' | 'red';
|
||||
|
||||
// Button types
|
||||
export type ButtonVariant = 'default' | 'destructive' | 'outline' | 'ghost' | 'link' | 'cyan' | 'knowledge';
|
||||
export type ButtonSize = 'xs' | 'sm' | 'default' | 'lg' | 'icon';
|
||||
|
||||
// Form types
|
||||
export type InputType = 'text' | 'email' | 'password' | 'number' | 'search' | 'url' | 'tel';
|
||||
export type LabelPosition = 'left' | 'right' | 'top' | 'bottom';
|
||||
|
||||
// Modal types
|
||||
export type ModalSize = 'sm' | 'md' | 'lg' | 'xl';
|
||||
|
||||
// Toggle types
|
||||
export type ToggleSize = 'sm' | 'md' | 'lg';
|
||||
82
archon-ui-main/src/features/ui/primitives/card.tsx
Normal file
82
archon-ui-main/src/features/ui/primitives/card.tsx
Normal file
@@ -0,0 +1,82 @@
|
||||
import React from "react";
|
||||
import { cn, glassCard } from "./styles";
|
||||
|
||||
interface CardProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
// Glass properties
|
||||
blur?: 'none' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl';
|
||||
transparency?: 'clear' | 'light' | 'medium' | 'frosted' | 'solid';
|
||||
glassTint?: 'none' | 'purple' | 'blue' | 'cyan' | 'green' | 'orange' | 'pink' | 'red';
|
||||
|
||||
// Glow properties
|
||||
glowColor?: 'none' | 'purple' | 'blue' | 'cyan' | 'green' | 'orange' | 'pink' | 'red';
|
||||
|
||||
// Edge-lit properties
|
||||
edgePosition?: 'none' | 'top' | 'left' | 'right' | 'bottom';
|
||||
edgeColor?: 'purple' | 'blue' | 'cyan' | 'green' | 'orange' | 'pink' | 'red';
|
||||
|
||||
// Size
|
||||
size?: 'sm' | 'md' | 'lg' | 'xl';
|
||||
}
|
||||
|
||||
export const Card = React.forwardRef<HTMLDivElement, CardProps>(
|
||||
({
|
||||
className,
|
||||
blur = 'xl', // Default to standard glass (3px blur - subtle)
|
||||
transparency = 'light', // Default to subtle transparency (3%)
|
||||
glassTint = 'none',
|
||||
glowColor = 'none',
|
||||
edgePosition = 'none',
|
||||
edgeColor = 'cyan',
|
||||
size = 'md',
|
||||
children,
|
||||
...props
|
||||
}, ref) => {
|
||||
const glowVariant = glassCard.variants[glowColor] || glassCard.variants.none;
|
||||
const hasEdge = edgePosition !== 'none';
|
||||
const isHorizontalEdge = edgePosition === 'top' || edgePosition === 'bottom';
|
||||
const edgeColorConfig = hasEdge ? glassCard.edgeLit.color[edgeColor] : null;
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn(
|
||||
// Base glass effect
|
||||
glassCard.base,
|
||||
|
||||
// Backdrop blur intensity - consistent glass effect
|
||||
glassCard.blur[blur],
|
||||
|
||||
// Apply background with proper transparency
|
||||
glassTint !== 'none'
|
||||
? glassCard.tints[glassTint][transparency]
|
||||
: glassCard.transparency[transparency],
|
||||
|
||||
// Size
|
||||
glassCard.sizes[size],
|
||||
|
||||
// Glow effects (border, shadow, hover) - only if no edge-lit
|
||||
!hasEdge && glowVariant.border,
|
||||
!hasEdge && glowVariant.glow,
|
||||
!hasEdge && glowVariant.hover,
|
||||
|
||||
// Edge-lit effects
|
||||
hasEdge && glassCard.edgeLit.position[edgePosition],
|
||||
hasEdge && edgeColorConfig ? edgeColorConfig.glow : false,
|
||||
hasEdge && edgeColorConfig ? (
|
||||
isHorizontalEdge
|
||||
? edgeColorConfig.gradient.horizontal
|
||||
: edgeColorConfig.gradient.vertical
|
||||
) : false,
|
||||
|
||||
// User overrides
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
Card.displayName = "Card";
|
||||
20
archon-ui-main/src/features/ui/primitives/label.tsx
Normal file
20
archon-ui-main/src/features/ui/primitives/label.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import * as LabelPrimitive from "@radix-ui/react-label";
|
||||
import React from "react";
|
||||
import { cn } from "./styles";
|
||||
|
||||
export const Label = React.forwardRef<
|
||||
React.ElementRef<typeof LabelPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<LabelPrimitive.Root
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"text-sm font-medium leading-none",
|
||||
"text-gray-700 dark:text-gray-200",
|
||||
"peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
Label.displayName = LabelPrimitive.Root.displayName;
|
||||
32
archon-ui-main/src/features/ui/primitives/radio-group.tsx
Normal file
32
archon-ui-main/src/features/ui/primitives/radio-group.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
import * as RadioGroupPrimitive from "@radix-ui/react-radio-group";
|
||||
import { Circle } from "lucide-react";
|
||||
import React from "react";
|
||||
import { cn, glassmorphism } from "./styles";
|
||||
|
||||
export const RadioGroup = RadioGroupPrimitive.Root;
|
||||
|
||||
export const RadioGroupItem = React.forwardRef<
|
||||
React.ElementRef<typeof RadioGroupPrimitive.Item>,
|
||||
React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Item>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<RadioGroupPrimitive.Item
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"aspect-square h-4 w-4 rounded-full",
|
||||
"backdrop-blur-md bg-gradient-to-b from-white/80 to-white/60",
|
||||
"dark:from-white/10 dark:to-black/30",
|
||||
glassmorphism.border.default,
|
||||
glassmorphism.interactive.base,
|
||||
"focus:outline-none focus:ring-2 focus:ring-cyan-500",
|
||||
"disabled:cursor-not-allowed disabled:opacity-50",
|
||||
"data-[state=checked]:border-cyan-500",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<RadioGroupPrimitive.Indicator className="flex items-center justify-center">
|
||||
<Circle className="h-2.5 w-2.5 fill-cyan-500 text-cyan-500" />
|
||||
</RadioGroupPrimitive.Indicator>
|
||||
</RadioGroupPrimitive.Item>
|
||||
));
|
||||
RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName;
|
||||
@@ -8,28 +8,27 @@
|
||||
* - For runtime theme values, use useThemeAware hook
|
||||
*/
|
||||
|
||||
// Base glassmorphism classes with Tron aesthetic
|
||||
// Base glassmorphism classes with Tron aesthetic - TRUE GLASS EFFECT
|
||||
export const glassmorphism = {
|
||||
// Background variations - matching existing Card.tsx patterns
|
||||
// Background variations - TRUE TRANSPARENCY for glass effect
|
||||
background: {
|
||||
subtle: "backdrop-blur-md bg-gradient-to-b from-white/80 to-white/60 dark:from-white/10 dark:to-black/30",
|
||||
strong: "backdrop-blur-md bg-gradient-to-b from-white/95 to-white/90 dark:from-gray-800/95 dark:to-gray-900/95",
|
||||
card: "backdrop-blur-md bg-gradient-to-b from-white/80 to-white/60 dark:from-white/10 dark:to-black/30",
|
||||
// Tron-style colored backgrounds
|
||||
cyan: "backdrop-blur-md bg-gradient-to-b from-cyan-100/80 dark:from-cyan-500/20 to-white/60 dark:to-cyan-500/5",
|
||||
blue: "backdrop-blur-md bg-gradient-to-b from-blue-100/80 dark:from-blue-500/20 to-white/60 dark:to-blue-500/5",
|
||||
purple:
|
||||
"backdrop-blur-md bg-gradient-to-b from-purple-100/80 dark:from-purple-500/20 to-white/60 dark:to-purple-500/5",
|
||||
subtle: "backdrop-blur-xl bg-white/5 dark:bg-white/10",
|
||||
strong: "backdrop-blur-xl bg-white/10 dark:bg-white/20",
|
||||
card: "backdrop-blur-xl bg-white/5 dark:bg-white/10",
|
||||
// Tron-style colored backgrounds - VERY transparent with strong blur
|
||||
cyan: "backdrop-blur-xl bg-cyan-400/5 dark:bg-cyan-400/10",
|
||||
blue: "backdrop-blur-xl bg-blue-400/5 dark:bg-blue-400/10",
|
||||
purple: "backdrop-blur-xl bg-purple-400/5 dark:bg-purple-400/10",
|
||||
},
|
||||
|
||||
// Border styles with neon glow
|
||||
// Border styles for glass effect - more prominent for edge definition
|
||||
border: {
|
||||
default: "border border-gray-200 dark:border-gray-700",
|
||||
cyan: "border-cyan-300 dark:border-cyan-500/30",
|
||||
blue: "border-blue-300 dark:border-blue-500/30",
|
||||
purple: "border-purple-300 dark:border-purple-500/30",
|
||||
focus: "focus:border-cyan-500 focus:shadow-[0_0_20px_5px_rgba(34,211,238,0.5)]",
|
||||
hover: "hover:border-cyan-400/70 hover:shadow-[0_0_15px_rgba(34,211,238,0.4)]",
|
||||
default: "border border-white/10 dark:border-white/[0.06]",
|
||||
cyan: "border border-cyan-400/50 dark:border-cyan-400/40",
|
||||
blue: "border border-blue-400/50 dark:border-blue-400/40",
|
||||
purple: "border border-purple-400/50 dark:border-purple-400/40",
|
||||
focus: "focus:border-cyan-400 focus:shadow-[0_0_30px_10px_rgba(34,211,238,0.6)]",
|
||||
hover: "hover:border-cyan-400/80 hover:shadow-[0_0_25px_5px_rgba(34,211,238,0.5)]",
|
||||
},
|
||||
|
||||
// Interactive states
|
||||
@@ -59,17 +58,37 @@ export const glassmorphism = {
|
||||
md: "shadow-md dark:shadow-lg",
|
||||
lg: "shadow-lg dark:shadow-2xl",
|
||||
elevated: "shadow-[0_10px_30px_-15px_rgba(0,0,0,0.1)] dark:shadow-[0_10px_30px_-15px_rgba(0,0,0,0.7)]",
|
||||
// Neon glow effects matching Card.tsx
|
||||
// Strong neon glow effects for true Tron aesthetic
|
||||
glow: {
|
||||
cyan: "shadow-[0_0_10px_2px_rgba(34,211,238,0.4)] dark:shadow-[0_0_20px_5px_rgba(34,211,238,0.7)]",
|
||||
blue: "shadow-[0_0_10px_2px_rgba(59,130,246,0.4)] dark:shadow-[0_0_20px_5px_rgba(59,130,246,0.7)]",
|
||||
purple: "shadow-[0_0_10px_2px_rgba(168,85,247,0.4)] dark:shadow-[0_0_20px_5px_rgba(168,85,247,0.7)]",
|
||||
green: "shadow-[0_0_10px_2px_rgba(16,185,129,0.4)] dark:shadow-[0_0_20px_5px_rgba(16,185,129,0.7)]",
|
||||
pink: "shadow-[0_0_10px_2px_rgba(236,72,153,0.4)] dark:shadow-[0_0_20px_5px_rgba(236,72,153,0.7)]",
|
||||
orange: "shadow-[0_0_10px_2px_rgba(251,146,60,0.4)] dark:shadow-[0_0_20px_5px_rgba(251,146,60,0.7)]",
|
||||
purple: "shadow-[0_0_30px_10px_rgba(168,85,247,0.5)] dark:shadow-[0_0_40px_15px_rgba(168,85,247,0.7)]",
|
||||
blue: "shadow-[0_0_30px_10px_rgba(59,130,246,0.5)] dark:shadow-[0_0_40px_15px_rgba(59,130,246,0.7)]",
|
||||
green: "shadow-[0_0_30px_10px_rgba(16,185,129,0.5)] dark:shadow-[0_0_40px_15px_rgba(16,185,129,0.7)]",
|
||||
red: "shadow-[0_0_30px_10px_rgba(239,68,68,0.5)] dark:shadow-[0_0_40px_15px_rgba(239,68,68,0.7)]",
|
||||
orange: "shadow-[0_0_30px_10px_rgba(251,146,60,0.5)] dark:shadow-[0_0_40px_15px_rgba(251,146,60,0.7)]",
|
||||
cyan: "shadow-[0_0_30px_10px_rgba(34,211,238,0.5)] dark:shadow-[0_0_40px_15px_rgba(34,211,238,0.7)]",
|
||||
pink: "shadow-[0_0_30px_10px_rgba(236,72,153,0.5)] dark:shadow-[0_0_40px_15px_rgba(236,72,153,0.7)]",
|
||||
},
|
||||
},
|
||||
|
||||
// Edge glow positions - now part of glassCard for better integration
|
||||
edgePositions: {
|
||||
none: "",
|
||||
top: "before:content-[''] before:absolute before:top-0 before:left-0 before:right-0 before:h-[2px]",
|
||||
left: "before:content-[''] before:absolute before:top-0 before:left-0 before:bottom-0 before:w-[2px]",
|
||||
right: "before:content-[''] before:absolute before:top-0 before:right-0 before:bottom-0 before:w-[2px]",
|
||||
bottom: "before:content-[''] before:absolute before:bottom-0 before:left-0 before:right-0 before:h-[2px]"
|
||||
},
|
||||
|
||||
// Configurable sizes for cards
|
||||
sizes: {
|
||||
card: {
|
||||
sm: "p-4 max-w-sm",
|
||||
md: "p-6 max-w-md",
|
||||
lg: "p-8 max-w-lg",
|
||||
xl: "p-10 max-w-xl"
|
||||
}
|
||||
},
|
||||
|
||||
// Priority colors (matching our task system)
|
||||
priority: {
|
||||
critical: {
|
||||
@@ -99,6 +118,207 @@ export const glassmorphism = {
|
||||
},
|
||||
};
|
||||
|
||||
// Card-specific glass styles with accent colors
|
||||
export const glassCard = {
|
||||
// Base glass card (true transparency) - NO blur here, controlled separately
|
||||
base: "relative rounded-lg overflow-hidden border transition-all duration-300",
|
||||
|
||||
// Blur intensity levels - EXTREMELY SUBTLE for true glass
|
||||
blur: {
|
||||
none: "backdrop-blur-none", // No blur (0px)
|
||||
sm: "backdrop-blur-[0.5px]", // Barely perceptible
|
||||
md: "backdrop-blur-[1px]", // Very subtle hint
|
||||
lg: "backdrop-blur-[2px]", // Light glass effect
|
||||
xl: "backdrop-blur-[3px]", // Standard glass
|
||||
"2xl": "backdrop-blur-[5px]", // Noticeable glass
|
||||
"3xl": "backdrop-blur-[8px]" // Maximum (what was "standard" before)
|
||||
},
|
||||
|
||||
// Glass transparency levels - Theme-aware for better color visibility
|
||||
transparency: {
|
||||
clear: "bg-white/[0.01] dark:bg-black/[0.01]", // 1% - almost invisible
|
||||
light: "bg-white/[0.03] dark:bg-black/[0.05]", // 3-5% - very subtle glass
|
||||
medium: "bg-white/[0.05] dark:bg-black/[0.08]", // 5-8% - standard glass
|
||||
frosted: "bg-white/[0.08] dark:bg-black/[0.12]", // 8-12% - frosted glass
|
||||
solid: "bg-white/[0.12] dark:bg-black/[0.20]" // 12-20% - more visible
|
||||
},
|
||||
|
||||
// Colored glass tints - BRIGHT NEON COLORS with higher opacity
|
||||
tints: {
|
||||
none: "",
|
||||
purple: {
|
||||
clear: "bg-purple-500/[0.03] dark:bg-purple-400/[0.04]", // 3-4% - barely visible tint
|
||||
light: "bg-purple-500/[0.08] dark:bg-purple-400/[0.10]", // 8-10% - subtle colored glass
|
||||
medium: "bg-purple-500/[0.15] dark:bg-purple-400/[0.20]", // 15-20% - standard colored glass
|
||||
frosted: "bg-purple-500/[0.25] dark:bg-purple-400/[0.35]", // 25-35% - frosted colored glass
|
||||
solid: "bg-purple-500/[0.40] dark:bg-purple-400/[0.60]" // 40-60% - bright neon glow
|
||||
},
|
||||
blue: {
|
||||
clear: "bg-blue-500/[0.03] dark:bg-blue-400/[0.04]",
|
||||
light: "bg-blue-500/[0.08] dark:bg-blue-400/[0.10]",
|
||||
medium: "bg-blue-500/[0.15] dark:bg-blue-400/[0.20]",
|
||||
frosted: "bg-blue-500/[0.25] dark:bg-blue-400/[0.35]",
|
||||
solid: "bg-blue-500/[0.40] dark:bg-blue-400/[0.60]"
|
||||
},
|
||||
cyan: {
|
||||
clear: "bg-cyan-500/[0.03] dark:bg-cyan-400/[0.04]",
|
||||
light: "bg-cyan-500/[0.08] dark:bg-cyan-400/[0.10]",
|
||||
medium: "bg-cyan-500/[0.15] dark:bg-cyan-400/[0.20]",
|
||||
frosted: "bg-cyan-500/[0.25] dark:bg-cyan-400/[0.35]",
|
||||
solid: "bg-cyan-500/[0.40] dark:bg-cyan-400/[0.60]"
|
||||
},
|
||||
green: {
|
||||
clear: "bg-emerald-500/[0.03] dark:bg-emerald-400/[0.04]",
|
||||
light: "bg-emerald-500/[0.08] dark:bg-emerald-400/[0.10]",
|
||||
medium: "bg-emerald-500/[0.15] dark:bg-emerald-400/[0.20]",
|
||||
frosted: "bg-emerald-500/[0.25] dark:bg-emerald-400/[0.35]",
|
||||
solid: "bg-emerald-500/[0.40] dark:bg-emerald-400/[0.60]"
|
||||
},
|
||||
orange: {
|
||||
clear: "bg-orange-500/[0.03] dark:bg-orange-400/[0.04]",
|
||||
light: "bg-orange-500/[0.08] dark:bg-orange-400/[0.10]",
|
||||
medium: "bg-orange-500/[0.15] dark:bg-orange-400/[0.20]",
|
||||
frosted: "bg-orange-500/[0.25] dark:bg-orange-400/[0.35]",
|
||||
solid: "bg-orange-500/[0.40] dark:bg-orange-400/[0.60]"
|
||||
},
|
||||
pink: {
|
||||
clear: "bg-pink-500/[0.03] dark:bg-pink-400/[0.04]",
|
||||
light: "bg-pink-500/[0.08] dark:bg-pink-400/[0.10]",
|
||||
medium: "bg-pink-500/[0.15] dark:bg-pink-400/[0.20]",
|
||||
frosted: "bg-pink-500/[0.25] dark:bg-pink-400/[0.35]",
|
||||
solid: "bg-pink-500/[0.40] dark:bg-pink-400/[0.60]"
|
||||
},
|
||||
red: {
|
||||
clear: "bg-red-500/[0.03] dark:bg-red-400/[0.04]",
|
||||
light: "bg-red-500/[0.08] dark:bg-red-400/[0.10]",
|
||||
medium: "bg-red-500/[0.15] dark:bg-red-400/[0.20]",
|
||||
frosted: "bg-red-500/[0.25] dark:bg-red-400/[0.35]",
|
||||
solid: "bg-red-500/[0.40] dark:bg-red-400/[0.60]"
|
||||
}
|
||||
},
|
||||
|
||||
// Neon glow effects - BRIGHTER & MORE INTENSE
|
||||
variants: {
|
||||
none: {
|
||||
border: "border-gray-300/20 dark:border-white/10",
|
||||
glow: "",
|
||||
hover: "hover:bg-white/[0.04] dark:hover:bg-white/[0.02]"
|
||||
},
|
||||
purple: {
|
||||
border: "border-purple-500/50 dark:border-purple-400/40",
|
||||
glow: "shadow-[0_0_40px_15px_rgba(168,85,247,0.4)] dark:shadow-[0_0_60px_25px_rgba(168,85,247,0.7)]",
|
||||
hover: "hover:shadow-[0_0_50px_20px_rgba(168,85,247,0.5)] dark:hover:shadow-[0_0_80px_30px_rgba(168,85,247,0.8)]"
|
||||
},
|
||||
blue: {
|
||||
border: "border-blue-500/50 dark:border-blue-400/40",
|
||||
glow: "shadow-[0_0_40px_15px_rgba(59,130,246,0.4)] dark:shadow-[0_0_60px_25px_rgba(59,130,246,0.7)]",
|
||||
hover: "hover:shadow-[0_0_50px_20px_rgba(59,130,246,0.5)] dark:hover:shadow-[0_0_80px_30px_rgba(59,130,246,0.8)]"
|
||||
},
|
||||
green: {
|
||||
border: "border-emerald-500/50 dark:border-emerald-400/40",
|
||||
glow: "shadow-[0_0_40px_15px_rgba(16,185,129,0.4)] dark:shadow-[0_0_60px_25px_rgba(16,185,129,0.7)]",
|
||||
hover: "hover:shadow-[0_0_50px_20px_rgba(16,185,129,0.5)] dark:hover:shadow-[0_0_80px_30px_rgba(16,185,129,0.8)]"
|
||||
},
|
||||
cyan: {
|
||||
border: "border-cyan-500/50 dark:border-cyan-400/40",
|
||||
glow: "shadow-[0_0_40px_15px_rgba(34,211,238,0.4)] dark:shadow-[0_0_60px_25px_rgba(34,211,238,0.7)]",
|
||||
hover: "hover:shadow-[0_0_50px_20px_rgba(34,211,238,0.5)] dark:hover:shadow-[0_0_80px_30px_rgba(34,211,238,0.8)]"
|
||||
},
|
||||
orange: {
|
||||
border: "border-orange-500/50 dark:border-orange-400/40",
|
||||
glow: "shadow-[0_0_40px_15px_rgba(251,146,60,0.4)] dark:shadow-[0_0_60px_25px_rgba(251,146,60,0.7)]",
|
||||
hover: "hover:shadow-[0_0_50px_20px_rgba(251,146,60,0.5)] dark:hover:shadow-[0_0_80px_30px_rgba(251,146,60,0.8)]"
|
||||
},
|
||||
pink: {
|
||||
border: "border-pink-500/50 dark:border-pink-400/40",
|
||||
glow: "shadow-[0_0_40px_15px_rgba(236,72,153,0.4)] dark:shadow-[0_0_60px_25px_rgba(236,72,153,0.7)]",
|
||||
hover: "hover:shadow-[0_0_50px_20px_rgba(236,72,153,0.5)] dark:hover:shadow-[0_0_80px_30px_rgba(236,72,153,0.8)]"
|
||||
},
|
||||
red: {
|
||||
border: "border-red-500/50 dark:border-red-400/40",
|
||||
glow: "shadow-[0_0_40px_15px_rgba(239,68,68,0.4)] dark:shadow-[0_0_60px_25px_rgba(239,68,68,0.7)]",
|
||||
hover: "hover:shadow-[0_0_50px_20px_rgba(239,68,68,0.5)] dark:hover:shadow-[0_0_80px_30px_rgba(239,68,68,0.8)]"
|
||||
}
|
||||
},
|
||||
|
||||
// Size variants
|
||||
sizes: {
|
||||
sm: "p-4",
|
||||
md: "p-6",
|
||||
lg: "p-8",
|
||||
xl: "p-10"
|
||||
},
|
||||
|
||||
// Edge-lit effects for cards (top, left, right, bottom edges)
|
||||
edgeLit: {
|
||||
position: {
|
||||
none: "",
|
||||
top: "before:content-[''] before:absolute before:top-0 before:left-0 before:right-0 before:h-[2px] before:rounded-t-lg",
|
||||
left: "before:content-[''] before:absolute before:top-0 before:left-0 before:bottom-0 before:w-[2px] before:rounded-l-lg",
|
||||
right: "before:content-[''] before:absolute before:top-0 before:right-0 before:bottom-0 before:w-[2px] before:rounded-r-lg",
|
||||
bottom: "before:content-[''] before:absolute before:bottom-0 before:left-0 before:right-0 before:h-[2px] before:rounded-b-lg"
|
||||
},
|
||||
color: {
|
||||
purple: {
|
||||
line: "before:bg-purple-500 dark:before:bg-purple-400",
|
||||
glow: "before:shadow-[0_0_15px_4px_rgba(168,85,247,0.8)]",
|
||||
gradient: {
|
||||
horizontal: "before:bg-gradient-to-r before:from-transparent before:via-purple-500 dark:before:via-purple-400 before:to-transparent",
|
||||
vertical: "before:bg-gradient-to-b before:from-transparent before:via-purple-500 dark:before:via-purple-400 before:to-transparent"
|
||||
}
|
||||
},
|
||||
blue: {
|
||||
line: "before:bg-blue-500 dark:before:bg-blue-400",
|
||||
glow: "before:shadow-[0_0_15px_4px_rgba(59,130,246,0.8)]",
|
||||
gradient: {
|
||||
horizontal: "before:bg-gradient-to-r before:from-transparent before:via-blue-500 dark:before:via-blue-400 before:to-transparent",
|
||||
vertical: "before:bg-gradient-to-b before:from-transparent before:via-blue-500 dark:before:via-blue-400 before:to-transparent"
|
||||
}
|
||||
},
|
||||
cyan: {
|
||||
line: "before:bg-cyan-500 dark:before:bg-cyan-400",
|
||||
glow: "before:shadow-[0_0_15px_4px_rgba(34,211,238,0.8)]",
|
||||
gradient: {
|
||||
horizontal: "before:bg-gradient-to-r before:from-transparent before:via-cyan-500 dark:before:via-cyan-400 before:to-transparent",
|
||||
vertical: "before:bg-gradient-to-b before:from-transparent before:via-cyan-500 dark:before:via-cyan-400 before:to-transparent"
|
||||
}
|
||||
},
|
||||
green: {
|
||||
line: "before:bg-emerald-500 dark:before:bg-emerald-400",
|
||||
glow: "before:shadow-[0_0_15px_4px_rgba(16,185,129,0.8)]",
|
||||
gradient: {
|
||||
horizontal: "before:bg-gradient-to-r before:from-transparent before:via-emerald-500 dark:before:via-emerald-400 before:to-transparent",
|
||||
vertical: "before:bg-gradient-to-b before:from-transparent before:via-emerald-500 dark:before:via-emerald-400 before:to-transparent"
|
||||
}
|
||||
},
|
||||
orange: {
|
||||
line: "before:bg-orange-500 dark:before:bg-orange-400",
|
||||
glow: "before:shadow-[0_0_15px_4px_rgba(251,146,60,0.8)]",
|
||||
gradient: {
|
||||
horizontal: "before:bg-gradient-to-r before:from-transparent before:via-orange-500 dark:before:via-orange-400 before:to-transparent",
|
||||
vertical: "before:bg-gradient-to-b before:from-transparent before:via-orange-500 dark:before:via-orange-400 before:to-transparent"
|
||||
}
|
||||
},
|
||||
pink: {
|
||||
line: "before:bg-pink-500 dark:before:bg-pink-400",
|
||||
glow: "before:shadow-[0_0_15px_4px_rgba(236,72,153,0.8)]",
|
||||
gradient: {
|
||||
horizontal: "before:bg-gradient-to-r before:from-transparent before:via-pink-500 dark:before:via-pink-400 before:to-transparent",
|
||||
vertical: "before:bg-gradient-to-b before:from-transparent before:via-pink-500 dark:before:via-pink-400 before:to-transparent"
|
||||
}
|
||||
},
|
||||
red: {
|
||||
line: "before:bg-red-500 dark:before:bg-red-400",
|
||||
glow: "before:shadow-[0_0_15px_4px_rgba(239,68,68,0.8)]",
|
||||
gradient: {
|
||||
horizontal: "before:bg-gradient-to-r before:from-transparent before:via-red-500 dark:before:via-red-400 before:to-transparent",
|
||||
vertical: "before:bg-gradient-to-b before:from-transparent before:via-red-500 dark:before:via-red-400 before:to-transparent"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Compound styles for common patterns
|
||||
export const compoundStyles = {
|
||||
// Standard interactive element (buttons, menu items, etc.)
|
||||
@@ -127,7 +347,7 @@ export const compoundStyles = {
|
||||
${glassmorphism.interactive.disabled}
|
||||
`,
|
||||
|
||||
// Cards and containers
|
||||
// Cards - use glassCard instead
|
||||
card: `
|
||||
${glassmorphism.background.card}
|
||||
${glassmorphism.border.default}
|
||||
|
||||
35
archon-ui-main/src/features/ui/primitives/switch.tsx
Normal file
35
archon-ui-main/src/features/ui/primitives/switch.tsx
Normal file
@@ -0,0 +1,35 @@
|
||||
import * as SwitchPrimitives from "@radix-ui/react-switch";
|
||||
import * as React from "react";
|
||||
import { cn, glassmorphism } from "./styles";
|
||||
|
||||
const Switch = React.forwardRef<
|
||||
React.ElementRef<typeof SwitchPrimitives.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SwitchPrimitives.Root
|
||||
className={cn(
|
||||
"peer inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full",
|
||||
"transition-colors focus-visible:outline-none focus-visible:ring-2",
|
||||
"focus-visible:ring-cyan-500 focus-visible:ring-offset-2",
|
||||
"disabled:cursor-not-allowed disabled:opacity-50",
|
||||
"data-[state=checked]:bg-cyan-500",
|
||||
"data-[state=unchecked]:bg-gray-200 dark:data-[state=unchecked]:bg-gray-700",
|
||||
glassmorphism.interactive.base,
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
ref={ref}
|
||||
>
|
||||
<SwitchPrimitives.Thumb
|
||||
className={cn(
|
||||
"pointer-events-none block h-4 w-4 rounded-full",
|
||||
"bg-white shadow-lg ring-0 transition-transform",
|
||||
"data-[state=checked]:translate-x-4",
|
||||
"data-[state=unchecked]:translate-x-0"
|
||||
)}
|
||||
/>
|
||||
</SwitchPrimitives.Root>
|
||||
));
|
||||
Switch.displayName = SwitchPrimitives.Root.displayName;
|
||||
|
||||
export { Switch };
|
||||
@@ -2,9 +2,10 @@ import * as ToggleGroupPrimitive from "@radix-ui/react-toggle-group";
|
||||
import React from "react";
|
||||
import { cn, glassmorphism } from "./styles";
|
||||
|
||||
export interface ToggleGroupProps extends React.ComponentPropsWithoutRef<typeof ToggleGroupPrimitive.Root> {
|
||||
type ToggleGroupProps = (ToggleGroupPrimitive.ToggleGroupSingleProps | ToggleGroupPrimitive.ToggleGroupMultipleProps) & {
|
||||
variant?: "subtle" | "solid";
|
||||
size?: "sm" | "md";
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const ToggleGroup = React.forwardRef<React.ElementRef<typeof ToggleGroupPrimitive.Root>, ToggleGroupProps>(
|
||||
|
||||
8
archon-ui-main/src/pages/StyleGuidePage.tsx
Normal file
8
archon-ui-main/src/pages/StyleGuidePage.tsx
Normal file
@@ -0,0 +1,8 @@
|
||||
import React from 'react';
|
||||
import { StyleGuideView } from '@/components/style-guide/StyleGuideView';
|
||||
|
||||
const StyleGuidePage: React.FC = () => {
|
||||
return <StyleGuideView />;
|
||||
};
|
||||
|
||||
export default StyleGuidePage;
|
||||
Reference in New Issue
Block a user